Skip to content

ParallelChain F Request for Comments 2 (PRFC 2)

PRFC Title Author Version Date First Published
2 Non-Fungible Token Standard ParallelChain Lab 2 July 23rd, 2022

Summary

ParallelChain F Request for Comments 2 defines a standard interface for non-fungible tokens implemented as ParallelChain F smart contracts. "Non-Fungible Tokens" or NFTs is taken here to have the same meaning as in Ethereum's ERC-721, namely a set of transferable entities on a blockchain with identification metadata unique to its creator. For example, a deed for a plot of land is a non-fungible token, since a deed identifies a singular, unique plot of land (i.e., no two deeds identifies the same plot of land).

A standard contract interface for non-fungible tokens allows more seamless interoperability, since applications can make the simplifying assumption that all PRFC 2-implementing contracts always export the same, named set of Methods (they may export more).

The 'Required Methods' section lists the set of methods that all smart contracts that want to be PRFC 2-compliant must implement, as well as the behavior that each defined method must exhibit. Required behavior involves emitting certain events. These are also listed and described.

Glossary

Collection: A set of Tokens with shared properties. A single PRFC 2-implementing contract represents a single Collection. For example, 'CryptoKitties' is a collection, whilst a single CryptoKitty is a Token.

Token: An instance of a Collection.

Spender: An account that approved (using method set_spender or set_exclusive_spender) to transfer tokens on behalf of an owner. Any token can have at most one Spender.

Exclusive Spender: A spender that is approved to transfer all tokens owned by an owner. An account can be made an Exclusive Spender either through a single call to set_exclusive_spender (preferred), or multiple calls to set_spender.

Required types

type TokenID = String;
struct Collection {
    name: String,
    symbol: String,

    // Recommendation: uri should be an Internet URL, viewable on a browser.
    uri: Option<String>,
}
struct Token {
    id: TokenID,

    // Recommendation: uri should be an Internet URL, viewable on a browser.
    uri: Option<String>,
    owner: Option<PublicAddress>,
    spender: Option<PublicAddress>,
    exclusive_spender: Option<PublicAddress>
}

Required Views

The following uses syntax from Rust (version 1.59.0).

fn collection() -> Collection

Returns information about the Collection represented by this contract.

fn token_ids() -> Vec<TokenID>

Returns the IDs of all tokens in this Collection.

fn token_ids_of(owner: PublicAddress) -> Vec<TokenID>

Returns the IDs of all tokens owned by owner.

fn tokens(ids: Vec<TokenID>) -> Vec<Token>

Returns information about the Tokens identified by ids.

If an ID does not identify a token, it must not appear in the returned vector.

Required Actions

fn transfer(token_id: TokenID, to_address: Option<PublicAddress>)

'transfer' transfers the token identified by token_id from the account identified by txn.from_address (its owner), to the account identified by to_address. If to_address is None, this burns the token.

'transfer' must panic if get_owner(token_id) != txn.from_address.

Event Transfer must trigger if 'transfer' is successful.

fn transfer_from(from_address: PublicAddress, to_address: Option<PublicAddress>, token_id: TokenID)

'transfer_from' transfers the token identified by token_id from the account identified by from_address, to the account identified by to_address, on behalf of the account identified by get_owner(token_id). If to_address is None, this burns the Token.

'transfer_from' must panic if: 1. get_spender(token_id) != txn.from_address. 2. get_owner(token_id) != Some(from_address). 3. Or, if evaluating 1. or 2. causes a panic.

Event Transfer must trigger if ‘transfer_from’ is successful.

fn set_spender(token_id: TokenID, spender_address: Option<PublicAddress>)

If spender_address is Some, 'set_spender' gives the account identified by spender_address the right to transfer the token identified by token_id on behalf of its owner. Otherwise, it revokes get_spender(token_id) of its right.

'set_spender' must panic if: 1. get_owner(token_id) != txn.from_address. 2. get_exclusive_spender(txn.from_address) is Some and != spender_address. 3. Or, if evaluating 1. causes a panic.

Event SetSpender must trigger if 'set_spender' is successful.

fn set_exclusive_spender(spender_address: Option<PublicAddress>)

If spender_address is Some, 'set_exclusive_spender' gives the account identified by spender_address the right to transfer all tokens owned by txn.from_address. Otherwise, it revokes get_exclusive_spender(token_id) of its right. Calling this method MUST have the same side effects (except events emitted) as calling set_spender for every token_id owned by txn.from_address, with the same spender_address.

Event SetExclusiveSpender must trigger if 'set_exclusive_spender' is successful.

Required Events

In this section, ++ denotes bytes concatenation.

Transfer

Field Value
Topic 0u8 ++ token_id ++ owner_address ++ recipient_address: Option<PublicAddress>
Value Empty.

Gets trigerred on successful call to methods 'transfer', or 'transfer_from'.

SetSpender

Field Value
Topic 1u8 ++ token_id + spender_address: Option<PublicAddress>
Value Empty.

Gets triggered on successful call to method 'set_spender'.

SetExclusiveSpender

Field Value
Topic 2u8 ++ owner_address: Option<PublicAddress> ++ spender_address: Option<PublicAddress>
Value Empty.

Gets triggered on successful call to method 'set_exclusive_spender'.

GNU Public License 3.

Back to top