fio.erc721 - FIONFT Contract Specification

Notes: _beforeTokenTransfer action of erc721 OZ implementation has been overridden to prevent tranasfers of NFTs to the contract address. Error: “Cannot transfer to contract”

Public/External Actions

Actions

Description

Actions

Description

wrapnft

mint to account with oracle approvals

unwrapnft

burn (called by user)

burnnft

oracles can burn nft by consensus

regoracle

register oracle

unregoracle

unregister oracle

regcust

register custodian

unregcust

unregister custodian

listDomainsOfOwner

Returns list of tokenIds owned by the provided ethereum account

getOracle

Get oracle status by ethereum account

getCustodian

Get custodian status by ethereum account

getApproval

getApproval status by indexhash. This is emitted in consensus_activity event after a successful approval has been executed.

tokenURI

retrieve JSON information about NFT

setBaseURI

Change the baseURI path (custodian only)

_baseURI

Retrieve baseURI path (used as first portion of tokenURI)

State Variables

State variables

Description

State variables

Description

custodian_count

Keeps track of the number of custodians active in the oracle. Required for consensus math

oracle_count

Keeps track of the number of custodians active in the oracle. Required for consensus math

oraclelist

list of active oracles

custodianlist

list of active custodians(private)

_baseURIextended

Stored baseURI

attribute[]

Holds the domain names for each tokenID 1:1

Mappings

Mapping

Description

Mapping

Description

attribute

Keeps track of the domain names by tokenId

approvals

Keeps track of the approval statuses

_owners

Keeps track of token owners

 

Events

Events

Description

Events

Description

wrapped

Event emitted when wrap has completed.
emit wrapped(account, tokenID, obtid);

unwrapped

Event emitted when user has unwrapped NFT.
emit unwrapped(fioaddress, tokenID);

custodian_registered

Event emitted when a custodian has been registered
event custodian_registered(address ethaddress, uint256 eid);

custodian_unregistered

Event emitted when a custodian has been unregistered
event custodian_unregistered(address ethaddress, uint256 eid);

oracle_registered

Event emitted when an oracle has been registered
event oracle_registered(address ethaddress, uint256 eid);

oracle_unregistered

Event emitted when an oracle has been unregistered
event oracle_unregistered(address ethaddress, uint256 eid);

consensus_activity

Event emitted when any oracle has approved a transaction
event consensus_activity(string signer, bytes32 hash);

Important: indexhash is emitted after every approval and is required to check status with getApproval action

Mappings

Mappings

Members

Description

Mappings

Members

Description

Custodians
mapping (address=>custodian) custodians

custodian
- active

Keeps track of the custodians that registered another custodian and their activation

Oracles
mapping (address=>oracles) oracles

oracle
- active

Keeps track of the custodians that registered an oracle and the oracles activation count

Approvals
mapping (bytes32 => bool) approver;

pending
- approvals
- account
- obtid

Keeps track of the approvals by obtid, the number of approvals, and the details of the approval
This table is also used for oracle and custodian registration

 

Roles

Role Name

Actions

Description

Role Name

Actions

Description

OWNER_ROLE

Deploy contract

default

ORACLE_ROLE

wrapnft/butnnft

Only FIONFT oracle may use this function

CUSTODIAN_ROLE

regoracle
unregoracle
regcust
unregcust
pause
unpause

Only FIONFT custodian may use this function

 

Misc. Requirements

Issue

Summary

Decision

Issue

Summary

Decision

Max transaction size

Should we put a max transaction size limit on mint into the ERC721 contract.

Not needed

ERC721 contract key

What is the status of the key used to set the contract?

Key should only be used for spinning up the contract and should be burned.

Oracle “Admin” functions

Do we need to enable the ability for Oracles to call various contract actions such as approve, transfer, mint, etc.

This can be performed in a couple of ways:
1 - The Oracles vote in who can execute the next essential action as follows:
Oracle 1 executes wrap action to mint using FIO obtid xxxxxxxx
Oracle 2 wrap action to transfer for the same FIO obtid to Alice
Oracle 3 executes wrap action for mint, but wrong recipient is provided. Trx goes to Alice defined by oracle 1 and 2

2 - The Oracles can sign the next transfer action as a multisig
This could be quite costly on the ethereum chain, but is possible to implement.
Example of multisig on ethereum https://github.com/christianlundkvist/simple-multisig/blob/master/contracts/SimpleMultiSig.sol

Decision: We do not want any Oracle admin functions.

Custodian “Admin” functions

