@artblocks/contracts 1.0.4 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/README.md +75 -7
  2. package/artifacts/contracts/AdminACLV0.sol/AdminACLV0.json +2 -2
  3. package/artifacts/contracts/AdminACLV1.sol/AdminACLV1.json +2 -2
  4. package/artifacts/contracts/DependencyRegistryV0.sol/DependencyRegistryV0.json +336 -131
  5. package/artifacts/contracts/archive/GenArt721CoreV3.sol/GenArt721CoreV3.json +2047 -0
  6. package/artifacts/contracts/archive/GenArt721CoreV3_Explorations.sol/GenArt721CoreV3_Explorations.json +2021 -0
  7. package/artifacts/contracts/{minter-suite → archive/minter-suite}/Minters/MinterBase_v0_1_1.sol/MinterBase.json +1 -1
  8. package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExp/MinterDAExpV2.sol/MinterDAExpV2.json +2 -2
  9. package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExp/MinterDAExpV3.sol/MinterDAExpV3.json +2 -2
  10. package/artifacts/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterDAExp}/MinterDAExpV4.sol/MinterDAExpV4.json +3 -3
  11. package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExpSettlement/MinterDAExpSettlementV0.sol/MinterDAExpSettlementV0.json +2 -2
  12. package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExpSettlement/MinterDAExpSettlementV1.sol/MinterDAExpSettlementV1.json +2 -2
  13. package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExpSettlement/MinterDAExpSettlementV2.sol/MinterDAExpSettlementV2.json +1109 -0
  14. package/artifacts/contracts/archive/minter-suite/Minters/MinterDALin/MinterDALinV2.sol/MinterDALinV2.json +2 -2
  15. package/artifacts/contracts/archive/minter-suite/Minters/MinterDALin/MinterDALinV3.sol/MinterDALinV3.json +2 -2
  16. package/artifacts/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterDALin}/MinterDALinV4.sol/MinterDALinV4.json +3 -3
  17. package/artifacts/contracts/archive/minter-suite/Minters/MinterHolder/MinterHolderV1.sol/MinterHolderV1.json +2 -2
  18. package/artifacts/contracts/archive/minter-suite/Minters/MinterHolder/MinterHolderV2.sol/MinterHolderV2.json +2 -2
  19. package/artifacts/contracts/archive/minter-suite/Minters/MinterHolder/MinterHolderV3.sol/MinterHolderV3.json +2 -2
  20. package/artifacts/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterHolder}/MinterHolderV4.sol/MinterHolderV4.json +3 -3
  21. package/artifacts/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV1.sol/MinterMerkleV1.json +2 -2
  22. package/artifacts/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV2.sol/MinterMerkleV2.json +2 -2
  23. package/artifacts/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV3.sol/MinterMerkleV3.json +2 -2
  24. package/artifacts/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV4.sol/MinterMerkleV4.json +2 -2
  25. package/artifacts/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterMerkle}/MinterMerkleV5.sol/MinterMerkleV5.json +3 -3
  26. package/artifacts/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterPolyptych}/MinterPolyptychV0.sol/IGenArt721CoreContractV3WithRandomizer.json +31 -63
  27. package/artifacts/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterPolyptych}/MinterPolyptychV0.sol/MinterPolyptychV0.json +3 -3
  28. package/artifacts/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterSEA}/MinterSEAV0.sol/MinterSEAV0.json +3 -3
  29. package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPrice/MinterSetPriceV2.sol/MinterSetPriceV2.json +2 -2
  30. package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPrice/MinterSetPriceV3.sol/MinterSetPriceV3.json +2 -2
  31. package/artifacts/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterSetPrice}/MinterSetPriceV4.sol/MinterSetPriceV4.json +3 -3
  32. package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPriceERC20/MinterSetPriceERC20V0.sol/MinterSetPriceERC20V0.json +65 -2
  33. package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPriceERC20/MinterSetPriceERC20V1.sol/MinterSetPriceERC20V1.json +65 -2
  34. package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPriceERC20/MinterSetPriceERC20V2.sol/MinterSetPriceERC20V2.json +2 -2
  35. package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPriceERC20/MinterSetPriceERC20V3.sol/MinterSetPriceERC20V3.json +2 -2
  36. package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPriceERC20/MinterSetPriceERC20V4.sol/MinterSetPriceERC20V4.json +807 -0
  37. package/artifacts/contracts/engine/V2/GenArt721MinterBurner_PBAB.sol/GenArt721MinterBurner_PBAB.json +65 -2
  38. package/artifacts/contracts/engine/V2/GenArt721Minter_PBAB.sol/GenArt721Minter_PBAB.json +65 -2
  39. package/artifacts/contracts/engine/V3/EngineFactoryV0.sol/EngineFactoryV0.json +606 -0
  40. package/artifacts/contracts/engine/V3/GenArt721CoreV3_Engine.sol/GenArt721CoreV3_Engine.json +565 -209
  41. package/artifacts/contracts/engine/V3/GenArt721CoreV3_Engine_Flex.sol/GenArt721CoreV3_Engine_Flex.json +785 -227
  42. package/artifacts/contracts/engine/V3/forks/GenArt721CoreV3_Engine_Flex_PROOF.sol/GenArt721CoreV3_Engine_Flex_PROOF.json +40 -15
  43. package/artifacts/contracts/engine/V3/forks/PROHIBITION/AdminACLV0_PROHIBITION.sol/AdminACLV0_PROHIBITION.json +2 -2
  44. package/artifacts/contracts/engine/V3/forks/PROHIBITION/GenArt721CoreV3_Engine_Flex_PROHIBITION.sol/GenArt721CoreV3_Engine_Flex_PROHIBITION.json +40 -15
  45. package/artifacts/contracts/engine/V3/forks/PROHIBITION/interfaces/IGenArt721CoreContractV3_Engine_Flex_PROHIBITION.sol/IGenArt721CoreContractV3_Engine_Flex_PROHIBITION.json +36 -68
  46. package/artifacts/contracts/engine-registry/CoreRegistryV1.sol/CoreRegistryV1.json +12 -12
  47. package/artifacts/contracts/interfaces/v0.8.x/IAdminACLV0_Extended.sol/IAdminACLV0_Extended.json +140 -0
  48. package/artifacts/contracts/interfaces/v0.8.x/IBytecodeStorageReaderV2.sol/IBytecodeStorageReaderV2.json +130 -0
  49. package/artifacts/contracts/interfaces/v0.8.x/ICoreRegistryV1.sol/ICoreRegistryV1.json +6 -6
  50. package/artifacts/contracts/interfaces/v0.8.x/IDependencyRegistryV0.sol/IDependencyRegistryV0.json +71 -39
  51. package/artifacts/contracts/interfaces/v0.8.x/IEngineFactoryV0.sol/IEngineFactoryV0.json +283 -0
  52. package/artifacts/contracts/interfaces/v0.8.x/IFilteredMinterDAExpSettlementV0.sol/IFilteredMinterDAExpSettlementV0.json +12 -12
  53. package/artifacts/contracts/interfaces/v0.8.x/IFilteredMinterDAExpSettlementV1.sol/IFilteredMinterDAExpSettlementV1.json +12 -12
  54. package/artifacts/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3.sol/IGenArt721CoreContractV3.json +30 -62
  55. package/artifacts/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_Base.sol/IGenArt721CoreContractV3_Base.json +30 -62
  56. package/artifacts/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_Engine.sol/IGenArt721CoreContractV3_Engine.json +170 -104
  57. package/artifacts/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_Engine_Flex.sol/IGenArt721CoreContractV3_Engine_Flex.json +170 -104
  58. package/artifacts/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_Engine_Flex_PreV3p2.sol/IGenArt721CoreContractV3_Engine_Flex_PreV3p2.json +1028 -0
  59. package/artifacts/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_Engine_PreV3p2.sol/IGenArt721CoreContractV3_Engine_PreV3p2.json +741 -0
  60. package/artifacts/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_ProjectFinance.sol/IGenArt721CoreContractV3_ProjectFinance.json +87 -0
  61. package/artifacts/contracts/interfaces/v0.8.x/IGenArt721Minter_PBAB.sol/IGenArt721Minter_PBAB.json +279 -0
  62. package/artifacts/contracts/interfaces/v0.8.x/IMinterFilterV1.sol/IMinterFilterV1.json +22 -22
  63. package/artifacts/contracts/interfaces/v0.8.x/IPseudorandomAtomic.sol/IPseudorandomAtomic.json +1 -1
  64. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterDAExpSettlementV0.sol/ISharedMinterDAExpSettlementV0.json +26 -101
  65. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterDAExpV0.sol/ISharedMinterDAExpV0.json +1 -57
  66. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterDALinV0.sol/ISharedMinterDALinV0.json +1 -57
  67. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterDAV0.sol/ISharedMinterDAV0.json +2 -21
  68. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterHolderERC20V0.sol/ISharedMinterHolderERC20V0.json +158 -0
  69. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterHolderV0.sol/ISharedMinterHolderV0.json +15 -90
  70. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterMerkleV0.sol/ISharedMinterMerkleV0.json +10 -36
  71. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterRAMV0.sol/ISharedMinterRAMV0.json +62 -0
  72. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterRequired.sol/ISharedMinterRequired.json +37 -0
  73. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterSEAV0.sol/ISharedMinterSEAV0.json +4 -236
  74. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterSimplePurchaseV0.sol/ISharedMinterSimplePurchaseV0.json +5 -5
  75. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterV0.sol/ISharedMinterV0.json +7 -423
  76. package/artifacts/contracts/interfaces/v0.8.x/ISharedRandomizerV0.sol/ISharedRandomizerV0.json +17 -17
  77. package/artifacts/contracts/interfaces/v0.8.x/ISplitAtomicFactoryV0.sol/ISplitAtomicFactoryV0.json +170 -0
  78. package/artifacts/contracts/interfaces/v0.8.x/ISplitAtomicV0.sol/ISplitAtomicV0.json +126 -0
  79. package/artifacts/contracts/interfaces/v0.8.x/ISplitProviderV0.sol/ISplitProviderV0.json +98 -0
  80. package/artifacts/contracts/interfaces/v0.8.x/integration-refs/splits-0x-v2/ISplitFactoryV2.sol/ISplitFactoryV2.json +123 -0
  81. package/artifacts/contracts/libs/integration-refs/delegation-registry/DelegationRegistry.sol/DelegationRegistry.json +2 -2
  82. package/artifacts/contracts/libs/integration-refs/gnosis-safe/GnosisSafeMock.sol/GnosisSafeMock.json +2 -2
  83. package/artifacts/contracts/libs/integration-refs/gnosis-safe/GnosisSafeProxyFactoryMock.sol/GnosisSafeProxyFactoryMock.json +2 -2
  84. package/artifacts/contracts/libs/integration-refs/gnosis-safe/MultiSendMock.sol/MultiSendMock.json +2 -2
  85. package/artifacts/contracts/libs/integration-refs/weth9/weth9.sol/WETH9_.json +2 -2
  86. package/artifacts/contracts/libs/v0.8.x/ABHelpers.sol/ABHelpers.json +2 -2
  87. package/artifacts/contracts/libs/v0.8.x/AuthLib.sol/AuthLib.json +2 -2
  88. package/artifacts/contracts/libs/v0.8.x/BitMap.sol/BitMaps256.json +10 -0
  89. package/artifacts/contracts/libs/v0.8.x/BytecodeStorageV0.sol/BytecodeStorage.json +2 -2
  90. package/artifacts/contracts/libs/v0.8.x/BytecodeStorageV1.sol/BytecodeStorageReader.json +2 -2
  91. package/artifacts/contracts/libs/v0.8.x/BytecodeStorageV1.sol/BytecodeStorageWriter.json +2 -2
  92. package/artifacts/contracts/libs/v0.8.x/BytecodeStorageV2.sol/BytecodeStorageReader.json +214 -0
  93. package/artifacts/contracts/libs/v0.8.x/BytecodeStorageV2.sol/BytecodeStorageWriter.json +10 -0
  94. package/artifacts/contracts/libs/v0.8.x/Bytes32Strings.sol/Bytes32Strings.json +2 -2
  95. package/artifacts/contracts/libs/v0.8.x/DependencyRegistryStorageLib.sol/DependencyRegistryStorageLib.json +10 -0
  96. package/artifacts/contracts/libs/v0.8.x/ERC721_PackedHashSeed.sol/ERC721_PackedHashSeed.json +2 -2
  97. package/artifacts/contracts/libs/v0.8.x/ERC721_PackedHashSeedV1.sol/ERC721_PackedHashSeedV1.json +444 -0
  98. package/artifacts/contracts/libs/v0.8.x/PackedBools.sol/PackedBools.json +10 -0
  99. package/artifacts/contracts/libs/v0.8.x/SSTORE2.sol/SSTORE2.json +2 -2
  100. package/artifacts/contracts/libs/v0.8.x/V3FlexLib.sol/V3FlexLib.json +208 -0
  101. package/artifacts/contracts/libs/v0.8.x/minter-libs/DAExpLib.sol/DAExpLib.json +60 -3
  102. package/artifacts/contracts/libs/v0.8.x/minter-libs/DALib.sol/DALib.json +30 -0
  103. package/artifacts/contracts/libs/v0.8.x/minter-libs/DALinLib.sol/DALinLib.json +60 -3
  104. package/artifacts/contracts/libs/v0.8.x/minter-libs/GenericMinterEventsLib.sol/GenericMinterEventsLib.json +346 -0
  105. package/artifacts/contracts/libs/v0.8.x/minter-libs/MaxInvocationsLib.sol/MaxInvocationsLib.json +29 -3
  106. package/artifacts/contracts/libs/v0.8.x/minter-libs/MerkleLib.sol/MerkleLib.json +30 -3
  107. package/artifacts/contracts/libs/v0.8.x/minter-libs/PolyptychLib.sol/PolyptychLib.json +2 -2
  108. package/artifacts/contracts/libs/v0.8.x/minter-libs/RAMLib.sol/RAMLib.json +398 -0
  109. package/artifacts/contracts/libs/v0.8.x/minter-libs/SEALib.sol/SEALib.json +254 -3
  110. package/artifacts/contracts/libs/v0.8.x/minter-libs/SetPriceLib.sol/SetPriceLib.json +48 -3
  111. package/artifacts/contracts/libs/v0.8.x/minter-libs/SettlementExpLib.sol/SettlementExpLib.json +41 -3
  112. package/artifacts/contracts/libs/v0.8.x/minter-libs/SplitFundsLib.sol/SplitFundsLib.json +35 -3
  113. package/artifacts/contracts/libs/v0.8.x/minter-libs/TokenHolderLib.sol/TokenHolderLib.json +79 -3
  114. package/artifacts/contracts/minter-suite/MinterFilter/MinterFilterV1.sol/MinterFilterV1.json +2 -2
  115. package/artifacts/contracts/minter-suite/MinterFilter/MinterFilterV2.sol/MinterFilterV2.json +41 -41
  116. package/artifacts/contracts/minter-suite/Minters/MinterDAExpHolderV5.sol/MinterDAExpHolderV5.json +66 -632
  117. package/artifacts/contracts/minter-suite/Minters/MinterDAExpSettlementV3.sol/MinterDAExpSettlementV3.json +67 -594
  118. package/artifacts/contracts/minter-suite/Minters/MinterDAExpV5.sol/MinterDAExpV5.json +33 -524
  119. package/artifacts/contracts/minter-suite/Minters/MinterDALinHolderV5.sol/MinterDALinHolderV5.json +66 -632
  120. package/artifacts/contracts/minter-suite/Minters/MinterDALinV5.sol/MinterDALinV5.json +33 -524
  121. package/artifacts/contracts/minter-suite/Minters/MinterRAMV0.sol/MinterRAMV0.json +1374 -0
  122. package/artifacts/contracts/minter-suite/Minters/MinterSEAV1.sol/MinterSEAV1.json +56 -722
  123. package/artifacts/contracts/minter-suite/Minters/MinterSetPriceERC20V5.sol/MinterSetPriceERC20V5.json +58 -454
  124. package/artifacts/contracts/minter-suite/Minters/MinterSetPriceHolderV5.sol/MinterSetPriceHolderV5.json +61 -552
  125. package/artifacts/contracts/minter-suite/Minters/MinterSetPriceMerkleV5.sol/MinterSetPriceMerkleV5.json +55 -497
  126. package/artifacts/contracts/minter-suite/Minters/MinterSetPricePolyptychERC20V5.sol/MinterSetPricePolyptychERC20V5.json +110 -571
  127. package/artifacts/contracts/minter-suite/Minters/MinterSetPricePolyptychV5.sol/MinterSetPricePolyptychV5.json +69 -560
  128. package/artifacts/contracts/minter-suite/Minters/MinterSetPriceV5.sol/MinterSetPriceV5.json +28 -444
  129. package/artifacts/contracts/minter-suite/Minters/one-off/MinterSetPriceV5_SingleCore.sol/MinterSetPriceV5_SingleCore.json +417 -0
  130. package/artifacts/contracts/mock/BytecodeV2LibCallsMock.sol/BytecodeV2LibCallsMock.json +154 -0
  131. package/artifacts/contracts/mock/BytecodeV2TextCR_DMock.sol/BytecodeV2TextCR_DMock.json +428 -0
  132. package/artifacts/contracts/mock/DummySharedMinter.sol/DummySharedMinter.json +2 -2
  133. package/artifacts/contracts/mock/ERC20Mock.sol/ERC20Mock.json +2 -2
  134. package/artifacts/contracts/mock/ForceSendMock.sol/ForceSendMock.json +24 -0
  135. package/artifacts/contracts/mock/GenArt721CoreV3_Engine_IncorrectCoreType.sol/GenArt721CoreV3_Engine_IncorrectCoreType.json +4 -9
  136. package/artifacts/contracts/mock/Mock0xSplitsV2PullFactory.sol/Mock0xSplitsV2PullFactory.json +149 -0
  137. package/artifacts/contracts/mock/Mock0xSplitsV2Splitter.sol/Mock0xSplitsV2Splitter.json +99 -0
  138. package/artifacts/contracts/mock/MockAdminACLV0.sol/MockAdminACLV0Events.json +20 -2
  139. package/artifacts/contracts/mock/MockAdminACLV0_PROHIBITION.sol/MockAdminACLV0Events_PROHIBITION.json +20 -2
  140. package/artifacts/contracts/mock/OriginRegisterMock.sol/OriginRegisterMock.json +2 -2
  141. package/artifacts/contracts/mock/RandomizerV2_NoAssignMock.sol/RandomizerV2_NoAssignMock.json +2 -2
  142. package/artifacts/contracts/mock/ReentrancyGenArt721MinterMock.sol/ReentrancyGenArt721MinterMock.json +100 -0
  143. package/artifacts/contracts/mock/ReentrancyHolderMock.sol/ReentrancyHolderMock.json +2 -2
  144. package/artifacts/contracts/mock/ReentrancyHolderMockShared.sol/ReentrancyHolderMockShared.json +136 -0
  145. package/artifacts/contracts/mock/ReentrancyMerkleMock.sol/ReentrancyMerkleMock.json +2 -2
  146. package/artifacts/contracts/mock/ReentrancyMerkleMockShared.sol/ReentrancyMerkleMockShared.json +105 -0
  147. package/artifacts/contracts/mock/ReentrancyMock.sol/ReentrancyMock.json +2 -2
  148. package/artifacts/contracts/mock/ReentrancyMockShared.sol/ReentrancyMockShared.json +100 -0
  149. package/artifacts/contracts/mock/ReentrancySEAMock.sol/ReentrancySEAAutoBidderMock.json +2 -2
  150. package/artifacts/contracts/mock/ReentrancySEAMockShared.sol/ReentrancySEAAutoBidderMockShared.json +69 -0
  151. package/artifacts/contracts/mock/ReentrancySplitterMocks.sol/ReentrancySplitterDrainETHMock.json +54 -0
  152. package/artifacts/contracts/mock/ReentrancySplitterMocks.sol/ReentrancySplitterSendETHMock.json +54 -0
  153. package/artifacts/contracts/randomizer/deprecated/PolyptychRandomizerV0.sol/BasicPolyptychRandomizerV0.json +2 -2
  154. package/artifacts/contracts/randomizer/deprecated/RandomizerV2.sol/BasicRandomizerV2.json +2 -2
  155. package/artifacts/contracts/randomizer/primitive/PseudorandomAtomicBasic.sol/PseudorandomAtomic.json +2 -2
  156. package/artifacts/contracts/randomizer/shared/SharedRandomizerV0.sol/SharedRandomizerV0.json +19 -19
  157. package/artifacts/contracts/split/SplitAtomicFactoryV0.sol/SplitAtomicFactoryV0.json +203 -0
  158. package/artifacts/contracts/split/SplitAtomicV0.sol/SplitAtomicV0.json +168 -0
  159. package/artifacts/contracts/split/split-provider/SplitProviderV0.sol/SplitProviderV0.json +141 -0
  160. package/contracts/AdminACLV0.sol +6 -2
  161. package/contracts/AdminACLV1.sol +2 -2
  162. package/contracts/DependencyRegistryV0.sol +737 -390
  163. package/contracts/{GenArt721CoreV3.sol → archive/GenArt721CoreV3.sol} +56 -20
  164. package/contracts/{explorations → archive}/GenArt721CoreV3_Explorations.sol +49 -13
  165. package/contracts/{minter-suite → archive/minter-suite}/Minters/MinterBase_v0_1_1.sol +4 -4
  166. package/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterDAExp}/MinterDAExpV4.sol +4 -4
  167. package/contracts/archive/minter-suite/Minters/MinterDAExpSettlement/MinterDAExpSettlementV0.sol +4 -4
  168. package/contracts/archive/minter-suite/Minters/MinterDAExpSettlement/MinterDAExpSettlementV1.sol +5 -5
  169. package/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterDAExpSettlement}/MinterDAExpSettlementV2.sol +9 -62
  170. package/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterDALin}/MinterDALinV4.sol +4 -4
  171. package/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterHolder}/MinterHolderV4.sol +5 -5
  172. package/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterMerkle}/MinterMerkleV5.sol +5 -5
  173. package/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterPolyptych}/MinterPolyptychV0.sol +7 -7
  174. package/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterSEA}/MinterSEAV0.sol +13 -13
  175. package/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterSetPrice}/MinterSetPriceV4.sol +4 -4
  176. package/contracts/archive/minter-suite/Minters/MinterSetPriceERC20/MinterSetPriceERC20V0.sol +82 -10
  177. package/contracts/archive/minter-suite/Minters/MinterSetPriceERC20/MinterSetPriceERC20V1.sol +82 -10
  178. package/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterSetPriceERC20}/MinterSetPriceERC20V4.sol +88 -21
  179. package/contracts/engine/V2/GenArt721MinterBurner_PBAB.sol +81 -22
  180. package/contracts/engine/V2/GenArt721Minter_PBAB.sol +80 -22
  181. package/contracts/engine/V3/EngineFactoryV0.sol +419 -0
  182. package/contracts/engine/V3/GenArt721CoreV3_Engine.sol +893 -493
  183. package/contracts/engine/V3/GenArt721CoreV3_Engine_Flex.sol +1053 -655
  184. package/contracts/engine/V3/forks/GenArt721CoreV3_Engine_Flex_PROOF.sol +7 -13
  185. package/contracts/engine/V3/forks/PROHIBITION/GenArt721CoreV3_Engine_Flex_PROHIBITION.sol +5 -11
  186. package/contracts/engine/V3/forks/PROHIBITION/interfaces/IGenArt721CoreContractV3_Engine_Flex_PROHIBITION.sol +2 -2
  187. package/contracts/engine-registry/CoreRegistryV1.sol +58 -65
  188. package/contracts/interfaces/v0.8.x/IAdminACLV0_Extended.sol +20 -0
  189. package/contracts/interfaces/v0.8.x/IBytecodeStorageReaderV2.sol +23 -0
  190. package/contracts/interfaces/v0.8.x/ICoreRegistryV1.sol +6 -8
  191. package/contracts/interfaces/v0.8.x/IDependencyRegistryV0.sol +48 -43
  192. package/contracts/interfaces/v0.8.x/IEngineFactoryV0.sol +124 -0
  193. package/contracts/interfaces/v0.8.x/IFilteredMinterDAExpSettlementV0.sol +1 -3
  194. package/contracts/interfaces/v0.8.x/IFilteredMinterDAExpSettlementV1.sol +1 -3
  195. package/contracts/interfaces/v0.8.x/IFilteredMinterDAExpV1.sol +1 -3
  196. package/contracts/interfaces/v0.8.x/IFilteredMinterDALinV1.sol +1 -3
  197. package/contracts/interfaces/v0.8.x/IFilteredMinterHolderV2.sol +1 -3
  198. package/contracts/interfaces/v0.8.x/IFilteredMinterMerkleV2.sol +1 -3
  199. package/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_Base.sol +115 -11
  200. package/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_Engine.sol +84 -8
  201. package/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_Engine_Flex.sol +26 -8
  202. package/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_Engine_Flex_PreV3p2.sol +141 -0
  203. package/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_Engine_PreV3p2.sol +78 -0
  204. package/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_ProjectFinance.sol +47 -0
  205. package/contracts/interfaces/v0.8.x/IGenArt721Minter_PBAB.sol +91 -0
  206. package/contracts/interfaces/v0.8.x/IMinterFilterV1.sol +25 -25
  207. package/contracts/interfaces/v0.8.x/IPseudorandomAtomic.sol +1 -3
  208. package/contracts/interfaces/v0.8.x/ISharedMinterDAExpSettlementV0.sol +30 -20
  209. package/contracts/interfaces/v0.8.x/ISharedMinterDAExpV0.sol +1 -14
  210. package/contracts/interfaces/v0.8.x/ISharedMinterDALinV0.sol +1 -15
  211. package/contracts/interfaces/v0.8.x/ISharedMinterDAV0.sol +2 -8
  212. package/contracts/interfaces/v0.8.x/ISharedMinterHolderERC20V0.sol +47 -0
  213. package/contracts/interfaces/v0.8.x/ISharedMinterHolderV0.sol +15 -43
  214. package/contracts/interfaces/v0.8.x/ISharedMinterMerkleV0.sol +10 -20
  215. package/contracts/interfaces/v0.8.x/ISharedMinterRAMV0.sol +23 -0
  216. package/contracts/interfaces/v0.8.x/ISharedMinterRequired.sol +20 -0
  217. package/contracts/interfaces/v0.8.x/ISharedMinterSEAV0.sol +1 -67
  218. package/contracts/interfaces/v0.8.x/ISharedMinterSimplePurchaseV0.sol +5 -5
  219. package/contracts/interfaces/v0.8.x/ISharedMinterV0.sol +18 -160
  220. package/contracts/interfaces/v0.8.x/ISharedRandomizerV0.sol +37 -37
  221. package/contracts/interfaces/v0.8.x/ISplitAtomicFactoryV0.sol +81 -0
  222. package/contracts/interfaces/v0.8.x/ISplitAtomicV0.sol +72 -0
  223. package/contracts/interfaces/v0.8.x/ISplitProviderV0.sol +49 -0
  224. package/contracts/interfaces/v0.8.x/integration-refs/splits-0x-v2/ISplitFactoryV2.sol +62 -0
  225. package/contracts/libs/v0.8.x/ABHelpers.sol +31 -17
  226. package/contracts/libs/v0.8.x/AuthLib.sol +101 -102
  227. package/contracts/libs/v0.8.x/BitMap.sol +112 -0
  228. package/contracts/libs/v0.8.x/BytecodeStorageV2.sol +593 -0
  229. package/contracts/libs/v0.8.x/Bytes32Strings.sol +5 -5
  230. package/contracts/libs/v0.8.x/DependencyRegistryStorageLib.sol +90 -0
  231. package/contracts/libs/v0.8.x/ERC721_PackedHashSeedV1.sol +591 -0
  232. package/contracts/libs/v0.8.x/PackedBools.sol +42 -0
  233. package/contracts/libs/v0.8.x/V3FlexLib.sol +591 -0
  234. package/contracts/libs/v0.8.x/minter-libs/DAExpLib.sol +162 -39
  235. package/contracts/libs/v0.8.x/minter-libs/DALib.sol +26 -0
  236. package/contracts/libs/v0.8.x/minter-libs/DALinLib.sol +139 -37
  237. package/contracts/libs/v0.8.x/minter-libs/GenericMinterEventsLib.sol +171 -0
  238. package/contracts/libs/v0.8.x/minter-libs/MaxInvocationsLib.sol +299 -158
  239. package/contracts/libs/v0.8.x/minter-libs/MerkleLib.sol +268 -55
  240. package/contracts/libs/v0.8.x/minter-libs/PolyptychLib.sol +138 -58
  241. package/contracts/libs/v0.8.x/minter-libs/RAMLib.sol +2927 -0
  242. package/contracts/libs/v0.8.x/minter-libs/SEALib.sol +796 -123
  243. package/contracts/libs/v0.8.x/minter-libs/SetPriceLib.sol +131 -13
  244. package/contracts/libs/v0.8.x/minter-libs/SettlementExpLib.sol +607 -224
  245. package/contracts/libs/v0.8.x/minter-libs/SplitFundsLib.sol +494 -330
  246. package/contracts/libs/v0.8.x/minter-libs/TokenHolderLib.sol +217 -78
  247. package/contracts/minter-suite/MinterFilter/MinterFilterV2.sol +358 -309
  248. package/contracts/minter-suite/Minters/MinterDAExpHolderV5.sol +391 -491
  249. package/contracts/minter-suite/Minters/MinterDAExpSettlementV3.sol +396 -632
  250. package/contracts/minter-suite/Minters/MinterDAExpV5.sol +212 -273
  251. package/contracts/minter-suite/Minters/MinterDALinHolderV5.sol +393 -488
  252. package/contracts/minter-suite/Minters/MinterDALinV5.sol +220 -277
  253. package/contracts/minter-suite/Minters/MinterRAMV0.sol +1205 -0
  254. package/contracts/minter-suite/Minters/MinterSEAV1.sol +329 -677
  255. package/contracts/minter-suite/Minters/MinterSetPriceERC20V5.sol +287 -303
  256. package/contracts/minter-suite/Minters/MinterSetPriceHolderV5.sol +355 -446
  257. package/contracts/minter-suite/Minters/MinterSetPriceMerkleV5.sol +328 -416
  258. package/contracts/minter-suite/Minters/MinterSetPricePolyptychERC20V5.sol +552 -622
  259. package/contracts/minter-suite/Minters/MinterSetPricePolyptychV5.sol +429 -553
  260. package/contracts/minter-suite/Minters/MinterSetPriceV5.sol +180 -227
  261. package/contracts/minter-suite/Minters/one-off/MinterSetPriceV5_SingleCore.sol +446 -0
  262. package/contracts/mock/BytecodeV2LibCallsMock.sol +128 -0
  263. package/contracts/mock/BytecodeV2TextCR_DMock.sol +316 -0
  264. package/contracts/mock/ForceSendMock.sol +22 -0
  265. package/contracts/mock/GenArt721CoreV3_Engine_IncorrectCoreType.sol +3 -10
  266. package/contracts/mock/Mock0xSplitsV2PullFactory.sol +75 -0
  267. package/contracts/mock/Mock0xSplitsV2Splitter.sol +47 -0
  268. package/contracts/mock/MockAdminACLV0.sol +21 -0
  269. package/contracts/mock/MockAdminACLV0_PROHIBITION.sol +21 -0
  270. package/contracts/mock/ReentrancyGenArt721MinterMock.sol +47 -0
  271. package/contracts/mock/ReentrancyHolderMockShared.sol +59 -0
  272. package/contracts/mock/ReentrancyMerkleMockShared.sol +58 -0
  273. package/contracts/mock/ReentrancyMockShared.sol +48 -0
  274. package/contracts/mock/ReentrancySEAMockShared.sol +40 -0
  275. package/contracts/mock/ReentrancySplitterMocks.sol +67 -0
  276. package/contracts/randomizer/primitive/PseudorandomAtomicBasic.sol +1 -1
  277. package/contracts/randomizer/shared/SharedRandomizerV0.sol +79 -71
  278. package/contracts/split/SplitAtomicFactoryV0.sol +121 -0
  279. package/contracts/split/SplitAtomicV0.sol +265 -0
  280. package/contracts/split/split-provider/SplitProviderV0.sol +201 -0
  281. package/package.json +79 -45
  282. package/artifacts/contracts/GenArt721CoreV3.sol/GenArt721CoreV3.json +0 -1965
  283. package/artifacts/contracts/explorations/GenArt721CoreV3_Explorations.sol/GenArt721CoreV3_Explorations.json +0 -1939
  284. package/artifacts/contracts/minter-suite/Minters/MinterDAExpSettlementV2.sol/MinterDAExpSettlementV2.json +0 -1127
  285. package/artifacts/contracts/minter-suite/Minters/MinterSetPriceERC20V4.sol/MinterSetPriceERC20V4.json +0 -768
  286. package/artifacts/contracts/royalty-registry/GenArt721RoyaltyOverride.sol/GenArt721RoyaltyOverride.json +0 -208
  287. package/artifacts/contracts/royalty-registry/GenArt721RoyaltyOverride_PBAB.sol/GenArt721RoyaltyOverride_PBAB.json +0 -301
  288. package/contracts/royalty-registry/GenArt721RoyaltyOverride.sol +0 -168
  289. package/contracts/royalty-registry/GenArt721RoyaltyOverride_PBAB.sol +0 -221
