Next iteration of thirdweb smart contracts. Install hooks in core contracts.
📣 Call for feedback: These contracts are NOT AUDITED. This design update is WIP and we encourage opening an issue with feedback.
Clone the repo:
git clone https://github.com/thirdweb-dev/contracts-next.git
Install dependencies:
If you are in the root directory of the project, run:
# Install dependecies for core contracts
forge install --root ./core
# Install dependecies for hooks contracts
forge install --root ./hooks
If you are in /core
:
# Install dependecies for core contracts
forge install
If you are in /hooks
:
# Install dependecies for hooks contracts
forge install
From within /contracts
, run benchmark comparison tests:
# create a wallet for the benchmark (make sure there's enough gas funds)
cast wallet import testnet -i
# deploy the benchmark contracts and perform the tests
forge script script/benchmark-ext/erc721/BenchmarkERC721.s.sol --rpc-url "https://sepolia.rpc.thirdweb.com" --account testnet [--broadcast]
From within /contracts
, run gas snapshot:
forge snapshot --isolate --mp 'test/benchmark/*'
You can find testnet deployments of this hooks design setup, and JS scripts to interact with an ERC-721 core contract and its hooks here: https://github.com/thirdweb-dev/contracts-next-scripts
Action | Gas consumption | Transaction |
---|---|---|
Mint 1 token (token ID 0 ) |
145_373 | ref |
Mint 1 token (token ID >0 ) |
116_173 | ref |
Mint 10 tokens (including token ID 0 ) |
365_414 | ref |
Mint 10 tokens (not including token ID 0 ) |
331_214 | ref |
Transfer token | 64_389 | ref |
Install 1 hook | 105_455 | ref |
Install 5 hooks | 191_918 | ref |
Uninstall 1 hook | 43_468 | ref |
Uninstall 5 hooks | 57_839 | ref |
Note:
- 'Minting tokens' benchmarks use the
AllowlistMintHook
contract as thebeforeMint
hook. All token minting benchmarks include distributing non-zero primary sale value and platform fee. - All hooks used in these benchmarks are minimal clone proxy contracts pointing to hook contract implementations.
Action | Thirdweb (Hooks) | Thirdweb Drop | Zora | Manifold |
---|---|---|---|---|
Deploy (developer-facing) | 213_434 tx | 719_842 tx | 499_968 tx | 232_917 tx |
Claim 1 token | 149_142 tx | 196_540 tx | 160_447 tx | 184_006 tx |
Transfer token | 59_587 tx | 76_102 tx | 71_362 tx | 69_042 tx |
Setup token metadata | 60_217 tx | 47_528 tx | 54_612 tx | 29_789 tx |
The Hooks architecture contains two types of smart contracts:
-
Core contract
A minimal, non-upgradeable smart contract that contains functions and state/storage that are core to the contract’s identity.
-
Hook contract
A stateful, upgradeable or non-upgradeable smart contract meant to be installed into a core contract. It contains fixed number of pre-defined hook functions that are called during the execution of a core contract’s functions.
The goal of this Hooks architecture is making upgradeability safer, more predictable and less likely to go wrong by giving developers:
- An API that contains a comprehensive, but fixed number of pre-defined points in a smart contract where developers can introduce customizations.
- Separation of state and logic that’s core to their contract, from state and logic that is ancillary i.e. providing assistance in updating the core state of the contract. (Core vs. Hook)
Core contracts are always non-upgradeable. However, considering a core contract and its hooks together as one system — the Hooks architecture allows for 3 different kinds of upgradeability setups:
- Non-upgradeable: Developer contracts are not upgradeable.
- Self-managed upgradeable: Only the developer is authorized to upgrade their (hook) contracts.
- Managed upgradeable: Developer authorizes a third party to upgrade their (hook) contracts.
We write one core contract implementation for each token type (ERC-20, ERC-721, ERC-1155). Developers deploy a token core contract for themselves and install hooks into it.
All 3 token core contracts implement:
- The token standard itself. (ERC-20 + EIP-2612 Permit / ERC-721 / ERC-1155).
- HookInstaller interface for installing hooks.
- A token standard specific
getAllHooks()
view function interface (e.g. IERC20HookInstaller) - EIP-173 Contract ownership standard
- EIP-7572 Contract-level metadata via
contractURI()
standard - Multicall interface
- External mint() and burn() functions.
The token core contracts use the solady implementations of the token standards, ownable and multicall contracts.
Hook Functions | ERC-20 | ERC-721 | ERC-1155 |
---|---|---|---|
beforeApprove (Approve) | ✅ called before a token is approved in the ERC20Core.approve call. |
✅ called before a token is approved in the ERC721Core.approve and ERC721.setApprovalForAll calls. |
✅ called before a token is approved in the ERC1155.setApprovalForAll call. |
beforeBurn (Burn) | ✅ called before a token is burned in the ERC20.burn call. |
✅ called before a token is burned in the ERC721.burn call. |
✅ called before a token is burned in the ERC1155.burn call. |
beforeMint (Minting) | ✅ called before a token is minted in the ERC20Core.mint |
✅ called before a token is minted in the ERC721Core.mint |
✅ called before a token is minted in the ERC1155Core.mint |
beforeTransfer (Transfer) | ✅ called before a token is transferred in the ERC20.transferFrom call. |
✅ called before a token is transferred in the ERC721.transferFrom and ERC721.safeTransferFrom calls. |
✅ called before a token is transferred in the ERC1155.transferFrom and ERC1155.safeTransferFrom calls. |
beforeBatchTransfer (Batch Transfer) | ❌ | ❌ | ✅ called once before a batch transfer of tokens in the ERC1155.safeBatchTransferredFrom call. |
onRoyaltyInfo (EIP-2981 Royalty) | ❌ | ✅ Called to retrieve royalty info on EIP2981.royaltyInfo call |
✅ Called to retrieve royalty info on EIP2981.royaltyInfo call |
onTokenURI (Token Metadata) | ❌ | ✅ Called to retrieve the metadata for a token on an ERC721Metadata.tokenURI call |
✅ Called to retrieve the metadata URI for a token on a ERC1155Metadata.uri call |
If you have any feedback, please create an issue or reach out to us at support@thirdweb.com.