@croptop/core-v6 0.0.13 → 0.0.15
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/STYLE_GUIDE.md +15 -2
- package/package.json +9 -9
- package/script/ConfigureFeeProject.s.sol +40 -15
- package/script/Deploy.s.sol +40 -25
- package/script/helpers/CroptopDeploymentLib.sol +23 -6
- package/src/CTDeployer.sol +6 -7
- package/src/CTProjectOwner.sol +4 -1
- package/src/CTPublisher.sol +11 -2
- package/src/interfaces/ICTPublisher.sol +2 -0
- package/src/structs/CTAllowedPost.sol +1 -0
- package/src/structs/CTDeployerAllowedPost.sol +1 -0
- package/src/structs/CTPost.sol +2 -0
- package/src/structs/CTProjectConfig.sol +1 -0
- package/src/structs/CTSuckerDeploymentConfig.sol +1 -0
- package/test/CTPublisher.t.sol +1 -0
- package/test/CroptopAttacks.t.sol +1 -0
- package/test/Fork.t.sol +6 -1
- package/test/Test_MetadataGeneration.t.sol +10 -1
- package/test/regression/DuplicateUriFeeEvasion.t.sol +3 -0
- package/test/regression/FeeEvasion.t.sol +1 -0
- package/test/regression/StaleTierIdMapping.t.sol +1 -0
package/STYLE_GUIDE.md
CHANGED
|
@@ -197,7 +197,7 @@ interface IJBExample is IJBBase {
|
|
|
197
197
|
| Public/external function | `camelCase` | `cashOutTokensOf` |
|
|
198
198
|
| Internal/private function | `_camelCase` | `_processFee` |
|
|
199
199
|
| Internal storage | `_camelCase` | `_accountingContextForTokenOf` |
|
|
200
|
-
| Function parameter | `camelCase` | `projectId`, `cashOutCount` |
|
|
200
|
+
| Function parameter | `camelCase` (no underscores) | `projectId`, `cashOutCount` |
|
|
201
201
|
|
|
202
202
|
## NatSpec
|
|
203
203
|
|
|
@@ -253,9 +253,12 @@ uint256 public constant MAX_RESERVED_PERCENT = 10_000;
|
|
|
253
253
|
|
|
254
254
|
## Function Calls
|
|
255
255
|
|
|
256
|
-
Use named
|
|
256
|
+
Use named arguments for all function calls with 2 or more arguments — in both `src/` and `script/`:
|
|
257
257
|
|
|
258
258
|
```solidity
|
|
259
|
+
// Good — named arguments
|
|
260
|
+
token.mint({account: beneficiary, amount: count});
|
|
261
|
+
_transferOwnership({newOwner: address(0), projectId: 0});
|
|
259
262
|
PERMISSIONS.hasPermission({
|
|
260
263
|
operator: sender,
|
|
261
264
|
account: account,
|
|
@@ -264,8 +267,18 @@ PERMISSIONS.hasPermission({
|
|
|
264
267
|
includeRoot: true,
|
|
265
268
|
includeWildcardProjectId: true
|
|
266
269
|
});
|
|
270
|
+
|
|
271
|
+
// Bad — positional arguments with 2+ args
|
|
272
|
+
token.mint(beneficiary, count);
|
|
273
|
+
_transferOwnership(address(0), 0);
|
|
267
274
|
```
|
|
268
275
|
|
|
276
|
+
Single-argument calls use positional style: `_burn(amount)`.
|
|
277
|
+
|
|
278
|
+
This also applies to constructor calls, struct literals, and inherited/library calls (e.g., OZ `_mint`, `_safeMint`, `safeTransfer`, `allowance`, `Clones.cloneDeterministic`).
|
|
279
|
+
|
|
280
|
+
Named argument keys must use **camelCase** — never underscores. If a function's parameter names use underscores, rename them to camelCase first.
|
|
281
|
+
|
|
269
282
|
## Multiline Signatures
|
|
270
283
|
|
|
271
284
|
```solidity
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@croptop/core-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -16,17 +16,17 @@
|
|
|
16
16
|
"artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'croptop-core-v5'"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@bananapus/721-hook-v6": "^0.0.
|
|
20
|
-
"@bananapus/buyback-hook-v6": "^0.0.
|
|
21
|
-
"@bananapus/core-v6": "^0.0.
|
|
22
|
-
"@bananapus/ownable-v6": "^0.0.
|
|
23
|
-
"@bananapus/permission-ids-v6": "^0.0.
|
|
24
|
-
"@bananapus/router-terminal-v6": "^0.0.
|
|
25
|
-
"@bananapus/suckers-v6": "^0.0.
|
|
19
|
+
"@bananapus/721-hook-v6": "^0.0.16",
|
|
20
|
+
"@bananapus/buyback-hook-v6": "^0.0.12",
|
|
21
|
+
"@bananapus/core-v6": "^0.0.16",
|
|
22
|
+
"@bananapus/ownable-v6": "^0.0.9",
|
|
23
|
+
"@bananapus/permission-ids-v6": "^0.0.9",
|
|
24
|
+
"@bananapus/router-terminal-v6": "^0.0.11",
|
|
25
|
+
"@bananapus/suckers-v6": "^0.0.10",
|
|
26
26
|
"@openzeppelin/contracts": "^5.6.1"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@rev-net/core-v6": "^0.0.
|
|
29
|
+
"@rev-net/core-v6": "^0.0.11",
|
|
30
30
|
"@sphinx-labs/plugins": "^0.33.1"
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
-
import "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
|
|
5
|
-
import "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
|
|
6
|
-
import "@bananapus/suckers-v6/script/helpers/SuckerDeploymentLib.sol";
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
import {Hook721Deployment, Hook721DeploymentLib} from "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
|
|
5
|
+
import {CoreDeployment, CoreDeploymentLib} from "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
|
|
6
|
+
import {SuckerDeployment, SuckerDeploymentLib} from "@bananapus/suckers-v6/script/helpers/SuckerDeploymentLib.sol";
|
|
7
|
+
import {
|
|
8
|
+
RouterTerminalDeployment,
|
|
9
|
+
RouterTerminalDeploymentLib
|
|
10
|
+
} from "@bananapus/router-terminal-v6/script/helpers/RouterTerminalDeploymentLib.sol";
|
|
11
|
+
import {
|
|
12
|
+
RevnetCoreDeployment,
|
|
13
|
+
RevnetCoreDeploymentLib
|
|
14
|
+
} from "@rev-net/core-v6/script/helpers/RevnetCoreDeploymentLib.sol";
|
|
15
|
+
import {CroptopDeployment, CroptopDeploymentLib} from "./helpers/CroptopDeploymentLib.sol";
|
|
10
16
|
|
|
11
17
|
import {Sphinx} from "@sphinx-labs/contracts/contracts/foundry/SphinxPlugin.sol";
|
|
12
18
|
import {Script} from "forge-std/Script.sol";
|
|
13
19
|
|
|
14
20
|
import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
|
|
15
|
-
import {JBDeploy721TiersHookConfig} from "@bananapus/721-hook-v6/src/structs/JBDeploy721TiersHookConfig.sol";
|
|
16
21
|
import {JB721InitTiersConfig} from "@bananapus/721-hook-v6/src/structs/JB721InitTiersConfig.sol";
|
|
17
22
|
import {JB721TierConfig} from "@bananapus/721-hook-v6/src/structs/JB721TierConfig.sol";
|
|
18
|
-
import {JB721TiersHookFlags} from "@bananapus/721-hook-v6/src/structs/JB721TiersHookFlags.sol";
|
|
19
23
|
import {IJBSplitHook} from "@bananapus/core-v6/src/interfaces/IJBSplitHook.sol";
|
|
20
24
|
import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
|
|
21
25
|
import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
|
|
@@ -28,6 +32,8 @@ import {JBTokenMapping} from "@bananapus/suckers-v6/src/structs/JBTokenMapping.s
|
|
|
28
32
|
import {REVAutoIssuance} from "@rev-net/core-v6/src/structs/REVAutoIssuance.sol";
|
|
29
33
|
import {REVConfig} from "@rev-net/core-v6/src/structs/REVConfig.sol";
|
|
30
34
|
import {REVCroptopAllowedPost} from "@rev-net/core-v6/src/structs/REVCroptopAllowedPost.sol";
|
|
35
|
+
import {REVBaseline721HookConfig} from "@rev-net/core-v6/src/structs/REVBaseline721HookConfig.sol";
|
|
36
|
+
import {REV721TiersHookFlags} from "@rev-net/core-v6/src/structs/REV721TiersHookFlags.sol";
|
|
31
37
|
import {REVDeploy721TiersHookConfig} from "@rev-net/core-v6/src/structs/REVDeploy721TiersHookConfig.sol";
|
|
32
38
|
import {REVDescription} from "@rev-net/core-v6/src/structs/REVDescription.sol";
|
|
33
39
|
import {REVStageConfig} from "@rev-net/core-v6/src/structs/REVStageConfig.sol";
|
|
@@ -57,25 +63,44 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
57
63
|
|
|
58
64
|
// @notice set this to a non-zero value to re-use an existing projectID. Having it set to 0 will deploy a new
|
|
59
65
|
// fee_project.
|
|
66
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
60
67
|
uint256 FEE_PROJECT_ID;
|
|
61
68
|
|
|
69
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
62
70
|
uint32 PREMINT_CHAIN_ID = 1;
|
|
71
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
63
72
|
string NAME = "Croptop Publishing Network";
|
|
73
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
64
74
|
string SYMBOL = "CPN";
|
|
75
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
65
76
|
string PROJECT_URI = "ipfs://QmUAFevoMn1iqSEQR8LogQYRxm39TNxQTPYnuLuq5BmfEi";
|
|
77
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
66
78
|
uint32 NATIVE_CURRENCY = uint32(uint160(JBConstants.NATIVE_TOKEN));
|
|
79
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
67
80
|
uint32 ETH_CURRENCY = JBCurrencyIds.ETH;
|
|
81
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
68
82
|
uint8 DECIMALS = 18;
|
|
83
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
69
84
|
uint256 DECIMAL_MULTIPLIER = 10 ** DECIMALS;
|
|
85
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
70
86
|
bytes32 SUCKER_SALT = "_CPN_SUCKERV6__";
|
|
87
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
71
88
|
bytes32 ERC20_SALT = "_CPN_ERC20_SALTV6__";
|
|
89
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
72
90
|
bytes32 HOOK_SALT = "_CPN_HOOK_SALTV6__";
|
|
91
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
73
92
|
address OPERATOR;
|
|
93
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
74
94
|
address TRUSTED_FORWARDER;
|
|
95
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
75
96
|
uint48 CPN_START_TIME = 1_740_089_444;
|
|
97
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
76
98
|
uint104 CPN_MAINNET_AUTO_ISSUANCE_ = 250_003_875_000_000_000_000_000;
|
|
99
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
77
100
|
uint104 CPN_OP_AUTO_ISSUANCE_ = 844_894_881_600_000_000_000;
|
|
101
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
78
102
|
uint104 CPN_BASE_AUTO_ISSUANCE_ = 844_894_881_600_000_000_000;
|
|
103
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
79
104
|
uint104 CPN_ARB_AUTO_ISSUANCE_ = 3_844_000_000_000_000_000;
|
|
80
105
|
|
|
81
106
|
function configureSphinx() public override {
|
|
@@ -168,6 +193,7 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
168
193
|
autoIssuances: issuanceConfs,
|
|
169
194
|
splitPercent: 3800, // 38%
|
|
170
195
|
splits: splits,
|
|
196
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
171
197
|
initialIssuance: uint112(10_000 * DECIMAL_MULTIPLIER),
|
|
172
198
|
issuanceCutFrequency: 120 days,
|
|
173
199
|
issuanceCutPercent: 380_000_000, // 38%
|
|
@@ -201,7 +227,7 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
201
227
|
|
|
202
228
|
// The project's revnet configuration
|
|
203
229
|
REVConfig memory revnetConfiguration = REVConfig({
|
|
204
|
-
description: REVDescription(NAME, SYMBOL, PROJECT_URI, ERC20_SALT),
|
|
230
|
+
description: REVDescription({name: NAME, ticker: SYMBOL, uri: PROJECT_URI, salt: ERC20_SALT}),
|
|
205
231
|
baseCurrency: ETH_CURRENCY,
|
|
206
232
|
splitOperator: OPERATOR,
|
|
207
233
|
stageConfigurations: stageConfigurations
|
|
@@ -298,22 +324,21 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
298
324
|
terminalConfigurations: terminalConfigurations,
|
|
299
325
|
suckerDeploymentConfiguration: suckerDeploymentConfiguration,
|
|
300
326
|
hookConfiguration: REVDeploy721TiersHookConfig({
|
|
301
|
-
baseline721HookConfiguration:
|
|
327
|
+
baseline721HookConfiguration: REVBaseline721HookConfig({
|
|
302
328
|
name: NAME,
|
|
303
329
|
symbol: SYMBOL,
|
|
304
330
|
baseUri: "ipfs://",
|
|
305
331
|
tokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
306
332
|
contractUri: "",
|
|
307
333
|
tiersConfig: JB721InitTiersConfig({
|
|
308
|
-
tiers: new JB721TierConfig[](0), currency: ETH_CURRENCY, decimals: DECIMALS
|
|
334
|
+
tiers: new JB721TierConfig[](0), currency: ETH_CURRENCY, decimals: DECIMALS
|
|
309
335
|
}),
|
|
310
336
|
reserveBeneficiary: address(0),
|
|
311
|
-
flags:
|
|
337
|
+
flags: REV721TiersHookFlags({
|
|
312
338
|
noNewTiersWithReserves: false,
|
|
313
339
|
noNewTiersWithVotes: true,
|
|
314
340
|
noNewTiersWithOwnerMinting: true,
|
|
315
|
-
preventOverspending: false
|
|
316
|
-
issueTokensForSplits: false
|
|
341
|
+
preventOverspending: false
|
|
317
342
|
})
|
|
318
343
|
}),
|
|
319
344
|
salt: HOOK_SALT,
|
|
@@ -330,7 +355,7 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
330
355
|
FeeProjectConfig memory feeProjectConfig = getCroptopRevnetConfig();
|
|
331
356
|
|
|
332
357
|
// Approve the basic deployer to configure the project and transfer it.
|
|
333
|
-
core.projects.approve(address(revnet.basic_deployer), FEE_PROJECT_ID);
|
|
358
|
+
core.projects.approve({to: address(revnet.basic_deployer), tokenId: FEE_PROJECT_ID});
|
|
334
359
|
|
|
335
360
|
// Deploy the NANA fee project.
|
|
336
361
|
revnet.basic_deployer
|
package/script/Deploy.s.sol
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
-
import "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
|
|
5
|
-
import "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
|
|
6
|
-
import "@bananapus/suckers-v6/script/helpers/SuckerDeploymentLib.sol";
|
|
4
|
+
import {Hook721Deployment, Hook721DeploymentLib} from "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
|
|
5
|
+
import {CoreDeployment, CoreDeploymentLib} from "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
|
|
6
|
+
import {SuckerDeployment, SuckerDeploymentLib} from "@bananapus/suckers-v6/script/helpers/SuckerDeploymentLib.sol";
|
|
7
7
|
|
|
8
8
|
import {Sphinx} from "@sphinx-labs/contracts/contracts/foundry/SphinxPlugin.sol";
|
|
9
9
|
import {Script} from "forge-std/Script.sol";
|
|
@@ -22,12 +22,17 @@ contract DeployScript is Script, Sphinx {
|
|
|
22
22
|
|
|
23
23
|
// @notice set this to a non-zero value to re-use an existing projectID. Having it set to 0 will deploy a new
|
|
24
24
|
// fee_project.
|
|
25
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
25
26
|
uint256 FEE_PROJECT_ID = 0;
|
|
26
27
|
|
|
27
28
|
/// @notice the salts that are used to deploy the contracts.
|
|
29
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
28
30
|
bytes32 PUBLISHER_SALT = "_PUBLISHER_SALTV6_";
|
|
31
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
29
32
|
bytes32 DEPLOYER_SALT = "_DEPLOYER_SALTV6_";
|
|
33
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
30
34
|
bytes32 PROJECT_OWNER_SALT = "_PROJECT_OWNER_SALTV6_";
|
|
35
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
31
36
|
address TRUSTED_FORWARDER;
|
|
32
37
|
|
|
33
38
|
function configureSphinx() public override {
|
|
@@ -67,51 +72,61 @@ contract DeployScript is Script, Sphinx {
|
|
|
67
72
|
CTPublisher publisher;
|
|
68
73
|
{
|
|
69
74
|
// Perform the check for the publisher.
|
|
70
|
-
(address _publisher, bool _publisherIsDeployed) = _isDeployed(
|
|
71
|
-
PUBLISHER_SALT,
|
|
72
|
-
type(CTPublisher).creationCode,
|
|
73
|
-
abi.encode(core.directory, core.permissions, FEE_PROJECT_ID, TRUSTED_FORWARDER)
|
|
74
|
-
);
|
|
75
|
+
(address _publisher, bool _publisherIsDeployed) = _isDeployed({
|
|
76
|
+
salt: PUBLISHER_SALT,
|
|
77
|
+
creationCode: type(CTPublisher).creationCode,
|
|
78
|
+
arguments: abi.encode(core.directory, core.permissions, FEE_PROJECT_ID, TRUSTED_FORWARDER)
|
|
79
|
+
});
|
|
75
80
|
|
|
76
81
|
// Deploy it if it has not been deployed yet.
|
|
77
82
|
publisher = !_publisherIsDeployed
|
|
78
|
-
? new CTPublisher{salt: PUBLISHER_SALT}(
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
? new CTPublisher{salt: PUBLISHER_SALT}({
|
|
84
|
+
directory: core.directory,
|
|
85
|
+
permissions: core.permissions,
|
|
86
|
+
feeProjectId: FEE_PROJECT_ID,
|
|
87
|
+
trustedForwarder: TRUSTED_FORWARDER
|
|
88
|
+
})
|
|
81
89
|
: CTPublisher(_publisher);
|
|
82
90
|
}
|
|
83
91
|
|
|
84
92
|
CTDeployer deployer;
|
|
85
93
|
{
|
|
86
94
|
// Perform the check for the publisher.
|
|
87
|
-
(address _deployer, bool _deployerIsDeployed) = _isDeployed(
|
|
88
|
-
DEPLOYER_SALT,
|
|
89
|
-
type(CTDeployer).creationCode,
|
|
90
|
-
abi.encode(
|
|
95
|
+
(address _deployer, bool _deployerIsDeployed) = _isDeployed({
|
|
96
|
+
salt: DEPLOYER_SALT,
|
|
97
|
+
creationCode: type(CTDeployer).creationCode,
|
|
98
|
+
arguments: abi.encode(
|
|
91
99
|
core.permissions, core.projects, hook.hook_deployer, publisher, suckers.registry, TRUSTED_FORWARDER
|
|
92
100
|
)
|
|
93
|
-
);
|
|
101
|
+
});
|
|
94
102
|
|
|
95
103
|
// Deploy it if it has not been deployed yet.
|
|
96
104
|
deployer = !_deployerIsDeployed
|
|
97
|
-
? new CTDeployer{salt: DEPLOYER_SALT}(
|
|
98
|
-
|
|
99
|
-
|
|
105
|
+
? new CTDeployer{salt: DEPLOYER_SALT}({
|
|
106
|
+
permissions: core.permissions,
|
|
107
|
+
projects: core.projects,
|
|
108
|
+
deployer: hook.hook_deployer,
|
|
109
|
+
publisher: publisher,
|
|
110
|
+
suckerRegistry: suckers.registry,
|
|
111
|
+
trustedForwarder: TRUSTED_FORWARDER
|
|
112
|
+
})
|
|
100
113
|
: CTDeployer(_deployer);
|
|
101
114
|
}
|
|
102
115
|
|
|
103
116
|
CTProjectOwner owner;
|
|
104
117
|
{
|
|
105
118
|
// Perform the check for the publisher.
|
|
106
|
-
(address _owner, bool _ownerIsDeployed) = _isDeployed(
|
|
107
|
-
PROJECT_OWNER_SALT,
|
|
108
|
-
type(CTProjectOwner).creationCode,
|
|
109
|
-
abi.encode(core.permissions, core.projects, publisher)
|
|
110
|
-
);
|
|
119
|
+
(address _owner, bool _ownerIsDeployed) = _isDeployed({
|
|
120
|
+
salt: PROJECT_OWNER_SALT,
|
|
121
|
+
creationCode: type(CTProjectOwner).creationCode,
|
|
122
|
+
arguments: abi.encode(core.permissions, core.projects, publisher)
|
|
123
|
+
});
|
|
111
124
|
|
|
112
125
|
// Deploy it if it has not been deployed yet.
|
|
113
126
|
owner = !_ownerIsDeployed
|
|
114
|
-
? new CTProjectOwner{salt: PROJECT_OWNER_SALT}(
|
|
127
|
+
? new CTProjectOwner{salt: PROJECT_OWNER_SALT}({
|
|
128
|
+
permissions: core.permissions, projects: core.projects, publisher: publisher
|
|
129
|
+
})
|
|
115
130
|
: CTProjectOwner(_owner);
|
|
116
131
|
}
|
|
117
132
|
}
|
|
@@ -13,12 +13,14 @@ import {CTProjectOwner} from "../../src/CTProjectOwner.sol";
|
|
|
13
13
|
struct CroptopDeployment {
|
|
14
14
|
CTPublisher publisher;
|
|
15
15
|
CTDeployer deployer;
|
|
16
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
16
17
|
CTProjectOwner project_owner;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
library CroptopDeploymentLib {
|
|
20
21
|
// Cheat code address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D.
|
|
21
22
|
address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
|
|
23
|
+
// forge-lint: disable-next-line(screaming-snake-case-const)
|
|
22
24
|
Vm internal constant vm = Vm(VM_ADDRESS);
|
|
23
25
|
|
|
24
26
|
function getDeployment(string memory path) internal returns (CroptopDeployment memory deployment) {
|
|
@@ -41,16 +43,28 @@ library CroptopDeploymentLib {
|
|
|
41
43
|
|
|
42
44
|
function getDeployment(
|
|
43
45
|
string memory path,
|
|
46
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
44
47
|
string memory network_name
|
|
45
48
|
)
|
|
46
49
|
internal
|
|
47
50
|
view
|
|
48
51
|
returns (CroptopDeployment memory deployment)
|
|
49
52
|
{
|
|
50
|
-
deployment.publisher = CTPublisher(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
deployment.publisher = CTPublisher(
|
|
54
|
+
_getDeploymentAddress({
|
|
55
|
+
path: path, project_name: "croptop-core-v6", network_name: network_name, contractName: "CTPublisher"
|
|
56
|
+
})
|
|
57
|
+
);
|
|
58
|
+
deployment.deployer = CTDeployer(
|
|
59
|
+
_getDeploymentAddress({
|
|
60
|
+
path: path, project_name: "croptop-core-v6", network_name: network_name, contractName: "CTDeployer"
|
|
61
|
+
})
|
|
62
|
+
);
|
|
63
|
+
deployment.project_owner = CTProjectOwner(
|
|
64
|
+
_getDeploymentAddress({
|
|
65
|
+
path: path, project_name: "croptop-core-v6", network_name: network_name, contractName: "CTProjectOwner"
|
|
66
|
+
})
|
|
67
|
+
);
|
|
54
68
|
}
|
|
55
69
|
|
|
56
70
|
/// @notice Get the address of a contract that was deployed by the Deploy script.
|
|
@@ -60,7 +74,9 @@ library CroptopDeploymentLib {
|
|
|
60
74
|
/// @return The address of the contract.
|
|
61
75
|
function _getDeploymentAddress(
|
|
62
76
|
string memory path,
|
|
77
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
63
78
|
string memory project_name,
|
|
79
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
64
80
|
string memory network_name,
|
|
65
81
|
string memory contractName
|
|
66
82
|
)
|
|
@@ -69,7 +85,8 @@ library CroptopDeploymentLib {
|
|
|
69
85
|
returns (address)
|
|
70
86
|
{
|
|
71
87
|
string memory deploymentJson =
|
|
72
|
-
|
|
73
|
-
|
|
88
|
+
// forge-lint: disable-next-line(unsafe-cheatcode)
|
|
89
|
+
vm.readFile(string.concat(path, project_name, "/", network_name, "/", contractName, ".json"));
|
|
90
|
+
return stdJson.readAddress({json: deploymentJson, key: ".address"});
|
|
74
91
|
}
|
|
75
92
|
}
|
package/src/CTDeployer.sol
CHANGED
|
@@ -25,7 +25,6 @@ import {JBPayHookSpecification} from "@bananapus/core-v6/src/structs/JBPayHookSp
|
|
|
25
25
|
import {JBPermissionsData} from "@bananapus/core-v6/src/structs/JBPermissionsData.sol";
|
|
26
26
|
import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
|
|
27
27
|
import {JBRulesetConfig} from "@bananapus/core-v6/src/structs/JBRulesetConfig.sol";
|
|
28
|
-
import {JBTerminalConfig} from "@bananapus/core-v6/src/structs/JBTerminalConfig.sol";
|
|
29
28
|
import {JBOwnable} from "@bananapus/ownable-v6/src/JBOwnable.sol";
|
|
30
29
|
import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
31
30
|
import {IJBSuckerRegistry} from "@bananapus/suckers-v6/src/interfaces/IJBSuckerRegistry.sol";
|
|
@@ -269,10 +268,7 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
|
|
|
269
268
|
tokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
270
269
|
contractUri: projectConfig.contractUri,
|
|
271
270
|
tiersConfig: JB721InitTiersConfig({
|
|
272
|
-
tiers: new JB721TierConfig[](0),
|
|
273
|
-
currency: JBCurrencyIds.ETH,
|
|
274
|
-
decimals: 18,
|
|
275
|
-
prices: controller.PRICES()
|
|
271
|
+
tiers: new JB721TierConfig[](0), currency: JBCurrencyIds.ETH, decimals: 18
|
|
276
272
|
}),
|
|
277
273
|
reserveBeneficiary: address(0),
|
|
278
274
|
flags: JB721TiersHookFlags({
|
|
@@ -322,7 +318,7 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
|
|
|
322
318
|
}
|
|
323
319
|
|
|
324
320
|
//transfer to _owner.
|
|
325
|
-
PROJECTS.transferFrom(address(this), owner, projectId);
|
|
321
|
+
PROJECTS.transferFrom({from: address(this), to: owner, tokenId: projectId});
|
|
326
322
|
|
|
327
323
|
// Set permission for the project's owner to do all the NFT things.
|
|
328
324
|
uint8[] memory permissionIds = new uint8[](4);
|
|
@@ -334,7 +330,10 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
|
|
|
334
330
|
PERMISSIONS.setPermissionsFor({
|
|
335
331
|
account: address(this),
|
|
336
332
|
permissionsData: JBPermissionsData({
|
|
337
|
-
operator: address(owner),
|
|
333
|
+
operator: address(owner),
|
|
334
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
335
|
+
projectId: uint64(projectId),
|
|
336
|
+
permissionIds: permissionIds
|
|
338
337
|
})
|
|
339
338
|
});
|
|
340
339
|
}
|
package/src/CTProjectOwner.sol
CHANGED
|
@@ -69,7 +69,10 @@ contract CTProjectOwner is IERC721Receiver, ICTProjectOwner {
|
|
|
69
69
|
PERMISSIONS.setPermissionsFor({
|
|
70
70
|
account: address(this),
|
|
71
71
|
permissionsData: JBPermissionsData({
|
|
72
|
-
operator: address(PUBLISHER),
|
|
72
|
+
operator: address(PUBLISHER),
|
|
73
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
74
|
+
projectId: uint56(tokenId),
|
|
75
|
+
permissionIds: permissionIds
|
|
73
76
|
})
|
|
74
77
|
});
|
|
75
78
|
|
package/src/CTPublisher.sol
CHANGED
|
@@ -26,6 +26,7 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
26
26
|
// --------------------------- custom errors ------------------------- //
|
|
27
27
|
//*********************************************************************//
|
|
28
28
|
|
|
29
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
29
30
|
error CTPublisher_DuplicatePost(bytes32 encodedIPFSUri);
|
|
30
31
|
error CTPublisher_EmptyEncodedIPFSUri();
|
|
31
32
|
error CTPublisher_InsufficientEthSent(uint256 expected, uint256 sent);
|
|
@@ -63,6 +64,7 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
63
64
|
/// @notice The ID of the tier that an IPFS metadata has been saved to.
|
|
64
65
|
/// @custom:param hook The hook for which the tier ID applies.
|
|
65
66
|
/// @custom:param encodedIPFSUri The IPFS URI.
|
|
67
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
66
68
|
mapping(address hook => mapping(bytes32 encodedIPFSUri => uint256)) public override tierIdForEncodedIPFSUriOf;
|
|
67
69
|
|
|
68
70
|
//*********************************************************************//
|
|
@@ -112,6 +114,7 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
112
114
|
/// is returned.
|
|
113
115
|
function tiersFor(
|
|
114
116
|
address hook,
|
|
117
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
115
118
|
bytes32[] memory encodedIPFSUris
|
|
116
119
|
)
|
|
117
120
|
external
|
|
@@ -119,6 +122,7 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
119
122
|
override
|
|
120
123
|
returns (JB721Tier[] memory tiers)
|
|
121
124
|
{
|
|
125
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
122
126
|
uint256 numberOfEncodedIPFSUris = encodedIPFSUris.length;
|
|
123
127
|
|
|
124
128
|
// Initialize the tier array being returned.
|
|
@@ -170,12 +174,16 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
170
174
|
uint256 packed = _packedAllowanceFor[hook][category];
|
|
171
175
|
|
|
172
176
|
// minimum price in bits 0-103 (104 bits).
|
|
177
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
173
178
|
minimumPrice = uint256(uint104(packed));
|
|
174
179
|
// minimum supply in bits 104-135 (32 bits).
|
|
180
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
175
181
|
minimumTotalSupply = uint256(uint32(packed >> 104));
|
|
176
182
|
// maximum supply in bits 136-167 (32 bits).
|
|
183
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
177
184
|
maximumTotalSupply = uint256(uint32(packed >> 136));
|
|
178
185
|
// maximum split percent in bits 168-199 (32 bits).
|
|
186
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
179
187
|
maximumSplitPercent = uint256(uint32(packed >> 168));
|
|
180
188
|
|
|
181
189
|
allowedAddresses = _allowedAddresses[hook][category];
|
|
@@ -445,6 +453,7 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
445
453
|
CTPost memory post = posts[i];
|
|
446
454
|
|
|
447
455
|
// Make sure the post includes an encodedIPFSUri.
|
|
456
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
448
457
|
if (post.encodedIPFSUri == bytes32("")) {
|
|
449
458
|
revert CTPublisher_EmptyEncodedIPFSUri();
|
|
450
459
|
}
|
|
@@ -473,7 +482,7 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
473
482
|
// For existing tiers, use the actual tier price (not the user-supplied post.price)
|
|
474
483
|
// to prevent fee evasion by passing price=0 for an existing tier.
|
|
475
484
|
// slither-disable-next-line calls-loop
|
|
476
|
-
totalPrice += store.tierOf(address(hook), tierId, false).price;
|
|
485
|
+
totalPrice += store.tierOf({hook: address(hook), id: tierId, includeResolvedUri: false}).price;
|
|
477
486
|
}
|
|
478
487
|
}
|
|
479
488
|
}
|
|
@@ -519,7 +528,7 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
519
528
|
}
|
|
520
529
|
|
|
521
530
|
// Make sure the address is allowed to post.
|
|
522
|
-
if (addresses.length != 0 && !_isAllowed(_msgSender(), addresses)) {
|
|
531
|
+
if (addresses.length != 0 && !_isAllowed({addrs: _msgSender(), addresses: addresses})) {
|
|
523
532
|
revert CTPublisher_NotInAllowList(_msgSender(), addresses);
|
|
524
533
|
}
|
|
525
534
|
}
|
|
@@ -73,6 +73,7 @@ interface ICTPublisher {
|
|
|
73
73
|
/// @param hook The hook for which the tier ID applies.
|
|
74
74
|
/// @param encodedIPFSUri The encoded IPFS URI to look up.
|
|
75
75
|
/// @return The tier ID, or 0 if the URI has not been published.
|
|
76
|
+
// forge-lint: disable-next-line(mixed-case-function, mixed-case-variable)
|
|
76
77
|
function tierIdForEncodedIPFSUriOf(address hook, bytes32 encodedIPFSUri) external view returns (uint256);
|
|
77
78
|
|
|
78
79
|
/// @notice Get the tiers for the provided encoded IPFS URIs.
|
|
@@ -80,6 +81,7 @@ interface ICTPublisher {
|
|
|
80
81
|
/// @param encodedIPFSUris The URIs to get tiers of.
|
|
81
82
|
/// @return tiers The tiers that correspond to the provided encoded IPFS URIs. Empty tiers are returned for URIs
|
|
82
83
|
/// without a tier.
|
|
84
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
83
85
|
function tiersFor(address hook, bytes32[] memory encodedIPFSUris) external view returns (JB721Tier[] memory tiers);
|
|
84
86
|
|
|
85
87
|
/// @notice Configure the allowed criteria for publishing new NFTs to a hook.
|
|
@@ -11,6 +11,7 @@ pragma solidity ^0.8.0;
|
|
|
11
11
|
/// @custom:member maximumSplitPercent The maximum split percent (out of JBConstants.SPLITS_TOTAL_PERCENT) that a
|
|
12
12
|
/// poster can set. 0 means splits are not allowed.
|
|
13
13
|
/// @custom:member allowedAddresses A list of addresses that are allowed to post on the category through Croptop.
|
|
14
|
+
// forge-lint: disable-next-line(pascal-case-struct)
|
|
14
15
|
struct CTAllowedPost {
|
|
15
16
|
address hook;
|
|
16
17
|
uint24 category;
|
|
@@ -10,6 +10,7 @@ pragma solidity ^0.8.0;
|
|
|
10
10
|
/// @custom:member maximumSplitPercent The maximum split percent (out of JBConstants.SPLITS_TOTAL_PERCENT) that a
|
|
11
11
|
/// poster can set. 0 means splits are not allowed.
|
|
12
12
|
/// @custom:member allowedAddresses A list of addresses that are allowed to post on the category through Croptop.
|
|
13
|
+
// forge-lint: disable-next-line(pascal-case-struct)
|
|
13
14
|
struct CTDeployerAllowedPost {
|
|
14
15
|
uint24 category;
|
|
15
16
|
uint104 minimumPrice;
|
package/src/structs/CTPost.sol
CHANGED
|
@@ -12,7 +12,9 @@ import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
|
|
|
12
12
|
/// @custom:member splitPercent The percent of the tier's price to route to the splits (out of
|
|
13
13
|
/// JBConstants.SPLITS_TOTAL_PERCENT).
|
|
14
14
|
/// @custom:member splits The splits to route funds to when this tier is minted.
|
|
15
|
+
// forge-lint: disable-next-line(pascal-case-struct)
|
|
15
16
|
struct CTPost {
|
|
17
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
16
18
|
bytes32 encodedIPFSUri;
|
|
17
19
|
uint32 totalSupply;
|
|
18
20
|
uint104 price;
|
|
@@ -11,6 +11,7 @@ import {CTDeployerAllowedPost} from "../structs/CTDeployerAllowedPost.sol";
|
|
|
11
11
|
/// @param name The name of the collection where posts will go.
|
|
12
12
|
/// @param symbol The symbol of the collection where posts will go.
|
|
13
13
|
/// @param salt A salt to use for the deterministic deployment.
|
|
14
|
+
// forge-lint: disable-next-line(pascal-case-struct)
|
|
14
15
|
struct CTProjectConfig {
|
|
15
16
|
JBTerminalConfig[] terminalConfigurations;
|
|
16
17
|
string projectUri;
|
|
@@ -5,6 +5,7 @@ import {JBSuckerDeployerConfig} from "@bananapus/suckers-v6/src/structs/JBSucker
|
|
|
5
5
|
|
|
6
6
|
/// @custom:member deployerConfigurations The information for how to suck tokens to other chains.
|
|
7
7
|
/// @custom:member salt The salt to use for creating suckers so that they use the same address across chains.
|
|
8
|
+
// forge-lint: disable-next-line(pascal-case-struct)
|
|
8
9
|
struct CTSuckerDeploymentConfig {
|
|
9
10
|
JBSuckerDeployerConfig[] deployerConfigurations;
|
|
10
11
|
bytes32 salt;
|
package/test/CTPublisher.t.sol
CHANGED
package/test/Fork.t.sol
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity ^0.8.17;
|
|
3
3
|
|
|
4
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
4
5
|
import "forge-std/Test.sol";
|
|
5
6
|
|
|
6
7
|
// JB core — deploy fresh within fork.
|
|
@@ -33,7 +34,10 @@ import {JBSuckerDeployerConfig} from "@bananapus/suckers-v6/src/structs/JBSucker
|
|
|
33
34
|
import {JBTokenMapping} from "@bananapus/suckers-v6/src/structs/JBTokenMapping.sol";
|
|
34
35
|
import {IJBSuckerDeployer} from "@bananapus/suckers-v6/src/interfaces/IJBSuckerDeployer.sol";
|
|
35
36
|
|
|
37
|
+
import {JBTerminalConfig} from "@bananapus/core-v6/src/structs/JBTerminalConfig.sol";
|
|
38
|
+
|
|
36
39
|
// Croptop — wildcard import pulls in all structs (CTProjectConfig, CTDeployerAllowedPost, etc.).
|
|
40
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
37
41
|
import "./../src/CTDeployer.sol";
|
|
38
42
|
import {CTPublisher} from "./../src/CTPublisher.sol";
|
|
39
43
|
|
|
@@ -158,6 +162,7 @@ contract ForkTest is Test {
|
|
|
158
162
|
// ───────────────────────── Internal deployment helpers
|
|
159
163
|
// ────────────────
|
|
160
164
|
|
|
165
|
+
// forge-lint: disable-next-line(mixed-case-function)
|
|
161
166
|
function _deployJBCore() internal {
|
|
162
167
|
jbPermissions = new JBPermissions(trustedForwarder);
|
|
163
168
|
jbProjects = new JBProjects(multisig, address(0), trustedForwarder);
|
|
@@ -191,7 +196,7 @@ contract ForkTest is Test {
|
|
|
191
196
|
JBAddressRegistry addressRegistry = new JBAddressRegistry();
|
|
192
197
|
|
|
193
198
|
JB721TiersHook hookImpl =
|
|
194
|
-
new JB721TiersHook(jbDirectory, jbPermissions, jbRulesets, store, jbSplits, trustedForwarder);
|
|
199
|
+
new JB721TiersHook(jbDirectory, jbPermissions, jbPrices, jbRulesets, store, jbSplits, trustedForwarder);
|
|
195
200
|
|
|
196
201
|
hookDeployer = new JB721TiersHookDeployer(hookImpl, store, addressRegistry, trustedForwarder);
|
|
197
202
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity ^0.8.17;
|
|
3
3
|
|
|
4
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
4
5
|
import "forge-std/Test.sol";
|
|
5
6
|
import {JBMetadataResolver} from "@bananapus/core-v6/src/libraries/JBMetadataResolver.sol";
|
|
6
7
|
|
|
@@ -22,15 +23,22 @@ contract Test_MetadataGeneration_Unit is Test {
|
|
|
22
23
|
bytes[] memory _datas = new bytes[](10);
|
|
23
24
|
|
|
24
25
|
for (uint256 _i; _i < _ids.length; _i++) {
|
|
26
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
25
27
|
_ids[_i] = bytes4(uint32(_i + 1 * 1000));
|
|
26
28
|
_datas[_i] = abi.encode(
|
|
27
|
-
|
|
29
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
30
|
+
bytes1(uint8(_i + 1)),
|
|
31
|
+
uint32(69),
|
|
32
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
33
|
+
bytes2(uint16(_i + 69)),
|
|
34
|
+
bytes32(uint256(type(uint256).max))
|
|
28
35
|
);
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
bytes memory _additionalPayMetadata = _resolverHelper.createMetadata(_ids, _datas);
|
|
32
39
|
|
|
33
40
|
// The referal to include in the first 32 bytes of the metadata
|
|
41
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
34
42
|
uint256 FEE_PROJECT_ID = 420;
|
|
35
43
|
|
|
36
44
|
// The additional metadata to include
|
|
@@ -65,6 +73,7 @@ contract Test_MetadataGeneration_Unit is Test {
|
|
|
65
73
|
assertTrue(found, "datahook metadata not found");
|
|
66
74
|
assertEq(targetData, abi.encode(true, tierIdsToMint), "datahook not equal");
|
|
67
75
|
|
|
76
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
68
77
|
assertEq(uint256(bytes32(mintMetadata)), FEE_PROJECT_ID, "referal id not equal");
|
|
69
78
|
}
|
|
70
79
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
4
5
|
import "forge-std/Test.sol";
|
|
5
6
|
|
|
6
7
|
import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
|
|
@@ -258,7 +259,9 @@ contract M6_DuplicateUriFeeEvasion is Test {
|
|
|
258
259
|
/// @notice Fuzz test: when two URIs are equal the call must revert with
|
|
259
260
|
/// CTPublisher_DuplicatePost; when they differ it must not.
|
|
260
261
|
function testFuzz_duplicateDetection(bytes32 uri1, bytes32 uri2) public {
|
|
262
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
261
263
|
vm.assume(uri1 != bytes32(""));
|
|
264
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
262
265
|
vm.assume(uri2 != bytes32(""));
|
|
263
266
|
|
|
264
267
|
_configureCategory();
|