@artblocks/contracts 1.3.1 → 1.4.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.
- package/README.md +11 -10
- package/artifacts/contracts/AdminACLV0RoyaltyRegistry.sol/AdminACLV0RoyaltyRegistry.json +193 -0
- package/artifacts/contracts/AdminACLV0RoyaltyRegistry.sol/IRoyaltyRegistry.json +35 -0
- package/artifacts/contracts/AdminACLV1.sol/AdminACLV1.json +2 -2
- package/artifacts/contracts/BytecodeStorageReaders/BytecodeStorageReaderContractV2.sol/BytecodeStorageReaderContractV2.json +2 -2
- package/artifacts/contracts/BytecodeStorageReaders/BytecodeStorageReaderContractV2_Web3Call.sol/BytecodeStorageReaderContractV2_Web3Call.json +2 -2
- package/artifacts/contracts/BytecodeStorageV2Writer.sol/BytecodeStorageV2Writer.json +2 -2
- package/artifacts/contracts/DependencyRegistryV0.sol/DependencyRegistryV0.json +210 -2
- package/artifacts/contracts/GenArt721CoreV3_Curated.sol/GenArt721CoreV3_Curated.json +235 -20
- package/artifacts/contracts/GenArt721CoreV3_Curated_Flex.sol/GenArt721CoreV3_Curated_Flex.json +3197 -0
- package/artifacts/contracts/GenArt721CoreV3_Explorations_Flex.sol/GenArt721CoreV3_Explorations_Flex.json +3192 -0
- package/artifacts/contracts/OwnedCreate2FactoryV0.sol/OwnedCreate2FactoryV0.json +2 -2
- package/artifacts/contracts/archive/GenArt721CoreV3.sol/GenArt721CoreV3.json +27 -2
- package/artifacts/contracts/archive/GenArt721CoreV3_Explorations.sol/GenArt721CoreV3_Explorations.json +27 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExp/MinterDAExpV2.sol/MinterDAExpV2.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExp/MinterDAExpV3.sol/MinterDAExpV3.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExp/MinterDAExpV4.sol/MinterDAExpV4.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExpSettlement/MinterDAExpSettlementV0.sol/MinterDAExpSettlementV0.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExpSettlement/MinterDAExpSettlementV1.sol/MinterDAExpSettlementV1.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExpSettlement/MinterDAExpSettlementV2.sol/MinterDAExpSettlementV2.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterDALin/MinterDALinV2.sol/MinterDALinV2.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterDALin/MinterDALinV3.sol/MinterDALinV3.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterDALin/MinterDALinV4.sol/MinterDALinV4.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterHolder/MinterHolderV1.sol/MinterHolderV1.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterHolder/MinterHolderV2.sol/MinterHolderV2.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterHolder/MinterHolderV3.sol/MinterHolderV3.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterHolder/MinterHolderV4.sol/MinterHolderV4.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV1.sol/MinterMerkleV1.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV2.sol/MinterMerkleV2.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV3.sol/MinterMerkleV3.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV4.sol/MinterMerkleV4.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV5.sol/MinterMerkleV5.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterPolyptych/MinterPolyptychV0.sol/IGenArt721CoreContractV3WithRandomizer.json +25 -0
- package/artifacts/contracts/archive/minter-suite/Minters/MinterPolyptych/MinterPolyptychV0.sol/MinterPolyptychV0.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterSEA/MinterSEAV0.sol/MinterSEAV0.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPrice/MinterSetPriceV2.sol/MinterSetPriceV2.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPrice/MinterSetPriceV3.sol/MinterSetPriceV3.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPrice/MinterSetPriceV4.sol/MinterSetPriceV4.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPriceERC20/MinterSetPriceERC20V2.sol/MinterSetPriceERC20V2.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPriceERC20/MinterSetPriceERC20V3.sol/MinterSetPriceERC20V3.json +2 -2
- package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPriceERC20/MinterSetPriceERC20V4.sol/MinterSetPriceERC20V4.json +2 -2
- package/artifacts/contracts/engine/V3/EngineFactoryV0.sol/EngineFactoryV0.json +28 -2
- package/artifacts/contracts/engine/V3/GenArt721CoreV3_Engine.sol/GenArt721CoreV3_Engine.json +239 -20
- package/artifacts/contracts/engine/V3/GenArt721CoreV3_Engine_Flex.sol/GenArt721CoreV3_Engine_Flex.json +267 -48
- package/artifacts/contracts/engine/V3/forks/GenArt721CoreV3_Engine_Flex_PROOF.sol/GenArt721CoreV3_Engine_Flex_PROOF.json +27 -2
- package/artifacts/contracts/engine/V3/forks/PROHIBITION/AdminACLV0_PROHIBITION.sol/AdminACLV0_PROHIBITION.json +2 -2
- package/artifacts/contracts/engine/V3/forks/PROHIBITION/GenArt721CoreV3_Engine_Flex_PROHIBITION.sol/GenArt721CoreV3_Engine_Flex_PROHIBITION.json +27 -2
- package/artifacts/contracts/engine/V3/forks/PROHIBITION/interfaces/IGenArt721CoreContractV3_Engine_Flex_PROHIBITION.sol/IGenArt721CoreContractV3_Engine_Flex_PROHIBITION.json +25 -0
- package/artifacts/contracts/engine/V3/transfer-hooks/AbstractTransferHook.sol/AbstractTransferHook.json +79 -0
- package/artifacts/contracts/engine/V3/transfer-hooks/OwnerHistoryTransferHook.sol/OwnerHistoryTransferHook.json +315 -0
- package/artifacts/contracts/generator/GenArt721GeneratorV0.sol/GenArt721GeneratorV0.json +2 -2
- package/artifacts/contracts/interfaces/v0.8.x/IClaimMinter.sol/IClaimMinter.json +189 -0
- package/artifacts/contracts/interfaces/v0.8.x/IDependencyRegistryV0.sol/IDependencyRegistryV0.json +149 -0
- package/artifacts/contracts/interfaces/v0.8.x/IEngineFactoryV0.sol/IEngineFactoryV0.json +26 -0
- package/artifacts/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3.sol/IGenArt721CoreContractV3.json +25 -0
- package/artifacts/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_Base.sol/IGenArt721CoreContractV3_Base.json +25 -0
- package/artifacts/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_Engine.sol/IGenArt721CoreContractV3_Engine.json +123 -0
- package/artifacts/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_Engine_Flex.sol/IGenArt721CoreContractV3_Engine_Flex.json +123 -0
- package/artifacts/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_Engine_Flex_PreV3p2.sol/IGenArt721CoreContractV3_Engine_Flex_PreV3p2.json +25 -0
- package/artifacts/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_Engine_PreV3p2.sol/IGenArt721CoreContractV3_Engine_PreV3p2.json +25 -0
- package/artifacts/contracts/interfaces/v0.8.x/IOwnerHistoryTransferHook.sol/IOwnerHistoryTransferHook.json +299 -0
- package/artifacts/contracts/interfaces/v0.8.x/IPMPV0.sol/IPMPV0.json +7 -1
- package/artifacts/contracts/interfaces/v0.8.x/ISRHooks.sol/ISRHooks.json +432 -0
- package/artifacts/contracts/interfaces/v0.8.x/ISeaDropShimForContract.sol/ISeaDropShimForContract.json +0 -13
- package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterTieredAllowV0.sol/ISharedMinterTieredAllowV0.json +94 -0
- package/artifacts/contracts/interfaces/v0.8.x/ITransferHook.sol/ITransferHook.json +63 -0
- package/artifacts/contracts/libs/v0.8.x/AuthLib.sol/AuthLib.json +2 -2
- package/artifacts/contracts/libs/v0.8.x/BytecodeStorageV2.sol/BytecodeStorageReader.json +2 -2
- package/artifacts/contracts/libs/v0.8.x/BytecodeStorageV2.sol/BytecodeStorageWriter.json +2 -2
- package/artifacts/contracts/libs/v0.8.x/DependencyRegistryStorageLib.sol/DependencyRegistryStorageLib.json +2 -2
- package/artifacts/contracts/libs/v0.8.x/ENSLib.sol/ENSLib.json +10 -0
- package/artifacts/contracts/libs/v0.8.x/ENSLib.sol/IUniversalResolver.json +45 -0
- package/artifacts/contracts/libs/v0.8.x/EnumerableSetUint16.sol/EnumerableSetUint16.json +10 -0
- package/artifacts/contracts/libs/v0.8.x/FeistelWalkLib.sol/FeistelWalkLib.json +10 -0
- package/artifacts/contracts/libs/v0.8.x/ImmutableUint16Array.sol/ImmutableUint16Array.json +10 -0
- package/artifacts/contracts/libs/v0.8.x/V3EngineLib.sol/V3EngineLib.json +163 -0
- package/artifacts/contracts/libs/v0.8.x/V3FlexLib.sol/V3FlexLib.json +2 -2
- package/artifacts/contracts/libs/v0.8.x/V3TransferHookLib.sol/V3TransferHookLib.json +79 -0
- package/artifacts/contracts/libs/v0.8.x/minter-libs/MaxInvocationsLib.sol/MaxInvocationsLib.json +2 -2
- package/artifacts/contracts/libs/v0.8.x/minter-libs/OnChainAllowlistLib.sol/OnChainAllowlistLib.json +10 -0
- package/artifacts/contracts/libs/v0.8.x/minter-libs/PolyptychLib.sol/PolyptychLib.json +2 -2
- package/artifacts/contracts/libs/v0.8.x/minter-libs/RAMLib.sol/RAMLib.json +2 -2
- package/artifacts/contracts/libs/v0.8.x/minter-libs/SEALib.sol/SEALib.json +2 -2
- package/artifacts/contracts/libs/v0.8.x/minter-libs/SettlementExpLib.sol/SettlementExpLib.json +2 -2
- package/artifacts/contracts/libs/v0.8.x/minter-libs/SplitFundsLib.sol/SplitFundsLib.json +2 -2
- package/artifacts/contracts/minter-suite/MinterFilter/MinterFilterV1.sol/MinterFilterV1.json +2 -2
- package/artifacts/contracts/minter-suite/MinterFilter/MinterFilterV2.sol/MinterFilterV2.json +2 -2
- package/artifacts/contracts/minter-suite/Minters/MinterDAExpHolderV5.sol/MinterDAExpHolderV5.json +2 -2
- package/artifacts/contracts/minter-suite/Minters/MinterDAExpSettlementV3.sol/MinterDAExpSettlementV3.json +2 -2
- package/artifacts/contracts/minter-suite/Minters/MinterDAExpV5.sol/MinterDAExpV5.json +2 -2
- package/artifacts/contracts/minter-suite/Minters/MinterDALinHolderV5.sol/MinterDALinHolderV5.json +2 -2
- package/artifacts/contracts/minter-suite/Minters/MinterDALinV5.sol/MinterDALinV5.json +2 -2
- package/artifacts/contracts/minter-suite/Minters/MinterMinPriceMerkleV0.sol/MinterMinPriceMerkleV0.json +2 -2
- package/artifacts/contracts/minter-suite/Minters/MinterMinPriceV0.sol/MinterMinPriceV0.json +2 -2
- package/artifacts/contracts/minter-suite/Minters/MinterRAMV0.sol/MinterRAMV0.json +2 -2
- package/artifacts/contracts/minter-suite/Minters/MinterSEAV1.sol/MinterSEAV1.json +2 -2
- package/artifacts/contracts/minter-suite/Minters/MinterSetPriceERC20V5.sol/MinterSetPriceERC20V5.json +2 -2
- package/artifacts/contracts/minter-suite/Minters/MinterSetPriceHolderV5.sol/MinterSetPriceHolderV5.json +2 -2
- package/artifacts/contracts/minter-suite/Minters/MinterSetPriceMerkleV5.sol/MinterSetPriceMerkleV5.json +2 -2
- package/artifacts/contracts/minter-suite/Minters/MinterSetPriceOnChainAllowV0.sol/MinterSetPriceOnChainAllowV0.json +522 -0
- package/artifacts/contracts/minter-suite/Minters/MinterSetPricePolyptychERC20V5.sol/MinterSetPricePolyptychERC20V5.json +2 -2
- package/artifacts/contracts/minter-suite/Minters/MinterSetPricePolyptychV5.sol/MinterSetPricePolyptychV5.json +2 -2
- package/artifacts/contracts/minter-suite/Minters/MinterSetPriceTieredAllowV1.sol/MinterSetPriceTieredAllowV1.json +715 -0
- package/artifacts/contracts/minter-suite/Minters/MinterSetPriceV5.sol/MinterSetPriceV5.json +2 -2
- package/artifacts/contracts/minter-suite/Minters/MinterSlidingScaleV0.sol/MinterSlidingScaleV0.json +431 -0
- package/artifacts/contracts/minter-suite/Minters/one-off/ClaimMinter.sol/ClaimMinter.json +502 -0
- package/artifacts/contracts/minter-suite/Minters/one-off/MinterSetPriceTieredOnChainAllowV0.sol/MinterSetPriceTieredOnChainAllowV0.json +673 -0
- package/artifacts/contracts/minter-suite/Minters/one-off/MinterSetPriceV5_SingleCore.sol/MinterSetPriceV5_SingleCore.json +2 -2
- package/artifacts/contracts/minter-suite/Minters/one-off/SeaDropXArtBlocksShim.sol/SeaDropXArtBlocksShim.json +6 -19
- package/artifacts/contracts/mock/BytecodeV2TextCR_DMock.sol/BytecodeV2TextCR_DMock.json +2 -2
- package/artifacts/contracts/mock/DummySharedMinter.sol/DummySharedMinter.json +2 -2
- package/artifacts/contracts/mock/ENSLibMock.sol/ENSLibMock.json +30 -0
- package/artifacts/contracts/mock/EnumerableSetUint16Mock.sol/EnumerableSetUint16Mock.json +146 -0
- package/artifacts/contracts/mock/FeistelWalkLibMock.sol/FeistelWalkLibMock.json +142 -0
- package/artifacts/contracts/mock/ForceSendMock.sol/ForceSendMock.json +2 -2
- package/artifacts/contracts/mock/GenArt721CoreV3_Engine_IncorrectCoreType.sol/GenArt721CoreV3_Engine_IncorrectCoreType.json +2 -2
- package/artifacts/contracts/mock/ImmutableUint16ArrayMock.sol/ImmutableUint16ArrayMock.json +89 -0
- package/artifacts/contracts/mock/MockAdminACLV0_PROHIBITION.sol/MockAdminACLV0Events_PROHIBITION.json +2 -2
- package/artifacts/contracts/mock/MockGenArt721V3Core.sol/MockGenArt721V3Core.json +490 -0
- package/artifacts/contracts/mock/MockPMP.sol/MockPMP.json +523 -0
- package/artifacts/contracts/mock/MockPMPAugmentHook.sol/MockPMPAugmentHook.json +2 -2
- package/artifacts/contracts/mock/MockPMPConfigureHook.sol/MockPMPConfigureHook.json +2 -2
- package/artifacts/contracts/mock/MockTransferHook.sol/MockTransferHook.json +453 -0
- package/artifacts/contracts/mock/MockTransferHookTestHelpers.sol/MockBatchTransferrer.json +39 -0
- package/artifacts/contracts/mock/MockTransferHookTestHelpers.sol/MockForwardingERC721Receiver.json +71 -0
- package/artifacts/contracts/mock/MockTransferHookTestHelpers.sol/MockSpoofingCore.json +94 -0
- package/artifacts/contracts/mock/RandomizerV2_NoAssignMock.sol/RandomizerV2_NoAssignMock.json +2 -2
- package/artifacts/contracts/mock/ReentrancyBatchMockShared.sol/IBatchMinter.json +64 -0
- package/artifacts/contracts/mock/ReentrancyBatchMockShared.sol/ReentrancyBatchMockShared.json +113 -0
- package/artifacts/contracts/mock/RelayExecutorTestHelper.sol/MockTarget.json +74 -0
- package/artifacts/contracts/mock/RelayExecutorTestHelper.sol/RelayExecutorPartialRevertsTestHelper.json +138 -0
- package/artifacts/contracts/mock/RelayExecutorTestHelper.sol/RelayExecutorTestHelper.json +119 -0
- package/artifacts/contracts/randomizer/deprecated/PolyptychRandomizerV0.sol/BasicPolyptychRandomizerV0.json +2 -2
- package/artifacts/contracts/randomizer/deprecated/RandomizerV2.sol/BasicRandomizerV2.json +2 -2
- package/artifacts/contracts/randomizer/shared/SharedRandomizerV0.sol/SharedRandomizerV0.json +2 -2
- package/artifacts/contracts/relay/RelayExecutor.sol/RelayExecutor.json +101 -0
- package/artifacts/contracts/relay/RelayExecutorPartialReverts.sol/RelayExecutorPartialReverts.json +120 -0
- package/artifacts/contracts/royalty-registry/GenArt721RoyaltyOverrideSplits.sol/GenArt721RoyaltyOverrideSplits.json +263 -0
- package/artifacts/contracts/utils/MintMulticallUtil.sol/IMinterSetPriceV5.json +40 -0
- package/artifacts/contracts/utils/MintMulticallUtil.sol/MintMulticallUtil.json +163 -0
- package/artifacts/contracts/web3call/PMPV0.sol/PMPV0.json +9 -3
- package/artifacts/contracts/web3call/PMPV1.sol/PMPV1.json +648 -0
- package/artifacts/contracts/web3call/augment-hooks/InjectBaseGasFee.sol/InjectBaseGasFee.json +2 -2
- package/artifacts/contracts/web3call/augment-hooks/InjectBlockHeight.sol/InjectBlockHeight.json +83 -0
- package/artifacts/contracts/web3call/augment-hooks/InjectBlockHeightAndArtistProjectOverrides.sol/InjectBlockHeightAndArtistProjectOverrides.json +226 -0
- package/artifacts/contracts/web3call/augment-hooks/InjectBlockHeightAndNumOwnedProjectTokens.sol/InjectBlockHeightAndNumOwnedProjectTokens.json +182 -0
- package/artifacts/contracts/web3call/augment-hooks/InjectBlockHeightProjectInvocationsAndArtistProjectOverrides.sol/InjectBlockHeightProjectInvocationsAndArtistProjectOverrides.json +226 -0
- package/artifacts/contracts/web3call/augment-hooks/InjectBlockTimestamp.sol/InjectBlockTimestamp.json +83 -0
- package/artifacts/contracts/web3call/augment-hooks/InjectBytesPerToken.sol/InjectBytesPerToken.json +358 -0
- package/artifacts/contracts/web3call/augment-hooks/InjectChromieSquiggleFloorPrice.sol/InjectChromieSquiggleFloorPrice.json +2 -2
- package/artifacts/contracts/web3call/augment-hooks/InjectIsNounsDaoMember.sol/InjectIsNounsDaoMember.json +2 -2
- package/artifacts/contracts/web3call/augment-hooks/InjectIsProjectFullyMinted.sol/InjectIsProjectFullyMinted.json +2 -2
- package/artifacts/contracts/web3call/augment-hooks/InjectPolyptychTokenHash.sol/InjectPolyptychTokenHash.json +2 -2
- package/artifacts/contracts/web3call/augment-hooks/InjectTokenHashSeed.sol/InjectTokenHashSeed.json +2 -2
- package/artifacts/contracts/web3call/augment-hooks/InjectTokenOwner.sol/InjectTokenOwner.json +2 -2
- package/artifacts/contracts/web3call/augment-hooks/InjectTokenOwnerEthBalance.sol/InjectTokenOwnerEthBalance.json +2 -2
- package/artifacts/contracts/web3call/augment-hooks/InjectUSDCTotalSupply.sol/InjectUSDCTotalSupply.json +2 -2
- package/artifacts/contracts/web3call/augment-hooks/SRHooks.sol/SRHooks.json +899 -0
- package/artifacts/contracts/web3call/combined-hooks/LiftHooks.sol/IGenArt721V0_Minimal.json +30 -0
- package/artifacts/contracts/web3call/combined-hooks/LiftHooks.sol/IRelic.json +35 -0
- package/artifacts/contracts/web3call/combined-hooks/LiftHooks.sol/LiftHooks.json +256 -0
- package/artifacts/contracts/web3call/configure-hooks/{abstractPMPConfigureHook.sol → AbstractPMPConfigureHook.sol}/AbstractPMPConfigureHook.json +1 -1
- package/artifacts/contracts/web3call/configure-hooks/PMPConfigureLockHook.sol/PMPConfigureLockHook.json +195 -0
- package/contracts/AdminACLV0RoyaltyRegistry.sol +142 -0
- package/contracts/DependencyRegistryV0.sol +143 -13
- package/contracts/GenArt721CoreV3_Curated.sol +1 -1
- package/contracts/GenArt721CoreV3_Curated_Flex.sol +173 -0
- package/contracts/GenArt721CoreV3_Explorations_Flex.sol +159 -0
- package/contracts/engine/V3/EngineFactoryV0.sol +20 -1
- package/contracts/engine/V3/GenArt721CoreV3_Engine.sol +286 -299
- package/contracts/engine/V3/GenArt721CoreV3_Engine_Flex.sol +292 -301
- package/contracts/engine/V3/transfer-hooks/AbstractTransferHook.sol +101 -0
- package/contracts/engine/V3/transfer-hooks/OwnerHistoryTransferHook.sol +291 -0
- package/contracts/generator/GenArt721GeneratorV0.sol +145 -47
- package/contracts/helpers/ERC1967ProxyImport.sol +5 -0
- package/contracts/interfaces/v0.8.x/IArtblocksRoyaltyOverride.sol +1 -1
- package/contracts/interfaces/v0.8.x/IClaimMinter.sol +70 -0
- package/contracts/interfaces/v0.8.x/IDependencyRegistryV0.sol +95 -0
- package/contracts/interfaces/v0.8.x/IEngineFactoryV0.sol +15 -0
- package/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_Base.sol +21 -2
- package/contracts/interfaces/v0.8.x/IGenArt721CoreContractV3_Engine.sol +72 -0
- package/contracts/interfaces/v0.8.x/IOwnerHistoryTransferHook.sol +119 -0
- package/contracts/interfaces/v0.8.x/IPMPV0.sol +5 -3
- package/contracts/interfaces/v0.8.x/ISRHooks.sol +229 -0
- package/contracts/interfaces/v0.8.x/ISeaDropShimForContract.sol +0 -6
- package/contracts/interfaces/v0.8.x/ISharedMinterTieredAllowV0.sol +69 -0
- package/contracts/interfaces/v0.8.x/ITransferHook.sol +48 -0
- package/contracts/libs/v0.8.x/DependencyRegistryStorageLib.sol +12 -0
- package/contracts/libs/v0.8.x/ENSLib.sol +85 -0
- package/contracts/libs/v0.8.x/EnumerableSetUint16.sol +429 -0
- package/contracts/libs/v0.8.x/FeistelWalkLib.sol +221 -0
- package/contracts/libs/v0.8.x/ImmutableUint16Array.sol +201 -0
- package/contracts/libs/v0.8.x/V3EngineLib.sol +490 -0
- package/contracts/libs/v0.8.x/V3TransferHookLib.sol +251 -0
- package/contracts/libs/v0.8.x/minter-libs/OnChainAllowlistLib.sol +165 -0
- package/contracts/minter-suite/Minters/MinterSetPriceOnChainAllowV0.sol +632 -0
- package/contracts/minter-suite/Minters/MinterSetPriceTieredAllowV1.sol +871 -0
- package/contracts/minter-suite/Minters/MinterSlidingScaleV0.sol +493 -0
- package/contracts/minter-suite/Minters/one-off/ClaimMinter.sol +595 -0
- package/contracts/minter-suite/Minters/one-off/MinterSetPriceTieredOnChainAllowV0.sol +894 -0
- package/contracts/minter-suite/Minters/one-off/SeaDropXArtBlocksShim.sol +59 -41
- package/contracts/mock/ENSLibMock.sol +25 -0
- package/contracts/mock/EnumerableSetUint16Mock.sol +88 -0
- package/contracts/mock/FeistelWalkLibMock.sol +81 -0
- package/contracts/mock/ImmutableUint16ArrayMock.sol +62 -0
- package/contracts/mock/MockGenArt721V3Core.sol +416 -0
- package/contracts/mock/MockPMP.sol +287 -0
- package/contracts/mock/MockTransferHook.sol +160 -0
- package/contracts/mock/MockTransferHookTestHelpers.sol +96 -0
- package/contracts/mock/ReentrancyBatchMockShared.sol +53 -0
- package/contracts/mock/RelayExecutorTestHelper.sol +44 -0
- package/contracts/relay/RelayExecutor.sol +61 -0
- package/contracts/relay/RelayExecutorPartialReverts.sol +79 -0
- package/contracts/royalty-registry/GenArt721RoyaltyOverrideSplits.sol +130 -0
- package/contracts/utils/MintMulticallUtil.sol +103 -0
- package/contracts/web3call/PMPV0.sol +92 -98
- package/contracts/web3call/PMPV1.sol +1204 -0
- package/contracts/web3call/augment-hooks/AbstractPMPAugmentHook.sol +1 -1
- package/contracts/web3call/augment-hooks/InjectBaseGasFee.sol +1 -1
- package/contracts/web3call/augment-hooks/InjectBlockHeight.sol +57 -0
- package/contracts/web3call/augment-hooks/InjectBlockHeightAndArtistProjectOverrides.sol +310 -0
- package/contracts/web3call/augment-hooks/InjectBlockHeightAndNumOwnedProjectTokens.sol +141 -0
- package/contracts/web3call/augment-hooks/InjectBlockHeightProjectInvocationsAndArtistProjectOverrides.sol +326 -0
- package/contracts/web3call/augment-hooks/InjectBlockTimestamp.sol +57 -0
- package/contracts/web3call/augment-hooks/InjectBytesPerToken.sol +279 -0
- package/contracts/web3call/augment-hooks/InjectIsProjectFullyMinted.sol +1 -1
- package/contracts/web3call/augment-hooks/InjectTokenHashSeed.sol +1 -1
- package/contracts/web3call/augment-hooks/InjectTokenOwner.sol +1 -1
- package/contracts/web3call/augment-hooks/InjectTokenOwnerEthBalance.sol +1 -1
- package/contracts/web3call/augment-hooks/SRHooks.sol +1722 -0
- package/contracts/web3call/combined-hooks/LiftHooks.sol +304 -0
- package/contracts/web3call/configure-hooks/{abstractPMPConfigureHook.sol → AbstractPMPConfigureHook.sol} +1 -1
- package/contracts/web3call/configure-hooks/PMPConfigureLockHook.sol +107 -0
- package/package.json +40 -25
|
@@ -0,0 +1,1722 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-3.0-only
|
|
2
|
+
// Created By: Art Blocks Inc.
|
|
3
|
+
|
|
4
|
+
pragma solidity 0.8.22;
|
|
5
|
+
|
|
6
|
+
import {AbstractPMPAugmentHook} from "../augment-hooks/AbstractPMPAugmentHook.sol";
|
|
7
|
+
|
|
8
|
+
import {IWeb3Call} from "../../interfaces/v0.8.x/IWeb3Call.sol";
|
|
9
|
+
import {IPMPV0} from "../../interfaces/v0.8.x/IPMPV0.sol";
|
|
10
|
+
import {ISRHooks} from "../../interfaces/v0.8.x/ISRHooks.sol";
|
|
11
|
+
import {Strings} from "@openzeppelin-5.0/contracts/utils/Strings.sol";
|
|
12
|
+
import {IERC721} from "@openzeppelin-5.0/contracts/interfaces/IERC721.sol";
|
|
13
|
+
import {IDelegationRegistry as IDelegationRegistryV1} from "../../interfaces/v0.8.x/IDelegationRegistry.sol";
|
|
14
|
+
import {IDelegateRegistry as IDelegationRegistryV2} from "../../interfaces/v0.8.x/IDelegateRegistry.sol";
|
|
15
|
+
import {IPMPConfigureHook} from "../../interfaces/v0.8.x/IPMPConfigureHook.sol";
|
|
16
|
+
import {IPMPAugmentHook} from "../../interfaces/v0.8.x/IPMPAugmentHook.sol";
|
|
17
|
+
|
|
18
|
+
import {SafeCast} from "@openzeppelin-5.0/contracts/utils/math/SafeCast.sol";
|
|
19
|
+
import {Initializable} from "@openzeppelin-5.0/contracts-upgradeable/proxy/utils/Initializable.sol";
|
|
20
|
+
import {UUPSUpgradeable} from "@openzeppelin-5.0/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
|
21
|
+
import {OwnableUpgradeable} from "@openzeppelin-5.0/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
|
22
|
+
|
|
23
|
+
import {ImmutableUint16Array} from "../../libs/v0.8.x/ImmutableUint16Array.sol";
|
|
24
|
+
import {SSTORE2} from "../../libs/v0.8.x/SSTORE2.sol";
|
|
25
|
+
import {ABHelpers} from "../../libs/v0.8.x/ABHelpers.sol";
|
|
26
|
+
import {FeistelWalkLib} from "../../libs/v0.8.x/FeistelWalkLib.sol";
|
|
27
|
+
import {ENSLib} from "../../libs/v0.8.x/ENSLib.sol";
|
|
28
|
+
import {EnumerableSetUint16} from "../../libs/v0.8.x/EnumerableSetUint16.sol";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @title SRHooks
|
|
32
|
+
* @author Art Blocks Inc.
|
|
33
|
+
* @notice This hook tracks the send and receive states of a token, and the metadata for a token.
|
|
34
|
+
* It implements the primary functionality of the SR project, and accepts most calls directly.
|
|
35
|
+
* It acts as a PMPV0 hook, and therefore derives from AbstractPMPAugmentHook.
|
|
36
|
+
* For clear off-chain provenance indexing and efficiency purposes, it emits events as if it were a
|
|
37
|
+
* PMPV0 contract, so it may be indexed directly by off-chain tools, including the Art Blocks subgraph.
|
|
38
|
+
* Some state emit events that may be custom, or may not be directly related to PMPV0, but may still be useful
|
|
39
|
+
* for off-chain indexing and analysis and frontend development.
|
|
40
|
+
* Implements a Feistel walk for efficient pseudo-random sampling at each block over large sets and arrays. This
|
|
41
|
+
* sampling is considered efficient and effective enough for our use case of streaming pseudorandom live data at each block.
|
|
42
|
+
* @dev Operationally, this contract assumes that the PMPV0 contract will be configured with at least the following PMP values,
|
|
43
|
+
* and that those values will be configured in a way where they are never able to be updated (e.g. auth option is dead address):
|
|
44
|
+
* - SendState
|
|
45
|
+
* - ReceiveState
|
|
46
|
+
* - ActiveSlot
|
|
47
|
+
* - ImageVersionSlot0
|
|
48
|
+
* - SoundVersionSlot0
|
|
49
|
+
* - ImageVersionSlot1
|
|
50
|
+
* - SoundVersionSlot1
|
|
51
|
+
* - ImageVersionSlot2
|
|
52
|
+
* - SoundVersionSlot2
|
|
53
|
+
* - ImageVersionSlot3
|
|
54
|
+
* - SoundVersionSlot3
|
|
55
|
+
* - ImageVersionSlot4
|
|
56
|
+
* - SoundVersionSlot4
|
|
57
|
+
* @dev This contract follows the UUPS (Universal Upgradeable Proxy Standard) pattern.
|
|
58
|
+
* It uses OpenZeppelin's upgradeable contracts and must be deployed behind a proxy.
|
|
59
|
+
* Only the owner can authorize upgrades via the _authorizeUpgrade function, which may
|
|
60
|
+
* be eventually disabled in future versions after to lock project functionality.
|
|
61
|
+
*/
|
|
62
|
+
contract SRHooks is
|
|
63
|
+
Initializable,
|
|
64
|
+
AbstractPMPAugmentHook,
|
|
65
|
+
OwnableUpgradeable,
|
|
66
|
+
UUPSUpgradeable,
|
|
67
|
+
ISRHooks
|
|
68
|
+
{
|
|
69
|
+
using EnumerableSetUint16 for EnumerableSetUint16.Uint16Set;
|
|
70
|
+
using ImmutableUint16Array for ImmutableUint16Array.Uint16Array;
|
|
71
|
+
using SafeCast for uint256;
|
|
72
|
+
|
|
73
|
+
address public CORE_CONTRACT_ADDRESS;
|
|
74
|
+
|
|
75
|
+
uint256 public CORE_PROJECT_ID;
|
|
76
|
+
|
|
77
|
+
uint256 public constant MAX_IMAGE_DATA_LENGTH = 1024 * 15; // 15 KB, beyond which is unlikely to represent a 64x64 image
|
|
78
|
+
uint256 public constant MAX_SOUND_DATA_LENGTH = 1024 * 10; // 10 KB, beyond which is unlikely to represent a sound
|
|
79
|
+
|
|
80
|
+
uint256 internal constant MAX_RECEIVE_RATE_PER_BLOCK = 36; // 36 tokens per 12s block, to limit payload size for art script
|
|
81
|
+
|
|
82
|
+
uint256 internal constant MAX_SENDING_TO_LENGTH = 25; // 25 tokens, beyond which gas costs are high, you are over-represented, and if dilution is considered in the future, it would greatly inflate live data iteration time
|
|
83
|
+
uint256 internal constant MAX_RECEIVING_FROM_ARRAY_LENGTH = 1_000; // max receiving from array length is 1k to bound worst case iteration time when getting live data
|
|
84
|
+
|
|
85
|
+
// constant delegation registry pointers and rights
|
|
86
|
+
IDelegationRegistryV2 public constant DELEGATE_V2 =
|
|
87
|
+
IDelegationRegistryV2(0x00000000000000447e69651d841bD8D104Bed493);
|
|
88
|
+
bytes32 public constant DELEGATION_REGISTRY_TOKEN_OWNER_RIGHTS =
|
|
89
|
+
bytes32("postmintparameters");
|
|
90
|
+
|
|
91
|
+
// ------ TOKEN METADATA STATE VARIABLES ------
|
|
92
|
+
|
|
93
|
+
uint256 public constant NUM_METADATA_SLOTS = 5;
|
|
94
|
+
|
|
95
|
+
// struct for the token metadata in storage
|
|
96
|
+
struct TokenMetadata {
|
|
97
|
+
address imageDataAddress; // 20 bytes
|
|
98
|
+
uint16 imageVersion; // 2 bytes - max 65,535 updates per slot
|
|
99
|
+
address soundDataAddress; // 20 bytes
|
|
100
|
+
uint16 soundVersion; // 2 bytes - max 65,535 updates per slot
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/// @notice mapping of token numbers to slot to metadata
|
|
104
|
+
mapping(uint256 tokenNumber => mapping(uint256 slot => TokenMetadata slotMetadata))
|
|
105
|
+
private _tokensMetadata;
|
|
106
|
+
|
|
107
|
+
struct TokenAuxStateData {
|
|
108
|
+
uint8 activeSlot;
|
|
109
|
+
bool isReceivingTo; // true if the token is only receiving tokens sending to it, false if in any other receive state
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/// @notice mapping of token numbers to aux state data
|
|
113
|
+
mapping(uint256 tokenNumber => TokenAuxStateData auxStateData)
|
|
114
|
+
private _tokenAuxStateData;
|
|
115
|
+
|
|
116
|
+
// ------ SEND/RECEIVE STATE (GLOBAL) ------
|
|
117
|
+
|
|
118
|
+
/// @notice Set of token numbers that are currently in state SendGeneral
|
|
119
|
+
// @dev Uses custom packed EnumerableSetUint16 to efficiently pack uint16 values into shared storage slots
|
|
120
|
+
// @dev need O(1) access and O(1) insertion/removal for both sending and receiving tokens, so use an EnumerableSet
|
|
121
|
+
EnumerableSetUint16.Uint16Set private _sendGeneralTokens;
|
|
122
|
+
|
|
123
|
+
/// @notice Set of token numbers that are currently in state ReceiveGeneral
|
|
124
|
+
// @dev Uses custom packed EnumerableSetUint16 to efficiently pack uint16 values into shared storage slots
|
|
125
|
+
// @dev need O(1) access and O(1) insertion/removal for both sending and receiving tokens, so use an EnumerableSet
|
|
126
|
+
EnumerableSetUint16.Uint16Set private _receiveGeneralTokens;
|
|
127
|
+
|
|
128
|
+
// ------ SEND/RECEIVE STATE (PER TOKEN) ------
|
|
129
|
+
|
|
130
|
+
/// @notice Set of token numbers that are sending to a specific token
|
|
131
|
+
// @dev Uses custom packed EnumerableSetUint16 to efficiently pack uint16 values into shared storage slots
|
|
132
|
+
// @dev need O(1) access and O(1) insertion/removal for both sending and receiving tokens, so use an EnumerableSet
|
|
133
|
+
mapping(uint256 receivingTokenNumber => EnumerableSetUint16.Uint16Set tokensSendingToMe)
|
|
134
|
+
private _tokensSendingToMe;
|
|
135
|
+
|
|
136
|
+
/// @notice Array of token numbers that a given token is sending to (when in state SendTo)
|
|
137
|
+
// @dev need only O(1) access (not insertion/removal) for sending tokens, so use an ImmutableUint16Array
|
|
138
|
+
mapping(uint256 sendingTokenNumber => ImmutableUint16Array.Uint16Array tokensSendingTo)
|
|
139
|
+
private _tokensSendingTo;
|
|
140
|
+
|
|
141
|
+
/// @notice Array of token numbers that a token is open to receiving from (when in state ReceiveFrom)
|
|
142
|
+
// @dev need only O(1) access (not insertion/removal) for receiving tokens, so use an ImmutableUint16Array
|
|
143
|
+
mapping(uint256 receivingTokenNumber => ImmutableUint16Array.Uint16Array tokensReceivingFrom)
|
|
144
|
+
private _tokensReceivingFrom;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @notice modifier-like internal function to check if an address is the owner or a valid delegate.xyz V2 of the token owner.
|
|
148
|
+
* @param tokenNumber The token number to check.
|
|
149
|
+
* @param addressToCheck The address to check.
|
|
150
|
+
* @return isOwnerOrDelegate True if the address is the owner or a valid delegate.xyz V2 of the token owner, false otherwise.
|
|
151
|
+
* @return ownerAddress The address of the token owner.
|
|
152
|
+
* @dev This function is used to check if an address is the owner or a valid delegate.xyz V2 of the token owner,
|
|
153
|
+
* and to get the address of the token owner.
|
|
154
|
+
*/
|
|
155
|
+
function _isOwnerOrDelegate(
|
|
156
|
+
uint256 tokenNumber,
|
|
157
|
+
address addressToCheck
|
|
158
|
+
) internal view returns (bool isOwnerOrDelegate, address ownerAddress) {
|
|
159
|
+
uint256 tokenId = ABHelpers.tokenIdFromProjectIdAndTokenNumber({
|
|
160
|
+
projectId: CORE_PROJECT_ID,
|
|
161
|
+
tokenNumber: tokenNumber
|
|
162
|
+
});
|
|
163
|
+
ownerAddress = IERC721(CORE_CONTRACT_ADDRESS).ownerOf(tokenId);
|
|
164
|
+
isOwnerOrDelegate =
|
|
165
|
+
addressToCheck == ownerAddress ||
|
|
166
|
+
DELEGATE_V2.checkDelegateForERC721({
|
|
167
|
+
to: addressToCheck,
|
|
168
|
+
from: ownerAddress,
|
|
169
|
+
contract_: CORE_CONTRACT_ADDRESS,
|
|
170
|
+
tokenId: tokenId,
|
|
171
|
+
rights: DELEGATION_REGISTRY_TOKEN_OWNER_RIGHTS
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/// disable initialization in deployed implementation contract for clarity
|
|
176
|
+
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
177
|
+
constructor() {
|
|
178
|
+
_disableInitializers();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* @notice Initializes the contract with the PMPV0 address and sets the owner.
|
|
183
|
+
* @dev This function replaces the constructor for upgradeable contracts.
|
|
184
|
+
* Can only be called once due to the initializer modifier.
|
|
185
|
+
* @param _owner The address that will own this contract and authorize upgrades.
|
|
186
|
+
* @param _coreContractAddress The address of the core contract.
|
|
187
|
+
* @param _coreProjectId The project ID of the core contract.
|
|
188
|
+
*/
|
|
189
|
+
function initialize(
|
|
190
|
+
address _owner,
|
|
191
|
+
address _coreContractAddress,
|
|
192
|
+
uint256 _coreProjectId
|
|
193
|
+
) public initializer {
|
|
194
|
+
__Ownable_init(_owner);
|
|
195
|
+
__UUPSUpgradeable_init();
|
|
196
|
+
|
|
197
|
+
CORE_CONTRACT_ADDRESS = _coreContractAddress;
|
|
198
|
+
CORE_PROJECT_ID = _coreProjectId;
|
|
199
|
+
|
|
200
|
+
emit SRHooksInitialized({
|
|
201
|
+
coreContractAddress: _coreContractAddress,
|
|
202
|
+
coreProjectId: _coreProjectId
|
|
203
|
+
});
|
|
204
|
+
// @dev this contract does not emit PMPV0 project configuration events.
|
|
205
|
+
// This decouples indexing of PMP Configure events from the indexing of the project configuration information.
|
|
206
|
+
// Operationally, this contract requires configuration of the PMP project configuration as described in the contract-
|
|
207
|
+
// level documentation.
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* @notice Augment the token parameters for a given token.
|
|
212
|
+
* Augments the token parameters by appending the token's active slot's metadata and send/receive states.
|
|
213
|
+
* The following fields are appended:
|
|
214
|
+
* - imageData: the hex string of the compressed image data of the token's active slot
|
|
215
|
+
* - soundData: the hex string of the compressed sound data of the token's active slot
|
|
216
|
+
* - sendState: the string of the send state of the token (SendGeneral, SendTo, Neutral)
|
|
217
|
+
* - receiveState: the string of the receive state of the token (ReceiveGeneral, ReceiveFrom, Neutral)
|
|
218
|
+
* @dev This hook is called when a token's PMPs are read.
|
|
219
|
+
* @dev This must return all desired tokenParams, not just additional data.
|
|
220
|
+
* @param tokenParams The token parameters for the queried token.
|
|
221
|
+
* @return augmentedTokenParams The augmented token parameters.
|
|
222
|
+
*/
|
|
223
|
+
function onTokenPMPReadAugmentation(
|
|
224
|
+
address /* coreContract */,
|
|
225
|
+
uint256 tokenId,
|
|
226
|
+
IWeb3Call.TokenParam[] calldata tokenParams
|
|
227
|
+
)
|
|
228
|
+
external
|
|
229
|
+
view
|
|
230
|
+
override
|
|
231
|
+
returns (IWeb3Call.TokenParam[] memory augmentedTokenParams)
|
|
232
|
+
{
|
|
233
|
+
// create a new augmentedTokenParams array with maximum length of
|
|
234
|
+
// input tokenParams + 1 extra element for the squiggle's token hash
|
|
235
|
+
uint256 originalLength = tokenParams.length;
|
|
236
|
+
uint256 augmentedMaxLength = originalLength + 4; // 4 extra elements for the token metadata fields
|
|
237
|
+
augmentedTokenParams = new IWeb3Call.TokenParam[](augmentedMaxLength);
|
|
238
|
+
|
|
239
|
+
// copy original tokenParams to augmentedTokenParams
|
|
240
|
+
for (uint256 i = 0; i < originalLength; i++) {
|
|
241
|
+
augmentedTokenParams[i] = tokenParams[i];
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// append each token metadata fields (image and sound data addresses) to the augmentedTokenParams array
|
|
245
|
+
// @dev load active slot from storage
|
|
246
|
+
uint256 tokenNumber = ABHelpers.tokenIdToTokenNumber(tokenId);
|
|
247
|
+
uint256 activeSlot = _tokenAuxStateData[tokenNumber].activeSlot;
|
|
248
|
+
TokenMetadata storage tokenMetadataStorage = _tokensMetadata[
|
|
249
|
+
tokenNumber
|
|
250
|
+
][activeSlot];
|
|
251
|
+
// return the image and sound data as hex strings
|
|
252
|
+
augmentedTokenParams[originalLength] = IWeb3Call.TokenParam({
|
|
253
|
+
key: "imageData",
|
|
254
|
+
value: _getHexStringFromSSTORE2(
|
|
255
|
+
tokenMetadataStorage.imageDataAddress
|
|
256
|
+
)
|
|
257
|
+
});
|
|
258
|
+
augmentedTokenParams[originalLength + 1] = IWeb3Call.TokenParam({
|
|
259
|
+
key: "soundData",
|
|
260
|
+
value: _getHexStringFromSSTORE2(
|
|
261
|
+
tokenMetadataStorage.soundDataAddress
|
|
262
|
+
)
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// include send and receive states
|
|
266
|
+
augmentedTokenParams[originalLength + 2] = IWeb3Call.TokenParam({
|
|
267
|
+
key: "sendState",
|
|
268
|
+
value: _sendStateToString(_getSendState(tokenNumber))
|
|
269
|
+
});
|
|
270
|
+
augmentedTokenParams[originalLength + 3] = IWeb3Call.TokenParam({
|
|
271
|
+
key: "receiveState",
|
|
272
|
+
value: _receiveStateToString(_getReceiveState(tokenNumber))
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// return the augmented tokenParams
|
|
276
|
+
return augmentedTokenParams;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* @notice Updates the state and metadata for a given token.
|
|
281
|
+
* Reverts if the token number is invalid or the msg.sender is not owner or valid delegate.xyz V2 of token owner.
|
|
282
|
+
* Reverts if invalid configuration is provided.
|
|
283
|
+
* Includes two boolean flags to update the send and receive states and token metadata separately, in a single function call.
|
|
284
|
+
* @param tokenNumber The token number to update.
|
|
285
|
+
* @param updateSendState Whether to update the send state.
|
|
286
|
+
* @param sendState The new send state. Valid values are SendGeneral, SendTo, Neutral.
|
|
287
|
+
* @param tokensSendingTo Tokens to send this token to. Only non-empty iff updateSendState is true and sendState is SendTo.
|
|
288
|
+
* @param updateReceiveState Whether to update the receive state.
|
|
289
|
+
* @param receiveState The new receive state. Valid values are ReceiveGeneral, ReceiveFrom, Neutral.
|
|
290
|
+
* @param tokensReceivingFrom Tokens this token is open to receive from. Only non-empty iff updateReceiveState is true and receiveState is ReceiveFrom.
|
|
291
|
+
* @param updateTokenMetadata Whether to update the token metadata.
|
|
292
|
+
* @param updatedActiveSlot The new active slot. If updating token metadata, this is the new active slot.
|
|
293
|
+
* @param tokenMetadataCalldata The new token metadata. If updating token metadata, this is the new token metadata at the updated active slot. Only non-empty iff updateTokenMetadata is true.
|
|
294
|
+
*/
|
|
295
|
+
function updateTokenStateAndMetadata(
|
|
296
|
+
uint256 tokenNumber,
|
|
297
|
+
bool updateSendState,
|
|
298
|
+
SendStates sendState,
|
|
299
|
+
uint16[] memory tokensSendingTo,
|
|
300
|
+
bool updateReceiveState,
|
|
301
|
+
ReceiveStates receiveState,
|
|
302
|
+
uint16[] memory tokensReceivingFrom,
|
|
303
|
+
bool updateTokenMetadata,
|
|
304
|
+
uint256 updatedActiveSlot,
|
|
305
|
+
TokenMetadataCalldata memory tokenMetadataCalldata
|
|
306
|
+
) external {
|
|
307
|
+
// CHECKS
|
|
308
|
+
// require token number is valid uint16
|
|
309
|
+
require(tokenNumber < type(uint16).max, "Invalid token number");
|
|
310
|
+
// msg.sender must be owner or valid delegate.xyz V2 of token owner
|
|
311
|
+
// @dev this also checks that the token number is valid (exists and has valid owner)
|
|
312
|
+
(bool isOwnerOrDelegate, address ownerAddress) = _isOwnerOrDelegate(
|
|
313
|
+
tokenNumber,
|
|
314
|
+
msg.sender
|
|
315
|
+
);
|
|
316
|
+
require(
|
|
317
|
+
isOwnerOrDelegate,
|
|
318
|
+
"Only owner or valid delegate.xyz V2 of token owner allowed"
|
|
319
|
+
);
|
|
320
|
+
require(
|
|
321
|
+
updateSendState || updateReceiveState || updateTokenMetadata,
|
|
322
|
+
"At least one update must be provided"
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
// CHECKS-AND-EFFECTS (BRANCHED LOGIC)
|
|
326
|
+
// update token metadata first, prior to any S/R state updates (since you need artwork before you can send/receive)
|
|
327
|
+
if (updateTokenMetadata) {
|
|
328
|
+
// EFFECTS
|
|
329
|
+
// update the token metadata
|
|
330
|
+
_updateTokenMetadata({
|
|
331
|
+
tokenNumber: tokenNumber,
|
|
332
|
+
updatedActiveSlot: updatedActiveSlot,
|
|
333
|
+
tokenMetadataCalldata: tokenMetadataCalldata,
|
|
334
|
+
ownerAddress: ownerAddress
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// update send/receive states second, after any metadata updates (since you need artwork before you can send/receive)
|
|
339
|
+
if (updateSendState) {
|
|
340
|
+
_updateSendState({
|
|
341
|
+
tokenNumber: tokenNumber,
|
|
342
|
+
sendState: sendState,
|
|
343
|
+
tokensSendingTo: tokensSendingTo,
|
|
344
|
+
ownerAddress: ownerAddress
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
if (updateReceiveState) {
|
|
348
|
+
_updateReceiveState({
|
|
349
|
+
tokenNumber: tokenNumber,
|
|
350
|
+
receiveState: receiveState,
|
|
351
|
+
tokensReceivingFrom: tokensReceivingFrom,
|
|
352
|
+
ownerAddress: ownerAddress
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* @notice Gets the live data for a given token.
|
|
359
|
+
* @param tokenNumber The token number to get the live data for.
|
|
360
|
+
* @param blockNumber The block number to get the live data for. Must be in latest 256 blocks.
|
|
361
|
+
* @param maxReceive The maximum number of tokens to receive, in each array of receivedTokensGeneral and receivedTokensTo.
|
|
362
|
+
* maxReceive must be less than or equal to MAX_RECEIVE_RATE_PER_BLOCK.
|
|
363
|
+
* Treats block number of 0 as latest completed block.
|
|
364
|
+
* Reverts if block number is in future - need block hash to be defined.
|
|
365
|
+
* NOTE: Each array of receivedTokensGeneral and receivedTokensTo has a maximum length of maxReceive,
|
|
366
|
+
* but their combined length may be greater than maxReceive. The art script should handle this by
|
|
367
|
+
* deterministically shuffling/sampling from the arrays if desired.
|
|
368
|
+
* WARNING: This function is designed for off-chain view calls only and may exceed block gas limits in cases where
|
|
369
|
+
* a token has many senders or is receiving from many tokens. It is not intended to be called within transactions.
|
|
370
|
+
* WARNING: Self-referential SendGeneral and SendTo tokens may be included in the results.
|
|
371
|
+
* @return sendState The send state of the token.
|
|
372
|
+
* @return receiveState The receive state of the token.
|
|
373
|
+
* @return receivedTokensGeneral The received tokens general of the token.
|
|
374
|
+
* @return receivedTokensTo The received tokens to of the token.
|
|
375
|
+
* @return numSendGeneral The number of tokens in the send general pool.
|
|
376
|
+
* @return numReceiveGeneral The number of tokens in the receive general pool.
|
|
377
|
+
* @return numSendingToMe The number of tokens sending to me.
|
|
378
|
+
* @return usedBlockNumber The block number used to generate the random numbers.
|
|
379
|
+
*/
|
|
380
|
+
function getLiveData(
|
|
381
|
+
uint256 tokenNumber,
|
|
382
|
+
uint256 blockNumber,
|
|
383
|
+
uint256 maxReceive
|
|
384
|
+
)
|
|
385
|
+
external
|
|
386
|
+
view
|
|
387
|
+
returns (
|
|
388
|
+
SendStates sendState,
|
|
389
|
+
ReceiveStates receiveState,
|
|
390
|
+
TokenLiveData[] memory receivedTokensGeneral,
|
|
391
|
+
TokenLiveData[] memory receivedTokensTo,
|
|
392
|
+
uint256 numSendGeneral,
|
|
393
|
+
uint256 numReceiveGeneral,
|
|
394
|
+
uint256 numSendingToMe,
|
|
395
|
+
uint256 usedBlockNumber
|
|
396
|
+
)
|
|
397
|
+
{
|
|
398
|
+
// CHECKS
|
|
399
|
+
// require token number is valid uint16 using SafeCast
|
|
400
|
+
tokenNumber.toUint16(); // will revert if tokenNumber > type(uint16).max
|
|
401
|
+
// require maxReceive is not too large
|
|
402
|
+
require(
|
|
403
|
+
maxReceive <= MAX_RECEIVE_RATE_PER_BLOCK,
|
|
404
|
+
"maxReceive is too large"
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
// treat block number of 0 as latest completed block
|
|
408
|
+
usedBlockNumber = blockNumber == 0 ? block.number - 1 : blockNumber;
|
|
409
|
+
if (usedBlockNumber > block.number - 1) {
|
|
410
|
+
revert("Block number in future - need block hash to be defined");
|
|
411
|
+
}
|
|
412
|
+
// ensure we get a valid block hash (must be in latest 256 blocks)
|
|
413
|
+
bytes32 blockhash_ = blockhash(usedBlockNumber); // returns zero if not in latest 256 blocks
|
|
414
|
+
require(
|
|
415
|
+
blockhash_ != bytes32(0),
|
|
416
|
+
"block hash not available - must be in lastest 256 blocks"
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
// populate send and receive states
|
|
420
|
+
sendState = _getSendState(tokenNumber);
|
|
421
|
+
receiveState = _getReceiveState(tokenNumber);
|
|
422
|
+
|
|
423
|
+
// populate the returned state variables
|
|
424
|
+
numSendGeneral = _sendGeneralTokens.length();
|
|
425
|
+
numReceiveGeneral = _receiveGeneralTokens.length();
|
|
426
|
+
numSendingToMe = _tokensSendingToMe[tokenNumber].length();
|
|
427
|
+
// used block number already populated
|
|
428
|
+
|
|
429
|
+
// case: neutral receiving state - no tokens received
|
|
430
|
+
if (receiveState == ReceiveStates.Neutral) {
|
|
431
|
+
return (
|
|
432
|
+
sendState,
|
|
433
|
+
receiveState,
|
|
434
|
+
new TokenLiveData[](0),
|
|
435
|
+
new TokenLiveData[](0),
|
|
436
|
+
numSendGeneral,
|
|
437
|
+
numReceiveGeneral,
|
|
438
|
+
numSendingToMe,
|
|
439
|
+
usedBlockNumber
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// case: receiveGeneral state - all tokens received - sample from general pool
|
|
444
|
+
if (receiveState == ReceiveStates.ReceiveGeneral) {
|
|
445
|
+
receivedTokensGeneral = _sampleReceivedTokensGeneral({
|
|
446
|
+
tokenNumber: tokenNumber,
|
|
447
|
+
blockhash_: blockhash_,
|
|
448
|
+
maxReceive: maxReceive
|
|
449
|
+
});
|
|
450
|
+
receivedTokensTo = _sampleReceivedTokensTo({
|
|
451
|
+
tokenNumber: tokenNumber,
|
|
452
|
+
blockhash_: blockhash_,
|
|
453
|
+
maxReceive: maxReceive
|
|
454
|
+
});
|
|
455
|
+
return (
|
|
456
|
+
sendState,
|
|
457
|
+
receiveState,
|
|
458
|
+
receivedTokensGeneral,
|
|
459
|
+
receivedTokensTo,
|
|
460
|
+
numSendGeneral,
|
|
461
|
+
numReceiveGeneral,
|
|
462
|
+
numSendingToMe,
|
|
463
|
+
usedBlockNumber
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// case: receiveFrom state - only tokens received from specific tokens
|
|
468
|
+
if (receiveState == ReceiveStates.ReceiveFrom) {
|
|
469
|
+
(
|
|
470
|
+
receivedTokensGeneral,
|
|
471
|
+
receivedTokensTo
|
|
472
|
+
) = _sampleTokensReceivedFrom({
|
|
473
|
+
tokenNumber: tokenNumber,
|
|
474
|
+
blockhash_: blockhash_,
|
|
475
|
+
maxReceive: maxReceive
|
|
476
|
+
});
|
|
477
|
+
return (
|
|
478
|
+
sendState,
|
|
479
|
+
receiveState,
|
|
480
|
+
receivedTokensGeneral,
|
|
481
|
+
receivedTokensTo,
|
|
482
|
+
numSendGeneral,
|
|
483
|
+
numReceiveGeneral,
|
|
484
|
+
numSendingToMe,
|
|
485
|
+
usedBlockNumber
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// case: receiveTo state - only tokens received to specific tokens
|
|
490
|
+
if (receiveState == ReceiveStates.ReceiveTo) {
|
|
491
|
+
receivedTokensTo = _sampleReceivedTokensTo({
|
|
492
|
+
tokenNumber: tokenNumber,
|
|
493
|
+
blockhash_: blockhash_,
|
|
494
|
+
maxReceive: maxReceive
|
|
495
|
+
});
|
|
496
|
+
return (
|
|
497
|
+
sendState,
|
|
498
|
+
receiveState,
|
|
499
|
+
new TokenLiveData[](0), // no tokens received from general pool
|
|
500
|
+
receivedTokensTo,
|
|
501
|
+
numSendGeneral,
|
|
502
|
+
numReceiveGeneral,
|
|
503
|
+
numSendingToMe,
|
|
504
|
+
usedBlockNumber
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* @notice Samples the received tokens general for a given token.
|
|
511
|
+
* Assumes token is receiving generally.
|
|
512
|
+
* @param tokenNumber The token number to get the received tokens general for.
|
|
513
|
+
* @param blockhash_ The block hash to get the received tokens general for.
|
|
514
|
+
* @param maxReceive The maximum number of tokens to receive.
|
|
515
|
+
* @return receivedTokensGeneral The received tokens general.
|
|
516
|
+
*/
|
|
517
|
+
function _sampleReceivedTokensGeneral(
|
|
518
|
+
uint256 tokenNumber,
|
|
519
|
+
bytes32 blockhash_,
|
|
520
|
+
uint256 maxReceive
|
|
521
|
+
) internal view returns (TokenLiveData[] memory) {
|
|
522
|
+
// calculate the general send pool size (tokens available to receive from)
|
|
523
|
+
uint256 sendGeneralLength = _sendGeneralTokens.length();
|
|
524
|
+
uint256 sampleQuantity = sendGeneralLength > maxReceive
|
|
525
|
+
? maxReceive
|
|
526
|
+
: sendGeneralLength;
|
|
527
|
+
|
|
528
|
+
// sample from general pool, quantity sampleQuantity
|
|
529
|
+
bytes32 seed = keccak256(abi.encodePacked(blockhash_, tokenNumber));
|
|
530
|
+
return
|
|
531
|
+
_sampleFromSendGeneralPool({seed: seed, quantity: sampleQuantity});
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* @notice Samples tokens from the general sending pool using an Feistel walk.
|
|
536
|
+
* @dev Uses FeistelWalkLib for efficient pseudo-random sampling over the EnumerableSet.
|
|
537
|
+
* @param seed The seed for pseudo-randomness.
|
|
538
|
+
* @param quantity The number of tokens to sample.
|
|
539
|
+
* @return result Array of TokenLiveData for sampled tokens.
|
|
540
|
+
*/
|
|
541
|
+
function _sampleFromSendGeneralPool(
|
|
542
|
+
bytes32 seed,
|
|
543
|
+
uint256 quantity
|
|
544
|
+
) internal view returns (TokenLiveData[] memory) {
|
|
545
|
+
// edge case: quantity is 0, return empty array
|
|
546
|
+
// @dev no coverage - this is a weird case
|
|
547
|
+
if (quantity == 0) {
|
|
548
|
+
return new TokenLiveData[](0);
|
|
549
|
+
}
|
|
550
|
+
// calculate the send general pool length to support making a plan
|
|
551
|
+
uint256 sendGeneralLength = _sendGeneralTokens.length();
|
|
552
|
+
// perform Feistel walk to sample token numbers from the set
|
|
553
|
+
FeistelWalkLib.Plan memory plan = FeistelWalkLib.makePlan({
|
|
554
|
+
seed: seed,
|
|
555
|
+
N: sendGeneralLength
|
|
556
|
+
});
|
|
557
|
+
uint256[] memory sampledTokenIndices = FeistelWalkLib.sample({
|
|
558
|
+
p: plan,
|
|
559
|
+
K: quantity
|
|
560
|
+
});
|
|
561
|
+
// populate TokenLiveData for each sampled token
|
|
562
|
+
uint256 sampledCount = sampledTokenIndices.length;
|
|
563
|
+
TokenLiveData[] memory result = new TokenLiveData[](sampledCount);
|
|
564
|
+
// @dev pull project id and core contract address into memory for efficient sload minimization
|
|
565
|
+
uint256 _projectId = CORE_PROJECT_ID;
|
|
566
|
+
address _coreContractAddress = CORE_CONTRACT_ADDRESS;
|
|
567
|
+
for (uint256 i = 0; i < sampledCount; i++) {
|
|
568
|
+
uint256 tokenNumber = _sendGeneralTokens.at(sampledTokenIndices[i]);
|
|
569
|
+
uint256 tokenId = ABHelpers.tokenIdFromProjectIdAndTokenNumber({
|
|
570
|
+
projectId: _projectId,
|
|
571
|
+
tokenNumber: tokenNumber
|
|
572
|
+
});
|
|
573
|
+
result[i] = _getLiveDataForToken({
|
|
574
|
+
tokenNumber: tokenNumber,
|
|
575
|
+
tokenId: tokenId,
|
|
576
|
+
coreContractAddress: _coreContractAddress
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
return result;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* @notice Samples the received tokens to for a given token, via a Feistel walk over the tokens sending to me.
|
|
584
|
+
* Assumes token is receiving generally.
|
|
585
|
+
* @param tokenNumber The token number to get the received tokens to for.
|
|
586
|
+
* @param blockhash_ The block hash to get the received tokens to for.
|
|
587
|
+
* @param maxReceive The maximum number of tokens to receive.
|
|
588
|
+
* @return receivedTokensTo The received tokens to.
|
|
589
|
+
*/
|
|
590
|
+
function _sampleReceivedTokensTo(
|
|
591
|
+
uint256 tokenNumber,
|
|
592
|
+
bytes32 blockhash_,
|
|
593
|
+
uint256 maxReceive
|
|
594
|
+
) internal view returns (TokenLiveData[] memory) {
|
|
595
|
+
// we perform a Feistel walk to sample token numbers from the set, and then get the live data for each token.
|
|
596
|
+
uint256 sendingToMeLength = _tokensSendingToMe[tokenNumber].length();
|
|
597
|
+
if (sendingToMeLength == 0) {
|
|
598
|
+
return new TokenLiveData[](0); // no tokens sending to me, return empty array
|
|
599
|
+
}
|
|
600
|
+
// return length will be lower of sendingToMeLength and maxReceive
|
|
601
|
+
uint256 sampleQuantity = sendingToMeLength > maxReceive
|
|
602
|
+
? maxReceive
|
|
603
|
+
: sendingToMeLength;
|
|
604
|
+
// iterate over the tokens sending to me; perform Feistel walk to sample token numbers from the set
|
|
605
|
+
bytes32 seed = keccak256(abi.encodePacked(blockhash_, tokenNumber));
|
|
606
|
+
FeistelWalkLib.Plan memory plan = FeistelWalkLib.makePlan({
|
|
607
|
+
seed: seed,
|
|
608
|
+
N: sendingToMeLength
|
|
609
|
+
});
|
|
610
|
+
// sample token numbers from the set
|
|
611
|
+
uint256[] memory sampledTokenIndices = FeistelWalkLib.sample({
|
|
612
|
+
p: plan,
|
|
613
|
+
K: sampleQuantity
|
|
614
|
+
});
|
|
615
|
+
// allocate array of TokenLiveData for the selected token numbers
|
|
616
|
+
TokenLiveData[] memory result = new TokenLiveData[](sampleQuantity);
|
|
617
|
+
// @dev pull project id and core contract address into memory for efficient sload minimization
|
|
618
|
+
uint256 _projectId = CORE_PROJECT_ID;
|
|
619
|
+
address _coreContractAddress = CORE_CONTRACT_ADDRESS;
|
|
620
|
+
EnumerableSetUint16.Uint16Set
|
|
621
|
+
storage tokensSendingToMe_ = _tokensSendingToMe[tokenNumber];
|
|
622
|
+
// for each selected token number, get the live data
|
|
623
|
+
for (uint256 i = 0; i < sampleQuantity; i++) {
|
|
624
|
+
uint256 selectedTokenNumber = tokensSendingToMe_.at(
|
|
625
|
+
sampledTokenIndices[i]
|
|
626
|
+
);
|
|
627
|
+
uint256 selectedTokenId = ABHelpers
|
|
628
|
+
.tokenIdFromProjectIdAndTokenNumber({
|
|
629
|
+
projectId: _projectId,
|
|
630
|
+
tokenNumber: selectedTokenNumber
|
|
631
|
+
});
|
|
632
|
+
result[i] = _getLiveDataForToken({
|
|
633
|
+
tokenNumber: selectedTokenNumber,
|
|
634
|
+
tokenId: selectedTokenId,
|
|
635
|
+
coreContractAddress: _coreContractAddress
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
return result;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* @notice Samples the tokens received from a given token, via a Feistel walk over the tokens receiving from me.
|
|
643
|
+
* @dev This function automatically deduplicates tokens if the tokensReceivingFrom array contains duplicates,
|
|
644
|
+
* ensuring each token appears at most once in the results. Self-referential tokens are also filtered out.
|
|
645
|
+
* @param tokenNumber The token number to get the tokens received from.
|
|
646
|
+
* @param blockhash_ The blockhash to use for pseudo-randomness.
|
|
647
|
+
* @param maxReceive The maximum number of tokens to receive.
|
|
648
|
+
* @return tokensReceivedFromGeneral The tokens received from the general pool.
|
|
649
|
+
* @return tokensReceivedFromTo The tokens received to the specific tokens.
|
|
650
|
+
*/
|
|
651
|
+
function _sampleTokensReceivedFrom(
|
|
652
|
+
uint256 tokenNumber,
|
|
653
|
+
bytes32 blockhash_,
|
|
654
|
+
uint256 maxReceive
|
|
655
|
+
)
|
|
656
|
+
internal
|
|
657
|
+
view
|
|
658
|
+
returns (
|
|
659
|
+
TokenLiveData[] memory tokensReceivedFromGeneral,
|
|
660
|
+
TokenLiveData[] memory tokensReceivedFromTo
|
|
661
|
+
)
|
|
662
|
+
{
|
|
663
|
+
// load the tokens receiving from into memory for efficient SSTORE2 load minimization
|
|
664
|
+
uint16[] memory tokensReceivingFrom = _tokensReceivingFrom[tokenNumber]
|
|
665
|
+
.getAll();
|
|
666
|
+
uint256 receivingFromMeLength = tokensReceivingFrom.length;
|
|
667
|
+
// we will iterate continuously over the tokens receiving from me, and include it if from the general pool,
|
|
668
|
+
// or if sending to me.
|
|
669
|
+
// if the token is not sending to me, or not sending generally, we skip it.
|
|
670
|
+
// we perform a Feistel walk to sample token numbers from the set, and then get the live data for each token.
|
|
671
|
+
// @dev no coverage - we never allow zero-length receiveFrom arrays, but check for redundancy.
|
|
672
|
+
if (receivingFromMeLength == 0) {
|
|
673
|
+
return (new TokenLiveData[](0), new TokenLiveData[](0)); // no tokens receiving from me, return empty arrays
|
|
674
|
+
}
|
|
675
|
+
// iterate over the tokens receiving from me; perform Feistel walk to sample token numbers from the set
|
|
676
|
+
bytes32 seed = keccak256(abi.encodePacked(blockhash_, tokenNumber));
|
|
677
|
+
FeistelWalkLib.Plan memory plan = FeistelWalkLib.makePlan({
|
|
678
|
+
seed: seed,
|
|
679
|
+
N: receivingFromMeLength
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
// SEND TO TOKEN ITERATION
|
|
683
|
+
// sample tokens from SendTo states (in separate function to avoid stack too deep)
|
|
684
|
+
uint256[]
|
|
685
|
+
memory selectedTokenNumbersTo = _sampleTokensReceivedFromSendToPool({
|
|
686
|
+
tokenNumber: tokenNumber,
|
|
687
|
+
tokensReceivingFrom: tokensReceivingFrom,
|
|
688
|
+
receivingFromMeLength: receivingFromMeLength,
|
|
689
|
+
plan: plan,
|
|
690
|
+
maxReceive: maxReceive
|
|
691
|
+
});
|
|
692
|
+
uint256 selectedTokenNumbersToLength = selectedTokenNumbersTo.length;
|
|
693
|
+
|
|
694
|
+
// SEND GENERAL TOKEN ITERATION
|
|
695
|
+
// sample tokens from general send pool (in separate function to avoid stack too deep)
|
|
696
|
+
uint256[]
|
|
697
|
+
memory selectedTokenNumbersGeneral = _sampleTokensReceivedFromGeneralPool({
|
|
698
|
+
tokenNumber: tokenNumber,
|
|
699
|
+
tokensReceivingFrom: tokensReceivingFrom,
|
|
700
|
+
receivingFromMeLength: receivingFromMeLength,
|
|
701
|
+
plan: plan,
|
|
702
|
+
maxReceive: maxReceive
|
|
703
|
+
});
|
|
704
|
+
uint256 selectedTokenNumbersGeneralLength = selectedTokenNumbersGeneral
|
|
705
|
+
.length;
|
|
706
|
+
|
|
707
|
+
// allocate array of TokenLiveData for the selected general token numbers
|
|
708
|
+
tokensReceivedFromGeneral = new TokenLiveData[](
|
|
709
|
+
selectedTokenNumbersGeneralLength
|
|
710
|
+
);
|
|
711
|
+
// @dev pull project id and core contract address into memory for efficient sload minimization
|
|
712
|
+
uint256 _projectId = CORE_PROJECT_ID;
|
|
713
|
+
address _coreContractAddress = CORE_CONTRACT_ADDRESS;
|
|
714
|
+
// for each selected general token number, get the live data
|
|
715
|
+
for (uint256 i = 0; i < selectedTokenNumbersGeneralLength; i++) {
|
|
716
|
+
uint256 selectedTokenNumber = selectedTokenNumbersGeneral[i];
|
|
717
|
+
tokensReceivedFromGeneral[i] = _getLiveDataForToken({
|
|
718
|
+
tokenNumber: selectedTokenNumber,
|
|
719
|
+
tokenId: ABHelpers.tokenIdFromProjectIdAndTokenNumber({
|
|
720
|
+
projectId: _projectId,
|
|
721
|
+
tokenNumber: selectedTokenNumber
|
|
722
|
+
}),
|
|
723
|
+
coreContractAddress: _coreContractAddress
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
// allocate array of TokenLiveData for the selected to token numbers
|
|
727
|
+
tokensReceivedFromTo = new TokenLiveData[](
|
|
728
|
+
selectedTokenNumbersToLength
|
|
729
|
+
);
|
|
730
|
+
// for each selected to token number, get the live data
|
|
731
|
+
for (uint256 i = 0; i < selectedTokenNumbersToLength; i++) {
|
|
732
|
+
uint256 selectedTokenNumber = selectedTokenNumbersTo[i];
|
|
733
|
+
tokensReceivedFromTo[i] = _getLiveDataForToken({
|
|
734
|
+
tokenNumber: selectedTokenNumber,
|
|
735
|
+
tokenId: ABHelpers.tokenIdFromProjectIdAndTokenNumber({
|
|
736
|
+
projectId: _projectId,
|
|
737
|
+
tokenNumber: selectedTokenNumber
|
|
738
|
+
}),
|
|
739
|
+
coreContractAddress: _coreContractAddress
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
return (tokensReceivedFromGeneral, tokensReceivedFromTo);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
* @notice Helper function to sample tokens from the SendTo pool that are in the tokensReceivingFrom array.
|
|
747
|
+
* @dev This function automatically deduplicates tokens if the tokensReceivingFrom array contains duplicates.
|
|
748
|
+
* Self-referential tokens are also filtered out.
|
|
749
|
+
* @param tokenNumber The token number we're checking receives from.
|
|
750
|
+
* @param tokensReceivingFrom The array of tokens that might be sending to this token.
|
|
751
|
+
* @param receivingFromMeLength The length of the tokensReceivingFrom array.
|
|
752
|
+
* @param plan The Feistel walk plan for sampling.
|
|
753
|
+
* @param maxReceive The maximum number of tokens to receive.
|
|
754
|
+
* @return selectedTokenNumbersTo Array of token numbers from SendTo states.
|
|
755
|
+
*/
|
|
756
|
+
function _sampleTokensReceivedFromSendToPool(
|
|
757
|
+
uint256 tokenNumber,
|
|
758
|
+
uint16[] memory tokensReceivingFrom,
|
|
759
|
+
uint256 receivingFromMeLength,
|
|
760
|
+
FeistelWalkLib.Plan memory plan,
|
|
761
|
+
uint256 maxReceive
|
|
762
|
+
) internal view returns (uint256[] memory selectedTokenNumbersTo) {
|
|
763
|
+
// allocate array for selected token numbers
|
|
764
|
+
selectedTokenNumbersTo = new uint256[](maxReceive);
|
|
765
|
+
uint256 selectedTokenNumbersToLength = 0;
|
|
766
|
+
|
|
767
|
+
// deduplication tracking array for tokensReceivingFrom (handles duplicates in user-provided array)
|
|
768
|
+
uint256[] memory seenTokens = new uint256[](maxReceive);
|
|
769
|
+
uint256 seenCount = 0;
|
|
770
|
+
|
|
771
|
+
for (uint256 i = 0; i < receivingFromMeLength; i++) {
|
|
772
|
+
uint256 sampledTokenNumber;
|
|
773
|
+
{
|
|
774
|
+
uint256 sampledTokenIndex = FeistelWalkLib.index(plan, i);
|
|
775
|
+
sampledTokenNumber = tokensReceivingFrom[sampledTokenIndex];
|
|
776
|
+
}
|
|
777
|
+
// skip self-referential (token receiving from itself)
|
|
778
|
+
if (sampledTokenNumber == tokenNumber) {
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
// check for duplicates in tokensReceivingFrom array
|
|
782
|
+
bool alreadySeen = false;
|
|
783
|
+
for (uint256 j = 0; j < seenCount; j++) {
|
|
784
|
+
if (seenTokens[j] == sampledTokenNumber) {
|
|
785
|
+
alreadySeen = true;
|
|
786
|
+
break;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
if (alreadySeen) {
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
// if sending to me, include it
|
|
793
|
+
if (
|
|
794
|
+
_tokensSendingToMe[tokenNumber].contains(
|
|
795
|
+
uint16(sampledTokenNumber)
|
|
796
|
+
)
|
|
797
|
+
) {
|
|
798
|
+
selectedTokenNumbersTo[
|
|
799
|
+
selectedTokenNumbersToLength
|
|
800
|
+
] = sampledTokenNumber;
|
|
801
|
+
selectedTokenNumbersToLength++;
|
|
802
|
+
// mark as seen to prevent duplicate processing
|
|
803
|
+
seenTokens[seenCount++] = sampledTokenNumber;
|
|
804
|
+
}
|
|
805
|
+
// if we have selected the maximum number of tokens, break
|
|
806
|
+
if (selectedTokenNumbersToLength >= maxReceive) {
|
|
807
|
+
break;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// resize array to actual length
|
|
812
|
+
assembly {
|
|
813
|
+
mstore(selectedTokenNumbersTo, selectedTokenNumbersToLength)
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* @notice Helper function to sample tokens from the general send pool that are in the tokensReceivingFrom array.
|
|
819
|
+
* @dev This function automatically deduplicates tokens if the tokensReceivingFrom array contains duplicates.
|
|
820
|
+
* Self-referential tokens are also filtered out.
|
|
821
|
+
* @param tokenNumber The token number we're checking receives from.
|
|
822
|
+
* @param tokensReceivingFrom The array of tokens that might be sending to this token.
|
|
823
|
+
* @param receivingFromMeLength The length of the tokensReceivingFrom array.
|
|
824
|
+
* @param plan The Feistel walk plan for sampling.
|
|
825
|
+
* @param maxReceive The maximum number of tokens to receive.
|
|
826
|
+
* @return selectedTokenNumbersGeneral Array of token numbers from the general pool.
|
|
827
|
+
*/
|
|
828
|
+
function _sampleTokensReceivedFromGeneralPool(
|
|
829
|
+
uint256 tokenNumber,
|
|
830
|
+
uint16[] memory tokensReceivingFrom,
|
|
831
|
+
uint256 receivingFromMeLength,
|
|
832
|
+
FeistelWalkLib.Plan memory plan,
|
|
833
|
+
uint256 maxReceive
|
|
834
|
+
) internal view returns (uint256[] memory selectedTokenNumbersGeneral) {
|
|
835
|
+
// allocate array for selected token numbers
|
|
836
|
+
selectedTokenNumbersGeneral = new uint256[](maxReceive);
|
|
837
|
+
uint256 selectedTokenNumbersGeneralLength = 0;
|
|
838
|
+
|
|
839
|
+
// deduplication tracking array for tokensReceivingFrom (handles duplicates in user-provided array)
|
|
840
|
+
uint256[] memory seenTokens = new uint256[](maxReceive);
|
|
841
|
+
uint256 seenCount = 0;
|
|
842
|
+
|
|
843
|
+
for (uint256 i = 0; i < receivingFromMeLength; i++) {
|
|
844
|
+
uint256 sampledTokenNumber;
|
|
845
|
+
{
|
|
846
|
+
uint256 sampledTokenIndex = FeistelWalkLib.index(plan, i);
|
|
847
|
+
sampledTokenNumber = tokensReceivingFrom[sampledTokenIndex];
|
|
848
|
+
}
|
|
849
|
+
// skip self-referential (token receiving from itself)
|
|
850
|
+
if (sampledTokenNumber == tokenNumber) {
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
// check for duplicates in tokensReceivingFrom array
|
|
854
|
+
bool alreadySeen = false;
|
|
855
|
+
for (uint256 j = 0; j < seenCount; j++) {
|
|
856
|
+
if (seenTokens[j] == sampledTokenNumber) {
|
|
857
|
+
alreadySeen = true;
|
|
858
|
+
break;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
if (alreadySeen) {
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
864
|
+
// if sending generally, include it for sure
|
|
865
|
+
if (_sendGeneralTokens.contains(uint16(sampledTokenNumber))) {
|
|
866
|
+
selectedTokenNumbersGeneral[
|
|
867
|
+
selectedTokenNumbersGeneralLength
|
|
868
|
+
] = sampledTokenNumber;
|
|
869
|
+
selectedTokenNumbersGeneralLength++;
|
|
870
|
+
// mark as seen to prevent duplicate processing
|
|
871
|
+
seenTokens[seenCount++] = sampledTokenNumber;
|
|
872
|
+
// if we have selected the maximum number of tokens, break
|
|
873
|
+
if (selectedTokenNumbersGeneralLength >= maxReceive) {
|
|
874
|
+
break;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// resize array to actual length
|
|
880
|
+
assembly {
|
|
881
|
+
mstore(
|
|
882
|
+
selectedTokenNumbersGeneral,
|
|
883
|
+
selectedTokenNumbersGeneralLength
|
|
884
|
+
)
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* @notice Gets the live data for a specific token.
|
|
890
|
+
* @dev Fetches token owner and metadata from storage.
|
|
891
|
+
* @param tokenNumber The token number to get live data for.
|
|
892
|
+
* @return liveData The TokenLiveData struct for the token.
|
|
893
|
+
*/
|
|
894
|
+
function _getLiveDataForToken(
|
|
895
|
+
uint256 tokenNumber,
|
|
896
|
+
uint256 tokenId,
|
|
897
|
+
address coreContractAddress
|
|
898
|
+
) internal view returns (TokenLiveData memory liveData) {
|
|
899
|
+
// get owner address
|
|
900
|
+
address ownerAddress = IERC721(coreContractAddress).ownerOf(tokenId);
|
|
901
|
+
|
|
902
|
+
// get active slot and metadata
|
|
903
|
+
uint256 activeSlot = _tokenAuxStateData[tokenNumber].activeSlot;
|
|
904
|
+
TokenMetadata storage metadata = _tokensMetadata[tokenNumber][
|
|
905
|
+
activeSlot
|
|
906
|
+
];
|
|
907
|
+
|
|
908
|
+
// populate live data
|
|
909
|
+
liveData.tokenNumber = tokenNumber;
|
|
910
|
+
liveData.ownerAddress = ownerAddress;
|
|
911
|
+
|
|
912
|
+
// get image and sound data
|
|
913
|
+
if (metadata.imageDataAddress != address(0)) {
|
|
914
|
+
liveData.imageDataCompressed = SSTORE2.read(
|
|
915
|
+
metadata.imageDataAddress
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
if (metadata.soundDataAddress != address(0)) {
|
|
919
|
+
liveData.soundDataCompressed = SSTORE2.read(
|
|
920
|
+
metadata.soundDataAddress
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// get owner ENS name using ENS Universal Resolver
|
|
925
|
+
// @dev try/catch pattern, avoids reverts, returns empty string if reverse lookup not configured
|
|
926
|
+
liveData.ownerEnsName = ENSLib.getEnsName(ownerAddress);
|
|
927
|
+
|
|
928
|
+
return liveData;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* @notice Updates the token metadata for a given token.
|
|
933
|
+
* Internal function - assumes token is valid
|
|
934
|
+
* @param tokenNumber The token number to update.
|
|
935
|
+
* @param updatedActiveSlot The new active slot.
|
|
936
|
+
* @param tokenMetadataCalldata The new token metadata.
|
|
937
|
+
* @param ownerAddress The address of the token owner.
|
|
938
|
+
*/
|
|
939
|
+
function _updateTokenMetadata(
|
|
940
|
+
uint256 tokenNumber,
|
|
941
|
+
uint256 updatedActiveSlot,
|
|
942
|
+
TokenMetadataCalldata memory tokenMetadataCalldata,
|
|
943
|
+
address ownerAddress
|
|
944
|
+
) internal {
|
|
945
|
+
// CHECKS
|
|
946
|
+
// updatedActiveSlot must be valid
|
|
947
|
+
require(updatedActiveSlot < NUM_METADATA_SLOTS, "Invalid active slot");
|
|
948
|
+
bool isUpdatingActiveSlot = _tokenAuxStateData[tokenNumber]
|
|
949
|
+
.activeSlot != updatedActiveSlot;
|
|
950
|
+
TokenMetadata storage tokenMetadataStorage = _tokensMetadata[
|
|
951
|
+
tokenNumber
|
|
952
|
+
][updatedActiveSlot];
|
|
953
|
+
// require that the new active slot has/will have image metadata if updating the active slot
|
|
954
|
+
// @dev this is to prevent participation in the pools without artwork
|
|
955
|
+
if (isUpdatingActiveSlot) {
|
|
956
|
+
require(
|
|
957
|
+
tokenMetadataCalldata.updateImage ||
|
|
958
|
+
tokenMetadataStorage.imageDataAddress != address(0),
|
|
959
|
+
"New active slot must have image metadata when updating active slot"
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// CHECKS-AND-EFFECTS (BRANCHED LOGIC)
|
|
964
|
+
uint256 tokenId = ABHelpers.tokenIdFromProjectIdAndTokenNumber({
|
|
965
|
+
projectId: CORE_PROJECT_ID,
|
|
966
|
+
tokenNumber: tokenNumber
|
|
967
|
+
});
|
|
968
|
+
// update the token metadata
|
|
969
|
+
// image data
|
|
970
|
+
if (tokenMetadataCalldata.updateImage) {
|
|
971
|
+
require(
|
|
972
|
+
tokenMetadataCalldata.imageDataCompressed.length > 0,
|
|
973
|
+
"Image data must be provided when updating"
|
|
974
|
+
);
|
|
975
|
+
require(
|
|
976
|
+
tokenMetadataCalldata.imageDataCompressed.length <=
|
|
977
|
+
MAX_IMAGE_DATA_LENGTH,
|
|
978
|
+
"Image data must be less than or equal to MAX_IMAGE_DATA_LENGTH"
|
|
979
|
+
);
|
|
980
|
+
// @dev image data, compressed + use sstore2 for efficient
|
|
981
|
+
tokenMetadataStorage.imageDataAddress = SSTORE2.write(
|
|
982
|
+
tokenMetadataCalldata.imageDataCompressed
|
|
983
|
+
);
|
|
984
|
+
tokenMetadataStorage.imageVersion += 1;
|
|
985
|
+
// emit PMPV0-indexable event for image data update
|
|
986
|
+
emit IPMPV0.TokenParamsConfigured({
|
|
987
|
+
coreContract: CORE_CONTRACT_ADDRESS,
|
|
988
|
+
tokenId: tokenId,
|
|
989
|
+
pmpInputs: _getPmpInputsForImageDataUpdate({
|
|
990
|
+
slot: updatedActiveSlot,
|
|
991
|
+
imageVersion: tokenMetadataStorage.imageVersion
|
|
992
|
+
}),
|
|
993
|
+
authAddresses: _getSingleElementAddressArray(ownerAddress)
|
|
994
|
+
});
|
|
995
|
+
} else {
|
|
996
|
+
require(
|
|
997
|
+
tokenMetadataCalldata.imageDataCompressed.length == 0,
|
|
998
|
+
"Image data must be empty when not updating"
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
// sound data
|
|
1002
|
+
if (tokenMetadataCalldata.updateSound) {
|
|
1003
|
+
// allow "clearing" the sound data by providing an empty bytes array
|
|
1004
|
+
if (tokenMetadataCalldata.soundDataCompressed.length == 0) {
|
|
1005
|
+
tokenMetadataStorage.soundDataAddress = address(0);
|
|
1006
|
+
tokenMetadataStorage.soundVersion += 1;
|
|
1007
|
+
// emit PMPV0-indexable event for sound data update
|
|
1008
|
+
emit IPMPV0.TokenParamsConfigured({
|
|
1009
|
+
coreContract: CORE_CONTRACT_ADDRESS,
|
|
1010
|
+
tokenId: tokenId,
|
|
1011
|
+
pmpInputs: _getPmpInputsForSoundDataUpdate({
|
|
1012
|
+
slot: updatedActiveSlot,
|
|
1013
|
+
soundVersion: tokenMetadataStorage.soundVersion
|
|
1014
|
+
}),
|
|
1015
|
+
authAddresses: _getSingleElementAddressArray(ownerAddress)
|
|
1016
|
+
});
|
|
1017
|
+
} else {
|
|
1018
|
+
require(
|
|
1019
|
+
tokenMetadataCalldata.soundDataCompressed.length <=
|
|
1020
|
+
MAX_SOUND_DATA_LENGTH,
|
|
1021
|
+
"Sound data must be less than or equal to MAX_SOUND_DATA_LENGTH"
|
|
1022
|
+
);
|
|
1023
|
+
// @dev sound data, compressed + use sstore2 for efficient
|
|
1024
|
+
tokenMetadataStorage.soundDataAddress = SSTORE2.write(
|
|
1025
|
+
tokenMetadataCalldata.soundDataCompressed
|
|
1026
|
+
);
|
|
1027
|
+
tokenMetadataStorage.soundVersion += 1;
|
|
1028
|
+
// emit PMPV0-indexable event for sound data update
|
|
1029
|
+
emit IPMPV0.TokenParamsConfigured({
|
|
1030
|
+
coreContract: CORE_CONTRACT_ADDRESS,
|
|
1031
|
+
tokenId: tokenId,
|
|
1032
|
+
pmpInputs: _getPmpInputsForSoundDataUpdate({
|
|
1033
|
+
slot: updatedActiveSlot,
|
|
1034
|
+
soundVersion: tokenMetadataStorage.soundVersion
|
|
1035
|
+
}),
|
|
1036
|
+
authAddresses: _getSingleElementAddressArray(ownerAddress)
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
} else {
|
|
1040
|
+
require(
|
|
1041
|
+
tokenMetadataCalldata.soundDataCompressed.length == 0,
|
|
1042
|
+
"Sound data must be empty when not updating"
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
// update the token's active slot if it has changed
|
|
1046
|
+
if (_tokenAuxStateData[tokenNumber].activeSlot != updatedActiveSlot) {
|
|
1047
|
+
// @dev we already checked that the new active slot has/will have image metadata if updating the active slot
|
|
1048
|
+
// update value and emit PMPV0-indexable event for active slot update
|
|
1049
|
+
_tokenAuxStateData[tokenNumber].activeSlot = updatedActiveSlot
|
|
1050
|
+
.toUint8();
|
|
1051
|
+
emit IPMPV0.TokenParamsConfigured({
|
|
1052
|
+
coreContract: CORE_CONTRACT_ADDRESS,
|
|
1053
|
+
tokenId: tokenId,
|
|
1054
|
+
pmpInputs: _getPmpInputsForActiveSlotUpdate(updatedActiveSlot),
|
|
1055
|
+
authAddresses: _getSingleElementAddressArray(ownerAddress)
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
/**
|
|
1061
|
+
* @notice Updates the send state for a given token.
|
|
1062
|
+
* Internal function - assumes token is valid
|
|
1063
|
+
* Assumes any interactions with send state and receive state are handled by the parent function.
|
|
1064
|
+
* @param tokenNumber The token number to update.
|
|
1065
|
+
* @param sendState The new send state. Valid values are SendGeneral, SendTo, Neutral.
|
|
1066
|
+
* @param tokensSendingTo Tokens to send this token to. Only non-empty iff sendState is SendTo.
|
|
1067
|
+
* @param ownerAddress The address of the token owner.
|
|
1068
|
+
*/
|
|
1069
|
+
function _updateSendState(
|
|
1070
|
+
uint256 tokenNumber,
|
|
1071
|
+
SendStates sendState,
|
|
1072
|
+
uint16[] memory tokensSendingTo,
|
|
1073
|
+
address ownerAddress
|
|
1074
|
+
) internal {
|
|
1075
|
+
// CHECKS
|
|
1076
|
+
// require that the token has image metadata at the active slot
|
|
1077
|
+
require(
|
|
1078
|
+
_tokensMetadata[tokenNumber][
|
|
1079
|
+
_tokenAuxStateData[tokenNumber].activeSlot
|
|
1080
|
+
].imageDataAddress != address(0),
|
|
1081
|
+
"Token must have image metadata at active slot when particpating"
|
|
1082
|
+
);
|
|
1083
|
+
// enforce SendTo arrays length
|
|
1084
|
+
(sendState == SendStates.SendTo)
|
|
1085
|
+
? require(
|
|
1086
|
+
tokensSendingTo.length > 0,
|
|
1087
|
+
"tokensSendingTo must be non-empty"
|
|
1088
|
+
)
|
|
1089
|
+
: require(
|
|
1090
|
+
tokensSendingTo.length == 0,
|
|
1091
|
+
"tokensSendingTo must be empty"
|
|
1092
|
+
);
|
|
1093
|
+
// enforce tokensSendingTo length is not too long
|
|
1094
|
+
require(
|
|
1095
|
+
tokensSendingTo.length <= MAX_SENDING_TO_LENGTH,
|
|
1096
|
+
"tokensSendingTo must be less than or equal to MAX_SENDING_TO_LENGTH"
|
|
1097
|
+
);
|
|
1098
|
+
|
|
1099
|
+
// EFFECTS
|
|
1100
|
+
uint256 tokenId = ABHelpers.tokenIdFromProjectIdAndTokenNumber({
|
|
1101
|
+
projectId: CORE_PROJECT_ID,
|
|
1102
|
+
tokenNumber: tokenNumber
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
// Step 1. clear previous send state, based on storage's send state
|
|
1106
|
+
SendStates previousSendState = _getSendState(tokenNumber);
|
|
1107
|
+
if (previousSendState == SendStates.SendGeneral) {
|
|
1108
|
+
// simply remove the token from the send general set
|
|
1109
|
+
_sendGeneralTokens.remove(uint16(tokenNumber));
|
|
1110
|
+
} else if (previousSendState == SendStates.SendTo) {
|
|
1111
|
+
// pop from every previous token's "sending to me" set, which is a O(n) operation for n tokens previously sent to
|
|
1112
|
+
// @dev pull into memory for efficient sload minimization
|
|
1113
|
+
uint16[] memory previousTokensSendingTo = _tokensSendingTo[
|
|
1114
|
+
tokenNumber
|
|
1115
|
+
].getAll();
|
|
1116
|
+
uint256 previousTokensSendingToLength = previousTokensSendingTo
|
|
1117
|
+
.length;
|
|
1118
|
+
for (uint256 i = 0; i < previousTokensSendingToLength; i++) {
|
|
1119
|
+
uint256 sendingToTokenNumber = previousTokensSendingTo[i];
|
|
1120
|
+
_tokensSendingToMe[sendingToTokenNumber].remove(
|
|
1121
|
+
uint16(tokenNumber)
|
|
1122
|
+
);
|
|
1123
|
+
}
|
|
1124
|
+
// clear my previous send to array
|
|
1125
|
+
_tokensSendingTo[tokenNumber].clear();
|
|
1126
|
+
// emit custom event for indexing of send-to if applicable
|
|
1127
|
+
// @dev acknowledge that this may cost gas for large arrays, but prefer strong indexing behavior, and
|
|
1128
|
+
// still minor relative to the cost of the SSTORE operations when storing the send-to array
|
|
1129
|
+
emit ISRHooks.TokenSendingToUpdated({
|
|
1130
|
+
coreContract: CORE_CONTRACT_ADDRESS,
|
|
1131
|
+
tokenId: tokenId,
|
|
1132
|
+
tokensSendingTo: new uint16[](0)
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
// case: neutral state - no-op
|
|
1136
|
+
|
|
1137
|
+
// Step 2. populate the new send state
|
|
1138
|
+
if (sendState == SendStates.SendGeneral) {
|
|
1139
|
+
_sendGeneralTokens.add(uint16(tokenNumber));
|
|
1140
|
+
} else if (sendState == SendStates.SendTo) {
|
|
1141
|
+
// push the tokens to my send to array
|
|
1142
|
+
_tokensSendingTo[tokenNumber].store(tokensSendingTo);
|
|
1143
|
+
// push me into every token's "sending to me" set
|
|
1144
|
+
// @dev no validation of target tokens needed - if they don't exist or aren't participating,
|
|
1145
|
+
// they simply won't appear in getLiveData results due to state set membership checks
|
|
1146
|
+
uint256 tokensSendingToLength = tokensSendingTo.length;
|
|
1147
|
+
for (uint256 i = 0; i < tokensSendingToLength; i++) {
|
|
1148
|
+
uint256 sendingToTokenNumber = tokensSendingTo[i];
|
|
1149
|
+
// add the token to the "sending to me" Set
|
|
1150
|
+
_tokensSendingToMe[sendingToTokenNumber].add(
|
|
1151
|
+
uint16(tokenNumber)
|
|
1152
|
+
);
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
// case: neutral state - no-op
|
|
1156
|
+
|
|
1157
|
+
// emit PMPV0-indexable event for send state update
|
|
1158
|
+
emit IPMPV0.TokenParamsConfigured({
|
|
1159
|
+
coreContract: CORE_CONTRACT_ADDRESS,
|
|
1160
|
+
tokenId: tokenId,
|
|
1161
|
+
pmpInputs: _getPmpInputsForSendStateUpdate(sendState),
|
|
1162
|
+
authAddresses: _getSingleElementAddressArray(ownerAddress)
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
// emit custom event for indexing of send-to if applicable
|
|
1166
|
+
// @dev acknowledge that this may cost gas for large arrays, but prefer strong indexing behavior, and
|
|
1167
|
+
// still minor relative to the cost of the SSTORE operations when storing the send-to array
|
|
1168
|
+
if (sendState == SendStates.SendTo) {
|
|
1169
|
+
emit ISRHooks.TokenSendingToUpdated({
|
|
1170
|
+
coreContract: CORE_CONTRACT_ADDRESS,
|
|
1171
|
+
tokenId: tokenId,
|
|
1172
|
+
tokensSendingTo: tokensSendingTo
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
/**
|
|
1178
|
+
* @notice Updates the receive state for a given token.
|
|
1179
|
+
* Internal function - assumes token is valid
|
|
1180
|
+
* Assumes any interactions with send state and receive state are handled by the parent function.
|
|
1181
|
+
* @param tokenNumber The token number to update.
|
|
1182
|
+
* @param receiveState The new receive state. Valid values are ReceiveGeneral, ReceiveFrom, Neutral.
|
|
1183
|
+
* @param tokensReceivingFrom Tokens this token is open to receive from. Only non-empty iff receiveState is ReceiveFrom.
|
|
1184
|
+
* @param ownerAddress The address of the token owner.
|
|
1185
|
+
*/
|
|
1186
|
+
function _updateReceiveState(
|
|
1187
|
+
uint256 tokenNumber,
|
|
1188
|
+
ReceiveStates receiveState,
|
|
1189
|
+
uint16[] memory tokensReceivingFrom,
|
|
1190
|
+
address ownerAddress
|
|
1191
|
+
) internal {
|
|
1192
|
+
// CHECKS
|
|
1193
|
+
// require that the token has image metadata at the active slot
|
|
1194
|
+
require(
|
|
1195
|
+
_tokensMetadata[tokenNumber][
|
|
1196
|
+
_tokenAuxStateData[tokenNumber].activeSlot
|
|
1197
|
+
].imageDataAddress != address(0),
|
|
1198
|
+
"Token must have image metadata at active slot when particpating"
|
|
1199
|
+
);
|
|
1200
|
+
// enforce ReceiveFrom arrays length
|
|
1201
|
+
(receiveState == ReceiveStates.ReceiveFrom)
|
|
1202
|
+
? require(
|
|
1203
|
+
tokensReceivingFrom.length > 0,
|
|
1204
|
+
"tokensReceivingFrom must be non-empty"
|
|
1205
|
+
)
|
|
1206
|
+
: require(
|
|
1207
|
+
tokensReceivingFrom.length == 0,
|
|
1208
|
+
"tokensReceivingFrom must be empty"
|
|
1209
|
+
);
|
|
1210
|
+
// enforce tokensReceivingFrom length is not too long
|
|
1211
|
+
require(
|
|
1212
|
+
tokensReceivingFrom.length <= MAX_RECEIVING_FROM_ARRAY_LENGTH,
|
|
1213
|
+
"tokensReceivingFrom must be less than or equal to MAX_RECEIVING_FROM_ARRAY_LENGTH"
|
|
1214
|
+
);
|
|
1215
|
+
|
|
1216
|
+
// EFFECTS
|
|
1217
|
+
uint256 tokenId = ABHelpers.tokenIdFromProjectIdAndTokenNumber({
|
|
1218
|
+
projectId: CORE_PROJECT_ID,
|
|
1219
|
+
tokenNumber: tokenNumber
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
// Step 1. clear previous receive state, based on storage's receive state
|
|
1223
|
+
ReceiveStates previousReceiveState = _getReceiveState(tokenNumber);
|
|
1224
|
+
if (previousReceiveState == ReceiveStates.ReceiveGeneral) {
|
|
1225
|
+
// simply remove the token from the receive general set
|
|
1226
|
+
_receiveGeneralTokens.remove(uint16(tokenNumber));
|
|
1227
|
+
} else if (previousReceiveState == ReceiveStates.ReceiveFrom) {
|
|
1228
|
+
// simple removal of my receive from array - we don't have a reverse mapping of the array across other tokens
|
|
1229
|
+
_tokensReceivingFrom[tokenNumber].clear();
|
|
1230
|
+
|
|
1231
|
+
// emit custom event for indexing of receive-from if applicable
|
|
1232
|
+
// @dev acknowledge that this may cost gas for large arrays, but prefer strong indexing behavior, and
|
|
1233
|
+
// still minor relative to the cost of the SSTORE operations when storing the receive-from array
|
|
1234
|
+
emit ISRHooks.TokenReceivingFromUpdated({
|
|
1235
|
+
coreContract: CORE_CONTRACT_ADDRESS,
|
|
1236
|
+
tokenId: tokenId,
|
|
1237
|
+
tokensReceivingFrom: new uint16[](0)
|
|
1238
|
+
});
|
|
1239
|
+
} else if (previousReceiveState == ReceiveStates.ReceiveTo) {
|
|
1240
|
+
// set isReceivingTo to false
|
|
1241
|
+
_tokenAuxStateData[tokenNumber].isReceivingTo = false;
|
|
1242
|
+
}
|
|
1243
|
+
// case: neutral state - no-op
|
|
1244
|
+
|
|
1245
|
+
// Step 2. populate the new receive state
|
|
1246
|
+
if (receiveState == ReceiveStates.ReceiveGeneral) {
|
|
1247
|
+
_receiveGeneralTokens.add(uint16(tokenNumber));
|
|
1248
|
+
} else if (receiveState == ReceiveStates.ReceiveFrom) {
|
|
1249
|
+
// @dev no validation of target tokens needed - if they don't exist or aren't participating,
|
|
1250
|
+
// they simply won't appear in getLiveData results due to state set membership checks
|
|
1251
|
+
// (tokens must be in _sendGeneralTokens or _tokensSendingToMe to be included)
|
|
1252
|
+
_tokensReceivingFrom[tokenNumber].store(tokensReceivingFrom);
|
|
1253
|
+
} else if (receiveState == ReceiveStates.ReceiveTo) {
|
|
1254
|
+
// set isReceivingTo to true
|
|
1255
|
+
_tokenAuxStateData[tokenNumber].isReceivingTo = true;
|
|
1256
|
+
}
|
|
1257
|
+
// case: neutral state - no-op
|
|
1258
|
+
|
|
1259
|
+
// emit PMPV0-indexable event for receive state update
|
|
1260
|
+
emit IPMPV0.TokenParamsConfigured({
|
|
1261
|
+
coreContract: CORE_CONTRACT_ADDRESS,
|
|
1262
|
+
tokenId: tokenId,
|
|
1263
|
+
pmpInputs: _getPmpInputsForReceiveStateUpdate(receiveState),
|
|
1264
|
+
authAddresses: _getSingleElementAddressArray(ownerAddress)
|
|
1265
|
+
});
|
|
1266
|
+
|
|
1267
|
+
// emit custom event for indexing of receive-from if applicable
|
|
1268
|
+
// @dev acknowledge that this may cost gas for large arrays, but prefer strong indexing behavior, and
|
|
1269
|
+
// still minor relative to the cost of the SSTORE operations when storing the receive-from array
|
|
1270
|
+
if (receiveState == ReceiveStates.ReceiveFrom) {
|
|
1271
|
+
emit ISRHooks.TokenReceivingFromUpdated({
|
|
1272
|
+
coreContract: CORE_CONTRACT_ADDRESS,
|
|
1273
|
+
tokenId: tokenId,
|
|
1274
|
+
tokensReceivingFrom: tokensReceivingFrom
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
/**
|
|
1280
|
+
* @notice Gets the state of the general pools.
|
|
1281
|
+
* @return sendGeneralTokensLength The length of the send general tokens.
|
|
1282
|
+
* @return receiveGeneralTokensLength The length of the receive general tokens.
|
|
1283
|
+
*/
|
|
1284
|
+
function getGeneralPoolState()
|
|
1285
|
+
external
|
|
1286
|
+
view
|
|
1287
|
+
returns (
|
|
1288
|
+
uint256 sendGeneralTokensLength,
|
|
1289
|
+
uint256 receiveGeneralTokensLength
|
|
1290
|
+
)
|
|
1291
|
+
{
|
|
1292
|
+
return (_sendGeneralTokens.length(), _receiveGeneralTokens.length());
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
/**
|
|
1296
|
+
* @notice Gets the state of a given token.
|
|
1297
|
+
* @param tokenNumber The token number to get the state for.
|
|
1298
|
+
* @return sendState The send state.
|
|
1299
|
+
* @return receiveState The receive state.
|
|
1300
|
+
* @return tokensSendingTo The tokens sending to the token.
|
|
1301
|
+
* @return tokensReceivingFrom The tokens receiving from the token.
|
|
1302
|
+
* @return activeSlot The active slot.
|
|
1303
|
+
* @return activeSlotTokenMetadata The token metadata at the active slot.
|
|
1304
|
+
* @return ownerAddress The address of the token owner.
|
|
1305
|
+
*/
|
|
1306
|
+
function getTokenState(
|
|
1307
|
+
uint256 tokenNumber
|
|
1308
|
+
)
|
|
1309
|
+
external
|
|
1310
|
+
view
|
|
1311
|
+
returns (
|
|
1312
|
+
SendStates sendState,
|
|
1313
|
+
ReceiveStates receiveState,
|
|
1314
|
+
uint16[] memory tokensSendingTo,
|
|
1315
|
+
uint16[] memory tokensReceivingFrom,
|
|
1316
|
+
uint256 activeSlot,
|
|
1317
|
+
TokenMetadataView memory activeSlotTokenMetadata,
|
|
1318
|
+
address ownerAddress
|
|
1319
|
+
)
|
|
1320
|
+
{
|
|
1321
|
+
// require token number is valid uint16 using SafeCast
|
|
1322
|
+
tokenNumber.toUint16(); // will revert if tokenNumber > type(uint16).max
|
|
1323
|
+
|
|
1324
|
+
sendState = _getSendState(tokenNumber);
|
|
1325
|
+
receiveState = _getReceiveState(tokenNumber);
|
|
1326
|
+
tokensSendingTo = ImmutableUint16Array.getAll({
|
|
1327
|
+
storageArray: _tokensSendingTo[tokenNumber]
|
|
1328
|
+
});
|
|
1329
|
+
tokensReceivingFrom = ImmutableUint16Array.getAll({
|
|
1330
|
+
storageArray: _tokensReceivingFrom[tokenNumber]
|
|
1331
|
+
});
|
|
1332
|
+
activeSlot = _tokenAuxStateData[tokenNumber].activeSlot;
|
|
1333
|
+
TokenMetadata storage tokenMetadataStorage = _tokensMetadata[
|
|
1334
|
+
tokenNumber
|
|
1335
|
+
][activeSlot];
|
|
1336
|
+
activeSlotTokenMetadata.imageDataCompressed = tokenMetadataStorage
|
|
1337
|
+
.imageDataAddress != address(0)
|
|
1338
|
+
? SSTORE2.read(tokenMetadataStorage.imageDataAddress)
|
|
1339
|
+
: bytes("");
|
|
1340
|
+
activeSlotTokenMetadata.imageVersion = tokenMetadataStorage
|
|
1341
|
+
.imageVersion;
|
|
1342
|
+
activeSlotTokenMetadata.soundDataCompressed = tokenMetadataStorage
|
|
1343
|
+
.soundDataAddress != address(0)
|
|
1344
|
+
? SSTORE2.read(tokenMetadataStorage.soundDataAddress)
|
|
1345
|
+
: bytes("");
|
|
1346
|
+
activeSlotTokenMetadata.soundVersion = tokenMetadataStorage
|
|
1347
|
+
.soundVersion;
|
|
1348
|
+
uint256 tokenId = ABHelpers.tokenIdFromProjectIdAndTokenNumber({
|
|
1349
|
+
projectId: CORE_PROJECT_ID,
|
|
1350
|
+
tokenNumber: tokenNumber
|
|
1351
|
+
});
|
|
1352
|
+
ownerAddress = IERC721(CORE_CONTRACT_ADDRESS).ownerOf(tokenId);
|
|
1353
|
+
return (
|
|
1354
|
+
sendState,
|
|
1355
|
+
receiveState,
|
|
1356
|
+
tokensSendingTo,
|
|
1357
|
+
tokensReceivingFrom,
|
|
1358
|
+
activeSlot,
|
|
1359
|
+
activeSlotTokenMetadata,
|
|
1360
|
+
ownerAddress
|
|
1361
|
+
);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
/**
|
|
1365
|
+
* @notice Gets the token metadata at a given slot.
|
|
1366
|
+
* @param tokenNumber The token number to get the token metadata for.
|
|
1367
|
+
* @param slot The slot to get the token metadata for.
|
|
1368
|
+
* @return tokenMetadata The token metadata at the given slot.
|
|
1369
|
+
*/
|
|
1370
|
+
function getTokenMetadataAtSlot(
|
|
1371
|
+
uint256 tokenNumber,
|
|
1372
|
+
uint256 slot
|
|
1373
|
+
) external view returns (TokenMetadataView memory tokenMetadata) {
|
|
1374
|
+
// require token number is valid uint16 using SafeCast
|
|
1375
|
+
tokenNumber.toUint16(); // will revert if tokenNumber > type(uint16).max
|
|
1376
|
+
// require slot is valid
|
|
1377
|
+
require(slot < NUM_METADATA_SLOTS, "Invalid slot");
|
|
1378
|
+
|
|
1379
|
+
TokenMetadata storage tokenMetadataStorage = _tokensMetadata[
|
|
1380
|
+
tokenNumber
|
|
1381
|
+
][slot];
|
|
1382
|
+
tokenMetadata.imageDataCompressed = tokenMetadataStorage
|
|
1383
|
+
.imageDataAddress != address(0)
|
|
1384
|
+
? SSTORE2.read(tokenMetadataStorage.imageDataAddress)
|
|
1385
|
+
: bytes("");
|
|
1386
|
+
tokenMetadata.imageVersion = tokenMetadataStorage.imageVersion;
|
|
1387
|
+
tokenMetadata.soundDataCompressed = tokenMetadataStorage
|
|
1388
|
+
.soundDataAddress != address(0)
|
|
1389
|
+
? SSTORE2.read(tokenMetadataStorage.soundDataAddress)
|
|
1390
|
+
: bytes("");
|
|
1391
|
+
tokenMetadata.soundVersion = tokenMetadataStorage.soundVersion;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
/**
|
|
1395
|
+
* @notice Gets the tokens sending to a given token.
|
|
1396
|
+
* WARNING: This function is unbounded in gas cost, and is designed to be used by view accessors only. Use with caution.
|
|
1397
|
+
* @param tokenNumber The token number to get the tokens sending to for.
|
|
1398
|
+
* @return tokensSendingTo The token numbers sending to the given token.
|
|
1399
|
+
*/
|
|
1400
|
+
function getTokensSendingToToken(
|
|
1401
|
+
uint256 tokenNumber
|
|
1402
|
+
) external view returns (uint256[] memory tokensSendingTo) {
|
|
1403
|
+
// require token number is valid uint16 using SafeCast
|
|
1404
|
+
tokenNumber.toUint16(); // will revert if tokenNumber > type(uint16).max
|
|
1405
|
+
|
|
1406
|
+
return _tokensSendingToMe[tokenNumber].values();
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
/**
|
|
1410
|
+
* @notice Gets the send state for a given token.
|
|
1411
|
+
* Internal function - assumes token is valid
|
|
1412
|
+
* @dev uses derived state to prefer SLOAD over SSTORE for efficiency
|
|
1413
|
+
* @param tokenNumber The token number to get the send state for.
|
|
1414
|
+
* @return sendState The send state.
|
|
1415
|
+
*/
|
|
1416
|
+
function _getSendState(
|
|
1417
|
+
uint256 tokenNumber
|
|
1418
|
+
) internal view returns (SendStates) {
|
|
1419
|
+
// check for existence in send general set
|
|
1420
|
+
if (_sendGeneralTokens.contains(uint16(tokenNumber))) {
|
|
1421
|
+
return SendStates.SendGeneral;
|
|
1422
|
+
}
|
|
1423
|
+
// check for non-empty send to array
|
|
1424
|
+
if (!ImmutableUint16Array.isEmpty(_tokensSendingTo[tokenNumber])) {
|
|
1425
|
+
return SendStates.SendTo;
|
|
1426
|
+
}
|
|
1427
|
+
// must be in neutral state
|
|
1428
|
+
return SendStates.Neutral;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
/**
|
|
1432
|
+
* @notice Converts a send state to a string.
|
|
1433
|
+
* Internal function - assumes send state is valid
|
|
1434
|
+
* @param sendState The send state to convert to a string.
|
|
1435
|
+
* @return string The string representation of the send state.
|
|
1436
|
+
*/
|
|
1437
|
+
function _sendStateToString(
|
|
1438
|
+
SendStates sendState
|
|
1439
|
+
) internal pure returns (string memory) {
|
|
1440
|
+
return
|
|
1441
|
+
sendState == SendStates.SendGeneral
|
|
1442
|
+
? "SendGeneral"
|
|
1443
|
+
: sendState == SendStates.SendTo
|
|
1444
|
+
? "SendTo"
|
|
1445
|
+
: "Neutral";
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
/**
|
|
1449
|
+
* @notice Gets the receive state for a given token.
|
|
1450
|
+
* Internal function - assumes token is valid
|
|
1451
|
+
* @dev uses derived state to prefer SLOAD over SSTORE for efficiency
|
|
1452
|
+
* @param tokenNumber The token number to get the receive state for.
|
|
1453
|
+
* @return receiveState The receive state.
|
|
1454
|
+
*/
|
|
1455
|
+
function _getReceiveState(
|
|
1456
|
+
uint256 tokenNumber
|
|
1457
|
+
) internal view returns (ReceiveStates) {
|
|
1458
|
+
// check for existence in receive general set
|
|
1459
|
+
if (_receiveGeneralTokens.contains(uint16(tokenNumber))) {
|
|
1460
|
+
return ReceiveStates.ReceiveGeneral;
|
|
1461
|
+
}
|
|
1462
|
+
// check for non-empty receive from array
|
|
1463
|
+
if (!ImmutableUint16Array.isEmpty(_tokensReceivingFrom[tokenNumber])) {
|
|
1464
|
+
return ReceiveStates.ReceiveFrom;
|
|
1465
|
+
}
|
|
1466
|
+
// check for isReceivingTo
|
|
1467
|
+
if (_tokenAuxStateData[tokenNumber].isReceivingTo) {
|
|
1468
|
+
return ReceiveStates.ReceiveTo;
|
|
1469
|
+
}
|
|
1470
|
+
// must be in neutral state
|
|
1471
|
+
return ReceiveStates.Neutral;
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
/**
|
|
1475
|
+
* @notice Converts a receive state to a string.
|
|
1476
|
+
* Internal function - assumes receive state is valid
|
|
1477
|
+
* @param receiveState The receive state to convert to a string.
|
|
1478
|
+
* @return string The string representation of the receive state.
|
|
1479
|
+
*/
|
|
1480
|
+
function _receiveStateToString(
|
|
1481
|
+
ReceiveStates receiveState
|
|
1482
|
+
) internal pure returns (string memory) {
|
|
1483
|
+
if (receiveState == ReceiveStates.Neutral) {
|
|
1484
|
+
return "Neutral";
|
|
1485
|
+
}
|
|
1486
|
+
if (receiveState == ReceiveStates.ReceiveGeneral) {
|
|
1487
|
+
return "ReceiveGeneral";
|
|
1488
|
+
}
|
|
1489
|
+
if (receiveState == ReceiveStates.ReceiveFrom) {
|
|
1490
|
+
return "ReceiveFrom";
|
|
1491
|
+
}
|
|
1492
|
+
if (receiveState == ReceiveStates.ReceiveTo) {
|
|
1493
|
+
return "ReceiveTo";
|
|
1494
|
+
}
|
|
1495
|
+
// case: invalid receive state (no coverage)
|
|
1496
|
+
revert("Invalid receive state");
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
function _getHexStringFromSSTORE2(
|
|
1500
|
+
address sstore2Address
|
|
1501
|
+
) internal view returns (string memory) {
|
|
1502
|
+
// case: empty sstore2 address
|
|
1503
|
+
if (sstore2Address == address(0)) {
|
|
1504
|
+
return "";
|
|
1505
|
+
}
|
|
1506
|
+
// case: non-empty sstore2 address
|
|
1507
|
+
return toHexString(SSTORE2.read(sstore2Address));
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
/**
|
|
1511
|
+
* @notice Checks if the contract supports an interface.
|
|
1512
|
+
* @dev This function is required by the ERC165 interface detection pattern.
|
|
1513
|
+
* @param interfaceId The interface identifier to check.
|
|
1514
|
+
* @return bool True if the contract supports the interface, false otherwise.
|
|
1515
|
+
*/
|
|
1516
|
+
function supportsInterface(
|
|
1517
|
+
bytes4 interfaceId
|
|
1518
|
+
) public view override(AbstractPMPAugmentHook) returns (bool) {
|
|
1519
|
+
return
|
|
1520
|
+
interfaceId == type(IPMPAugmentHook).interfaceId ||
|
|
1521
|
+
super.supportsInterface(interfaceId);
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
/**
|
|
1525
|
+
* @notice Gets the PMP inputs for the receive state update.
|
|
1526
|
+
* @param receiveState The receive state to update.
|
|
1527
|
+
* @return pmpInputs The PMP inputs for the receive state update.
|
|
1528
|
+
* @dev This function is used to get the PMP inputs for the receive state update, based on what this hook uses.
|
|
1529
|
+
*/
|
|
1530
|
+
function _getPmpInputsForReceiveStateUpdate(
|
|
1531
|
+
ReceiveStates receiveState
|
|
1532
|
+
) internal pure returns (IPMPV0.PMPInput[] memory) {
|
|
1533
|
+
// build PMPInputs for the receive state update, based on what this hook uses
|
|
1534
|
+
IPMPV0.PMPInput[] memory pmpInputs = new IPMPV0.PMPInput[](1);
|
|
1535
|
+
pmpInputs[0] = IPMPV0.PMPInput({
|
|
1536
|
+
key: "ReceiveState",
|
|
1537
|
+
configuredParamType: IPMPV0.ParamType.Select,
|
|
1538
|
+
configuredValue: bytes32(uint256(receiveState)), // @dev enums are aligned with select options
|
|
1539
|
+
configuringArtistString: false,
|
|
1540
|
+
configuredValueString: ""
|
|
1541
|
+
});
|
|
1542
|
+
return pmpInputs;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
/**
|
|
1546
|
+
* @notice Gets the PMP inputs for the send state update.
|
|
1547
|
+
* @param sendState The send state to update.
|
|
1548
|
+
* @return pmpInputs The PMP inputs for the send state update.
|
|
1549
|
+
* @dev This function is used to get the PMP inputs for the send state update, based on what this hook uses.
|
|
1550
|
+
*/
|
|
1551
|
+
function _getPmpInputsForSendStateUpdate(
|
|
1552
|
+
SendStates sendState
|
|
1553
|
+
) internal pure returns (IPMPV0.PMPInput[] memory) {
|
|
1554
|
+
// build PMPInputs for the send state update, based on what this hook uses
|
|
1555
|
+
IPMPV0.PMPInput[] memory pmpInputs = new IPMPV0.PMPInput[](1);
|
|
1556
|
+
pmpInputs[0] = IPMPV0.PMPInput({
|
|
1557
|
+
key: "SendState",
|
|
1558
|
+
configuredParamType: IPMPV0.ParamType.Select,
|
|
1559
|
+
configuredValue: bytes32(uint256(sendState)), // @dev enums are aligned with select options
|
|
1560
|
+
configuringArtistString: false,
|
|
1561
|
+
configuredValueString: ""
|
|
1562
|
+
});
|
|
1563
|
+
return pmpInputs;
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
/**
|
|
1567
|
+
* @notice Gets the PMP inputs for the active slot update.
|
|
1568
|
+
* @param activeSlot The active slot to update.
|
|
1569
|
+
* @return pmpInputs The PMP inputs for the active slot update.
|
|
1570
|
+
* @dev This function is used to get the PMP inputs for the active slot update, based on what this hook uses.
|
|
1571
|
+
*/
|
|
1572
|
+
function _getPmpInputsForActiveSlotUpdate(
|
|
1573
|
+
uint256 activeSlot
|
|
1574
|
+
) internal pure returns (IPMPV0.PMPInput[] memory) {
|
|
1575
|
+
IPMPV0.PMPInput[] memory pmpInputs = new IPMPV0.PMPInput[](1);
|
|
1576
|
+
pmpInputs[0] = IPMPV0.PMPInput({
|
|
1577
|
+
key: "ActiveSlot",
|
|
1578
|
+
configuredParamType: IPMPV0.ParamType.Uint256Range,
|
|
1579
|
+
configuredValue: bytes32(uint256(activeSlot)),
|
|
1580
|
+
configuringArtistString: false,
|
|
1581
|
+
configuredValueString: ""
|
|
1582
|
+
});
|
|
1583
|
+
return pmpInputs;
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
/**
|
|
1587
|
+
* @notice Gets the PMP inputs for the image data update.
|
|
1588
|
+
* @param slot The slot to update.
|
|
1589
|
+
* @param imageVersion The image version to update.
|
|
1590
|
+
* @return pmpInputs The PMP inputs for the image data update.
|
|
1591
|
+
* @dev This function is used to get the PMP inputs for the image data update, based on what this hook uses.
|
|
1592
|
+
*/
|
|
1593
|
+
function _getPmpInputsForImageDataUpdate(
|
|
1594
|
+
uint256 slot,
|
|
1595
|
+
uint16 imageVersion
|
|
1596
|
+
) internal pure returns (IPMPV0.PMPInput[] memory) {
|
|
1597
|
+
string memory key = string(
|
|
1598
|
+
bytes.concat(
|
|
1599
|
+
bytes("ImageVersionSlot"),
|
|
1600
|
+
bytes(Strings.toString(slot))
|
|
1601
|
+
)
|
|
1602
|
+
);
|
|
1603
|
+
// build PMPInputs for the image data update, based on what this hook uses
|
|
1604
|
+
IPMPV0.PMPInput[] memory pmpInputs = new IPMPV0.PMPInput[](1);
|
|
1605
|
+
pmpInputs[0] = IPMPV0.PMPInput({
|
|
1606
|
+
key: key,
|
|
1607
|
+
configuredParamType: IPMPV0.ParamType.Uint256Range,
|
|
1608
|
+
configuredValue: bytes32(uint256(imageVersion)),
|
|
1609
|
+
configuringArtistString: false,
|
|
1610
|
+
configuredValueString: ""
|
|
1611
|
+
});
|
|
1612
|
+
return pmpInputs;
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
/**
|
|
1616
|
+
* @notice Gets the PMP inputs for the sound data update.
|
|
1617
|
+
* @param slot The slot to update.
|
|
1618
|
+
* @param soundVersion The sound version to update.
|
|
1619
|
+
* @return pmpInputs The PMP inputs for the sound data update.
|
|
1620
|
+
* @dev This function is used to get the PMP inputs for the sound data update, based on what this hook uses.
|
|
1621
|
+
*/
|
|
1622
|
+
function _getPmpInputsForSoundDataUpdate(
|
|
1623
|
+
uint256 slot,
|
|
1624
|
+
uint16 soundVersion
|
|
1625
|
+
) internal pure returns (IPMPV0.PMPInput[] memory) {
|
|
1626
|
+
string memory key = string(
|
|
1627
|
+
bytes.concat(
|
|
1628
|
+
bytes("SoundVersionSlot"),
|
|
1629
|
+
bytes(Strings.toString(slot))
|
|
1630
|
+
)
|
|
1631
|
+
);
|
|
1632
|
+
// build PMPInputs for the sound data update, based on what this hook uses
|
|
1633
|
+
IPMPV0.PMPInput[] memory pmpInputs = new IPMPV0.PMPInput[](1);
|
|
1634
|
+
pmpInputs[0] = IPMPV0.PMPInput({
|
|
1635
|
+
key: key,
|
|
1636
|
+
configuredParamType: IPMPV0.ParamType.Uint256Range,
|
|
1637
|
+
configuredValue: bytes32(uint256(soundVersion)),
|
|
1638
|
+
configuringArtistString: false,
|
|
1639
|
+
configuredValueString: ""
|
|
1640
|
+
});
|
|
1641
|
+
return pmpInputs;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
/**
|
|
1645
|
+
* @notice Gets a single element address array.
|
|
1646
|
+
* @param address_ The address to include in the array.
|
|
1647
|
+
* @return addressArray The single element address array containing the address.
|
|
1648
|
+
*/
|
|
1649
|
+
function _getSingleElementAddressArray(
|
|
1650
|
+
address address_
|
|
1651
|
+
) internal pure returns (address[] memory) {
|
|
1652
|
+
address[] memory addressArray = new address[](1);
|
|
1653
|
+
addressArray[0] = address_;
|
|
1654
|
+
return addressArray;
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
/**
|
|
1658
|
+
* @notice Authorizes an upgrade to a new implementation.
|
|
1659
|
+
* @dev This function is required by the UUPS pattern and can only be called by the owner.
|
|
1660
|
+
*
|
|
1661
|
+
* UPGRADE SAFETY REQUIREMENTS:
|
|
1662
|
+
* - All existing state variables MUST remain in their current storage slots
|
|
1663
|
+
* - New state variables MUST be appended to the end (never inserted between existing variables)
|
|
1664
|
+
* - Never modify the type or order of existing state variables
|
|
1665
|
+
* - Never remove existing state variables (they can be deprecated but must remain)
|
|
1666
|
+
* - The storage layout must be append-only to maintain compatibility
|
|
1667
|
+
*
|
|
1668
|
+
* @param newImplementation The address of the new implementation contract.
|
|
1669
|
+
*/
|
|
1670
|
+
function _authorizeUpgrade(
|
|
1671
|
+
address newImplementation
|
|
1672
|
+
) internal override onlyOwner {
|
|
1673
|
+
// this version allows the owner to upgrade the contract to a new implementation
|
|
1674
|
+
// in future versions, we may choose to disable this functionality and lock project functionality permanently
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
// --- HELPER FUNCTIONS FROM SOLADY https://github.com/Vectorized/solady/blob/main/src/utils/LibString.sol ---
|
|
1678
|
+
|
|
1679
|
+
/// @dev Returns the hex encoded string from the raw bytes.
|
|
1680
|
+
/// The output is encoded using 2 hexadecimal digits per byte.
|
|
1681
|
+
function toHexString(
|
|
1682
|
+
bytes memory raw
|
|
1683
|
+
) internal pure returns (string memory result) {
|
|
1684
|
+
result = toHexStringNoPrefix(raw);
|
|
1685
|
+
/// @solidity memory-safe-assembly
|
|
1686
|
+
assembly {
|
|
1687
|
+
let n := add(mload(result), 2) // Compute the length.
|
|
1688
|
+
mstore(result, 0x3078) // Store the "0x" prefix.
|
|
1689
|
+
result := sub(result, 2) // Move the pointer.
|
|
1690
|
+
mstore(result, n) // Store the length.
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
/// @dev Returns the hex encoded string from the raw bytes.
|
|
1695
|
+
/// The output is encoded using 2 hexadecimal digits per byte.
|
|
1696
|
+
function toHexStringNoPrefix(
|
|
1697
|
+
bytes memory raw
|
|
1698
|
+
) internal pure returns (string memory result) {
|
|
1699
|
+
/// @solidity memory-safe-assembly
|
|
1700
|
+
assembly {
|
|
1701
|
+
let n := mload(raw)
|
|
1702
|
+
result := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
|
|
1703
|
+
mstore(result, add(n, n)) // Store the length of the output.
|
|
1704
|
+
|
|
1705
|
+
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
|
|
1706
|
+
let o := add(result, 0x20)
|
|
1707
|
+
let end := add(raw, n)
|
|
1708
|
+
for {
|
|
1709
|
+
|
|
1710
|
+
} iszero(eq(raw, end)) {
|
|
1711
|
+
|
|
1712
|
+
} {
|
|
1713
|
+
raw := add(raw, 1)
|
|
1714
|
+
mstore8(add(o, 1), mload(and(mload(raw), 15)))
|
|
1715
|
+
mstore8(o, mload(and(shr(4, mload(raw)), 15)))
|
|
1716
|
+
o := add(o, 2)
|
|
1717
|
+
}
|
|
1718
|
+
mstore(o, 0) // Zeroize the slot after the string.
|
|
1719
|
+
mstore(0x40, add(o, 0x20)) // Allocate memory.
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
}
|