@@ -0,0 +1,2927 @@
1
+ // SPDX-License-Identifier: LGPL-3.0-only
2
+ // Created By: Art Blocks Inc.
3
+
4
+ pragma solidity ^0.8.0;
5
+
6
+ import {IMinterFilterV1} from "../../../interfaces/v0.8.x/IMinterFilterV1.sol";
7
+
8
+ import {BitMaps256} from "../BitMap.sol";
9
+ import {PackedBools} from "../PackedBools.sol";
10
+ import {ABHelpers} from "../ABHelpers.sol";
11
+ import {SplitFundsLib} from "./SplitFundsLib.sol";
12
+ import {MaxInvocationsLib} from "./MaxInvocationsLib.sol";
13
+ import {GenericMinterEventsLib} from "./GenericMinterEventsLib.sol";
14
+
15
+ import {IERC721} from "@openzeppelin-5.0/contracts/token/ERC721/IERC721.sol";
16
+ import {SafeCast} from "@openzeppelin-5.0/contracts/utils/math/SafeCast.sol";
17
+ import {Math} from "@openzeppelin-5.0/contracts/utils/math/Math.sol";
18
+
19
+ /**
20
+ * @title Art Blocks Ranked Auction Minter (RAM) Library
21
+ * @notice This library is designed for the Art Blocks platform. It includes
22
+ * Structs and functions that help with ranked auction minters.
23
+ * @author Art Blocks Inc.
24
+ */
25
+ library RAMLib {
26
+ using SafeCast for uint256;
27
+ using BitMaps256 for uint256;
28
+ using PackedBools for uint256;
29
+ /**
30
+ * @notice Minimum auction length, in seconds, was updated to be the
31
+ * provided value.
32
+ * @param minAuctionDurationSeconds Minimum auction length, in seconds
33
+ */
34
+ event MinAuctionDurationSecondsUpdated(uint256 minAuctionDurationSeconds);
35
+
36
+ /**
37
+ * @notice Admin-controlled refund gas limit updated
38
+ * @param refundGasLimit Gas limit to use when refunding the previous
39
+ * highest bidder, prior to using fallback force-send to refund
40
+ */
41
+ event MinterRefundGasLimitUpdated(uint24 refundGasLimit);
42
+
43
+ /**
44
+ * @notice Number of slots used by this RAM minter
45
+ * @param numSlots Number of slots used by this RAM minter
46
+ */
47
+ event NumSlotsUpdated(uint256 numSlots);
48
+
49
+ /**
50
+ * @notice RAM auction buffer time parameters updated
51
+ * @param auctionBufferSeconds time period at end of auction when new bids
52
+ * can affect auction end time, updated to be this many seconds after the
53
+ * bid is placed.
54
+ * @param maxAuctionExtraSeconds maximum amount of time that can be added
55
+ * to the auction end time due to new bids.
56
+ */
57
+ event AuctionBufferTimeParamsUpdated(
58
+ uint256 auctionBufferSeconds,
59
+ uint256 maxAuctionExtraSeconds
60
+ );
61
+
62
+ /**
63
+ * @notice Admin minting constraint configuration updated
64
+ * @param coreContract Core contract address to update
65
+ * @param adminMintingConstraint enum representing admin minting constraint imposed on this contract
66
+ */
67
+ event ContractConfigUpdated(
68
+ address indexed coreContract,
69
+ AdminMintingConstraint adminMintingConstraint
70
+ );
71
+
72
+ /**
73
+ * @notice Auction parameters updated
74
+ * @param projectId Project Id to update
75
+ * @param coreContract Core contract address to update
76
+ * @param timestampStart Auction start timestamp
77
+ * @param timestampEnd Auction end timestamp
78
+ * @param basePrice Auction base price
79
+ * @param allowExtraTime Auction allows extra time
80
+ * @param adminArtistOnlyMintPeriodIfSellout Auction admin-artist-only mint period if
81
+ * sellout
82
+ * @param numTokensInAuction Number of tokens in auction
83
+ */
84
+ event AuctionConfigUpdated(
85
+ uint256 indexed projectId,
86
+ address indexed coreContract,
87
+ uint256 timestampStart,
88
+ uint256 timestampEnd,
89
+ uint256 basePrice,
90
+ bool allowExtraTime,
91
+ bool adminArtistOnlyMintPeriodIfSellout,
92
+ uint256 numTokensInAuction
93
+ );
94
+
95
+ /**
96
+ * @notice Number of tokens in auction updated
97
+ * @dev okay to not index this event if prior to AuctionConfigUpdated, as
98
+ * the state value will be emitted in another future event
99
+ * @dev generic event not used due to additional indexing logic desired
100
+ * @param projectId Project Id to update
101
+ * @param coreContract Core contract address to update
102
+ * @param numTokensInAuction Number of tokens in auction
103
+ */
104
+ event NumTokensInAuctionUpdated(
105
+ uint256 indexed projectId,
106
+ address indexed coreContract,
107
+ uint256 numTokensInAuction
108
+ );
109
+
110
+ /**
111
+ * @notice Auction timestamp end updated. Occurs when auction is extended
112
+ * due to new bids near the end of an auction, when the auction is
113
+ * configured to allow extra time.
114
+ * Also may occur when an admin extends the auction within the emergency
115
+ * extension time limit.
116
+ * @dev generic event not used due to additional indexing logic desired
117
+ * when event is encountered (want to understand what caused time
118
+ * extension)
119
+ * @param projectId Project Id to update
120
+ * @param coreContract Core contract address to update
121
+ * @param timestampEnd Auction end timestamp
122
+ */
123
+ event AuctionTimestampEndUpdated(
124
+ uint256 indexed projectId,
125
+ address indexed coreContract,
126
+ uint256 timestampEnd
127
+ );
128
+
129
+ /**
130
+ * @notice Bid created in auction
131
+ * @param projectId Project Id to update
132
+ * @param coreContract Core contract address to update
133
+ * @param slotIndex Slot index of bid that was created
134
+ * @param bidId Bid Id that was created
135
+ * @param bidder Address of bidder
136
+ */
137
+ event BidCreated(
138
+ uint256 indexed projectId,
139
+ address indexed coreContract,
140
+ uint256 slotIndex,
141
+ uint256 bidId,
142
+ address bidder
143
+ );
144
+
145
+ /**
146
+ * @notice Bid removed from auction because it was outbid.
147
+ * @param projectId Project Id to update
148
+ * @param coreContract Core contract address to update
149
+ * @param bidId Bid Id that was removed
150
+ */
151
+ event BidRemoved(
152
+ uint256 indexed projectId,
153
+ address indexed coreContract,
154
+ uint256 bidId
155
+ );
156
+
157
+ /**
158
+ * @notice Bid topped up in auction
159
+ * @param projectId Project Id to update
160
+ * @param coreContract Core contract address to update
161
+ * @param bidId Bid Id that was topped up
162
+ * @param newSlotIndex New slot index of bid that was topped up
163
+ */
164
+ event BidToppedUp(
165
+ uint256 indexed projectId,
166
+ address indexed coreContract,
167
+ uint256 bidId,
168
+ uint256 newSlotIndex
169
+ );
170
+
171
+ /**
172
+ * @notice Bid was settled, and any payment above the lowest winning bid,
173
+ * or base price if not a sellout, was refunded to the bidder.
174
+ * @param projectId Project Id to update
175
+ * @param coreContract Core contract address to update
176
+ * @param bidId ID of bid that was settled
177
+ */
178
+ event BidSettled(
179
+ uint256 indexed projectId,
180
+ address indexed coreContract,
181
+ uint256 bidId
182
+ );
183
+
184
+ /**
185
+ * @notice A token was minted to the bidder for bid `bidId`. The tokenId is
186
+ * the token that was minted.
187
+ * @param projectId Project Id to update
188
+ * @param coreContract Core contract address to update
189
+ * @param bidId ID of bid that was settled
190
+ * @param tokenId Token Id that was minted
191
+ *
192
+ */
193
+ event BidMinted(
194
+ uint256 indexed projectId,
195
+ address indexed coreContract,
196
+ uint256 bidId,
197
+ uint256 tokenId
198
+ );
199
+
200
+ /**
201
+ * @notice Bid was refunded, and the entire bid value was sent to the
202
+ * bidder.
203
+ * This only occurs if the minter encountered an unexpected error state
204
+ * due to operational issues, and the minter was unable to mint a token to
205
+ * the bidder.
206
+ * @param projectId Project Id to update
207
+ * @param coreContract Core contract address to update
208
+ * @param bidId ID of bid that was settled
209
+ */
210
+ event BidRefunded(
211
+ uint256 indexed projectId,
212
+ address indexed coreContract,
213
+ uint256 bidId
214
+ );
215
+
216
+ /**
217
+ * @notice Token was directly purchased after an auction ended, and the
218
+ * token was minted to the buyer.
219
+ * @param projectId Project Id to update
220
+ * @param coreContract Core contract address to update
221
+ * @param tokenId Token Id that was minted
222
+ * @param to Address that the token was minted to
223
+ */
224
+ event TokenPurchased(
225
+ uint256 indexed projectId,
226
+ address indexed coreContract,
227
+ uint256 tokenId,
228
+ address to
229
+ );
230
+
231
+ // position of RAM Lib storage, using a diamond storage pattern
232
+ // for this library
233
+ bytes32 constant RAM_LIB_STORAGE_POSITION = keccak256("ramlib.storage");
234
+
235
+ // generic event key constants
236
+ bytes32 internal constant CONFIG_AUCTION_REVENUES_COLLECTED =
237
+ "auctionRevenuesCollected";
238
+ bytes32 internal constant CONFIG_TIMESTAMP_END = "timestampEnd";
239
+
240
+ uint256 constant NUM_SLOTS = 512;
241
+
242
+ // pricing assumes maxPrice = minPrice * 2^8, pseudo-exponential curve
243
+ uint256 constant SLOTS_PER_PRICE_DOUBLE = NUM_SLOTS / 8; // 64 slots per double
244
+
245
+ // auction extension time constants
246
+ uint256 constant AUCTION_BUFFER_SECONDS = 5 minutes;
247
+ uint256 constant MAX_AUCTION_EXTRA_SECONDS = 1 hours;
248
+ // @dev store value in hours to improve storage packing
249
+ uint256 constant MAX_AUCTION_ADMIN_EMERGENCY_EXTENSION_HOURS = 72; // 72 hours
250
+
251
+ uint256 constant ADMIN_ARTIST_ONLY_MINT_TIME_SECONDS = 72 hours;
252
+
253
+ // packed bools constants for Bid struct
254
+ uint8 constant INDEX_IS_SETTLED = 0;
255
+ uint8 constant INDEX_IS_MINTED = 1;
256
+ uint8 constant INDEX_IS_REFUNDED = 2;
257
+
258
+ enum ProjectMinterStates {
259
+ PreAuction, // Pre-Auction, State A
260
+ LiveAuction, // Live-Auction, State B
261
+ PostAuctionSellOutAdminArtistMint, // Post-Auction, sell out, not all bids handled, admin-artist-only mint period, State C
262
+ PostAuctionOpenMint, // Post-Auction, not all bids handled, post-admin-artist-only mint period, State D
263
+ PostAuctionAllBidsHandled // Post-Auction, all bids handled, State E
264
+ }
265
+
266
+ // project-specific parameters
267
+ struct RAMProjectConfig {
268
+ // mapping of all bids by Bid ID
269
+ mapping(uint256 bidId => Bid) bids;
270
+ // doubly linked list of bids for each slot
271
+ mapping(uint256 slot => uint256 headBidId) headBidIdBySlot;
272
+ mapping(uint256 slot => uint256 tailBidId) tailBidIdBySlot;
273
+ // --- slot metadata for efficiency ---
274
+ // two bitmaps with index set only if one or more active bids exist for
275
+ // the corresponding slot. The first bitmap (A) is for slots 0-255, the
276
+ // second bitmap (B) is for slots 256-511.
277
+ uint256 slotsBitmapA;
278
+ uint256 slotsBitmapB;
279
+ // minimum bitmap index with an active bid
280
+ // @dev set to 512 if no active bids
281
+ // @dev max uint16 >> max possible value of 512
282
+ uint16 minBidSlotIndex;
283
+ // maximum bitmap index with an active bid
284
+ // @dev set to 0 if no active bids
285
+ uint16 maxBidSlotIndex;
286
+ // --- bid auto-minting tracking ---
287
+ uint32 latestMintedBidId;
288
+ // --- error state bid auto-refund tracking ---
289
+ uint32 latestRefundedBidId;
290
+ // --- next bid ID ---
291
+ // nonce for generating new bid IDs on this project
292
+ // @dev allows for gt 4 billion bids, and max possible bids for a
293
+ // 1M token project is 1M * 512 slots = 512M bids < 4B max uint32
294
+ uint32 nextBidId;
295
+ // --- auction parameters ---
296
+ // number of tokens and related values
297
+ // @dev max uint24 is 16,777,215 > 1_000_000 max project size
298
+ uint24 numTokensInAuction;
299
+ uint24 numBids;
300
+ uint24 numBidsMintedTokens;
301
+ uint24 numBidsErrorRefunded;
302
+ // timing
303
+ // @dev max uint40 ~= 1.1e12 sec ~= 34 thousand years
304
+ uint40 timestampStart;
305
+ // @dev timestampOriginalEnd & timestampEnd are the same if not in extra time
306
+ uint40 timestampOriginalEnd;
307
+ uint40 timestampEnd;
308
+ // @dev max uint8 ~= 256 hours, which is gt max auction extension time of 72 hours
309
+ uint8 adminEmergencyExtensionHoursApplied;
310
+ bool allowExtraTime;
311
+ bool adminArtistOnlyMintPeriodIfSellout;
312
+ // pricing
313
+ // @dev max uint88 ~= 3e26 Wei = ~300 million ETH, which is well above
314
+ // the expected prices of any NFT mint in the foreseeable future.
315
+ uint88 basePrice;
316
+ // -- redundant backstops --
317
+ // Track per-project fund balance, in wei. This is used as a redundant
318
+ // backstop to prevent one project from draining the minter's balance
319
+ // of ETH from other projects, which is a worthwhile failsafe on this
320
+ // shared minter.
321
+ // @dev max uint120 > max basPrice * 256 * 1_000_000
322
+ // @dev while uint88 is gt max ETH supply, use uin120 to prevent reliance on
323
+ // max token supply
324
+ uint120 projectBalance;
325
+ // --- revenue collection state ---
326
+ bool revenuesCollected;
327
+ }
328
+
329
+ struct Bid {
330
+ uint32 prevBidId;
331
+ uint32 nextBidId;
332
+ uint16 slotIndex;
333
+ address bidder;
334
+ // three bool values packed into a single uint8
335
+ // index 0 - isSettled (INDEX_IS_SETTLED)
336
+ // index 1 - isMinted (INDEX_IS_MINTED)
337
+ // index 2 - isRefunded (INDEX_IS_REFUNDED)
338
+ uint8 packedBools;
339
+ }
340
+
341
+ // contract-specific parameters
342
+ enum AdminMintingConstraint {
343
+ None,
344
+ AdminArtistOnlyMintPeriod,
345
+ NoAdminArtistOnlyMintPeriod
346
+ }
347
+
348
+ // Diamond storage pattern is used in this library
349
+ struct RAMLibStorage {
350
+ mapping(address coreContract => mapping(uint256 projectId => RAMProjectConfig)) RAMProjectConfigs;
351
+ mapping(address => AdminMintingConstraint) RAMAdminMintingConstraint;
352
+ }
353
+
354
+ /**
355
+ * @notice Update a contract's requirements on if a post-auction
356
+ * admin-artist-only mint period is required or not, for and-on
357
+ * configured projects.
358
+ * @param coreContract The address of the core contract being configured
359
+ * @param adminMintingConstraint The AdminMintingConstraint setting for the contract
360
+ */
361
+ function setContractConfig(
362
+ address coreContract,
363
+ AdminMintingConstraint adminMintingConstraint
364
+ ) internal {
365
+ // set the contract admin minting constraint with the new constraint enum value
366
+ s().RAMAdminMintingConstraint[coreContract] = adminMintingConstraint;
367
+ // emit event
368
+ emit ContractConfigUpdated({
369
+ coreContract: coreContract,
370
+ adminMintingConstraint: adminMintingConstraint
371
+ });
372
+ }
373
+
374
+ /**
375
+ * @notice Function to add emergency auction hours to auction of
376
+ * project `projectId` on core contract `coreContract`.
377
+ * Protects against unexpected frontend downtime, etc.
378
+ * Reverts if called by anyone other than a contract admin.
379
+ * Reverts if project is not in a Live Auction.
380
+ * Reverts if auction is already in extra time.
381
+ * Reverts if adding more than the maximum number of emergency hours.
382
+ * @param projectId Project ID to add emergency auction hours to.
383
+ * @param coreContract Core contract address for the given project.
384
+ * @param emergencyHoursToAdd Number of emergency hours to add to the
385
+ * project's auction.
386
+ */
387
+ function adminAddEmergencyAuctionHours(
388
+ uint256 projectId,
389
+ address coreContract,
390
+ uint8 emergencyHoursToAdd
391
+ ) internal {
392
+ // load project config
393
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
394
+ projectId: projectId,
395
+ coreContract: coreContract
396
+ });
397
+ // CHECKS
398
+ // require auction in state B (Live Auction)
399
+ require(
400
+ getProjectMinterState(projectId, coreContract) ==
401
+ ProjectMinterStates.LiveAuction,
402
+ "Only live auction"
403
+ );
404
+ // require auction has not reached extra time
405
+ require(
406
+ RAMProjectConfig_.timestampOriginalEnd ==
407
+ RAMProjectConfig_.timestampEnd,
408
+ "Not allowed in extra time"
409
+ );
410
+ // require auction is not being extended beyond limit
411
+ uint8 newAdminEmergencyExtensionHours = RAMProjectConfig_
412
+ .adminEmergencyExtensionHoursApplied + emergencyHoursToAdd;
413
+
414
+ require(
415
+ newAdminEmergencyExtensionHours <=
416
+ MAX_AUCTION_ADMIN_EMERGENCY_EXTENSION_HOURS,
417
+ "Only emergency hours lt max"
418
+ );
419
+ // calculate auction end time
420
+ // @dev overflow automatically checked in solidity 0.8
421
+ uint40 newTimestampEnd = RAMProjectConfig_.timestampEnd +
422
+ emergencyHoursToAdd *
423
+ 1 hours;
424
+
425
+ // EFFECTS
426
+ // update emergency hours applied
427
+ // @dev overflow automatically checked in solidity 0.8
428
+ RAMProjectConfig_
429
+ .adminEmergencyExtensionHoursApplied = newAdminEmergencyExtensionHours;
430
+
431
+ // update auction end time
432
+ // @dev update both original end timestamp and current end timestamp
433
+ // because this is not extra time, but rather an emergency extension
434
+ RAMProjectConfig_.timestampEnd = newTimestampEnd;
435
+ RAMProjectConfig_.timestampOriginalEnd = newTimestampEnd;
436
+
437
+ // emit event
438
+ emit AuctionTimestampEndUpdated({
439
+ projectId: projectId,
440
+ coreContract: coreContract,
441
+ timestampEnd: newTimestampEnd
442
+ });
443
+ }
444
+
445
+ /**
446
+ * @notice Function to set auction details on project `projectId` on core contract `coreContract`.
447
+ * Reverts if not currently in ProjectMinterState A.
448
+ * Reverts if base price does not meet the minimum.
449
+ * Reverts if not for future auction.
450
+ * Reverts if end time not greater than start time.
451
+ * Reverts if adminArtistOnlyMintPeriodIfSellout disagrees with the admin configured constraint.
452
+ * @param projectId Project ID to add emergency auction hours to.
453
+ * @param coreContract Core contract address for the given project.
454
+ * @param auctionTimestampStart New timestamp at which to start the auction.
455
+ * @param auctionTimestampEnd New timestamp at which to end the auction.
456
+ * @param basePrice Base price (or reserve price) of the auction, in Wei
457
+ * @param allowExtraTime Auction allows extra time
458
+ * @param adminArtistOnlyMintPeriodIfSellout Auction admin-artist-only mint period if
459
+ * sellout
460
+ */
461
+ function setAuctionDetails(
462
+ uint256 projectId,
463
+ address coreContract,
464
+ uint40 auctionTimestampStart,
465
+ uint40 auctionTimestampEnd,
466
+ uint88 basePrice,
467
+ bool allowExtraTime,
468
+ bool adminArtistOnlyMintPeriodIfSellout
469
+ ) internal {
470
+ // load project config
471
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
472
+ projectId: projectId,
473
+ coreContract: coreContract
474
+ });
475
+ // CHECKS
476
+ // require ProjectMinterState Pre-auction (State A)
477
+ require(
478
+ getProjectMinterState({
479
+ projectId: projectId,
480
+ coreContract: coreContract
481
+ }) == ProjectMinterStates.PreAuction,
482
+ "Only pre-auction"
483
+ );
484
+ // require base price >= 0.05 ETH
485
+ require(basePrice >= 0.05 ether, "Only base price gte 0.05 ETH");
486
+ // only future start time
487
+ require(
488
+ auctionTimestampStart > block.timestamp,
489
+ "Only future auctions"
490
+ );
491
+
492
+ // enforce contract-level constraints set by contract admin
493
+ AdminMintingConstraint RAMAdminMintingConstraint_ = getRAMAdminMintingConstraintValue({
494
+ coreContract: coreContract
495
+ });
496
+
497
+ if (
498
+ RAMAdminMintingConstraint_ ==
499
+ AdminMintingConstraint.AdminArtistOnlyMintPeriod
500
+ ) {
501
+ require(
502
+ adminArtistOnlyMintPeriodIfSellout,
503
+ "Only admin-artist mint period"
504
+ );
505
+ } else if (
506
+ RAMAdminMintingConstraint_ ==
507
+ AdminMintingConstraint.NoAdminArtistOnlyMintPeriod
508
+ ) {
509
+ require(
510
+ !adminArtistOnlyMintPeriodIfSellout,
511
+ "Only no admin-artist mint period"
512
+ );
513
+ }
514
+
515
+ // refresh max invocations to eliminate any stale state
516
+ MaxInvocationsLib.refreshMaxInvocations({
517
+ projectId: projectId,
518
+ coreContract: coreContract
519
+ });
520
+
521
+ // set auction details
522
+ RAMProjectConfig_.timestampStart = auctionTimestampStart;
523
+ RAMProjectConfig_.timestampEnd = auctionTimestampEnd;
524
+ RAMProjectConfig_.timestampOriginalEnd = auctionTimestampEnd;
525
+ RAMProjectConfig_.basePrice = basePrice;
526
+ RAMProjectConfig_.allowExtraTime = allowExtraTime;
527
+ RAMProjectConfig_
528
+ .adminArtistOnlyMintPeriodIfSellout = adminArtistOnlyMintPeriodIfSellout;
529
+ // refresh numTokensInAuction
530
+ uint256 numTokensInAuction = refreshNumTokensInAuction({
531
+ projectId: projectId,
532
+ coreContract: coreContract
533
+ });
534
+
535
+ // initialize min slot metadata to NUM_SLOTS (an invalid index) to represent NULL value
536
+ RAMProjectConfig_.minBidSlotIndex = uint16(NUM_SLOTS);
537
+
538
+ // emit state change event
539
+ emit AuctionConfigUpdated({
540
+ projectId: projectId,
541
+ coreContract: coreContract,
542
+ timestampStart: auctionTimestampStart,
543
+ timestampEnd: auctionTimestampEnd,
544
+ basePrice: basePrice,
545
+ allowExtraTime: allowExtraTime,
546
+ adminArtistOnlyMintPeriodIfSellout: adminArtistOnlyMintPeriodIfSellout,
547
+ numTokensInAuction: numTokensInAuction
548
+ });
549
+ }
550
+
551
+ /**
552
+ * @notice Reduces the auction length for project `projectId` on core
553
+ * contract `coreContract` to `auctionTimestampEnd`.
554
+ * Only allowed to be called during a live auction, and protects against
555
+ * the case of an accidental excessively long auction, which locks funds.
556
+ * Reverts if called by anyone other than the project's artist.
557
+ * Reverts if project is not in a Live Auction.
558
+ * Reverts if auction is not being reduced in length.
559
+ * Reverts if in extra time.
560
+ * Reverts if `auctionTimestampEnd` results in auction that is not at least
561
+ * `minimumAuctionDurationSeconds` in duration.
562
+ * Reverts if admin previously applied a time extension.
563
+ * @param projectId Project ID to reduce the auction length for.
564
+ * @param coreContract Core contract address for the given project.
565
+ * @param auctionTimestampEnd New timestamp at which to end the auction.
566
+ * @param minimumAuctionDurationSeconds Minimum auction duration, in seconds
567
+ */
568
+ function reduceAuctionLength(
569
+ uint256 projectId,
570
+ address coreContract,
571
+ uint40 auctionTimestampEnd,
572
+ uint256 minimumAuctionDurationSeconds
573
+ ) internal {
574
+ // load project config
575
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
576
+ projectId: projectId,
577
+ coreContract: coreContract
578
+ });
579
+ // CHECKS
580
+ // require auction state B, live auction
581
+ require(
582
+ getProjectMinterState(projectId, coreContract) ==
583
+ ProjectMinterStates.LiveAuction,
584
+ "Only live auction"
585
+ );
586
+ // require no previous admin extension time
587
+ require(
588
+ RAMProjectConfig_.adminEmergencyExtensionHoursApplied == 0,
589
+ "No previous admin extension"
590
+ );
591
+ // require not in extra time
592
+ require(
593
+ RAMProjectConfig_.timestampOriginalEnd ==
594
+ RAMProjectConfig_.timestampEnd,
595
+ "Not allowed in extra time"
596
+ );
597
+ // require reduction in auction length
598
+ require(
599
+ auctionTimestampEnd < RAMProjectConfig_.timestampEnd,
600
+ "Only reduce auction length"
601
+ );
602
+ // require meet minimum auction length requirement
603
+ require(
604
+ auctionTimestampEnd >
605
+ RAMProjectConfig_.timestampStart +
606
+ minimumAuctionDurationSeconds,
607
+ "Auction too short"
608
+ );
609
+ // require new end time in future
610
+ require(auctionTimestampEnd > block.timestamp, "Only future end time");
611
+
612
+ // set auction details
613
+ RAMProjectConfig_.timestampEnd = auctionTimestampEnd;
614
+ // also update original end for accurate extra time calculation
615
+ RAMProjectConfig_.timestampOriginalEnd = auctionTimestampEnd;
616
+
617
+ // emit state change event
618
+ emit AuctionTimestampEndUpdated({
619
+ projectId: projectId,
620
+ coreContract: coreContract,
621
+ timestampEnd: auctionTimestampEnd
622
+ });
623
+ }
624
+
625
+ /**
626
+ * @notice Update the number of tokens in the auction, based on the state
627
+ * of the core contract and the minter-local max invocations.
628
+ * @param projectId Project ID to update
629
+ * @param coreContract Core contract address to update
630
+ */
631
+ function refreshNumTokensInAuction(
632
+ uint256 projectId,
633
+ address coreContract
634
+ ) internal returns (uint256 numTokensInAuction) {
635
+ // load project config
636
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
637
+ projectId: projectId,
638
+ coreContract: coreContract
639
+ });
640
+ // @dev safe to cast - max uint24 is 16_777_215 > 1_000_000 max project size
641
+ numTokensInAuction = MaxInvocationsLib.getInvocationsAvailable({
642
+ projectId: projectId,
643
+ coreContract: coreContract
644
+ });
645
+ RAMProjectConfig_.numTokensInAuction = uint24(numTokensInAuction);
646
+
647
+ // emit event for state change
648
+ emit NumTokensInAuctionUpdated({
649
+ projectId: projectId,
650
+ coreContract: coreContract,
651
+ numTokensInAuction: numTokensInAuction
652
+ });
653
+ }
654
+
655
+ /**
656
+ * @notice Collects settlement for project `projectId` on core contract
657
+ * `coreContract` for all bids in `bidIds`.
658
+ * Reverts if project is not in a post-auction state.
659
+ * Reverts if bidder is not the bidder for all bids.
660
+ * Reverts if one or more bids has already been settled.
661
+ * Reverts if invalid bid is found.
662
+ * @param projectId Project ID of bid to collect settlement for
663
+ * @param coreContract Core contract address for the given project.
664
+ * @param bidIds IDs of bids to collect settlements for
665
+ * @param bidder Bidder address of bid to collect settlements for
666
+ * @param minterRefundGasLimit Gas limit to use when refunding the bidder.
667
+ */
668
+ function collectSettlements(
669
+ uint256 projectId,
670
+ address coreContract,
671
+ uint32[] calldata bidIds,
672
+ address bidder,
673
+ uint256 minterRefundGasLimit
674
+ ) internal {
675
+ // load project config
676
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
677
+ projectId: projectId,
678
+ coreContract: coreContract
679
+ });
680
+ // CHECKS
681
+ // @dev block scope to avoid stack too deep error
682
+ {
683
+ // require project minter state C or D (Post-Auction, Open Mint or Admin-Artist Mint Period)
684
+ ProjectMinterStates projectMinterState = getProjectMinterState({
685
+ projectId: projectId,
686
+ coreContract: coreContract
687
+ });
688
+ require(
689
+ projectMinterState ==
690
+ ProjectMinterStates.PostAuctionSellOutAdminArtistMint ||
691
+ projectMinterState ==
692
+ ProjectMinterStates.PostAuctionOpenMint,
693
+ "Only state C or D"
694
+ );
695
+ }
696
+
697
+ // get project price
698
+ uint256 projectPrice = _getProjectPrice({
699
+ RAMProjectConfig_: RAMProjectConfig_
700
+ });
701
+
702
+ // settle each input bid
703
+ // @dev already verified that input lengths match
704
+ uint256 inputBidsLength = bidIds.length;
705
+ // @dev overflow check optimization as of 0.8.22
706
+ for (uint256 i = 0; i < inputBidsLength; ++i) {
707
+ // settle the bid
708
+ _settleBidWithChecks({
709
+ RAMProjectConfig_: RAMProjectConfig_,
710
+ projectId: projectId,
711
+ coreContract: coreContract,
712
+ projectPrice: projectPrice,
713
+ bidId: bidIds[i],
714
+ bidder: bidder,
715
+ minterRefundGasLimit: minterRefundGasLimit
716
+ });
717
+ }
718
+ }
719
+
720
+ /**
721
+ * @notice Directly mint tokens to winners of project `projectId` on core
722
+ * contract `coreContract`.
723
+ * Does not guarantee an optimal ordering or handling of E1 state like
724
+ * `adminArtistAutoMintTokensToWinners` does while in State C.
725
+ * Skips over bids that have already been minted or refunded (front-running
726
+ * protection)
727
+ * Reverts if project is not in a post-auction state,
728
+ * post-admin-artist-only mint period (i.e. State D), with tokens available
729
+ * Reverts if bid does not exist at bidId.
730
+ * Reverts if msg.sender is not the bidder for all bids if
731
+ * requireSenderIsBidder is true.
732
+ * @param projectId Project ID to mint tokens on.
733
+ * @param coreContract Core contract address for the given project.
734
+ * @param bidIds IDs of bids to mint tokens for
735
+ * @param requireSenderIsBidder bool representing if the sender must be the
736
+ * bidder for all bids
737
+ * @param minterFilter Minter filter to use when minting tokens
738
+ * @param minterRefundGasLimit Gas limit to use when refunding the bidder.
739
+ */
740
+ function directMintTokensToWinners(
741
+ uint256 projectId,
742
+ address coreContract,
743
+ uint32[] calldata bidIds,
744
+ bool requireSenderIsBidder,
745
+ IMinterFilterV1 minterFilter,
746
+ uint256 minterRefundGasLimit
747
+ ) internal {
748
+ // load project config
749
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
750
+ projectId: projectId,
751
+ coreContract: coreContract
752
+ });
753
+
754
+ // get project price
755
+ uint256 projectPrice = _getProjectPrice({
756
+ RAMProjectConfig_: RAMProjectConfig_
757
+ });
758
+
759
+ // CHECKS
760
+ // @dev memoize length for gas efficiency
761
+ uint256 bidIdsLength = bidIds.length;
762
+ // @dev block scope to limit stack depth
763
+ {
764
+ // require project minter state D (Post-Auction,
765
+ // post-admin-artist-only, not all bids handled)
766
+ ProjectMinterStates projectMinterState = getProjectMinterState({
767
+ projectId: projectId,
768
+ coreContract: coreContract
769
+ });
770
+ require(
771
+ projectMinterState == ProjectMinterStates.PostAuctionOpenMint,
772
+ "Only post-auction open mint"
773
+ );
774
+ // require numTokensToMint does not exceed number of tokens
775
+ // owed.
776
+ // @dev must check this here to avoid minting more tokens than max
777
+ // invocations, which could potentially not revert if minter
778
+ // max invocations was limiting (+other unexpected conditions)
779
+ require(
780
+ bidIdsLength <=
781
+ _getNumTokensOwed({RAMProjectConfig_: RAMProjectConfig_}),
782
+ "tokens to mint gt tokens owed"
783
+ );
784
+ }
785
+
786
+ // main loop to mint tokens
787
+ for (uint256 i; i < bidIdsLength; ++i) {
788
+ // @dev current slot index and bid index in slot not memoized due
789
+ // to stack depth limitations
790
+ // get bid
791
+ uint256 currentBidId = bidIds[i];
792
+ Bid storage bid = RAMProjectConfig_.bids[currentBidId];
793
+ address bidderAddress = bid.bidder;
794
+ // CHECKS
795
+ // require bid exists
796
+ require(bidderAddress != address(0), "invalid Bid ID");
797
+ // if bid is already minted or refunded, skip to next bid
798
+ // @dev do not revert, since this could be due to front-running
799
+ if (
800
+ _getBidPackedBool(bid, INDEX_IS_MINTED) ||
801
+ _getBidPackedBool(bid, INDEX_IS_REFUNDED)
802
+ ) {
803
+ continue;
804
+ }
805
+ // require sender is bidder if requireSenderIsBidder is true
806
+ if (requireSenderIsBidder) {
807
+ require(msg.sender == bidderAddress, "Only sender is bidder");
808
+ }
809
+ // EFFECTS
810
+ // @dev num bids minted tokens not memoized due to stack depth
811
+ // limitations
812
+ RAMProjectConfig_.numBidsMintedTokens++;
813
+ // Mint bid and settle if not already settled
814
+ _mintAndSettle({
815
+ projectId: projectId,
816
+ coreContract: coreContract,
817
+ projectPrice: projectPrice,
818
+ slotIndex: bid.slotIndex,
819
+ bidId: currentBidId,
820
+ minterFilter: minterFilter,
821
+ minterRefundGasLimit: minterRefundGasLimit
822
+ });
823
+ }
824
+ }
825
+
826
+ /**
827
+ * @notice Function that enables a contract admin or artist (checked by
828
+ * external function) to mint tokens to winners of project `projectId` on
829
+ * core contract `coreContract`.
830
+ * Automatically mints tokens to most-winning bids, in order from highest
831
+ * and earliest bid to lowest and latest bid.
832
+ * Settles bids as tokens are minted, if not already settled.
833
+ * Reverts if project is not in a post-auction state, admin-artist-only mint
834
+ * period (i.e. State C), with tokens available.
835
+ * to be minted.
836
+ * Reverts if number of tokens to mint is greater than the number of
837
+ * tokens available to be minted.
838
+ * @param projectId Project ID to mint tokens on.
839
+ * @param coreContract Core contract address for the given project.
840
+ * @param numTokensToMint Number of tokens to mint in this transaction.
841
+ * @param minterFilter Minter filter contract address
842
+ * @param minterRefundGasLimit Gas limit to use when settling bid if not already settled
843
+ */
844
+ function adminArtistAutoMintTokensToWinners(
845
+ uint256 projectId,
846
+ address coreContract,
847
+ uint24 numTokensToMint,
848
+ IMinterFilterV1 minterFilter,
849
+ uint256 minterRefundGasLimit
850
+ ) internal {
851
+ // load project config
852
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
853
+ projectId: projectId,
854
+ coreContract: coreContract
855
+ });
856
+
857
+ // get project price
858
+ uint256 projectPrice = _getProjectPrice({
859
+ RAMProjectConfig_: RAMProjectConfig_
860
+ });
861
+
862
+ // CHECKS
863
+ // @dev block scope to limit stack depth
864
+ {
865
+ // require project minter state C (Post-Auction, sell out, admin-artist-only,
866
+ // not all bids handled)
867
+ ProjectMinterStates projectMinterState = getProjectMinterState({
868
+ projectId: projectId,
869
+ coreContract: coreContract
870
+ });
871
+ require(
872
+ projectMinterState ==
873
+ ProjectMinterStates.PostAuctionSellOutAdminArtistMint,
874
+ "Only state C"
875
+ );
876
+ // require numTokensToMint does not exceed number of tokens
877
+ // owed
878
+ // @dev must check this here to avoid minting more tokens than max
879
+ // invocations, which could potentially not revert if minter
880
+ // max invocations was limiting (+other unexpected conditions)
881
+ require(
882
+ numTokensToMint <=
883
+ _getNumTokensOwed({RAMProjectConfig_: RAMProjectConfig_}),
884
+ "tokens to mint gt tokens owed"
885
+ );
886
+ }
887
+
888
+ // EFFECTS
889
+ // load values to memory for gas efficiency
890
+ uint256 currentLatestMintedBidId = RAMProjectConfig_.latestMintedBidId;
891
+ // @dev will be zero if no bids minted yet
892
+ uint256 currentLatestMintedBidSlotIndex = RAMProjectConfig_
893
+ .bids[currentLatestMintedBidId]
894
+ .slotIndex;
895
+
896
+ uint256 numNewTokensMinted; // = 0
897
+
898
+ // main loop to mint tokens
899
+ while (numNewTokensMinted < numTokensToMint) {
900
+ // EFFECTS
901
+ // STEP 1: scroll to next bid to be minted a token
902
+ // set latest minted bid indices to the bid to be minted a token
903
+ if (currentLatestMintedBidId == 0) {
904
+ // first mint, so need to initialize cursor values
905
+ // set bid to highest bid in the project, head of max bid slot
906
+ currentLatestMintedBidSlotIndex = RAMProjectConfig_
907
+ .maxBidSlotIndex;
908
+ currentLatestMintedBidId = RAMProjectConfig_.headBidIdBySlot[
909
+ currentLatestMintedBidSlotIndex
910
+ ];
911
+ } else {
912
+ // scroll to next bid in current slot
913
+ // @dev scrolling to null is okay and handled below
914
+ currentLatestMintedBidId = RAMProjectConfig_
915
+ .bids[currentLatestMintedBidId]
916
+ .nextBidId;
917
+ // if scrolled off end of list, then find next slot with bids
918
+ if (currentLatestMintedBidId == 0) {
919
+ // past tail of current slot's linked list, so need to find next
920
+ // bid slot with bids
921
+ currentLatestMintedBidSlotIndex = _getMaxSlotWithBid({
922
+ RAMProjectConfig_: RAMProjectConfig_,
923
+ startSlotIndex: uint16(
924
+ currentLatestMintedBidSlotIndex - 1
925
+ )
926
+ });
927
+ // @dev no coverage on else branch because it is unreachable as used
928
+ require(
929
+ currentLatestMintedBidSlotIndex < NUM_SLOTS,
930
+ "slot with bid not found"
931
+ );
932
+ // current bid is now the head of the linked list
933
+ currentLatestMintedBidId = RAMProjectConfig_
934
+ .headBidIdBySlot[currentLatestMintedBidSlotIndex];
935
+ }
936
+ }
937
+
938
+ // @dev minter is in State C, so bid must not have been minted or
939
+ // refunded due to scrolling logic of admin mint and refund
940
+ // functions available for use while in State C. The bid may have
941
+ // been previously settled, however.
942
+
943
+ // Mint bid and settle if not already settled
944
+ // @dev scrolling logic in State C ensures bid **exists** is not yet minted
945
+ _mintAndSettle({
946
+ projectId: projectId,
947
+ coreContract: coreContract,
948
+ projectPrice: projectPrice,
949
+ slotIndex: currentLatestMintedBidSlotIndex,
950
+ bidId: currentLatestMintedBidId,
951
+ minterFilter: minterFilter,
952
+ minterRefundGasLimit: minterRefundGasLimit
953
+ });
954
+
955
+ // increment num new tokens minted
956
+ unchecked {
957
+ ++numNewTokensMinted;
958
+ }
959
+ }
960
+
961
+ // finally, update auction metadata storage state from memoized values
962
+ // @dev safe to cast numNewTokensMinted to uint24
963
+ RAMProjectConfig_.numBidsMintedTokens += uint24(numNewTokensMinted);
964
+ // @dev safe to cast to uint32 because directly derived from bid ID
965
+ RAMProjectConfig_.latestMintedBidId = uint32(currentLatestMintedBidId);
966
+ }
967
+
968
+ /**
969
+ * @notice Directly refund bids for project `projectId` on core contract
970
+ * `coreContract` to resolve error state E1.
971
+ * Does not guarantee an optimal ordering or handling of E1 state like
972
+ * `autoRefundBidsToResolveE1` does while in State C.
973
+ * Skips over bids that have already been minted or refunded (front-running
974
+ * protection)
975
+ * Reverts if project is not in post-auction state,
976
+ * post-admin-artist-only mint period (i.e. State D).
977
+ * Reverts if project is not in error state E1.
978
+ * Reverts if length of bids to refund exceeds the number of bids that need
979
+ * to be refunded to resolve the error state E1.
980
+ * Reverts if bid does not exist at bidId.
981
+ * Reverts if msg.sender is not the bidder for all bids if
982
+ * requireSenderIsBidder is true.
983
+ * @param projectId Project ID to refunds bids for.
984
+ * @param coreContract Core contract address for the given project.
985
+ * @param bidIds IDs of bids to refund bid values for
986
+ * @param requireSenderIsBidder Require sender is bidder for all bids
987
+ * @param minterRefundGasLimit Gas limit to use when refunding the bidder.
988
+ */
989
+ function directRefundBidsToResolveE1(
990
+ uint256 projectId,
991
+ address coreContract,
992
+ uint32[] calldata bidIds,
993
+ bool requireSenderIsBidder,
994
+ uint256 minterRefundGasLimit
995
+ ) internal {
996
+ // CHECKS
997
+ // @dev memoize length for gas efficiency
998
+ uint256 bidIdsLength = bidIds.length;
999
+ // @dev block scope to limit stack depth
1000
+ {
1001
+ // require project minter state D (Post-Auction, post-admin-artist-only,
1002
+ // not all bids handled)
1003
+ ProjectMinterStates projectMinterState = getProjectMinterState({
1004
+ projectId: projectId,
1005
+ coreContract: coreContract
1006
+ });
1007
+ require(
1008
+ projectMinterState == ProjectMinterStates.PostAuctionOpenMint,
1009
+ "Only post-auction open mint"
1010
+ );
1011
+ // require is in state E1
1012
+ (bool isErrorE1_, uint256 numBidsToResolveE1, ) = isErrorE1FlagF1({
1013
+ projectId: projectId,
1014
+ coreContract: coreContract
1015
+ });
1016
+ require(isErrorE1_, "Only in state E1");
1017
+ // require numBidsToRefund does not exceed max number of bids
1018
+ // to resolve E1 error state
1019
+ require(
1020
+ bidIdsLength <= numBidsToResolveE1,
1021
+ "bids to refund gt available qty"
1022
+ );
1023
+ }
1024
+
1025
+ // load project config
1026
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
1027
+ projectId: projectId,
1028
+ coreContract: coreContract
1029
+ });
1030
+ // get project price
1031
+ uint256 projectPrice = _getProjectPrice({
1032
+ RAMProjectConfig_: RAMProjectConfig_
1033
+ });
1034
+
1035
+ // @dev memoize for gas efficiency
1036
+ uint24 numRefundsIssued = 0;
1037
+ // main loop to refund tokens
1038
+ for (uint256 i; i < bidIdsLength; ++i) {
1039
+ // @dev current slot index and bid index in slot not memoized due
1040
+ // to stack depth limitations
1041
+ // get bid
1042
+ uint256 currentBidId = bidIds[i];
1043
+ Bid storage bid = RAMProjectConfig_.bids[currentBidId];
1044
+ // CHECKS
1045
+ // require bidder is non-zero address (i.e. bid exists)
1046
+ address bidderAddress = bid.bidder;
1047
+ require(bidderAddress != address(0), "invalid Bid ID");
1048
+ // if bid is already minted or refunded, skip to next bid
1049
+ // @dev do not revert, since this could be due to front-running
1050
+ if (
1051
+ _getBidPackedBool(bid, INDEX_IS_MINTED) ||
1052
+ _getBidPackedBool(bid, INDEX_IS_REFUNDED)
1053
+ ) {
1054
+ continue;
1055
+ }
1056
+ // require sender is bidder if requireSenderIsBidder is true
1057
+ if (requireSenderIsBidder) {
1058
+ require(msg.sender == bidderAddress, "Only sender is bidder");
1059
+ }
1060
+ // EFFECTS
1061
+ // Settle and Refund the Bid
1062
+ _settleAndRefundBid({
1063
+ projectId: projectId,
1064
+ coreContract: coreContract,
1065
+ projectPrice: projectPrice,
1066
+ slotIndex: bid.slotIndex,
1067
+ bidId: currentBidId,
1068
+ minterRefundGasLimit: minterRefundGasLimit
1069
+ });
1070
+ numRefundsIssued++;
1071
+ }
1072
+ // update number of bids refunded
1073
+ RAMProjectConfig_.numBidsErrorRefunded += numRefundsIssued;
1074
+ }
1075
+
1076
+ /**
1077
+ * @notice Function to automatically refund the lowest winning bids for
1078
+ * project `projectId` on core contract `coreContract` to resolve error
1079
+ * state E1.
1080
+ * Reverts if project is not in post-auction state C.
1081
+ * Reverts if project is not in error state E1.
1082
+ * Reverts if numBidsToRefund exceeds the number of bids that need to be
1083
+ * refunded to resolve the error state E1.
1084
+ * @dev Recommend admin-only not for security, but rather to enable Admin
1085
+ * to be aware that an error state has been encountered while in post-
1086
+ * auction state C.
1087
+ * @param projectId Project ID to refunds bids for.
1088
+ * @param coreContract Core contract address for the given project.
1089
+ * @param numBidsToRefund Number of bids to refund in this call.
1090
+ * @param minterRefundGasLimit Gas limit to use when refunding the bidder.
1091
+ */
1092
+ function autoRefundBidsToResolveE1(
1093
+ uint256 projectId,
1094
+ address coreContract,
1095
+ uint24 numBidsToRefund,
1096
+ uint256 minterRefundGasLimit
1097
+ ) internal {
1098
+ // CHECKS
1099
+ // @dev block scope to limit stack depth
1100
+ {
1101
+ // require project minter state C (Post-Auction, admin-artist-only,
1102
+ // not all bids handled)
1103
+ ProjectMinterStates projectMinterState = getProjectMinterState({
1104
+ projectId: projectId,
1105
+ coreContract: coreContract
1106
+ });
1107
+ require(
1108
+ projectMinterState ==
1109
+ ProjectMinterStates.PostAuctionSellOutAdminArtistMint,
1110
+ "Only state C"
1111
+ );
1112
+ // require is in state E1
1113
+ (bool isErrorE1_, uint256 numBidsToResolveE1, ) = isErrorE1FlagF1({
1114
+ projectId: projectId,
1115
+ coreContract: coreContract
1116
+ });
1117
+ require(isErrorE1_, "Only in state E1");
1118
+ // require numBidsToRefund does not exceed max number of bids
1119
+ // to resolve E1 error state
1120
+ require(
1121
+ numBidsToRefund <= numBidsToResolveE1,
1122
+ "bids to refund gt available qty"
1123
+ );
1124
+ }
1125
+ // load project config
1126
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
1127
+ projectId: projectId,
1128
+ coreContract: coreContract
1129
+ });
1130
+ // get project price
1131
+ uint256 projectPrice = _getProjectPrice({
1132
+ RAMProjectConfig_: RAMProjectConfig_
1133
+ });
1134
+
1135
+ // EFFECTS
1136
+ // load values to memory for gas efficiency
1137
+ uint256 currentLatestRefundedBidId = RAMProjectConfig_
1138
+ .latestRefundedBidId;
1139
+ uint256 currentLatestRefundedBidSlotIndex = RAMProjectConfig_
1140
+ .bids[currentLatestRefundedBidId]
1141
+ .slotIndex;
1142
+ // settlement values
1143
+ uint256 numRefundsIssued; // = 0
1144
+
1145
+ // main loop to refund bids
1146
+ while (numRefundsIssued < numBidsToRefund) {
1147
+ // EFFECTS
1148
+ // STEP 1: Get next bid to be refunded
1149
+ // set latest refunded bid indices to the bid to be refunded
1150
+ if (currentLatestRefundedBidId == 0) {
1151
+ // first refund, so need to initialize cursor values
1152
+ // set bid to lowest bid in the project, tail of min bid slot
1153
+ currentLatestRefundedBidSlotIndex = RAMProjectConfig_
1154
+ .minBidSlotIndex;
1155
+ currentLatestRefundedBidId = RAMProjectConfig_.tailBidIdBySlot[
1156
+ currentLatestRefundedBidSlotIndex
1157
+ ];
1158
+ } else {
1159
+ // scroll to previous bid in current slot
1160
+ // @dev scrolling to null is okay and handled below
1161
+ currentLatestRefundedBidId = RAMProjectConfig_
1162
+ .bids[currentLatestRefundedBidId]
1163
+ .prevBidId;
1164
+ }
1165
+
1166
+ // if scrolled off end of list, then find next slot with bids
1167
+ if (currentLatestRefundedBidId == 0) {
1168
+ // past head of current slot's linked list, so need to find next
1169
+ // bid slot with bids
1170
+ // @dev not possible to not find next slot during auto-refund,
1171
+ // so no need to handle case where slot not found
1172
+ currentLatestRefundedBidSlotIndex = _getMinSlotWithBid({
1173
+ RAMProjectConfig_: RAMProjectConfig_,
1174
+ startSlotIndex: uint16(
1175
+ currentLatestRefundedBidSlotIndex + 1
1176
+ )
1177
+ });
1178
+ // current bid is now the tail of the linked list
1179
+ currentLatestRefundedBidId = RAMProjectConfig_.tailBidIdBySlot[
1180
+ currentLatestRefundedBidSlotIndex
1181
+ ];
1182
+ }
1183
+
1184
+ // @dev minter is in State C, so bid must not have been minted or
1185
+ // refunded due to scrolling logic of admin mint and refund
1186
+ // functions available for use while in State C. The bid may have
1187
+ // been previously settled, however.
1188
+
1189
+ // Settle & Refund the Bid
1190
+ _settleAndRefundBid({
1191
+ projectId: projectId,
1192
+ coreContract: coreContract,
1193
+ projectPrice: projectPrice,
1194
+ slotIndex: uint16(currentLatestRefundedBidSlotIndex),
1195
+ bidId: currentLatestRefundedBidId,
1196
+ minterRefundGasLimit: minterRefundGasLimit
1197
+ });
1198
+ // increment loop counter and current num bids refunded
1199
+ unchecked {
1200
+ ++numRefundsIssued;
1201
+ }
1202
+ }
1203
+
1204
+ // finally, update auction metadata storage state from memoized values
1205
+ // @dev safe to cast currentNumBidsErrorRefunded to uint24
1206
+ RAMProjectConfig_.numBidsErrorRefunded += uint24(numRefundsIssued);
1207
+ // @dev safe to cast to uint32 because directly derived from bid ID
1208
+ RAMProjectConfig_.latestRefundedBidId = uint32(
1209
+ currentLatestRefundedBidId
1210
+ );
1211
+ }
1212
+
1213
+ /**
1214
+ * @notice This withdraws project revenues for project `projectId` on core
1215
+ * contract `coreContract` to the artist and admin, only after all bids
1216
+ * have been minted+settled or refunded.
1217
+ * Note that the conditions described are the equivalent of project minter
1218
+ * State E.
1219
+ * @param projectId Project ID to withdraw revenues for.
1220
+ * @param coreContract Core contract address for the given project.
1221
+ */
1222
+ function withdrawArtistAndAdminRevenues(
1223
+ uint256 projectId,
1224
+ address coreContract
1225
+ ) internal {
1226
+ // load project config
1227
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
1228
+ projectId: projectId,
1229
+ coreContract: coreContract
1230
+ });
1231
+
1232
+ // CHECKS
1233
+ // require project minter state E (Post-Auction, all bids handled)
1234
+ ProjectMinterStates projectMinterState = getProjectMinterState({
1235
+ projectId: projectId,
1236
+ coreContract: coreContract
1237
+ });
1238
+ require(
1239
+ projectMinterState == ProjectMinterStates.PostAuctionAllBidsHandled,
1240
+ "Only state E"
1241
+ );
1242
+ // require revenues not already withdrawn
1243
+ require(
1244
+ !(RAMProjectConfig_.revenuesCollected),
1245
+ "Revenues already withdrawn"
1246
+ );
1247
+
1248
+ // EFFECTS
1249
+ // update state to indicate revenues withdrawn
1250
+ RAMProjectConfig_.revenuesCollected = true;
1251
+
1252
+ // get project price
1253
+ uint256 projectPrice = _getProjectPrice({
1254
+ RAMProjectConfig_: RAMProjectConfig_
1255
+ });
1256
+ // get netRevenues
1257
+ // @dev refunded bids do not count towards amount due because they
1258
+ // did not generate revenue
1259
+ uint256 netRevenues = projectPrice *
1260
+ RAMProjectConfig_.numBidsMintedTokens;
1261
+
1262
+ // update project balance
1263
+ // @dev reverts on underflow
1264
+ RAMProjectConfig_.projectBalance -= uint120(netRevenues);
1265
+
1266
+ // INTERACTIONS
1267
+ SplitFundsLib.splitRevenuesETHNoRefund({
1268
+ projectId: projectId,
1269
+ valueInWei: netRevenues,
1270
+ coreContract: coreContract
1271
+ });
1272
+
1273
+ emit GenericMinterEventsLib.ConfigValueSet({
1274
+ projectId: projectId,
1275
+ coreContract: coreContract,
1276
+ key: CONFIG_AUCTION_REVENUES_COLLECTED,
1277
+ value: true
1278
+ });
1279
+ }
1280
+
1281
+ /**
1282
+ * @notice Function to mint tokens if an auction is over, but did not sell
1283
+ * out and tokens are still available to be minted.
1284
+ * @dev must be called within non-reentrant context
1285
+ * @param to Address to be the new token's owner.
1286
+ * @param projectId Project ID to mint a token on.
1287
+ * @param coreContract Core contract address for the given project.
1288
+ * @param minterFilter Minter filter to use when minting token.
1289
+ * @return tokenId Token ID of minted token
1290
+ */
1291
+ function purchaseTo(
1292
+ address to,
1293
+ uint256 projectId,
1294
+ address coreContract,
1295
+ IMinterFilterV1 minterFilter
1296
+ ) internal returns (uint256 tokenId) {
1297
+ // load project config
1298
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
1299
+ projectId: projectId,
1300
+ coreContract: coreContract
1301
+ });
1302
+
1303
+ // CHECKS
1304
+ // @dev block scope to limit stack depth
1305
+ {
1306
+ // require project minter state D, or E (Post-Auction)
1307
+ ProjectMinterStates projectMinterState = getProjectMinterState({
1308
+ projectId: projectId,
1309
+ coreContract: coreContract
1310
+ });
1311
+ require(
1312
+ projectMinterState == ProjectMinterStates.PostAuctionOpenMint ||
1313
+ projectMinterState ==
1314
+ ProjectMinterStates.PostAuctionAllBidsHandled,
1315
+ "Only state D or E"
1316
+ );
1317
+ // require Flag F1, i.e. at least one excess token available to be
1318
+ // minted
1319
+ // @dev this ensures minter and core contract max-invocations
1320
+ // constraints are not violated, as well as confirms that one
1321
+ // additional mint will not send the minter into an E1 state
1322
+ (, , uint256 numExcessInvocationsAvailable) = isErrorE1FlagF1({
1323
+ projectId: projectId,
1324
+ coreContract: coreContract
1325
+ });
1326
+ require(
1327
+ numExcessInvocationsAvailable > 0,
1328
+ "Reached max invocations"
1329
+ );
1330
+ }
1331
+ // require sufficient payment
1332
+ // since excess invocations are available, know not a sellout, so
1333
+ // project price is base price
1334
+ uint256 pricePerTokenInWei = RAMProjectConfig_.basePrice;
1335
+ require(
1336
+ msg.value == pricePerTokenInWei,
1337
+ "Only send auction reserve price"
1338
+ );
1339
+
1340
+ // EFFECTS
1341
+ // mint token
1342
+ tokenId = minterFilter.mint_joo({
1343
+ to: to,
1344
+ projectId: projectId,
1345
+ coreContract: coreContract,
1346
+ sender: msg.sender
1347
+ });
1348
+
1349
+ // @dev this minter specifically does not update max invocations has
1350
+ // been reached, since it must consider unminted bids when determining
1351
+ // if max invocations has been reached
1352
+
1353
+ // INTERACTIONS
1354
+ // split revenue from sale
1355
+ // @dev no refund because previously verified msg.value == pricePerTokenInWei
1356
+ // @dev no effect on project balance, splitting same amount received
1357
+ SplitFundsLib.splitRevenuesETHNoRefund({
1358
+ projectId: projectId,
1359
+ valueInWei: pricePerTokenInWei,
1360
+ coreContract: coreContract
1361
+ });
1362
+
1363
+ // emit event for state change
1364
+ emit TokenPurchased({
1365
+ projectId: projectId,
1366
+ coreContract: coreContract,
1367
+ tokenId: tokenId,
1368
+ to: to
1369
+ });
1370
+ }
1371
+
1372
+ /**
1373
+ * @notice Place a new bid for a project.
1374
+ * Assumes check that minter is set for project on minter filter has
1375
+ * already been performed.
1376
+ * Reverts if project is not in state B (Live Auction).
1377
+ * Reverts if bid value is not equal to the slot value.
1378
+ * @param projectId Project Id to place bid for
1379
+ * @param coreContract Core contract address to place bid for
1380
+ * @param slotIndex Slot index to place bid at
1381
+ * @param bidder Bidder address
1382
+ * @param bidValue Bid value, in Wei (verified to align with slotIndex)
1383
+ * @param minterRefundGasLimit Gas limit to use when refunding the previous
1384
+ * highest bidder, prior to using fallback force-send to refund
1385
+ */
1386
+ function placeBid(
1387
+ uint256 projectId,
1388
+ address coreContract,
1389
+ uint16 slotIndex,
1390
+ address bidder,
1391
+ uint256 bidValue,
1392
+ uint256 minterRefundGasLimit
1393
+ ) internal {
1394
+ // load project config
1395
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
1396
+ projectId: projectId,
1397
+ coreContract: coreContract
1398
+ });
1399
+ // CHECKS
1400
+ // require project minter state B (Live Auction)
1401
+ require(
1402
+ getProjectMinterState(projectId, coreContract) ==
1403
+ ProjectMinterStates.LiveAuction,
1404
+ "Only live auction"
1405
+ );
1406
+ // require slot index not out of range
1407
+ // @dev slot index out of range is checked in slotIndexToBidValue
1408
+ // require bid value must equal slot value
1409
+ uint256 newBidRequiredValue = slotIndexToBidValue({
1410
+ basePrice: RAMProjectConfig_.basePrice,
1411
+ slotIndex: slotIndex
1412
+ });
1413
+ require(
1414
+ bidValue == newBidRequiredValue,
1415
+ "msg.value must equal slot value"
1416
+ );
1417
+
1418
+ // EFFECTS
1419
+ // add bid value to project balance
1420
+ RAMProjectConfig_.projectBalance += uint120(bidValue);
1421
+ // if first bid, refresh max invocations in case artist has reduced
1422
+ // the core contract's max invocations after the auction was configured
1423
+ // @dev this helps prevent E1 error state
1424
+ if (RAMProjectConfig_.numBids == 0) {
1425
+ // refresh max invocations
1426
+ MaxInvocationsLib.refreshMaxInvocations({
1427
+ projectId: projectId,
1428
+ coreContract: coreContract
1429
+ });
1430
+ // also refresh numTokensInAuction for RAM project config
1431
+ refreshNumTokensInAuction({
1432
+ projectId: projectId,
1433
+ coreContract: coreContract
1434
+ });
1435
+ }
1436
+ // require at least one token allowed in auction
1437
+ // @dev this case would revert in _removeMinBid, but prefer clean error
1438
+ // message here
1439
+ uint256 numTokensInAuction = RAMProjectConfig_.numTokensInAuction;
1440
+ require(numTokensInAuction > 0, "No tokens in auction");
1441
+ // determine if have reached max bids
1442
+ bool reachedMaxBids = RAMProjectConfig_.numBids >= numTokensInAuction;
1443
+ if (reachedMaxBids) {
1444
+ // remove + refund the minimum Bid
1445
+ uint256 removedBidValue = _removeMinBid({
1446
+ RAMProjectConfig_: RAMProjectConfig_,
1447
+ projectId: projectId,
1448
+ coreContract: coreContract,
1449
+ minterRefundGasLimit: minterRefundGasLimit
1450
+ });
1451
+ // require new bid is sufficiently greater than removed minimum bid
1452
+ require(
1453
+ _isSufficientOutbid({
1454
+ oldBidValue: removedBidValue,
1455
+ newBidValue: bidValue
1456
+ }),
1457
+ "Insufficient bid value"
1458
+ );
1459
+
1460
+ // apply auction extension time if needed
1461
+ bool timeExtensionNeeded = RAMProjectConfig_.allowExtraTime &&
1462
+ block.timestamp >
1463
+ RAMProjectConfig_.timestampEnd - AUCTION_BUFFER_SECONDS;
1464
+ if (timeExtensionNeeded) {
1465
+ // extend auction end time to no longer than
1466
+ // MAX_AUCTION_EXTRA_SECONDS after original end time
1467
+ RAMProjectConfig_.timestampEnd = uint40(
1468
+ Math.min(
1469
+ RAMProjectConfig_.timestampOriginalEnd +
1470
+ MAX_AUCTION_EXTRA_SECONDS,
1471
+ block.timestamp + AUCTION_BUFFER_SECONDS
1472
+ )
1473
+ );
1474
+ emit AuctionTimestampEndUpdated({
1475
+ projectId: projectId,
1476
+ coreContract: coreContract,
1477
+ timestampEnd: RAMProjectConfig_.timestampEnd
1478
+ });
1479
+ }
1480
+ }
1481
+ // insert the new Bid
1482
+ _insertBid({
1483
+ RAMProjectConfig_: RAMProjectConfig_,
1484
+ projectId: projectId,
1485
+ coreContract: coreContract,
1486
+ slotIndex: slotIndex,
1487
+ bidder: bidder,
1488
+ bidId: 0 // zero triggers new bid ID to be assigned
1489
+ });
1490
+ }
1491
+
1492
+ /**
1493
+ * @notice Top up bid for project `projectId` on core contract
1494
+ * `coreContract` for bid `bidId` to new slot index `newSlotIndex`.
1495
+ * Reverts if Bid ID has been kicked out of the auction or does not exist.
1496
+ * Reverts if bidder is not the bidder of the bid.
1497
+ * Reverts if project is not in a Live Auction.
1498
+ * Reverts if addedValue is not equal to difference in bid values between
1499
+ * new and old slots.
1500
+ * Reverts if new slot index is not greater than or equal to the current
1501
+ * slot index.
1502
+ * @param projectId Project ID to top up bid for.
1503
+ * @param coreContract Core contract address for the given project.
1504
+ * @param bidId ID of bid to top up.
1505
+ * @param newSlotIndex New slot index to move bid to.
1506
+ * @param bidder Bidder address
1507
+ * @param addedValue Value to add to the bid, in Wei
1508
+ */
1509
+ function topUpBid(
1510
+ uint256 projectId,
1511
+ address coreContract,
1512
+ uint32 bidId,
1513
+ uint16 newSlotIndex,
1514
+ address bidder,
1515
+ uint256 addedValue
1516
+ ) internal {
1517
+ // load project config
1518
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
1519
+ projectId: projectId,
1520
+ coreContract: coreContract
1521
+ });
1522
+ Bid storage bid = RAMProjectConfig_.bids[bidId];
1523
+ // memoize for gas efficiency
1524
+ uint16 oldSlotIndex = bid.slotIndex;
1525
+ // CHECKS
1526
+ {
1527
+ // require project minter state B (Live Auction)
1528
+ require(
1529
+ getProjectMinterState(projectId, coreContract) ==
1530
+ ProjectMinterStates.LiveAuction,
1531
+ "Only live auction"
1532
+ );
1533
+ // require new slot index not out of range
1534
+ // @dev slot index out of range is checked in slotIndexToBidValue
1535
+ // @dev give clean error message if bid is null or deleted
1536
+ require(bid.bidder != address(0), "Bid dne - were you outbid?");
1537
+ // require bidder owns referenced bid
1538
+ require(bid.bidder == bidder, "Only bidder of existing bid");
1539
+ // require correct added bid value
1540
+ uint256 oldBidValue = slotIndexToBidValue({
1541
+ basePrice: RAMProjectConfig_.basePrice,
1542
+ slotIndex: oldSlotIndex
1543
+ });
1544
+ uint256 newBidValue = slotIndexToBidValue({
1545
+ basePrice: RAMProjectConfig_.basePrice,
1546
+ slotIndex: newSlotIndex
1547
+ });
1548
+ // implicitly checks that newSlotIndex > oldSlotIndex, since
1549
+ // addedValue must be positive
1550
+ require(
1551
+ oldBidValue + addedValue == newBidValue,
1552
+ "incorrect added value"
1553
+ );
1554
+ }
1555
+
1556
+ // EFFECTS
1557
+ // add the added value to project balance
1558
+ RAMProjectConfig_.projectBalance += uint120(addedValue);
1559
+ // eject bid from the linked list at oldSlotIndex
1560
+ _ejectBidFromSlot({
1561
+ RAMProjectConfig_: RAMProjectConfig_,
1562
+ slotIndex: oldSlotIndex,
1563
+ bidId: bidId
1564
+ });
1565
+ // insert the existing bid into newSlotIndex's linked list
1566
+ _insertBid({
1567
+ RAMProjectConfig_: RAMProjectConfig_,
1568
+ projectId: projectId,
1569
+ coreContract: coreContract,
1570
+ slotIndex: newSlotIndex,
1571
+ bidder: bidder,
1572
+ bidId: bidId
1573
+ });
1574
+
1575
+ // emit top-up event
1576
+ emit BidToppedUp({
1577
+ projectId: projectId,
1578
+ coreContract: coreContract,
1579
+ bidId: bidId,
1580
+ newSlotIndex: newSlotIndex
1581
+ });
1582
+ }
1583
+
1584
+ /**
1585
+ * @notice Returns a storage pointer to the Bid struct and slot index of the lowest bid in the
1586
+ * project's auction, in Wei.
1587
+ * Reverts if no bids exist in the auction.
1588
+ * @param projectId Project ID to get the lowest bid value for
1589
+ * @param coreContract Core contract address for the given project
1590
+ * @return minBid Storage pointer to Bid struct of the lowest bid in
1591
+ * the auction
1592
+ * @return minSlotIndex Slot index of the lowest bid in the auction
1593
+ */
1594
+ function getLowestBid(
1595
+ uint256 projectId,
1596
+ address coreContract
1597
+ ) internal view returns (Bid storage minBid, uint16 minSlotIndex) {
1598
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
1599
+ projectId: projectId,
1600
+ coreContract: coreContract
1601
+ });
1602
+ // revert if no bids in auction
1603
+ require(RAMProjectConfig_.numBids > 0, "No bids in auction");
1604
+ // get min slot with a bid
1605
+ minSlotIndex = RAMProjectConfig_.minBidSlotIndex;
1606
+ // get the tail bid ID for the min slot
1607
+ uint256 tailBidId = RAMProjectConfig_.tailBidIdBySlot[minSlotIndex];
1608
+ minBid = RAMProjectConfig_.bids[tailBidId];
1609
+ }
1610
+
1611
+ /**
1612
+ * @notice Returns the auction details for project `projectId` on core
1613
+ * contract `coreContract`.
1614
+ * @param projectId is an existing project ID.
1615
+ * @param coreContract is an existing core contract address.
1616
+ * @return auctionTimestampStart is the timestamp at which the auction
1617
+ * starts.
1618
+ * @return auctionTimestampEnd is the timestamp at which the auction ends.
1619
+ * @return basePrice is the resting price of the auction, in Wei.
1620
+ * @return numTokensInAuction is the number of tokens in the auction.
1621
+ * @return numBids is the number of bids in the auction.
1622
+ * @return numBidsMintedTokens is the number of bids that have been minted
1623
+ * into tokens.
1624
+ * @return numBidsErrorRefunded is the number of bids that have been
1625
+ * refunded due to an error state.
1626
+ * @return minBidSlotIndex is the index of the slot with the minimum bid
1627
+ * value.
1628
+ * @return allowExtraTime is a bool indicating if the auction is allowed to
1629
+ * have extra time.
1630
+ * @return adminArtistOnlyMintPeriodIfSellout is a bool indicating if an
1631
+ * admin-artist-only mint period is required if the auction sells out.
1632
+ * @return revenuesCollected is a bool indicating if the auction revenues
1633
+ * have been collected.
1634
+ * @return projectMinterState is the current state of the project minter.
1635
+ * @dev projectMinterState is a RAMLib.ProjectMinterStates enum value.
1636
+ */
1637
+ function getAuctionDetails(
1638
+ uint256 projectId,
1639
+ address coreContract
1640
+ )
1641
+ internal
1642
+ view
1643
+ returns (
1644
+ uint256 auctionTimestampStart,
1645
+ uint256 auctionTimestampEnd,
1646
+ uint256 basePrice,
1647
+ uint256 numTokensInAuction,
1648
+ uint256 numBids,
1649
+ uint256 numBidsMintedTokens,
1650
+ uint256 numBidsErrorRefunded,
1651
+ uint256 minBidSlotIndex,
1652
+ bool allowExtraTime,
1653
+ bool adminArtistOnlyMintPeriodIfSellout,
1654
+ bool revenuesCollected,
1655
+ RAMLib.ProjectMinterStates projectMinterState
1656
+ )
1657
+ {
1658
+ // asign project minter state
1659
+ projectMinterState = getProjectMinterState({
1660
+ projectId: projectId,
1661
+ coreContract: coreContract
1662
+ });
1663
+ // get project config
1664
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
1665
+ projectId: projectId,
1666
+ coreContract: coreContract
1667
+ });
1668
+ // get auction details
1669
+ auctionTimestampStart = RAMProjectConfig_.timestampStart;
1670
+ auctionTimestampEnd = RAMProjectConfig_.timestampEnd;
1671
+ basePrice = RAMProjectConfig_.basePrice;
1672
+ numTokensInAuction = RAMProjectConfig_.numTokensInAuction;
1673
+ numBids = RAMProjectConfig_.numBids;
1674
+ numBidsMintedTokens = RAMProjectConfig_.numBidsMintedTokens;
1675
+ numBidsErrorRefunded = RAMProjectConfig_.numBidsErrorRefunded;
1676
+ minBidSlotIndex = RAMProjectConfig_.minBidSlotIndex;
1677
+ allowExtraTime = RAMProjectConfig_.allowExtraTime;
1678
+ adminArtistOnlyMintPeriodIfSellout = RAMProjectConfig_
1679
+ .adminArtistOnlyMintPeriodIfSellout;
1680
+ revenuesCollected = RAMProjectConfig_.revenuesCollected;
1681
+ }
1682
+
1683
+ /**
1684
+ * @notice Returns the price information for a given project.
1685
+ * If an auction is not configured, `isConfigured` will be false, and a
1686
+ * dummy price of zero is assigned to `tokenPriceInWei`.
1687
+ * If an auction is configured but still in a pre-auction state,
1688
+ * `isConfigured` will be true, and `tokenPriceInWei` will be the minimum
1689
+ * initial bid price for the next token auction.
1690
+ * If there is an active auction, `isConfigured` will be true, and
1691
+ * `tokenPriceInWei` will be the current minimum bid's value + min bid
1692
+ * increment due to the minter's increment percentage, rounded up to next
1693
+ * slot's bid value.
1694
+ * If there is an auction that has ended (no longer accepting bids), but
1695
+ * the project is configured, `isConfigured` will be true, and
1696
+ * `tokenPriceInWei` will be either the sellout price or the reserve price
1697
+ * of the auction if it did not sell out during its auction.
1698
+ * @param projectId Project ID to get price information for
1699
+ * @param coreContract Core contract address for the given project
1700
+ * @return isConfigured True if the project is configured, false otherwise
1701
+ * @return tokenPriceInWei Price of a token in Wei, if configured
1702
+ */
1703
+ function getPriceInfo(
1704
+ uint256 projectId,
1705
+ address coreContract
1706
+ ) internal view returns (bool isConfigured, uint256 tokenPriceInWei) {
1707
+ // get minter state
1708
+ RAMLib.ProjectMinterStates projectMinterState = getProjectMinterState({
1709
+ projectId: projectId,
1710
+ coreContract: coreContract
1711
+ });
1712
+ // load project config
1713
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
1714
+ projectId: projectId,
1715
+ coreContract: coreContract
1716
+ });
1717
+ // handle pre-auction State A
1718
+ if (projectMinterState == RAMLib.ProjectMinterStates.PreAuction) {
1719
+ isConfigured = RAMProjectConfig_.timestampStart > 0;
1720
+ // if not configured, leave tokenPriceInWei as 0
1721
+ if (isConfigured) {
1722
+ tokenPriceInWei = RAMProjectConfig_.basePrice;
1723
+ }
1724
+ } else {
1725
+ // values that apply to all live-auction and post-auction states
1726
+ isConfigured = true;
1727
+ bool isSellout = RAMProjectConfig_.numBids >=
1728
+ RAMProjectConfig_.numTokensInAuction;
1729
+
1730
+ // handle live-auction State B
1731
+ if (projectMinterState == RAMLib.ProjectMinterStates.LiveAuction) {
1732
+ if (isSellout) {
1733
+ // find next valid bid value
1734
+ // @dev okay if we extend past the maximum slot index value
1735
+ // for this view function
1736
+ (, tokenPriceInWei) = _findNextValidBidSlotIndexAndValue({
1737
+ projectId: projectId,
1738
+ coreContract: coreContract,
1739
+ startSlotIndex: RAMProjectConfig_.minBidSlotIndex
1740
+ });
1741
+ } else {
1742
+ // not sellout, so min bid is base price
1743
+ tokenPriceInWei = RAMProjectConfig_.basePrice;
1744
+ }
1745
+ } else {
1746
+ // handle post-auction States C, D, E
1747
+ if (isSellout) {
1748
+ // if sellout, return min bid price
1749
+ tokenPriceInWei = slotIndexToBidValue({
1750
+ basePrice: RAMProjectConfig_.basePrice,
1751
+ slotIndex: RAMProjectConfig_.minBidSlotIndex
1752
+ });
1753
+ } else {
1754
+ // not sellout, so return base price
1755
+ tokenPriceInWei = RAMProjectConfig_.basePrice;
1756
+ }
1757
+ }
1758
+ }
1759
+ }
1760
+
1761
+ /**
1762
+ * @notice Gets minimum next bid value in Wei and slot index for project `projectId`
1763
+ * on core contract `coreContract`.
1764
+ * If in a pre-auction state, reverts if unconfigured, otherwise returns
1765
+ * the minimum initial bid price for the upcoming auction.
1766
+ * If in an active auction, returns the minimum next bid's value and slot
1767
+ * index.
1768
+ * If in a post-auction state, reverts if auction was a sellout, otherwise
1769
+ * returns the auction's reserve price and slot index 0 (because tokens may
1770
+ * still be purchasable at the reserve price).
1771
+ * @param projectId Project ID to get the minimum next bid value for
1772
+ * @param coreContract Core contract address for the given project
1773
+ * @return minNextBidValueInWei minimum next bid value in Wei
1774
+ * @return minNextBidSlotIndex slot index of the minimum next bid
1775
+ */
1776
+ function getMinimumNextBid(
1777
+ uint256 projectId,
1778
+ address coreContract
1779
+ )
1780
+ internal
1781
+ view
1782
+ returns (uint256 minNextBidValueInWei, uint256 minNextBidSlotIndex)
1783
+ {
1784
+ // get minter state
1785
+ RAMLib.ProjectMinterStates projectMinterState = getProjectMinterState({
1786
+ projectId: projectId,
1787
+ coreContract: coreContract
1788
+ });
1789
+ // load project config
1790
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
1791
+ projectId: projectId,
1792
+ coreContract: coreContract
1793
+ });
1794
+ // handle pre-auction State A
1795
+ if (projectMinterState == RAMLib.ProjectMinterStates.PreAuction) {
1796
+ bool isConfigured = RAMProjectConfig_.timestampStart > 0;
1797
+ if (!isConfigured) {
1798
+ // if not configured, revert
1799
+ revert("auction not configured");
1800
+ }
1801
+ // if configured, min next bid is base price at slot 0
1802
+ minNextBidValueInWei = RAMProjectConfig_.basePrice;
1803
+ minNextBidSlotIndex = 0;
1804
+ } else {
1805
+ // values that apply to all live-auction and post-auction states
1806
+ bool isSellout = RAMProjectConfig_.numBids >=
1807
+ RAMProjectConfig_.numTokensInAuction;
1808
+
1809
+ // handle live-auction State B
1810
+ if (projectMinterState == RAMLib.ProjectMinterStates.LiveAuction) {
1811
+ if (isSellout) {
1812
+ // find next valid bid slot index and value
1813
+ // @dev okay if we extend past the maximum slot index and value
1814
+ // for this view function
1815
+ (
1816
+ minNextBidSlotIndex,
1817
+ minNextBidValueInWei
1818
+ ) = _findNextValidBidSlotIndexAndValue({
1819
+ projectId: projectId,
1820
+ coreContract: coreContract,
1821
+ startSlotIndex: RAMProjectConfig_.minBidSlotIndex
1822
+ });
1823
+ } else {
1824
+ // not sellout, so min bid is base price
1825
+ minNextBidValueInWei = RAMProjectConfig_.basePrice;
1826
+ minNextBidSlotIndex = 0;
1827
+ }
1828
+ } else {
1829
+ // handle post-auction States C, D, E
1830
+ if (isSellout) {
1831
+ // if sellout, revert
1832
+ revert("auction ended, sellout");
1833
+ } else {
1834
+ // not sellout, so return base price
1835
+ minNextBidValueInWei = RAMProjectConfig_.basePrice;
1836
+ minNextBidSlotIndex = 0;
1837
+ }
1838
+ }
1839
+ }
1840
+ }
1841
+
1842
+ /**
1843
+ * @notice Gets the project minter state of project `projectId` on core
1844
+ * contract `coreContract`.
1845
+ * @param projectId Project ID to get the minimum next bid value for
1846
+ * @param coreContract Core contract address for the given project
1847
+ * @return ProjectMinterStates enum representing the minter state.
1848
+ */
1849
+ function getProjectMinterState(
1850
+ uint256 projectId,
1851
+ address coreContract
1852
+ ) internal view returns (ProjectMinterStates) {
1853
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
1854
+ projectId: projectId,
1855
+ coreContract: coreContract
1856
+ });
1857
+ // State A: Pre-Auction
1858
+ // @dev load to memory for gas efficiency
1859
+ uint256 timestampStart = RAMProjectConfig_.timestampStart;
1860
+ // helper value(s) for readability
1861
+ bool auctionIsConfigured = timestampStart > 0;
1862
+ bool isPreAuction = block.timestamp < timestampStart;
1863
+ // confirm that auction is either not configured or is pre-auction
1864
+ if ((!auctionIsConfigured) || isPreAuction) {
1865
+ return ProjectMinterStates.PreAuction;
1866
+ }
1867
+ // State B: Live-Auction
1868
+ // @dev auction is configured due to previous State A return
1869
+ // helper value(s) for readability
1870
+ // @dev load to memory for gas efficiency
1871
+ uint256 timestampEnd = RAMProjectConfig_.timestampEnd;
1872
+ bool isPostAuction = block.timestamp > timestampEnd;
1873
+ // pre-auction is checked above
1874
+ if (!isPostAuction) {
1875
+ return ProjectMinterStates.LiveAuction;
1876
+ }
1877
+ // States C, D, E: Post-Auction
1878
+ // @dev auction is configured and post auction due to previous States A, B returns
1879
+ // all winners sent tokens means all bids have either been minted tokens or refunded if error state occurred
1880
+ bool allBidsHandled = RAMProjectConfig_.numBidsMintedTokens +
1881
+ RAMProjectConfig_.numBidsErrorRefunded ==
1882
+ RAMProjectConfig_.numBids;
1883
+ if (allBidsHandled) {
1884
+ // State E: Post-Auction, all bids handled
1885
+ return ProjectMinterStates.PostAuctionAllBidsHandled;
1886
+ }
1887
+ // @dev all bids are not handled due to previous State E return
1888
+ bool adminOnlyMintPeriod = RAMProjectConfig_
1889
+ // @dev if project is configured to have an admin-artist-only mint period
1890
+ .adminArtistOnlyMintPeriodIfSellout &&
1891
+ // @dev sellout if numBids >= numTokensInAuction
1892
+ RAMProjectConfig_.numBids >= RAMProjectConfig_.numTokensInAuction &&
1893
+ // @dev still in admin-artist-only mint period if current time < end time + admin-artist-only mint period
1894
+ block.timestamp <
1895
+ timestampEnd + ADMIN_ARTIST_ONLY_MINT_TIME_SECONDS;
1896
+ if (adminOnlyMintPeriod) {
1897
+ // State C: Post-Auction, sell out, not all bids handled, admin-artist-only mint period
1898
+ return ProjectMinterStates.PostAuctionSellOutAdminArtistMint;
1899
+ }
1900
+ // State D: Post-Auction, not all bids handled, post-admin-artist-only mint period
1901
+ // @dev states are mutually exclusive, so must be in final remaining state
1902
+ return ProjectMinterStates.PostAuctionOpenMint;
1903
+ }
1904
+
1905
+ /**
1906
+ * @notice Returns if project minter is in ERROR state E1, and the number
1907
+ * of bids that need to be refunded to resolve the error. Also returns the
1908
+ * number of excess invocations available, if any, indicating Flag F1.
1909
+ * E1: Tokens owed > invocations available
1910
+ * Occurs when: tokens are minted on different minter after auction begins,
1911
+ * or when core contract max invocations are reduced after auction begins.
1912
+ * Resolution: Admin must refund the lowest bids after auction ends.
1913
+ * @param projectId Project Id to query
1914
+ * @param coreContract Core contract address to query
1915
+ * @return isError True if in error state, false otherwise
1916
+ * @return numBidsToRefund Number of bids to refund to resolve error, 0 if
1917
+ * not in error state
1918
+ * @return numExcessInvocationsAvailable Number of excess invocations
1919
+ * available. Value above 0 indicates Flag F1.
1920
+ */
1921
+ function isErrorE1FlagF1(
1922
+ uint256 projectId,
1923
+ address coreContract
1924
+ )
1925
+ internal
1926
+ view
1927
+ returns (
1928
+ bool isError,
1929
+ uint256 numBidsToRefund,
1930
+ uint256 numExcessInvocationsAvailable
1931
+ )
1932
+ {
1933
+ // get project config
1934
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
1935
+ projectId: projectId,
1936
+ coreContract: coreContract
1937
+ });
1938
+ // E1: Tokens owed > invocations available
1939
+ uint256 tokensOwed = _getNumTokensOwed({
1940
+ RAMProjectConfig_: RAMProjectConfig_
1941
+ });
1942
+ uint256 invocationsAvailable = MaxInvocationsLib
1943
+ .getInvocationsAvailable({
1944
+ projectId: projectId,
1945
+ coreContract: coreContract
1946
+ });
1947
+ // populate return values
1948
+ isError = tokensOwed > invocationsAvailable;
1949
+ numBidsToRefund = isError ? tokensOwed - invocationsAvailable : 0;
1950
+ // no excess invocations available if in error state, otherwise is the
1951
+ // difference between invocations available and tokens owed
1952
+ numExcessInvocationsAvailable = isError
1953
+ ? 0
1954
+ : invocationsAvailable - tokensOwed;
1955
+ }
1956
+
1957
+ /**
1958
+ * @notice Returns the MaxInvocationsProjectConfig for a given project and
1959
+ * core contract, properly accounting for the auction state, unminted bids,
1960
+ * core contract invocations, and minter max invocations when determining
1961
+ * maxHasBeenInvoked
1962
+ * @param projectId Project Id to get config for
1963
+ * @param coreContract Core contract address to get config for
1964
+ * @return maxInvocationsProjectConfig max invocations project configuration
1965
+ */
1966
+ function getMaxInvocationsProjectConfig(
1967
+ uint256 projectId,
1968
+ address coreContract
1969
+ )
1970
+ internal
1971
+ view
1972
+ returns (
1973
+ MaxInvocationsLib.MaxInvocationsProjectConfig
1974
+ memory maxInvocationsProjectConfig
1975
+ )
1976
+ {
1977
+ // get max invocations project config from MaxInvocationsLib
1978
+ maxInvocationsProjectConfig.maxInvocations = uint24(
1979
+ MaxInvocationsLib.getMaxInvocations({
1980
+ projectId: projectId,
1981
+ coreContract: coreContract
1982
+ })
1983
+ );
1984
+ maxInvocationsProjectConfig.maxHasBeenInvoked = getMaxHasBeenInvoked({
1985
+ projectId: projectId,
1986
+ coreContract: coreContract
1987
+ });
1988
+ }
1989
+
1990
+ /**
1991
+ * @notice Returns if project has reached maximum number of invocations for
1992
+ * a given project and core contract, properly accounting for the auction
1993
+ * state, unminted bids, core contract invocations, and minter max
1994
+ * invocations when determining maxHasBeenInvoked
1995
+ * @param projectId Project Id to get config for
1996
+ * @param coreContract Core contract address to get config for
1997
+ * @return maxHasBeenInvoked bool indicating if max invocations have been invoked
1998
+ */
1999
+ function getMaxHasBeenInvoked(
2000
+ uint256 projectId,
2001
+ address coreContract
2002
+ ) internal view returns (bool maxHasBeenInvoked) {
2003
+ // calculate if max has been invoked based on auction state
2004
+ ProjectMinterStates projectMinterState = getProjectMinterState({
2005
+ projectId: projectId,
2006
+ coreContract: coreContract
2007
+ });
2008
+ if (projectMinterState == ProjectMinterStates.PreAuction) {
2009
+ // pre-auction, true if numTokensInAuction == 0
2010
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
2011
+ projectId: projectId,
2012
+ coreContract: coreContract
2013
+ });
2014
+ if (RAMProjectConfig_.timestampStart > 0) {
2015
+ // if auction configured, look at num tokens in auction
2016
+ maxHasBeenInvoked = RAMProjectConfig_.numTokensInAuction == 0;
2017
+ } else {
2018
+ // if auction not configured, defer to max invocation lib
2019
+ maxHasBeenInvoked = MaxInvocationsLib.getMaxHasBeenInvoked({
2020
+ projectId: projectId,
2021
+ coreContract: coreContract
2022
+ });
2023
+ }
2024
+ } else if (projectMinterState == ProjectMinterStates.LiveAuction) {
2025
+ // live auction, set to true if num bids >= num tokens in auction
2026
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
2027
+ projectId: projectId,
2028
+ coreContract: coreContract
2029
+ });
2030
+ maxHasBeenInvoked =
2031
+ RAMProjectConfig_.numBids >=
2032
+ RAMProjectConfig_.numTokensInAuction;
2033
+ } else {
2034
+ // post auction, set to true if remaining excess invocations is zero
2035
+ (, , uint256 numExcessInvocationsAvailable) = isErrorE1FlagF1({
2036
+ projectId: projectId,
2037
+ coreContract: coreContract
2038
+ });
2039
+ maxHasBeenInvoked = numExcessInvocationsAvailable == 0;
2040
+ }
2041
+ }
2042
+
2043
+ /**
2044
+ * Returns balance of project `projectId` on core contract `coreContract`
2045
+ * on this minter contract.
2046
+ * @param projectId Project ID to get the balance for
2047
+ * @param coreContract Core contract address for the given project
2048
+ */
2049
+ function getProjectBalance(
2050
+ uint256 projectId,
2051
+ address coreContract
2052
+ ) internal view returns (uint256) {
2053
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
2054
+ projectId: projectId,
2055
+ coreContract: coreContract
2056
+ });
2057
+ return RAMProjectConfig_.projectBalance;
2058
+ }
2059
+
2060
+ /**
2061
+ * Loads the RAMProjectConfig for a given project and core
2062
+ * contract.
2063
+ * @param projectId Project Id to get config for
2064
+ * @param coreContract Core contract address to get config for
2065
+ */
2066
+ function getRAMProjectConfig(
2067
+ uint256 projectId,
2068
+ address coreContract
2069
+ ) internal view returns (RAMProjectConfig storage) {
2070
+ return s().RAMProjectConfigs[coreContract][projectId];
2071
+ }
2072
+
2073
+ /**
2074
+ * Loads the RAMAdminMintingConstraint for a given core contract.
2075
+ * @param coreContract Core contract address to get config for
2076
+ */
2077
+ function getRAMAdminMintingConstraintValue(
2078
+ address coreContract
2079
+ ) internal view returns (AdminMintingConstraint) {
2080
+ return s().RAMAdminMintingConstraint[coreContract];
2081
+ }
2082
+
2083
+ /**
2084
+ * @notice private helper function to mint a token.
2085
+ * @dev assumes all checks have been performed
2086
+ * @param projectId project ID to mint token for
2087
+ * @param coreContract core contract address for the given project
2088
+ * @param bidId bid ID of bid to mint token for
2089
+ * @param bidder bidder address of bid to mint token for
2090
+ * @param minterFilter minter filter contract address
2091
+ */
2092
+ function _mintTokenForBid(
2093
+ uint256 projectId,
2094
+ address coreContract,
2095
+ uint32 bidId,
2096
+ address bidder,
2097
+ IMinterFilterV1 minterFilter
2098
+ ) private {
2099
+ // mint token
2100
+ uint256 tokenId = minterFilter.mint_joo({
2101
+ to: bidder,
2102
+ projectId: projectId,
2103
+ coreContract: coreContract,
2104
+ sender: msg.sender
2105
+ });
2106
+ // emit event for state change
2107
+ emit BidMinted({
2108
+ projectId: projectId,
2109
+ coreContract: coreContract,
2110
+ bidId: bidId,
2111
+ tokenId: tokenId
2112
+ });
2113
+ }
2114
+
2115
+ /**
2116
+ * @notice private helper function to mint and settle bid if not already settled.
2117
+ * Assumes check that bidder for bid `bidId` is not null
2118
+ * @param projectId Project ID to mint token on.
2119
+ * @param coreContract Core contract address for the given project.
2120
+ * @param projectPrice Price of a token for the given project.
2121
+ * @param slotIndex Slot index of bid.
2122
+ * @param bidId ID of bid to settle.
2123
+ * @param minterFilter Minter filter contract address
2124
+ * @param minterRefundGasLimit Gas limit to use when settling bid, prior to using fallback force-send to refund
2125
+ */
2126
+ function _mintAndSettle(
2127
+ uint256 projectId,
2128
+ address coreContract,
2129
+ uint256 projectPrice,
2130
+ uint256 slotIndex,
2131
+ uint256 bidId,
2132
+ IMinterFilterV1 minterFilter,
2133
+ uint256 minterRefundGasLimit
2134
+ ) private {
2135
+ // load project config
2136
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
2137
+ projectId: projectId,
2138
+ coreContract: coreContract
2139
+ });
2140
+ Bid storage bid = RAMProjectConfig_.bids[bidId];
2141
+ // Mark bid as minted
2142
+ _setBidPackedBool({bid: bid, index: INDEX_IS_MINTED, value: true});
2143
+
2144
+ // Mint token for bid
2145
+ _mintTokenForBid({
2146
+ projectId: projectId,
2147
+ coreContract: coreContract,
2148
+ bidId: uint32(bidId),
2149
+ bidder: bid.bidder,
2150
+ minterFilter: minterFilter
2151
+ });
2152
+
2153
+ // Settle if not already settled
2154
+ // @dev collector could have previously settled bid, so need to
2155
+ // settle only if not already settled
2156
+ if (!(_getBidPackedBool(bid, INDEX_IS_SETTLED))) {
2157
+ _settleBid({
2158
+ RAMProjectConfig_: RAMProjectConfig_,
2159
+ projectId: projectId,
2160
+ coreContract: coreContract,
2161
+ projectPrice: projectPrice,
2162
+ slotIndex: slotIndex,
2163
+ bidId: uint32(bidId),
2164
+ minterRefundGasLimit: minterRefundGasLimit
2165
+ });
2166
+ }
2167
+ }
2168
+
2169
+ /**
2170
+ * @notice Helper function to handle settling a bid.
2171
+ * Reverts if bidder is not the bid's bidder.
2172
+ * Reverts if bid has already been settled.
2173
+ * @param RAMProjectConfig_ RAMProjectConfig to update
2174
+ * @param projectId Project ID of bid to settle
2175
+ * @param coreContract Core contract address for the given project.
2176
+ * @param projectPrice Price of token on the project
2177
+ * @param bidId ID of bid to settle
2178
+ * @param bidder Bidder address of bid to settle
2179
+ * @param minterRefundGasLimit Gas limit to use when refunding the bidder.
2180
+ */
2181
+ function _settleBidWithChecks(
2182
+ RAMProjectConfig storage RAMProjectConfig_,
2183
+ uint256 projectId,
2184
+ address coreContract,
2185
+ uint256 projectPrice,
2186
+ uint32 bidId,
2187
+ address bidder,
2188
+ uint256 minterRefundGasLimit
2189
+ ) private {
2190
+ // CHECKS
2191
+ Bid storage bid = RAMProjectConfig_.bids[bidId];
2192
+ // require bidder is the bid's bidder
2193
+ require(bid.bidder == bidder, "Only bidder");
2194
+ // require bid is not yet settled
2195
+ require(
2196
+ !(_getBidPackedBool(bid, INDEX_IS_SETTLED)),
2197
+ "Only un-settled bid"
2198
+ );
2199
+
2200
+ _settleBid({
2201
+ RAMProjectConfig_: RAMProjectConfig_,
2202
+ projectId: projectId,
2203
+ coreContract: coreContract,
2204
+ slotIndex: bid.slotIndex,
2205
+ bidId: bidId,
2206
+ projectPrice: projectPrice,
2207
+ minterRefundGasLimit: minterRefundGasLimit
2208
+ });
2209
+ }
2210
+
2211
+ /**
2212
+ * @notice private helper function to handle settling a bid.
2213
+ * @dev assumes bid has not been previously settled, and that all other
2214
+ * checks have been performed.
2215
+ * @param RAMProjectConfig_ RAMProjectConfig to update
2216
+ * @param projectId Project ID of bid to settle
2217
+ * @param coreContract Core contract address for the given project.
2218
+ * @param slotIndex Slot index of bid to settle
2219
+ * @param bidId ID of bid to settle
2220
+ * @param projectPrice Price of token on the project
2221
+ * @param minterRefundGasLimit Gas limit to use when refunding the previous
2222
+ * highest bidder, prior to using fallback force-send to refund
2223
+ */
2224
+ function _settleBid(
2225
+ RAMProjectConfig storage RAMProjectConfig_,
2226
+ uint256 projectId,
2227
+ address coreContract,
2228
+ uint256 slotIndex,
2229
+ uint32 bidId,
2230
+ uint256 projectPrice,
2231
+ uint256 minterRefundGasLimit
2232
+ ) private {
2233
+ // @dev bid not passed as parameter to avoid stack too deep error in
2234
+ // functions that utilize this helper function
2235
+ Bid storage bid = RAMProjectConfig_.bids[bidId];
2236
+ // EFFECTS
2237
+ // update state
2238
+ _setBidPackedBool({bid: bid, index: INDEX_IS_SETTLED, value: true});
2239
+ // amount due = bid amount - project price
2240
+ uint256 amountDue = slotIndexToBidValue({
2241
+ basePrice: RAMProjectConfig_.basePrice,
2242
+ // @dev safe to cast to uint16
2243
+ slotIndex: uint16(slotIndex)
2244
+ }) - projectPrice;
2245
+ if (amountDue > 0) {
2246
+ // force-send settlement to bidder
2247
+ // @dev reverts on underflow
2248
+ RAMProjectConfig_.projectBalance -= uint120(amountDue);
2249
+ SplitFundsLib.forceSafeTransferETH({
2250
+ to: bid.bidder,
2251
+ amount: amountDue,
2252
+ minterRefundGasLimit: minterRefundGasLimit
2253
+ });
2254
+ }
2255
+ // emit event for state change
2256
+ emit BidSettled({
2257
+ projectId: projectId,
2258
+ coreContract: coreContract,
2259
+ bidId: bidId
2260
+ });
2261
+ }
2262
+
2263
+ /**
2264
+ * @notice private helper function to settle and refund bids to resolve E1 error state.
2265
+ * Assumes check that bidder for bid `bidId` is not null
2266
+ * @param projectId Project ID to refund bid for.
2267
+ * @param coreContract Core contract address for the given project.
2268
+ * @param projectPrice Price of a token for the given project.
2269
+ * @param slotIndex Slot index of bid.
2270
+ * @param bidId ID of bid to settle.
2271
+ * @param minterRefundGasLimit Gas limit to use when refunding bidder
2272
+ */
2273
+ function _settleAndRefundBid(
2274
+ uint256 projectId,
2275
+ address coreContract,
2276
+ uint256 projectPrice,
2277
+ uint256 slotIndex,
2278
+ uint256 bidId,
2279
+ uint256 minterRefundGasLimit
2280
+ ) private {
2281
+ // load project config
2282
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
2283
+ projectId: projectId,
2284
+ coreContract: coreContract
2285
+ });
2286
+ // load bid
2287
+ Bid storage bid = RAMProjectConfig_.bids[bidId];
2288
+ // @dev bidderAddress previously checked not null
2289
+ address bidderAddress = bid.bidder;
2290
+ // Settle and refund the Bid
2291
+ // Minimum value to send is the project price
2292
+ uint256 valueToSend = projectPrice;
2293
+ bool didSettleBid = false;
2294
+
2295
+ // if not isSettled, then settle the bid
2296
+ if (!_getBidPackedBool(bid, INDEX_IS_SETTLED)) {
2297
+ // mark bid as settled
2298
+ _setBidPackedBool({bid: bid, index: INDEX_IS_SETTLED, value: true});
2299
+ didSettleBid = true;
2300
+ // send entire bid value if not previously settled
2301
+ valueToSend = slotIndexToBidValue({
2302
+ basePrice: RAMProjectConfig_.basePrice,
2303
+ slotIndex: uint16(slotIndex)
2304
+ });
2305
+ }
2306
+ // mark bid as refunded
2307
+ _setBidPackedBool({bid: bid, index: INDEX_IS_REFUNDED, value: true});
2308
+ // INTERACTIONS
2309
+ // force-send refund to bidder
2310
+ // @dev reverts on underflow
2311
+ RAMProjectConfig_.projectBalance -= uint120(valueToSend);
2312
+ SplitFundsLib.forceSafeTransferETH({
2313
+ to: bidderAddress,
2314
+ amount: valueToSend,
2315
+ minterRefundGasLimit: minterRefundGasLimit
2316
+ });
2317
+ // emit event for state changes
2318
+ if (didSettleBid) {
2319
+ emit BidSettled({
2320
+ projectId: projectId,
2321
+ coreContract: coreContract,
2322
+ bidId: bidId
2323
+ });
2324
+ }
2325
+ emit BidRefunded({
2326
+ projectId: projectId,
2327
+ coreContract: coreContract,
2328
+ bidId: bidId
2329
+ });
2330
+ }
2331
+
2332
+ /**
2333
+ * @notice Helper function to get the price of a token on a project.
2334
+ * @dev Assumes project is configured, has a base price, and generally
2335
+ * makes sense to get a price for.
2336
+ * @param RAMProjectConfig_ RAMProjectConfig to query
2337
+ */
2338
+ function _getProjectPrice(
2339
+ RAMProjectConfig storage RAMProjectConfig_
2340
+ ) private view returns (uint256 projectPrice) {
2341
+ bool wasSellout = RAMProjectConfig_.numBids >=
2342
+ RAMProjectConfig_.numTokensInAuction;
2343
+ // price is lowest bid if sellout, otherwise base price
2344
+ projectPrice = wasSellout
2345
+ ? slotIndexToBidValue({
2346
+ basePrice: RAMProjectConfig_.basePrice,
2347
+ slotIndex: RAMProjectConfig_.minBidSlotIndex
2348
+ })
2349
+ : RAMProjectConfig_.basePrice;
2350
+ }
2351
+
2352
+ /**
2353
+ * @notice Helper function to get the number of tokens owed for a given
2354
+ * project.
2355
+ * @param RAMProjectConfig_ RAMProjectConfig to query
2356
+ * @return tokensOwed The number of bids in a project minus the sum of tokens already
2357
+ * minted and bids that have been refunded due to an error state.
2358
+ */
2359
+ function _getNumTokensOwed(
2360
+ RAMProjectConfig storage RAMProjectConfig_
2361
+ ) private view returns (uint256 tokensOwed) {
2362
+ tokensOwed =
2363
+ RAMProjectConfig_.numBids -
2364
+ (RAMProjectConfig_.numBidsMintedTokens +
2365
+ RAMProjectConfig_.numBidsErrorRefunded);
2366
+ }
2367
+
2368
+ /**
2369
+ * @notice Inserts a bid into the project's RAMProjectConfig.
2370
+ * Assumes the bid is valid and may be inserted into the bucket-sort data
2371
+ * structure.
2372
+ * Creates a new bid if bidId is zero, otherwise moves an existing bid,
2373
+ * which is assumed to exist and be valid.
2374
+ * Emits BidCreated event if a new bid is created.
2375
+ * @dev assumes slot index is valid and < NUM_SLOTS
2376
+ * @param RAMProjectConfig_ RAM project config to insert bid into
2377
+ * @param projectId Project ID to insert bid for
2378
+ * @param coreContract Core contract address to insert bid for
2379
+ * @param slotIndex Slot index to insert bid at
2380
+ * @param bidder Bidder address
2381
+ * @param bidId Bid ID to insert, or zero if a new bid should be created
2382
+ */
2383
+ function _insertBid(
2384
+ RAMProjectConfig storage RAMProjectConfig_,
2385
+ uint256 projectId,
2386
+ address coreContract,
2387
+ uint16 slotIndex,
2388
+ address bidder,
2389
+ uint32 bidId
2390
+ ) private {
2391
+ // add the new Bid to tail of the slot's doubly linked list
2392
+ bool createNewBid = bidId == 0;
2393
+ if (createNewBid) {
2394
+ // prefix ++ to skip initial bid ID of zero (indicates null value)
2395
+ bidId = ++RAMProjectConfig_.nextBidId;
2396
+ }
2397
+ uint256 prevTailBidId = RAMProjectConfig_.tailBidIdBySlot[slotIndex];
2398
+ RAMProjectConfig_.bids[bidId] = Bid({
2399
+ prevBidId: uint32(prevTailBidId),
2400
+ nextBidId: 0, // null value at end of tail
2401
+ slotIndex: slotIndex,
2402
+ bidder: bidder,
2403
+ packedBools: 0 // all packed bools false
2404
+ });
2405
+ // update tail pointer to new bid
2406
+ RAMProjectConfig_.tailBidIdBySlot[slotIndex] = bidId;
2407
+ // update head pointer or next pointer of previous bid
2408
+ if (prevTailBidId == 0) {
2409
+ // first bid in slot, update head pointer
2410
+ RAMProjectConfig_.headBidIdBySlot[slotIndex] = bidId;
2411
+ } else {
2412
+ // update previous bid's next pointer
2413
+ RAMProjectConfig_.bids[prevTailBidId].nextBidId = bidId;
2414
+ }
2415
+
2416
+ // update number of active bids
2417
+ RAMProjectConfig_.numBids++;
2418
+ // update metadata if first bid for this slot
2419
+ // @dev assumes minting has not yet started
2420
+ if (prevTailBidId == 0) {
2421
+ // set the slot in the bitmap
2422
+ _setBitmapSlot({
2423
+ RAMProjectConfig_: RAMProjectConfig_,
2424
+ slotIndex: slotIndex
2425
+ });
2426
+ // update bitmap metadata - reduce min bid index if necessary
2427
+ if (slotIndex < RAMProjectConfig_.minBidSlotIndex) {
2428
+ RAMProjectConfig_.minBidSlotIndex = slotIndex;
2429
+ }
2430
+ // update bitmap metadata - increase max bid index if necessary
2431
+ if (slotIndex > RAMProjectConfig_.maxBidSlotIndex) {
2432
+ RAMProjectConfig_.maxBidSlotIndex = slotIndex;
2433
+ }
2434
+ }
2435
+
2436
+ if (createNewBid) {
2437
+ // emit state change event
2438
+ emit BidCreated({
2439
+ projectId: projectId,
2440
+ coreContract: coreContract,
2441
+ slotIndex: slotIndex,
2442
+ bidId: bidId,
2443
+ bidder: bidder
2444
+ });
2445
+ }
2446
+ }
2447
+
2448
+ /**
2449
+ * @notice Remove minimum bid from the project's RAMProjectConfig.
2450
+ * Reverts if no bids exist in slot RAMProjectConfig_.minBidSlotIndex.
2451
+ * @param RAMProjectConfig_ RAM project config to remove bid from
2452
+ * @param projectId Project ID to remove bid from
2453
+ * @param coreContract Core contract address for the given project
2454
+ * @param minterRefundGasLimit Gas limit to use when refunding the previous
2455
+ * highest bidder, prior to using fallback force-send to refund
2456
+ * @return removedBidAmount The value of the removed bid, in Wei
2457
+ */
2458
+ function _removeMinBid(
2459
+ RAMProjectConfig storage RAMProjectConfig_,
2460
+ uint256 projectId,
2461
+ address coreContract,
2462
+ uint256 minterRefundGasLimit
2463
+ ) private returns (uint256 removedBidAmount) {
2464
+ // get the minimum bid slot and bid id
2465
+ uint16 removedSlotIndex = RAMProjectConfig_.minBidSlotIndex;
2466
+ uint256 removedBidId = RAMProjectConfig_.tailBidIdBySlot[
2467
+ removedSlotIndex
2468
+ ];
2469
+ // @dev no coverage on else branch because it is unreachable as used
2470
+ require(removedBidId > 0, "No bids");
2471
+ // record the previous min bidder
2472
+ Bid storage removedBid = RAMProjectConfig_.bids[removedBidId];
2473
+ address removedBidder = removedBid.bidder;
2474
+ // update the tail pointer of the slot's doubly linked list
2475
+ uint32 newTailBidId = removedBid.prevBidId;
2476
+ RAMProjectConfig_.tailBidIdBySlot[removedSlotIndex] = newTailBidId;
2477
+
2478
+ RAMProjectConfig_.numBids--;
2479
+ // update metadata if no more active bids for this slot
2480
+ if (newTailBidId == 0) {
2481
+ // update the head pointer of the slot's doubly linked list
2482
+ RAMProjectConfig_.headBidIdBySlot[removedSlotIndex] = 0;
2483
+
2484
+ // unset the slot in the bitmap
2485
+ // update minBidIndex, efficiently starting at minBidSlotIndex + 1
2486
+ _unsetBitmapSlot({
2487
+ RAMProjectConfig_: RAMProjectConfig_,
2488
+ slotIndex: removedSlotIndex
2489
+ });
2490
+ // @dev reverts if removedSlotIndex was the maximum slot 511,
2491
+ // preventing bids from being removed entirely from the last slot,
2492
+ // which is acceptable and non-impacting for this minter
2493
+ // @dev sets minBidSlotIndex to 512 if no more active bids, which
2494
+ // is desired behavior for this minter
2495
+ RAMProjectConfig_.minBidSlotIndex = _getMinSlotWithBid({
2496
+ RAMProjectConfig_: RAMProjectConfig_,
2497
+ startSlotIndex: removedSlotIndex + 1
2498
+ });
2499
+ } else {
2500
+ // if the removed bid was not the head, then unset the nextBidId pointer of the bid
2501
+ Bid storage newTailBid = RAMProjectConfig_.bids[newTailBidId];
2502
+ newTailBid.nextBidId = 0;
2503
+ }
2504
+ // refund the removed bidder
2505
+ removedBidAmount = slotIndexToBidValue({
2506
+ basePrice: RAMProjectConfig_.basePrice,
2507
+ slotIndex: removedSlotIndex
2508
+ });
2509
+ // @dev reverts on underflow
2510
+ RAMProjectConfig_.projectBalance -= uint120(removedBidAmount);
2511
+
2512
+ // delete the removed bid to prevent future claiming
2513
+ // @dev performed last to avoid pointing to deleted bid struct
2514
+ delete RAMProjectConfig_.bids[removedBidId];
2515
+
2516
+ SplitFundsLib.forceSafeTransferETH({
2517
+ to: removedBidder,
2518
+ amount: removedBidAmount,
2519
+ minterRefundGasLimit: minterRefundGasLimit
2520
+ });
2521
+ // emit state change event
2522
+ emit BidRemoved({
2523
+ projectId: projectId,
2524
+ coreContract: coreContract,
2525
+ bidId: removedBidId
2526
+ });
2527
+ }
2528
+
2529
+ /**
2530
+ * @notice Ejects a bid from the project's RAMProjectConfig.
2531
+ * Assumes the bid is valid (i.e. bid ID is a valid, active bid).
2532
+ * Does not refund the bidder, does not emit events, does not delete Bid.
2533
+ * @param RAMProjectConfig_ RAM project config to eject bid from
2534
+ * @param slotIndex Slot index to eject bid from
2535
+ * @param bidId ID of bid to eject
2536
+ */
2537
+ function _ejectBidFromSlot(
2538
+ RAMProjectConfig storage RAMProjectConfig_,
2539
+ uint16 slotIndex,
2540
+ uint256 bidId
2541
+ ) private {
2542
+ // get the bid to remove
2543
+ Bid storage removedBid = RAMProjectConfig_.bids[bidId];
2544
+ uint32 prevBidId = removedBid.prevBidId;
2545
+ uint32 nextBidId = removedBid.nextBidId;
2546
+ // update previous bid's next pointer
2547
+ if (prevBidId == 0) {
2548
+ // removed bid was the head bid
2549
+ RAMProjectConfig_.headBidIdBySlot[slotIndex] = nextBidId;
2550
+ } else {
2551
+ // removed bid was not the head bid
2552
+ RAMProjectConfig_.bids[prevBidId].nextBidId = nextBidId;
2553
+ }
2554
+ // update next bid's previous pointer
2555
+ if (nextBidId == 0) {
2556
+ // removed bid was the tail bid
2557
+ RAMProjectConfig_.tailBidIdBySlot[slotIndex] = prevBidId;
2558
+ } else {
2559
+ // removed bid was not the tail bid
2560
+ RAMProjectConfig_.bids[nextBidId].prevBidId = prevBidId;
2561
+ }
2562
+
2563
+ // decrement the number of active bids
2564
+ RAMProjectConfig_.numBids--;
2565
+
2566
+ // update metadata if no more active bids for this slot
2567
+ if (prevBidId == 0 && nextBidId == 0) {
2568
+ // unset the slot in the bitmap
2569
+ // update minBidIndex, efficiently starting at minBidSlotIndex + 1
2570
+ _unsetBitmapSlot({
2571
+ RAMProjectConfig_: RAMProjectConfig_,
2572
+ slotIndex: slotIndex
2573
+ });
2574
+ // @dev reverts if removedSlotIndex was the maximum slot 511,
2575
+ // preventing bids from being removed entirely from the last slot,
2576
+ // which is acceptable and non-impacting for this minter
2577
+ // @dev sets minBidSlotIndex to 512 if no more active bids, which
2578
+ // is desired behavior for this minter
2579
+ if (RAMProjectConfig_.minBidSlotIndex == slotIndex) {
2580
+ RAMProjectConfig_.minBidSlotIndex = _getMinSlotWithBid({
2581
+ RAMProjectConfig_: RAMProjectConfig_,
2582
+ startSlotIndex: slotIndex + 1
2583
+ });
2584
+ }
2585
+ }
2586
+
2587
+ // @dev do not refund, do not emit event, do not delete bid
2588
+ }
2589
+
2590
+ /**
2591
+ * @notice Helper function to handle setting slot in 512-bit bitmap
2592
+ * Reverts if slotIndex > 511
2593
+ * @param slotIndex Index of slot to set (between 0 and 511)
2594
+ * @param RAMProjectConfig_ RAMProjectConfig to update
2595
+ */
2596
+ function _setBitmapSlot(
2597
+ RAMProjectConfig storage RAMProjectConfig_,
2598
+ uint256 slotIndex
2599
+ ) private {
2600
+ // revert if slotIndex >= NUM_SLOTS, since this is an invalid input
2601
+ // @dev no coverage as slot index out of range checked in placeBid and implicitly in topUpBid
2602
+ require(slotIndex < NUM_SLOTS, "Only slot index lt NUM_SLOTS");
2603
+ // set the slot in the bitmap
2604
+ if (slotIndex < 256) {
2605
+ // @dev <256 conditional ensures no overflow when casting to uint8
2606
+ RAMProjectConfig_.slotsBitmapA = RAMProjectConfig_.slotsBitmapA.set(
2607
+ uint8(slotIndex)
2608
+ );
2609
+ } else {
2610
+ // @dev <512 results in no overflow when casting to uint8
2611
+ RAMProjectConfig_.slotsBitmapB = RAMProjectConfig_.slotsBitmapB.set(
2612
+ // @dev casting to uint8 intentional overflow instead of
2613
+ // subtracting 256 from slotIndex
2614
+ uint8(slotIndex)
2615
+ );
2616
+ }
2617
+ }
2618
+
2619
+ /**
2620
+ * @notice Helper function to handle unsetting slot in 512-bit bitmap
2621
+ * Reverts if slotIndex > 511
2622
+ * @param slotIndex Index of slot to set (between 0 and 511)
2623
+ * @param RAMProjectConfig_ RAMProjectConfig to update
2624
+ */
2625
+ function _unsetBitmapSlot(
2626
+ RAMProjectConfig storage RAMProjectConfig_,
2627
+ uint256 slotIndex
2628
+ ) private {
2629
+ // revert if slotIndex >= NUM_SLOTS, since this is an invalid input
2630
+ // @dev no coverage as slot index out of range checked in placeBid and implicitly in topUpBid
2631
+ require(slotIndex < NUM_SLOTS, "Only slot index lt NUM_SLOTS");
2632
+ // unset the slot in the bitmap
2633
+ if (slotIndex < 256) {
2634
+ // @dev <256 conditional ensures no overflow when casting to uint8
2635
+ RAMProjectConfig_.slotsBitmapA = RAMProjectConfig_
2636
+ .slotsBitmapA
2637
+ .unset(uint8(slotIndex));
2638
+ } else {
2639
+ // @dev <512 results in no overflow when casting to uint8
2640
+ RAMProjectConfig_.slotsBitmapB = RAMProjectConfig_
2641
+ .slotsBitmapB
2642
+ .unset(
2643
+ // @dev casting to uint8 intentional overflow instead of
2644
+ // subtracting 256 from slotIndex
2645
+ uint8(slotIndex)
2646
+ );
2647
+ }
2648
+ }
2649
+
2650
+ /**
2651
+ * @notice Helper function to set a packed boolean in a Bid struct.
2652
+ * @param bid Bid to update
2653
+ * @param index Index of packed boolean to update
2654
+ * @param value Value to set packed boolean to
2655
+ */
2656
+ function _setBidPackedBool(
2657
+ Bid storage bid,
2658
+ uint8 index,
2659
+ bool value
2660
+ ) private {
2661
+ // @dev no coverage on else branch because it is unreachable as used
2662
+ if (value) {
2663
+ bid.packedBools = uint8(
2664
+ uint256(bid.packedBools).setBoolTrue(index)
2665
+ );
2666
+ } else {
2667
+ bid.packedBools = uint8(
2668
+ uint256(bid.packedBools).setBoolFalse(index)
2669
+ );
2670
+ }
2671
+ }
2672
+
2673
+ /**
2674
+ * @notice Helper function to get a packed boolean from a Bid struct.
2675
+ * @param bid Bid to query
2676
+ * @param index Index of packed boolean to query
2677
+ * @return Value of packed boolean
2678
+ */
2679
+ function _getBidPackedBool(
2680
+ Bid storage bid,
2681
+ uint8 index
2682
+ ) private view returns (bool) {
2683
+ return uint256(bid.packedBools).getBool(index);
2684
+ }
2685
+
2686
+ /**
2687
+ * @notice Helper function to get minimum slot index with an active bid,
2688
+ * starting at a given slot index and searching upwards.
2689
+ * Returns 512, (invalid slot index) if no slots with bids were found.
2690
+ * Reverts if startSlotIndex > 511, since this library only supports 512
2691
+ * slots.
2692
+ * @param RAMProjectConfig_ RAM project config to query
2693
+ * @param startSlotIndex Slot index to start search at
2694
+ * @return minSlotWithBid Minimum slot index with an active bid, or 512 (invalid index) if
2695
+ * no slots with bids were found.
2696
+ */
2697
+ function _getMinSlotWithBid(
2698
+ RAMProjectConfig storage RAMProjectConfig_,
2699
+ uint16 startSlotIndex
2700
+ ) private view returns (uint16 minSlotWithBid) {
2701
+ bool foundSlotWithBid;
2702
+ // revert if startSlotIndex > 511, since this is an invalid input
2703
+ // @dev no coverage on if branch because unreachable as used
2704
+ if (startSlotIndex > 511) {
2705
+ revert("Only start slot index lt 512");
2706
+ }
2707
+ // temporary uint256 in working memory
2708
+ uint256 minSlotWithBid_;
2709
+ // start at startSlotIndex
2710
+ if (startSlotIndex > 255) {
2711
+ // @dev <512 check results in no overflow when casting to uint8
2712
+ (minSlotWithBid_, foundSlotWithBid) = RAMProjectConfig_
2713
+ .slotsBitmapB
2714
+ .minBitSet(
2715
+ // @dev casting to uint8 intentional overflow instead of
2716
+ // subtracting 256 from slotIndex
2717
+ uint8(startSlotIndex)
2718
+ );
2719
+ // add 256 to account for slotsBitmapB offset
2720
+ minSlotWithBid_ += 256;
2721
+ } else {
2722
+ // @dev <256 conditional ensures no overflow when casting to uint8
2723
+ (minSlotWithBid_, foundSlotWithBid) = RAMProjectConfig_
2724
+ .slotsBitmapA
2725
+ .minBitSet(uint8(startSlotIndex));
2726
+
2727
+ // if no bids in first bitmap, check second bitmap
2728
+ // @dev behavior of library's minBitSet is to return 256 if no bits
2729
+ // were set
2730
+ if (!foundSlotWithBid) {
2731
+ // @dev <512 check results in no overflow when casting to uint8
2732
+ (minSlotWithBid_, foundSlotWithBid) = RAMProjectConfig_
2733
+ .slotsBitmapB
2734
+ .minBitSet(
2735
+ // start at beginning of second bitmap
2736
+ uint8(0)
2737
+ );
2738
+ // add 256 to account for slotsBitmapB offset
2739
+ minSlotWithBid_ += 256;
2740
+ }
2741
+ }
2742
+ // populate return value
2743
+ if (!foundSlotWithBid) {
2744
+ return uint16(NUM_SLOTS);
2745
+ } else {
2746
+ minSlotWithBid = uint16(minSlotWithBid_);
2747
+ return minSlotWithBid;
2748
+ }
2749
+ }
2750
+
2751
+ /**
2752
+ * @notice Helper function to get maximum slot index with an active bid,
2753
+ * starting at a given slot index and searching downwards.
2754
+ * Returns 512, (invalid slot index) if no slots with bids were found.
2755
+ * Reverts if startSlotIndex > 511, since this library only supports 512
2756
+ * slots.
2757
+ * @param RAMProjectConfig_ RAM project config to query
2758
+ * @param startSlotIndex Slot index to start search at
2759
+ * @return maxSlotWithBid Maximum slot index with an active bid, and 512 (invalid index) if
2760
+ * no slots with bids were found.
2761
+ */
2762
+ function _getMaxSlotWithBid(
2763
+ RAMProjectConfig storage RAMProjectConfig_,
2764
+ uint16 startSlotIndex
2765
+ ) private view returns (uint16 maxSlotWithBid) {
2766
+ bool foundSlotWithBid;
2767
+ // revert if startSlotIndex > 511, since this is an invalid input
2768
+ // @dev no coverage on if branch because unreachable as used
2769
+ if (startSlotIndex > 511) {
2770
+ revert("Only start slot index lt 512");
2771
+ }
2772
+ // temporary uint256 in working memory
2773
+ uint256 maxSlotWithBid_;
2774
+ // start at startSlotIndex
2775
+ if (startSlotIndex < 256) {
2776
+ // @dev <256 conditional ensures no overflow when casting to uint8
2777
+ (maxSlotWithBid_, foundSlotWithBid) = RAMProjectConfig_
2778
+ .slotsBitmapA
2779
+ .maxBitSet(uint8(startSlotIndex));
2780
+ } else {
2781
+ // need to potentially check both bitmaps
2782
+ (maxSlotWithBid_, foundSlotWithBid) = RAMProjectConfig_
2783
+ .slotsBitmapB
2784
+ .maxBitSet(
2785
+ // @dev casting to uint8 intentional overflow instead of
2786
+ // subtracting 256 from slotIndex
2787
+ uint8(startSlotIndex)
2788
+ );
2789
+ // add 256 to account for slotsBitmapB offset
2790
+ maxSlotWithBid_ += 256;
2791
+ if (!foundSlotWithBid) {
2792
+ // no bids in first bitmap B, so check second bitmap A
2793
+ (maxSlotWithBid_, foundSlotWithBid) = RAMProjectConfig_
2794
+ .slotsBitmapA
2795
+ .maxBitSet(
2796
+ // start at the end of the first bitmap
2797
+ uint8(255)
2798
+ );
2799
+ }
2800
+ }
2801
+ // populate return value
2802
+ // @dev no coverage on if branch because it is unreachable as used
2803
+ if (!foundSlotWithBid) {
2804
+ return uint16(NUM_SLOTS);
2805
+ } else {
2806
+ maxSlotWithBid = uint16(maxSlotWithBid_);
2807
+ return maxSlotWithBid;
2808
+ }
2809
+ }
2810
+
2811
+ /**
2812
+ * @notice Returns the next valid bid slot index and value for a given project.
2813
+ * @dev this may return slot index and value higher than the maximum slot index and value
2814
+ * allowed by the minter, in which case a bid cannot actually be placed
2815
+ * to outbid a bid at `startSlotIndex`.
2816
+ * @param projectId Project ID to find next valid bid slot index for
2817
+ * @param coreContract Core contract address for the given project
2818
+ * @param startSlotIndex Slot index to start search from
2819
+ * @return nextValidBidSlotIndex Next valid bid slot index
2820
+ * @return nextValidBidValue Next valid bid value at nextValidBidSlotIndex slot index, in Wei
2821
+ */
2822
+ function _findNextValidBidSlotIndexAndValue(
2823
+ uint256 projectId,
2824
+ address coreContract,
2825
+ uint16 startSlotIndex
2826
+ )
2827
+ private
2828
+ view
2829
+ returns (uint16 nextValidBidSlotIndex, uint256 nextValidBidValue)
2830
+ {
2831
+ RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
2832
+ projectId: projectId,
2833
+ coreContract: coreContract
2834
+ });
2835
+ uint88 basePrice = RAMProjectConfig_.basePrice;
2836
+ uint256 startBidValue = slotIndexToBidValue({
2837
+ basePrice: basePrice,
2838
+ slotIndex: startSlotIndex
2839
+ });
2840
+ // start search at next slot, incremented in while loop
2841
+ uint16 currentSlotIndex = startSlotIndex;
2842
+ while (true) {
2843
+ // increment slot index and re-calc current slot bid value
2844
+ unchecked {
2845
+ currentSlotIndex++;
2846
+ }
2847
+ nextValidBidValue = slotIndexToBidValue({
2848
+ basePrice: basePrice,
2849
+ slotIndex: currentSlotIndex
2850
+ });
2851
+ // break if current slot's bid value is sufficiently greater than
2852
+ // the starting slot's bid value
2853
+ if (
2854
+ _isSufficientOutbid({
2855
+ oldBidValue: startBidValue,
2856
+ newBidValue: nextValidBidValue
2857
+ })
2858
+ ) {
2859
+ break;
2860
+ }
2861
+ // otherwise continue to next iteration
2862
+ }
2863
+ // return the found valid slot index
2864
+ nextValidBidSlotIndex = currentSlotIndex;
2865
+ }
2866
+
2867
+ /**
2868
+ * @notice Returns a bool indicating if a new bid value is sufficiently
2869
+ * greater than an old bid value, to replace the old bid value.
2870
+ * @param oldBidValue Old bid value to compare
2871
+ * @param newBidValue New bid value to compare
2872
+ * @return isSufficientOutbid True if new bid is sufficiently greater than
2873
+ * old bid, false otherwise
2874
+ */
2875
+ function _isSufficientOutbid(
2876
+ uint256 oldBidValue,
2877
+ uint256 newBidValue
2878
+ ) private pure returns (bool) {
2879
+ if (oldBidValue > 0.5 ether) {
2880
+ // require new bid is at least 2.5% greater than removed minimum bid
2881
+ return newBidValue > (oldBidValue * 10250) / 10000;
2882
+ }
2883
+ // require new bid is at least 5% greater than removed minimum bid
2884
+ return newBidValue > (oldBidValue * 10500) / 10000;
2885
+ }
2886
+
2887
+ /**
2888
+ * @notice Returns the value of a bid in a given slot, in Wei.
2889
+ * @dev returns 0 if base price is zero
2890
+ * @param basePrice Base price (or reserve price) of the auction, in Wei
2891
+ * @param slotIndex Slot index to query
2892
+ * @return slotBidValue Value of a bid in the slot, in Wei
2893
+ */
2894
+ function slotIndexToBidValue(
2895
+ uint88 basePrice,
2896
+ uint16 slotIndex
2897
+ ) internal pure returns (uint256 slotBidValue) {
2898
+ // @dev for overflow safety, always revert if slotIndex >= NUM_SLOTS
2899
+ require(slotIndex < NUM_SLOTS, "Only slot index lt NUM_SLOTS");
2900
+ // use pseud-exponential pricing curve
2901
+ // multiply by two (via bit-shifting) for the number of entire
2902
+ // slots-per-price-double associated with the slot index
2903
+ // @dev overflow not possible due to typing, constants, and check above
2904
+ // (max(uint88) << (512 / 64)) < max(uint256)
2905
+ slotBidValue =
2906
+ uint256(basePrice) <<
2907
+ (slotIndex / SLOTS_PER_PRICE_DOUBLE);
2908
+ // perform a linear interpolation between partial half-life points, to
2909
+ // approximate the current place on a perfect exponential curve.
2910
+ // @dev overflow automatically checked in solidity 0.8, not expected
2911
+ slotBidValue +=
2912
+ (slotBidValue * (slotIndex % SLOTS_PER_PRICE_DOUBLE)) /
2913
+ SLOTS_PER_PRICE_DOUBLE;
2914
+ }
2915
+
2916
+ /**
2917
+ * @notice Return the storage struct for reading and writing. This library
2918
+ * uses a diamond storage pattern when managing storage.
2919
+ * @return storageStruct The RAMLibStorage struct.
2920
+ */
2921
+ function s() private pure returns (RAMLibStorage storage storageStruct) {
2922
+ bytes32 position = RAM_LIB_STORAGE_POSITION;
2923
+ assembly ("memory-safe") {
2924
+ storageStruct.slot := position
2925
+ }
2926
+ }
2927
+ }