AssemblyPress℗ Walkthrough

AssemblyPress℗ - v0.0

This document is a breakdown of the AssemblyPress (AP) architecture for anyone who is interested in its capabilities and details. The Metalabel Protocol Walkthrough served as a template for the information provided below:


AP comprises two contract factories (ERC721 + ERC1155) that simplify the process of leveraging common token standards as onchain databases for any application. This is accomplished via:

  1. Configurable external state/access control/metadata rendering for each contract (ERC721) or token (ERC1155)
  2. Optional soulbound functionality using ERC-5192 (ERC721) and ERC-5633 (ERC1155)

This flexibility means AP is suitable for use cases ranging from lightweight edition implementations, to app registries with complex user profiles.

Link to deployed goerli addresses
Link to vid walkthrough of contract intractions for ERC721 + ERC1155

External State + Access Control

The state or “database” of the contract (ERC721) or token (ERC1155) is stored in an external logic contract that is initialized upon the creation of a contract or token. Access control is also managed through this logic contract, and is configurable for all common actions relevant to each token standard through interfaces. These permissions can either be hardcoded directly or externalized once more through an access control module (whoever is writing the logic contract can make this decision).

Both ERC721 + ERC1155 implementations facilitate writing to these external logic contracts via functions that pass in arbitrary data alongside the minting of new tokens. This means that you can use these as the backbone for apps with different data structures, as long as the data that needs to be stored + managed is able to be initialized within the restrictions of the gas limits of the calls that mint new tokens.

External Metadata Rendering

The results of tokenURI/contractURI (ERC721), and URI (ERC1155) are returned via calls to external metadata renderer contracts, in a similar pattern implemented in the ZORA drop architecture.

Metadata renderers are set at the contract level for ERC721s and at the token level for ERC1155s. While they allow a user to pass in data along with a mint call, they do not require it, thus more simplified use cases are supported in addition to the complex functionality AP is designed for.

Separating the metadata renderer from the logic contract allows for the hot-swapping of metadata renderers to provide updated methods to interpret data — without affecting the underlying data stored in the logic contract.

Soulbound Functionality

Soulbound tokens aka “SBTs” or “non-transferrable tokens” can be very useful tools in applications that rely on user profiles tied to a specific wallet address. Upon deployment of an ERC721 contract, you can determine whether all of the tokens minted from that contract will be soulbound or not – and a similar setting is enabled in the ERC1155 implementation upon the mintng of a new token.

The soulbound status of tokens can NOT be updated after initialization, and this provides end users better confidence that the tokens minted to their accounts will follow the rules they were created under.

ERC721 Details

Sales details Initial mint logic is governed via a Configuration struct that is passed in on contract initialization (this can be updated later). Primary sales fee related info cannot be updated after they are initially set, to allow projects providing custom create flows to be sure any fees they set for themselves will be realized.

The mintWithData call provides the option to pass in arbitrary data, effectively making this a database writing instrument. The logic contract associated with the ERC721 will be updated with whatever data is passed in upon mint, so it is up to the user and/or app developer to make sure the data getting passed is compatible with the designated logic contract.

ERC1155 Details

The ERC1155 implementation has two sets of logic.

  • Contract level logic gates access to calls like who can mint new tokens, who can update the contract level logic, and who can transfer ownership of the contract.

  • Token level logic gates all of the functions on individual tokens which can basically be considered themselves as collections.

Similarly to how the ERC721 “mintWithData” call allows you to pass in arbitrary data when minting a new token, the ERC1155 “mintNew” call allows you to configure everything about a new token upon its creation. Like in the ERC721 implementation, soulbound + primary sale related info cannot be adjusted after they are set.

Its important to note that the ERC1155 implementation differs from the ERC721 implementation due to the fact that the ERC1155 “mintExisting” call – which mints already initialized tokens and does not allow a user to pass in input data – has no corresponding equivalent on the ERC721 implementation.

Example Use Cases

Curation (ERC721) - this protocol was the impetus for the work that became AssemblyPress. After developing multiple apps around a curation specific protocol, we realized that the storing of app specific data was actually a generic need and not restricted to the specific structs of information we were storing for curation. With the generic mintWithData approach, you can pass in whatever data is necessary when minting a new token, and that data can be decoded + stored in a logic contract, and interpreted accordingly via the metadata renderer to provide clean data that is returned by ERC721/ERC1155 indexers. See the screenshot for how this token level information looks like when queried from an NFT indexer:

Editions (ERC1155) - Opinionated media distribution logic that reduces user input on the setting of their edition to two choices: price + time of release

Who Manages the Protocol

What this really means is who has the ability to upgrade the ERC721 + ERC1155 Factory proxies, as this is what most of the front end tooling will be built around. Important to note though that the underlying factory implementations will always be deployed immutably.

Progression of factory proxy ownership

  • Initially drafted/deployed by Max Bochman + Salief Lewis
  • Eventually transferred to FF89DE (Public Assembly Founding team)
  • Overtime ideally moved to a yet-to-be-formed Public Assembly Protocol Team.

