@towns-protocol/contracts 0.0.430 → 0.0.432

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -58,11 +58,10 @@ The system also supports cross-chain delegation between Ethereum L1 and Base L2,
58
58
 
59
59
  ## Requirements
60
60
 
61
- Install [yarn](https://yarnpkg.com/getting-started/install) via corepack:
61
+ Install [Bun](https://bun.sh/):
62
62
 
63
63
  ```shell
64
- npm install -g corepack
65
- corepack enable
64
+ curl -fsSL https://bun.sh/install | bash
66
65
  ```
67
66
 
68
67
  Install [Foundry](https://github.com/foundry-rs/foundry):
@@ -77,7 +76,7 @@ foundryup
77
76
  Clone the repo, then:
78
77
 
79
78
  ```shell
80
- yarn
79
+ bun install
81
80
  ```
82
81
 
83
82
  **To compile the smart contracts:**
@@ -39,7 +39,7 @@ function permitAndStake(
39
39
  #### Dependencies
40
40
 
41
41
  ```bash
42
- yarn add @uniswap/permit2-sdk viem
42
+ bun add @uniswap/permit2-sdk viem
43
43
  ```
44
44
 
45
45
  #### Complete Integration Example
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@towns-protocol/contracts",
3
- "version": "0.0.430",
3
+ "version": "0.0.432",
4
4
  "scripts": {
5
5
  "clean": "forge clean",
6
6
  "compile": "forge build",
7
- "format": "yarn prettier:write",
8
- "lint": "yarn solhint \"{script,src,tests}/**/*.sol\"",
7
+ "format": "bun run prettier:write",
8
+ "lint": "bun run solhint \"{script,src,tests}/**/*.sol\"",
9
9
  "prettier:check": "prettier --check \"**/*.{sol,json,js,md,yml,ts}\" --ignore-path \".prettierignore\"",
10
10
  "prettier:write": "prettier --write \"**/*.{sol,json,js,md,yml,ts}\" --ignore-path \".prettierignore\"",
11
11
  "snapshot": "forge snapshot --isolate",
12
12
  "test": "forge test --ffi --nmc Fork --fuzz-runs 4096",
13
- "test:unit": "yarn test",
13
+ "test:unit": "bun run test",
14
14
  "typings": "wagmi generate"
15
15
  },
16
16
  "dependencies": {
17
- "@erc6900/reference-implementation": "https://github.com/erc6900/reference-implementation/archive/refs/tags/v0.8.0.tar.gz",
17
+ "@erc6900/reference-implementation": "github:erc6900/reference-implementation#v0.8.0",
18
18
  "@ethereum-attestation-service/eas-contracts": "^1.8.0",
19
19
  "@layerzerolabs/oft-evm": "^3.1.4",
20
20
  "@openzeppelin/contracts": "^5.4.0",
@@ -33,7 +33,7 @@
33
33
  "@layerzerolabs/oapp-evm": "^0.3.2",
34
34
  "@openzeppelin/merkle-tree": "^1.0.8",
35
35
  "@prb/test": "^0.6.4",
36
- "@towns-protocol/prettier-config": "^0.0.430",
36
+ "@towns-protocol/prettier-config": "^0.0.432",
37
37
  "@wagmi/cli": "^2.2.0",
38
38
  "forge-std": "github:foundry-rs/forge-std#v1.10.0",
39
39
  "prettier": "^3.5.3",
@@ -47,11 +47,8 @@
47
47
  "scripts/**/*.sol",
48
48
  "README.md"
49
49
  ],
50
- "installConfig": {
51
- "hoistingLimits": "dependencies"
52
- },
53
50
  "publishConfig": {
54
51
  "access": "public"
55
52
  },
56
- "gitHead": "200adfdd8222b53b62d2af9609ed04f3eb831584"
53
+ "gitHead": "aed1393cc61f669c852b2d244630e27ae27a5922"
57
54
  }
@@ -0,0 +1,182 @@
1
+ # Bytecode-Diff Tool
2
+
3
+ Bytecode-Diff is a tool to retrieve and display contract bytecode diff for Base deployed contracts and Solidity source compiled bytecode compiled with forge. It provides functionality to run source code diffs or remote bytecode diffs and create reports that detail changes between two compiled bytecode versions of contracts.
4
+
5
+ ## Prerequisites
6
+
7
+ - Go 1.22 or later
8
+ - Base RPC Provider URL
9
+ - River Chain RPC Provider URL
10
+ - Basescan API Key
11
+
12
+ ## Usage
13
+
14
+ ### Local Source Code Diff
15
+
16
+ Compile contracts with forge and run source code diff comparing nearest commit report with checked out commit.
17
+
18
+ The basic command structure is:
19
+
20
+ ```bash
21
+ # ensure contracts are compiled with forge
22
+ cd ../../packages/contracts
23
+ make build
24
+ # disable go.work file since bytecode-diff is not a module in parent go workspace
25
+ go mod download
26
+ # run source diff from checked out commitSha compared nearest commit with a source diff report in SOURCE_DIFF_DIR
27
+ GOWORK=off go run main.go -v -s
28
+
29
+ # write report with contract addresses, their keccak256 compilaed bytecode hash under two keys, existing and updated.
30
+ ➜ bytecode-diff ✗ yq eval '.existing' source-diffs/00adc44f_08292024_5.yaml
31
+ Architect: 0xd291e489716f2c9cfc2e2c6047ce777159969943c85d09c51aaf7bbad10f7c13
32
+ ArchitectBase: 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
33
+ ArchitectStorage: 0x86159d997458669c4df8af2da4b5ce9ca742099a3f854c5eb3e718e16a74e4da
34
+ Banning: 0xde1354882fd30088cce4b00ff720a6dbc8c9f25653477c6ee99e20e17edb6068
35
+ BanningBase: 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
36
+ BanningStorage: 0x86159d997458669c4df8af2da4b5ce9ca742099a3f854c5eb3e718e16a74e4da
37
+ ChannelBase: 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
38
+ ...
39
+ ```
40
+
41
+ ### Run pairwise remote bytecode diff on facets deployed to two networks
42
+
43
+ Runs bytecode diff from deployed facets for diamonds in alpha, beta, and omega environments as per source coordinates of diamonds for each environment.
44
+
45
+ ```bash
46
+ # compare omega against beta facets and facet selectors
47
+ GOWORK=off go run ./main.go beta omega -v
48
+
49
+ # output facet implementation changes by facet or selectors that are missing from omega
50
+ ➜ bytecode-diff git:(jt/net-62-contract-differ) ✗ yq eval deployed-diffs/facet_diff_090324_18.yaml
51
+ diamonds:
52
+ - name: spaceOwner
53
+ source: beta
54
+ target: omega
55
+ facets:
56
+ - sourceContractName: ""
57
+ sourceFacetAddress: 0xfa98a1648761e494fc7d6efe5a06e357a76bd6fb
58
+ selectorsDiff:
59
+ - "0x3953801b"
60
+ - "0x91de4a83"
61
+ sourceBytecodeHash: 0xf86d9dbe53c89e14fa69cde925cca02b6efad519fe172f7b04d9515d7700a59b
62
+ sourceVerified: false
63
+ targetVerified: false
64
+ - sourceContractName: SpaceOwner
65
+ sourceFacetAddress: 0x30c912d8ceb9793e4cd240862acfd0e6c4436c52
66
+ targetContractAddresses:
67
+ - 0x63bC35259Ac32DF43Fba3b890F0F74951451976A
68
+ - 0xe7EB1313f0E7076616534225e16E971B72b50C42
69
+ selectorsDiff: []
70
+ sourceBytecodeHash: 0x461b53ab37fd24283ecd63eb0d4e71bd554a266036c73caf6d2ac39c435e7732
71
+ targetBytecodeHashes:
72
+ - 0x86d20161a13671a6138b80551e94dd8c1638bc5151807ff2194aa1e50cdb3cac
73
+ - 0xff0a94e93a4f4f6ee0ecd0d0e469e55ca40f1ab6c10e6af9da5b2b597f32b178
74
+ sourceVerified: true
75
+ targetVerified: true
76
+ - sourceContractName: ""
77
+ sourceFacetAddress: 0xdba2ce6125cc6b7f93c63d181a0780d5b421940b
78
+ selectorsDiff:
79
+ - "0x0d653654"
80
+ - "0x466a18de"
81
+ sourceBytecodeHash: 0x583c2852056f90c96ed1cab935489f644b8ef564e0a7f11564925d07cf3bc593
82
+ sourceVerified: false
83
+ targetVerified: false
84
+
85
+ ```
86
+
87
+ ### Run keccak256 hash generation on deployed contracts
88
+
89
+ ```bash
90
+ GOWORK=off go run main.go add-hashes beta deployed-diffs/facet_diff_090624_1.yaml
91
+
92
+ # output to new yaml file suffixed with _hashed.yaml including bytecodeHash for each contract in deployments section
93
+ ➜ bytecode-diff git:(jt/net-62-upgrade-script-2) ✗ yq e '.deployments' deployed-diffs/facet_diff_090624_1_hashed.yaml
94
+ Architect:
95
+ address: 0xa18a3df4f63cdcae943d9c76730adf2812388de4
96
+ baseScanLink: https://sepolia.basescan.org/tx/0x4280ef1300fe001e7d85e7495eba13fc99be53ee7a7060e753d466f8bebf1622
97
+ bytecodeHash: 0x20d0a86e9ea31a39663285aacfe88705983520a4482a7bac5ada891c9adfe090
98
+ deploymentDate: 2024-09-06 19:04
99
+ transactionHash: 0x4280ef1300fe001e7d85e7495eba13fc99be53ee7a7060e753d466f8bebf1622
100
+ Banning:
101
+ address: 0x4d88d1fbba6ce6bcdb4381549ee0b7c0d2b56919
102
+ baseScanLink: https://sepolia.basescan.org/tx/0x4ccbaf9750bcd0971975e73a24b05f1c51d4703cf72a406356c79eb54de9c33c
103
+ bytecodeHash: 0xa2ce3e77ba060ff1d59ed384e1c6c5788f308ad8bbbef612eb3e5de4e1d79de8
104
+ deploymentDate: 2024-09-06 19:05
105
+ transactionHash: 0x4ccbaf9750bcd0971975e73a24b05f1c51d4703cf72a406356c79eb54de9c33c
106
+ ...
107
+ ```
108
+
109
+ ### Flags
110
+
111
+ ```bash
112
+ ➜ bytecode-diff ✗ GOWORK=off go run ./main.go --help
113
+ A tool to retrieve and display contract bytecode diff for Base
114
+
115
+ Usage:
116
+ bytecode-diff [source_environment] [target_environment] [flags]
117
+
118
+ Flags:
119
+ -b, --base-rpc string Base RPC provider URL
120
+ --base-sepolia-rpc string Base Sepolia RPC provider URL
121
+ --compiled-facets string Path to compiled facets
122
+ --deployments string Path to deployments directory (default "../../packages/contracts/deployments")
123
+ --facets string Path to facet source files
124
+ -h, --help help for bytecode-diff
125
+ --log-level string Set the logging level (debug, info, warn, error) (default "info")
126
+ --report-out-dir string Path to report output directory (default "deployed-diffs")
127
+ --source-diff-log string Path to diff log file (default "source-diffs")
128
+ -s, --source-diff-only Run source code diff
129
+ -v, --verbose Enable verbose output
130
+ ```
131
+
132
+ ### Environment Variables
133
+
134
+ You can also set the following environment variables instead of using flags:
135
+
136
+ - `BASE_RPC_URL`: Base RPC provider URL
137
+ - `BASE_SEPOLIA_RPC_URL`: Base Sepolia RPC provider URL
138
+ - `FACET_SOURCE_PATH`: Path to facet source files
139
+ - `ETHERSCAN_API_KEY`: Your API key for Etherscan.
140
+ - `COMPILED_FACETS_PATH`: (Optional) Path to compiled facets
141
+ - `DEPLOYMENTS_PATH`: (Optional) Path to deployed contracts
142
+ - `REPORT_OUT_DIR`: (Optional) Path to report output directory
143
+ - `SOURCE_DIFF_DIR`: (Optional) Path to source diff reports
144
+
145
+ ## Examples
146
+
147
+ 1. Run source code diff with all parameters specified via flags:
148
+
149
+ ```
150
+ ./bytecode-diff --source-diff-only \
151
+ --source-diff-dir /path/to/source-diff-reports \
152
+ --facets /path/to/facet/sources \
153
+ --compiled-facets /path/to/compiled/facets \
154
+ --report-out-dir /path/to/report/output \
155
+ --verbose
156
+ ```
157
+
158
+ 2. Run source code diff using environment variables:
159
+
160
+ ```bash
161
+ export SOURCE_DIFF_DIR=/path/to/source_diff.log
162
+ export FACET_SOURCE_PATH=/path/to/facet/sources
163
+ export COMPILED_FACETS_PATH=/path/to/compiled/facets
164
+ export REPORT_OUT_DIR=/path/to/report/output
165
+
166
+ ./bytecode-diff -s --verbose
167
+ ```
168
+
169
+ 3. Run source code diff with r/w to remote s3 bucket:
170
+
171
+ ```bash
172
+ export AWS_ACCESS_KEY_ID=<your-access-key-id>
173
+ export AWS_SECRET_ACCESS_KEY=<your-secret-access-key>
174
+ export SOURCE_DIFF_DIR=s3://bucket/path
175
+ ./bytecode-diff -s --verbose
176
+ ```
177
+
178
+ ## Notes
179
+
180
+ - If a `.env` file is present in the same directory as the script, it will be loaded automatically.
181
+ - When running source code diff, all required paths must be provided either via flags or environment variables.
182
+ - Use the `--verbose` flag for more detailed output during execution.
@@ -0,0 +1,289 @@
1
+ ## Opinionated deployment scripting 🚀
2
+
3
+ Inspired by [hardhat-deploy](https://github.com/wighawag/hardhat-deploy)
4
+
5
+ This project supports two methods for deploying contracts:
6
+
7
+ ### Method 1: Custom Deployment Scripts
8
+
9
+ For each contract being deployed, we create a script that will:
10
+
11
+ 1. inherit from `Deployer`
12
+ 2. implement a `versionName()` and `__deploy()` function
13
+
14
+ Example contract:
15
+
16
+ <details>
17
+ <summary>Click to expand example code</summary>
18
+
19
+ ```solidity
20
+ // SPDX-License-Identifier: MIT
21
+ pragma solidity ^0.8.24;
22
+
23
+ import { Deployer } from "scripts/common/Deployer.s.sol";
24
+ import { MockERC721A } from "test/mocks/MockERC721A.sol";
25
+
26
+ contract DeployMockERC721A is Deployer {
27
+ function versionName() public pure override returns (string memory) {
28
+ return "mockERC721A";
29
+ }
30
+
31
+ function __deploy(address deployer) public override returns (address) {
32
+ vm.broadcast(deployer);
33
+ return address(new MockERC721A());
34
+ }
35
+ }
36
+ ```
37
+
38
+ </details>
39
+
40
+ The framework will:
41
+
42
+ 1. Load an existing deployment from
43
+ `contracts/deployments/<deploymentContext>/<chainIdAlias>/<contracts>.json`
44
+
45
+ 2. if `OVERRIDE_DEPLOYMENTS=1` is set or if no deployments are found, it will:
46
+
47
+ - read `PRIVATE_KEY` from env (LOCAL_PRIVATE_KEY for anvil) or wait for ledger
48
+ - invoke `__deploy()` function
49
+ - if `SAVE_DEPLOYMENTS` is set; it will save the deployment to
50
+ `contracts/deployments/deploymentContext/<network>/<contract>.json`
51
+
52
+ ### Method 2: DeployFacet Script (For Facet Contracts)
53
+
54
+ For facet contract deployments, we also support a standardized deployment process using the `DeployFacet.s.sol` script which:
55
+
56
+ 1. Uses deterministic CREATE2 addresses for predictable deployment outcomes
57
+ 2. Supports batch deployments for gas efficiency
58
+ 3. Provides advanced gas estimation and deployment verification
59
+
60
+ - `OVERRIDE_DEPLOYMENTS=1`: It will redeploy a version of the contracts even if there's a cache in
61
+ deployments assigned, be very careful when using this
62
+ - `SAVE_DEPLOYMENTS=1`: It will save a cached address of deployments to
63
+ `contracts/deployments/<network>/<contract>.json`
64
+ - `DEPLOYMENT_CONTEXT=string`: It will save the addresses on a subdirectory with the given name,
65
+ useful for deployment contract to same network
66
+
67
+ ## How to write new deployment libraries and diamonds
68
+
69
+ This project uses two distinct but complementary components for diamond pattern deployments:
70
+
71
+ ### 1. Facet Deployment Libraries
72
+
73
+ Facet deployment libraries (like `DeployMembershipMetadata.s.sol`) define helper functions for diamond cuts but don't handle the actual facet contract deployment. These libraries typically provide:
74
+
75
+ 1. A `selectors()` function that returns an array of function selectors the facet provides
76
+ 2. A `makeCut()` function that creates a `FacetCut` struct for diamond upgrades
77
+ 3. Optional: A `deploy()` function for manual deployment (though we prefer using `DeployFacet` for this)
78
+
79
+ To create a new facet deployment library:
80
+
81
+ 1. Create a new file `Deploy[YourFacet].s.sol` in `scripts/deployments/facets/`
82
+ 2. Implement the required functions as shown in the example below
83
+
84
+ Example of a facet deployment library:
85
+
86
+ <details>
87
+ <summary>Click to expand example code</summary>
88
+
89
+ ```solidity
90
+ import { IDiamond } from "@towns-protocol/diamond/src/Diamond.sol";
91
+ import { IMembershipMetadata } from "src/spaces/facets/membership/metadata/IMembershipMetadata.sol";
92
+ import { LibDeploy } from "@towns-protocol/diamond/src/utils/LibDeploy.sol";
93
+
94
+ library DeployMembershipMetadata {
95
+ // Return all function selectors this facet provides
96
+ function selectors() internal pure returns (bytes4[] memory res) {
97
+ res = new bytes4[](2);
98
+ res[0] = IMembershipMetadata.refreshMetadata.selector;
99
+ res[1] = IMembershipMetadata.tokenURI.selector;
100
+ }
101
+
102
+ // Create a FacetCut struct for diamond upgrades
103
+ function makeCut(
104
+ address facetAddress,
105
+ IDiamond.FacetCutAction action
106
+ ) internal pure returns (IDiamond.FacetCut memory) {
107
+ return IDiamond.FacetCut(facetAddress, action, selectors());
108
+ }
109
+
110
+ // Optional direct deployment method (prefer using DeployFacet instead)
111
+ function deploy() internal returns (address) {
112
+ return LibDeploy.deployCode("MembershipMetadata.sol", "");
113
+ }
114
+ }
115
+ ```
116
+
117
+ </details>
118
+
119
+ ### 2. Diamond Deployment Scripts
120
+
121
+ Diamond deployment scripts (like `DeploySpace.s.sol`) coordinate the deployment of multiple facets and their integration into a diamond. These scripts:
122
+
123
+ 1. Use `DeployFacet` to efficiently deploy multiple facets using CREATE2 and Multicall3
124
+ 2. Use the facet deployment libraries to prepare diamond cuts
125
+ 3. Handle the initialization and configuration of the diamond
126
+
127
+ To create a new diamond deployment script:
128
+
129
+ 1. Create a new file `Deploy[YourDiamond].s.sol` in `scripts/deployments/diamonds/`
130
+ 2. Inherit from `DiamondHelper`, `Deployer` and implement required functions
131
+ 3. Use `DeployFacet` for batch deployments of facets
132
+ 4. Use facet deployment libraries to create diamond cuts
133
+
134
+ Example pattern from `DeploySpace.s.sol`:
135
+
136
+ <details>
137
+ <summary>Click to expand example code</summary>
138
+
139
+ ```solidity
140
+ contract DeploySpace is DiamondHelper, Deployer {
141
+ // Create a DeployFacet helper for batch deployments
142
+ DeployFacet private facetHelper = new DeployFacet();
143
+
144
+ function versionName() public pure override returns (string memory) {
145
+ return "space";
146
+ }
147
+
148
+ function __deploy(address deployer) internal override returns (address) {
149
+ // Add core facets (like DiamondCut, DiamondLoupe, etc.)
150
+ addImmutableCuts(deployer);
151
+
152
+ // Set up diamond initialization parameters with additional facets
153
+ Diamond.InitParams memory initDiamondCut = diamondInitParams(deployer);
154
+
155
+ // Deploy the diamond with all facets
156
+ vm.broadcast(deployer);
157
+ Diamond diamond = new Diamond(initDiamondCut);
158
+
159
+ return address(diamond);
160
+ }
161
+
162
+ function diamondInitParams(
163
+ address deployer
164
+ ) public returns (Diamond.InitParams memory) {
165
+ // Queue facets for batch deployment
166
+ facetHelper.add("MembershipToken");
167
+ facetHelper.add("MembershipMetadata");
168
+ // ... add other facets ...
169
+
170
+ // Deploy all queued facets in a single transaction
171
+ facetHelper.deployBatch(deployer);
172
+
173
+ // Add each facet to the diamond cut using the corresponding deployment library
174
+ address facet = facetHelper.getDeployedAddress("MembershipMetadata");
175
+ addCut(
176
+ DeployMembershipMetadata.makeCut(facet, IDiamond.FacetCutAction.Add)
177
+ );
178
+
179
+ // ... add other facets ...
180
+
181
+ // Return the diamond initialization parameters
182
+ return
183
+ Diamond.InitParams({
184
+ baseFacets: baseFacets(),
185
+ init: multiInit,
186
+ initData: abi.encodeCall(
187
+ MultiInit.multiInit,
188
+ (_initAddresses, _initDatas)
189
+ )
190
+ });
191
+ }
192
+ }
193
+ ```
194
+
195
+ </details>
196
+
197
+ ### Integration with makefile
198
+
199
+ Once you've created your deployment components, you can use them with the makefile:
200
+
201
+ ```bash
202
+ # To deploy a specific facet:
203
+ make deploy-facet rpc=base_sepolia contract=YourFacet
204
+
205
+ # To deploy a diamond:
206
+ make deploy-base-sepolia contract=DeployYourDiamond type=diamonds
207
+ ```
208
+
209
+ This approach separates facet contract deployment (using `DeployFacet` with CREATE2) from diamond integration (using facet deployment libraries), providing both efficiency and flexibility.
210
+
211
+ ### How to deploy locally (step-by-step)
212
+
213
+ <details>
214
+ <summary>Click to expand deployment steps</summary>
215
+
216
+ ```bash
217
+ # say you want to deploy a new MockERC721A
218
+
219
+ # Provision a new deployer
220
+ -> cast wallet new
221
+
222
+ # save the key in .env (LOCAL_PRIVATE_KEY=...)
223
+
224
+ # Fund the deployer address (this is the first address shown when runing `anvil`)
225
+ -> cast send ${NEW_WALLET_ADDRESS} --value 1ether -f 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --unlocked
226
+
227
+ # perform a local simulation
228
+ -> forge script script/${CONTRACT}.s.sol
229
+
230
+ # perform a simulation against a network
231
+ -> forge script script/${CONTRACT}.s.sol --rpc-url <network>
232
+
233
+ # run anvil in separate terminal
234
+ -> anvil
235
+
236
+ # perform the deployment to a local network
237
+ # Option 1: Custom deployment script
238
+ -> make deploy-any-local contract=DeployMockERC721A type=facets
239
+
240
+ # Option 2: Using the DeployFacet script (for facet contracts)
241
+ -> make deploy-facet-local rpc=base_anvil contract=MockERC721A
242
+ ```
243
+
244
+ </details>
245
+
246
+ ### How to deploy to a testnet or mainnet
247
+
248
+ <details>
249
+ <summary>Click to expand deployment steps</summary>
250
+
251
+ ```bash
252
+ # Method 1: Using custom deployment scripts
253
+ # To deploy a contract to Base Sepolia in the "beta" deployment context:
254
+ -> make deploy-base-sepolia contract=DeployWalletLink type=facets context=beta
255
+
256
+ # Method 2: Using the DeployFacet script
257
+ # To deploy a facet to Base Sepolia:
258
+ -> make deploy-facet rpc=base_sepolia contract=WalletLink context=beta
259
+
260
+ # To deploy with a ledger hardware wallet to Base mainnet:
261
+ # Method 1: Using custom deployment scripts
262
+ -> make deploy-base contract=DeploySpaceFactory type=diamonds context=omega
263
+
264
+ # Method 2: Using the DeployFacet script
265
+ -> make deploy-facet-ledger rpc=base contract=WalletLink context=omega
266
+
267
+ # To redeploy a contract to Base Sepolia in the "beta" deployment context:
268
+ -> OVERRIDE_DEPLOYMENTS=1 make deploy-base-sepolia contract=DeployWalletLink type=facets context=beta
269
+ ```
270
+
271
+ </details>
272
+
273
+ ### How to script (interact with deployed contracts through foundry)
274
+
275
+ <details>
276
+ <summary>Click to expand example steps</summary>
277
+
278
+ ```bash
279
+ # say you want to mint from MockERC721A
280
+
281
+ # deploy a local implementation of MockERC721A by calling DeployFacet
282
+ -> make deploy-facet-local rpc=base_anvil contract=MockERC721A
283
+
284
+ # next we'll call the script InteractMockERC721A
285
+ # This will grab new and existing deployment addresses from our deployments cache and use those to interact with each other
286
+ -> make interact-any-local rpc=base_anvil contract=InteractMockERC721A
287
+ ```
288
+
289
+ </details>
@@ -0,0 +1,50 @@
1
+ ## Guidelines for creating a facet
2
+
3
+ - Use diamond storage `Layout` for storing variables `<FacetName>Storage`
4
+ - Specify your internal business logic, and interaction with storage in `<FacetName>Base` abstract
5
+ contract
6
+ - Create you initializers and your protected external calls in the `<FacetName>Facet` contract
7
+ - Define your external and internal interface in `I<FacetName>` interface file
8
+ - `I<FacetName>Base` internal interface gets inherited by your `<FacetName>Base` abstract contract
9
+ and it usually holds structs, enums, errors and events
10
+ - `I<FacetName>` external interface gets inherited by your `<FacetName>Facet` contract and it
11
+ usually holds external functions, this interface can inherit your internal `I<FacetName>Base` to
12
+ have access to its data types
13
+
14
+ > Minimal example
15
+
16
+ ```solidity
17
+ library SampleStorage {
18
+ struct Layout {
19
+ uint256 value;
20
+ }
21
+
22
+ function layout() internal pure returns (Layout storage ds) {
23
+ bytes32 slot = keccak256("sample.storage");
24
+ assembly {
25
+ ds.slot := slot
26
+ }
27
+ }
28
+ }
29
+
30
+ interface ISampleBase {
31
+ event ValueSet(uint256 value);
32
+ }
33
+
34
+ inteface ISample is ISampleBase {
35
+ function setValue(uint256) external;
36
+ }
37
+
38
+ abstract contract SampleBase is ISampleBase {
39
+ function _setValue(uint256 val) internal {
40
+ SampleStorage.layout().value = x;
41
+ emit ValueSet(val);
42
+ }
43
+ }
44
+
45
+ contract SampleFacet is ISample, SampleBase {
46
+ function setValue(uint256 val) onlyOwner external {
47
+ _setValue(val);
48
+ }
49
+ }
50
+ ```
@@ -103,21 +103,26 @@ contract MembershipFacet is IMembership, MembershipJoin, ReentrancyGuard, Facet
103
103
 
104
104
  /// @inheritdoc IMembership
105
105
  function getMembershipPrice() external view returns (uint256 totalRequired) {
106
- (totalRequired, ) = _getTotalMembershipPayment(_getMembershipPrice(_totalSupply()));
106
+ uint256 membershipPrice = _getMembershipPrice(_totalSupply());
107
+ if (membershipPrice == 0) return 0;
108
+
109
+ (totalRequired, ) = _getTotalMembershipPayment(membershipPrice);
107
110
  }
108
111
 
109
112
  /// @inheritdoc IMembership
110
113
  function getMembershipRenewalPrice(
111
114
  uint256 tokenId
112
115
  ) external view returns (uint256 totalRequired) {
113
- (totalRequired, ) = _getTotalMembershipPayment(
114
- _getMembershipRenewalPrice(tokenId, _totalSupply())
115
- );
116
+ uint256 renewalPrice = _getMembershipRenewalPrice(tokenId, _totalSupply());
117
+ if (renewalPrice == 0) return 0;
118
+ (totalRequired, ) = _getTotalMembershipPayment(renewalPrice);
116
119
  }
117
120
 
118
121
  /// @inheritdoc IMembership
119
122
  function getProtocolFee() external view returns (uint256 protocolFee) {
120
- (, protocolFee) = _getTotalMembershipPayment(_getMembershipPrice(_totalSupply()));
123
+ uint256 membershipPrice = _getMembershipPrice(_totalSupply());
124
+ if (membershipPrice == 0) return 0;
125
+ (, protocolFee) = _getTotalMembershipPayment(membershipPrice);
121
126
  }
122
127
 
123
128
  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
@@ -499,6 +499,20 @@ abstract contract MembershipJoin is
499
499
 
500
500
  uint256 duration = _getMembershipDuration();
501
501
  uint256 basePrice = _getMembershipRenewalPrice(tokenId, _totalSupply());
502
+
503
+ // Handle free renewal
504
+ if (basePrice == 0) {
505
+ // Refund any ETH sent
506
+ CurrencyTransfer.transferCurrency(
507
+ _getMembershipCurrency(),
508
+ address(this),
509
+ payer,
510
+ msg.value
511
+ );
512
+ _renewSubscription(tokenId, uint64(duration));
513
+ return;
514
+ }
515
+
502
516
  (uint256 totalRequired, ) = _getTotalMembershipPayment(basePrice);
503
517
 
504
518
  if (totalRequired > msg.value) Membership__InvalidPayment.selector.revertWith();