@croptop/core-v6 0.0.21 → 0.0.23
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/AUDIT_INSTRUCTIONS.md +2 -2
- package/CHANGE_LOG.md +6 -2
- package/STYLE_GUIDE.md +2 -2
- package/foundry.toml +1 -1
- package/package.json +10 -9
- package/script/ConfigureFeeProject.s.sol +1 -1
- package/script/Deploy.s.sol +22 -3
- package/script/helpers/CroptopDeploymentLib.sol +1 -1
- package/src/CTDeployer.sol +11 -8
- package/src/CTProjectOwner.sol +1 -1
- package/src/CTPublisher.sol +1 -1
- package/test/CTDeployer.t.sol +1 -1
- package/test/CTProjectOwner.t.sol +1 -1
- package/test/CTPublisher.t.sol +1 -1
- package/test/ClaimCollectionOwnership.t.sol +1 -1
- package/test/CroptopAttacks.t.sol +1 -1
- package/test/TestAuditGaps.sol +1 -1
- package/test/audit/{CodexFeeBeneficiaryReentrancy.t.sol → FeeBeneficiaryReentrancy.t.sol} +6 -2
- package/test/regression/DuplicateUriFeeEvasion.t.sol +1 -1
- package/test/regression/FeeEvasion.t.sol +1 -1
- package/test/regression/StaleTierIdMapping.t.sol +1 -1
package/AUDIT_INSTRUCTIONS.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Audit preparation document for experienced Solidity auditors. This repo contains the Croptop content publishing system: three contracts that allow permissioned posting of NFT content as 721 tiers to Juicebox V6 projects, with fee accounting, an allowlist system, and a data hook proxy for cross-chain cash-out interception.
|
|
4
4
|
|
|
5
|
-
Compiler: `solc
|
|
5
|
+
Compiler: `solc 0.8.28`. Framework: Foundry.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -479,7 +479,7 @@ Six Nemesis findings plus two regression-test findings. See `.audit/findings/nem
|
|
|
479
479
|
|
|
480
480
|
## Compiler and Version Info
|
|
481
481
|
|
|
482
|
-
- **Solidity**:
|
|
482
|
+
- **Solidity**: 0.8.28
|
|
483
483
|
- **EVM target**: Cancun
|
|
484
484
|
- **Optimizer**: 200 runs
|
|
485
485
|
- **Dependencies**: OpenZeppelin 5.x, nana-core-v6, nana-721-hook-v6, nana-suckers-v6
|
package/CHANGE_LOG.md
CHANGED
|
@@ -15,7 +15,7 @@ This document describes all changes between `croptop-core` (v5) and `croptop-cor
|
|
|
15
15
|
## 1. Breaking Changes
|
|
16
16
|
|
|
17
17
|
### Solidity Version
|
|
18
|
-
- Compiler version bumped from `0.8.23` to
|
|
18
|
+
- Compiler version bumped from `0.8.23` to `0.8.28` across all implementation contracts (`CTDeployer`, `CTProjectOwner`, `CTPublisher`).
|
|
19
19
|
|
|
20
20
|
### Dependency Namespace Migration
|
|
21
21
|
All imports updated from v5 to v6 namespaces:
|
|
@@ -103,6 +103,10 @@ Posts can now include a `splitPercent` and an array of `splits` (JBSplit[]) that
|
|
|
103
103
|
|
|
104
104
|
## 3. Event Changes
|
|
105
105
|
|
|
106
|
+
Indexer note:
|
|
107
|
+
- event names are stable, but embedded struct payloads changed ABI shape;
|
|
108
|
+
- if your graph decodes `ConfigurePostingCriteria` or `Mint`, update the event-decoding schema for the new `maximumSplitPercent`, `splitPercent`, and `splits` fields.
|
|
109
|
+
|
|
106
110
|
No event signatures were changed. Both versions emit the same two events:
|
|
107
111
|
- `ConfigurePostingCriteria(address indexed hook, CTAllowedPost allowedPost, address caller)` — note that the `CTAllowedPost` struct gained a `maximumSplitPercent` field, which changes the ABI encoding of this event's data.
|
|
108
112
|
- `Mint(uint256 indexed projectId, IJB721TiersHook indexed hook, address indexed nftBeneficiary, address feeBeneficiary, CTPost[] posts, uint256 postValue, uint256 txValue, address caller)` — note that the `CTPost` struct gained `splitPercent` and `splits` fields, which changes the ABI encoding of this event's data.
|
|
@@ -259,7 +263,7 @@ No field changes. Import path updated from `@bananapus/suckers-v5` to `@bananapu
|
|
|
259
263
|
| `JB721TiersHookFlags`: 4 flags | `JB721TiersHookFlags`: 5 flags | Added `issueTokensForSplits` |
|
|
260
264
|
| -- | `CTPublisher_DuplicatePost` | New error |
|
|
261
265
|
| -- | `CTPublisher_SplitPercentExceedsMaximum` | New error |
|
|
262
|
-
| Solidity `0.8.23` | Solidity
|
|
266
|
+
| Solidity `0.8.23` | Solidity `0.8.28` | Compiler bump |
|
|
263
267
|
| `@bananapus/*-v5` | `@bananapus/*-v6` | All dependency namespaces |
|
|
264
268
|
|
|
265
269
|
> **Cross-repo impact**: The `CTPost.splitPercent` and `splits` fields feed directly into `nana-721-hook-v6`'s tier splits system. `nana-suckers-v6` suckers are detected via `SUCKER_REGISTRY.isSuckerOf` for the 0% tax cashout path. `nana-permission-ids-v6` `uint64` projectId width change drove the `CTProjectOwner` cast update.
|
package/STYLE_GUIDE.md
CHANGED
|
@@ -21,7 +21,7 @@ One contract/interface/struct/enum per file. Name the file after the type it con
|
|
|
21
21
|
|
|
22
22
|
```solidity
|
|
23
23
|
// Contracts — pin to exact version
|
|
24
|
-
pragma solidity
|
|
24
|
+
pragma solidity 0.8.28;
|
|
25
25
|
|
|
26
26
|
// Interfaces, structs, enums — caret for forward compatibility
|
|
27
27
|
pragma solidity ^0.8.0;
|
|
@@ -326,7 +326,7 @@ Standard config across all repos:
|
|
|
326
326
|
|
|
327
327
|
```toml
|
|
328
328
|
[profile.default]
|
|
329
|
-
solc = '0.8.
|
|
329
|
+
solc = '0.8.28'
|
|
330
330
|
evm_version = 'cancun'
|
|
331
331
|
optimizer_runs = 200
|
|
332
332
|
libs = ["node_modules", "lib"]
|
package/foundry.toml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@croptop/core-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -16,17 +16,18 @@
|
|
|
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/
|
|
21
|
-
"@bananapus/
|
|
22
|
-
"@bananapus/
|
|
23
|
-
"@bananapus/
|
|
24
|
-
"@bananapus/
|
|
25
|
-
"@bananapus/
|
|
19
|
+
"@bananapus/721-hook-v6": "^0.0.22",
|
|
20
|
+
"@bananapus/address-registry-v6": "^0.0.16",
|
|
21
|
+
"@bananapus/buyback-hook-v6": "^0.0.22",
|
|
22
|
+
"@bananapus/core-v6": "^0.0.28",
|
|
23
|
+
"@bananapus/ownable-v6": "^0.0.15",
|
|
24
|
+
"@bananapus/permission-ids-v6": "^0.0.14",
|
|
25
|
+
"@bananapus/router-terminal-v6": "^0.0.21",
|
|
26
|
+
"@bananapus/suckers-v6": "^0.0.18",
|
|
26
27
|
"@openzeppelin/contracts": "^5.6.1"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
|
-
"@rev-net/core-v6": "^0.0.
|
|
30
|
+
"@rev-net/core-v6": "^0.0.17",
|
|
30
31
|
"@sphinx-labs/plugins": "^0.33.1"
|
|
31
32
|
}
|
|
32
33
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {Hook721Deployment, Hook721DeploymentLib} from "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
|
|
5
5
|
import {CoreDeployment, CoreDeploymentLib} from "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
|
package/script/Deploy.s.sol
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {Hook721Deployment, Hook721DeploymentLib} from "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
|
|
5
5
|
import {CoreDeployment, CoreDeploymentLib} from "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
|
|
@@ -64,9 +64,28 @@ contract DeployScript is Script, Sphinx {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
function deploy() public sphinx {
|
|
67
|
-
// If the fee project id is 0, then we want to deploy a new fee project
|
|
67
|
+
// If the fee project id is 0, then we want to deploy a new fee project — but only if the publisher
|
|
68
|
+
// singleton doesn't already exist. Re-running with FEE_PROJECT_ID=0 would create a second fee project
|
|
69
|
+
// with different CREATE2 addresses, stranding the previous suite.
|
|
68
70
|
if (FEE_PROJECT_ID == 0) {
|
|
69
|
-
|
|
71
|
+
// Check if the publisher already exists by scanning existing project IDs.
|
|
72
|
+
uint256 _existingCount = core.projects.count();
|
|
73
|
+
bool _found;
|
|
74
|
+
for (uint256 _candidateId = 1; _candidateId <= _existingCount; _candidateId++) {
|
|
75
|
+
(, bool _exists) = _isDeployed({
|
|
76
|
+
salt: PUBLISHER_SALT,
|
|
77
|
+
creationCode: type(CTPublisher).creationCode,
|
|
78
|
+
arguments: abi.encode(core.directory, core.permissions, _candidateId, TRUSTED_FORWARDER)
|
|
79
|
+
});
|
|
80
|
+
if (_exists) {
|
|
81
|
+
FEE_PROJECT_ID = _candidateId;
|
|
82
|
+
_found = true;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (!_found) {
|
|
87
|
+
FEE_PROJECT_ID = core.projects.createFor(safeAddress());
|
|
88
|
+
}
|
|
70
89
|
}
|
|
71
90
|
|
|
72
91
|
CTPublisher publisher;
|
package/src/CTDeployer.sol
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
|
|
5
5
|
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
|
|
@@ -225,9 +225,12 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
|
|
|
225
225
|
//*********************************************************************//
|
|
226
226
|
|
|
227
227
|
/// @notice Claim ownership of the collection.
|
|
228
|
-
/// @dev
|
|
229
|
-
///
|
|
230
|
-
///
|
|
228
|
+
/// @dev Two-step ownership transfer process:
|
|
229
|
+
/// Step 1 (this function): Transfers hook ownership to the project via `transferOwnershipToProject()`.
|
|
230
|
+
/// After this call, `hook.owner()` resolves dynamically through `PROJECTS.ownerOf(projectId)`.
|
|
231
|
+
/// Step 2 (caller must do separately): The project owner grants CTPublisher the `ADJUST_721_TIERS` permission
|
|
232
|
+
/// for the project so that `mintFrom()` continues to work.
|
|
233
|
+
/// Without the Step 2 permission grant, all subsequent posts will revert. This cannot be done atomically here
|
|
231
234
|
/// because after transferring ownership to the project, this contract no longer has authority to set permissions
|
|
232
235
|
/// on the project's behalf.
|
|
233
236
|
/// @param hook The hook to claim ownership of.
|
|
@@ -283,7 +286,6 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
|
|
|
283
286
|
tiersConfig: JB721InitTiersConfig({
|
|
284
287
|
tiers: new JB721TierConfig[](0), currency: JBCurrencyIds.ETH, decimals: 18
|
|
285
288
|
}),
|
|
286
|
-
reserveBeneficiary: address(0),
|
|
287
289
|
flags: JB721TiersHookFlags({
|
|
288
290
|
noNewTiersWithReserves: false,
|
|
289
291
|
noNewTiersWithVotes: false,
|
|
@@ -337,9 +339,10 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
|
|
|
337
339
|
PROJECTS.transferFrom({from: address(this), to: owner, tokenId: projectId});
|
|
338
340
|
|
|
339
341
|
// Set permission for the project's owner to do all the NFT things.
|
|
340
|
-
// These permissions are granted from CTDeployer (address(this))
|
|
341
|
-
//
|
|
342
|
-
//
|
|
342
|
+
// These permissions are granted from CTDeployer (address(this)) to the initial owner.
|
|
343
|
+
// The hook checks permissions against hook.owner(), which after claimCollectionOwnershipOf() resolves
|
|
344
|
+
// dynamically via PROJECTS.ownerOf(projectId). Before claiming, CTDeployer is the static hook owner,
|
|
345
|
+
// so these permissions allow the project owner to manage tiers through CTDeployer.
|
|
343
346
|
uint8[] memory permissionIds = new uint8[](4);
|
|
344
347
|
permissionIds[0] = JBPermissionIds.ADJUST_721_TIERS;
|
|
345
348
|
permissionIds[1] = JBPermissionIds.SET_721_METADATA;
|
package/src/CTProjectOwner.sol
CHANGED
package/src/CTPublisher.sol
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {IJB721TiersHook} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHook.sol";
|
|
5
5
|
import {IJB721TiersHookStore} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHookStore.sol";
|
package/test/CTDeployer.t.sol
CHANGED
package/test/CTPublisher.t.sol
CHANGED
package/test/TestAuditGaps.sol
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
5
5
|
import "forge-std/Test.sol";
|
|
@@ -19,6 +19,7 @@ import {CTAllowedPost} from "../../src/structs/CTAllowedPost.sol";
|
|
|
19
19
|
import {CTPost} from "../../src/structs/CTPost.sol";
|
|
20
20
|
|
|
21
21
|
contract MockPermissions is IJBPermissions {
|
|
22
|
+
// forge-lint: disable-next-line(mixed-case-function)
|
|
22
23
|
function WILDCARD_PROJECT_ID() external pure returns (uint256) {
|
|
23
24
|
return 0;
|
|
24
25
|
}
|
|
@@ -136,8 +137,11 @@ contract FeeTerminalRecorder {
|
|
|
136
137
|
}
|
|
137
138
|
|
|
138
139
|
contract ReentrantProjectTerminal {
|
|
140
|
+
// forge-lint: disable-next-line(screaming-snake-case-immutable)
|
|
139
141
|
CTPublisher public immutable publisher;
|
|
142
|
+
// forge-lint: disable-next-line(screaming-snake-case-immutable)
|
|
140
143
|
IJB721TiersHook public immutable hook;
|
|
144
|
+
// forge-lint: disable-next-line(screaming-snake-case-immutable)
|
|
141
145
|
address public immutable attackerFeeBeneficiary;
|
|
142
146
|
bool internal entered;
|
|
143
147
|
|
|
@@ -182,7 +186,7 @@ contract ReentrantProjectTerminal {
|
|
|
182
186
|
receive() external payable {}
|
|
183
187
|
}
|
|
184
188
|
|
|
185
|
-
contract
|
|
189
|
+
contract FeeBeneficiaryReentrancyTest is Test {
|
|
186
190
|
MockPermissions permissions;
|
|
187
191
|
MockDirectory directory;
|
|
188
192
|
MockStore store;
|