Royalty Enforcement Specification | Algorand Developer Portal

Royalty Enforcement Specification

Abstract

A specification to describe a set of methods that offer an API to enforce Royalty Payments to a Royalty Receiver given a policy describing the royalty shares, both on primary and secondary sales.

This is an implementation of an ARC-20 specification and other methods may be implemented in the same contract according to that specification.

Motivation

This ARC is defined to provide a consistent set of asset configurations and ABI methods that, together, enable a royalty payment to a Royalty Receiver. An example may include some music rights where the label, the artist, and any investors have some assigned royalty percentage that should be enforced on transfer. During the sale transaction, the appropriate royalty payments should be included or the transaction must be rejected.

Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 822.

Royalty Policy

interface RoyaltyPolicy {
    royalty_basis:     number   // The percentage of the payment due, specified in basis points (0-10,000)
    royalty_recipient: string   // The address that should collect the payment
}

A Royalty Share consists of a royalty_receiver that should receive a Royalty payment and a royalty_basis representing some share of the total payment amount.

Royalty Enforcer

The Royalty Enforcer is an instance of the contract, an Application, that controls the transfer of ASAs subject to the Royalty Policy. This is accomplished by exposing an interface defined as a set of ABI Methods allowing a grouped transaction call containing a payment and a Transfer request.

Royalty Enforcer Administrator

The Royalty Enforcer Administrator is the account that has privileges to call administrative actions against the Royalty Enforcer. If one is not set the account that created the application MUST be used. To update the Royalty Enforcer Administrator the Set Administrator method is called by the current administrator and passed the address of the new administrator.

Royalty Receiver

The Royalty Receiver is a generic account that could be set to a Single Signature, a Multi Signature, a Smart Signature, or even to another Smart Contract. The Royalty Receiver is then responsible for any further royalty distribution logic, making the Royalty Enforcement Specification more general and composable.

Royalty Basis

The Royalty Basis is value representing the percentage of the payment made during a transfer that is due to the Royalty Receiver. The Royalty Basis MUST be specified in terms of basis points.

Royalty Asset

The Royalty Asset is an ASA subject to royalty payment collection and MUST be created with the appropriate parameters.

Because the protocol does not allow updating an address parameter after it’s been deleted, if the asset creator thinks they may want to modify them later, they must be set to some non-zero address.

Asset Offer

The Asset Offer is a data structure stored in the owner’s local state. It is keyed in local storage by the byte string representing the ASA Id.

interface AssetOffer {
    auth_address:   string // The address of a marketplace or account that may issue a transfer request
    offered_amount: number // The number of units being offered
}

This concept is important to this specification because we use the clawback feature to transfer the assets. Without some signal that the current owner is willing to have their assets transferred, it may be possible to transfer the asset without their permission.

Royalty Asset Parameters

The Clawback parameter MUST be set to the Application Address of the Royalty Enforcer.

Since the Royalty Enforcer relies on using the Clawback mechanism to perform the transfer the Clawback should NEVER be set to the zero address. The Freeze parameter MUST be set to the Application Address of the Royalty Enforcer if FreezeAddr != ZeroAddress, else set to ZeroAddress. If the asset creator wants to allow an ASA to be Royalty Free after some conditions are met, it should be set to the Application Address. The Manager parameter MUST be set to the Application Address of the Royalty Enforcer if ManagerAddr != ZeroAddress, else set to ZeroAddress. If the asset creator wants to update the Freeze parameter, this should be set to the application address. The Reserve parameter MAY be set to anything. The DefaultFrozen MUST be set to true.

Third Party Marketplace

In order to support secondary sales on external markets this spec was designed such that the Royalty Asset may be listed without transferring it from the current owner’s account. A Marketplace may call the transfer request as long as the address initiating the transfer has been set as the auth_address through the offer method in some previous transaction by the current owner.

ABI Methods

