TLDR ABF, at its core, takes on many design patterns reflective of modern front-end libraries. Brainfuck! code you write provides the 'state' to the NFT that is rendered by renderers. Renderers are pure function contracts, providing a deterministic output based on its provided inputs (props).
TABLE OF CONTENTS
The best way to appreciate the ABF protocol is to, as the old adage goes, follow the code. In this case, we will follow the Brainfuck! code all the way to the actual svg produced in a renderer.
When a NFT marketplace or indexer calls the tokenURI
function to retrieve the metadata for an ABF NFT, quite a few things happen in sequence.
In many ways, your NFT is the product of two integral components: the Brainfuck! code and the renderer contracts. The best ABF NFTs will be the result of unique use of a well chosen renderer.
Beyond your capabilities to write Brainfuck! code, your understanding of a renderer contract will level up your artistry with ABF.
Renderer contracts, are, simply, contracts (or libraries) that receive bytes and output a new set of bytes, preferably in a image format. (But nothing in the spec stops you from writing renderers rendering to other formats)
Like the ERC20 token spec, ABF utilizes a IRenderer
spec created by the ABF Corps. Any contract written by anybody that follows this spec correctly can be utilized within the ABF protocol.
The potential is limitless, Renderers can be written to ingest different colorspaces, produce animated SVGs, or can even produce a full HTML page.
The ABF Corps has provided a "default" set of renderers that cover most bases; but dependent towards the demands of your vision, a more bespoke renderer can be created and utilized.
A registry, maintained by the Corps, provides a up to date list of all known renderers, both written by the Corps and external users.
We recommend learning in depth how a renderer works before you use it. Consult a renderer's file to learn how it can be utilized.
In Brainfuck!'s 9 opcodes, the ,
opcode stands to be one of the most powerful when utilized correctly.
Whenever ,
is executed, a byte from the input is popped (read and removed from the array) and inputted to wherever the current pointer is.
In layman terms, the ,
opcode allows a Brainfuck! code to receive parameters.
ABF provides 64 bytes (you can use ,
64 times to read inputs) to your Brainfuck!
code.
The 64 bytes provided are shaped as the following
32 bytes set by user + 32 bytes of deterministically random bytes
You can configure the first 32 bytes in the NFT creator console. Consider these bytes as "global" constants set by you to use within your code.
The next 32 bytes are the result of a keccak256
hashing of the following input:
keccak256(seed, tokenId)
The seed
is the source of random provided by you. The NFT creator console presets a random set of bytes, but you can change it to control the expected random bytes. Consider these last 32 bytes as your Math.random
.
Why hash the tokenId? ABF is fully capable of not just creating 1 of 1 NFTs. With a different source of random for each tokenId, you can easily create a generative art collection where each tokenId is not the same.
With the use of input bytes, you can easily access numbers, colors, and a source of random in your Brainfuck! code.
ABF is a conglomeration of a few contracts, some user written and some canonically deployed by the ABF Corps' Board. Registry of canonical contracts.
As described earlier, Renderers are contracts that ingest bytes and produce a deterministic output.
NOTE: Renderer contracts have no understanding of "how" the inputted bytes are produced, as in they don't know about the Brainfuck! code. They are only concerned with rendering the bytes into something.
Renderers can be considered as public goods; anybody can utilize them in their ABF NFT.
All Renderers compatible to the ABF protocol abides to the IRenderer
interface:
interface IRenderer is IERC165 { function name() external view returns (string memory); function owner() external view returns (address); function propsSize() external view returns (uint256); function additionalMetadataURI() external view returns (string memory); function renderAttributeKey() external view returns (string memory); function renderRaw(bytes calldata props) external view returns (bytes memory); function render(bytes calldata props) external view returns (string memory); function attributes(bytes calldata props) external view returns (string memory); }
name
: a human readable label to identify the renderer.
owner
: an address representing the creator of the renderer; useful to provide credit in a NFT to the renderer developer.
propsSize
: the expected size of the provided bytes for the renderer to function. Can be set to MAX_UINT
if renderer can work with any amount of bytes.
additionalMetadataURI
: a ipfs file pointing to a json that provides documentation around using the renderer. Example
renderAttributeKey
: the key within the NFT metadata JSON that will map to the output of renderRaw
. (i.e image
, animation_url
)
renderRaw
: a more debug friendly render output, (usually means it is not base64 encoded). Useful to understand what the renderer is producing.
render
: typically calls renderRaw
, produces the final consumable data used in the NFT metadata.
attributes
: provide a string (in JSON format) that will be added in the attributes
part of a NFT metadata.
When the ABF system becomes fully open source, we will release sample Renderer variants to help new developers.
Renderers are vital to the ABF ecosystem as well as extremely useful for pushing the boundaries of on-chain art. We are committed to fostering the development of Renderers and open to feedback on the current spec.
Renderers can be registered for viewing / availability on the ABF site via the Renderer Registry Contract. This allows anyone to create and upload a Renderer to the ABF Protocol for usage. To register, simply navigate to the contract on Etherscan and use the Write functions.
Additionally, the Brainfuck! NFT supports an optional royalty system for Renderer creators so Brainfuck! artists who use a "third-party" Renderer contract can award funds automatically.
To get started building a Renderer, view code created by the ABFC over at the ABF Monorepo in the /protocol/contracts/renderers
folder. Here you can see some of the default Renderers available in the Project Builder.
The additionalMetadataURI
field should point to a URI that hosts a standardized documentation stub. Currently, we upload a JSON object to IPFS and store the CID in the Renderer. With the documentation, any other developers can easily utilize a renderer.
In order facilitate the developer experience and hyperstructure qualities, we included a light specification around documentation. The ABF website conforms to this specification which allows it to directly pull Renderer data and documentation for a user friendly Renderer Registry.
interface RendererAdditionalMetadata { description: string; sampleOptions?: { input?: string; }; previewOptions?: { byteGroups: RendererAdditionalMetadataByteGroup[]; }; } interface RendererAdditionalMetadataByteGroup { numGroups: number | 'infinity'; groupBytesIn: number; label?: string; } // Example Upload const additionalMetadata = { description: 'A 24 by 24 mono-chrome renderer. Bytes map to grayscale colors (0x00 is black, 0xFF is black). Provide 576 bytes in a continuous hex-string.', sampleOptions: { input: '0x000102030405060708090a0...', }, previewOptions: { byteGroups: [{ numGroups: 'infinity', groupBytesIn: 1, label: 'Pixel' }], }, };
To view a Renderer's additional metadata from the abf.dev/docs/renderers
page, click on VIEW FILE
and scroll down to RAW FILE SOURCE
and click on the IPFS
link.
The ABF NFT is a ERC721A
derived + ERC2981
(Royalty fees) compatible NFT implementation with a number of ABF specific configurable settings.
At NFT creation you can configure the following (beyond the obvious need of the Brainfuck! code and related data/settings):
mintingSupply
: the amount of this NFT that can be minted.price
: the price (in wei) to pay to mint a single NFT in this collection.rendererRoyaltyFraction
: the percentage of minting profits (in bps) directed to the renderer contract's owner
.whitelistToken
: A ERC721 or ERC20 token address, if set, enforces that minters must own whitelistToken
to mint this ABF NFT.There is 3 main means to mint an ABF NFT:
mint(address to, uint256 numMints)
: standard minting function, can mint up to 6 in one call, pay the price
or reverts.airdropMint(address[] to, uint256 numMintsEach)
: admin only function, used to distribute NFTs in an airdrop scheme.mint(address to, uint256 numMints)
(with whitelistToken set): behaves just live standard mint but needs minter to own the whitelistToken.NOTE: Both minting functions abide to the mintingSupply
. There is no way to exceed the configured amount.
The admin of the ABF NFT contract has a number of admin-only functions:
setIsActive(bool isActive)
: toggles if mint
can be called or not.setSeed(bytes seed)
: if at NFT creation, seed is 0, you can set the seed at a later point to mitigate rarity abusing minting.The ABF NFT factory is a handy utility contract to easily (and cheaply) deploy a new ABF NFT.
We strongly recommend using the ABF NFT factory over manually deploying an ABF NFT yourself. (If you are using the dapp, you are using factory). The factory saves you money, and allows for your NFT to be discoverable via the dapp.
The library BrainFuckVM
is the canonical Brainfuck! interpreter utilized by the ABF protocol.
There is one function, run
that takes your code and input and runs it, returning the output.
Consult the Brainfuck! language docs for more implementation details.
The library BrainfuckVM
is the canonical Brainfuck JSON constructor utility contract utilized by ABF protocol in the contractURI
+ tokenURI
functions.
While the ABF dapp is a complete experience to deploy an ABF NFT, we do not provide the best experience with working with Brainfuck!
. Here's some tooling created that can help:
Ready to join the ABF Corps?
JOIN NOWREAD DOCS