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 |
---|---|
mint to account with oracle approvals | |
burn (called by user) | |
register oracle | |
unregister oracle | |
register custodian | |
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 |
---|---|
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 |
---|---|
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 |
---|---|
wrapped | Event emitted when wrap has completed. |
unwrapped | Event emitted when user has unwrapped NFT. |
custodian_registered | Event emitted when a custodian has been registered |
custodian_unregistered | Event emitted when a custodian has been unregistered |
oracle_registered | Event emitted when an oracle has been registered |
oracle_unregistered | Event emitted when an oracle has been unregistered |
Mappings
Mappings | Members | Description |
---|---|---|
Custodians | custodian | Keeps track of the custodians that registered another custodian and their activation |
Oracles | oracle | Keeps track of the custodians that registered an oracle and the oracles activation count |
Approvals | pending | Keeps track of the approvals by obtid, the number of approvals, and the details of the approval |
Roles
Role Name | Actions | Description |
---|---|---|
OWNER_ROLE | Deploy contract | default |
ORACLE_ROLE | wrapnft/butnnft | Only FIONFT oracle may use this function |
CUSTODIAN_ROLE | regoracle | Only FIONFT custodian may use this function |
Misc. Requirements
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: 2 - The Oracles can sign the next transfer action as a multisig | Decision: We do not want any Oracle admin functions. |
Custodian “Admin” functions | Are there contract actions that should be exposed to Custodian “admin” approval?
| TBD: Are there any other contract actions that might be used for “admin” functionality? |
Unwrap fee | Is there a fee for | 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 |
---|---|---|---|
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 |
---|---|---|---|
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 |
---|---|---|
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
Which FIO parameter should get passed in?
FIO Address
Less likely to be input incorrectly
Requires someone who buys NFT and then wants to unwrap it to own a FIO Handle.
Devs agree that FIO Handle is probably the cleanest path.
FIO Handle hashed
Hash of address somewhat obfuscates the recipient
FIO Pub Key
Would allow for a checksum key check in the contract (which consumes gas). Not sure if the public key checksum is worth it
Current Decision: Use FIO Handle
Fees
Alice covers the fee to unwrap
Request body
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 |
---|---|---|---|
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 |
---|---|---|
Events | String | Approval, transfer and unwrapped event emitted on successful unwrap |
Example
{ logIndex: 0, transactionIndex: 0, transactionHash: '0x788d48faa5632eaad296ac61a818f1d4d7abbee940fd6c9e486b179486dfde7a', blockHash: '0xe18f9250af3b0a8f2a4f253cadf6eb6595281675b08dd477e8bf4761c79d189d', blockNumber: 32, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_aff8da75', event: 'Approval', args: [Result] }, { logIndex: 1, transactionIndex: 0, transactionHash: '0x788d48faa5632eaad296ac61a818f1d4d7abbee940fd6c9e486b179486dfde7a', blockHash: '0xe18f9250af3b0a8f2a4f253cadf6eb6595281675b08dd477e8bf4761c79d189d', blockNumber: 32, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_c254637f', event: 'Transfer', args: [Result] }, { logIndex: 2, transactionIndex: 0, transactionHash: '0x788d48faa5632eaad296ac61a818f1d4d7abbee940fd6c9e486b179486dfde7a', blockHash: '0xe18f9250af3b0a8f2a4f253cadf6eb6595281675b08dd477e8bf4761c79d189d', blockNumber: 32, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_f1c5d062', event: 'unwrapped', args: [Result] }
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 |
---|---|---|---|
account | Yes | Ethereum public address | Ethereum address owned by the Oracle. |
Example
{ "account": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B" }
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 |
---|---|---|---|
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 |
---|---|---|
Events | String | consensus_activity event emitted successful approval RoleGranted, oracle_registered and consensus_activity events emitted on completed registration after all approvals |
Example
{ logIndex: 0, transactionIndex: 0, transactionHash: '0x966530233a34543db3bffe10c57ecd27091c113eade2d42e35c9005bb000b2e6', blockHash: '0x73e13c8a1f2da8acb67341488a36bacdfa5e3d9d97414cbd4c23fac22a64bc3f', blockNumber: 25, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_ee623d8e', event: 'RoleGranted', args: [Result] }, { logIndex: 1, transactionIndex: 0, transactionHash: '0x966530233a34543db3bffe10c57ecd27091c113eade2d42e35c9005bb000b2e6', blockHash: '0x73e13c8a1f2da8acb67341488a36bacdfa5e3d9d97414cbd4c23fac22a64bc3f', blockNumber: 25, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_65a9a9ce', event: 'oracle_registered', args: [Result] }, { logIndex: 2, transactionIndex: 0, transactionHash: '0x966530233a34543db3bffe10c57ecd27091c113eade2d42e35c9005bb000b2e6', blockHash: '0x73e13c8a1f2da8acb67341488a36bacdfa5e3d9d97414cbd4c23fac22a64bc3f', blockNumber: 25, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_91bff696', event: 'consensus_activity', args: [Result] }
unregoracle
Unregister Oracle.
Implementation
New action:
unregoracle
Request body
Parameter | Required | Format | Definition |
---|---|---|---|
account | Yes | Ethereum account | Ethereum account owned by the Oracle |
Example
{ "account": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B" }
Processing
Consensus required. Oracle is removed after 2/3+1 Custodians have called
unregoracle
with sameethaddress
.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 |
---|---|---|---|
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” |
No oracles are registered | Trying to unregister an oracle when none are registered anymore | 400 | “No oracles remaining” |
Response body
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
{ logIndex: 0, transactionIndex: 0, transactionHash: '0x934e8335cbd0e48c2a323ca9bfbeb6a8b1f4145f37b68c157349ae47caf8eba3', blockHash: '0x374407d2d6f1200845a7b3c009985bb43c10f27c48dbf8ebacb814b1353f357a', blockNumber: 42, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_830fd387', event: 'RoleRevoked', args: [Result] }, { logIndex: 1, transactionIndex: 0, transactionHash: '0x934e8335cbd0e48c2a323ca9bfbeb6a8b1f4145f37b68c157349ae47caf8eba3', blockHash: '0x374407d2d6f1200845a7b3c009985bb43c10f27c48dbf8ebacb814b1353f357a', blockNumber: 42, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_27824a7b', event: 'oracle_unregistered', args: [Result] }, { logIndex: 2, transactionIndex: 0, transactionHash: '0x934e8335cbd0e48c2a323ca9bfbeb6a8b1f4145f37b68c157349ae47caf8eba3', blockHash: '0x374407d2d6f1200845a7b3c009985bb43c10f27c48dbf8ebacb814b1353f357a', blockNumber: 42, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_7e6cdb8a', event: 'consensus_activity', args: [Result] }
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 |
---|---|---|---|
account | Yes | Ethereum account | Ethereum account owned by the Custodian |
Example
{ "account": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B" }
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 |
---|---|---|---|
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 |
---|---|---|
Event | String | consensus_activity event emitted on successful approval RoleGranted, custodian_registered and consensus_activity emitted on successful registration of a custodian |
Example
{ logIndex: 0, transactionIndex: 0, transactionHash: '0x512d8dc05b8a89f7392f0520e6b2f75af7874d9fcbb1489d450a2705ba8abc54', blockHash: '0x76a8f146d5052765821349d21e276f39fdf1146b43b6e946b2668d0b06842a61', blockNumber: 59, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_e9bf03d8', event: 'RoleGranted', args: [Result] }, { logIndex: 1, transactionIndex: 0, transactionHash: '0x512d8dc05b8a89f7392f0520e6b2f75af7874d9fcbb1489d450a2705ba8abc54', blockHash: '0x76a8f146d5052765821349d21e276f39fdf1146b43b6e946b2668d0b06842a61', blockNumber: 59, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_2ea5e791', event: 'custodian_registered', args: [Result] }, { logIndex: 2, transactionIndex: 0, transactionHash: '0x512d8dc05b8a89f7392f0520e6b2f75af7874d9fcbb1489d450a2705ba8abc54', blockHash: '0x76a8f146d5052765821349d21e276f39fdf1146b43b6e946b2668d0b06842a61', blockNumber: 59, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_431af3e0', event: 'consensus_activity', args: [Result] }
unregcust
Unregister Custodian.
Implementation
New action: unregcust
Request body
Parameter | Required | Format | Definition |
---|---|---|---|
account | Yes | Ethereum public address | Ethereum address owned by the Custodian |
Example
{ "account": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B" }
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 |
---|---|---|---|
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 |
---|---|---|
status | String | consensus_activity event emitted on successful approval RoleRevoked, custodian_unregistered and consensus_activity events are emitted after successfully unregistering a custodian |
Example
{ logIndex: 0, transactionIndex: 0, transactionHash: '0x9b72e1dcad97f39a127e064dd055d44958a1af97cc83a271e78f4243dee68711', blockHash: '0x883323d797bd7d25179b021835a2f4d2200aa9aeb2f2431357574cffd8797133', blockNumber: 8, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_fbbd6f39', event: 'RoleRevoked', args: [Result] }, { logIndex: 1, transactionIndex: 0, transactionHash: '0x9b72e1dcad97f39a127e064dd055d44958a1af97cc83a271e78f4243dee68711', blockHash: '0x883323d797bd7d25179b021835a2f4d2200aa9aeb2f2431357574cffd8797133', blockNumber: 8, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_81d06a32', event: 'custodian_unregistered', args: [Result] }, { logIndex: 2, transactionIndex: 0, transactionHash: '0x9b72e1dcad97f39a127e064dd055d44958a1af97cc83a271e78f4243dee68711', blockHash: '0x883323d797bd7d25179b021835a2f4d2200aa9aeb2f2431357574cffd8797133', blockNumber: 8, address: '0x9488Ca72f463f3faB9357Fc40B2786eaeD646F8D', type: 'mined', id: 'log_e1677ec2', event: 'consensus_activity', args: [Result] }
tokenURI
Get tokenURI JSON
Implementation
New action: tokenURI
Request body
Parameter | Required | Format | Definition |
---|---|---|---|
_tokenId | Yes | Integer | TokenId of NFT |
Example
{ "_tokenId": "1" }
Processing
JSON is formed inline and returned in response
Function is overriden
Exception handling
Error condition | Trigger | Type | Error message |
---|---|---|---|
NFT tokenId does not exist | Token ID not present | 400 | No token |
Response
'data:application/json, { "name":"domain: dapixdev", "description": "Create FIO Handles on your custom FIO Domain.", "image": "ipfs://QmdKqei7KGp1fJCP1tkhNMdm9BwYFXzKLPsbSMSPW325sH" } '
0 Comments