Request For Comment

You can check out all of the code here.

The following is a non-exhaustive list of areas that could use review with the current implementation before a final v1 is released. Any assistance in reviewing/solving these issues (and any others that exist in the codebase) would be tremendously appreciated.

Feel free to open direct issues/pull-requests in the AssemblyPress repo, and don’t be afraid to leave your ENS/wallet address if you do :slight_smile:


  • Review the soul bound implementations for ERC721 (erc-5192) + ERC1155 (erc-5633)
    • Particularly the 1155 one where we made a bunch of custom edits to the 1155 solmate base
  • Probably want to move the IAccessControlRegistry into this repo instead of importing from onchain to have better control of it
  • Prob want to add in generic multi call stuff to allow for more complex contract set up ability
  • Should we take out a lot of the functions in the ERC721Press + ERC1155Press interfaces to make them more flexible?
  • Should we pick a specific pragma of solidity rather than ^0.8.16 ??
  • clean up lib/imports/remappings


  • Add a settable/initializable “description” field to CurationStorageV1 + update CurationMetadataRenderer so we can provide channel level descriptions for curation contracts
    • currently the description is generated programatically, doesn’t allow for custom descriptions
  • mintQuantity situation + maxSupply situation on erc721Press is funky
    • Restricted it mintQuantity to uint16 at some point bc we thought we were getting ran out of gas errors because of the data that gets passed in at large quantities but we think that was wrong
  • Double check for redundant logic + events
    • ex: removal of unnecessary value != 0 checks
  • Struct breakdown docs in ICurationLogic are weird even though helpful
  • Missing byte struct breakdown for Curation Config
  • Should updates to the logic contract that itself may have configurable access control extensions on it all have to go through the press contract?
    • ex: to update what access control module the logic contract is using, should you be calling setLogic on the underlying Press contract
      • Or should you be targeting the logic contract with some “update access control function”?
        • We imagine it should live at the logic contract level just not sure
  • Missing tests
    • deploy + upgrade paths
    • Additional config / metadata update things
    • Curation pause, freeze, sort orders, get listings for curator, tokenURI stuff
  • The “HybridAccessWithFee” is really wack and just in there for withdraw testing purposes. Is there a way to make this more universal and helpful? we actually dont think so but tbd
  • Update CurationLogic with a verson that uses assembly in updateLogicWithData to decode data more efficiently
    • could mean something like pass in one long packed bytes string and chop it up into corresponding listing chunks (still in bytes form) based on wtv the total length is / length of one packed one. and can get rid of the curator field cuz it’s soul bound and will always goes to msg sender.
      • will require an update to ICurationLogic as well since the Listing struct may change (or not even be necessary anymore)
      • will also require an updated CurationMetadataRenderer


  • Check if withdraw implementation was done correctly
  • Because erc1155press is using OwnableUpgradeable, it means that the entire “canTransferOwnership” check is irrelevant
    • Solutions
  • Should we move away from using Ownable because of this? Set up custom owner() + setOwner() solution? Returning owner() is helpful for frontends
  • Remove canTransferOwnership check
  • Did I set up custom upgradeability correct?
  • Is it ok that the URI update event happens in the renderer rather than the underlying impl?
  • Seems like token + contract level logic might be
  • Create a better tokenEditionLogic that allows for configurable access control on initialization?
    • similar to how a separate module that controls access + mint price is initialized in CurationLogic
    • Might be unnecessary though because the whole point is the 1155 editions are really minimal with barely any access restrictions
  • Create a better token/contractEditionLogic contracts that allow for token gating of access
    • ex: if you own PA token you can mintNew
  • Add in an airdrop function that gets around the normal mint call which will check for mint price
  • Add back in Batch mint? Or dont need?
  • Add back Batch burn? Or dont need?
  • Add in edit contract level name/symbol functionality?
  • EditionTokenLogic
    • Get rid of the mint cap check in the “canMintExisting” call?


To learn more about Public Assembly, check out our website for more information


@0xTranqui why is it necessary for you to have loops in your smart contracts? Please read Getting Loopy with Solidity. How to avoid unbounded “for” loops in… | by Rob Hitchens | B9lab blog. No wonder I was spending 3 Eth to deploying the contracts :grimacing:. Please look into it this article before making major adoption with the curation contracts.

1 Like

certain loops may be necessary to enable batch functions – the unbounded/unchecked ones will error out due to gas failures if people put in inputs that are too large, but those numbers are extremely large and atypical. front ends can generally handle data input validation

in terms of the deploy costs, the loops aren’t factored in differently than any other code, and there are no loops in the either of the deploy scripts (erc721 + erc1155). the reason the costs are so high is bc this is a relatively large architecture that optimizes for lowest gas costs on the user end, but this means incurring higher costs on the deploy side to get more of the reusable infrastructure out there

^please take all of this with a grain of salt since im certainly no expert, but wanted to respond to give u an idea why things are currently implemented the way they are. thank u for checking it out so far … will need tons of eyes on this as we get it out into the wild !!!