RARA! Protocol for Devs
0xDB31
June 10th, 2022

Hello Curators.

The Social Curation Protocol is officially open-sourced. In an effort to expand the definition of curators beyond the few to any and all, we are publishing our first article for developers, builders, and savvy curators alike. From curated music playlists to curation bots feeding galleries in the metaverse, let’s rethink how the Internet’s most valuable assets are curated and presented to the world. We hope this resource starts the conversation.

What is RARA?

RARA is a social curation protocol, empowering the crowd to help find, promote, and add social context to the best NFTs. With the current state of NFTs, finding and organizing content is a hard problem to solve. RARA is set to change that by introducing curation incentives.

To learn more about the protocol, objectives, and the rollout, check out:

The deployment addresses of all contracts mentioned in this document can be found at the end of this article and in the deployment artifacts in the Github repo in the subfolder for the desired network.

Developer Use Cases

This document is geared towards developers interacting with the protocol. Check the Documentation link above for the latest details about any interface or parameter changes.

Ethereum (“L1”)  and Polygon (“L2”)

The RARA protocol lives on the Polygon L2 network. Most interactions with the protocol will be done on this layer. However since many popular and valuable NFTs are minted on Ethereum L1, the protocol maintains a bridge from L1 to L2.

Owners

The L1 - L2 bridge is used only to register an L1 NFT into the protocol. Owners register their NFTs to earn rewards by selling Reaction tokens or by claiming curation rewards for NFTs receiving Reactions. The act of registration simply provides proof of ownership of that NFT and allows the protocol to save details about the current owner and other metadata. Registering an NFT does not require you to give up custody of the NFT or provide any allowances for the protocol to move your NFT. NFT owners are simply allowing the protocol to verify that your wallet owns the specified NFT.

When you register your NFT on L1, the ownership verification occurs on L1 and then sends a message through the bridge from L1 to L2 – the details about that NFT will be stored in L2.

If you are registering on L1, you need to call the registerNFT function on the RootRegistrar contract. If you are registering on L2, you need to call the registerNFT function on the MakerRegistrar contract.

    function registerNft(
        address nftContractAddress,
        uint256 nftId,
        address creatorAddress,
        uint256 creatorSaleBasisPoints,
        uint256 optionBits,
        string calldata ipfsMetadataHash
    )

To register, pass your NFT Address and ID. The contract will verify that the calling address owns the NFT before registering it into the protocol. You can optionally pass in royalty information for the Creator Address and percentages (see “NFT Creators” below for more details) formatted as basis points. The optionBits parameter is reserved for future usage and should be set to “0” for now.  In the future, this parameter may be used to allow registrants the ability to specify how a transform can be applied against the underlying artwork. Lastly, the optional ipfsMetadataHash parameter accepts an IPFS CID.  This address should point to a JSON asset that contains metadata,including things like Reaction media, tags, descriptions, and comments. Other than the NFT address and ID, all other values can be left as “0” if you do not wish to use them.

Finally, NFTs that live on Ethereum L1 can also be the target of Reactions spends, as you will see below that the functions to perform this action contain the Chain ID, the target NFT Address, and ID.

NFT Creator Earning Royalties

Creators of NFTs can earn royalties from the protocol in two ways:  (1) selling Reactions, or (2) receiving Reactions.

To earn royalties from Reaction sales, the NFT owner (“Maker”) must register the NFT with the protocol (as detailed above) as Reactions, non-transferrable ERC-1155 tokens generated by the underlying NFT’s media. Curators buy and mint Reactions from the protocol for a cost of 1 $USDC. Reactions are then spent to react, provide context, and vote on other NFTs. Think of a Reaction as a sticker/emoji/like button for other NFTs. This “spend” action attaches the spent Reaction to the target Taker NFT. Makers earn a portion of each Reaction purchased.

To earn royalties from receiving Reactions, a Reaction must be “spent” on an NFT created or owned by you. The owner of the NFT receiving Reactions (“Taker”) and its Creator share in the Taker rewards when their NFT is the receiver of a Reaction spend.

As the Creator of an NFT, you may have set creator royalties on-chain. The protocol will honor these royalties automatically each time your NFT earns royalties from Reaction sales or Reaction spends.

Under the hood, RARA uses the Royalty Registry to query a creator percentage cut. The Royalty Registry supports:

  • EIP2981 compatible NFTs
  • Minted NFTs with creator rewards defined on
    • SuperRare
    • Rarible
    • Manifold
    • Zora (limited functionality)

