This companion contract (OnChainBrewsRenderer) is responsible for composing SVG art on-chain and serving standard ERC‑721 metadata via tokenURI. Below is a section‑by‑section walkthrough of how images are generated, layered, and hosted entirely from contract storage.
Linking to the Core Contract
The renderer is linked to the main (core) contract via IOnChainBrews core and queries the core contract for:
tokenIdToAuction(tokenId) → which auction produced a token
getAuctionSeed(auctionId) → deterministic per‑auction seed for traits
Data Model
The metadata (description and traits) for On-Chain brews are stored in the core contract, which queries the renderer for the SVG image of each brew.
/// each trait contains a name, an svg snippet, and a weight
struct Trait {
string name; /// human‑readable trait name (e.g., "amber", "frosted mug")
string svg; /// an SVG fragment (layer) stored directly on-chain
uint16 weight; /// relative rarity used in weighted random selection
}
/// traits are assigned to 1 of 4 categories
/// this defines both attributes order and
/// layering order when composing the final SVG
string[4] public categories = ["Background", "Brew", "Mug", "Top"];
/// a registry of traits per category
mapping(string => Trait[]) public traitCategories;
Admin: Authoring & Maintaining the Art
Because the owner can add/update/remove traits, the art set is mutable until ownership is renounced. This is a conscious choice to allow live curation.
The SVG layers are managed by upsert and remove functions on the renderer contract. This is to allow for continued updates until the renderer contract is renounced. This allows the contract owner to:
-
Publish new traits over time (e.g., seasonal backgrounds).
-
Tweak rarity
weight without changing deployed code.
-
Patch/iterate on SVG fragments (fix a pixel, adjust a color) by overwriting
svg for that name.
/// adds a new trait or updates an existing one by name within a category
function upsertTrait(
string memory category,
string memory name,
string memory svg,
uint16 weight
) external onlyOwner {};
/// deletes a trait via swap‑and‑pop pattern
/// reverts if the named trait is not found
function removeTrait(
string memory category,
string memory name
) external onlyOwner {};