The following is a set of methods that conform to the ABI specification meant to enable the configuration of a Royalty Policy and perform transfers. Any Inner Transactions that may be performed as part of the execution of the Royalty Enforcer application SHOULD set the fee to 0 and enforce fee payment through fee pooling by the caller.

Set Administrator:

set_administrator(
    administrator: address,
)

Sets the administrator for the Royalty Enforcer contract. If this method is never called the creator of the application MUST be considered the administrator. This method SHOULD have checks to ensure it is being called by the current administrator.

Set Policy:

set_policy(
    royalty_basis: uint64,
    royalty_recipient: account,
)

Sets the policy for any assets using this application as a Royalty Enforcer. The royalty_basis is the percentage for royalty payment collection, specified in basis points (e.g., 1% is 100). A Royalty Basis SHOULD be immutable; if an application call is made that would overwrite an existing value, it SHOULD fail.

Set Payment Asset:

set_payment_asset(
    payment_asset: asset,
    allowed: boolean,
)

The payment_asset argument represents the ASA id that is acceptable for payment. The contract logic MUST opt into the asset specified in order to accept them as payment as part of a transfer.

Transfer:

transfer_algo_payment(
    royalty_asset: asset,
    royalty_asset_amount: uint64,
    from: account,
    to: account,
    royalty_receiver: account,
    payment: pay,
    current_offer_amount: uint64,
)

And

transfer_asset_payment(
    royalty_asset: asset,
    royalty_asset_amount: uint64,
    from: account,
    to: account,
    royalty_receiver: account,
    payment: axfer,
    payment_asset: asset,
    current_offer_amount: uint64,
)

Transfers the Asset after checking that the royalty policy is adhered to. The royalty_asset is the ASA ID to be transferred. The from parameter is the account the ASA is transferred from. The to parameter is the account the ASA is transferred to.

Read Only Methods

Get Policy:

get_policy()(address,uint64)

Gets the current Royalty Policy setting for this Royalty Enforcer. The return value is a tuple of type (address,uint64).

Get Offer:

get_offer(
    royalty_asset: asset,
    from: account,
)(address,uint64)

Gets the current Asset Offer for a given asset as set by its owner.

Get Administrator:

get_administrator()address

Gets the Royalty Enforcer Administrator set for this Royalty Enforcer. The return value is of type address representing the account that may call administrative methods.

Storage

While the details of storage are described here, readonly methods are specified to provide callers with a method to retrieve the information without having to write parsing logic. The exact location and encoding of these fields are left to the implementer.

Global Storage

The parameters that describe a policy are stored in Global State. The relevant keys are: royalty_basis - The percentage specified in basis points of the payment. royalty_receiver - The account that should be paid the royalty.

Local Storage

For an offered Asset, the authorizing address and amount offered should be stored in a Local State field for the account offering the Asset.

Full ABI Spec

{
    "name": "ARC18",
    "methods": [
        {
            "name": "set_policy",
            "args": [
                {
                    "type": "uint64",
                    "name": "royalty_basis"
                },
                {
                    "type": "address",
                    "name": "royalty_receiver"
                }
            ],
            "returns": {
                "type": "void"
            },
            "desc": "Sets the royalty basis and royalty receiver for this royalty enforcer"
        },
        {
            "name": "get_policy",
            "args": [],
            "returns": {
                "type": "(address,uint64)"
            },
            "read-only": true
        }
    ],
    "desc": "ARC18 Contract providing an interface to create and enforce a royalty policy over a given ASA, See https://github.com/algorandfoundation/ARCs/blob/main/ARCs/arc-0018.md for details.",
    "networks": {}
}

Rationale

The motivation behind defining a Royalty Enforcement specification is the need to guarantee a portion of a payment is received by select royalty collector on sale of an asset. The use of a smart contract as a clawback address is a guaranteed way to know an asset transfer is only ever made when certain conditions are met.

Copyright

Copyright and related rights waived via CCO.