Are there contract actions that should be exposed to Custodian “admin” approval?

  • Pause switch is an admin function.

  • Use case: refunds. If someone calls unwrap to the wrong address and it gets lost. We may want to enable the ability for Custodians to call 2/3+1 approval for a “mint” refund.

    • This can already be done by the Oracles calling wrap to a specific Ethereum address.

TBD: Are there any other contract actions that might be used for “admin” functionality?

Unwrap fee

Is there a fee for unwrap?

Yes. Unwrapping user pays the gas fee

 

Wrap/Unwrap

wrapnft

Create FIONFT on Ethereum chain

Registering oracles

  • NFT is created using the wrapnft action

Implementation

  • New action: wrapnft

Request body

Parameter

Required

Format

Definition

Parameter

Required

Format

Definition

account

Yes

address

Public address on ETH blockchain where wrapped NFT should be delivered.

domain

Yes

string

FIO Domain being wrapped. This will be included in the NFT TokenURI

obtid

Yes

uint256

FIO chain transaction ID

Example

{ "account": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B", "domain": "dapixdev", "obtid": "dfe50aad8e2271f84f87b8e603776d7e7970c636bb899c8993c08e9e2d21c106" }

Processing

  • Request is validated per Exception handling.

  • Consensus required. Transaction is executed when all registered oracles have called wrap (submitted their “observation”).

    • 3 oracles call wrap to get it approved

    • Do the mint when the 3rd oracle calls wrap DONE

  • NFT is minted and transferred to account

Exception handling

Error condition

Trigger

Type

Error message

Error condition

Trigger

Type

Error message

Invalid tokenURI

tokenURI is not valid.

400

"Invalid tokenURI"

Invalid public address

Recipient public address is not valid

400

"Invalid public address"

Invalid account

Uninitialized address is provided

400

“Invalid account”

Invalid obtid

Obtid is is empty or uninitialized

400

“Invalid obtid”

Domain string is invalid

Length is less than 1 or greater than 64

400

“Invalid domain”

Account parameter is contract address

Cannot wrap to contract account

400

“Cannot wrap to contract account”

Already approved

Oracle address tries to approve a wrap multiple times

400

“sender has already approved this hash”

Already complete

Oracle tries to approve a wrap that has already been completed

400

“Approval already complete”

Approver must execute

Oracle tries to execute wrap it didn’t approve

400

“An approver must execute”

Not enough oracles

Oracle tries to execute wrap when there are not enough registered oracles

400

“Oracles must be 3 or greater”

No authority

The signer is not a registered Oracle and does not have authority to call this function

403

"AccessControl: account [user account] is missing role [role hash]"

Response body

Parameter

Format

Definition

Parameter

Format

Definition

Return

uint256

TokenID of minted NFT returned on success

Events

String

consensus_activity emitted on successful approval

Transfer, Wrapped, and Consensus_Activity (oracle) events emmitted on successful wrap

Example
{ logIndex: 0, transactionIndex: 0, transactionHash: '0xbc61e4a5b6b1b0aca24bc7efc41394c953722de8f179223d13531cfc7002cdbc', blockHash: '0x473345db7cecdf94afa1f33dcfb467d04f2bd184a659b01ed380880d8ef04d57', blockNumber: 29, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_d4b4d126', event: 'Transfer', args: [Result] }, { logIndex: 1, transactionIndex: 0, transactionHash: '0xbc61e4a5b6b1b0aca24bc7efc41394c953722de8f179223d13531cfc7002cdbc', blockHash: '0x473345db7cecdf94afa1f33dcfb467d04f2bd184a659b01ed380880d8ef04d57', blockNumber: 29, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_664694e0', event: 'wrapped', args: [Result] }, { logIndex: 2, transactionIndex: 0, transactionHash: '0xbc61e4a5b6b1b0aca24bc7efc41394c953722de8f179223d13531cfc7002cdbc', blockHash: '0x473345db7cecdf94afa1f33dcfb467d04f2bd184a659b01ed380880d8ef04d57', blockNumber: 29, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_52b744c3', event: 'consensus_activity', args: [Result] }

unwrapnft

Convert NFT (domain/address) on Ethereum chain to FIO domain/address on FIO chain.

Implementation

  • New action: unwrapnft

Action is signed by domain holder and does not require oracle consensus

Fees

  • Alice covers the fee to unwrap

Request body

Parameter

Required

Format

Definition

Parameter

Required

Format

Definition

tokenID

Yes

uint256

TokenID to be unwrapped (will be burned on Ethereum chain, sent to 0x000000000000…)

fio_address

Yes

String

The FIO Handle where NFT should be delivered on FIO chain

Example

{ "fio_address": "alice@wallet", "tokenID": 123 }

Processing

  • Request is validated per Exception handling.

  • No Oracle Consensus required.

  • Contract burns NFT and event is emitted

Exception handling

Error condition

Trigger

Type

Error message

Error condition

Trigger

Type

Error message

Invalid FIO handle

Recipient FIO handle is not valid with less than 3 characters or greater than 64 characters

400

"Invalid FIO handle"

Invalid TokenID

TokenID is not valid

400

"Invalid tokenID"

No authority

The signer does not own the NFT to burn

403

“Invalid token owner“

Response body

Parameter

Format

Definition

Parameter

Format

Definition

Events

String

Approval, transfer and unwrapped event emitted on successful unwrap

Example

 

burnnft

Burn target NFT. This is an action carried out by oracles for maintenance and burning domains that may be stuck across the bridge

Implementation

  • New action: burnnft

Which FIO parameter should get passed in?

  • TokenId

    • TokenId of the domain held by the user

  • obtid

    • Unique transaction id

Fees

  • Oracles cover the fee to burn

Request body

Parameter

Required

Format

Definition

Parameter

Required

Format

Definition

tokenID

Yes

uint256

TokenID to be unwrapped (will be burned on Ethereum chain, sent to 0x000000000000…)

obtid

Yes

String

FIO transaction ID or other unique identifier

Example

Processing

  • Request is validated per Exception handling.

  • Oracle Consensus required.

  • Contract burns NFT and event is emitted

Exception handling

Error condition

Trigger

Type

Error message

Error condition

Trigger

Type

Error message

Invalid FIO handle

Recipient FIO handle is not valid with less than 3 characters or greater than 64 characters

400

"Invalid FIO handle"

Invalid TokenID

TokenID is not valid

400

"Invalid tokenID"

No authority

The signer does not own the NFT to burn

403

“Invalid token owner“

Invalid obtid

Obtid is is empty or uninitialized

400

“Invalid obtid”

Already approved

Oracle address tries to approve a wrap multiple times

400

“sender has already approved this hash”

Already complete

Oracle tries to approve a wrap that has already been completed

400

“Approval already complete”

Approver must execute

Oracle tries to execute wrap it didn’t approve

400

“An approver must execute”

Response body

Parameter

Format

Definition

Parameter

Format

Definition

Events

String

Consensus_activity event emitted on successful approval by oracle

Approval, Transfer, domainburned and consensus_activity logs emitted at successful execution

Example

Oracles - register/unregister

regoracle

Register Oracle.

Registering oracles

  • Each Oracle is registered by custodians using regoracle action.

Implementation

  • New action: regoracle

Request body

Parameter

Required

Format

Definition

Parameter

Required

Format

Definition

account

Yes

Ethereum public address

Ethereum address owned by the Oracle.

Example

Processing

  • Request is validated per Exception handling.

  • Consensus required.

  • Oracles are registered by custodians. 2/3+1 of active custodians are required to complete registration of a new oracle.

  • A minimum of 3 oracles is required to approve a wrap transaction.

  • Ethereum address is registered as a valid Oracle.

Exception handling

Error condition

Trigger

Type

Error message

Error condition

Trigger

Type

Error message

Invalid account

Ethereum account is not valid

400

"Invalid account"

Self registration

Custodian tries to register itself as oracle

400

“Cannot register self”

Already registered

The Ethereum account is already registered as an Oracle

400

“Oracle already registered”

Already approved

Oracle address tries to approve a wrap multiple times

400

“sender has already approved this hash”

Already complete

Oracle tries to approve a wrap that has already been completed

400

“Approval already complete”

Approver must execute

Oracle tries to execute wrap it didn’t approve

400

“An approver must execute”

No authority

The signer is not a registered Custodian and does not have authority to call this function

403

"AccessControl: account [user account] is missing role [role hash]"

Response body

Parameter

Format

Definition

Parameter

Format

Definition

Events

String

consensus_activity event emitted successful approval

RoleGranted, oracle_registered and consensus_activity events emitted on completed registration after all approvals

Example

unregoracle

Unregister Oracle.

Implementation

  • New action: unregoracle

Request body

Parameter

Required

Format

Definition

Parameter

Required

Format

Definition

account

Yes

Ethereum account

Ethereum account owned by the Oracle

Example

Processing

  • Consensus required. Oracle is removed after 2/3+1 Custodians have called unregoracle with same ethaddress.

  • Ethereum address is removed as a valid Oracle.

  • A minimum of 3 oracles must be maintained. If only 3 Oracles are registered, unregoracle can not be called.

Exception handling

Error condition

Trigger

Type

Error message

Error condition

Trigger

Type

Error message

Invalid oracle

Oracle is not registered

400

"Oracle not registered"

No authority

The signer is not a registered Custodian and does not have authority to call this function

403

"AccessControl: account [user account] is missing role [role hash]"

Already approved

Oracle address tries to approve a wrap multiple times

400

“sender has already approved this hash”

Already complete

Oracle tries to approve a wrap that has already been completed

400

“Approval already complete”

Approver must execute

Oracle tries to execute wrap it didn’t approve

400

“An approver must execute”

Invalid account

Ethereum account is not valid

400

“Invalid account”

Minimum 3 oracles required

Trying to unregister an oracle when there are only 3 registered

400

“Minimum 3 oracles required”

Response body

Parameter

Format

Definition

Parameter

Format

Definition

Event

String

consensus_activity event emitted on successful approval

RoleRevoked, oracle_unregistered and consensus_activity events emitted on successful unregister of an oracle

Example

Custodians - register/unregister

regcust

Register Custodian

Registering Custodians

  • Each Custodian is registered using regcust action.

Implementation

  • New action: regcust

Request body

Parameter

Required

Format

Definition

Parameter

Required

Format

Definition

account

Yes

Ethereum account

Ethereum account owned by the Custodian

Example

Processing

  • Request is validated per Exception handling.

  • Consensus required. Custodians are registered as follows:

    • The initial 10 custodians are set upon contract deployment by the contract owner.

    • Subsequently, 2/3+1 of active custodians must call regcust to register a new custodian.

  • Ethereum account is added as a valid Custodian.

Exception handling

Error condition

Trigger

Type

Error message

Error condition

Trigger

Type

Error message

Invalid account

Ethereum account is not valid

400

"Invalid account"

Self registration

Custodian tries to register itself as custodian

400

“Cannot register self”

Already registered

The Ethereum account is already registered as a custodian

400

“Already registered”

Already approved

Custodian account tries to approve a wrap multiple times

400

“sender has already approved this hash”

Already complete

Custodian tries to approve a wrap that has already been completed

400

“Approval already complete”

Approver must execute

Custodian tries to execute wrap it didn’t approve

400

“An approver must execute”

No authority

The signer is not a registered Custodian and does not have authority to call this function

403

"AccessControl: account [user account] is missing role [role hash]"

Response body

Parameter

Format

Definition

Parameter

Format

Definition

Event

String

consensus_activity event emitted on successful approval

RoleGranted, custodian_registered and consensus_activity emitted on successful registration of a custodian

Example

unregcust

Unregister Custodian.

Implementation

  • New action: unregcust

Request body

Parameter

Required

Format

Definition

Parameter

Required

Format

Definition

account

Yes

Ethereum public address

Ethereum address owned by the Custodian

Example

Processing

  • Request is validated per Exception handling.

  • Consensus required. Custodian is removed after 2/3+1 of active custodians have called unregcust the same Ethereum address.

  • Ethereum address is removed as a valid Custodian.

  • A minimum of 7 Custodians must be maintained. If only 7 Custodians are registered, unregcust can not be called.

  • A custodian can not singlehandedly unregister themselves. All changes require 2/3+1. Bug, the custodian can call unregcust for themselves and be counted in the 2/3+1.

Exception handling

Error condition

Trigger

Type

Error message

Error condition

Trigger

Type

Error message

Invalid custodian

Custodian is not registered

400

"Custodian not registered"

No authority

The signer is not a registered Custodian and does not have authority to call this function

403

"AccessControl: account [user account] is missing role [role hash]"

Already approved

Oracle address tries to approve a wrap multiple times

400

“sender has already approved this hash”

Already complete

Oracle tries to approve a wrap that has already been completed

400

“Approval already complete”

Approver must execute

Oracle tries to execute wrap it didn’t approve

400

“An approver must execute”

Invalid account

Ethereum account is not valid

400

“Invalid account”

Not enough custodians

Trying to unregister a custodian when there are not enough remaining

400

“Must contain 7 custodians”

Response body

Parameter

Format

Definition

Parameter

Format

Definition

status

String

consensus_activity event emitted on successful approval

RoleRevoked, custodian_unregistered and consensus_activity events are emitted after successfully unregistering a custodian

Example



tokenURI

Get tokenURI JSON

Implementation

  • New action: tokenURI

Request body

Parameter

Required

Format

Definition

Parameter

Required

Format

Definition

_tokenId

Yes

Integer

TokenId of NFT

Example

Processing

  • JSON is formed inline and returned in response

  • Function is overriden

Exception handling

Error condition

Trigger

Type

Error message

Error condition

Trigger

Type

Error message

NFT tokenId does not exist

Token ID not present

400

No token

Response