Transaction Hash:
Block:
21819055 at Feb-10-2025 10:18:35 PM +UTC
Transaction Fee:
0.004152437123247192 ETH
$10.61
Gas Used:
4,697,069 Gas / 0.884048568 Gwei
Emitted Events:
218 |
TWCloneFactory.ProxyDeployed( implementation=MarketplaceV3, proxy=MarketplaceV3, deployer=[Sender] 0x7b41dac8d7233972990458b2144151dcd0ff9085 )
|
219 |
MarketplaceV3.ExtensionAdded( 0xbb37a605de78ba6bc667aeaf438d0aae8247e6f48a8fad23730e4fbbb480abf3, 0x6619bfd581c6c4b9930f949d6f1b18ac428c6f82863ee8938fee454ed04784ad, 0x000000000000000000000000cdf042aea1e2b3022f43c49b63bbf1b092eabee3, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000100, 0000000000000000000000000000000000000000000000000000000000000060, 00000000000000000000000000000000000000000000000000000000000000a0, 000000000000000000000000cdf042aea1e2b3022f43c49b63bbf1b092eabee3, 000000000000000000000000000000000000000000000000000000000000000f, 446972656374204c697374696e67730000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000000000000000000000d, 00000000000000000000000000000000000000000000000000000000000001a0, 0000000000000000000000000000000000000000000000000000000000000240, 00000000000000000000000000000000000000000000000000000000000002e0, 0000000000000000000000000000000000000000000000000000000000000380, 0000000000000000000000000000000000000000000000000000000000000400, 00000000000000000000000000000000000000000000000000000000000004c0, 0000000000000000000000000000000000000000000000000000000000000560, 00000000000000000000000000000000000000000000000000000000000005e0, 0000000000000000000000000000000000000000000000000000000000000680, 0000000000000000000000000000000000000000000000000000000000000700, 00000000000000000000000000000000000000000000000000000000000007a0, 0000000000000000000000000000000000000000000000000000000000000840, 00000000000000000000000000000000000000000000000000000000000008c0, 48dd77df00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 000000000000000000000000000000000000000000000000000000000000002c, 617070726f76654275796572466f724c697374696e672875696e743235362c61, 6464726573732c626f6f6c290000000000000000000000000000000000000000, ea8f9a3c00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000032, 617070726f766543757272656e6379466f724c697374696e672875696e743235, 362c616464726573732c75696e74323536290000000000000000000000000000, 704232dc00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000037, 62757946726f6d4c697374696e672875696e743235362c616464726573732c75, 696e743235362c616464726573732c75696e7432353629000000000000000000, 305a67a800000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000016, 63616e63656c4c697374696e672875696e743235362900000000000000000000, 746415b500000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 000000000000000000000000000000000000000000000000000000000000004d, 6372656174654c697374696e672828616464726573732c75696e743235362c75, 696e743235362c616464726573732c75696e743235362c75696e743132382c75, 696e743132382c626f6f6c292900000000000000000000000000000000000000, fb14079d00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000028, 63757272656e63795072696365466f724c697374696e672875696e743235362c, 6164647265737329000000000000000000000000000000000000000000000000, c5275fb000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 000000000000000000000000000000000000000000000000000000000000001f, 676574416c6c4c697374696e67732875696e743235362c75696e743235362900, 31654b4d00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000024, 676574416c6c56616c69644c697374696e67732875696e743235362c75696e74, 3235362900000000000000000000000000000000000000000000000000000000, 107a274a00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000013, 6765744c697374696e672875696e743235362900000000000000000000000000, 9cfbe2a600000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 000000000000000000000000000000000000000000000000000000000000002a, 69734275796572417070726f766564466f724c697374696e672875696e743235, 362c616464726573732900000000000000000000000000000000000000000000, a851904700000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 000000000000000000000000000000000000000000000000000000000000002d, 697343757272656e6379417070726f766564466f724c697374696e672875696e, 743235362c616464726573732900000000000000000000000000000000000000, c78b616c00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 000000000000000000000000000000000000000000000000000000000000000f, 746f74616c4c697374696e677328290000000000000000000000000000000000, 07b6775800000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000055, 7570646174654c697374696e672875696e743235362c28616464726573732c75, 696e743235362c75696e743235362c616464726573732c75696e743235362c75, 696e743132382c75696e743132382c626f6f6c29290000000000000000000000 )
|
220 |
MarketplaceV3.ExtensionAdded( 0xbb37a605de78ba6bc667aeaf438d0aae8247e6f48a8fad23730e4fbbb480abf3, 0x872839dc74a711147022763ad8965be6edd95e09e11066cf613e5f378c3d1d3e, 0x0000000000000000000000004627f57818167e1a0cc69a5b06c7da5143f53a09, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000100, 0000000000000000000000000000000000000000000000000000000000000060, 00000000000000000000000000000000000000000000000000000000000000a0, 0000000000000000000000004627f57818167e1a0cc69a5b06c7da5143f53a09, 0000000000000000000000000000000000000000000000000000000000000010, 456e676c6973682041756374696f6e7300000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000000000000000000000c, 0000000000000000000000000000000000000000000000000000000000000180, 0000000000000000000000000000000000000000000000000000000000000200, 0000000000000000000000000000000000000000000000000000000000000280, 0000000000000000000000000000000000000000000000000000000000000300, 0000000000000000000000000000000000000000000000000000000000000380, 0000000000000000000000000000000000000000000000000000000000000440, 00000000000000000000000000000000000000000000000000000000000004c0, 0000000000000000000000000000000000000000000000000000000000000560, 00000000000000000000000000000000000000000000000000000000000005e0, 0000000000000000000000000000000000000000000000000000000000000660, 00000000000000000000000000000000000000000000000000000000000006e0, 0000000000000000000000000000000000000000000000000000000000000760, 0858e5ad00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 000000000000000000000000000000000000000000000000000000000000001d, 626964496e41756374696f6e2875696e743235362c75696e7432353629000000, 96b5a75500000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000016, 63616e63656c41756374696f6e2875696e743235362900000000000000000000, ebf05a6200000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 000000000000000000000000000000000000000000000000000000000000001d, 636f6c6c65637441756374696f6e5061796f75742875696e7432353629000000, 03a54fe000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 000000000000000000000000000000000000000000000000000000000000001d, 636f6c6c65637441756374696f6e546f6b656e732875696e7432353629000000, 16654d4000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 000000000000000000000000000000000000000000000000000000000000005c, 63726561746541756374696f6e2828616464726573732c75696e743235362c75, 696e743235362c616464726573732c75696e743235362c75696e743235362c75, 696e7436342c75696e7436342c75696e7436342c75696e743634292900000000, c291537c00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 000000000000000000000000000000000000000000000000000000000000001f, 676574416c6c41756374696f6e732875696e743235362c75696e743235362900, 7b06380100000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000024, 676574416c6c56616c696441756374696f6e732875696e743235362c75696e74, 3235362900000000000000000000000000000000000000000000000000000000, 78bd793500000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000013, 67657441756374696f6e2875696e743235362900000000000000000000000000, 6891939d00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000016, 67657457696e6e696e674269642875696e743235362900000000000000000000, 1389b11700000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000019, 697341756374696f6e457870697265642875696e743235362900000000000000, 2eb566bd00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000020, 69734e657757696e6e696e674269642875696e743235362c75696e7432353629, 16002f4a00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 000000000000000000000000000000000000000000000000000000000000000f, 746f74616c41756374696f6e7328290000000000000000000000000000000000 )
|
221 |
MarketplaceV3.ExtensionAdded( 0xbb37a605de78ba6bc667aeaf438d0aae8247e6f48a8fad23730e4fbbb480abf3, 0xeb5c88563ff9c3f38a061945e1fe8abcb460bfa20c81997fb3f856125d7fee00, 0x00000000000000000000000081b851fb4e850c5aadc00f12f147a6a27aaa66ee, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000100, 0000000000000000000000000000000000000000000000000000000000000060, 00000000000000000000000000000000000000000000000000000000000000a0, 00000000000000000000000081b851fb4e850c5aadc00f12f147a6a27aaa66ee, 0000000000000000000000000000000000000000000000000000000000000006, 4f66666572730000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000007, 00000000000000000000000000000000000000000000000000000000000000e0, 0000000000000000000000000000000000000000000000000000000000000160, 00000000000000000000000000000000000000000000000000000000000001e0, 0000000000000000000000000000000000000000000000000000000000000260, 0000000000000000000000000000000000000000000000000000000000000300, 0000000000000000000000000000000000000000000000000000000000000380, 0000000000000000000000000000000000000000000000000000000000000420, c815729d00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000014, 6163636570744f666665722875696e7432353629000000000000000000000000, ef706adf00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000014, 63616e63656c4f666665722875696e7432353629000000000000000000000000, c1edcfbe00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 000000000000000000000000000000000000000000000000000000000000001d, 676574416c6c4f66666572732875696e743235362c75696e7432353629000000, 91940b3e00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000022, 676574416c6c56616c69644f66666572732875696e743235362c75696e743235, 3629000000000000000000000000000000000000000000000000000000000000, 4579268a00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000011, 6765744f666665722875696e7432353629000000000000000000000000000000, 016767fa00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 000000000000000000000000000000000000000000000000000000000000003c, 6d616b654f666665722828616464726573732c75696e743235362c75696e7432, 35362c616464726573732c75696e743235362c75696e74323536292900000000, a9fd8ed100000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 000000000000000000000000000000000000000000000000000000000000000d, 746f74616c4f6666657273282900000000000000000000000000000000000000 )
|
222 |
MarketplaceV3.ContractURIUpdated( prevURI=, newURI=ipfs://Qmcf2qsvX8S952nsDfKRe1PvQx9rXzVrb7HR17pCwSNLX7/0 )
|
223 |
MarketplaceV3.PlatformFeeInfoUpdated( platformFeeRecipient=[Sender] 0x7b41dac8d7233972990458b2144151dcd0ff9085, platformFeeBps=0 )
|
224 |
MarketplaceV3.RoleGranted( role=0000000000000000000000000000000000000000000000000000000000000000, account=[Sender] 0x7b41dac8d7233972990458b2144151dcd0ff9085, sender=[Receiver] TWCloneFactory )
|
225 |
MarketplaceV3.RoleGranted( role=55ADD213C41F3851B4506717B8AF695A4256979DFF496DCAAE7789F6121331AA, account=[Sender] 0x7b41dac8d7233972990458b2144151dcd0ff9085, sender=[Receiver] TWCloneFactory )
|
226 |
MarketplaceV3.RoleGranted( role=F94103142C1BAABE9AC2B5D1487BF783DE9E69CFEEA9A72F5C9C94AFD7877B8C, account=0x00000000...000000000, sender=[Receiver] TWCloneFactory )
|
227 |
MarketplaceV3.RoleGranted( role=86D5CF0A6BDC8D859BA3BDC97043337C82A0E609035F378E419298B6A3E00AE6, account=0x00000000...000000000, sender=[Receiver] TWCloneFactory )
|
228 |
MarketplaceV3.RoleGranted( role=55ADD213C41F3851B4506717B8AF695A4256979DFF496DCAAE7789F6121331AA, account=[Sender] 0x7b41dac8d7233972990458b2144151dcd0ff9085, sender=[Receiver] TWCloneFactory )
|
229 |
MarketplaceV3.RoleAdminChanged( role=55ADD213C41F3851B4506717B8AF695A4256979DFF496DCAAE7789F6121331AA, previousAdminRole=0000000000000000000000000000000000000000000000000000000000000000, newAdminRole=55ADD213C41F3851B4506717B8AF695A4256979DFF496DCAAE7789F6121331AA )
|
230 |
MarketplaceV3.Initialized( version=1 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x33F2cC9C...aA6C42d2F |
0 Eth
Nonce: 0
|
0 Eth
Nonce: 1
| |||
0x4838B106...B0BAD5f97
Miner
| (Titan Builder) | 8.698103862689971434 Eth | 8.69812015421076207 Eth | 0.000016291520790636 | |
0x76F948E5...Bf524805E | |||||
0x7B41dAC8...cd0FF9085 |
0.012887565375646878 Eth
Nonce: 21
|
0.008735128252399686 Eth
Nonce: 22
| 0.004152437123247192 |
Execution Trace
TWCloneFactory.deployProxyByImplementation( _implementation=0x904fbdcBf7F08Bb51177D3C1571D171dD9bC8DaE, _data=0xAAAE56330000000000000000000000007B41DAC8D7233972990458B2144151DCD0FF908500000000000000000000000000000000000000000000000000000000000000A000000000000000000000000000000000000000000000000000000000000001000000000000000000000000007B41DAC8D7233972990458B2144151DCD0FF908500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037697066733A2F2F516D6366327173765838533935326E7344664B526531507651783972587A5672623748523137704377534E4C58372F300000000000000000000000000000000000000000000000000000000000000000000000000000000000, _salt=00000000000000000000000000000000000000000000000000000000014CEEAC ) => ( deployedProxy=0x33F2cC9CD2F8bAc404Afb55B5a18751aA6C42d2F )
-
MarketplaceV3.3d602d80( )
MarketplaceV3.initialize( _defaultAdmin=0x7B41dAC8D7233972990458b2144151Dcd0FF9085, _contractURI=ipfs://Qmcf2qsvX8S952nsDfKRe1PvQx9rXzVrb7HR17pCwSNLX7/0, _trustedForwarders=[], _platformFeeRecipient=0x7B41dAC8D7233972990458b2144151Dcd0FF9085, _platformFeeBps=0 )
-
MarketplaceV3.initialize( _defaultAdmin=0x7B41dAC8D7233972990458b2144151Dcd0FF9085, _contractURI=ipfs://Qmcf2qsvX8S952nsDfKRe1PvQx9rXzVrb7HR17pCwSNLX7/0, _trustedForwarders=[], _platformFeeRecipient=0x7B41dAC8D7233972990458b2144151Dcd0FF9085, _platformFeeBps=0 )
-
deployProxyByImplementation[TWCloneFactory (ln:21)]
_msgSender[TWCloneFactory (ln:26)]
_msgSender[TWCloneFactory (ln:35)]
isTrustedForwarder[ERC2771Context (ln:77)]
_msgSender[ERC2771Context (ln:84)]
cloneDeterministic[TWCloneFactory (ln:27)]
ProxyDeployed[TWCloneFactory (ln:28)]
_msgSender[TWCloneFactory (ln:28)]
_msgSender[TWCloneFactory (ln:35)]
isTrustedForwarder[ERC2771Context (ln:77)]
_msgSender[ERC2771Context (ln:84)]
functionCall[TWCloneFactory (ln:31)]
functionCall[Address (ln:256)]
File 1 of 3: TWCloneFactory
File 2 of 3: MarketplaceV3
File 3 of 3: MarketplaceV3
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.11; /// @author thirdweb // $$\\ $$\\ $$\\ $$\\ $$\\ // $$ | $$ | \\__| $$ | $$ | // $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$$$$\\ // \\_$$ _| $$ __$$\\ $$ |$$ __$$\\ $$ __$$ |$$ | $$ | $$ |$$ __$$\\ $$ __$$\\ // $$ | $$ | $$ |$$ |$$ | \\__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | // $$ |$$\\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | // \\$$$$ |$$ | $$ |$$ |$$ | \\$$$$$$$ |\\$$$$$\\$$$$ |\\$$$$$$$\\ $$$$$$$ | // \\____/ \\__| \\__|\\__|\\__| \\_______| \\_____\\____/ \\_______|\\_______/ import "./extension/interface/IContractFactory.sol"; import "@openzeppelin/contracts/metatx/ERC2771Context.sol"; import "@openzeppelin/contracts/utils/Multicall.sol"; import "@openzeppelin/contracts/proxy/Clones.sol"; contract TWCloneFactory is Multicall, ERC2771Context, IContractFactory { /// @dev Emitted when a proxy is deployed. event ProxyDeployed(address indexed implementation, address proxy, address indexed deployer); constructor(address _trustedForwarder) ERC2771Context(_trustedForwarder) {} /// @dev Deploys a proxy that points to the given implementation. function deployProxyByImplementation( address _implementation, bytes memory _data, bytes32 _salt ) public override returns (address deployedProxy) { bytes32 salthash = keccak256(abi.encodePacked(_msgSender(), _salt)); deployedProxy = Clones.cloneDeterministic(_implementation, salthash); emit ProxyDeployed(_implementation, deployedProxy, _msgSender()); if (_data.length > 0) { // slither-disable-next-line unused-return Address.functionCall(deployedProxy, _data); } } function _msgSender() internal view virtual override returns (address sender) { return ERC2771Context._msgSender(); } function _msgData() internal view virtual override returns (bytes calldata) { return ERC2771Context._msgData(); } } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb interface IContractFactory { /** * @notice Deploys a proxy that points to that points to the given implementation. * * @param implementation Address of the implementation to point to. * * @param data Additional data to pass to the proxy constructor or any other data useful during deployement. * @param salt Salt to use for the deterministic address generation. */ function deployProxyByImplementation( address implementation, bytes memory data, bytes32 salt ) external returns (address); } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (metatx/ERC2771Context.sol) pragma solidity ^0.8.9; import "../utils/Context.sol"; /** * @dev Context variant with ERC2771 support. */ abstract contract ERC2771Context is Context { /// @custom:oz-upgrades-unsafe-allow state-variable-immutable address private immutable _trustedForwarder; /// @custom:oz-upgrades-unsafe-allow constructor constructor(address trustedForwarder) { _trustedForwarder = trustedForwarder; } function isTrustedForwarder(address forwarder) public view virtual returns (bool) { return forwarder == _trustedForwarder; } function _msgSender() internal view virtual override returns (address sender) { if (isTrustedForwarder(msg.sender)) { // The assembly code is more direct than the Solidity version using `abi.decode`. /// @solidity memory-safe-assembly assembly { sender := shr(96, calldataload(sub(calldatasize(), 20))) } } else { return super._msgSender(); } } function _msgData() internal view virtual override returns (bytes calldata) { if (isTrustedForwarder(msg.sender)) { return msg.data[:msg.data.length - 20]; } else { return super._msgData(); } } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (proxy/Clones.sol) pragma solidity ^0.8.0; /** * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for * deploying minimal proxy contracts, also known as "clones". * * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies * > a minimal bytecode implementation that delegates all calls to a known, fixed address. * * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2` * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the * deterministic method. * * _Available since v3.4._ */ library Clones { /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create opcode, which should never revert. */ function clone(address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) mstore(add(ptr, 0x14), shl(0x60, implementation)) mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) instance := create(0, ptr, 0x37) } require(instance != address(0), "ERC1167: create failed"); } /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create2 opcode and a `salt` to deterministically deploy * the clone. Using the same `implementation` and `salt` multiple time will revert, since * the clones cannot be deployed twice at the same address. */ function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) mstore(add(ptr, 0x14), shl(0x60, implementation)) mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) instance := create2(0, ptr, 0x37, salt) } require(instance != address(0), "ERC1167: create2 failed"); } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) mstore(add(ptr, 0x14), shl(0x60, implementation)) mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000) mstore(add(ptr, 0x38), shl(0x60, deployer)) mstore(add(ptr, 0x4c), salt) mstore(add(ptr, 0x6c), keccak256(ptr, 0x37)) predicted := keccak256(add(ptr, 0x37), 0x55) } } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress(address implementation, bytes32 salt) internal view returns (address predicted) { return predictDeterministicAddress(implementation, salt, address(this)); } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol) pragma solidity ^0.8.0; import "./Address.sol"; /** * @dev Provides a function to batch together multiple calls in a single external call. * * _Available since v4.1._ */ abstract contract Multicall { /** * @dev Receives and executes a batch of function calls on this contract. */ function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) { results = new bytes[](data.length); for (uint256 i = 0; i < data.length; i++) { results[i] = Address.functionDelegateCall(address(this), data[i]); } return results; } }
File 2 of 3: MarketplaceV3
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./interface/IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * [EIP](https://eips.ethereum.org/EIPS/eip-165). * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified) * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); } // SPDX-License-Identifier: Apache 2.0 pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Interface for the NFT Royalty Standard. * * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal * support for royalty payments across all NFT marketplaces and ecosystem participants. * * _Available since v4.5._ */ interface IERC2981 is IERC165 { /** * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of * exchange. The royalty amount is denominated and should be payed in that same unit of exchange. */ function royaltyInfo( uint256 tokenId, uint256 salePrice ) external view returns (address receiver, uint256 royaltyAmount); } // SPDX-License-Identifier: Apache 2.0 pragma solidity ^0.8.0; /// @author thirdweb import "../lib/Address.sol"; import "./interface/IMulticall.sol"; /** * @dev Provides a function to batch together multiple calls in a single external call. * * _Available since v4.1._ */ contract Multicall is IMulticall { /** * @notice Receives and executes a batch of function calls on this contract. * @dev Receives and executes a batch of function calls on this contract. * * @param data The bytes data that makes up the batch of function calls to execute. * @return results The bytes data that makes up the result of the batch of function calls executed. */ function multicall(bytes[] calldata data) external returns (bytes[] memory results) { results = new bytes[](data.length); address sender = _msgSender(); bool isForwarder = msg.sender != sender; for (uint256 i = 0; i < data.length; i++) { if (isForwarder) { results[i] = Address.functionDelegateCall(address(this), abi.encodePacked(data[i], sender)); } else { results[i] = Address.functionDelegateCall(address(this), data[i]); } } return results; } /// @notice Returns the sender in the given execution context. function _msgSender() internal view virtual returns (address) { return msg.sender; } } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb /** * Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI * for you contract. * * Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea. */ interface IContractMetadata { /// @dev Returns the metadata URI of the contract. function contractURI() external view returns (string memory); /** * @dev Sets contract URI for the storefront-level metadata of the contract. * Only module admin can call this function. */ function setContractURI(string calldata _uri) external; /// @dev Emitted when the contract URI is updated. event ContractURIUpdated(string prevURI, string newURI); } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; interface IERC2771Context { function isTrustedForwarder(address forwarder) external view returns (bool); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @author thirdweb /** * @dev Provides a function to batch together multiple calls in a single external call. * * _Available since v4.1._ */ interface IMulticall { /** * @dev Receives and executes a batch of function calls on this contract. */ function multicall(bytes[] calldata data) external returns (bytes[] memory results); } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IPermissions { /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) external; } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb import "./IPermissions.sol"; /** * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. */ interface IPermissionsEnumerable is IPermissions { /** * @dev Returns one of the accounts that have `role`. `index` must be a * value between 0 and {getRoleMemberCount}, non-inclusive. * * Role bearers are not sorted in any particular way, and their ordering may * change at any point. * * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure * you perform all queries on the same block. See the following * [forum post](https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296) * for more information. */ function getRoleMember(bytes32 role, uint256 index) external view returns (address); /** * @dev Returns the number of accounts that have `role`. Can be used * together with {getRoleMember} to enumerate all bearers of a role. */ function getRoleMemberCount(bytes32 role) external view returns (uint256); } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb /** * Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading * the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic * that uses information about platform fees, if desired. */ interface IPlatformFee { /// @dev Fee type variants: percentage fee and flat fee enum PlatformFeeType { Bps, Flat } /// @dev Returns the platform fee bps and recipient. function getPlatformFeeInfo() external view returns (address, uint16); /// @dev Lets a module admin update the fees on primary sales. function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external; /// @dev Emitted when fee on primary sales is updated. event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); /// @dev Emitted when the flat platform fee is updated. event FlatPlatformFeeUpdated(address platformFeeRecipient, uint256 flatFee); /// @dev Emitted when the platform fee type is updated. event PlatformFeeTypeUpdated(PlatformFeeType feeType); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @author: manifold.xyz import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /** * @dev Lookup engine interface */ interface IRoyaltyEngineV1 is IERC165 { /** * Get the royalty for a given token (address, id) and value amount. Does not cache the bps/amounts. Caches the spec for a given token address * * @param tokenAddress - The address of the token * @param tokenId - The id of the token * @param value - The value you wish to get the royalty of * * returns Two arrays of equal length, royalty recipients and the corresponding amount each recipient should get */ function getRoyalty( address tokenAddress, uint256 tokenId, uint256 value ) external returns (address payable[] memory recipients, uint256[] memory amounts); /** * View only version of getRoyalty * * @param tokenAddress - The address of the token * @param tokenId - The id of the token * @param value - The value you wish to get the royalty of * * returns Two arrays of equal length, royalty recipients and the corresponding amount each recipient should get */ function getRoyaltyView( address tokenAddress, uint256 tokenId, uint256 value ) external view returns (address payable[] memory recipients, uint256[] memory amounts); } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /** * @dev Read royalty info for a token. * Supports RoyaltyEngineV1 and RoyaltyRegistry by manifold.xyz. */ interface IRoyaltyPayments is IERC165 { /// @dev Emitted when the address of RoyaltyEngine is set or updated. event RoyaltyEngineUpdated(address indexed previousAddress, address indexed newAddress); /** * Get the royalty for a given token (address, id) and value amount. * * @param tokenAddress - The address of the token * @param tokenId - The id of the token * @param value - The value you wish to get the royalty of * * returns Two arrays of equal length, royalty recipients and the corresponding amount each recipient should get */ function getRoyalty( address tokenAddress, uint256 tokenId, uint256 value ) external returns (address payable[] memory recipients, uint256[] memory amounts); /** * Set or override RoyaltyEngine address * * @param _royaltyEngineAddress - RoyaltyEngineV1 address */ function setRoyaltyEngine(address _royaltyEngineAddress) external; } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb import "../interface/IContractMetadata.sol"; /** * @author thirdweb.com * * @title Contract Metadata * @notice Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI * for you contract. * Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea. */ library ContractMetadataStorage { /// @custom:storage-location erc7201:contract.metadata.storage /// @dev keccak256(abi.encode(uint256(keccak256("contract.metadata.storage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 public constant CONTRACT_METADATA_STORAGE_POSITION = 0x4bc804ba64359c0e35e5ed5d90ee596ecaa49a3a930ddcb1470ea0dd625da900; struct Data { /// @notice Returns the contract metadata URI. string contractURI; } function data() internal pure returns (Data storage data_) { bytes32 position = CONTRACT_METADATA_STORAGE_POSITION; assembly { data_.slot := position } } } abstract contract ContractMetadata is IContractMetadata { /** * @notice Lets a contract admin set the URI for contract-level metadata. * @dev Caller should be authorized to setup contractURI, e.g. contract admin. * See {_canSetContractURI}. * Emits {ContractURIUpdated Event}. * * @param _uri keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") */ function setContractURI(string memory _uri) external override { if (!_canSetContractURI()) { revert("Not authorized"); } _setupContractURI(_uri); } /// @dev Lets a contract admin set the URI for contract-level metadata. function _setupContractURI(string memory _uri) internal { string memory prevURI = _contractMetadataStorage().contractURI; _contractMetadataStorage().contractURI = _uri; emit ContractURIUpdated(prevURI, _uri); } /// @notice Returns the contract metadata URI. function contractURI() public view virtual override returns (string memory) { return _contractMetadataStorage().contractURI; } /// @dev Returns the AccountPermissions storage. function _contractMetadataStorage() internal pure returns (ContractMetadataStorage.Data storage data) { data = ContractMetadataStorage.data(); } /// @dev Returns whether contract metadata can be set in the given execution context. function _canSetContractURI() internal view virtual returns (bool); } // SPDX-License-Identifier: Apache 2.0 pragma solidity ^0.8.0; import "../interface/IERC2771Context.sol"; import "./Initializable.sol"; /** * @dev Context variant with ERC2771 support. */ library ERC2771ContextStorage { /// @custom:storage-location erc7201:erc2771.context.storage /// @dev keccak256(abi.encode(uint256(keccak256("erc2771.context.storage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 public constant ERC2771_CONTEXT_STORAGE_POSITION = 0x82aadcdf5bea62fd30615b6c0754b644e71b6c1e8c55b71bb927ad005b504f00; struct Data { mapping(address => bool) trustedForwarder; } function data() internal pure returns (Data storage data_) { bytes32 position = ERC2771_CONTEXT_STORAGE_POSITION; assembly { data_.slot := position } } } /** * @dev Context variant with ERC2771 support. */ abstract contract ERC2771ContextUpgradeable is Initializable { function __ERC2771Context_init(address[] memory trustedForwarder) internal onlyInitializing { __ERC2771Context_init_unchained(trustedForwarder); } function __ERC2771Context_init_unchained(address[] memory trustedForwarder) internal onlyInitializing { for (uint256 i = 0; i < trustedForwarder.length; i++) { _erc2771ContextStorage().trustedForwarder[trustedForwarder[i]] = true; } } function isTrustedForwarder(address forwarder) public view virtual returns (bool) { return _erc2771ContextStorage().trustedForwarder[forwarder]; } function _msgSender() internal view virtual returns (address sender) { if (isTrustedForwarder(msg.sender)) { // The assembly code is more direct than the Solidity version using `abi.decode`. assembly { sender := shr(96, calldataload(sub(calldatasize(), 20))) } } else { return msg.sender; } } function _msgData() internal view virtual returns (bytes calldata) { if (isTrustedForwarder(msg.sender)) { return msg.data[:msg.data.length - 20]; } else { return msg.data; } } /// @dev Returns the ERC2771ContextStorage storage. function _erc2771ContextStorage() internal pure returns (ERC2771ContextStorage.Data storage data) { data = ERC2771ContextStorage.data(); } uint256[49] private __gap; } // SPDX-License-Identifier: Apache 2.0 pragma solidity ^0.8.0; import "../../lib/Address.sol"; library InitStorage { /// @custom:storage-location erc7201:init.storage /// @dev keccak256(abi.encode(uint256(keccak256("init.storage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 constant INIT_STORAGE_POSITION = 0x322cf19c484104d3b1a9c2982ebae869ede3fa5f6c4703ca41b9a48c76ee0300; /// @dev Layout of the entrypoint contract's storage. struct Data { uint8 initialized; bool initializing; } /// @dev Returns the entrypoint contract's data at the relevant storage location. function data() internal pure returns (Data storage data_) { bytes32 position = INIT_STORAGE_POSITION; assembly { data_.slot := position } } } abstract contract Initializable { /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. */ modifier initializer() { uint8 _initialized = _initStorage().initialized; bool _initializing = _initStorage().initializing; bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initStorage().initialized = 1; if (isTopLevelCall) { _initStorage().initializing = true; } _; if (isTopLevelCall) { _initStorage().initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original * initialization step. This is essential to configure modules that are added through upgrades and that require * initialization. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. */ modifier reinitializer(uint8 version) { uint8 _initialized = _initStorage().initialized; bool _initializing = _initStorage().initializing; require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initStorage().initialized = version; _initStorage().initializing = true; _; _initStorage().initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initStorage().initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. */ function _disableInitializers() internal virtual { uint8 _initialized = _initStorage().initialized; bool _initializing = _initStorage().initializing; require(!_initializing, "Initializable: contract is initializing"); if (_initialized < type(uint8).max) { _initStorage().initialized = type(uint8).max; emit Initialized(type(uint8).max); } } /// @dev Returns the InitStorage storage. function _initStorage() internal pure returns (InitStorage.Data storage data) { data = InitStorage.data(); } } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb import "../interface/IPermissions.sol"; import "../../lib/Strings.sol"; /** * @title Permissions * @dev This contracts provides extending-contracts with role-based access control mechanisms */ library PermissionsStorage { /// @custom:storage-location erc7201:permissions.storage /// @dev keccak256(abi.encode(uint256(keccak256("permissions.storage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 public constant PERMISSIONS_STORAGE_POSITION = 0x0a7b0f5c59907924802379ebe98cdc23e2ee7820f63d30126e10b3752010e500; struct Data { /// @dev Map from keccak256 hash of a role => a map from address => whether address has role. mapping(bytes32 => mapping(address => bool)) _hasRole; /// @dev Map from keccak256 hash of a role to role admin. See {getRoleAdmin}. mapping(bytes32 => bytes32) _getRoleAdmin; } function data() internal pure returns (Data storage data_) { bytes32 position = PERMISSIONS_STORAGE_POSITION; assembly { data_.slot := position } } } contract Permissions is IPermissions { /// @dev Default admin role for all roles. Only accounts with this role can grant/revoke other roles. bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /// @dev Modifier that checks if an account has the specified role; reverts otherwise. modifier onlyRole(bytes32 role) { _checkRole(role, _msgSender()); _; } /** * @notice Checks whether an account has a particular role. * @dev Returns `true` if `account` has been granted `role`. * * @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") * @param account Address of the account for which the role is being checked. */ function hasRole(bytes32 role, address account) public view override returns (bool) { return _permissionsStorage()._hasRole[role][account]; } /** * @notice Checks whether an account has a particular role; * role restrictions can be swtiched on and off. * * @dev Returns `true` if `account` has been granted `role`. * Role restrictions can be swtiched on and off: * - If address(0) has ROLE, then the ROLE restrictions * don't apply. * - If address(0) does not have ROLE, then the ROLE * restrictions will apply. * * @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") * @param account Address of the account for which the role is being checked. */ function hasRoleWithSwitch(bytes32 role, address account) public view returns (bool) { if (!_permissionsStorage()._hasRole[role][address(0)]) { return _permissionsStorage()._hasRole[role][account]; } return true; } /** * @notice Returns the admin role that controls the specified role. * @dev See {grantRole} and {revokeRole}. * To change a role's admin, use {_setRoleAdmin}. * * @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") */ function getRoleAdmin(bytes32 role) external view override returns (bytes32) { return _permissionsStorage()._getRoleAdmin[role]; } /** * @notice Grants a role to an account, if not previously granted. * @dev Caller must have admin role for the `role`. * Emits {RoleGranted Event}. * * @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") * @param account Address of the account to which the role is being granted. */ function grantRole(bytes32 role, address account) public virtual override { _checkRole(_permissionsStorage()._getRoleAdmin[role], _msgSender()); if (_permissionsStorage()._hasRole[role][account]) { revert("Can only grant to non holders"); } _setupRole(role, account); } /** * @notice Revokes role from an account. * @dev Caller must have admin role for the `role`. * Emits {RoleRevoked Event}. * * @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") * @param account Address of the account from which the role is being revoked. */ function revokeRole(bytes32 role, address account) public virtual override { _checkRole(_permissionsStorage()._getRoleAdmin[role], _msgSender()); _revokeRole(role, account); } /** * @notice Revokes role from the account. * @dev Caller must have the `role`, with caller being the same as `account`. * Emits {RoleRevoked Event}. * * @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") * @param account Address of the account from which the role is being revoked. */ function renounceRole(bytes32 role, address account) public virtual override { if (_msgSender() != account) { revert("Can only renounce for self"); } _revokeRole(role, account); } /// @dev Sets `adminRole` as `role`'s admin role. function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = _permissionsStorage()._getRoleAdmin[role]; _permissionsStorage()._getRoleAdmin[role] = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /// @dev Sets up `role` for `account` function _setupRole(bytes32 role, address account) internal virtual { _permissionsStorage()._hasRole[role][account] = true; emit RoleGranted(role, account, _msgSender()); } /// @dev Revokes `role` from `account` function _revokeRole(bytes32 role, address account) internal virtual { _checkRole(role, account); delete _permissionsStorage()._hasRole[role][account]; emit RoleRevoked(role, account, _msgSender()); } /// @dev Checks `role` for `account`. Reverts with a message including the required role. function _checkRole(bytes32 role, address account) internal view virtual { if (!_permissionsStorage()._hasRole[role][account]) { revert( string( abi.encodePacked( "Permissions: account ", Strings.toHexString(uint160(account), 20), " is missing role ", Strings.toHexString(uint256(role), 32) ) ) ); } } /// @dev Checks `role` for `account`. Reverts with a message including the required role. function _checkRoleWithSwitch(bytes32 role, address account) internal view virtual { if (!hasRoleWithSwitch(role, account)) { revert( string( abi.encodePacked( "Permissions: account ", Strings.toHexString(uint160(account), 20), " is missing role ", Strings.toHexString(uint256(role), 32) ) ) ); } } function _msgSender() internal view virtual returns (address sender) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } /// @dev Returns the Permissions storage. function _permissionsStorage() internal pure returns (PermissionsStorage.Data storage data) { data = PermissionsStorage.data(); } } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb import "../interface/IPermissionsEnumerable.sol"; import "./Permissions.sol"; /** * @title PermissionsEnumerable * @dev This contracts provides extending-contracts with role-based access control mechanisms. * Also provides interfaces to view all members with a given role, and total count of members. */ library PermissionsEnumerableStorage { /// @custom:storage-location erc7201:extension.manager.storage bytes32 public constant PERMISSIONS_ENUMERABLE_STORAGE_POSITION = keccak256(abi.encode(uint256(keccak256("permissions.enumerable.storage")) - 1)) & ~bytes32(uint256(0xff)); /** * @notice A data structure to store data of members for a given role. * * @param index Current index in the list of accounts that have a role. * @param members map from index => address of account that has a role * @param indexOf map from address => index which the account has. */ struct RoleMembers { uint256 index; mapping(uint256 => address) members; mapping(address => uint256) indexOf; } struct Data { /// @dev map from keccak256 hash of a role to its members' data. See {RoleMembers}. mapping(bytes32 => RoleMembers) roleMembers; } function data() internal pure returns (Data storage data_) { bytes32 position = PERMISSIONS_ENUMERABLE_STORAGE_POSITION; assembly { data_.slot := position } } } contract PermissionsEnumerable is IPermissionsEnumerable, Permissions { /** * @notice Returns the role-member from a list of members for a role, * at a given index. * @dev Returns `member` who has `role`, at `index` of role-members list. * See struct {RoleMembers}, and mapping {roleMembers} * * @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") * @param index Index in list of current members for the role. * * @return member Address of account that has `role` */ function getRoleMember(bytes32 role, uint256 index) external view override returns (address member) { uint256 currentIndex = _permissionsEnumerableStorage().roleMembers[role].index; uint256 check; for (uint256 i = 0; i < currentIndex; i += 1) { if (_permissionsEnumerableStorage().roleMembers[role].members[i] != address(0)) { if (check == index) { member = _permissionsEnumerableStorage().roleMembers[role].members[i]; return member; } check += 1; } else if ( hasRole(role, address(0)) && i == _permissionsEnumerableStorage().roleMembers[role].indexOf[address(0)] ) { check += 1; } } } /** * @notice Returns total number of accounts that have a role. * @dev Returns `count` of accounts that have `role`. * See struct {RoleMembers}, and mapping {roleMembers} * * @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") * * @return count Total number of accounts that have `role` */ function getRoleMemberCount(bytes32 role) external view override returns (uint256 count) { uint256 currentIndex = _permissionsEnumerableStorage().roleMembers[role].index; for (uint256 i = 0; i < currentIndex; i += 1) { if (_permissionsEnumerableStorage().roleMembers[role].members[i] != address(0)) { count += 1; } } if (hasRole(role, address(0))) { count += 1; } } /// @dev Revokes `role` from `account`, and removes `account` from {roleMembers} /// See {_removeMember} function _revokeRole(bytes32 role, address account) internal virtual override { super._revokeRole(role, account); _removeMember(role, account); } /// @dev Grants `role` to `account`, and adds `account` to {roleMembers} /// See {_addMember} function _setupRole(bytes32 role, address account) internal virtual override { super._setupRole(role, account); _addMember(role, account); } /// @dev adds `account` to {roleMembers}, for `role` function _addMember(bytes32 role, address account) internal { uint256 idx = _permissionsEnumerableStorage().roleMembers[role].index; _permissionsEnumerableStorage().roleMembers[role].index += 1; _permissionsEnumerableStorage().roleMembers[role].members[idx] = account; _permissionsEnumerableStorage().roleMembers[role].indexOf[account] = idx; } /// @dev removes `account` from {roleMembers}, for `role` function _removeMember(bytes32 role, address account) internal { uint256 idx = _permissionsEnumerableStorage().roleMembers[role].indexOf[account]; delete _permissionsEnumerableStorage().roleMembers[role].members[idx]; delete _permissionsEnumerableStorage().roleMembers[role].indexOf[account]; } /// @dev Returns the PermissionsEnumerable storage. function _permissionsEnumerableStorage() internal pure returns (PermissionsEnumerableStorage.Data storage data) { data = PermissionsEnumerableStorage.data(); } } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb import "../interface/IPlatformFee.sol"; /** * @author thirdweb.com */ library PlatformFeeStorage { /// @custom:storage-location erc7201:platform.fee.storage /// @dev keccak256(abi.encode(uint256(keccak256("platform.fee.storage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 public constant PLATFORM_FEE_STORAGE_POSITION = 0xc0c34308b4a2f4c5ee9af8ba82541cfb3c33b076d1fd05c65f9ce7060c64c400; struct Data { /// @dev The address that receives all platform fees from all sales. address platformFeeRecipient; /// @dev The % of primary sales collected as platform fees. uint16 platformFeeBps; /// @dev Fee type variants: percentage fee and flat fee IPlatformFee.PlatformFeeType platformFeeType; /// @dev The flat amount collected by the contract as fees on primary sales. uint256 flatPlatformFee; } function data() internal pure returns (Data storage data_) { bytes32 position = PLATFORM_FEE_STORAGE_POSITION; assembly { data_.slot := position } } } /** * @author thirdweb.com * * @title Platform Fee * @notice Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading * the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic * that uses information about platform fees, if desired. */ abstract contract PlatformFee is IPlatformFee { /// @dev Returns the platform fee recipient and bps. function getPlatformFeeInfo() public view override returns (address, uint16) { return (_platformFeeStorage().platformFeeRecipient, uint16(_platformFeeStorage().platformFeeBps)); } /// @dev Returns the platform fee bps and recipient. function getFlatPlatformFeeInfo() public view returns (address, uint256) { return (_platformFeeStorage().platformFeeRecipient, _platformFeeStorage().flatPlatformFee); } /// @dev Returns the platform fee type. function getPlatformFeeType() public view returns (PlatformFeeType) { return _platformFeeStorage().platformFeeType; } /** * @notice Updates the platform fee recipient and bps. * @dev Caller should be authorized to set platform fee info. * See {_canSetPlatformFeeInfo}. * Emits {PlatformFeeInfoUpdated Event}; See {_setupPlatformFeeInfo}. * * @param _platformFeeRecipient Address to be set as new platformFeeRecipient. * @param _platformFeeBps Updated platformFeeBps. */ function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external override { if (!_canSetPlatformFeeInfo()) { revert("Not authorized"); } _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); } /// @dev Lets a contract admin update the platform fee recipient and bps function _setupPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) internal { if (_platformFeeBps > 10_000) { revert("Exceeds max bps"); } if (_platformFeeRecipient == address(0)) { revert("Invalid recipient"); } _platformFeeStorage().platformFeeBps = uint16(_platformFeeBps); _platformFeeStorage().platformFeeRecipient = _platformFeeRecipient; emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps); } /// @notice Lets a module admin set a flat fee on primary sales. function setFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee) external { if (!_canSetPlatformFeeInfo()) { revert("Not authorized"); } _setupFlatPlatformFeeInfo(_platformFeeRecipient, _flatFee); } /// @dev Sets a flat fee on primary sales. function _setupFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee) internal { _platformFeeStorage().flatPlatformFee = _flatFee; _platformFeeStorage().platformFeeRecipient = _platformFeeRecipient; emit FlatPlatformFeeUpdated(_platformFeeRecipient, _flatFee); } /// @notice Lets a module admin set platform fee type. function setPlatformFeeType(PlatformFeeType _feeType) external { if (!_canSetPlatformFeeInfo()) { revert("Not authorized"); } _setupPlatformFeeType(_feeType); } /// @dev Sets platform fee type. function _setupPlatformFeeType(PlatformFeeType _feeType) internal { _platformFeeStorage().platformFeeType = _feeType; emit PlatformFeeTypeUpdated(_feeType); } /// @dev Returns the PlatformFee storage. function _platformFeeStorage() internal pure returns (PlatformFeeStorage.Data storage data) { data = PlatformFeeStorage.data(); } /// @dev Returns whether platform fee info can be set in the given execution context. function _canSetPlatformFeeInfo() internal view virtual returns (bool); } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; library ReentrancyGuardStorage { /// @custom:storage-location erc7201:reentrancy.guard.storage /// @dev keccak256(abi.encode(uint256(keccak256("reentrancy.guard.storage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 public constant REENTRANCY_GUARD_STORAGE_POSITION = 0x1d281c488dae143b6ea4122e80c65059929950b9c32f17fc57be22089d9c3b00; struct Data { uint256 _status; } function data() internal pure returns (Data storage data_) { bytes32 position = REENTRANCY_GUARD_STORAGE_POSITION; assembly { data_.slot := position } } } abstract contract ReentrancyGuard { uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; constructor() { _reentrancyGuardStorage()._status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_reentrancyGuardStorage()._status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _reentrancyGuardStorage()._status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _reentrancyGuardStorage()._status = _NOT_ENTERED; } /// @dev Returns the ReentrancyGuard storage. function _reentrancyGuardStorage() internal pure returns (ReentrancyGuardStorage.Data storage data) { data = ReentrancyGuardStorage.data(); } } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb import "../interface/IRoyaltyPayments.sol"; import "../interface/IRoyaltyEngineV1.sol"; import { IERC2981 } from "../../eip/interface/IERC2981.sol"; library RoyaltyPaymentsStorage { /// @custom:storage-location erc7201:royalty.payments.storage /// @dev keccak256(abi.encode(uint256(keccak256("royalty.payments.storage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 public constant ROYALTY_PAYMENTS_STORAGE_POSITION = 0xc802b338f3fb784853cf3c808df5ff08335200e394ea2c687d12571a91045000; struct Data { /// @dev The address of RoyaltyEngineV1, replacing the one set during construction. address royaltyEngineAddressOverride; } function royaltyPaymentsStorage() internal pure returns (Data storage royaltyPaymentsData) { bytes32 position = ROYALTY_PAYMENTS_STORAGE_POSITION; assembly { royaltyPaymentsData.slot := position } } } /** * @author thirdweb.com * * @title Royalty Payments * @notice Thirdweb's `RoyaltyPayments` is a contract extension to be used with a marketplace contract. * It exposes functions for fetching royalty settings for a token. * It Supports RoyaltyEngineV1 and RoyaltyRegistry by manifold.xyz. */ abstract contract RoyaltyPaymentsLogic is IRoyaltyPayments { // solhint-disable-next-line var-name-mixedcase address immutable ROYALTY_ENGINE_ADDRESS; constructor(address _royaltyEngineAddress) { // allow address(0) in case RoyaltyEngineV1 not present on a network require( _royaltyEngineAddress == address(0) || IERC165(_royaltyEngineAddress).supportsInterface(type(IRoyaltyEngineV1).interfaceId), "Doesn't support IRoyaltyEngineV1 interface" ); ROYALTY_ENGINE_ADDRESS = _royaltyEngineAddress; } /** * Get the royalty for a given token (address, id) and value amount. Does not cache the bps/amounts. Caches the spec for a given token address * * @param tokenAddress - The address of the token * @param tokenId - The id of the token * @param value - The value you wish to get the royalty of * * returns Two arrays of equal length, royalty recipients and the corresponding amount each recipient should get */ function getRoyalty( address tokenAddress, uint256 tokenId, uint256 value ) external returns (address payable[] memory recipients, uint256[] memory amounts) { address royaltyEngineAddress = getRoyaltyEngineAddress(); if (royaltyEngineAddress == address(0)) { try IERC2981(tokenAddress).royaltyInfo(tokenId, value) returns (address recipient, uint256 amount) { require(amount <= value, "Invalid royalty amount"); recipients = new address payable[](1); amounts = new uint256[](1); recipients[0] = payable(recipient); amounts[0] = amount; } catch {} } else { (recipients, amounts) = IRoyaltyEngineV1(royaltyEngineAddress).getRoyalty(tokenAddress, tokenId, value); } } /** * Set or override RoyaltyEngine address * * @param _royaltyEngineAddress - RoyaltyEngineV1 address */ function setRoyaltyEngine(address _royaltyEngineAddress) external { if (!_canSetRoyaltyEngine()) { revert("Not authorized"); } require( _royaltyEngineAddress != address(0) && IERC165(_royaltyEngineAddress).supportsInterface(type(IRoyaltyEngineV1).interfaceId), "Doesn't support IRoyaltyEngineV1 interface" ); _setupRoyaltyEngine(_royaltyEngineAddress); } /// @dev Returns original or overridden address for RoyaltyEngineV1 function getRoyaltyEngineAddress() public view returns (address royaltyEngineAddress) { RoyaltyPaymentsStorage.Data storage data = RoyaltyPaymentsStorage.royaltyPaymentsStorage(); address royaltyEngineOverride = data.royaltyEngineAddressOverride; royaltyEngineAddress = royaltyEngineOverride != address(0) ? royaltyEngineOverride : ROYALTY_ENGINE_ADDRESS; } /// @dev Lets a contract admin update the royalty engine address function _setupRoyaltyEngine(address _royaltyEngineAddress) internal { RoyaltyPaymentsStorage.Data storage data = RoyaltyPaymentsStorage.royaltyPaymentsStorage(); address currentAddress = data.royaltyEngineAddressOverride; data.royaltyEngineAddressOverride = _royaltyEngineAddress; emit RoyaltyEngineUpdated(currentAddress, _royaltyEngineAddress); } /// @dev Returns whether royalty engine address can be set in the given execution context. function _canSetRoyaltyEngine() internal view virtual returns (bool); } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; import { ReentrancyGuardStorage } from "../ReentrancyGuard.sol"; import "../Initializable.sol"; contract ReentrancyGuardInit is Initializable { uint256 private constant _NOT_ENTERED = 1; function __ReentrancyGuard_init() internal onlyInitializing { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal onlyInitializing { ReentrancyGuardStorage.Data storage data = ReentrancyGuardStorage.data(); data._status = _NOT_ENTERED; } } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.1; /// @author thirdweb, OpenZeppelin Contracts (v4.9.0) /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{ value: value }(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb /** * @dev String operations. */ library Strings { bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _HEX_SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte, /// and the alphabets are capitalized conditionally according to /// https://eips.ethereum.org/EIPS/eip-55 function toHexStringChecksummed(address value) internal pure returns (string memory str) { str = toHexString(value); /// @solidity memory-safe-assembly assembly { let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...` let o := add(str, 0x22) let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... ` let t := shl(240, 136) // `0b10001000 << 240` for { let i := 0 } 1 { } { mstore(add(i, i), mul(t, byte(i, hashed))) i := add(i, 1) if eq(i, 20) { break } } mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask))))) o := add(o, 0x20) mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask))))) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. function toHexString(address value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(address value) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { str := mload(0x40) // Allocate the memory. // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, // 0x02 bytes for the prefix, and 0x28 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80. mstore(0x40, add(str, 0x80)) // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) str := add(str, 2) mstore(str, 40) let o := add(str, 0x20) mstore(add(o, 40), 0) value := shl(96, value) // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let i := 0 } 1 { } { let p := add(o, add(i, i)) let temp := byte(i, value) mstore8(add(p, 1), mload(and(temp, 15))) mstore8(p, mload(shr(4, temp))) i := add(i, 1) if eq(i, 20) { break } } } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexString(bytes memory raw) internal pure returns (string memory str) { str = toHexStringNoPrefix(raw); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { let length := mload(raw) str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix. mstore(str, add(length, length)) // Store the length of the output. // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let o := add(str, 0x20) let end := add(raw, length) for { } iszero(eq(raw, end)) { } { raw := add(raw, 1) mstore8(add(o, 1), mload(and(mload(raw), 15))) mstore8(o, mload(and(shr(4, mload(raw)), 15))) o := add(o, 2) } mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate the memory. } } } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb // $$\\ $$\\ $$\\ $$\\ $$\\ // $$ | $$ | \\__| $$ | $$ | // $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$$$$\\ // \\_$$ _| $$ __$$\\ $$ |$$ __$$\\ $$ __$$ |$$ | $$ | $$ |$$ __$$\\ $$ __$$\\ // $$ | $$ | $$ |$$ |$$ | \\__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | // $$ |$$\\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | // \\$$$$ |$$ | $$ |$$ |$$ | \\$$$$$$$ |\\$$$$$\\$$$$ |\\$$$$$$$\\ $$$$$$$ | // \\____/ \\__| \\__|\\__|\\__| \\_______| \\_____\\____/ \\_______|\\_______/ // ====== External imports ====== import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import { ERC721Holder } from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; import { ERC1155Holder, ERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; // ========== Internal imports ========== import { BaseRouter, IRouter, IRouterState } from "@thirdweb-dev/dynamic-contracts/src/presets/BaseRouter.sol"; import { ERC165 } from "../../../eip/ERC165.sol"; import "../../../extension/Multicall.sol"; import "../../../extension/upgradeable/Initializable.sol"; import "../../../extension/upgradeable/ContractMetadata.sol"; import "../../../extension/upgradeable/PlatformFee.sol"; import "../../../extension/upgradeable/PermissionsEnumerable.sol"; import "../../../extension/upgradeable/init/ReentrancyGuardInit.sol"; import "../../../extension/upgradeable/ERC2771ContextUpgradeable.sol"; import { RoyaltyPaymentsLogic } from "../../../extension/upgradeable/RoyaltyPayments.sol"; /** * @author thirdweb.com */ contract MarketplaceV3 is Initializable, Multicall, BaseRouter, ContractMetadata, PlatformFee, PermissionsEnumerable, ReentrancyGuardInit, ERC2771ContextUpgradeable, RoyaltyPaymentsLogic, ERC721Holder, ERC1155Holder, ERC165 { /// @dev Only EXTENSION_ROLE holders can perform upgrades. bytes32 private constant EXTENSION_ROLE = keccak256("EXTENSION_ROLE"); bytes32 private constant MODULE_TYPE = bytes32("MarketplaceV3"); uint256 private constant VERSION = 3; /// @dev The address of the native token wrapper contract. address private immutable nativeTokenWrapper; /*/////////////////////////////////////////////////////////////// Constructor + initializer logic //////////////////////////////////////////////////////////////*/ /// @dev We accept constructor params as a struct to avoid `Stack too deep` errors. struct MarketplaceConstructorParams { Extension[] extensions; address royaltyEngineAddress; address nativeTokenWrapper; } constructor( MarketplaceConstructorParams memory _marketplaceV3Params ) BaseRouter(_marketplaceV3Params.extensions) RoyaltyPaymentsLogic(_marketplaceV3Params.royaltyEngineAddress) { nativeTokenWrapper = _marketplaceV3Params.nativeTokenWrapper; _disableInitializers(); } receive() external payable { assert(msg.sender == nativeTokenWrapper); // only accept ETH via fallback from the native token wrapper contract } /// @dev Initializes the contract, like a constructor. function initialize( address _defaultAdmin, string memory _contractURI, address[] memory _trustedForwarders, address _platformFeeRecipient, uint16 _platformFeeBps ) external initializer { // Initialize BaseRouter __BaseRouter_init(); // Initialize inherited contracts, most base-like -> most derived. __ReentrancyGuard_init(); __ERC2771Context_init(_trustedForwarders); // Initialize this contract's state. _setupContractURI(_contractURI); _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin); _setupRole(EXTENSION_ROLE, _defaultAdmin); _setupRole(keccak256("LISTER_ROLE"), address(0)); _setupRole(keccak256("ASSET_ROLE"), address(0)); _setupRole(EXTENSION_ROLE, _defaultAdmin); _setRoleAdmin(EXTENSION_ROLE, EXTENSION_ROLE); } /*/////////////////////////////////////////////////////////////// Generic contract logic //////////////////////////////////////////////////////////////*/ /// @dev Returns the type of the contract. function contractType() external pure returns (bytes32) { return MODULE_TYPE; } /// @dev Returns the version of the contract. function contractVersion() external pure returns (uint8) { return uint8(VERSION); } /*/////////////////////////////////////////////////////////////// ERC 165 / 721 / 1155 logic //////////////////////////////////////////////////////////////*/ function supportsInterface( bytes4 interfaceId ) public view virtual override(ERC165, IERC165, ERC1155Receiver) returns (bool) { return interfaceId == type(IERC1155Receiver).interfaceId || interfaceId == type(IERC721Receiver).interfaceId || interfaceId == type(IRouter).interfaceId || interfaceId == type(IRouterState).interfaceId || super.supportsInterface(interfaceId); } /*/////////////////////////////////////////////////////////////// Overridable Permissions //////////////////////////////////////////////////////////////*/ /// @dev Checks whether platform fee info can be set in the given execution context. function _canSetPlatformFeeInfo() internal view override returns (bool) { return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); } /// @dev Checks whether contract metadata can be set in the given execution context. function _canSetContractURI() internal view override returns (bool) { return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); } /// @dev Returns whether royalty engine address can be set in the given execution context. function _canSetRoyaltyEngine() internal view override returns (bool) { return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); } /// @dev Checks whether an account has a particular role. function _hasRole(bytes32 _role, address _account) internal view returns (bool) { PermissionsStorage.Data storage data = PermissionsStorage.data(); return data._hasRole[_role][_account]; } /// @dev Returns whether all relevant permission and other checks are met before any upgrade. function _isAuthorizedCallToUpgrade() internal view virtual override returns (bool) { return _hasRole(EXTENSION_ROLE, msg.sender); } function _msgSender() internal view override(ERC2771ContextUpgradeable, Permissions, Multicall) returns (address sender) { return ERC2771ContextUpgradeable._msgSender(); } function _msgData() internal view override(ERC2771ContextUpgradeable, Permissions) returns (bytes calldata) { return ERC2771ContextUpgradeable._msgData(); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./utils/Bytecode.sol"; /** @title A key-value storage with auto-generated keys for storing chunks of data with a lower write & read cost. @author Agustin Aguilar <[email protected]> Readme: https://github.com/0xsequence/sstore2#readme */ library SSTORE2 { error WriteError(); /** @notice Stores `_data` and returns `pointer` as key for later retrieval @dev The pointer is a contract address with `_data` as code @param _data to be written @return pointer Pointer to the written `_data` */ function write(bytes memory _data) internal returns (address pointer) { // Append 00 to _data so contract can't be called // Build init code bytes memory code = Bytecode.creationCodeFor( abi.encodePacked( hex'00', _data ) ); // Deploy contract using create assembly { pointer := create(0, add(code, 32), mload(code)) } // Address MUST be non-zero if (pointer == address(0)) revert WriteError(); } /** @notice Reads the contents of the `_pointer` code as data, skips the first byte @dev The function is intended for reading pointers generated by `write` @param _pointer to be read @return data read from `_pointer` contract */ function read(address _pointer) internal view returns (bytes memory) { return Bytecode.codeAt(_pointer, 1, type(uint256).max); } /** @notice Reads the contents of the `_pointer` code as data, skips the first byte @dev The function is intended for reading pointers generated by `write` @param _pointer to be read @param _start number of bytes to skip @return data read from `_pointer` contract */ function read(address _pointer, uint256 _start) internal view returns (bytes memory) { return Bytecode.codeAt(_pointer, _start + 1, type(uint256).max); } /** @notice Reads the contents of the `_pointer` code as data, skips the first byte @dev The function is intended for reading pointers generated by `write` @param _pointer to be read @param _start number of bytes to skip @param _end index before which to end extraction @return data read from `_pointer` contract */ function read(address _pointer, uint256 _start, uint256 _end) internal view returns (bytes memory) { return Bytecode.codeAt(_pointer, _start + 1, _end + 1); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; library Bytecode { error InvalidCodeAtRange(uint256 _size, uint256 _start, uint256 _end); /** @notice Generate a creation code that results on a contract with `_code` as bytecode @param _code The returning value of the resulting `creationCode` @return creationCode (constructor) for new contract */ function creationCodeFor(bytes memory _code) internal pure returns (bytes memory) { /* 0x00 0x63 0x63XXXXXX PUSH4 _code.length size 0x01 0x80 0x80 DUP1 size size 0x02 0x60 0x600e PUSH1 14 14 size size 0x03 0x60 0x6000 PUSH1 00 0 14 size size 0x04 0x39 0x39 CODECOPY size 0x05 0x60 0x6000 PUSH1 00 0 size 0x06 0xf3 0xf3 RETURN <CODE> */ return abi.encodePacked( hex"63", uint32(_code.length), hex"80_60_0E_60_00_39_60_00_F3", _code ); } /** @notice Returns the size of the code on a given address @param _addr Address that may or may not contain code @return size of the code on the given `_addr` */ function codeSize(address _addr) internal view returns (uint256 size) { assembly { size := extcodesize(_addr) } } /** @notice Returns the code of a given address @dev It will fail if `_end < _start` @param _addr Address that may or may not contain code @param _start number of bytes of code to skip on read @param _end index before which to end extraction @return oCode read from `_addr` deployed bytecode Forked from: https://gist.github.com/KardanovIR/fe98661df9338c842b4a30306d507fbd */ function codeAt(address _addr, uint256 _start, uint256 _end) internal view returns (bytes memory oCode) { uint256 csize = codeSize(_addr); if (csize == 0) return bytes(""); if (_start > csize) return bytes(""); if (_end < _start) revert InvalidCodeAtRange(csize, _start, _end); unchecked { uint256 reqSize = _end - _start; uint256 maxSize = csize - _start; uint256 size = maxSize < reqSize ? maxSize : reqSize; assembly { // allocate output byte array - this could also be done without assembly // by using o_code = new bytes(size) oCode := mload(0x40) // new "memory end" including padding mstore(0x40, add(oCode, and(add(add(size, 0x20), 0x1f), not(0x1f)))) // store length in memory mstore(oCode, size) // actually retrieve the code, this needs assembly extcodecopy(_addr, add(oCode, 0x20), _start, size) } } } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interface/IRouter.sol"; /// @title ERC-7504 Dynamic Contracts: Router. /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Routes an incoming call to an appropriate implementation address. abstract contract Router is IRouter { /** \t *\t@notice delegateCalls the appropriate implementation address for the given incoming function call. \t *\t@dev The implementation address to delegateCall MUST be retrieved from calling `getImplementationForFunction` with the * incoming call's function selector. \t */ fallback() external payable virtual { if(msg.data.length == 0) return; address implementation = getImplementationForFunction(msg.sig); require(implementation != address(0), "Router: function does not exist."); _delegate(implementation); } /// @dev delegateCalls an `implementation` smart contract. function _delegate(address implementation) internal virtual { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize()) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize()) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } /** \t *\t@notice Returns the implementation address to delegateCall for the given function selector. \t *\t@param _functionSelector The function selector to get the implementation address for. \t *\t@return implementation The implementation address to delegateCall for the given function selector. \t */ function getImplementationForFunction(bytes4 _functionSelector) public view virtual returns (address implementation); }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title IExtension /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Provides an `Extension` abstraction for a router's implementation contracts. interface IExtension { /*/////////////////////////////////////////////////////////////// Structs //////////////////////////////////////////////////////////////*/ /** * @notice An interface to describe an extension's metadata. * * @param name The unique name of the extension. * @param metadataURI The URI where the metadata for the extension lives. * @param implementation The implementation smart contract address of the extension. */ struct ExtensionMetadata { string name; string metadataURI; address implementation; } /** * @notice An interface to describe an extension's function. * * @param functionSelector The 4 byte selector of the function. * @param functionSignature Function signature as a string. E.g. "transfer(address,address,uint256)" */ struct ExtensionFunction { bytes4 functionSelector; string functionSignature; } /** * @notice An interface to describe an extension. * * @param metadata The extension's metadata; it's name, metadata URI and implementation contract address. * @param functions The functions that belong to the extension. */ struct Extension { ExtensionMetadata metadata; ExtensionFunction[] functions; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./IExtension.sol"; /// @title IExtensionManager /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Defined storage and API for managing a router's extensions. interface IExtensionManager is IExtension { /*/////////////////////////////////////////////////////////////// Events //////////////////////////////////////////////////////////////*/ /// @dev Emitted when a extension is added. event ExtensionAdded(string indexed name, address indexed implementation, Extension extension); /// @dev Emitted when a extension is replaced. event ExtensionReplaced(string indexed name, address indexed implementation, Extension extension); /// @dev Emitted when a extension is removed. event ExtensionRemoved(string indexed name, Extension extension); /// @dev Emitted when a function is enabled i.e. made callable. event FunctionEnabled(string indexed name, bytes4 indexed functionSelector, ExtensionFunction extFunction, ExtensionMetadata extMetadata); /// @dev Emitted when a function is disabled i.e. made un-callable. event FunctionDisabled(string indexed name, bytes4 indexed functionSelector, ExtensionMetadata extMetadata); /*/////////////////////////////////////////////////////////////// External functions //////////////////////////////////////////////////////////////*/ /** * @notice Add a new extension to the router. * @param extension The extension to add. */ function addExtension(Extension memory extension) external; /** * @notice Fully replace an existing extension of the router. * @dev The extension with name `extension.name` is the extension being replaced. * @param extension The extension to replace or overwrite. */ function replaceExtension(Extension memory extension) external; /** * @notice Remove an existing extension from the router. * @param extensionName The name of the extension to remove. */ function removeExtension(string memory extensionName) external; /** * @notice Enables a single function in an existing extension. * @dev Makes the given function callable on the router. * * @param extensionName The name of the extension to which `extFunction` belongs. * @param extFunction The function to enable. */ function enableFunctionInExtension(string memory extensionName, ExtensionFunction memory extFunction) external; /** * @notice Disables a single function in an Extension. * * @param extensionName The name of the extension to which the function of `functionSelector` belongs. * @param functionSelector The function to disable. */ function disableFunctionInExtension(string memory extensionName, bytes4 functionSelector) external; }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title ERC-7504 Dynamic Contracts: IRouter. /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Routes an incoming call to an appropriate implementation address. /// @dev Fallback function delegateCalls `getImplementationForFunction(msg.sig)` for a given incoming call. /// NOTE: The ERC-165 identifier for this interface is 0xce0b6013. interface IRouter { \t/** \t *\t@notice delegateCalls the appropriate implementation address for the given incoming function call. \t *\t@dev The implementation address to delegateCall MUST be retrieved from calling `getImplementationForFunction` with the * incoming call's function selector. \t */ \tfallback() external payable; \t/*/////////////////////////////////////////////////////////////// View Functions //////////////////////////////////////////////////////////////*/ \t/** \t *\t@notice Returns the implementation address to delegateCall for the given function selector. \t *\t@param _functionSelector The function selector to get the implementation address for. \t *\t@return implementation The implementation address to delegateCall for the given function selector. \t */ function getImplementationForFunction(bytes4 _functionSelector) external view returns (address implementation); }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./IExtension.sol"; /// @title ERC-7504 Dynamic Contracts: IRouterState. /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Defines an API to expose a router's extensions. interface IRouterState is IExtension { /*/////////////////////////////////////////////////////////////// View Functions //////////////////////////////////////////////////////////////*/ /** * @notice Returns all extensions of the Router. * @return allExtensions An array of all extensions. */ function getAllExtensions() external view returns (Extension[] memory allExtensions); }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./IExtension.sol"; /// @title IRouterStateGetters. /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Helper view functions to inspect a router's state. interface IRouterStateGetters is IExtension { /*/////////////////////////////////////////////////////////////// View functions //////////////////////////////////////////////////////////////*/ /** * @notice Returns the extension metadata for a given function. * @param functionSelector The function selector to get the extension metadata for. * @return metadata The extension metadata for a given function. */ function getMetadataForFunction(bytes4 functionSelector) external view returns (ExtensionMetadata memory metadata); /** * @notice Returns the extension metadata and functions for a given extension. * @param extensionName The name of the extension to get the metadata and functions for. * @return extension The extension metadata and functions for a given extension. */ function getExtension(string memory extensionName) external view returns (Extension memory); }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title BaseRouterStorage /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Defined storage for base router library BaseRouterStorage { /// @custom:storage-location erc7201:base.router.storage bytes32 public constant BASE_ROUTER_STORAGE_POSITION = keccak256(abi.encode(uint256(keccak256("base.router.storage")) - 1)); struct Data { /// @dev Mapping used only for checking default extension validity in constructor. mapping(bytes4 => bool) functionMap; /// @dev Mapping used only for checking default extension validity in constructor. mapping(string => bool) extensionMap; } /// @dev Returns access to base router storage. function data() internal pure returns (Data storage data_) { bytes32 position = BASE_ROUTER_STORAGE_POSITION; assembly { data_.slot := position } } }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./StringSet.sol"; import "../interface/IExtension.sol"; /// @title IExtensionManagerStorage /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Defined storage for managing a router's extensions. library ExtensionManagerStorage { /// @custom:storage-location erc7201:extension.manager.storage bytes32 public constant EXTENSION_MANAGER_STORAGE_POSITION = keccak256(abi.encode(uint256(keccak256("extension.manager.storage")) - 1)); struct Data { /// @dev Set of names of all extensions of the router. StringSet.Set extensionNames; /// @dev Mapping from extension name => `Extension` i.e. extension metadata and functions. mapping(string => IExtension.Extension) extensions; /// @dev Mapping from function selector => metadata of the extension the function belongs to. mapping(bytes4 => IExtension.ExtensionMetadata) extensionMetadata; } /// @dev Returns access to the extension manager's storage. function data() internal pure returns (Data storage data_) { bytes32 position = EXTENSION_MANAGER_STORAGE_POSITION; assembly { data_.slot := position } } }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; library StringSet { struct Set { // Storage of set values string[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(string => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, string memory value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, string memory value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { string memory lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, string memory value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (string memory) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (string[] memory) { return set._values; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Set storage set, string memory value) internal returns (bool) { return _add(set, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Set storage set, string memory value) internal returns (bool) { return _remove(set, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Set storage set, string memory value) internal view returns (bool) { return _contains(set, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Set storage set) internal view returns (uint256) { return _length(set); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Set storage set, uint256 index) internal view returns (string memory) { return _at(set, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Set storage set) internal view returns (string[] memory) { return _values(set); } }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { Router, IRouter } from "../core/Router.sol"; import { IRouterState } from "../interface/IRouterState.sol"; import { IRouterStateGetters } from "../interface/IRouterStateGetters.sol"; import { BaseRouterStorage } from "../lib/BaseRouterStorage.sol"; import { ExtensionManager } from "./ExtensionManager.sol"; import { StringSet } from "../lib/StringSet.sol"; import "lib/sstore2/contracts/SSTORE2.sol"; /// @title BaseRouter /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice A router with an API to manage its extensions. abstract contract BaseRouter is Router, ExtensionManager { using StringSet for StringSet.Set; /// @notice The address where the router's default extension set is stored. address public immutable defaultExtensions; /// @notice Initialize the Router with a set of default extensions. constructor(Extension[] memory _extensions) { address pointer; if(_extensions.length > 0) { _validateExtensions(_extensions); pointer = SSTORE2.write(abi.encode(_extensions)); } defaultExtensions = pointer; } /// @notice Initialize the Router with a set of default extensions. function __BaseRouter_init() internal { if(defaultExtensions == address(0)) { return; } bytes memory data = SSTORE2.read(defaultExtensions); Extension[] memory defaults = abi.decode(data, (Extension[])); // Unchecked since we already validated extensions in constructor. __BaseRouter_init_unchecked(defaults); } /// @notice Initializes the Router with a set of extensions. function __BaseRouter_init_checked(Extension[] memory _extensions) internal { _validateExtensions(_extensions); __BaseRouter_init_unchecked(_extensions); } /// @notice Initializes the Router with a set of extensions. function __BaseRouter_init_unchecked(Extension[] memory _extensions) internal { for(uint256 i = 0; i < _extensions.length; i += 1) { Extension memory extension = _extensions[i]; // Store: new extension name. _extensionManagerStorage().extensionNames.add(extension.metadata.name); // 1. Store: metadata for extension. _setMetadataForExtension(extension.metadata.name, extension.metadata); uint256 len = extension.functions.length; for (uint256 j = 0; j < len; j += 1) { // 2. Store: name -> extension.functions map _extensionManagerStorage().extensions[extension.metadata.name].functions.push(extension.functions[j]); // 3. Store: metadata for function. _setMetadataForFunction(extension.functions[j].functionSelector, extension.metadata); } emit ExtensionAdded(extension.metadata.name, extension.metadata.implementation, extension); } } /// @notice Returns the implementation contract address for a given function signature. function getImplementationForFunction(bytes4 _functionSelector) public view virtual override returns (address) { return getMetadataForFunction(_functionSelector).implementation; } /// @dev Validates default extensions. function _validateExtensions(Extension[] memory _extensions) internal { uint256 len = _extensions.length; bool isValid = true; for (uint256 i = 0; i < len; i += 1) { isValid = _isValidExtension(_extensions[i]); if(!isValid) { break; } } require(isValid, "BaseRouter: invalid extension."); } function _isValidExtension(Extension memory _extension) internal returns (bool isValid) { isValid = bytes(_extension.metadata.name).length > 0 // non-empty name && !BaseRouterStorage.data().extensionMap[_extension.metadata.name] // unused name && _extension.metadata.implementation != address(0); // non-empty implementation BaseRouterStorage.data().extensionMap[_extension.metadata.name] = true; if(!isValid) { return false; } uint256 len = _extension.functions.length; for(uint256 i = 0; i < len; i += 1) { if(!isValid) { break; } ExtensionFunction memory _extFunction = _extension.functions[i]; /** * Note: `bytes4(0)` is the function selector for the `receive` function. * So, we maintain a special fn selector-signature mismatch check for the `receive` function. **/ bool mismatch = false; if(_extFunction.functionSelector == bytes4(0)) { mismatch = keccak256(abi.encode(_extFunction.functionSignature)) != keccak256(abi.encode("receive()")); } else { mismatch = _extFunction.functionSelector != bytes4(keccak256(abi.encodePacked(_extFunction.functionSignature))); } // No fn signature-selector mismatch and no duplicate function. isValid = !mismatch && !BaseRouterStorage.data().functionMap[_extFunction.functionSelector]; BaseRouterStorage.data().functionMap[_extFunction.functionSelector] = true; } } }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interface/IExtensionManager.sol"; import "../interface/IRouterState.sol"; import "../interface/IRouterStateGetters.sol"; import "../lib/ExtensionManagerStorage.sol"; /// @title ExtensionManager /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Defined storage and API for managing a router's extensions. abstract contract ExtensionManager is IExtensionManager, IRouterState, IRouterStateGetters { using StringSet for StringSet.Set; /*/////////////////////////////////////////////////////////////// Modifier //////////////////////////////////////////////////////////////*/ /// @notice Checks that a call to any external function is authorized. modifier onlyAuthorizedCall() { require(_isAuthorizedCallToUpgrade(), "ExtensionManager: unauthorized."); _; } /*/////////////////////////////////////////////////////////////// View functions //////////////////////////////////////////////////////////////*/ /** * @notice Returns all extensions of the Router. * @return allExtensions An array of all extensions. */ function getAllExtensions() external view virtual override returns (Extension[] memory allExtensions) { string[] memory names = _extensionManagerStorage().extensionNames.values(); uint256 len = names.length; allExtensions = new Extension[](len); for (uint256 i = 0; i < len; i += 1) { allExtensions[i] = _getExtension(names[i]); } } /** * @notice Returns the extension metadata for a given function. * @param functionSelector The function selector to get the extension metadata for. * @return metadata The extension metadata for a given function. */ function getMetadataForFunction(bytes4 functionSelector) public view virtual returns (ExtensionMetadata memory) { return _extensionManagerStorage().extensionMetadata[functionSelector]; } /** * @notice Returns the extension metadata and functions for a given extension. * @param extensionName The name of the extension to get the metadata and functions for. * @return extension The extension metadata and functions for a given extension. */ function getExtension(string memory extensionName) public view virtual returns (Extension memory) { return _getExtension(extensionName); } /*/////////////////////////////////////////////////////////////// External functions //////////////////////////////////////////////////////////////*/ /** * @notice Add a new extension to the router. * @param _extension The extension to add. */ function addExtension(Extension memory _extension) public virtual onlyAuthorizedCall { _addExtension(_extension); } /** * @notice Fully replace an existing extension of the router. * @dev The extension with name `extension.name` is the extension being replaced. * @param _extension The extension to replace or overwrite. */ function replaceExtension(Extension memory _extension) public virtual onlyAuthorizedCall { _replaceExtension(_extension); } /** * @notice Remove an existing extension from the router. * @param _extensionName The name of the extension to remove. */ function removeExtension(string memory _extensionName) public virtual onlyAuthorizedCall { _removeExtension(_extensionName); } /** * @notice Enables a single function in an existing extension. * @dev Makes the given function callable on the router. * * @param _extensionName The name of the extension to which `extFunction` belongs. * @param _function The function to enable. */ function enableFunctionInExtension(string memory _extensionName, ExtensionFunction memory _function) public virtual onlyAuthorizedCall { _enableFunctionInExtension(_extensionName, _function); } /** * @notice Disables a single function in an Extension. * * @param _extensionName The name of the extension to which the function of `functionSelector` belongs. * @param _functionSelector The function to disable. */ function disableFunctionInExtension(string memory _extensionName, bytes4 _functionSelector) public virtual onlyAuthorizedCall { _disableFunctionInExtension(_extensionName, _functionSelector); } /*/////////////////////////////////////////////////////////////// Internal functions //////////////////////////////////////////////////////////////*/ /// @dev Add a new extension to the router. function _addExtension(Extension memory _extension) internal virtual { // Check: extension namespace must not already exist. // Check: provided extension namespace must not be empty. // Check: provided extension implementation must be non-zero. // Store: new extension name. require(_canAddExtension(_extension), "ExtensionManager: cannot add extension."); // 1. Store: metadata for extension. _setMetadataForExtension(_extension.metadata.name, _extension.metadata); uint256 len = _extension.functions.length; for (uint256 i = 0; i < len; i += 1) { // 2. Store: function for extension. _addToFunctionMap(_extension.metadata.name, _extension.functions[i]); // 3. Store: metadata for function. _setMetadataForFunction(_extension.functions[i].functionSelector, _extension.metadata); } emit ExtensionAdded(_extension.metadata.name, _extension.metadata.implementation, _extension); } /// @dev Fully replace an existing extension of the router. function _replaceExtension(Extension memory _extension) internal virtual { // Check: extension namespace must already exist. // Check: provided extension implementation must be non-zero. require(_canReplaceExtension(_extension), "ExtensionManager: cannot replace extension."); // 1. Store: metadata for extension. _setMetadataForExtension(_extension.metadata.name, _extension.metadata); // 2. Delete: existing extension.functions and metadata for each function. _removeAllFunctionsFromExtension(_extension.metadata.name); uint256 len = _extension.functions.length; for (uint256 i = 0; i < len; i += 1) { // 2. Store: function for extension. _addToFunctionMap(_extension.metadata.name, _extension.functions[i]); // 3. Store: metadata for function. _setMetadataForFunction(_extension.functions[i].functionSelector, _extension.metadata); } emit ExtensionReplaced(_extension.metadata.name, _extension.metadata.implementation, _extension); } /// @dev Remove an existing extension from the router. function _removeExtension(string memory _extensionName) internal virtual { // Check: extension namespace must already exist. // Delete: extension namespace. require(_canRemoveExtension(_extensionName), "ExtensionManager: cannot remove extension."); Extension memory extension = _extensionManagerStorage().extensions[_extensionName]; // 1. Delete: metadata for extension. _deleteMetadataForExtension(_extensionName); // 2. Delete: existing extension.functions and metadata for each function. _removeAllFunctionsFromExtension(_extensionName); emit ExtensionRemoved(_extensionName, extension); } /// @dev Makes the given function callable on the router. function _enableFunctionInExtension(string memory _extensionName, ExtensionFunction memory _function) internal virtual { // Check: extension namespace must already exist. require(_canEnableFunctionInExtension(_extensionName, _function), "ExtensionManager: cannot Store: function for extension."); // 1. Store: function for extension. _addToFunctionMap(_extensionName, _function); ExtensionMetadata memory metadata = _extensionManagerStorage().extensions[_extensionName].metadata; // 2. Store: metadata for function. _setMetadataForFunction(_function.functionSelector, metadata); emit FunctionEnabled(_extensionName, _function.functionSelector, _function, metadata); } /// @dev Disables a single function in an Extension. function _disableFunctionInExtension(string memory _extensionName, bytes4 _functionSelector) public virtual onlyAuthorizedCall { // Check: extension namespace must already exist. // Check: function must be mapped to provided extension. require(_canDisableFunctionInExtension(_extensionName, _functionSelector), "ExtensionManager: cannot remove function from extension."); ExtensionMetadata memory extMetadata = _extensionManagerStorage().extensionMetadata[_functionSelector]; // 1. Delete: function from extension. _deleteFromFunctionMap(_extensionName, _functionSelector); // 2. Delete: metadata for function. _deleteMetadataForFunction(_functionSelector); emit FunctionDisabled(_extensionName, _functionSelector, extMetadata); } /// @dev Returns the Extension for a given name. function _getExtension(string memory _extensionName) internal view returns (Extension memory) { return _extensionManagerStorage().extensions[_extensionName]; } /// @dev Sets the ExtensionMetadata for a given extension. function _setMetadataForExtension(string memory _extensionName, ExtensionMetadata memory _metadata) internal { _extensionManagerStorage().extensions[_extensionName].metadata = _metadata; } /// @dev Deletes the ExtensionMetadata for a given extension. function _deleteMetadataForExtension(string memory _extensionName) internal { delete _extensionManagerStorage().extensions[_extensionName].metadata; } /// @dev Sets the ExtensionMetadata for a given function. function _setMetadataForFunction(bytes4 _functionSelector, ExtensionMetadata memory _metadata) internal { _extensionManagerStorage().extensionMetadata[_functionSelector] = _metadata; } /// @dev Deletes the ExtensionMetadata for a given function. function _deleteMetadataForFunction(bytes4 _functionSelector) internal { delete _extensionManagerStorage().extensionMetadata[_functionSelector]; } /// @dev Adds a function to the function map of an extension. function _addToFunctionMap(string memory _extensionName, ExtensionFunction memory _extFunction) internal virtual { /** * Note: `bytes4(0)` is the function selector for the `receive` function. * So, we maintain a special fn selector-signature mismatch check for the `receive` function. **/ bool mismatch = false; if(_extFunction.functionSelector == bytes4(0)) { mismatch = keccak256(abi.encode(_extFunction.functionSignature)) != keccak256(abi.encode("receive()")); } else { mismatch = _extFunction.functionSelector != bytes4(keccak256(abi.encodePacked(_extFunction.functionSignature))); } // Check: function selector and signature must match. require( !mismatch, "ExtensionManager: fn selector and signature mismatch." ); // Check: function must not already be mapped to an implementation. require( _extensionManagerStorage().extensionMetadata[_extFunction.functionSelector].implementation == address(0), "ExtensionManager: function impl already exists." ); // Store: name -> extension.functions map _extensionManagerStorage().extensions[_extensionName].functions.push(_extFunction); } /// @dev Deletes a function from an extension's function map. function _deleteFromFunctionMap(string memory _extensionName, bytes4 _functionSelector) internal { ExtensionFunction[] memory extensionFunctions = _extensionManagerStorage().extensions[_extensionName].functions; uint256 len = extensionFunctions.length; for (uint256 i = 0; i < len; i += 1) { if(extensionFunctions[i].functionSelector == _functionSelector) { // Delete: particular function from name -> extension.functions map _extensionManagerStorage().extensions[_extensionName].functions[i] = _extensionManagerStorage().extensions[_extensionName].functions[len - 1]; _extensionManagerStorage().extensions[_extensionName].functions.pop(); break; } } } /// @dev Removes all functions from an Extension. function _removeAllFunctionsFromExtension(string memory _extensionName) internal { ExtensionFunction[] memory functions = _extensionManagerStorage().extensions[_extensionName].functions; // Delete: existing name -> extension.functions map delete _extensionManagerStorage().extensions[_extensionName].functions; for(uint256 i = 0; i < functions.length; i += 1) { // Delete: metadata for function. _deleteMetadataForFunction(functions[i].functionSelector); } } /// @dev Returns whether a new extension can be added in the given execution context. function _canAddExtension(Extension memory _extension) internal virtual returns (bool) { // Check: provided extension namespace must not be empty. require(bytes(_extension.metadata.name).length > 0, "ExtensionManager: empty name."); // Check: extension namespace must not already exist. // Store: new extension name. require(_extensionManagerStorage().extensionNames.add(_extension.metadata.name), "ExtensionManager: extension already exists."); // Check: extension implementation must be non-zero. require(_extension.metadata.implementation != address(0), "ExtensionManager: adding extension without implementation."); return true; } /// @dev Returns whether an extension can be replaced in the given execution context. function _canReplaceExtension(Extension memory _extension) internal virtual returns (bool) { // Check: extension namespace must already exist. require(_extensionManagerStorage().extensionNames.contains(_extension.metadata.name), "ExtensionManager: extension does not exist."); // Check: extension implementation must be non-zero. require(_extension.metadata.implementation != address(0), "ExtensionManager: adding extension without implementation."); return true; } /// @dev Returns whether an extension can be removed in the given execution context. function _canRemoveExtension(string memory _extensionName) internal virtual returns (bool) { // Check: extension namespace must already exist. // Delete: extension namespace. require(_extensionManagerStorage().extensionNames.remove(_extensionName), "ExtensionManager: extension does not exist."); return true; } /// @dev Returns whether a function can be enabled in an extension in the given execution context. function _canEnableFunctionInExtension(string memory _extensionName, ExtensionFunction memory) internal view virtual returns (bool) { // Check: extension namespace must already exist. require(_extensionManagerStorage().extensionNames.contains(_extensionName), "ExtensionManager: extension does not exist."); return true; } /// @dev Returns whether a function can be disabled in an extension in the given execution context. function _canDisableFunctionInExtension(string memory _extensionName, bytes4 _functionSelector) internal view virtual returns (bool) { // Check: extension namespace must already exist. require(_extensionManagerStorage().extensionNames.contains(_extensionName), "ExtensionManager: extension does not exist."); // Check: function must be mapped to provided extension. require(keccak256(abi.encode(_extensionManagerStorage().extensionMetadata[_functionSelector].name)) == keccak256(abi.encode(_extensionName)), "ExtensionManager: incorrect extension."); return true; } /// @dev Returns the ExtensionManager storage. function _extensionManagerStorage() internal pure returns (ExtensionManagerStorage.Data storage data) { data = ExtensionManagerStorage.data(); } /// @dev To override; returns whether all relevant permission and other checks are met before any upgrade. function _isAuthorizedCallToUpgrade() internal view virtual returns (bool); }// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev _Available since v3.1._ */ interface IERC1155Receiver is IERC165 { /** * @dev Handles the receipt of a single ERC1155 token type. This function is * called at the end of a `safeTransferFrom` after the balance has been updated. * * NOTE: To accept the transfer, this must return * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` * (i.e. 0xf23a6e61, or its own function selector). * * @param operator The address which initiated the transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param id The ID of the token being transferred * @param value The amount of tokens being transferred * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed */ function onERC1155Received( address operator, address from, uint256 id, uint256 value, bytes calldata data ) external returns (bytes4); /** * @dev Handles the receipt of a multiple ERC1155 token types. This function * is called at the end of a `safeBatchTransferFrom` after the balances have * been updated. * * NOTE: To accept the transfer(s), this must return * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` * (i.e. 0xbc197c81, or its own function selector). * * @param operator The address which initiated the batch transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param ids An array containing ids of each token being transferred (order and length must match values array) * @param values An array containing amounts of each token being transferred (order and length must match ids array) * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed */ function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external returns (bytes4); } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/utils/ERC1155Holder.sol) pragma solidity ^0.8.0; import "./ERC1155Receiver.sol"; /** * Simple implementation of `ERC1155Receiver` that will allow a contract to hold ERC1155 tokens. * * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be * stuck. * * @dev _Available since v3.1._ */ contract ERC1155Holder is ERC1155Receiver { function onERC1155Received( address, address, uint256, uint256, bytes memory ) public virtual override returns (bytes4) { return this.onERC1155Received.selector; } function onERC1155BatchReceived( address, address, uint256[] memory, uint256[] memory, bytes memory ) public virtual override returns (bytes4) { return this.onERC1155BatchReceived.selector; } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol) pragma solidity ^0.8.0; import "../IERC1155Receiver.sol"; import "../../../utils/introspection/ERC165.sol"; /** * @dev _Available since v3.1._ */ abstract contract ERC1155Receiver is ERC165, IERC1155Receiver { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId); } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol) pragma solidity ^0.8.0; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. * * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/utils/ERC721Holder.sol) pragma solidity ^0.8.0; import "../IERC721Receiver.sol"; /** * @dev Implementation of the {IERC721Receiver} interface. * * Accepts all token transfers. * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}. */ contract ERC721Holder is IERC721Receiver { /** * @dev See {IERC721Receiver-onERC721Received}. * * Always returns `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) { return this.onERC721Received.selector; } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
File 3 of 3: MarketplaceV3
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./interface/IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * [EIP](https://eips.ethereum.org/EIPS/eip-165). * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified) * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); } // SPDX-License-Identifier: Apache 2.0 pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Interface for the NFT Royalty Standard. * * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal * support for royalty payments across all NFT marketplaces and ecosystem participants. * * _Available since v4.5._ */ interface IERC2981 is IERC165 { /** * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of * exchange. The royalty amount is denominated and should be payed in that same unit of exchange. */ function royaltyInfo( uint256 tokenId, uint256 salePrice ) external view returns (address receiver, uint256 royaltyAmount); } // SPDX-License-Identifier: Apache 2.0 pragma solidity ^0.8.0; /// @author thirdweb import "../lib/Address.sol"; import "./interface/IMulticall.sol"; /** * @dev Provides a function to batch together multiple calls in a single external call. * * _Available since v4.1._ */ contract Multicall is IMulticall { /** * @notice Receives and executes a batch of function calls on this contract. * @dev Receives and executes a batch of function calls on this contract. * * @param data The bytes data that makes up the batch of function calls to execute. * @return results The bytes data that makes up the result of the batch of function calls executed. */ function multicall(bytes[] calldata data) external returns (bytes[] memory results) { results = new bytes[](data.length); address sender = _msgSender(); bool isForwarder = msg.sender != sender; for (uint256 i = 0; i < data.length; i++) { if (isForwarder) { results[i] = Address.functionDelegateCall(address(this), abi.encodePacked(data[i], sender)); } else { results[i] = Address.functionDelegateCall(address(this), data[i]); } } return results; } /// @notice Returns the sender in the given execution context. function _msgSender() internal view virtual returns (address) { return msg.sender; } } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb /** * Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI * for you contract. * * Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea. */ interface IContractMetadata { /// @dev Returns the metadata URI of the contract. function contractURI() external view returns (string memory); /** * @dev Sets contract URI for the storefront-level metadata of the contract. * Only module admin can call this function. */ function setContractURI(string calldata _uri) external; /// @dev Emitted when the contract URI is updated. event ContractURIUpdated(string prevURI, string newURI); } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; interface IERC2771Context { function isTrustedForwarder(address forwarder) external view returns (bool); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @author thirdweb /** * @dev Provides a function to batch together multiple calls in a single external call. * * _Available since v4.1._ */ interface IMulticall { /** * @dev Receives and executes a batch of function calls on this contract. */ function multicall(bytes[] calldata data) external returns (bytes[] memory results); } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IPermissions { /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) external; } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb import "./IPermissions.sol"; /** * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. */ interface IPermissionsEnumerable is IPermissions { /** * @dev Returns one of the accounts that have `role`. `index` must be a * value between 0 and {getRoleMemberCount}, non-inclusive. * * Role bearers are not sorted in any particular way, and their ordering may * change at any point. * * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure * you perform all queries on the same block. See the following * [forum post](https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296) * for more information. */ function getRoleMember(bytes32 role, uint256 index) external view returns (address); /** * @dev Returns the number of accounts that have `role`. Can be used * together with {getRoleMember} to enumerate all bearers of a role. */ function getRoleMemberCount(bytes32 role) external view returns (uint256); } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb /** * Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading * the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic * that uses information about platform fees, if desired. */ interface IPlatformFee { /// @dev Fee type variants: percentage fee and flat fee enum PlatformFeeType { Bps, Flat } /// @dev Returns the platform fee bps and recipient. function getPlatformFeeInfo() external view returns (address, uint16); /// @dev Lets a module admin update the fees on primary sales. function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external; /// @dev Emitted when fee on primary sales is updated. event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); /// @dev Emitted when the flat platform fee is updated. event FlatPlatformFeeUpdated(address platformFeeRecipient, uint256 flatFee); /// @dev Emitted when the platform fee type is updated. event PlatformFeeTypeUpdated(PlatformFeeType feeType); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @author: manifold.xyz import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /** * @dev Lookup engine interface */ interface IRoyaltyEngineV1 is IERC165 { /** * Get the royalty for a given token (address, id) and value amount. Does not cache the bps/amounts. Caches the spec for a given token address * * @param tokenAddress - The address of the token * @param tokenId - The id of the token * @param value - The value you wish to get the royalty of * * returns Two arrays of equal length, royalty recipients and the corresponding amount each recipient should get */ function getRoyalty( address tokenAddress, uint256 tokenId, uint256 value ) external returns (address payable[] memory recipients, uint256[] memory amounts); /** * View only version of getRoyalty * * @param tokenAddress - The address of the token * @param tokenId - The id of the token * @param value - The value you wish to get the royalty of * * returns Two arrays of equal length, royalty recipients and the corresponding amount each recipient should get */ function getRoyaltyView( address tokenAddress, uint256 tokenId, uint256 value ) external view returns (address payable[] memory recipients, uint256[] memory amounts); } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /** * @dev Read royalty info for a token. * Supports RoyaltyEngineV1 and RoyaltyRegistry by manifold.xyz. */ interface IRoyaltyPayments is IERC165 { /// @dev Emitted when the address of RoyaltyEngine is set or updated. event RoyaltyEngineUpdated(address indexed previousAddress, address indexed newAddress); /** * Get the royalty for a given token (address, id) and value amount. * * @param tokenAddress - The address of the token * @param tokenId - The id of the token * @param value - The value you wish to get the royalty of * * returns Two arrays of equal length, royalty recipients and the corresponding amount each recipient should get */ function getRoyalty( address tokenAddress, uint256 tokenId, uint256 value ) external returns (address payable[] memory recipients, uint256[] memory amounts); /** * Set or override RoyaltyEngine address * * @param _royaltyEngineAddress - RoyaltyEngineV1 address */ function setRoyaltyEngine(address _royaltyEngineAddress) external; } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb import "../interface/IContractMetadata.sol"; /** * @author thirdweb.com * * @title Contract Metadata * @notice Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI * for you contract. * Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea. */ library ContractMetadataStorage { /// @custom:storage-location erc7201:contract.metadata.storage /// @dev keccak256(abi.encode(uint256(keccak256("contract.metadata.storage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 public constant CONTRACT_METADATA_STORAGE_POSITION = 0x4bc804ba64359c0e35e5ed5d90ee596ecaa49a3a930ddcb1470ea0dd625da900; struct Data { /// @notice Returns the contract metadata URI. string contractURI; } function data() internal pure returns (Data storage data_) { bytes32 position = CONTRACT_METADATA_STORAGE_POSITION; assembly { data_.slot := position } } } abstract contract ContractMetadata is IContractMetadata { /** * @notice Lets a contract admin set the URI for contract-level metadata. * @dev Caller should be authorized to setup contractURI, e.g. contract admin. * See {_canSetContractURI}. * Emits {ContractURIUpdated Event}. * * @param _uri keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") */ function setContractURI(string memory _uri) external override { if (!_canSetContractURI()) { revert("Not authorized"); } _setupContractURI(_uri); } /// @dev Lets a contract admin set the URI for contract-level metadata. function _setupContractURI(string memory _uri) internal { string memory prevURI = _contractMetadataStorage().contractURI; _contractMetadataStorage().contractURI = _uri; emit ContractURIUpdated(prevURI, _uri); } /// @notice Returns the contract metadata URI. function contractURI() public view virtual override returns (string memory) { return _contractMetadataStorage().contractURI; } /// @dev Returns the AccountPermissions storage. function _contractMetadataStorage() internal pure returns (ContractMetadataStorage.Data storage data) { data = ContractMetadataStorage.data(); } /// @dev Returns whether contract metadata can be set in the given execution context. function _canSetContractURI() internal view virtual returns (bool); } // SPDX-License-Identifier: Apache 2.0 pragma solidity ^0.8.0; import "../interface/IERC2771Context.sol"; import "./Initializable.sol"; /** * @dev Context variant with ERC2771 support. */ library ERC2771ContextStorage { /// @custom:storage-location erc7201:erc2771.context.storage /// @dev keccak256(abi.encode(uint256(keccak256("erc2771.context.storage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 public constant ERC2771_CONTEXT_STORAGE_POSITION = 0x82aadcdf5bea62fd30615b6c0754b644e71b6c1e8c55b71bb927ad005b504f00; struct Data { mapping(address => bool) trustedForwarder; } function data() internal pure returns (Data storage data_) { bytes32 position = ERC2771_CONTEXT_STORAGE_POSITION; assembly { data_.slot := position } } } /** * @dev Context variant with ERC2771 support. */ abstract contract ERC2771ContextUpgradeable is Initializable { function __ERC2771Context_init(address[] memory trustedForwarder) internal onlyInitializing { __ERC2771Context_init_unchained(trustedForwarder); } function __ERC2771Context_init_unchained(address[] memory trustedForwarder) internal onlyInitializing { for (uint256 i = 0; i < trustedForwarder.length; i++) { _erc2771ContextStorage().trustedForwarder[trustedForwarder[i]] = true; } } function isTrustedForwarder(address forwarder) public view virtual returns (bool) { return _erc2771ContextStorage().trustedForwarder[forwarder]; } function _msgSender() internal view virtual returns (address sender) { if (isTrustedForwarder(msg.sender)) { // The assembly code is more direct than the Solidity version using `abi.decode`. assembly { sender := shr(96, calldataload(sub(calldatasize(), 20))) } } else { return msg.sender; } } function _msgData() internal view virtual returns (bytes calldata) { if (isTrustedForwarder(msg.sender)) { return msg.data[:msg.data.length - 20]; } else { return msg.data; } } /// @dev Returns the ERC2771ContextStorage storage. function _erc2771ContextStorage() internal pure returns (ERC2771ContextStorage.Data storage data) { data = ERC2771ContextStorage.data(); } uint256[49] private __gap; } // SPDX-License-Identifier: Apache 2.0 pragma solidity ^0.8.0; import "../../lib/Address.sol"; library InitStorage { /// @custom:storage-location erc7201:init.storage /// @dev keccak256(abi.encode(uint256(keccak256("init.storage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 constant INIT_STORAGE_POSITION = 0x322cf19c484104d3b1a9c2982ebae869ede3fa5f6c4703ca41b9a48c76ee0300; /// @dev Layout of the entrypoint contract's storage. struct Data { uint8 initialized; bool initializing; } /// @dev Returns the entrypoint contract's data at the relevant storage location. function data() internal pure returns (Data storage data_) { bytes32 position = INIT_STORAGE_POSITION; assembly { data_.slot := position } } } abstract contract Initializable { /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. */ modifier initializer() { uint8 _initialized = _initStorage().initialized; bool _initializing = _initStorage().initializing; bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initStorage().initialized = 1; if (isTopLevelCall) { _initStorage().initializing = true; } _; if (isTopLevelCall) { _initStorage().initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original * initialization step. This is essential to configure modules that are added through upgrades and that require * initialization. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. */ modifier reinitializer(uint8 version) { uint8 _initialized = _initStorage().initialized; bool _initializing = _initStorage().initializing; require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initStorage().initialized = version; _initStorage().initializing = true; _; _initStorage().initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initStorage().initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. */ function _disableInitializers() internal virtual { uint8 _initialized = _initStorage().initialized; bool _initializing = _initStorage().initializing; require(!_initializing, "Initializable: contract is initializing"); if (_initialized < type(uint8).max) { _initStorage().initialized = type(uint8).max; emit Initialized(type(uint8).max); } } /// @dev Returns the InitStorage storage. function _initStorage() internal pure returns (InitStorage.Data storage data) { data = InitStorage.data(); } } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb import "../interface/IPermissions.sol"; import "../../lib/Strings.sol"; /** * @title Permissions * @dev This contracts provides extending-contracts with role-based access control mechanisms */ library PermissionsStorage { /// @custom:storage-location erc7201:permissions.storage /// @dev keccak256(abi.encode(uint256(keccak256("permissions.storage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 public constant PERMISSIONS_STORAGE_POSITION = 0x0a7b0f5c59907924802379ebe98cdc23e2ee7820f63d30126e10b3752010e500; struct Data { /// @dev Map from keccak256 hash of a role => a map from address => whether address has role. mapping(bytes32 => mapping(address => bool)) _hasRole; /// @dev Map from keccak256 hash of a role to role admin. See {getRoleAdmin}. mapping(bytes32 => bytes32) _getRoleAdmin; } function data() internal pure returns (Data storage data_) { bytes32 position = PERMISSIONS_STORAGE_POSITION; assembly { data_.slot := position } } } contract Permissions is IPermissions { /// @dev Default admin role for all roles. Only accounts with this role can grant/revoke other roles. bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /// @dev Modifier that checks if an account has the specified role; reverts otherwise. modifier onlyRole(bytes32 role) { _checkRole(role, _msgSender()); _; } /** * @notice Checks whether an account has a particular role. * @dev Returns `true` if `account` has been granted `role`. * * @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") * @param account Address of the account for which the role is being checked. */ function hasRole(bytes32 role, address account) public view override returns (bool) { return _permissionsStorage()._hasRole[role][account]; } /** * @notice Checks whether an account has a particular role; * role restrictions can be swtiched on and off. * * @dev Returns `true` if `account` has been granted `role`. * Role restrictions can be swtiched on and off: * - If address(0) has ROLE, then the ROLE restrictions * don't apply. * - If address(0) does not have ROLE, then the ROLE * restrictions will apply. * * @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") * @param account Address of the account for which the role is being checked. */ function hasRoleWithSwitch(bytes32 role, address account) public view returns (bool) { if (!_permissionsStorage()._hasRole[role][address(0)]) { return _permissionsStorage()._hasRole[role][account]; } return true; } /** * @notice Returns the admin role that controls the specified role. * @dev See {grantRole} and {revokeRole}. * To change a role's admin, use {_setRoleAdmin}. * * @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") */ function getRoleAdmin(bytes32 role) external view override returns (bytes32) { return _permissionsStorage()._getRoleAdmin[role]; } /** * @notice Grants a role to an account, if not previously granted. * @dev Caller must have admin role for the `role`. * Emits {RoleGranted Event}. * * @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") * @param account Address of the account to which the role is being granted. */ function grantRole(bytes32 role, address account) public virtual override { _checkRole(_permissionsStorage()._getRoleAdmin[role], _msgSender()); if (_permissionsStorage()._hasRole[role][account]) { revert("Can only grant to non holders"); } _setupRole(role, account); } /** * @notice Revokes role from an account. * @dev Caller must have admin role for the `role`. * Emits {RoleRevoked Event}. * * @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") * @param account Address of the account from which the role is being revoked. */ function revokeRole(bytes32 role, address account) public virtual override { _checkRole(_permissionsStorage()._getRoleAdmin[role], _msgSender()); _revokeRole(role, account); } /** * @notice Revokes role from the account. * @dev Caller must have the `role`, with caller being the same as `account`. * Emits {RoleRevoked Event}. * * @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") * @param account Address of the account from which the role is being revoked. */ function renounceRole(bytes32 role, address account) public virtual override { if (_msgSender() != account) { revert("Can only renounce for self"); } _revokeRole(role, account); } /// @dev Sets `adminRole` as `role`'s admin role. function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = _permissionsStorage()._getRoleAdmin[role]; _permissionsStorage()._getRoleAdmin[role] = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /// @dev Sets up `role` for `account` function _setupRole(bytes32 role, address account) internal virtual { _permissionsStorage()._hasRole[role][account] = true; emit RoleGranted(role, account, _msgSender()); } /// @dev Revokes `role` from `account` function _revokeRole(bytes32 role, address account) internal virtual { _checkRole(role, account); delete _permissionsStorage()._hasRole[role][account]; emit RoleRevoked(role, account, _msgSender()); } /// @dev Checks `role` for `account`. Reverts with a message including the required role. function _checkRole(bytes32 role, address account) internal view virtual { if (!_permissionsStorage()._hasRole[role][account]) { revert( string( abi.encodePacked( "Permissions: account ", Strings.toHexString(uint160(account), 20), " is missing role ", Strings.toHexString(uint256(role), 32) ) ) ); } } /// @dev Checks `role` for `account`. Reverts with a message including the required role. function _checkRoleWithSwitch(bytes32 role, address account) internal view virtual { if (!hasRoleWithSwitch(role, account)) { revert( string( abi.encodePacked( "Permissions: account ", Strings.toHexString(uint160(account), 20), " is missing role ", Strings.toHexString(uint256(role), 32) ) ) ); } } function _msgSender() internal view virtual returns (address sender) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } /// @dev Returns the Permissions storage. function _permissionsStorage() internal pure returns (PermissionsStorage.Data storage data) { data = PermissionsStorage.data(); } } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb import "../interface/IPermissionsEnumerable.sol"; import "./Permissions.sol"; /** * @title PermissionsEnumerable * @dev This contracts provides extending-contracts with role-based access control mechanisms. * Also provides interfaces to view all members with a given role, and total count of members. */ library PermissionsEnumerableStorage { /// @custom:storage-location erc7201:extension.manager.storage bytes32 public constant PERMISSIONS_ENUMERABLE_STORAGE_POSITION = keccak256(abi.encode(uint256(keccak256("permissions.enumerable.storage")) - 1)) & ~bytes32(uint256(0xff)); /** * @notice A data structure to store data of members for a given role. * * @param index Current index in the list of accounts that have a role. * @param members map from index => address of account that has a role * @param indexOf map from address => index which the account has. */ struct RoleMembers { uint256 index; mapping(uint256 => address) members; mapping(address => uint256) indexOf; } struct Data { /// @dev map from keccak256 hash of a role to its members' data. See {RoleMembers}. mapping(bytes32 => RoleMembers) roleMembers; } function data() internal pure returns (Data storage data_) { bytes32 position = PERMISSIONS_ENUMERABLE_STORAGE_POSITION; assembly { data_.slot := position } } } contract PermissionsEnumerable is IPermissionsEnumerable, Permissions { /** * @notice Returns the role-member from a list of members for a role, * at a given index. * @dev Returns `member` who has `role`, at `index` of role-members list. * See struct {RoleMembers}, and mapping {roleMembers} * * @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") * @param index Index in list of current members for the role. * * @return member Address of account that has `role` */ function getRoleMember(bytes32 role, uint256 index) external view override returns (address member) { uint256 currentIndex = _permissionsEnumerableStorage().roleMembers[role].index; uint256 check; for (uint256 i = 0; i < currentIndex; i += 1) { if (_permissionsEnumerableStorage().roleMembers[role].members[i] != address(0)) { if (check == index) { member = _permissionsEnumerableStorage().roleMembers[role].members[i]; return member; } check += 1; } else if ( hasRole(role, address(0)) && i == _permissionsEnumerableStorage().roleMembers[role].indexOf[address(0)] ) { check += 1; } } } /** * @notice Returns total number of accounts that have a role. * @dev Returns `count` of accounts that have `role`. * See struct {RoleMembers}, and mapping {roleMembers} * * @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") * * @return count Total number of accounts that have `role` */ function getRoleMemberCount(bytes32 role) external view override returns (uint256 count) { uint256 currentIndex = _permissionsEnumerableStorage().roleMembers[role].index; for (uint256 i = 0; i < currentIndex; i += 1) { if (_permissionsEnumerableStorage().roleMembers[role].members[i] != address(0)) { count += 1; } } if (hasRole(role, address(0))) { count += 1; } } /// @dev Revokes `role` from `account`, and removes `account` from {roleMembers} /// See {_removeMember} function _revokeRole(bytes32 role, address account) internal virtual override { super._revokeRole(role, account); _removeMember(role, account); } /// @dev Grants `role` to `account`, and adds `account` to {roleMembers} /// See {_addMember} function _setupRole(bytes32 role, address account) internal virtual override { super._setupRole(role, account); _addMember(role, account); } /// @dev adds `account` to {roleMembers}, for `role` function _addMember(bytes32 role, address account) internal { uint256 idx = _permissionsEnumerableStorage().roleMembers[role].index; _permissionsEnumerableStorage().roleMembers[role].index += 1; _permissionsEnumerableStorage().roleMembers[role].members[idx] = account; _permissionsEnumerableStorage().roleMembers[role].indexOf[account] = idx; } /// @dev removes `account` from {roleMembers}, for `role` function _removeMember(bytes32 role, address account) internal { uint256 idx = _permissionsEnumerableStorage().roleMembers[role].indexOf[account]; delete _permissionsEnumerableStorage().roleMembers[role].members[idx]; delete _permissionsEnumerableStorage().roleMembers[role].indexOf[account]; } /// @dev Returns the PermissionsEnumerable storage. function _permissionsEnumerableStorage() internal pure returns (PermissionsEnumerableStorage.Data storage data) { data = PermissionsEnumerableStorage.data(); } } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb import "../interface/IPlatformFee.sol"; /** * @author thirdweb.com */ library PlatformFeeStorage { /// @custom:storage-location erc7201:platform.fee.storage /// @dev keccak256(abi.encode(uint256(keccak256("platform.fee.storage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 public constant PLATFORM_FEE_STORAGE_POSITION = 0xc0c34308b4a2f4c5ee9af8ba82541cfb3c33b076d1fd05c65f9ce7060c64c400; struct Data { /// @dev The address that receives all platform fees from all sales. address platformFeeRecipient; /// @dev The % of primary sales collected as platform fees. uint16 platformFeeBps; /// @dev Fee type variants: percentage fee and flat fee IPlatformFee.PlatformFeeType platformFeeType; /// @dev The flat amount collected by the contract as fees on primary sales. uint256 flatPlatformFee; } function data() internal pure returns (Data storage data_) { bytes32 position = PLATFORM_FEE_STORAGE_POSITION; assembly { data_.slot := position } } } /** * @author thirdweb.com * * @title Platform Fee * @notice Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading * the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic * that uses information about platform fees, if desired. */ abstract contract PlatformFee is IPlatformFee { /// @dev Returns the platform fee recipient and bps. function getPlatformFeeInfo() public view override returns (address, uint16) { return (_platformFeeStorage().platformFeeRecipient, uint16(_platformFeeStorage().platformFeeBps)); } /// @dev Returns the platform fee bps and recipient. function getFlatPlatformFeeInfo() public view returns (address, uint256) { return (_platformFeeStorage().platformFeeRecipient, _platformFeeStorage().flatPlatformFee); } /// @dev Returns the platform fee type. function getPlatformFeeType() public view returns (PlatformFeeType) { return _platformFeeStorage().platformFeeType; } /** * @notice Updates the platform fee recipient and bps. * @dev Caller should be authorized to set platform fee info. * See {_canSetPlatformFeeInfo}. * Emits {PlatformFeeInfoUpdated Event}; See {_setupPlatformFeeInfo}. * * @param _platformFeeRecipient Address to be set as new platformFeeRecipient. * @param _platformFeeBps Updated platformFeeBps. */ function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external override { if (!_canSetPlatformFeeInfo()) { revert("Not authorized"); } _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); } /// @dev Lets a contract admin update the platform fee recipient and bps function _setupPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) internal { if (_platformFeeBps > 10_000) { revert("Exceeds max bps"); } if (_platformFeeRecipient == address(0)) { revert("Invalid recipient"); } _platformFeeStorage().platformFeeBps = uint16(_platformFeeBps); _platformFeeStorage().platformFeeRecipient = _platformFeeRecipient; emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps); } /// @notice Lets a module admin set a flat fee on primary sales. function setFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee) external { if (!_canSetPlatformFeeInfo()) { revert("Not authorized"); } _setupFlatPlatformFeeInfo(_platformFeeRecipient, _flatFee); } /// @dev Sets a flat fee on primary sales. function _setupFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee) internal { _platformFeeStorage().flatPlatformFee = _flatFee; _platformFeeStorage().platformFeeRecipient = _platformFeeRecipient; emit FlatPlatformFeeUpdated(_platformFeeRecipient, _flatFee); } /// @notice Lets a module admin set platform fee type. function setPlatformFeeType(PlatformFeeType _feeType) external { if (!_canSetPlatformFeeInfo()) { revert("Not authorized"); } _setupPlatformFeeType(_feeType); } /// @dev Sets platform fee type. function _setupPlatformFeeType(PlatformFeeType _feeType) internal { _platformFeeStorage().platformFeeType = _feeType; emit PlatformFeeTypeUpdated(_feeType); } /// @dev Returns the PlatformFee storage. function _platformFeeStorage() internal pure returns (PlatformFeeStorage.Data storage data) { data = PlatformFeeStorage.data(); } /// @dev Returns whether platform fee info can be set in the given execution context. function _canSetPlatformFeeInfo() internal view virtual returns (bool); } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; library ReentrancyGuardStorage { /// @custom:storage-location erc7201:reentrancy.guard.storage /// @dev keccak256(abi.encode(uint256(keccak256("reentrancy.guard.storage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 public constant REENTRANCY_GUARD_STORAGE_POSITION = 0x1d281c488dae143b6ea4122e80c65059929950b9c32f17fc57be22089d9c3b00; struct Data { uint256 _status; } function data() internal pure returns (Data storage data_) { bytes32 position = REENTRANCY_GUARD_STORAGE_POSITION; assembly { data_.slot := position } } } abstract contract ReentrancyGuard { uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; constructor() { _reentrancyGuardStorage()._status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_reentrancyGuardStorage()._status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _reentrancyGuardStorage()._status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _reentrancyGuardStorage()._status = _NOT_ENTERED; } /// @dev Returns the ReentrancyGuard storage. function _reentrancyGuardStorage() internal pure returns (ReentrancyGuardStorage.Data storage data) { data = ReentrancyGuardStorage.data(); } } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb import "../interface/IRoyaltyPayments.sol"; import "../interface/IRoyaltyEngineV1.sol"; import { IERC2981 } from "../../eip/interface/IERC2981.sol"; library RoyaltyPaymentsStorage { /// @custom:storage-location erc7201:royalty.payments.storage /// @dev keccak256(abi.encode(uint256(keccak256("royalty.payments.storage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 public constant ROYALTY_PAYMENTS_STORAGE_POSITION = 0xc802b338f3fb784853cf3c808df5ff08335200e394ea2c687d12571a91045000; struct Data { /// @dev The address of RoyaltyEngineV1, replacing the one set during construction. address royaltyEngineAddressOverride; } function royaltyPaymentsStorage() internal pure returns (Data storage royaltyPaymentsData) { bytes32 position = ROYALTY_PAYMENTS_STORAGE_POSITION; assembly { royaltyPaymentsData.slot := position } } } /** * @author thirdweb.com * * @title Royalty Payments * @notice Thirdweb's `RoyaltyPayments` is a contract extension to be used with a marketplace contract. * It exposes functions for fetching royalty settings for a token. * It Supports RoyaltyEngineV1 and RoyaltyRegistry by manifold.xyz. */ abstract contract RoyaltyPaymentsLogic is IRoyaltyPayments { // solhint-disable-next-line var-name-mixedcase address immutable ROYALTY_ENGINE_ADDRESS; constructor(address _royaltyEngineAddress) { // allow address(0) in case RoyaltyEngineV1 not present on a network require( _royaltyEngineAddress == address(0) || IERC165(_royaltyEngineAddress).supportsInterface(type(IRoyaltyEngineV1).interfaceId), "Doesn't support IRoyaltyEngineV1 interface" ); ROYALTY_ENGINE_ADDRESS = _royaltyEngineAddress; } /** * Get the royalty for a given token (address, id) and value amount. Does not cache the bps/amounts. Caches the spec for a given token address * * @param tokenAddress - The address of the token * @param tokenId - The id of the token * @param value - The value you wish to get the royalty of * * returns Two arrays of equal length, royalty recipients and the corresponding amount each recipient should get */ function getRoyalty( address tokenAddress, uint256 tokenId, uint256 value ) external returns (address payable[] memory recipients, uint256[] memory amounts) { address royaltyEngineAddress = getRoyaltyEngineAddress(); if (royaltyEngineAddress == address(0)) { try IERC2981(tokenAddress).royaltyInfo(tokenId, value) returns (address recipient, uint256 amount) { require(amount <= value, "Invalid royalty amount"); recipients = new address payable[](1); amounts = new uint256[](1); recipients[0] = payable(recipient); amounts[0] = amount; } catch {} } else { (recipients, amounts) = IRoyaltyEngineV1(royaltyEngineAddress).getRoyalty(tokenAddress, tokenId, value); } } /** * Set or override RoyaltyEngine address * * @param _royaltyEngineAddress - RoyaltyEngineV1 address */ function setRoyaltyEngine(address _royaltyEngineAddress) external { if (!_canSetRoyaltyEngine()) { revert("Not authorized"); } require( _royaltyEngineAddress != address(0) && IERC165(_royaltyEngineAddress).supportsInterface(type(IRoyaltyEngineV1).interfaceId), "Doesn't support IRoyaltyEngineV1 interface" ); _setupRoyaltyEngine(_royaltyEngineAddress); } /// @dev Returns original or overridden address for RoyaltyEngineV1 function getRoyaltyEngineAddress() public view returns (address royaltyEngineAddress) { RoyaltyPaymentsStorage.Data storage data = RoyaltyPaymentsStorage.royaltyPaymentsStorage(); address royaltyEngineOverride = data.royaltyEngineAddressOverride; royaltyEngineAddress = royaltyEngineOverride != address(0) ? royaltyEngineOverride : ROYALTY_ENGINE_ADDRESS; } /// @dev Lets a contract admin update the royalty engine address function _setupRoyaltyEngine(address _royaltyEngineAddress) internal { RoyaltyPaymentsStorage.Data storage data = RoyaltyPaymentsStorage.royaltyPaymentsStorage(); address currentAddress = data.royaltyEngineAddressOverride; data.royaltyEngineAddressOverride = _royaltyEngineAddress; emit RoyaltyEngineUpdated(currentAddress, _royaltyEngineAddress); } /// @dev Returns whether royalty engine address can be set in the given execution context. function _canSetRoyaltyEngine() internal view virtual returns (bool); } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; import { ReentrancyGuardStorage } from "../ReentrancyGuard.sol"; import "../Initializable.sol"; contract ReentrancyGuardInit is Initializable { uint256 private constant _NOT_ENTERED = 1; function __ReentrancyGuard_init() internal onlyInitializing { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal onlyInitializing { ReentrancyGuardStorage.Data storage data = ReentrancyGuardStorage.data(); data._status = _NOT_ENTERED; } } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.1; /// @author thirdweb, OpenZeppelin Contracts (v4.9.0) /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{ value: value }(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb /** * @dev String operations. */ library Strings { bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _HEX_SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte, /// and the alphabets are capitalized conditionally according to /// https://eips.ethereum.org/EIPS/eip-55 function toHexStringChecksummed(address value) internal pure returns (string memory str) { str = toHexString(value); /// @solidity memory-safe-assembly assembly { let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...` let o := add(str, 0x22) let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... ` let t := shl(240, 136) // `0b10001000 << 240` for { let i := 0 } 1 { } { mstore(add(i, i), mul(t, byte(i, hashed))) i := add(i, 1) if eq(i, 20) { break } } mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask))))) o := add(o, 0x20) mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask))))) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. function toHexString(address value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(address value) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { str := mload(0x40) // Allocate the memory. // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, // 0x02 bytes for the prefix, and 0x28 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80. mstore(0x40, add(str, 0x80)) // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) str := add(str, 2) mstore(str, 40) let o := add(str, 0x20) mstore(add(o, 40), 0) value := shl(96, value) // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let i := 0 } 1 { } { let p := add(o, add(i, i)) let temp := byte(i, value) mstore8(add(p, 1), mload(and(temp, 15))) mstore8(p, mload(shr(4, temp))) i := add(i, 1) if eq(i, 20) { break } } } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexString(bytes memory raw) internal pure returns (string memory str) { str = toHexStringNoPrefix(raw); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { let length := mload(raw) str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix. mstore(str, add(length, length)) // Store the length of the output. // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let o := add(str, 0x20) let end := add(raw, length) for { } iszero(eq(raw, end)) { } { raw := add(raw, 1) mstore8(add(o, 1), mload(and(mload(raw), 15))) mstore8(o, mload(and(shr(4, mload(raw)), 15))) o := add(o, 2) } mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate the memory. } } } // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb // $$\\ $$\\ $$\\ $$\\ $$\\ // $$ | $$ | \\__| $$ | $$ | // $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$$$$\\ // \\_$$ _| $$ __$$\\ $$ |$$ __$$\\ $$ __$$ |$$ | $$ | $$ |$$ __$$\\ $$ __$$\\ // $$ | $$ | $$ |$$ |$$ | \\__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | // $$ |$$\\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | // \\$$$$ |$$ | $$ |$$ |$$ | \\$$$$$$$ |\\$$$$$\\$$$$ |\\$$$$$$$\\ $$$$$$$ | // \\____/ \\__| \\__|\\__|\\__| \\_______| \\_____\\____/ \\_______|\\_______/ // ====== External imports ====== import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import { ERC721Holder } from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; import { ERC1155Holder, ERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; // ========== Internal imports ========== import { BaseRouter, IRouter, IRouterState } from "@thirdweb-dev/dynamic-contracts/src/presets/BaseRouter.sol"; import { ERC165 } from "../../../eip/ERC165.sol"; import "../../../extension/Multicall.sol"; import "../../../extension/upgradeable/Initializable.sol"; import "../../../extension/upgradeable/ContractMetadata.sol"; import "../../../extension/upgradeable/PlatformFee.sol"; import "../../../extension/upgradeable/PermissionsEnumerable.sol"; import "../../../extension/upgradeable/init/ReentrancyGuardInit.sol"; import "../../../extension/upgradeable/ERC2771ContextUpgradeable.sol"; import { RoyaltyPaymentsLogic } from "../../../extension/upgradeable/RoyaltyPayments.sol"; /** * @author thirdweb.com */ contract MarketplaceV3 is Initializable, Multicall, BaseRouter, ContractMetadata, PlatformFee, PermissionsEnumerable, ReentrancyGuardInit, ERC2771ContextUpgradeable, RoyaltyPaymentsLogic, ERC721Holder, ERC1155Holder, ERC165 { /// @dev Only EXTENSION_ROLE holders can perform upgrades. bytes32 private constant EXTENSION_ROLE = keccak256("EXTENSION_ROLE"); bytes32 private constant MODULE_TYPE = bytes32("MarketplaceV3"); uint256 private constant VERSION = 3; /// @dev The address of the native token wrapper contract. address private immutable nativeTokenWrapper; /*/////////////////////////////////////////////////////////////// Constructor + initializer logic //////////////////////////////////////////////////////////////*/ /// @dev We accept constructor params as a struct to avoid `Stack too deep` errors. struct MarketplaceConstructorParams { Extension[] extensions; address royaltyEngineAddress; address nativeTokenWrapper; } constructor( MarketplaceConstructorParams memory _marketplaceV3Params ) BaseRouter(_marketplaceV3Params.extensions) RoyaltyPaymentsLogic(_marketplaceV3Params.royaltyEngineAddress) { nativeTokenWrapper = _marketplaceV3Params.nativeTokenWrapper; _disableInitializers(); } receive() external payable { assert(msg.sender == nativeTokenWrapper); // only accept ETH via fallback from the native token wrapper contract } /// @dev Initializes the contract, like a constructor. function initialize( address _defaultAdmin, string memory _contractURI, address[] memory _trustedForwarders, address _platformFeeRecipient, uint16 _platformFeeBps ) external initializer { // Initialize BaseRouter __BaseRouter_init(); // Initialize inherited contracts, most base-like -> most derived. __ReentrancyGuard_init(); __ERC2771Context_init(_trustedForwarders); // Initialize this contract's state. _setupContractURI(_contractURI); _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin); _setupRole(EXTENSION_ROLE, _defaultAdmin); _setupRole(keccak256("LISTER_ROLE"), address(0)); _setupRole(keccak256("ASSET_ROLE"), address(0)); _setupRole(EXTENSION_ROLE, _defaultAdmin); _setRoleAdmin(EXTENSION_ROLE, EXTENSION_ROLE); } /*/////////////////////////////////////////////////////////////// Generic contract logic //////////////////////////////////////////////////////////////*/ /// @dev Returns the type of the contract. function contractType() external pure returns (bytes32) { return MODULE_TYPE; } /// @dev Returns the version of the contract. function contractVersion() external pure returns (uint8) { return uint8(VERSION); } /*/////////////////////////////////////////////////////////////// ERC 165 / 721 / 1155 logic //////////////////////////////////////////////////////////////*/ function supportsInterface( bytes4 interfaceId ) public view virtual override(ERC165, IERC165, ERC1155Receiver) returns (bool) { return interfaceId == type(IERC1155Receiver).interfaceId || interfaceId == type(IERC721Receiver).interfaceId || interfaceId == type(IRouter).interfaceId || interfaceId == type(IRouterState).interfaceId || super.supportsInterface(interfaceId); } /*/////////////////////////////////////////////////////////////// Overridable Permissions //////////////////////////////////////////////////////////////*/ /// @dev Checks whether platform fee info can be set in the given execution context. function _canSetPlatformFeeInfo() internal view override returns (bool) { return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); } /// @dev Checks whether contract metadata can be set in the given execution context. function _canSetContractURI() internal view override returns (bool) { return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); } /// @dev Returns whether royalty engine address can be set in the given execution context. function _canSetRoyaltyEngine() internal view override returns (bool) { return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); } /// @dev Checks whether an account has a particular role. function _hasRole(bytes32 _role, address _account) internal view returns (bool) { PermissionsStorage.Data storage data = PermissionsStorage.data(); return data._hasRole[_role][_account]; } /// @dev Returns whether all relevant permission and other checks are met before any upgrade. function _isAuthorizedCallToUpgrade() internal view virtual override returns (bool) { return _hasRole(EXTENSION_ROLE, msg.sender); } function _msgSender() internal view override(ERC2771ContextUpgradeable, Permissions, Multicall) returns (address sender) { return ERC2771ContextUpgradeable._msgSender(); } function _msgData() internal view override(ERC2771ContextUpgradeable, Permissions) returns (bytes calldata) { return ERC2771ContextUpgradeable._msgData(); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./utils/Bytecode.sol"; /** @title A key-value storage with auto-generated keys for storing chunks of data with a lower write & read cost. @author Agustin Aguilar <[email protected]> Readme: https://github.com/0xsequence/sstore2#readme */ library SSTORE2 { error WriteError(); /** @notice Stores `_data` and returns `pointer` as key for later retrieval @dev The pointer is a contract address with `_data` as code @param _data to be written @return pointer Pointer to the written `_data` */ function write(bytes memory _data) internal returns (address pointer) { // Append 00 to _data so contract can't be called // Build init code bytes memory code = Bytecode.creationCodeFor( abi.encodePacked( hex'00', _data ) ); // Deploy contract using create assembly { pointer := create(0, add(code, 32), mload(code)) } // Address MUST be non-zero if (pointer == address(0)) revert WriteError(); } /** @notice Reads the contents of the `_pointer` code as data, skips the first byte @dev The function is intended for reading pointers generated by `write` @param _pointer to be read @return data read from `_pointer` contract */ function read(address _pointer) internal view returns (bytes memory) { return Bytecode.codeAt(_pointer, 1, type(uint256).max); } /** @notice Reads the contents of the `_pointer` code as data, skips the first byte @dev The function is intended for reading pointers generated by `write` @param _pointer to be read @param _start number of bytes to skip @return data read from `_pointer` contract */ function read(address _pointer, uint256 _start) internal view returns (bytes memory) { return Bytecode.codeAt(_pointer, _start + 1, type(uint256).max); } /** @notice Reads the contents of the `_pointer` code as data, skips the first byte @dev The function is intended for reading pointers generated by `write` @param _pointer to be read @param _start number of bytes to skip @param _end index before which to end extraction @return data read from `_pointer` contract */ function read(address _pointer, uint256 _start, uint256 _end) internal view returns (bytes memory) { return Bytecode.codeAt(_pointer, _start + 1, _end + 1); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; library Bytecode { error InvalidCodeAtRange(uint256 _size, uint256 _start, uint256 _end); /** @notice Generate a creation code that results on a contract with `_code` as bytecode @param _code The returning value of the resulting `creationCode` @return creationCode (constructor) for new contract */ function creationCodeFor(bytes memory _code) internal pure returns (bytes memory) { /* 0x00 0x63 0x63XXXXXX PUSH4 _code.length size 0x01 0x80 0x80 DUP1 size size 0x02 0x60 0x600e PUSH1 14 14 size size 0x03 0x60 0x6000 PUSH1 00 0 14 size size 0x04 0x39 0x39 CODECOPY size 0x05 0x60 0x6000 PUSH1 00 0 size 0x06 0xf3 0xf3 RETURN <CODE> */ return abi.encodePacked( hex"63", uint32(_code.length), hex"80_60_0E_60_00_39_60_00_F3", _code ); } /** @notice Returns the size of the code on a given address @param _addr Address that may or may not contain code @return size of the code on the given `_addr` */ function codeSize(address _addr) internal view returns (uint256 size) { assembly { size := extcodesize(_addr) } } /** @notice Returns the code of a given address @dev It will fail if `_end < _start` @param _addr Address that may or may not contain code @param _start number of bytes of code to skip on read @param _end index before which to end extraction @return oCode read from `_addr` deployed bytecode Forked from: https://gist.github.com/KardanovIR/fe98661df9338c842b4a30306d507fbd */ function codeAt(address _addr, uint256 _start, uint256 _end) internal view returns (bytes memory oCode) { uint256 csize = codeSize(_addr); if (csize == 0) return bytes(""); if (_start > csize) return bytes(""); if (_end < _start) revert InvalidCodeAtRange(csize, _start, _end); unchecked { uint256 reqSize = _end - _start; uint256 maxSize = csize - _start; uint256 size = maxSize < reqSize ? maxSize : reqSize; assembly { // allocate output byte array - this could also be done without assembly // by using o_code = new bytes(size) oCode := mload(0x40) // new "memory end" including padding mstore(0x40, add(oCode, and(add(add(size, 0x20), 0x1f), not(0x1f)))) // store length in memory mstore(oCode, size) // actually retrieve the code, this needs assembly extcodecopy(_addr, add(oCode, 0x20), _start, size) } } } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interface/IRouter.sol"; /// @title ERC-7504 Dynamic Contracts: Router. /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Routes an incoming call to an appropriate implementation address. abstract contract Router is IRouter { /** \t *\t@notice delegateCalls the appropriate implementation address for the given incoming function call. \t *\t@dev The implementation address to delegateCall MUST be retrieved from calling `getImplementationForFunction` with the * incoming call's function selector. \t */ fallback() external payable virtual { if(msg.data.length == 0) return; address implementation = getImplementationForFunction(msg.sig); require(implementation != address(0), "Router: function does not exist."); _delegate(implementation); } /// @dev delegateCalls an `implementation` smart contract. function _delegate(address implementation) internal virtual { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize()) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize()) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } /** \t *\t@notice Returns the implementation address to delegateCall for the given function selector. \t *\t@param _functionSelector The function selector to get the implementation address for. \t *\t@return implementation The implementation address to delegateCall for the given function selector. \t */ function getImplementationForFunction(bytes4 _functionSelector) public view virtual returns (address implementation); }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title IExtension /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Provides an `Extension` abstraction for a router's implementation contracts. interface IExtension { /*/////////////////////////////////////////////////////////////// Structs //////////////////////////////////////////////////////////////*/ /** * @notice An interface to describe an extension's metadata. * * @param name The unique name of the extension. * @param metadataURI The URI where the metadata for the extension lives. * @param implementation The implementation smart contract address of the extension. */ struct ExtensionMetadata { string name; string metadataURI; address implementation; } /** * @notice An interface to describe an extension's function. * * @param functionSelector The 4 byte selector of the function. * @param functionSignature Function signature as a string. E.g. "transfer(address,address,uint256)" */ struct ExtensionFunction { bytes4 functionSelector; string functionSignature; } /** * @notice An interface to describe an extension. * * @param metadata The extension's metadata; it's name, metadata URI and implementation contract address. * @param functions The functions that belong to the extension. */ struct Extension { ExtensionMetadata metadata; ExtensionFunction[] functions; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./IExtension.sol"; /// @title IExtensionManager /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Defined storage and API for managing a router's extensions. interface IExtensionManager is IExtension { /*/////////////////////////////////////////////////////////////// Events //////////////////////////////////////////////////////////////*/ /// @dev Emitted when a extension is added. event ExtensionAdded(string indexed name, address indexed implementation, Extension extension); /// @dev Emitted when a extension is replaced. event ExtensionReplaced(string indexed name, address indexed implementation, Extension extension); /// @dev Emitted when a extension is removed. event ExtensionRemoved(string indexed name, Extension extension); /// @dev Emitted when a function is enabled i.e. made callable. event FunctionEnabled(string indexed name, bytes4 indexed functionSelector, ExtensionFunction extFunction, ExtensionMetadata extMetadata); /// @dev Emitted when a function is disabled i.e. made un-callable. event FunctionDisabled(string indexed name, bytes4 indexed functionSelector, ExtensionMetadata extMetadata); /*/////////////////////////////////////////////////////////////// External functions //////////////////////////////////////////////////////////////*/ /** * @notice Add a new extension to the router. * @param extension The extension to add. */ function addExtension(Extension memory extension) external; /** * @notice Fully replace an existing extension of the router. * @dev The extension with name `extension.name` is the extension being replaced. * @param extension The extension to replace or overwrite. */ function replaceExtension(Extension memory extension) external; /** * @notice Remove an existing extension from the router. * @param extensionName The name of the extension to remove. */ function removeExtension(string memory extensionName) external; /** * @notice Enables a single function in an existing extension. * @dev Makes the given function callable on the router. * * @param extensionName The name of the extension to which `extFunction` belongs. * @param extFunction The function to enable. */ function enableFunctionInExtension(string memory extensionName, ExtensionFunction memory extFunction) external; /** * @notice Disables a single function in an Extension. * * @param extensionName The name of the extension to which the function of `functionSelector` belongs. * @param functionSelector The function to disable. */ function disableFunctionInExtension(string memory extensionName, bytes4 functionSelector) external; }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title ERC-7504 Dynamic Contracts: IRouter. /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Routes an incoming call to an appropriate implementation address. /// @dev Fallback function delegateCalls `getImplementationForFunction(msg.sig)` for a given incoming call. /// NOTE: The ERC-165 identifier for this interface is 0xce0b6013. interface IRouter { \t/** \t *\t@notice delegateCalls the appropriate implementation address for the given incoming function call. \t *\t@dev The implementation address to delegateCall MUST be retrieved from calling `getImplementationForFunction` with the * incoming call's function selector. \t */ \tfallback() external payable; \t/*/////////////////////////////////////////////////////////////// View Functions //////////////////////////////////////////////////////////////*/ \t/** \t *\t@notice Returns the implementation address to delegateCall for the given function selector. \t *\t@param _functionSelector The function selector to get the implementation address for. \t *\t@return implementation The implementation address to delegateCall for the given function selector. \t */ function getImplementationForFunction(bytes4 _functionSelector) external view returns (address implementation); }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./IExtension.sol"; /// @title ERC-7504 Dynamic Contracts: IRouterState. /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Defines an API to expose a router's extensions. interface IRouterState is IExtension { /*/////////////////////////////////////////////////////////////// View Functions //////////////////////////////////////////////////////////////*/ /** * @notice Returns all extensions of the Router. * @return allExtensions An array of all extensions. */ function getAllExtensions() external view returns (Extension[] memory allExtensions); }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./IExtension.sol"; /// @title IRouterStateGetters. /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Helper view functions to inspect a router's state. interface IRouterStateGetters is IExtension { /*/////////////////////////////////////////////////////////////// View functions //////////////////////////////////////////////////////////////*/ /** * @notice Returns the extension metadata for a given function. * @param functionSelector The function selector to get the extension metadata for. * @return metadata The extension metadata for a given function. */ function getMetadataForFunction(bytes4 functionSelector) external view returns (ExtensionMetadata memory metadata); /** * @notice Returns the extension metadata and functions for a given extension. * @param extensionName The name of the extension to get the metadata and functions for. * @return extension The extension metadata and functions for a given extension. */ function getExtension(string memory extensionName) external view returns (Extension memory); }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title BaseRouterStorage /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Defined storage for base router library BaseRouterStorage { /// @custom:storage-location erc7201:base.router.storage bytes32 public constant BASE_ROUTER_STORAGE_POSITION = keccak256(abi.encode(uint256(keccak256("base.router.storage")) - 1)); struct Data { /// @dev Mapping used only for checking default extension validity in constructor. mapping(bytes4 => bool) functionMap; /// @dev Mapping used only for checking default extension validity in constructor. mapping(string => bool) extensionMap; } /// @dev Returns access to base router storage. function data() internal pure returns (Data storage data_) { bytes32 position = BASE_ROUTER_STORAGE_POSITION; assembly { data_.slot := position } } }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./StringSet.sol"; import "../interface/IExtension.sol"; /// @title IExtensionManagerStorage /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Defined storage for managing a router's extensions. library ExtensionManagerStorage { /// @custom:storage-location erc7201:extension.manager.storage bytes32 public constant EXTENSION_MANAGER_STORAGE_POSITION = keccak256(abi.encode(uint256(keccak256("extension.manager.storage")) - 1)); struct Data { /// @dev Set of names of all extensions of the router. StringSet.Set extensionNames; /// @dev Mapping from extension name => `Extension` i.e. extension metadata and functions. mapping(string => IExtension.Extension) extensions; /// @dev Mapping from function selector => metadata of the extension the function belongs to. mapping(bytes4 => IExtension.ExtensionMetadata) extensionMetadata; } /// @dev Returns access to the extension manager's storage. function data() internal pure returns (Data storage data_) { bytes32 position = EXTENSION_MANAGER_STORAGE_POSITION; assembly { data_.slot := position } } }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; library StringSet { struct Set { // Storage of set values string[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(string => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, string memory value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, string memory value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { string memory lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, string memory value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (string memory) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (string[] memory) { return set._values; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Set storage set, string memory value) internal returns (bool) { return _add(set, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Set storage set, string memory value) internal returns (bool) { return _remove(set, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Set storage set, string memory value) internal view returns (bool) { return _contains(set, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Set storage set) internal view returns (uint256) { return _length(set); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Set storage set, uint256 index) internal view returns (string memory) { return _at(set, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Set storage set) internal view returns (string[] memory) { return _values(set); } }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { Router, IRouter } from "../core/Router.sol"; import { IRouterState } from "../interface/IRouterState.sol"; import { IRouterStateGetters } from "../interface/IRouterStateGetters.sol"; import { BaseRouterStorage } from "../lib/BaseRouterStorage.sol"; import { ExtensionManager } from "./ExtensionManager.sol"; import { StringSet } from "../lib/StringSet.sol"; import "lib/sstore2/contracts/SSTORE2.sol"; /// @title BaseRouter /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice A router with an API to manage its extensions. abstract contract BaseRouter is Router, ExtensionManager { using StringSet for StringSet.Set; /// @notice The address where the router's default extension set is stored. address public immutable defaultExtensions; /// @notice Initialize the Router with a set of default extensions. constructor(Extension[] memory _extensions) { address pointer; if(_extensions.length > 0) { _validateExtensions(_extensions); pointer = SSTORE2.write(abi.encode(_extensions)); } defaultExtensions = pointer; } /// @notice Initialize the Router with a set of default extensions. function __BaseRouter_init() internal { if(defaultExtensions == address(0)) { return; } bytes memory data = SSTORE2.read(defaultExtensions); Extension[] memory defaults = abi.decode(data, (Extension[])); // Unchecked since we already validated extensions in constructor. __BaseRouter_init_unchecked(defaults); } /// @notice Initializes the Router with a set of extensions. function __BaseRouter_init_checked(Extension[] memory _extensions) internal { _validateExtensions(_extensions); __BaseRouter_init_unchecked(_extensions); } /// @notice Initializes the Router with a set of extensions. function __BaseRouter_init_unchecked(Extension[] memory _extensions) internal { for(uint256 i = 0; i < _extensions.length; i += 1) { Extension memory extension = _extensions[i]; // Store: new extension name. _extensionManagerStorage().extensionNames.add(extension.metadata.name); // 1. Store: metadata for extension. _setMetadataForExtension(extension.metadata.name, extension.metadata); uint256 len = extension.functions.length; for (uint256 j = 0; j < len; j += 1) { // 2. Store: name -> extension.functions map _extensionManagerStorage().extensions[extension.metadata.name].functions.push(extension.functions[j]); // 3. Store: metadata for function. _setMetadataForFunction(extension.functions[j].functionSelector, extension.metadata); } emit ExtensionAdded(extension.metadata.name, extension.metadata.implementation, extension); } } /// @notice Returns the implementation contract address for a given function signature. function getImplementationForFunction(bytes4 _functionSelector) public view virtual override returns (address) { return getMetadataForFunction(_functionSelector).implementation; } /// @dev Validates default extensions. function _validateExtensions(Extension[] memory _extensions) internal { uint256 len = _extensions.length; bool isValid = true; for (uint256 i = 0; i < len; i += 1) { isValid = _isValidExtension(_extensions[i]); if(!isValid) { break; } } require(isValid, "BaseRouter: invalid extension."); } function _isValidExtension(Extension memory _extension) internal returns (bool isValid) { isValid = bytes(_extension.metadata.name).length > 0 // non-empty name && !BaseRouterStorage.data().extensionMap[_extension.metadata.name] // unused name && _extension.metadata.implementation != address(0); // non-empty implementation BaseRouterStorage.data().extensionMap[_extension.metadata.name] = true; if(!isValid) { return false; } uint256 len = _extension.functions.length; for(uint256 i = 0; i < len; i += 1) { if(!isValid) { break; } ExtensionFunction memory _extFunction = _extension.functions[i]; /** * Note: `bytes4(0)` is the function selector for the `receive` function. * So, we maintain a special fn selector-signature mismatch check for the `receive` function. **/ bool mismatch = false; if(_extFunction.functionSelector == bytes4(0)) { mismatch = keccak256(abi.encode(_extFunction.functionSignature)) != keccak256(abi.encode("receive()")); } else { mismatch = _extFunction.functionSelector != bytes4(keccak256(abi.encodePacked(_extFunction.functionSignature))); } // No fn signature-selector mismatch and no duplicate function. isValid = !mismatch && !BaseRouterStorage.data().functionMap[_extFunction.functionSelector]; BaseRouterStorage.data().functionMap[_extFunction.functionSelector] = true; } } }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interface/IExtensionManager.sol"; import "../interface/IRouterState.sol"; import "../interface/IRouterStateGetters.sol"; import "../lib/ExtensionManagerStorage.sol"; /// @title ExtensionManager /// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) /// @notice Defined storage and API for managing a router's extensions. abstract contract ExtensionManager is IExtensionManager, IRouterState, IRouterStateGetters { using StringSet for StringSet.Set; /*/////////////////////////////////////////////////////////////// Modifier //////////////////////////////////////////////////////////////*/ /// @notice Checks that a call to any external function is authorized. modifier onlyAuthorizedCall() { require(_isAuthorizedCallToUpgrade(), "ExtensionManager: unauthorized."); _; } /*/////////////////////////////////////////////////////////////// View functions //////////////////////////////////////////////////////////////*/ /** * @notice Returns all extensions of the Router. * @return allExtensions An array of all extensions. */ function getAllExtensions() external view virtual override returns (Extension[] memory allExtensions) { string[] memory names = _extensionManagerStorage().extensionNames.values(); uint256 len = names.length; allExtensions = new Extension[](len); for (uint256 i = 0; i < len; i += 1) { allExtensions[i] = _getExtension(names[i]); } } /** * @notice Returns the extension metadata for a given function. * @param functionSelector The function selector to get the extension metadata for. * @return metadata The extension metadata for a given function. */ function getMetadataForFunction(bytes4 functionSelector) public view virtual returns (ExtensionMetadata memory) { return _extensionManagerStorage().extensionMetadata[functionSelector]; } /** * @notice Returns the extension metadata and functions for a given extension. * @param extensionName The name of the extension to get the metadata and functions for. * @return extension The extension metadata and functions for a given extension. */ function getExtension(string memory extensionName) public view virtual returns (Extension memory) { return _getExtension(extensionName); } /*/////////////////////////////////////////////////////////////// External functions //////////////////////////////////////////////////////////////*/ /** * @notice Add a new extension to the router. * @param _extension The extension to add. */ function addExtension(Extension memory _extension) public virtual onlyAuthorizedCall { _addExtension(_extension); } /** * @notice Fully replace an existing extension of the router. * @dev The extension with name `extension.name` is the extension being replaced. * @param _extension The extension to replace or overwrite. */ function replaceExtension(Extension memory _extension) public virtual onlyAuthorizedCall { _replaceExtension(_extension); } /** * @notice Remove an existing extension from the router. * @param _extensionName The name of the extension to remove. */ function removeExtension(string memory _extensionName) public virtual onlyAuthorizedCall { _removeExtension(_extensionName); } /** * @notice Enables a single function in an existing extension. * @dev Makes the given function callable on the router. * * @param _extensionName The name of the extension to which `extFunction` belongs. * @param _function The function to enable. */ function enableFunctionInExtension(string memory _extensionName, ExtensionFunction memory _function) public virtual onlyAuthorizedCall { _enableFunctionInExtension(_extensionName, _function); } /** * @notice Disables a single function in an Extension. * * @param _extensionName The name of the extension to which the function of `functionSelector` belongs. * @param _functionSelector The function to disable. */ function disableFunctionInExtension(string memory _extensionName, bytes4 _functionSelector) public virtual onlyAuthorizedCall { _disableFunctionInExtension(_extensionName, _functionSelector); } /*/////////////////////////////////////////////////////////////// Internal functions //////////////////////////////////////////////////////////////*/ /// @dev Add a new extension to the router. function _addExtension(Extension memory _extension) internal virtual { // Check: extension namespace must not already exist. // Check: provided extension namespace must not be empty. // Check: provided extension implementation must be non-zero. // Store: new extension name. require(_canAddExtension(_extension), "ExtensionManager: cannot add extension."); // 1. Store: metadata for extension. _setMetadataForExtension(_extension.metadata.name, _extension.metadata); uint256 len = _extension.functions.length; for (uint256 i = 0; i < len; i += 1) { // 2. Store: function for extension. _addToFunctionMap(_extension.metadata.name, _extension.functions[i]); // 3. Store: metadata for function. _setMetadataForFunction(_extension.functions[i].functionSelector, _extension.metadata); } emit ExtensionAdded(_extension.metadata.name, _extension.metadata.implementation, _extension); } /// @dev Fully replace an existing extension of the router. function _replaceExtension(Extension memory _extension) internal virtual { // Check: extension namespace must already exist. // Check: provided extension implementation must be non-zero. require(_canReplaceExtension(_extension), "ExtensionManager: cannot replace extension."); // 1. Store: metadata for extension. _setMetadataForExtension(_extension.metadata.name, _extension.metadata); // 2. Delete: existing extension.functions and metadata for each function. _removeAllFunctionsFromExtension(_extension.metadata.name); uint256 len = _extension.functions.length; for (uint256 i = 0; i < len; i += 1) { // 2. Store: function for extension. _addToFunctionMap(_extension.metadata.name, _extension.functions[i]); // 3. Store: metadata for function. _setMetadataForFunction(_extension.functions[i].functionSelector, _extension.metadata); } emit ExtensionReplaced(_extension.metadata.name, _extension.metadata.implementation, _extension); } /// @dev Remove an existing extension from the router. function _removeExtension(string memory _extensionName) internal virtual { // Check: extension namespace must already exist. // Delete: extension namespace. require(_canRemoveExtension(_extensionName), "ExtensionManager: cannot remove extension."); Extension memory extension = _extensionManagerStorage().extensions[_extensionName]; // 1. Delete: metadata for extension. _deleteMetadataForExtension(_extensionName); // 2. Delete: existing extension.functions and metadata for each function. _removeAllFunctionsFromExtension(_extensionName); emit ExtensionRemoved(_extensionName, extension); } /// @dev Makes the given function callable on the router. function _enableFunctionInExtension(string memory _extensionName, ExtensionFunction memory _function) internal virtual { // Check: extension namespace must already exist. require(_canEnableFunctionInExtension(_extensionName, _function), "ExtensionManager: cannot Store: function for extension."); // 1. Store: function for extension. _addToFunctionMap(_extensionName, _function); ExtensionMetadata memory metadata = _extensionManagerStorage().extensions[_extensionName].metadata; // 2. Store: metadata for function. _setMetadataForFunction(_function.functionSelector, metadata); emit FunctionEnabled(_extensionName, _function.functionSelector, _function, metadata); } /// @dev Disables a single function in an Extension. function _disableFunctionInExtension(string memory _extensionName, bytes4 _functionSelector) public virtual onlyAuthorizedCall { // Check: extension namespace must already exist. // Check: function must be mapped to provided extension. require(_canDisableFunctionInExtension(_extensionName, _functionSelector), "ExtensionManager: cannot remove function from extension."); ExtensionMetadata memory extMetadata = _extensionManagerStorage().extensionMetadata[_functionSelector]; // 1. Delete: function from extension. _deleteFromFunctionMap(_extensionName, _functionSelector); // 2. Delete: metadata for function. _deleteMetadataForFunction(_functionSelector); emit FunctionDisabled(_extensionName, _functionSelector, extMetadata); } /// @dev Returns the Extension for a given name. function _getExtension(string memory _extensionName) internal view returns (Extension memory) { return _extensionManagerStorage().extensions[_extensionName]; } /// @dev Sets the ExtensionMetadata for a given extension. function _setMetadataForExtension(string memory _extensionName, ExtensionMetadata memory _metadata) internal { _extensionManagerStorage().extensions[_extensionName].metadata = _metadata; } /// @dev Deletes the ExtensionMetadata for a given extension. function _deleteMetadataForExtension(string memory _extensionName) internal { delete _extensionManagerStorage().extensions[_extensionName].metadata; } /// @dev Sets the ExtensionMetadata for a given function. function _setMetadataForFunction(bytes4 _functionSelector, ExtensionMetadata memory _metadata) internal { _extensionManagerStorage().extensionMetadata[_functionSelector] = _metadata; } /// @dev Deletes the ExtensionMetadata for a given function. function _deleteMetadataForFunction(bytes4 _functionSelector) internal { delete _extensionManagerStorage().extensionMetadata[_functionSelector]; } /// @dev Adds a function to the function map of an extension. function _addToFunctionMap(string memory _extensionName, ExtensionFunction memory _extFunction) internal virtual { /** * Note: `bytes4(0)` is the function selector for the `receive` function. * So, we maintain a special fn selector-signature mismatch check for the `receive` function. **/ bool mismatch = false; if(_extFunction.functionSelector == bytes4(0)) { mismatch = keccak256(abi.encode(_extFunction.functionSignature)) != keccak256(abi.encode("receive()")); } else { mismatch = _extFunction.functionSelector != bytes4(keccak256(abi.encodePacked(_extFunction.functionSignature))); } // Check: function selector and signature must match. require( !mismatch, "ExtensionManager: fn selector and signature mismatch." ); // Check: function must not already be mapped to an implementation. require( _extensionManagerStorage().extensionMetadata[_extFunction.functionSelector].implementation == address(0), "ExtensionManager: function impl already exists." ); // Store: name -> extension.functions map _extensionManagerStorage().extensions[_extensionName].functions.push(_extFunction); } /// @dev Deletes a function from an extension's function map. function _deleteFromFunctionMap(string memory _extensionName, bytes4 _functionSelector) internal { ExtensionFunction[] memory extensionFunctions = _extensionManagerStorage().extensions[_extensionName].functions; uint256 len = extensionFunctions.length; for (uint256 i = 0; i < len; i += 1) { if(extensionFunctions[i].functionSelector == _functionSelector) { // Delete: particular function from name -> extension.functions map _extensionManagerStorage().extensions[_extensionName].functions[i] = _extensionManagerStorage().extensions[_extensionName].functions[len - 1]; _extensionManagerStorage().extensions[_extensionName].functions.pop(); break; } } } /// @dev Removes all functions from an Extension. function _removeAllFunctionsFromExtension(string memory _extensionName) internal { ExtensionFunction[] memory functions = _extensionManagerStorage().extensions[_extensionName].functions; // Delete: existing name -> extension.functions map delete _extensionManagerStorage().extensions[_extensionName].functions; for(uint256 i = 0; i < functions.length; i += 1) { // Delete: metadata for function. _deleteMetadataForFunction(functions[i].functionSelector); } } /// @dev Returns whether a new extension can be added in the given execution context. function _canAddExtension(Extension memory _extension) internal virtual returns (bool) { // Check: provided extension namespace must not be empty. require(bytes(_extension.metadata.name).length > 0, "ExtensionManager: empty name."); // Check: extension namespace must not already exist. // Store: new extension name. require(_extensionManagerStorage().extensionNames.add(_extension.metadata.name), "ExtensionManager: extension already exists."); // Check: extension implementation must be non-zero. require(_extension.metadata.implementation != address(0), "ExtensionManager: adding extension without implementation."); return true; } /// @dev Returns whether an extension can be replaced in the given execution context. function _canReplaceExtension(Extension memory _extension) internal virtual returns (bool) { // Check: extension namespace must already exist. require(_extensionManagerStorage().extensionNames.contains(_extension.metadata.name), "ExtensionManager: extension does not exist."); // Check: extension implementation must be non-zero. require(_extension.metadata.implementation != address(0), "ExtensionManager: adding extension without implementation."); return true; } /// @dev Returns whether an extension can be removed in the given execution context. function _canRemoveExtension(string memory _extensionName) internal virtual returns (bool) { // Check: extension namespace must already exist. // Delete: extension namespace. require(_extensionManagerStorage().extensionNames.remove(_extensionName), "ExtensionManager: extension does not exist."); return true; } /// @dev Returns whether a function can be enabled in an extension in the given execution context. function _canEnableFunctionInExtension(string memory _extensionName, ExtensionFunction memory) internal view virtual returns (bool) { // Check: extension namespace must already exist. require(_extensionManagerStorage().extensionNames.contains(_extensionName), "ExtensionManager: extension does not exist."); return true; } /// @dev Returns whether a function can be disabled in an extension in the given execution context. function _canDisableFunctionInExtension(string memory _extensionName, bytes4 _functionSelector) internal view virtual returns (bool) { // Check: extension namespace must already exist. require(_extensionManagerStorage().extensionNames.contains(_extensionName), "ExtensionManager: extension does not exist."); // Check: function must be mapped to provided extension. require(keccak256(abi.encode(_extensionManagerStorage().extensionMetadata[_functionSelector].name)) == keccak256(abi.encode(_extensionName)), "ExtensionManager: incorrect extension."); return true; } /// @dev Returns the ExtensionManager storage. function _extensionManagerStorage() internal pure returns (ExtensionManagerStorage.Data storage data) { data = ExtensionManagerStorage.data(); } /// @dev To override; returns whether all relevant permission and other checks are met before any upgrade. function _isAuthorizedCallToUpgrade() internal view virtual returns (bool); }// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev _Available since v3.1._ */ interface IERC1155Receiver is IERC165 { /** * @dev Handles the receipt of a single ERC1155 token type. This function is * called at the end of a `safeTransferFrom` after the balance has been updated. * * NOTE: To accept the transfer, this must return * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` * (i.e. 0xf23a6e61, or its own function selector). * * @param operator The address which initiated the transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param id The ID of the token being transferred * @param value The amount of tokens being transferred * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed */ function onERC1155Received( address operator, address from, uint256 id, uint256 value, bytes calldata data ) external returns (bytes4); /** * @dev Handles the receipt of a multiple ERC1155 token types. This function * is called at the end of a `safeBatchTransferFrom` after the balances have * been updated. * * NOTE: To accept the transfer(s), this must return * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` * (i.e. 0xbc197c81, or its own function selector). * * @param operator The address which initiated the batch transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param ids An array containing ids of each token being transferred (order and length must match values array) * @param values An array containing amounts of each token being transferred (order and length must match ids array) * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed */ function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external returns (bytes4); } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/utils/ERC1155Holder.sol) pragma solidity ^0.8.0; import "./ERC1155Receiver.sol"; /** * Simple implementation of `ERC1155Receiver` that will allow a contract to hold ERC1155 tokens. * * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be * stuck. * * @dev _Available since v3.1._ */ contract ERC1155Holder is ERC1155Receiver { function onERC1155Received( address, address, uint256, uint256, bytes memory ) public virtual override returns (bytes4) { return this.onERC1155Received.selector; } function onERC1155BatchReceived( address, address, uint256[] memory, uint256[] memory, bytes memory ) public virtual override returns (bytes4) { return this.onERC1155BatchReceived.selector; } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol) pragma solidity ^0.8.0; import "../IERC1155Receiver.sol"; import "../../../utils/introspection/ERC165.sol"; /** * @dev _Available since v3.1._ */ abstract contract ERC1155Receiver is ERC165, IERC1155Receiver { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId); } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol) pragma solidity ^0.8.0; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. * * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/utils/ERC721Holder.sol) pragma solidity ^0.8.0; import "../IERC721Receiver.sol"; /** * @dev Implementation of the {IERC721Receiver} interface. * * Accepts all token transfers. * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}. */ contract ERC721Holder is IERC721Receiver { /** * @dev See {IERC721Receiver-onERC721Received}. * * Always returns `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) { return this.onERC721Received.selector; } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }