@bannynet/core-v6 0.0.1
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 +53 -0
- package/SKILLS.md +94 -0
- package/deployments/banny-core-v5/arbitrum/Banny721TokenUriResolver.json +1809 -0
- package/deployments/banny-core-v5/arbitrum_sepolia/Banny721TokenUriResolver.json +1795 -0
- package/deployments/banny-core-v5/base/Banny721TokenUriResolver.json +1810 -0
- package/deployments/banny-core-v5/base_sepolia/Banny721TokenUriResolver.json +1796 -0
- package/deployments/banny-core-v5/ethereum/Banny721TokenUriResolver.json +1795 -0
- package/deployments/banny-core-v5/optimism/Banny721TokenUriResolver.json +1810 -0
- package/deployments/banny-core-v5/optimism_sepolia/Banny721TokenUriResolver.json +1796 -0
- package/deployments/banny-core-v5/sepolia/Banny721TokenUriResolver.json +1795 -0
- package/foundry.toml +22 -0
- package/package.json +53 -0
- package/remappings.txt +1 -0
- package/script/1.Fix.s.sol +290 -0
- package/script/Add.Denver.s.sol +75 -0
- package/script/AirdropOutfits.s.sol +2302 -0
- package/script/Deploy.s.sol +440 -0
- package/script/Drop1.s.sol +979 -0
- package/script/MigrationContractArbitrum.sol +494 -0
- package/script/MigrationContractArbitrum1.sol +170 -0
- package/script/MigrationContractArbitrum2.sol +204 -0
- package/script/MigrationContractArbitrum3.sol +174 -0
- package/script/MigrationContractArbitrum4.sol +478 -0
- package/script/MigrationContractBase1.sol +444 -0
- package/script/MigrationContractBase2.sol +175 -0
- package/script/MigrationContractBase3.sol +309 -0
- package/script/MigrationContractBase4.sol +350 -0
- package/script/MigrationContractBase5.sol +259 -0
- package/script/MigrationContractEthereum1.sol +468 -0
- package/script/MigrationContractEthereum2.sol +306 -0
- package/script/MigrationContractEthereum3.sol +349 -0
- package/script/MigrationContractEthereum4.sol +352 -0
- package/script/MigrationContractEthereum5.sol +354 -0
- package/script/MigrationContractEthereum6.sol +270 -0
- package/script/MigrationContractEthereum7.sol +439 -0
- package/script/MigrationContractEthereum8.sol +385 -0
- package/script/MigrationContractOptimism.sol +196 -0
- package/script/helpers/BannyverseDeploymentLib.sol +73 -0
- package/script/helpers/MigrationHelper.sol +155 -0
- package/script/outfit_drop/generate-migration.js +3441 -0
- package/script/outfit_drop/raw.json +43276 -0
- package/slither-ci.config.json +10 -0
- package/sphinx.lock +521 -0
- package/src/Banny721TokenUriResolver.sol +1288 -0
- package/src/interfaces/IBanny721TokenUriResolver.sol +137 -0
- package/test/Banny721TokenUriResolver.t.sol +669 -0
- package/test/BannyAttacks.t.sol +322 -0
- package/test/DecorateFlow.t.sol +1056 -0
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# banny-retail-v5
|
|
2
|
+
|
|
3
|
+
On-chain composable avatar system for Juicebox 721 collections -- manages Banny character bodies, backgrounds, and outfit NFTs with layered SVG rendering.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
| Contract | Description |
|
|
8
|
+
|----------|-------------|
|
|
9
|
+
| `Banny721TokenUriResolver` | The sole contract. Implements `IJB721TokenUriResolver` to serve fully on-chain SVG token URIs for any Juicebox 721 hook. Manages a composable asset system where Banny body NFTs can be dressed with outfit NFTs and placed on background NFTs, all rendered as layered SVGs with base64-encoded JSON metadata. Owner can register SVG content and hashes for product IDs (UPCs). |
|
|
10
|
+
|
|
11
|
+
### Asset Categories
|
|
12
|
+
|
|
13
|
+
| Category ID | Name | Role |
|
|
14
|
+
|-------------|------|------|
|
|
15
|
+
| 0 | Body | The base Banny character. Owns outfits and backgrounds. |
|
|
16
|
+
| 1 | Background | Scene behind the Banny. One per body. |
|
|
17
|
+
| 2 | Backside | Layer behind the body. |
|
|
18
|
+
| 3 | Necklace | Accessory layer (default provided). |
|
|
19
|
+
| 4 | Head | Head accessory. One per body. |
|
|
20
|
+
| 5 | Eyes | Eye style (defaults: standard, alien). |
|
|
21
|
+
| 6 | Glasses | Eyewear layer. |
|
|
22
|
+
| 7 | Mouth | Mouth expression (default provided). |
|
|
23
|
+
| 8 | Legs | Lower body clothing. |
|
|
24
|
+
| 9 | Suit | Full body suit (one-piece). |
|
|
25
|
+
| 10 | Suit Bottom | Lower suit piece. |
|
|
26
|
+
| 11 | Suit Top | Upper suit piece. |
|
|
27
|
+
| 12 | Headtop | Top-of-head accessory. |
|
|
28
|
+
| 13 | Hand | Held item layer. |
|
|
29
|
+
| 14-17 | Special variants | Special suit, legs, head, and body overlays. |
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install @bannynet/core-v5
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Develop
|
|
38
|
+
|
|
39
|
+
`banny-retail-v5` uses [npm](https://www.npmjs.com/) for package management and [Foundry](https://github.com/foundry-rs/foundry) for builds, tests, and deployments. Requires `via-ir = true` in foundry.toml.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
curl -L https://foundry.paradigm.xyz | sh
|
|
43
|
+
npm install && forge install
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
| Command | Description |
|
|
47
|
+
|---------|-------------|
|
|
48
|
+
| `forge build` | Compile contracts and write artifacts to `out`. |
|
|
49
|
+
| `forge test` | Run the test suite. |
|
|
50
|
+
| `forge fmt` | Lint Solidity files. |
|
|
51
|
+
| `forge build --sizes` | Get contract sizes. |
|
|
52
|
+
| `forge coverage` | Generate a test coverage report. |
|
|
53
|
+
| `forge clean` | Remove build artifacts and cache. |
|
package/SKILLS.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# banny-retail-v5
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
On-chain composable NFT avatar system that renders Banny characters with layered SVG outfits and backgrounds for Juicebox 721 collections.
|
|
6
|
+
|
|
7
|
+
## Contracts
|
|
8
|
+
|
|
9
|
+
| Contract | Role |
|
|
10
|
+
|----------|------|
|
|
11
|
+
| `Banny721TokenUriResolver` | Resolves token URIs for any Juicebox 721 hook by composing layered SVGs from registered asset content. Manages outfit attachment, background assignment, outfit locking, and on-chain SVG storage. |
|
|
12
|
+
|
|
13
|
+
## Key Functions
|
|
14
|
+
|
|
15
|
+
| Function | Contract | What it does |
|
|
16
|
+
|----------|----------|--------------|
|
|
17
|
+
| `tokenUriOf` | `Banny721TokenUriResolver` | Returns a base64-encoded JSON metadata URI with an on-chain SVG image. For body tokens, composes the full dressed Banny with attached outfits and background. For outfit/background tokens, renders the item on a mannequin Banny. |
|
|
18
|
+
| `decorateBannyWith` | `Banny721TokenUriResolver` | Attaches a background and outfit NFTs to a Banny body. Validates ownership (caller must own the body, background, and all outfits via the same 721 hook). Enforces category ordering and uniqueness (one head, one suit, etc.). Detaches previously worn items. |
|
|
19
|
+
| `lockOutfitChangesFor` | `Banny721TokenUriResolver` | Locks a Banny body's outfit for 7 days. Cannot accelerate an existing lock. Caller must own the body NFT. |
|
|
20
|
+
| `svgOf` | `Banny721TokenUriResolver` | Returns the composed SVG for a token. For bodies, layers: background + backside + body + necklace + eyes + mouth + outfits (in category order). Falls back to SVG base URI + hash if on-chain content is not stored. |
|
|
21
|
+
| `assetIdsOf` | `Banny721TokenUriResolver` | Returns the background ID and outfit IDs currently attached to a Banny body. |
|
|
22
|
+
| `namesOf` | `Banny721TokenUriResolver` | Returns the product name, category name, and Banny type (Alien/Pink/Orange/Original) for a token. |
|
|
23
|
+
| `setSvgContentsOf` | `Banny721TokenUriResolver` | Owner-only. Stores SVG content strings on-chain for given UPCs. Content must match the previously registered hash. Cannot overwrite existing content. |
|
|
24
|
+
| `setSvgHashsOf` | `Banny721TokenUriResolver` | Owner-only. Registers SVG content hashes for UPCs. Hash cannot be changed once set. |
|
|
25
|
+
| `setProductNames` | `Banny721TokenUriResolver` | Owner-only. Sets human-readable names for product UPCs. |
|
|
26
|
+
| `setSvgBaseUri` | `Banny721TokenUriResolver` | Owner-only. Sets the base URI for lazily-loaded SVG files (used when on-chain content is not yet stored). |
|
|
27
|
+
| `userOf` | `Banny721TokenUriResolver` | Returns the Banny body ID that a background is attached to. |
|
|
28
|
+
| `wearerOf` | `Banny721TokenUriResolver` | Returns the Banny body ID that an outfit is worn by. |
|
|
29
|
+
| `onERC721Received` | `Banny721TokenUriResolver` | Handles receiving 721 tokens. Validates the transfer is authorized (outfit must be worn by sender's Banny, or sender is hook). Used for outfit management. |
|
|
30
|
+
|
|
31
|
+
## Integration Points
|
|
32
|
+
|
|
33
|
+
| Dependency | Import | Used For |
|
|
34
|
+
|------------|--------|----------|
|
|
35
|
+
| `@bananapus/721-hook-v6` | `IJB721TiersHook`, `IJB721TiersHookStore`, `IJB721TokenUriResolver`, `JB721Tier`, `JBIpfsDecoder`, `IERC721` (custom ERC721) | Token ownership checks, tier data resolution, IPFS URI decoding, hook store queries. |
|
|
36
|
+
| `@openzeppelin/contracts` | `Ownable`, `ReentrancyGuard`, `ERC2771Context`, `IERC721Receiver`, `Strings` | Access control, reentrancy protection, meta-transactions, safe NFT receipt, string utilities. |
|
|
37
|
+
| `lib/base64` | `Base64` | Base64 encoding for on-chain SVG and JSON metadata. |
|
|
38
|
+
|
|
39
|
+
## Key Types
|
|
40
|
+
|
|
41
|
+
| Struct/Enum | Key Fields | Used In |
|
|
42
|
+
|-------------|------------|---------|
|
|
43
|
+
| `JB721Tier` | `id`, `category`, `encodedIPFSUri`, `price` | `tokenUriOf` (product resolution via tier store) |
|
|
44
|
+
|
|
45
|
+
### Internal Mappings (not structs, but critical state)
|
|
46
|
+
|
|
47
|
+
| Mapping | Key | Value | Purpose |
|
|
48
|
+
|---------|-----|-------|---------|
|
|
49
|
+
| `_attachedOutfitIdsOf` | `hook => bannyBodyId` | `uint256[]` | Outfit token IDs attached to a body. |
|
|
50
|
+
| `_attachedBackgroundIdOf` | `hook => bannyBodyId` | `uint256` | Background token ID attached to a body. |
|
|
51
|
+
| `_svgContentOf` | `upc` | `string` | On-chain SVG content for a product. |
|
|
52
|
+
| `svgHashOf` | `upc` | `bytes32` | Keccak256 hash of expected SVG content. |
|
|
53
|
+
| `_customProductNameOf` | `upc` | `string` | Human-readable product name. |
|
|
54
|
+
| `outfitLockedUntil` | `hook => upc` | `uint256` | Timestamp until outfit changes are locked. |
|
|
55
|
+
| `_userOf` | `hook => backgroundId` | `uint256` | Which body is using a background. |
|
|
56
|
+
| `_wearerOf` | `hook => outfitId` | `uint256` | Which body is wearing an outfit. |
|
|
57
|
+
|
|
58
|
+
## Gotchas
|
|
59
|
+
|
|
60
|
+
- The `_LOCK_DURATION` is hardcoded to 7 days. `lockOutfitChangesFor` prevents reducing an existing lock (reverts with `CantAccelerateTheLock`).
|
|
61
|
+
- SVG content is immutable once stored. `setSvgContentsOf` reverts if content already exists. The content must hash-match the previously registered `svgHashOf[upc]`.
|
|
62
|
+
- SVG hashes are also immutable. `setSvgHashsOf` reverts if a hash is already registered for a UPC.
|
|
63
|
+
- `decorateBannyWith` enforces strict ascending category order for outfits. Passing outfits in wrong order reverts with `UnorderedCategories`.
|
|
64
|
+
- Only one item per "slot" category is allowed on a body: one head, one suit (full suit XOR suit top + suit bottom), one background.
|
|
65
|
+
- Body type detection uses the UPC modulo: UPC 1 = Alien, 2 = Pink, 3 = Orange, 4 = Original. Alien bodies get `DEFAULT_ALIEN_EYES`, others get `DEFAULT_STANDARD_EYES`.
|
|
66
|
+
- Outfit/background ownership is validated against the 721 hook, not `msg.sender` directly. The caller must own the body AND all attached items through the same hook contract.
|
|
67
|
+
- `via-ir = true` is required in `foundry.toml` due to stack-too-deep in the complex SVG composition logic.
|
|
68
|
+
|
|
69
|
+
## Example Integration
|
|
70
|
+
|
|
71
|
+
```solidity
|
|
72
|
+
import {IBanny721TokenUriResolver} from "@bannynet/core-v6/src/interfaces/IBanny721TokenUriResolver.sol";
|
|
73
|
+
|
|
74
|
+
// Get the composed SVG for a dressed Banny
|
|
75
|
+
string memory svg = resolver.svgOf(
|
|
76
|
+
hookAddress,
|
|
77
|
+
bannyBodyTokenId,
|
|
78
|
+
true, // dress the banny with attached outfits
|
|
79
|
+
true // include the attached background
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Dress a Banny body with outfits
|
|
83
|
+
uint256[] memory outfitIds = new uint256[](2);
|
|
84
|
+
outfitIds[0] = hatTokenId; // must be category 4 (head)
|
|
85
|
+
outfitIds[1] = shirtTokenId; // must be category 11 (suit top)
|
|
86
|
+
|
|
87
|
+
// Caller must own the body, background, and all outfits on the same hook
|
|
88
|
+
resolver.decorateBannyWith(
|
|
89
|
+
hookAddress,
|
|
90
|
+
bannyBodyTokenId,
|
|
91
|
+
backgroundTokenId,
|
|
92
|
+
outfitIds // must be in ascending category order
|
|
93
|
+
);
|
|
94
|
+
```
|