If you minted an NFT not supported by the Royalty Registry, you can still earn rewards by creating a royalty override contract and registering it on the Royalty Registry.

For NFTs that do not have on-chain royalties, the registering owner can optionally add the parameters to enable you to collect rewards. Simply set a Creator Address and a Creator Sale Basis Points during registration to set it up. If royalty details are found on-chain, these will override any royalty information that the registrant manually set.

Once you have defined the creator royalties, you will earn rewards in two scenarios. First, when a Reaction backed by an NFT you created is sold, a cut of the purchase price is allocated to your address in the form of an ERC20 Payment Token(currently $USDC). You will accumulate rewards until you decide to withdraw them. Second, if a user curates an NFT that you created by sending Reactions, you receive Curation Tokens for the NFT. You will accumulate these Curation Tokens until you sell them back to the bonding curve, withdrawing the value of those Curation tokens in ERC20 Payment Tokens.

See the details below for developer interactions for withdrawing Payment Token Rewards and Selling Curator Tokens.

NFT Makers Registering Reactions

If you currently own an NFT, you can register it as a “Maker” to be used as a Reaction. NFTs can be transformed into one or multiple Reactions (all based on the same underlying NFT) using the optionBits parameter. Each time someone purchases a Reaction, you will get a cut of the purchase price with the remaining funds going towards curation incentives.

To submit an NFT to be purchased as a Reaction, register it with the protocol. As mentioned above, depending on where your NFT resides, you will need to call the registerNFT function on the RootRegistrar contract (if your NFT was minted on L1). If your NFT is on L2, call the registerNFT function on the MakerRegistrar contract.

    function registerNft(
        address nftContractAddress,
        uint256 nftId,
        address creatorAddress,
        uint256 creatorSaleBasisPoints,
        uint256 optionBits,
        string calldata ipfsMetadataHash
    )

Curators Spending Reactions for Curation Tokens

Addresses that buy and spend Reactions in the protocol are known as  “Curators”. Curating is similar to “liking” an image or adding a comment to a social media post. Reactions themselves may have associated context such as a “thumbs up”, “thumbs down”, or other memes like “GM”, “WAGMI”, or similar. Additionally a Reaction spend can include metadata, such as comments or tags.

Curators are motivated to use Reactions (1) to provide social context to receiving NFTs and (2) to earn rewards by identifying viral NFTs early. Every time you spend a Reaction on an NFT, the portion of each Reaction’s purchase price reserved for curation incentives is used to buy Curation Tokens associated with that NFT.

Each curated NFT has a bonding curve associated with it. For launch, we chose a sigmoid curve. The sigmoid curve is used to represent the adoption curve of technology by three groups: early adopters, majority users, and finally laggards.

Sigmoid bonding curve | Y-Axis = Token Price | X-Axis = Curation Tokens Sold
Sigmoid bonding curve | Y-Axis = Token Price | X-Axis = Curation Tokens Sold

Early spenders of Reactions on a curated NFTs receive Curation Tokens for a cheap price. As more Reactions are spent by others, the Curation Token price increases - slowly at first, then aggressively, and finally slowing back down.

As a holder of Curation Tokens, you can sell the tokens back to the bonding curve whenever you choose in exchange for Payment Tokens ($USDC). This gamifies the virality of curated NFTs. Curation Token holders are incentivized to hold the tokens until they think the peak virality is achieved. As Curation Tokens are sold back to the bonding curve, new curators can react to earn from on-going virality of important NFTs.

To buy Reactions, you need to call the buyReaction function on the ReactionVault contract.

    function buyReaction(
        uint256 transformId,
        uint256 quantity,
        address destinationWallet,
        address referrer,
        uint256 optionBits
    )

When an NFT is registered in the protocol, it is assigned a Transform ID. This ID is derived from the hash of a constant prefix, the Source ID of the NFT, and the Option Bits specified by the user registering.

        // Generate reaction ID
        uint256 transformId = uint256(
            keccak256(abi.encode(MAKER_META_PREFIX, sourceId, optionBits))
        )

The Prefix is defined as the string “MAKER”.

The Source ID is defined as a hash of the Chain ID where the NFT lives, the NFT contract address, and the NFT ID.

    function _deriveSourceId(
        uint256 chainId,
        address nftContractAddress,
        uint256 nftId
    ) internal pure returns (uint256) {
        return
            uint256(keccak256(abi.encode(chainId, nftContractAddress, nftId)));
    }

