fip | title | status | type | author | created | updated |
---|---|---|---|---|---|---|
17a |
FIO Token Wrapping |
Final |
Functionality |
Pawel Mastalerz <[email protected]> |
2020-09-17 |
2022-10-19 |
Functionality proposed in this FIP is intended to make wrapping of FIO Tokens easier.
Wrapping cannot be accomplished entirely inside the FIO Protocol and therefore will not be possible with this FIP alone. This FIP only defines the functionality required in FIO Protocol. For detailed overview of the greater initiative see Enable FIO Token and Domain NFT wrapping.
Proposed new actions:
Contract | Action | Endpoint | Description |
---|---|---|---|
fio.oracle | wraptokens | wrap_fio_tokens | Transfers FIO Tokens to designated FIO account on the way to another chain. |
fio.oracle | unwraptokens | Transfers FIO Tokens to designated FIO account back from another chain. | |
fio.oracle | regoracle | Registers an Oracle. | |
fio.oracle | unregoracle | Unregisters an Oracle. | |
fio.oracle | setoraclefee | Allows Oracle to set Oracle fee. | |
get_oracle_fees | Fetches Oracles fees. |
It is believed that the ability to wrap FIO Tokens and Domain NFTs will be beneficial to the FIO Community. It will open up new use cases for FIO Tokens and Domains such as:
- Enable FIO Domains to be traded on NFT trading sites such as Open Sea as ERC-721s.
- Enable FIO tokens to be used in a rapidly growing Defi ecosystem such as Uniswap which rewards token holders for providing liquidity to decentralized exchanges.
This FIP proposes a new fio.oracle system contract (controlled by eosio account or block producers). It will allow pre-defined oracles to transfer FIO Tokens owned by the smart contract if all oracles agree to do so.
- User A wants to wrap X FIO Tokens to P public address on C chain.
- User A fetches amount of Oracle fee using /get_oracle_fees.
- User A executes wraptokens action and passes in:
- amount (X) of FIO tokens to wrap
- public_address (P) on chain C where tokens should be delivered
- chain_code C
- max_oracle_fee - maximum amount of FIO user A is willing to pay the Oracles
- X FIO Tokens are transferred to wrapping smart contract account.
- Oracle fee is transferred from user A and distributed evenly to all registered Oracles.
- Oracles monitor every block looking for wraptoken action (not covered by this FIP).
- Once detected Oracles trigger minting of wrapped tokens on another chain (not covered by this FIP).
- User A wants to unwrap X FIO Tokens to P public address on FIO Chain.
- User A executes action on another chain (not covered by this FIP):
- Oracles monitor every block on another chain (not covered by this FIP).
- Once detected each Oracle executes unwraptoken action and passes in:
- fio_public_key where to transfer tokens
- amount (X) of FIO tokens to transfer
- Once the last Oracle executes unwraptoken action, the tokens are transferred to P.
- Each Oracle is registered using regoracle action. Only top 21 BPs can register Oracles via an msig.
- Each Oracle submits a fee for token and domain wrapping using setoraclefee.
- Oracle fee is set to median of fees provided by all Oracles and then multiplied by number of registered oracles.
Transfers FIO Tokens to designated FIO account for the purpose of wrapping.
Parameter | Required | Format | Definition |
---|---|---|---|
amount | Yes | Positive Int | Amount of SUFs to wrap. |
chain_code | Yes | String | Chain code of destination blockchain. Min chars: 1, Max chars: 10, Characters allowed: ASCII a-z0-9, case-insensitive. |
public_address | Yes | String | Public address on destination blockchain where wrapped token should be delivered. |
max_oracle_fee | Yes | Positive Int | Maximum amount of SUFs the user is willing to pay as Oracle fee. Should be preceded by /get_oracle_fees |
max_fee | Yes | Positive Int | Maximum amount of SUFs the user is willing to pay for fee. Should be preceded by /get_fee for correct value. |
tpid | Yes | FIO Address | FIO Address of the entity which generates this transaction. TPID rewards will be paid to this address. Set to empty if not known. |
actor | Yes | 12 character string | Valid actor of signer |
{
"amount": 100000000000,
"chain_code": "ETH",
"public_address": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
"max_oracle_fee": 1000000000,
"max_fee": 1000000000,
"tpid": "rewards@wallet",
"actor": "aftyershcu22"
}
- Request is validated per Exception handling.
- Chain wrap_fio_token fee is collected.
- RAM of signer is increased
- Tokens are transferred to fio.oracle contract.
- Fee distribution:
- Oracle fee is transferred from actor account to all registered oracles in even amount.
Error condition | Trigger | Type | fields:name | fields:value | Error message |
---|---|---|---|---|---|
Invalid amount | Transfer amount is not valid. | 400 | "amount" | Value sent in, e.g. "-1" | "Invalid amount" |
Insufficient balance | Balance is less than amount + oracle fee + chain fee | 400 | "amount" | Value sent in, e.g. "100000000000" | "Insufficient balance" |
Invalid oracle fee value | max_oracle_fee format is not valid | 400 | "max_oracle_fee" | Value sent in, e.g. "-100" | "Invalid oracle fee value" |
Invalid public address | Recipient public address is not valid | 400 | "public_address" | Value sent in, e.g. "notvalidethaddress" | "Invalid public address" |
Invalid chain code | Recipient chain code is not valid | 400 | "chain_code" | Value sent in, e.g. "BTC!@#$%^&()" | "Invalid chain code" |
Oracle fee exceeds maximum | Actual oracle fee is greater than supplied max_oracle_fee | 400 | "max_oracle_fee" | Value sent in, e.g. "1000000000" | "Oracle fee exceeds supplied maximum" |
Invalid fee value | max_fee format is not valid | 400 | "max_fee" | Value sent in, e.g. "-100" | "Invalid fee value" |
Fee exceeds maximum | Actual fee is greater than supplied max_fee | 400 | "max_fee" | Value sent in, e.g. "1000000000" | "Fee exceeds supplied maximum" |
Invalid TPID | tpid format is not valid | 400 | "tpid" | Value sent in, e.g. "notvalidfioaddress" | "TPID must be empty or valid FIO address" |
Parameter | Format | Definition |
---|---|---|
status | String | OK if successful |
oracle_fee_collected | Int | Amount of SUFs collected as Oracle fee |
fee_collected | Int | Amount of SUFs collected as fee |
{
"status": "OK",
"oracle_fee_collected": 2000000000,
"fee_collected": 2000000000
}
Transfers FIO Tokens from fio.oracle to designated FIO account.
Parameter | Required | Format | Definition |
---|---|---|---|
amount | Yes | Positive Int | Amount of SUFs to unwrap. |
obt_id | Yes | String | Other Blockchain Transaction ID to identify specific transaction which triggers the unwrap. |
fio_address | Yes | String | FIO Address where domain should be delivered. |
actor | Yes | 12 character string | Valid actor of signer |
{
"amount": 100000000000,
"obt_id": "0x399b9129571fee35d450d60fffe3652433b5295b0161e622d2d27b18b04784fe",
"fio_address": "alice@wallet",
"actor": "aftyershcu22"
}
- Request is validated per Exception handling.
- If all other Oracles have alreday called unwraptokens for supplied obt_id, amount is transferred from fio.oracle to account which hashes down from FIO Public Key mapped to supplied FIO Address. If does not exist, it is created.
- RAM of signer is increased
- Consensus
- verify fio.address is registered
- search for obt_id inside
oracle_votes
- if found, search if oracle has voted prior. If oracle has not voted, add the account to the voters' vector
- if not found, emplace a new record into
oracle_votes
table. - compute the number of registered oracles and total votes inside the record for that obt_id
- If 100% consensus is achieved,
isComplete
flag is set as true, and tokens are transferred to the fio.address supplied.
Error condition | Trigger | Type | fields:name | fields:value | Error message |
---|---|---|---|---|---|
Invalid amount | Transfer amount is not valid. | 400 | "amount" | Value sent in, e.g. "-1" | "Invalid amount" |
Invalid FIO Address | FIO Address is invalid or does not exist | 400 | "fio_address" | Value sent in, e.g. "alice@wallet" | "Invalid FIO Address" |
Insufficient balance | Balance is less than amount | 400 | "amount" | Value sent in, e.g. "100000000000" | "Insufficient balance" |
Not an Oracle | actor is not a registered Oracle | 400 | "actor" | Value sent in, e.g. "aftyershcu22" | "Not a registered Oracle" |
Oracle already voted | Oracle has already called unwraptokens for obt_id | 400 | "actor" | Value sent in, e.g. "aftyershcu22" | "Oracle has already voted." |
Parameter | Format | Definition |
---|---|---|
status | String | OK if successful |
{
"status": "OK"
}
Register Oracle.
Parameter | Required | Format | Definition |
---|---|---|---|
oracle_actor | Yes | 12 character string | Valid actor (account) of oracle. |
actor | Yes | 12 character string | Valid actor of signer |
{
"oracle_actor": "aftyershcu21",
"actor": "eosio"
}
- Request is validated per Exception handling.
- regoracle must be approved by eosio permissions ( BP msig )
- Verifies oracle is a top 21 block producer (Only top 21 BPs can register as an oracle )
- Emplaces a new record into the oracles table
Error condition | Trigger | Type | fields:name | fields:value | Error message |
---|---|---|---|---|---|
Invalid oracle | Oracle is not valid or does not exist | 400 | "oracle_actor" | Value sent in, e.g. "alice" | "Invalid oracle" |
No authority | The signer does not have authority to register oracles | 403 | Type: invalid_signature |
Parameter | Format | Definition |
---|---|---|
status | String | OK if successful |
{
"status": "OK"
}
Unregister Oracle.
Parameter | Required | Format | Definition |
---|---|---|---|
oracle_actor | Yes | 12 character string | Valid actor (account) of oracle. |
{
"oracle_actor": "aftyershcu21"
}
- Request is validated per Exception handling.
- Only top 21 BPs via msig (eosio authority) can unregister an oracle
- oracle_actor is removed
Error condition | Trigger | Type | fields:name | fields:value | Error message |
---|---|---|---|---|---|
Invalid oracle | Oracle is not registered | 400 | "oracle_actor" | Value sent in, e.g. "alice" | "Invalid oracle" |
No authority | The signer does not have authority to register oracles | 403 | Type: invalid_signature |
Parameter | Format | Definition |
---|---|---|
status | String | OK if successful |
{
"status": "OK"
}
Allows Oracle to set a fee they desire for wrapping.
Parameter | Required | Format | Definition |
---|---|---|---|
wrap_fio_domain | Yes | Positive Int | Amount of SUFs the oracle is requesting for wrapping FIO Domain. |
wrap_fio_tokens | Yes | Positive Int | Amount of SUFs the oracle is requesting for wrapping FIO Tokens. |
actor | Yes | 12 character string | Valid actor of signer |
{
"wrap_fio_domain": 2000000000,
"wrap_fio_tokens": 2000000000,
"actor": "aftyershcu21"
}
- Request is validated per Exception handling.
- Actor must be registered oracle.
- Oracle fees are set
- RAM of signer is increased
- Each Oracle submits a fee for token and domain wrapping using
setoraclefee
. - Oracle fee is set to median of fees provided by all Oracles and then multiplied by number of registered oracles.
Error condition | Trigger | Type | fields:name | fields:value | Error message |
---|---|---|---|---|---|
Invalid wrap_fio_domain value | wrap_fio_domain format is not valid | 400 | "wrap_fio_domain" | Value sent in, e.g. "-100" | "Invalid wrap_fio_domain value" |
Invalid wrap_fio_tokens value | wrap_fio_tokens format is not valid | 400 | "wrap_fio_tokens" | Value sent in, e.g. "-100" | "Invalid wrap_fio_tokens value" |
Not an Oracle | actor is not a registered Oracle | 400 | "actor" | Value sent in, e.g. "aftyershcu22" | "Not a registered Oracle" |
Parameter | Format | Definition |
---|---|---|
status | String | OK if successful |
{
"status": "OK"
}
Returns current fees for wrapping
None
- Fees are computed and returned
- Median is derived for each oracle fee type and multiplied by number of oracles
- Example:
- Oracle 1
- wrap_fio_domain: 2000000000
- wrap_fio_tokens: 2000000000
- Oracle 2
- wrap_fio_domain: 3000000000
- wrap_fio_tokens: 5000000000
- Oracle 3
- wrap_fio_domain: 5000000000
- wrap_fio_tokens: 9000000000
- Fees are:
- wrap_fio_domain: median is 3000000000 x 3 oracles = 9000000000
- wrap_fio_domain: median is 5000000000 x 3 oracles = 15000000000
- Oracle 1
Parameter | Format | Definition |
---|---|---|
wrap_fio_domain | Positive Int | Fee to wrap domain in SUFs. |
wrap_fio_tokens | Positive Int | Fee to wrap tokens in SUFs. |
{
"wrap_fio_domain": 9000000000,
"wrap_fio_tokens": 15000000000,
}
Even though wrapping could be accomplished without any new functionality being added to the FIO Protocol, it would be hard. Specifically:
- /transfer_fio_domain does not support memo to pass intended destination public address on another chain. This would require the information to be exchanged off-chain.
- /transfer_tokens_pub_key does not support memo either. /transfer_tokens_fio_add or /record_obt_data would have to be used, but even then the information would have to be provided as part of free-form memo field.
get_wrapped_fio_domains and get_wrapped_fio_tokens calls were considered, but deemed unnecessary, as selected FIO Token and Domain NFT wrapping solution will rely on monitoring every block.
An approach was considered to make the contract generic, so that anyone could create a wrapping system, but configuring that generic contract and building their own oracles and smart contracts on another chain. This approach was deemed too complex to consider at this stage, but could be revisited in the future. In the meantime anyone can submit their own FIP to deploy a new wrapping contract to the FIO Chain.
TBD
The following changes were included in fio.contracts v2.8.0
- New contract actions
wraptokens
,unwraptokens
,regoracle
,unregoracle
, andsetoraclefee
were added
The following changes were included in fio v3.4.0
- New
wrap_fio_tokens
andget_oracle_fees
API endpoints were added
New actions with no impact on existing functionality.
- Consider generic wrapping contract.