@bannynet/core-v6 0.0.6 → 0.0.7
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/RISKS.md +10 -10
- package/STYLE_GUIDE.md +130 -54
- package/foundry.toml +3 -3
- package/package.json +8 -6
- package/remappings.txt +1 -1
- package/script/Add.Denver.s.sol +1 -1
- package/script/Deploy.s.sol +6 -7
- package/script/Drop1.s.sol +1 -1
- package/script/helpers/BannyverseDeploymentLib.sol +1 -1
- package/script/outfit_drop/generate-migration.js +2 -2
- package/test/Banny721TokenUriResolver.t.sol +1 -4
- package/test/BannyAttacks.t.sol +1 -4
- package/test/DecorateFlow.t.sol +1 -4
- package/test/Fork.t.sol +4 -12
- package/test/regression/{L58_ArrayLengthValidation.t.sol → ArrayLengthValidation.t.sol} +2 -3
- package/test/regression/{L57_BodyCategoryValidation.t.sol → BodyCategoryValidation.t.sol} +2 -2
- package/test/regression/{L62_BurnedTokenCheck.t.sol → BurnedTokenCheck.t.sol} +2 -2
- package/test/regression/{I25_CEIReorder.t.sol → CEIReorder.t.sol} +2 -2
- package/test/regression/{L59_ClearMetadata.t.sol → ClearMetadata.t.sol} +2 -2
- package/test/regression/{L56_MsgSenderEvents.t.sol → MsgSenderEvents.t.sol} +2 -2
- package/test/regression/{M8_RemovedTierDesync.t.sol → RemovedTierDesync.t.sol} +2 -2
package/RISKS.md
CHANGED
|
@@ -17,7 +17,7 @@ Deep implementation-level risk analysis of `Banny721TokenUriResolver`. All line
|
|
|
17
17
|
- Severity: **CRITICAL** | Tested: **YES** (Fork.t.sol lines 931-956, BannyAttacks.t.sol)
|
|
18
18
|
- When outfits or backgrounds are equipped via `decorateBannyWith()`, the NFT is transferred to the resolver contract via `safeTransferFrom` (lines 1191, 1325, 1366). The resolver holds custody until the body owner unequips.
|
|
19
19
|
- **Impact**: If the resolver contract has a bug, equipped NFTs could become permanently locked. All value of equipped outfits depends on the resolver's correctness.
|
|
20
|
-
- **Mitigation**: ReentrancyGuard on `decorateBannyWith` (line 977). `_tryTransferFrom` (lines 1375-1378) uses try-catch for returning old outfits, so burned/removed-tier tokens do not block redecoration. Tested in regression
|
|
20
|
+
- **Mitigation**: ReentrancyGuard on `decorateBannyWith` (line 977). `_tryTransferFrom` (lines 1375-1378) uses try-catch for returning old outfits, so burned/removed-tier tokens do not block redecoration. Tested in regression BurnedTokenCheck.t.sol.
|
|
21
21
|
- **Residual risk**: The resolver is not upgradeable. If a critical bug is found post-deployment, there is no admin mechanism to rescue stuck NFTs. The owner has no function to force-return assets.
|
|
22
22
|
|
|
23
23
|
**Risk: Body transfer transfers outfit control**
|
|
@@ -47,7 +47,7 @@ Deep implementation-level risk analysis of `Banny721TokenUriResolver`. All line
|
|
|
47
47
|
- Severity: **HIGH** | Tested: **YES** (Fork.t.sol lines 853-925)
|
|
48
48
|
- `decorateBannyWith` calls `safeTransferFrom` (lines 1191, 1325, 1366) which triggers `onERC721Received` on receiving contracts. A malicious hook could attempt to re-enter `decorateBannyWith` during these callbacks.
|
|
49
49
|
- **Mitigation**: `decorateBannyWith` has `nonReentrant` modifier (line 977, OpenZeppelin ReentrancyGuard). Tested with a purpose-built `ReentrantHook` (Fork.t.sol line 46) that re-enters during `safeTransferFrom`. The reentrancy attempt is caught and silently fails via try-catch.
|
|
50
|
-
- **CEI pattern**: Background replacement follows Checks-Effects-Interactions. State updates (`_attachedBackgroundIdOf`, `_userOf`) happen at lines 1181-1182 before external transfers at lines 1186-1192. Verified in regression
|
|
50
|
+
- **CEI pattern**: Background replacement follows Checks-Effects-Interactions. State updates (`_attachedBackgroundIdOf`, `_userOf`) happen at lines 1181-1182 before external transfers at lines 1186-1192. Verified in regression CEIReorder.t.sol.
|
|
51
51
|
- **Residual risk**: None identified. The ReentrancyGuard provides a hard block, and CEI ordering provides defense-in-depth.
|
|
52
52
|
|
|
53
53
|
### MEDIUM -- Hook Trust Boundary
|
|
@@ -60,7 +60,7 @@ Deep implementation-level risk analysis of `Banny721TokenUriResolver`. All line
|
|
|
60
60
|
- **Residual risk**: If a user interacts with a malicious hook, they could lose the NFTs they equip on that hook. The resolver cannot distinguish legitimate from malicious hooks.
|
|
61
61
|
|
|
62
62
|
**Risk: Removed tier desynchronization**
|
|
63
|
-
- Severity: **MEDIUM** | Tested: **YES** (
|
|
63
|
+
- Severity: **MEDIUM** | Tested: **YES** (RemovedTierDesync.t.sol, 6 test cases)
|
|
64
64
|
- When a tier is removed from the JB721TiersHookStore, `_productOfTokenId()` returns a zeroed struct (category=0, id=0). Previously equipped outfits from that tier have category 0 in the redecoration loop.
|
|
65
65
|
- **Impact**: Without proper handling, removed-tier outfits could cause the `_decorateBannyWithOutfits` loop to malfunction -- the category-0 entries would be processed incorrectly by the `while` loop (line 1297).
|
|
66
66
|
- **Mitigation**: The current code handles this correctly. Category-0 entries are processed and transferred out by the while loop. The `_tryTransferFrom` (line 1303, 1345) silently handles cases where the token no longer exists. Six regression tests verify: first/middle/last tier removal, all tiers removed, replacement after removal, and two consecutive removed tiers.
|
|
@@ -133,13 +133,13 @@ Deep implementation-level risk analysis of `Banny721TokenUriResolver`. All line
|
|
|
133
133
|
| `DecorateFlow.t.sol` | ~40 | L18 vulnerability proof, multi-body outfit reuse, authorization flows, three-party interactions, background replacement, edge cases |
|
|
134
134
|
| `BannyAttacks.t.sol` | 8 | Adversarial: outfit reuse, lock bypass, category conflicts, unauthorized decoration, out-of-order categories, body/background as outfit |
|
|
135
135
|
| `Fork.t.sol` | 40+ | E2E against real JB infrastructure: full lifecycle, multi-actor, reentrancy (ReentrantHook), griefing/front-running, cross-hook isolation, redressing cycles |
|
|
136
|
-
| `regression/
|
|
137
|
-
| `regression/
|
|
138
|
-
| `regression/
|
|
139
|
-
| `regression/
|
|
140
|
-
| `regression/
|
|
141
|
-
| `regression/
|
|
142
|
-
| `regression/
|
|
136
|
+
| `regression/CEIReorder.t.sol` | 3 | CEI ordering in background replacement |
|
|
137
|
+
| `regression/RemovedTierDesync.t.sol` | 6 | Removed tier handling during redecoration |
|
|
138
|
+
| `regression/ArrayLengthValidation.t.sol` | 3 | Array length mismatch reverts |
|
|
139
|
+
| `regression/BodyCategoryValidation.t.sol` | 2 | Non-body token as bannyBodyId rejection |
|
|
140
|
+
| `regression/MsgSenderEvents.t.sol` | 4 | Events emit `_msgSender()` not `msg.sender` |
|
|
141
|
+
| `regression/BurnedTokenCheck.t.sol` | 2 | Burned equipped tokens do not lock the body |
|
|
142
|
+
| `regression/ClearMetadata.t.sol` | 2 | setMetadata can clear fields to empty strings |
|
|
143
143
|
|
|
144
144
|
## Untested Areas
|
|
145
145
|
|
package/STYLE_GUIDE.md
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
How we write Solidity and organize repos across the Juicebox V6 ecosystem. `nana-core-v6` is the gold standard — when in doubt, match what it does.
|
|
4
4
|
|
|
5
|
-
**This repo's deviations:** `via_ir = true` (stack depth). Package scope: `@bannynet/`.
|
|
6
|
-
|
|
7
5
|
## File Organization
|
|
8
6
|
|
|
9
7
|
```
|
|
@@ -19,8 +17,6 @@ src/
|
|
|
19
17
|
|
|
20
18
|
One contract/interface/struct/enum per file. Name the file after the type it contains.
|
|
21
19
|
|
|
22
|
-
**Structs, enums, libraries, and interfaces always go in their subdirectories** (`src/structs/`, `src/enums/`, `src/libraries/`, `src/interfaces/`) — never inline in contract files or placed in `src/` root. This keeps type definitions discoverable and import paths consistent across repos.
|
|
23
|
-
|
|
24
20
|
## Pragma Versions
|
|
25
21
|
|
|
26
22
|
```solidity
|
|
@@ -108,14 +104,6 @@ contract JBExample is JBPermissioned, IJBExample {
|
|
|
108
104
|
// -------------------------- constructor ---------------------------- //
|
|
109
105
|
//*********************************************************************//
|
|
110
106
|
|
|
111
|
-
//*********************************************************************//
|
|
112
|
-
// ---------------------- receive / fallback ------------------------- //
|
|
113
|
-
//*********************************************************************//
|
|
114
|
-
|
|
115
|
-
//*********************************************************************//
|
|
116
|
-
// --------------------------- modifiers ----------------------------- //
|
|
117
|
-
//*********************************************************************//
|
|
118
|
-
|
|
119
107
|
//*********************************************************************//
|
|
120
108
|
// ---------------------- external transactions ---------------------- //
|
|
121
109
|
//*********************************************************************//
|
|
@@ -143,28 +131,23 @@ contract JBExample is JBPermissioned, IJBExample {
|
|
|
143
131
|
```
|
|
144
132
|
|
|
145
133
|
**Section order:**
|
|
146
|
-
1.
|
|
147
|
-
2.
|
|
148
|
-
3.
|
|
149
|
-
4.
|
|
150
|
-
5.
|
|
151
|
-
6.
|
|
152
|
-
7.
|
|
153
|
-
8.
|
|
154
|
-
9.
|
|
155
|
-
10.
|
|
156
|
-
11.
|
|
157
|
-
12.
|
|
158
|
-
13.
|
|
159
|
-
14.
|
|
160
|
-
15. Internal helpers
|
|
161
|
-
16. Internal views
|
|
162
|
-
17. Private helpers
|
|
134
|
+
1. Custom errors
|
|
135
|
+
2. Public constants
|
|
136
|
+
3. Internal constants
|
|
137
|
+
4. Public immutable stored properties
|
|
138
|
+
5. Internal immutable stored properties
|
|
139
|
+
6. Public stored properties
|
|
140
|
+
7. Internal stored properties
|
|
141
|
+
8. Constructor
|
|
142
|
+
9. External transactions
|
|
143
|
+
10. External views
|
|
144
|
+
11. Public transactions
|
|
145
|
+
12. Internal helpers
|
|
146
|
+
13. Internal views
|
|
147
|
+
14. Private helpers
|
|
163
148
|
|
|
164
149
|
Functions are alphabetized within each section.
|
|
165
150
|
|
|
166
|
-
**Events:** Events are declared in interfaces only, never in implementation contracts. Implementations inherit events from their interface and emit them unqualified. This keeps the ABI definition in one place and allows tests to use interface-qualified event expectations (e.g., `emit IJBController.LaunchProject(...)`).
|
|
167
|
-
|
|
168
151
|
## Interface Structure
|
|
169
152
|
|
|
170
153
|
```solidity
|
|
@@ -333,13 +316,9 @@ Standard config across all repos:
|
|
|
333
316
|
solc = '0.8.26'
|
|
334
317
|
evm_version = 'cancun'
|
|
335
318
|
optimizer_runs = 200
|
|
336
|
-
via_ir = true
|
|
337
319
|
libs = ["node_modules", "lib"]
|
|
338
320
|
fs_permissions = [{ access = "read-write", path = "./"}]
|
|
339
321
|
|
|
340
|
-
[profile.ci_sizes]
|
|
341
|
-
optimizer_runs = 200
|
|
342
|
-
|
|
343
322
|
[fuzz]
|
|
344
323
|
runs = 4096
|
|
345
324
|
|
|
@@ -354,10 +333,14 @@ multiline_func_header = "all"
|
|
|
354
333
|
wrap_comments = true
|
|
355
334
|
```
|
|
356
335
|
|
|
357
|
-
**
|
|
358
|
-
- `
|
|
359
|
-
- `
|
|
360
|
-
|
|
336
|
+
**Optional sections (add only when needed):**
|
|
337
|
+
- `[rpc_endpoints]` — repos with fork tests. Maps named endpoints to env vars (e.g. `ethereum = "${RPC_ETHEREUM_MAINNET}"`).
|
|
338
|
+
- `[profile.ci_sizes]` — only when CI needs different optimizer settings than defaults for the size check step (e.g. `optimizer_runs = 200` when the default profile uses a lower value).
|
|
339
|
+
|
|
340
|
+
**Common variations:**
|
|
341
|
+
- `via_ir = true` when hitting stack-too-deep
|
|
342
|
+
- `optimizer = false` when optimization causes stack-too-deep
|
|
343
|
+
- `optimizer_runs` reduced when deep struct nesting causes stack-too-deep at 200 runs
|
|
361
344
|
|
|
362
345
|
### CI Workflows
|
|
363
346
|
|
|
@@ -390,7 +373,7 @@ jobs:
|
|
|
390
373
|
env:
|
|
391
374
|
RPC_ETHEREUM_MAINNET: ${{ secrets.RPC_ETHEREUM_MAINNET }}
|
|
392
375
|
- name: Check contract sizes
|
|
393
|
-
run:
|
|
376
|
+
run: forge build --sizes --skip "*/test/**" --skip "*/script/**" --skip SphinxUtils
|
|
394
377
|
```
|
|
395
378
|
|
|
396
379
|
**lint.yml:**
|
|
@@ -412,11 +395,60 @@ jobs:
|
|
|
412
395
|
run: forge fmt --check
|
|
413
396
|
```
|
|
414
397
|
|
|
398
|
+
**slither.yml** (repos with `src/` contracts only):
|
|
399
|
+
```yaml
|
|
400
|
+
name: slither
|
|
401
|
+
on:
|
|
402
|
+
pull_request:
|
|
403
|
+
branches:
|
|
404
|
+
- main
|
|
405
|
+
push:
|
|
406
|
+
branches:
|
|
407
|
+
- main
|
|
408
|
+
jobs:
|
|
409
|
+
analyze:
|
|
410
|
+
runs-on: ubuntu-latest
|
|
411
|
+
steps:
|
|
412
|
+
- uses: actions/checkout@v4
|
|
413
|
+
with:
|
|
414
|
+
submodules: recursive
|
|
415
|
+
- uses: actions/setup-node@v4
|
|
416
|
+
with:
|
|
417
|
+
node-version: latest
|
|
418
|
+
- name: Install npm dependencies
|
|
419
|
+
run: npm install --omit=dev
|
|
420
|
+
- name: Install Foundry
|
|
421
|
+
uses: foundry-rs/foundry-toolchain@v1
|
|
422
|
+
- name: Run slither
|
|
423
|
+
uses: crytic/slither-action@v0.3.1
|
|
424
|
+
with:
|
|
425
|
+
slither-config: slither-ci.config.json
|
|
426
|
+
fail-on: medium
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
**slither-ci.config.json:**
|
|
430
|
+
```json
|
|
431
|
+
{
|
|
432
|
+
"detectors_to_exclude": "timestamp,uninitialized-local,naming-convention,solc-version,shadowing-local",
|
|
433
|
+
"exclude_informational": true,
|
|
434
|
+
"exclude_low": false,
|
|
435
|
+
"exclude_medium": false,
|
|
436
|
+
"exclude_high": false,
|
|
437
|
+
"disable_color": false,
|
|
438
|
+
"filter_paths": "(mocks/|test/|node_modules/|lib/)",
|
|
439
|
+
"legacy_ast": false
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
**Variations:**
|
|
444
|
+
- Deployer-only repos (no `src/`, only `script/`) skip slither entirely — the action's internal `forge build` skips `test/` and `script/` by default, leaving nothing to compile.
|
|
445
|
+
- Use inline `// slither-disable-next-line <detector>` to suppress known false positives rather than adding to `detectors_to_exclude` in the config. The comment must be on the line immediately before the flagged expression.
|
|
446
|
+
|
|
415
447
|
### package.json
|
|
416
448
|
|
|
417
449
|
```json
|
|
418
450
|
{
|
|
419
|
-
"name": "@
|
|
451
|
+
"name": "@bananapus/package-name-v6",
|
|
420
452
|
"version": "x.x.x",
|
|
421
453
|
"license": "MIT",
|
|
422
454
|
"repository": { "type": "git", "url": "git+https://github.com/Org/repo.git" },
|
|
@@ -436,13 +468,62 @@ jobs:
|
|
|
436
468
|
|
|
437
469
|
### remappings.txt
|
|
438
470
|
|
|
439
|
-
Every repo has a `remappings.txt
|
|
471
|
+
Every repo has a `remappings.txt` as the **single source of truth** for import remappings. Never add remappings to `foundry.toml`.
|
|
472
|
+
|
|
473
|
+
**Principle:** Import paths in Solidity source must match npm package names exactly. With `libs = ["node_modules", "lib"]`, Foundry auto-resolves `@scope/package/path/File.sol` → `node_modules/@scope/package/path/File.sol`. No remapping needed for packages installed as real directories.
|
|
474
|
+
|
|
475
|
+
**Note:** Auto-resolution does **not** work for symlinked packages (e.g. npm workspace links). Workspace repos like `deploy-all-v6` and `nana-cli-v6` need explicit `@scope/package/=node_modules/@scope/package/` remappings for each symlinked dependency.
|
|
476
|
+
|
|
477
|
+
**Minimal content** (most repos):
|
|
440
478
|
|
|
441
479
|
```
|
|
442
|
-
|
|
480
|
+
forge-std/=lib/forge-std/src/
|
|
443
481
|
```
|
|
444
482
|
|
|
445
|
-
|
|
483
|
+
Only add extra remappings for:
|
|
484
|
+
- **`forge-std`** — always needed (git submodule with `src/` subdirectory)
|
|
485
|
+
- **Repo-specific `lib/` submodules** that have no npm package (e.g., `hookmate/=lib/hookmate/src/`)
|
|
486
|
+
- **Symlinked npm packages** — need explicit `@scope/package/=node_modules/@scope/package/` entries
|
|
487
|
+
- **Nested transitive deps** — e.g., `@chainlink/contracts-ccip/` nested inside `@bananapus/suckers-v6/node_modules/`
|
|
488
|
+
|
|
489
|
+
**Never add remappings for:**
|
|
490
|
+
- npm packages that match their import path and are installed as real directories — they auto-resolve
|
|
491
|
+
- Short-form aliases (e.g., `@bananapus/core/` → `@bananapus/core-v6/src/`) — fix the import instead
|
|
492
|
+
- Packages available via npm that are also git submodules — remove the submodule, use npm
|
|
493
|
+
|
|
494
|
+
**Import path convention:**
|
|
495
|
+
|
|
496
|
+
| Package | Import path | Resolves to |
|
|
497
|
+
|---------|------------|-------------|
|
|
498
|
+
| `@bananapus/core-v6` | `@bananapus/core-v6/src/libraries/JBConstants.sol` | `node_modules/@bananapus/core-v6/src/...` |
|
|
499
|
+
| `@openzeppelin/contracts` | `@openzeppelin/contracts/token/ERC20/IERC20.sol` | `node_modules/@openzeppelin/contracts/...` |
|
|
500
|
+
| `@uniswap/v4-core` | `@uniswap/v4-core/src/interfaces/IPoolManager.sol` | `node_modules/@uniswap/v4-core/src/...` |
|
|
501
|
+
|
|
502
|
+
### Linting
|
|
503
|
+
|
|
504
|
+
Solar (Foundry's built-in linter) runs automatically during `forge build`. It scans all `.sol` files in `libs` directories, including `node_modules`.
|
|
505
|
+
|
|
506
|
+
**All test helpers must use relative imports** (e.g. `../../src/structs/JBRuleset.sol`), not bare `src/` imports. This ensures solar can resolve paths when the helper is consumed via npm in downstream repos.
|
|
507
|
+
|
|
508
|
+
### Fork Tests
|
|
509
|
+
|
|
510
|
+
Fork tests use named RPC endpoints defined in `[rpc_endpoints]` of `foundry.toml`. No skip guards — fork tests should hard-fail if the RPC endpoint is unavailable, making CI failures explicit.
|
|
511
|
+
|
|
512
|
+
```solidity
|
|
513
|
+
function setUp() public {
|
|
514
|
+
vm.createSelectFork("ethereum");
|
|
515
|
+
// ... setup code
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
The endpoint name (e.g. `"ethereum"`) maps to an env var via `foundry.toml`:
|
|
520
|
+
|
|
521
|
+
```toml
|
|
522
|
+
[rpc_endpoints]
|
|
523
|
+
ethereum = "${RPC_ETHEREUM_MAINNET}"
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
For multi-chain fork tests, add all needed endpoints.
|
|
446
527
|
|
|
447
528
|
### Formatting
|
|
448
529
|
|
|
@@ -453,15 +534,6 @@ Run `forge fmt` before committing. The `[fmt]` config in `foundry.toml` enforces
|
|
|
453
534
|
|
|
454
535
|
CI checks formatting via `forge fmt --check`.
|
|
455
536
|
|
|
456
|
-
### CI Secrets
|
|
457
|
-
|
|
458
|
-
| Secret | Purpose |
|
|
459
|
-
|--------|--------|
|
|
460
|
-
| `NPM_TOKEN` | npm publish access (used by `publish.yml`) |
|
|
461
|
-
| `RPC_ETHEREUM_MAINNET` | Ethereum mainnet RPC URL for fork tests (used by `test.yml`) |
|
|
462
|
-
|
|
463
|
-
Fork tests require `RPC_ETHEREUM_MAINNET` — they fail if it's missing.
|
|
464
|
-
|
|
465
537
|
### Branching
|
|
466
538
|
|
|
467
539
|
- `main` is the primary branch
|
|
@@ -479,4 +551,8 @@ Fork tests require `RPC_ETHEREUM_MAINNET` — they fail if it's missing.
|
|
|
479
551
|
|
|
480
552
|
### Contract Size Checks
|
|
481
553
|
|
|
482
|
-
CI runs `
|
|
554
|
+
CI runs `forge build --sizes` to catch contracts approaching the 24KB limit. When the repo's default `optimizer_runs` differs from what you want for size checking, use `FOUNDRY_PROFILE=ci_sizes forge build --sizes` with a `[profile.ci_sizes]` section in `foundry.toml`.
|
|
555
|
+
|
|
556
|
+
## Repo-Specific Deviations
|
|
557
|
+
|
|
558
|
+
None. This repo follows the standard configuration exactly.
|
package/foundry.toml
CHANGED
|
@@ -6,9 +6,6 @@ via_ir = true
|
|
|
6
6
|
libs = ["node_modules", "lib"]
|
|
7
7
|
fs_permissions = [{ access = "read-write", path = "./"}]
|
|
8
8
|
|
|
9
|
-
[profile.ci_sizes]
|
|
10
|
-
optimizer_runs = 200
|
|
11
|
-
|
|
12
9
|
[fuzz]
|
|
13
10
|
runs = 4096
|
|
14
11
|
|
|
@@ -21,3 +18,6 @@ fail_on_revert = false
|
|
|
21
18
|
number_underscore = "thousands"
|
|
22
19
|
multiline_func_header = "all"
|
|
23
20
|
wrap_comments = true
|
|
21
|
+
|
|
22
|
+
[rpc_endpoints]
|
|
23
|
+
ethereum = "${RPC_ETHEREUM_MAINNET}"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bannynet/core-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -20,15 +20,17 @@
|
|
|
20
20
|
"artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'banny-core-v6'"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@bananapus/721-hook-v6": "^0.0.
|
|
24
|
-
"@bananapus/core-v6": "^0.0.
|
|
23
|
+
"@bananapus/721-hook-v6": "^0.0.14",
|
|
24
|
+
"@bananapus/core-v6": "^0.0.15",
|
|
25
|
+
"@bananapus/permission-ids-v6": "^0.0.7",
|
|
25
26
|
"@bananapus/router-terminal-v6": "^0.0.6",
|
|
26
27
|
"@bananapus/suckers-v6": "^0.0.7",
|
|
27
|
-
"@
|
|
28
|
-
"@
|
|
28
|
+
"@croptop/core-v6": "^0.0.12",
|
|
29
|
+
"@openzeppelin/contracts": "^5.6.1",
|
|
30
|
+
"@rev-net/core-v6": "^0.0.10",
|
|
29
31
|
"keccak": "^3.0.4"
|
|
30
32
|
},
|
|
31
33
|
"devDependencies": {
|
|
32
34
|
"@sphinx-labs/plugins": "^0.33.2"
|
|
33
35
|
}
|
|
34
|
-
}
|
|
36
|
+
}
|
package/remappings.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
forge-std/=lib/forge-std/src/
|
package/script/Add.Denver.s.sol
CHANGED
|
@@ -9,7 +9,7 @@ import "./helpers/BannyverseDeploymentLib.sol";
|
|
|
9
9
|
import "@rev-net/core-v6/script/helpers/RevnetCoreDeploymentLib.sol";
|
|
10
10
|
import "@openzeppelin/contracts/utils/Strings.sol";
|
|
11
11
|
|
|
12
|
-
import {Sphinx} from "@sphinx-labs/contracts/SphinxPlugin.sol";
|
|
12
|
+
import {Sphinx} from "@sphinx-labs/contracts/contracts/foundry/SphinxPlugin.sol";
|
|
13
13
|
import {Script} from "forge-std/Script.sol";
|
|
14
14
|
|
|
15
15
|
contract Drop1Script is Script, Sphinx {
|
package/script/Deploy.s.sol
CHANGED
|
@@ -10,8 +10,8 @@ import "@rev-net/core-v6/script/helpers/RevnetCoreDeploymentLib.sol";
|
|
|
10
10
|
import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
|
|
11
11
|
import {JB721InitTiersConfig} from "@bananapus/721-hook-v6/src/structs/JB721InitTiersConfig.sol";
|
|
12
12
|
import {JB721TierConfig} from "@bananapus/721-hook-v6/src/structs/JB721TierConfig.sol";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
13
|
+
import {REVBaseline721HookConfig} from "@rev-net/core-v6/src/structs/REVBaseline721HookConfig.sol";
|
|
14
|
+
import {REV721TiersHookFlags} from "@rev-net/core-v6/src/structs/REV721TiersHookFlags.sol";
|
|
15
15
|
import {IJBPrices} from "@bananapus/core-v6/src/interfaces/IJBPrices.sol";
|
|
16
16
|
import {IJBSplitHook} from "@bananapus/core-v6/src/interfaces/IJBSplitHook.sol";
|
|
17
17
|
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
@@ -30,7 +30,7 @@ import {REVSuckerDeploymentConfig} from "@rev-net/core-v6/src/structs/REVSuckerD
|
|
|
30
30
|
import {JBSuckerDeployerConfig} from "@bananapus/suckers-v6/src/structs/JBSuckerDeployerConfig.sol";
|
|
31
31
|
import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
|
|
32
32
|
|
|
33
|
-
import {Sphinx} from "@sphinx-labs/contracts/SphinxPlugin.sol";
|
|
33
|
+
import {Sphinx} from "@sphinx-labs/contracts/contracts/foundry/SphinxPlugin.sol";
|
|
34
34
|
import {Script} from "forge-std/Script.sol";
|
|
35
35
|
|
|
36
36
|
import {Banny721TokenUriResolver} from "./../src/Banny721TokenUriResolver.sol";
|
|
@@ -331,7 +331,7 @@ contract DeployScript is Script, Sphinx {
|
|
|
331
331
|
terminalConfigurations: terminalConfigurations,
|
|
332
332
|
suckerDeploymentConfiguration: suckerDeploymentConfiguration,
|
|
333
333
|
hookConfiguration: REVDeploy721TiersHookConfig({
|
|
334
|
-
baseline721HookConfiguration:
|
|
334
|
+
baseline721HookConfiguration: REVBaseline721HookConfig({
|
|
335
335
|
name: "Banny Retail",
|
|
336
336
|
symbol: "BANNY",
|
|
337
337
|
baseUri: BASE_URI,
|
|
@@ -342,12 +342,11 @@ contract DeployScript is Script, Sphinx {
|
|
|
342
342
|
tiers: tiers, currency: ETH_CURRENCY, decimals: DECIMALS, prices: core.prices
|
|
343
343
|
}),
|
|
344
344
|
reserveBeneficiary: address(0),
|
|
345
|
-
flags:
|
|
345
|
+
flags: REV721TiersHookFlags({
|
|
346
346
|
noNewTiersWithReserves: false,
|
|
347
347
|
noNewTiersWithVotes: false,
|
|
348
348
|
noNewTiersWithOwnerMinting: false,
|
|
349
|
-
preventOverspending: false
|
|
350
|
-
issueTokensForSplits: false
|
|
349
|
+
preventOverspending: false
|
|
351
350
|
})
|
|
352
351
|
}),
|
|
353
352
|
salt: HOOK_SALT,
|
package/script/Drop1.s.sol
CHANGED
|
@@ -9,7 +9,7 @@ import "./helpers/BannyverseDeploymentLib.sol";
|
|
|
9
9
|
import "@rev-net/core-v6/script/helpers/RevnetCoreDeploymentLib.sol";
|
|
10
10
|
import "@openzeppelin/contracts/utils/Strings.sol";
|
|
11
11
|
|
|
12
|
-
import {Sphinx} from "@sphinx-labs/contracts/SphinxPlugin.sol";
|
|
12
|
+
import {Sphinx} from "@sphinx-labs/contracts/contracts/foundry/SphinxPlugin.sol";
|
|
13
13
|
import {Script} from "forge-std/Script.sol";
|
|
14
14
|
|
|
15
15
|
contract Drop1Script is Script, Sphinx {
|
|
@@ -6,7 +6,7 @@ import {Vm} from "forge-std/Vm.sol";
|
|
|
6
6
|
|
|
7
7
|
import {Banny721TokenUriResolver} from "../../src/Banny721TokenUriResolver.sol";
|
|
8
8
|
|
|
9
|
-
import {SphinxConstants, NetworkInfo} from "@sphinx-labs/contracts/SphinxConstants.sol";
|
|
9
|
+
import {SphinxConstants, NetworkInfo} from "@sphinx-labs/contracts/contracts/foundry/SphinxConstants.sol";
|
|
10
10
|
|
|
11
11
|
struct BannyverseDeployment {
|
|
12
12
|
uint256 revnetId;
|
|
@@ -1029,7 +1029,7 @@ import {Script} from "forge-std/Script.sol";
|
|
|
1029
1029
|
import {console} from "forge-std/console.sol";
|
|
1030
1030
|
${imports}
|
|
1031
1031
|
import {JB721TiersHook} from "@bananapus/721-hook-v5/src/JB721TiersHook.sol";
|
|
1032
|
-
import {Sphinx} from "@sphinx-labs/contracts/SphinxPlugin.sol";
|
|
1032
|
+
import {Sphinx} from "@sphinx-labs/contracts/contracts/foundry/SphinxPlugin.sol";
|
|
1033
1033
|
import {IJBTerminal} from "@bananapus/core-v5/src/interfaces/IJBTerminal.sol";
|
|
1034
1034
|
import {JBConstants} from "@bananapus/core-v5/src/libraries/JBConstants.sol";
|
|
1035
1035
|
import {JBMetadataResolver} from "@bananapus/core-v5/src/libraries/JBMetadataResolver.sol";
|
|
@@ -1596,7 +1596,7 @@ import {MigrationContractArbitrum3} from "./MigrationContractArbitrum3.sol";
|
|
|
1596
1596
|
import {MigrationContractArbitrum4} from "./MigrationContractArbitrum4.sol";
|
|
1597
1597
|
|
|
1598
1598
|
import {JB721TiersHook} from "@bananapus/721-hook-v5/src/JB721TiersHook.sol";
|
|
1599
|
-
import {Sphinx} from "@sphinx-labs/contracts/SphinxPlugin.sol";
|
|
1599
|
+
import {Sphinx} from "@sphinx-labs/contracts/contracts/foundry/SphinxPlugin.sol";
|
|
1600
1600
|
import {IJBTerminal} from "@bananapus/core-v5/src/interfaces/IJBTerminal.sol";
|
|
1601
1601
|
import {JBConstants} from "@bananapus/core-v5/src/libraries/JBConstants.sol";
|
|
1602
1602
|
import {JBMetadataResolver} from "@bananapus/core-v5/src/libraries/JBMetadataResolver.sol";
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
-
import "forge-std/Test.sol";
|
|
5
|
-
import {IERC721} from "@bananapus/721-hook-v6/src/abstract/ERC721.sol";
|
|
6
|
-
import {IJB721TiersHook} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHook.sol";
|
|
7
|
-
import {IJB721TiersHookStore} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHookStore.sol";
|
|
4
|
+
import {Test} from "forge-std/Test.sol";
|
|
8
5
|
import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
|
|
9
6
|
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
|
|
10
7
|
|
package/test/BannyAttacks.t.sol
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
-
import "forge-std/Test.sol";
|
|
5
|
-
import {IERC721} from "@bananapus/721-hook-v6/src/abstract/ERC721.sol";
|
|
6
|
-
import {IJB721TiersHook} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHook.sol";
|
|
7
|
-
import {IJB721TiersHookStore} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHookStore.sol";
|
|
4
|
+
import {Test} from "forge-std/Test.sol";
|
|
8
5
|
import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
|
|
9
6
|
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
|
|
10
7
|
|
package/test/DecorateFlow.t.sol
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
-
import "forge-std/Test.sol";
|
|
5
|
-
import {IERC721} from "@bananapus/721-hook-v6/src/abstract/ERC721.sol";
|
|
6
|
-
import {IJB721TiersHook} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHook.sol";
|
|
7
|
-
import {IJB721TiersHookStore} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHookStore.sol";
|
|
4
|
+
import {Test} from "forge-std/Test.sol";
|
|
8
5
|
import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
|
|
9
6
|
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
|
|
10
7
|
|
package/test/Fork.t.sol
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
-
import "forge-std/Test.sol";
|
|
4
|
+
import {Test} from "forge-std/Test.sol";
|
|
5
5
|
|
|
6
6
|
// JB core — deploy fresh within fork.
|
|
7
7
|
import {JBPermissions} from "@bananapus/core-v6/src/JBPermissions.sol";
|
|
@@ -14,7 +14,6 @@ import {JBSplits} from "@bananapus/core-v6/src/JBSplits.sol";
|
|
|
14
14
|
import {JBPrices} from "@bananapus/core-v6/src/JBPrices.sol";
|
|
15
15
|
import {JBController} from "@bananapus/core-v6/src/JBController.sol";
|
|
16
16
|
import {JBFundAccessLimits} from "@bananapus/core-v6/src/JBFundAccessLimits.sol";
|
|
17
|
-
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
18
17
|
|
|
19
18
|
// 721 hook — deploy fresh within fork.
|
|
20
19
|
import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
@@ -22,7 +21,6 @@ import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
|
|
|
22
21
|
import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
23
22
|
import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
24
23
|
import {IJB721TiersHook} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHook.sol";
|
|
25
|
-
import {IJB721TiersHookStore} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHookStore.sol";
|
|
26
24
|
import {JB721TierConfig} from "@bananapus/721-hook-v6/src/structs/JB721TierConfig.sol";
|
|
27
25
|
import {JB721InitTiersConfig} from "@bananapus/721-hook-v6/src/structs/JB721InitTiersConfig.sol";
|
|
28
26
|
import {JB721TiersHookFlags} from "@bananapus/721-hook-v6/src/structs/JB721TiersHookFlags.sol";
|
|
@@ -39,7 +37,6 @@ import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Recei
|
|
|
39
37
|
|
|
40
38
|
// Banny.
|
|
41
39
|
import {Banny721TokenUriResolver} from "../src/Banny721TokenUriResolver.sol";
|
|
42
|
-
import {IBanny721TokenUriResolver} from "../src/interfaces/IBanny721TokenUriResolver.sol";
|
|
43
40
|
|
|
44
41
|
/// @notice Malicious hook for reentrancy testing. Re-enters the resolver during safeTransferFrom.
|
|
45
42
|
contract ReentrantHook {
|
|
@@ -235,13 +232,7 @@ contract BannyForkTest is Test {
|
|
|
235
232
|
// ──────────────────────────────────────
|
|
236
233
|
|
|
237
234
|
function setUp() public {
|
|
238
|
-
|
|
239
|
-
string memory rpcUrl = vm.envOr("RPC_ETHEREUM_MAINNET", string(""));
|
|
240
|
-
if (bytes(rpcUrl).length == 0) {
|
|
241
|
-
vm.skip(true);
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
vm.createSelectFork(rpcUrl);
|
|
235
|
+
vm.createSelectFork("ethereum");
|
|
245
236
|
|
|
246
237
|
// Clear any mainnet code at actor addresses (makeAddr may collide with deployed contracts).
|
|
247
238
|
vm.etch(alice, "");
|
|
@@ -1832,7 +1823,8 @@ contract BannyForkTest is Test {
|
|
|
1832
1823
|
JB721TiersHookStore store = new JB721TiersHookStore();
|
|
1833
1824
|
JBAddressRegistry addressRegistry = new JBAddressRegistry();
|
|
1834
1825
|
|
|
1835
|
-
JB721TiersHook hookImpl =
|
|
1826
|
+
JB721TiersHook hookImpl =
|
|
1827
|
+
new JB721TiersHook(jbDirectory, jbPermissions, jbRulesets, store, jbSplits, trustedForwarder);
|
|
1836
1828
|
|
|
1837
1829
|
hookDeployer = new JB721TiersHookDeployer(hookImpl, store, addressRegistry, trustedForwarder);
|
|
1838
1830
|
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
-
import "forge-std/Test.sol";
|
|
5
|
-
import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
|
|
4
|
+
import {Test} from "forge-std/Test.sol";
|
|
6
5
|
|
|
7
6
|
import {Banny721TokenUriResolver} from "../../src/Banny721TokenUriResolver.sol";
|
|
8
7
|
|
|
9
8
|
/// @notice Mismatched array lengths should revert.
|
|
10
|
-
contract
|
|
9
|
+
contract ArrayLengthValidationTest is Test {
|
|
11
10
|
Banny721TokenUriResolver resolver;
|
|
12
11
|
address deployer = makeAddr("deployer");
|
|
13
12
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
-
import "forge-std/Test.sol";
|
|
4
|
+
import {Test} from "forge-std/Test.sol";
|
|
5
5
|
import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
|
|
6
6
|
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
|
|
7
7
|
|
|
@@ -68,7 +68,7 @@ contract MockStore57 {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
/// @notice decorateBannyWith should reject non-body-category tokens as bannyBodyId.
|
|
71
|
-
contract
|
|
71
|
+
contract BodyCategoryValidationTest is Test {
|
|
72
72
|
Banny721TokenUriResolver resolver;
|
|
73
73
|
MockHook57 hook;
|
|
74
74
|
MockStore57 store;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
-
import "forge-std/Test.sol";
|
|
4
|
+
import {Test} from "forge-std/Test.sol";
|
|
5
5
|
import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
|
|
6
6
|
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
|
|
7
7
|
|
|
@@ -79,7 +79,7 @@ contract MockStore62 {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/// @notice Burned equipped tokens should not lock the body.
|
|
82
|
-
contract
|
|
82
|
+
contract BurnedTokenCheckTest is Test {
|
|
83
83
|
Banny721TokenUriResolver resolver;
|
|
84
84
|
MockHook62 hook;
|
|
85
85
|
MockStore62 store;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
-
import "forge-std/Test.sol";
|
|
4
|
+
import {Test} from "forge-std/Test.sol";
|
|
5
5
|
import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
|
|
6
6
|
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
|
|
7
7
|
|
|
@@ -79,7 +79,7 @@ contract MockStoreI25 {
|
|
|
79
79
|
/// @dev The fix reordered state writes (effects) before external transfers (interactions)
|
|
80
80
|
/// in _decorateBannyWithBackground. This test verifies that after a background replacement,
|
|
81
81
|
/// state is consistent and both the old background return and new background custody work.
|
|
82
|
-
contract
|
|
82
|
+
contract CEIReorderTest is Test {
|
|
83
83
|
Banny721TokenUriResolver resolver;
|
|
84
84
|
MockHookI25 hook;
|
|
85
85
|
MockStoreI25 store;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
-
import "forge-std/Test.sol";
|
|
4
|
+
import {Test} from "forge-std/Test.sol";
|
|
5
5
|
|
|
6
6
|
import {Banny721TokenUriResolver} from "../../src/Banny721TokenUriResolver.sol";
|
|
7
7
|
|
|
8
8
|
/// @notice setMetadata should allow clearing fields to empty string.
|
|
9
|
-
contract
|
|
9
|
+
contract ClearMetadataTest is Test {
|
|
10
10
|
Banny721TokenUriResolver resolver;
|
|
11
11
|
address deployer = makeAddr("deployer");
|
|
12
12
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
-
import "forge-std/Test.sol";
|
|
4
|
+
import {Test} from "forge-std/Test.sol";
|
|
5
5
|
import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
|
|
6
6
|
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
|
|
7
7
|
|
|
@@ -69,7 +69,7 @@ contract MockStore56 {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/// @notice Events should emit _msgSender(), not msg.sender.
|
|
72
|
-
contract
|
|
72
|
+
contract MsgSenderEventsTest is Test {
|
|
73
73
|
Banny721TokenUriResolver resolver;
|
|
74
74
|
MockHook56 hook;
|
|
75
75
|
MockStore56 store;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
-
import "forge-std/Test.sol";
|
|
4
|
+
import {Test} from "forge-std/Test.sol";
|
|
5
5
|
import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
|
|
6
6
|
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
|
|
7
7
|
|
|
@@ -86,7 +86,7 @@ contract MockStoreM8 {
|
|
|
86
86
|
/// @dev When a previously equipped outfit's tier is removed, `_productOfTokenId` returns category 0.
|
|
87
87
|
/// Before the fix, this caused the first while loop to exit immediately (due to `!= 0` guard),
|
|
88
88
|
/// and the second while loop would transfer out outfits that were being re-equipped.
|
|
89
|
-
contract
|
|
89
|
+
contract RemovedTierDesyncTest is Test {
|
|
90
90
|
Banny721TokenUriResolver resolver;
|
|
91
91
|
MockHookM8 hook;
|
|
92
92
|
MockStoreM8 store;
|