The other parameters included in the buyReaction function call are the quantity (how many Reactions to purchase), the destinationWallet (where the Reactions should be sent), the referrer (optional address where to send referral fees for builders like you), and optionsBits (a reserved parameter for future usage to allow the purchaser to specify additional parameters which should just be 0 for now).

Once the Reaction purchase is complete, the Payment Tokens for the amount of Reactions purchased is moved into the ReactionVault from your wallet (you must approve the amount first) and ERC1155 Reaction NFTs with the associated Reaction ID will be minted to your wallet. Reactions are non-transferable and can only be spent at this point.

To generate the Reaction ID, first a valid ParameterVersion must be generated. This is a unique hash of the Payment Token, the Reaction Price, and the amount of the Reaction price that went towards the Curator Liability. This is tracked because Reactions purchased with a different price, token, or Curator Liability should be unique from each other.

    function deriveParameterVersion(IParameterManager parameterManager)
        public
        returns (uint256)
    {
        // Build the parameter version from the price details
        return
            uint256(
                keccak256(
                    abi.encode(
                        parameterManager.paymentToken(),
                        parameterManager.reactionPrice(),
                   parameterManager.saleCuratorLiabilityBasisPoints()
                    )
                )
            );
    }

Once the Parameter Version is determined, a Reaction Prefix (“REACTION”), the Transform ID of the NFT, and user specified option bits are all hashed to generate the Reaction ID.

    function deriveReactionId(
        uint256 transformId,
        uint256 optionBits,
        uint256 parameterVersion
    ) public pure returns (uint256) {
        // Build and return the reaction ID
        return
            uint256(
                keccak256(
                    abi.encode(
                        REACTION_META_PREFIX,
                        parameterVersion,
                        transformId,
                        optionBits
                    )
                )
            );
    }

Reactions generated from the same NFTs with the same parameters will have the same Reaction IDs, so purchasing additional amounts of these Reactions will increase your balance for the same Reaction in your wallet.

Once you have purchased Reactions, you can then use them to Curate. The act of Curating is “Spending” a Reaction on a target Taker NFT. To Curate an NFT, call the spendReaction function of the ReactionVault contract.

    function spendReaction(
        uint256 takerNftChainId,
        address takerNftAddress,
        uint256 takerNftId,
        uint256 reactionId,
        uint256 reactionQuantity,
        address referrer,
        address curatorVaultOverride,
        string memory ipfsMetadataHash
    )

To spend the Reaction, you will need to specify details about the target NFT (the “Taker” NFT). This includes the Chain ID where the Taker NFT lives, the Taker NFT contract address, and the Taker NFT ID.

You will also need to specify the reactionId and the quantity to spend.

Finally, other parameters include (1) an optional referrer address (address to send referral fees for spend rewards); (2) an optional Curator Vault Override (to override the default sigmoid bonding curve for future alternative incentive mechanisms. Leave as address 0x0 for the default); and (3) an optional IPFS hash (a JSON file with tags or comments with additional context. Leave as empty if not used).

When the Reactions are spent, the ERC1155 tokens are burned from your address, and Curation Tokens from the Curator Vault will be minted to your address. The earlier you are in the Taker NFT’s bonding curve, the lower the price of Curation Tokens will be.

As others spend Reactions against the same curated NFT, the Curation Tokens will increase in value and you can sell them back to capture the curation rewards. To sell your Curation Tokens, you need to call the sellCuratorTokens function on the SigmoidCuratorVault contract.

    function sellCuratorTokens(
        uint256 nftChainId,
        address nftAddress,
        uint256 nftId,
        IERC20Upgradeable paymentToken,
        uint256 tokensToBurn,
        address refundToAddress
    )

The parameters for this function are the details for the Taker NFT receiving Reactions spends. This includes the Taker NFT Chain ID, the Taker NFT contract address, and the Taker NFT ID. Additionally you must include the Payment Token associated with the Reactions that were originally spent, the amount of Curation Tokens to burn, and the address where you would like the funds generated from the sale of your Curation Tokens to end up.

NFT Takers

If you own an NFT that has been curated, you are allocated Curation Tokens as the Taker NFT. Each time a curator spends Reactions against your NFT, a percentage of the funds is used to buy Curation Tokens and assign them to your NFT. To claim and sell these Curation Tokens, you must first register your NFT into the protocol as the owner (See Owners Registering NFTs above).

