SRC-8; Bridged Asset Standard
bitzoic opened this issue · 5 comments
Abstract
The following standard attempts to define the retrieval of relevant on-chain metadata for any bridged Native Assets. Any contract that implements the SRC-8 standard MUST implement the SRC-7 standard.
Motivation
The SRC-8 standard seeks to enable relevant data for bridged assets on the Fuel Network. This data includes the origin chain, address, ID, decimals, and any arbitrary data. All metadata queries are done through a single function to facilitate cross-contract calls.
Prior Art
The use of generic metadata for Native Assets is defined in the SRC-7 standard. This standard integrates into the existing SRC-7 standard.
Specification
Token Creation
The SubId
of the token MUST be the digest of the sha256(origin_chain_id, origin_asset_address, origin_asset_id)
hash where:
origin_chain_id
is au64
of the chain ID where the asset was originally minted.origin_asset_address
is ab256
of the asset's address on the chain where the asset was originally minted.origin_asset_id
is ab256
of the asset's ID on the chain where the asset was originally minted. IF there is no ID,ZERO_B256
SHALL be used.
SRC-20 Metadata
Any bridged assets MUST use the name and symbol of the asset on the chain where the asset was originally minted.
SRC-7 Metadata
- origin_chain
The key origin_chain
SHALL return an Int
variant the chain ID where the asset was originally minted.
- origin_asset_address
The key origin_asset_address
SHALL return a B256
variant of the asset's address on the chain where the asset was originally minted.
- origin_asset_id
The key origin_asset_id
MAY return a B256
variant of the asset's ID on the chain where the asset was originally minted. IF there is no ID, None
SHALL be returned.
- origin_asset_decimals
The key origin_decimals
MAY return an Int
variant of the asset's decimals on the chain where the asset was originally minted. IF there are no decimals, None
SHALL be returned.
- origin_asset_data
The key origin_asset_data
MAY return any arbitrary Metadata
of the asset's data that exists on the chain where the asset was originally minted. IF there is no data, None
SHALL be returned.
Rationale
The SRC-8 standard should allow for data on any bridged assets on the Fuel Network. This standard builds off existing standards and should allow other contracts to query any relevant information on the bridged asset.
Backwards Compatibility
This standard is compatible with Fuel's Native Assets, the SRC-20 standard, and the SRC-7 standard.
The standard is also compatible with both tokens and NFTs native to other ecosystems by introducing a token ID element of the original chain.
Security Considerations
There is no guarantee that any data returned from the origin_asset_data
key is up to date with the data on the origin chain.
This standard does not call external contracts, nor does it define any mutations of the contract state.
Example
impl SRC7 for Contract {
fn metadata(asset: AssetId, key: String) -> Option<Metadata> {
if (asset != AssetId::from(ZERO_B256)) {
return Option::None();
}
match key {
String::from_ascii_str("origin_chain") => {
Option::Some(Metadata::Int(1))
},
String::from_ascii_str("origin_asset_address") => {
let origin_asset_address = ZERO_B256;
Option::Some(Metadata::B256(origin_asset_address))
},
String::from_ascii_str("origin_asset_id") => {
let origin_asset_id = ZERO_B256;
Option::Some(Metadata::B256(origin_asset_id))
},
String::from_ascii_str("origin_asset_decimals") => {
Option::Some(Metadata::Int(1))
},
String::from_ascii_str("origin_asset_data") => {
let origin_asset_data = String::from_ascii_str("My Data");
Option::Some(Metadata::StringData(origin_asset_data))
},
_ => Option::None(),
}
}
}
impl SRC20 for Contract {
fn total_assets() -> u64 {
1
}
fn total_supply(asset: AssetId) -> Option<u64> {
match asset {
AssetId::from(ZERO_B256)) => Option::Some(1),
_ => Option::None(),
}
}
fn name(asset: AssetId) -> Option<String> {
match asset {
AssetId::from(ZERO_B256)) => Option::Some(String::from_ascii_str("Name")),
_ => Option::None(),
}
}
fn symbol(asset: AssetId) -> Option<String> {
match asset {
AssetId::from(ZERO_B256)) => Option::Some(String::from_ascii_str("Symbol")),
_ => Option::None(),
}
}
fn decimals(asset: AssetId) -> Option<u8> {
match asset {
AssetId::from(ZERO_B256)) => Option::Some(0u8),
_ => Option::None(),
}
}
}
Looks good some initial feedback:
- Links to SRC-7 give file not found :
the [SRC-7](https://github.com/FuelLabs/sway-standards/tree/master/standards/src_7) standard.
origin_asset_id is a u64 of the asset's ID on the chain where the asset was originally minted. IF there is no ID, 0u64 SHALL be used.
Is it worth considering this value being anOption< u64 >
; to distinguish between a fungible token (id None) and an NFT with an id of 0? Where this Option is used in the hash to create the SubId.- In the example; consider changing the order of impl blocks so that it's clear that theres only 1 asset when reading the
if
statement in the impl SRC7 block
Few things that come to my mind:
origin_asset_id
-> this should be B256 / U256. An example of this would be ERC721 or ERC1155, where theID
of the non-fungible / fungible assets is an uint256 - this is just for theSubId = sha256(origin_chain_id, origin_asset_address, origin_asset_id)
. The metadata could fit in aBytesData
type I guess, though we could consider adding another typeb256
for SRC-7.- What 's the rationale behind making the
origin_asset_decimals
aBytesData
? I need to reread the standards for fungible assets, but I think they are anuint8
, for the cases they implement it. - wrt
origin_chain_id
, I am not sure what is the spec for EVM and non-EVMs. I would double check that we do not limit the size of this one as well, you never know when a funky chain can use something like the max of uint256 as chain ID.
Looks good some initial feedback:
- Links to SRC-7 give file not found :
the [SRC-7](https://github.com/FuelLabs/sway-standards/tree/master/standards/src_7) standard.
This link will not work until the SRC-7 PR is merged.
origin_asset_id is a u64 of the asset's ID on the chain where the asset was originally minted. IF there is no ID, 0u64 SHALL be used.
Is it worth considering this value being anOption< u64 >
; to distinguish between a fungible token (id None) and an NFT with an id of 0? Where this Option is used in the hash to create the SubId.
There is no Hash
trait implemented on an Option<T>
. This may be possible in the future but serialization/deserialization would be needed.
- In the example; consider changing the order of impl blocks so that it's clear that theres only 1 asset when reading the
if
statement in the impl SRC7 block
Could you elaborate on this? The match
statement is also a return statement and the if
block just ensures any random AssetId
isn't passed.
origin_asset_id
-> this should be B256 / U256. An example of this would be ERC721 or ERC1155, where theID
of the non-fungible / fungible assets is an uint256 - this is just for theSubId = sha256(origin_chain_id, origin_asset_address, origin_asset_id)
. The metadata could fit in aBytesData
type I guess, though we could consider adding another typeb256
for SRC-7.
I have considered adding a b256
to the SRC-7 standard as I think this would be a frequently used type. I was looking to get some feedback before adding this. I can also make the change to a b256
.
- What 's the rationale behind making the
origin_asset_decimals
aBytesData
? I need to reread the standards for fungible assets, but I think they are anuint8
, for the cases they implement it.
This is a typo. It should be a IntData
type.
- wrt
origin_chain_id
, I am not sure what is the spec for EVM and non-EVMs. I would double check that we do not limit the size of this one as well, you never know when a funky chain can use something like the max of uint256 as chain ID.
I am also unsure what the spec calls for in this case. Perhaps this is a good use case of the newly added u256
type.
Could you elaborate on this? The
match
statement is also a return statement and theif
block just ensures any randomAssetId
isn't passed.
only a nit; I just meant to switch the order of impl blocks so that SRC20 is impl'd first. Perhaps it was just a smooth brain moment but it didn't click until I saw the SRC20 block what the purpose of the if
block in impl SRC7 was.
impl SRC20 for Contract {
...
}
impl SRC7 for Contract {
...
}