Skip to content

Commit 3989220

Browse files
jakeloonkrishang
andauthored
ModularCoreUpgradeable (#86)
* Update dependencies * core benchmark * Simplifies core and update coding style * Benchmark core on function fallback overhead * SupportedCallbackFunctions, simplifies installExtension * Fix merge conflicts * Update deps * Updated extension contracts in /extension (#91) * .vscode * replace /hooks with /extension * rename files * reorg files * Simplify and make uniform: LazyMint.sol -> BatchMetadata.sol * Comments + simplify language --------- Co-authored-by: Jake Loo <[email protected]> * rename order, mode. support required mode * update deps * Remove CallbackOrder. Implement callback returns data * Optimize installExtension struct packing * Remove unused field in struct. Memory safe assembly * Rename core, extension * Update solady * Move directory * includes ERC165 interfaceId * Implement ERC165 on core and extension check * Init ExtensionProxy * Sender in onInstall/Uninstall. ExtensionProxy * registerInstallationCallback on Extension * Latest updates to ModularCore (#93) * Rename IModular -> IExtensionConfig * Rename IModularExtensionCallback -> IInstallationCallback * ModularCore inherits OwnableRoles; replace ExtensionFunction permissioned -> permissionBits * Move InstalledExtensionFunction to ModularContract implementation * rename extensionABI -> extensionFunctions * remove unused isAuthorizedToCallExtensionFunctions * Replace unused _isAuthorized checks with native roles * only deploy extension proxy if undeployed * calculate salt based on address(this) instead of msg.sender * re-arrange imports * Only use EnumerableSetLib for EnumerableSetLib.AddressSet * Cleanup styles and add comments to ModularCore * add comments to _installExtension * Only install extension with all supported callback functions * Delete ExtensionProxy and its usage * add ExtensionProxyFactory * Update token contracts getSupportedCallbackFunctions selectors * Add ModularCoreTest * Verify extension bytecodehash on every call to extension * Cleanup ModularCore errors * Fix initializeOwner * Remove bytecode check in fallback * Add Extension name and version * ModularCore is naive implementation; ModularCoreUpgradeable handles upgradeability * Rename extensionName -> extensionID * Full ModularCoreUpgradeable implementation * Remove unused error * ModularCoreUpgradeable: add test and fix bugs * Update interface comments * Rename extensionFunctions to fallbackFunctions * rename extensionFunction to fallbackFunction cont. * Create and use CallbackFunction type which includes CallType * Add top level readme * Add readme assets * Update sub header * Update headings * Add design doc inside /core readme * Add installation and usage instructions * Add comments to IExtensionCOnfig * Add comments to IInstallationCallback * Update interface comments * Acceot factory address in constructor * Delete onRoyaltyInfoCallback * Remove onRoyalty callback from token Cores * Update extensionProxySaltSeed generation * fix github actions * fix github test aciton + forge fmt in /extension --------- Co-authored-by: nkrishang <[email protected]> Co-authored-by: Krishang <[email protected]>
1 parent 353e4ed commit 3989220

File tree

96 files changed

+5938
-7996
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+5938
-7996
lines changed

.github/workflows/formatter.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,5 @@ jobs:
3535

3636
- name: Formatter
3737
run: |
38-
cd core && forge fmt --check test src script && cd ../hooks && forge fmt --check test src script
38+
cd core && forge fmt --check test src script && cd ../extension && forge fmt --check test src script
3939
id: formatter

.github/workflows/test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,5 @@ jobs:
4141

4242
- name: Run Forge tests
4343
run: |
44-
cd core && forge test -vvv && cd ../hooks && forge test -vvv
44+
cd core && forge test -vvv
4545
id: test

.gitmodules

+7-13
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,12 @@
77
[submodule "core/lib/erc721a"]
88
path = core/lib/erc721a
99
url = https://github.com/chiru-labs/erc721a
10-
[submodule "hooks/lib/forge-std"]
11-
path = hooks/lib/forge-std
10+
[submodule "extension/lib/forge-std"]
11+
path = extension/lib/forge-std
1212
url = https://github.com/foundry-rs/forge-std
13-
[submodule "hooks/lib/contracts-next"]
14-
path = hooks/lib/contracts-next
15-
url = https://github.com/thirdweb-dev/contracts-next
16-
[submodule "hooks/lib/solady"]
17-
path = hooks/lib/solady
13+
[submodule "extension/lib/modular-contracts"]
14+
path = extension/lib/modular-contracts
15+
url = https://github.com/thirdweb-dev/modular-contracts
16+
[submodule "extension/lib/solady"]
17+
path = extension/lib/solady
1818
url = https://github.com/vectorized/solady
19-
[submodule "hooks/lib/erc721a"]
20-
path = hooks/lib/erc721a
21-
url = https://github.com/chiru-labs/erc721a
22-
[submodule "hooks/lib/murky"]
23-
path = hooks/lib/murky
24-
url = https://github.com/dmfxyz/murky

.vscode/settings.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"solidity.compileUsingRemoteVersion": "v0.8.23+commit.f704f362"
3+
}

README.md

+47-111
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,70 @@
33
<a href="https://thirdweb.com"><img src="https://github.com/thirdweb-dev/typescript-sdk/blob/main/logo.svg?raw=true" width="200" alt=""/></a>
44
<br />
55
</p>
6-
<h1 align="center">thirdweb Contracts-Next</h1>
7-
<p align="center"><strong>Next iteration of thirdweb smart contracts. Install hooks in core contracts.</strong></p>
6+
<h1 align="center">Modular Contracts</h1>
7+
<p align="center"><strong>Write smart contracts for which you can add, remove, upgrade or switch out the exact parts you want.</strong></p>
88
<br />
99

10-
> :mega: **Call for feedback**: These contracts are NOT AUDITED. This design update is WIP and we encourage opening an issue with feedback.
10+
A Modular Contract is built of two kinds of parts: a _Modular Core_ and its _Modular Extensions_.
1111

12-
# Run this repo
12+
![modular-contracts-analogy](./assets/readme-hero-image.png)
1313

14-
Clone the repo:
14+
A developer writes a **_Core_** smart contract as the foundation that can be customized by adding new parts and updating or removing these parts over time. These ‘parts’ are **_Extension_** smart contracts which any third-party developer can independently develop with reference to the **_Core_** smart contract as the known foundation to build around.
15+
16+
Both `/core` and `/extension` directories are their own forge projects where we develop the Core API of the architecture independently from the Extensions that use this Core API as a dependency.
17+
18+
# Install and Use
19+
20+
This project can currently be installed as a dependency in [foundry](https://book.getfoundry.sh/) projects. To install, run:
1521

1622
```bash
17-
git clone https://github.com/thirdweb-dev/contracts-next.git
23+
forge install https://github.com/thirdweb-dev/modular-contracts
1824
```
1925

20-
Install dependencies:
26+
Add the following in a `remappings.txt` file:
2127

22-
If you are in the root directory of the project, run:
28+
```
29+
@core-contracts/=lib/modular-contracts/core/src/
30+
```
2331

24-
```bash
25-
# Install dependecies for core contracts
26-
forge install --root ./core
32+
Import `ModularCore` inherit to build a Modular Core contract (e.g. ERC-721 Core):
33+
34+
```solidity
35+
import {ModularCore} from "@core-contracts/ModularCore.sol";
36+
import {ERC721A} from "@erc721a/extensions/ERC721AQueryable.sol";
37+
38+
contract ModularNFTCollection is ERC721A, ModularCore {}
39+
```
40+
41+
Import `ModularExtension` to create an Extension for your Core contract (e.g. `Soulbound`):
2742

28-
# Install dependecies for hooks contracts
29-
forge install --root ./hooks
43+
```solidity
44+
import {ModularExtension} from "@core-contracts/ModularExtension.sol";
45+
46+
contract SoulboundERC721 is ModularExtension {}
3047
```
3148

32-
If you are in `/core`:
49+
# Run this repo
50+
51+
Clone the repo:
3352

3453
```bash
35-
# Install dependecies for core contracts
36-
forge install
54+
git clone https://github.com/thirdweb-dev/modular-contracts.git
3755
```
3856

39-
If you are in `/hooks`:
57+
Install dependencies:
58+
59+
If you are in the root directory of the project, run:
4060

4161
```bash
42-
# Install dependecies for hooks contracts
43-
forge install
62+
# Install dependecies for core contracts
63+
forge install --root ./core
64+
65+
# Install dependecies for extension contracts
66+
forge install --root ./extension
4467
```
4568

46-
From within `/contracts`, run benchmark comparison tests:
69+
<!-- From within `/contracts`, run benchmark comparison tests:
4770
4871
```bash
4972
# create a wallet for the benchmark (make sure there's enough gas funds)
@@ -57,103 +80,16 @@ From within `/contracts`, run gas snapshot:
5780
5881
```bash
5982
forge snapshot --isolate --mp 'test/benchmark/*'
60-
```
61-
62-
## Usage
63-
64-
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
65-
66-
# Benchmarks
67-
68-
### ERC-721 Core Benchmarks via transactions on Goerli
69-
70-
| Action | Gas consumption | Transaction |
71-
| ------------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------- |
72-
| Mint 1 token (token ID `0`) | 145_373 | [ref](https://goerli.etherscan.io/tx/0x1de1431200f6d39e9f4ddba3386e413078308a6eae1ebcc722884443b643d7d0) |
73-
| Mint 1 token (token ID `>0`) | 116_173 | [ref](https://goerli.etherscan.io/tx/0xc38e82228a1f8cf877abfeeb28e3f294bb38b90f51cbb2df1c899f03fad4e355) |
74-
| Mint 10 tokens (including token ID `0`) | 365_414 | [ref](https://goerli.etherscan.io/tx/0x1e8a79bd1806a3410a46f8d0ec0fcff099e3aeff6d4e64815c1f400ab092c77e) |
75-
| Mint 10 tokens (not including token ID `0`) | 331_214 | [ref](https://goerli.etherscan.io/tx/0xe4ab2650f8827d52d2ec15956da910915b2b08f67d3f59ac8091da2fbd0369a0) |
76-
| Transfer token | 64_389 | [ref](https://goerli.etherscan.io/tx/0x3ca2c4c74d6c8a4859fd78af5091c4dc4dc0fc0452202b18b611e4f0308c3673) |
77-
| Install 1 hook | 105_455 | [ref](https://goerli.etherscan.io/tx/0x8df68fefe6f0318220795f4c56aec81fdafea2a3d17da2d45a0a762aac6cf6d0) |
78-
| Install 5 hooks | 191_918 | [ref](https://goerli.etherscan.io/tx/0x184f59ce6f83a6927e2269879bdec9ccd29f8ed3fd98be9d4d359e34cfde4ce5) |
79-
| Uninstall 1 hook | 43_468 | [ref](https://goerli.etherscan.io/tx/0x30c678277603c80b1f412049b13ba6742712c64ef9973b00d8866169589ad40f) |
80-
| Uninstall 5 hooks | 57_839 | [ref](https://goerli.etherscan.io/tx/0xf1869d1b6fdc0f7e340cd30df2f0b57408cf0d752e4898ef14836a7672877050) |
81-
82-
**Note:**
83-
84-
- 'Minting tokens' benchmarks use the `AllowlistMintHook` contract as the `beforeMint` hook. All token minting benchmarks include distributing non-zero primary sale value and platform fee.
85-
- All hooks used in these benchmarks are minimal clone proxy contracts pointing to hook contract implementations.
86-
87-
### ERC-721 Contracts Benchmarks Comparison via transactions on Sepolia
88-
89-
| Action | Thirdweb (Hooks) | Thirdweb Drop | Zora | Manifold |
90-
| ------------------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
91-
| Deploy (developer-facing) | 213_434 [tx](https://sepolia.etherscan.io/tx/0x4899fd74e09b4994162f0ce4ea8783a93712825cb20373612263cbfcf83137dc) | 719_842 [tx](https://sepolia.etherscan.io/tx/0x69bf0d597b4db864d50b1c592451156e23a0eca598fd337f0888f3f0d45eda85) | 499_968 [tx](https://sepolia.etherscan.io/tx/0x1d3e653ab587f3203abdcb233ca3502f5a99b2ba38c62b9097b4d9cecac39016) | 232_917 [tx](https://sepolia.etherscan.io/tx/0x80fcff853e07bb99475e0258c315eaf4f91f6e954f9edc170e05a833568401d3) |
92-
| Claim 1 token | 149_142 [tx](https://sepolia.etherscan.io/tx/0xce9155496a5e1705fd91e25fa5c4b6a2664ee8951010e0d236b8f1bc1bebb2a9) | 196_540 [tx](https://sepolia.etherscan.io/tx/0xd2d5eaa191ebe342f34a2647a253782795d93cecc90ae0e3de4bb6a6afe29d0a) | 160_447 [tx](https://sepolia.etherscan.io/tx/0xe1b431c28f0cc8d03489ea1a54fb6b70eea4dc0210cbf24f9cf6b22c3a68f99d) | 184_006 [tx](https://sepolia.etherscan.io/tx/0x149ac1f2aec78753f73a7b16bdea9dd81dc5823893a128422499ba49af3b07f4) |
93-
| Transfer token | 59_587 [tx](https://sepolia.etherscan.io/tx/0x252311fc0ada4873d8fcc036ec7145b1dfb76bef7d974aae15111623cf4e889b) | 76_102 [tx](https://sepolia.etherscan.io/tx/0xd5dd3f3f33e6755fd07617795a382e7e34005771a6374dacbfb686c6b4d981cc) | 71_362 [tx](https://sepolia.etherscan.io/tx/0x4f3d79e1b5582ebab5ff6e1f343d640b2e47ded458223de4e1cf248f1c72776d) | 69_042 [tx](https://sepolia.etherscan.io/tx/0xb3eb80ea9cf88d3a731490f6c9fd8c3f9d8b8283e5b376e4aaf001fb46e84ae2) |
94-
| Setup token metadata | 60_217 [tx](https://sepolia.etherscan.io/tx/0xfe99caf84e543f1b73055da7ef23f8071d3b8d893c838c12b9b3567f0a4cdcb8) | 47_528 [tx](https://sepolia.etherscan.io/tx/0x6526f514b47d60785eb3951046dc1e30b175d2aa74aa2693932ea68ed2054d4d) | 54_612 [tx](https://sepolia.etherscan.io/tx/0xd959c6dfc531994bca58df41c30952a04a6f1ab7e86b3ab3457ed4f4d5bd1846) | 29_789 [tx](https://sepolia.etherscan.io/tx/0x1d3e653ab587f3203abdcb233ca3502f5a99b2ba38c62b9097b4d9cecac39016) |
95-
96-
# Design Overview: Hooks architecture
97-
98-
The _Hooks architecture_ contains two types of smart contracts:
99-
100-
1. **Core contract**
101-
102-
A minimal, non-upgradeable smart contract that contains functions and state/storage that are core to the contract’s identity.
103-
104-
2. **_Hook_ contract**
105-
106-
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.
107-
108-
![Hooks-architecture-overview](https://github.com/thirdweb-dev/contracts-next/blob/main/assets/hooks-banner.png?raw=true)
109-
110-
The goal of this _Hooks architecture_ is making upgradeability **_safer_**, **_more predictable and less likely to go wrong_** by giving developers:
111-
112-
- An API that contains a comprehensive, but fixed number of pre-defined points in a smart contract where developers can introduce customizations.
113-
- 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)
114-
115-
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:
116-
117-
- **Non-upgradeable**: Developer contracts are not upgradeable.
118-
- **Self-managed upgradeable:** Only the developer is authorized to upgrade their (hook) contracts.
119-
- **Managed upgradeable:** Developer authorizes a third party to upgrade their (hook) contracts.
120-
121-
## Token contracts in the Hooks architecture
122-
123-
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.
124-
125-
All 3 token core contracts implement:
126-
127-
- The token standard itself. ([ERC-20](https://eips.ethereum.org/EIPS/eip-20) + [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) Permit / [ERC-721](https://eips.ethereum.org/EIPS/eip-721) / [ERC-1155](https://eips.ethereum.org/EIPS/eip-1155)).
128-
- [HookInstaller](https://github.com/thirdweb-dev/contracts-next/blob/main/src/core/src/interface/IHookInstaller.sol) interface for installing hooks.
129-
- A token standard specific `getAllHooks()` view function interface (e.g. [IERC20HookInstaller](https://github.com/thirdweb-dev/contracts-next/blob/main/src/core/src/interface/IERC20HookInstaller.sol))
130-
- [EIP-173](https://eips.ethereum.org/EIPS/eip-173) Contract ownership standard
131-
- [EIP-7572](https://eips.ethereum.org/EIPS/eip-7572) Contract-level metadata via `contractURI()` standard
132-
- Multicall interface
133-
- External mint() and burn() functions.
134-
135-
The token core contracts use the [solady implementations](https://github.com/Vectorized/solady) of the token standards, ownable and multicall contracts.
136-
137-
### Available hooks for token contracts
138-
139-
| **Hook Functions** | ERC-20 | ERC-721 | ERC-1155 |
140-
| ------------------------------------ | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
141-
| 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. |
142-
| 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. |
143-
| 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` |
144-
| 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. |
145-
| beforeBatchTransfer (Batch Transfer) ||| ✅ called once before a batch transfer of tokens in the `ERC1155.safeBatchTransferredFrom` call. |
146-
| onRoyaltyInfo (EIP-2981 Royalty) || ✅ Called to retrieve royalty info on `EIP2981.royaltyInfo` call | ✅ Called to retrieve royalty info on `EIP2981.royaltyInfo` call |
147-
| 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 |
83+
``` -->
14884

149-
## Feedback
85+
# Feedback
15086

15187
If you have any feedback, please create an issue or reach out to us at [email protected].
15288

153-
## Authors
89+
# Authors
15490

15591
- [thirdweb](https://thirdweb.com)
15692

157-
## License
93+
# License
15894

15995
[Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt)
435 KB
Loading

assets/hooks-banner.png

-957 KB
Binary file not shown.

assets/readme-hero-image.png

1.58 MB
Loading

0 commit comments

Comments
 (0)