After registering your NFT, you can call withdrawTakerRewards function on the ReactionVault contract.

    function withdrawTakerRewards(
        uint256 takerNftChainId,
        address takerNftAddress,
        uint256 takerNftId,
        IERC20Upgradeable paymentToken,
        address curatorVault,
        uint256 curatorTokenId,
        uint256 tokensToBurn,
        address refundToAddress
    )

To withdraw the rewards from your accumulated Curation Tokens, you will need to provide the Taker NFT details. This includes the Chain ID where the NFT lives, the NFT contract address, and the NFT ID. Additionally, you need to provide the Payment Tokens address associated with the Reactions spent against the NFT, the address for the Curator Vault, the Curator Vault Token ID for your NFT, and the amount of tokens to withdraw and the address where the rewards should be sent. This function will return the amount of Payment Tokens that were sent back to your refund address.

The curatorTokenId is a hash of the NFT details and the Payment Token address. This ensures a unique Curator Vault Token ID and bonding curve is used for each NFT and Payment Token.

    function _getTokenId(
        uint256 nftChainId,
        address nftAddress,
        uint256 nftId,
        IERC20Upgradeable paymentToken
    ) internal pure returns (uint256) {
        return
            uint256(
                keccak256(
                    abi.encode(nftChainId, nftAddress, nftId, paymentToken)
                )
            );
    }

When you withdraw the Taker Rewards, the tokens are sold into the bonding curve for Payment Tokens. These Payment Tokens are then split between the Creator Rewards (set during registration) with the remaining amount sent to the Refund Address. The Creator Rewards are stored in the ReactionVault contract for the Creator Address to withdraw at a later date.

Referrers

If you are building an application that interacts with the RARA protocol, you can earn referral fees by facilitating both the Buying and Spending of Reactions. Each time you build a transaction to facilitate one of these interactions for your users, you can earn payment tokens from the transaction. To collect a referrer fee, insert your address into the “referrer” parameter in the transaction and a percentage of the value being exchanged will be stored in the ReactionVault contract for you to withdraw at the time.

Reward Withdrawal

As a Creator, Maker, or Referrer, you are allocated rewards in the form of Payment Tokens each time users interact with your NFT (by buying or spending Reactions). Rewards are ERC20 compatible tokens that accumulate against your address. You can query the amount of rewards you have accumulated by reading the ownerToRewardsMapping and passing in the Payment Token address and your Address. The automatically generated public getter function will be available to query for this mapping in the ReactionVault contract.

    mapping(IERC20Upgradeable => mapping(address => uint256))
        public ownerToRewardsMapping;

To withdraw your rewards, call the withdrawErc20Rewards function on the ReactionVault contract by passing the associated Payment Token address. This function will return the amount of Payment Tokens that were sent to your account.

    function withdrawErc20Rewards(IERC20Upgradeable token)
        external
        nonReentrant
        returns (uint256)
    {

Reading Protocol Configuration

To view details about the protocol configuration, you can query the public variables stored in the ParameterManager contract. This will provide you details about contract addresses, current Payment Tokens being used, the percentages of fee splits, the price of a single Reaction, and any approved Curator Vaults (in addition to the default one used). More details will be provided about percentages and flow of funds in a later post.


That’s it. That’s the protocol. Open-sourced, permissionless curation by all - even you, anon.


Contracts

RootRegistrar - 0x2665Aa3846EC61e6D28A0d9F76b70047719F3664

MakerRegistrar 0x47CD3266FA94E40613B37a88D98196325Cd28412

ReactionVault - 0xE5BA5c73378BC8Da94738CB04490680ae3eab88C

SigmoidCuratorVault - 0x7D278Aa2d95ABa7C969c9F316be6f0cFE0D18A50

ParameterManager 0xF60de25472b10e5886270b13dDec51D8BaDcd764

Related Contracts

USDC Contract (Polygon) - 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174


Next Steps

  1. Review the docs.
  2. Contribute to the Github Repo.
  3. Build a bot on Discord or Twitter for the Protocol
  4. Ask questions in the #developers channel of our discord at RARA.house.
  5. Subscribe to our email list on RARA.social.
Subscribe to RARA
Receive new entries directly to your inbox.
Collectors
View
#1
#2
#3
View collectors
This entry has been permanently stored on-chain and signed by its creator.