diff --git a/README.md b/README.md index ef90876..c6b4a79 100644 --- a/README.md +++ b/README.md @@ -48,12 +48,12 @@ If you don't find what you're looking for, feel free to create an issue and prop ### Bridge - [SRC-8; Bridged Asset](./standards/src8-bridged-asset/) defines the metadata required for an asset bridged to the Fuel Network. +- [SRC-10; Native Bridge Standard](./standards/src10-native-bridge/) defines the standard API for the Native Bridge between the Fuel Chain and the canonical base chain. ### Documentation - [SRC-2; Inline Documentation](./standards/src2-inline-docs/) defines how to document your Sway files. - ## Using a standard To import a standard the following should be added to the project's `Forc.toml` file under `[dependencies]` with the most recent release: diff --git a/standards/Forc.toml b/standards/Forc.toml index ee28ffc..93ba3af 100644 --- a/standards/Forc.toml +++ b/standards/Forc.toml @@ -4,5 +4,6 @@ members = [ "src5-ownership", "src6-vault", "src7-metadata", + "src10-native-bridge", "src20-token", ] diff --git a/standards/src10-native-bridge/.docs/src-10-logo-dark-theme.png b/standards/src10-native-bridge/.docs/src-10-logo-dark-theme.png new file mode 100644 index 0000000..1eee3b5 Binary files /dev/null and b/standards/src10-native-bridge/.docs/src-10-logo-dark-theme.png differ diff --git a/standards/src10-native-bridge/.docs/src-10-logo-light-theme.png b/standards/src10-native-bridge/.docs/src-10-logo-light-theme.png new file mode 100644 index 0000000..720e473 Binary files /dev/null and b/standards/src10-native-bridge/.docs/src-10-logo-light-theme.png differ diff --git a/standards/src10-native-bridge/Forc.toml b/standards/src10-native-bridge/Forc.toml new file mode 100644 index 0000000..331e3ff --- /dev/null +++ b/standards/src10-native-bridge/Forc.toml @@ -0,0 +1,5 @@ +[project] +authors = ["Fuel Labs "] +entry = "src10.sw" +license = "Apache-2.0" +name = "src10" diff --git a/standards/src10-native-bridge/README.md b/standards/src10-native-bridge/README.md new file mode 100644 index 0000000..8930a38 --- /dev/null +++ b/standards/src10-native-bridge/README.md @@ -0,0 +1,123 @@ +

+ + + SRC-10 logo + +

+ +# Abstract + +The following standard allows for the implementation of a standard API for Native Bridges using the Sway Language. The standardized design has the bridge contract send a message to the origin chain to register which token it accepts to prevent a loss of funds. + +# Motivation + +A standard interface for bridges intends to provide a safe and efficient bridge between the settlement or canonical chain and the Fuel Network. + +# Prior Art + +The standard is centered on Fuel’s [Bridge Architecture](https://github.com/FuelLabs/fuel-bridge/blob/main/docs/ARCHITECTURE.md). Fuel's bridge system is built on a message protocol that allows to send (and receive) messages between entities located in two different blockchains. + +The following standard takes reference from the [FungibleBridge](https://github.com/FuelLabs/fuel-bridge/blob/3971081850e7961d9b649edda4cad8a848ee248e/packages/fungible-token/bridge-fungible-token/src/interface.sw#L22) ABI defined in the fuel-bridge repository. + +# Specification + +The following functions MUST be implemented to follow the SRC-10; Native Bridge Standard: + +## Required Functions + +### - `fn register_token(token_address: b256, gateway_contract: b256)` + +The `register_token()` function compiles a message to be sent back to the canonical chain to register a token to be bridged. The `gateway_contract` contract on the canonical chain receives the `token_address` token address in the message such that when `token_addess` tokens are deposited on the canonical chain they are reported to prevent loss of funds. + +> **NOTE:*** Trying to deposit tokens to a contract ID that does not exist or does not implement the Fuel Messaging Portal would mean permanent loss of funds. + +- This function MUST send a message on the canonical chain to the `gateway_contract` contract, registering the specified `token_address` token that exists on the canonical chain. + +### - `fn process_message(message_index: u64)` + +The `process_message()` function accepts incoming deposit messages from the canonical chain and issues the corresponding bridged asset. + +- This function MUST parse a message at the given `message_index` index. +- This function SHALL mint a token that follows the [SRC-8; Bridged Asset Standard](https://github.com/FuelLabs/sway-standards/tree/master/standards/src_8). +- This function SHALL issue a refund if there is an error in the bridging process. + +### - `fn withdraw(to_address: b256, sub_id: SubId, gateway_contract: b256)` + +The `withdraw()` function accepts and burns a bridged Native Asset on Fuel and sends a message to the `gateway_contract` contract on the canonical chain to release the originally deposited tokens to the `to_address` address. + +- This function SHALL send a message to the `gateway_contract` contract to release the bridged tokens to the `to_address` address on the canonical chain. +- This function MUST ensure the `sha256(contract_id(), sub_id)` digest matches the asset's `AssetId` sent in the transaction. +- This function SHALL burn all tokens sent in the transaction. + +### - `fn claim_refund(to_address: b256, token_address: b256, token_id: b256, gateway_contract: b256)` + +The `claim_refund()` function is called if something goes wrong in the bridging process and an error occurs. It sends a message to the `gateway_contract` contract on the canonical chain to release the `token_address` token with token id `token_id` to the `to_address` address. + +- This function SHALL send a message to the `gateway_contract` contract to release the `token_address` token with id `token_id` to the `to_address` address on the canonical chain. +- This function MUST ensure a refund was issued. + +## Required Data Types + +### `MessageData` + +The following describes a struct that encapsulates various message metadata to a single type. There MUST be the following fields in the `MessageData` struct: + +#### - amount: `u256` + +The `amount` field MUST represent the number of tokens. + +#### - from: `b256` + +The `from` field MUST represent the bridging user’s address on the canonical chain. + +#### - len: `u16` + +The `len` field MUST represent the number of the deposit messages to discern between deposits that must be forwarded to an EOA vs deposits that must be forwarded to a contract. + +#### - to: `Identity` + +The `to` field MUST represent the bridging target destination `Address` or `ContractId` on the Fuel Chain. + +#### - token_address: `b256` + +The `token_address` field MUST represent the bridged token's address on the canonical chain. + +#### - token_id: `b256` + +The `token_id` field MUST represent the token's ID on the canonical chain. The `ZERO_B256` MUST be used if this is a fungible token and no token ID exists. + +### Example + +```sway +struct MessageData { + amount: b256, + from: b256, + len: u16, + to: Identity, + token_address: b256, + token_id: b256, +} +``` + +## Required Standards + +Any contract that implements the SRC-10; Native Bridge Standard MUST implement the [SRC-8; Bridged Asset Standard](https://github.com/FuelLabs/sway-standards/tree/master/standards/src_8) for all bridged assets. + +# Rationale + +The SRC-10; Native Bridge Standard is designed to standardize the native bridge interface between all Fuel instances. + +# Backwards Compatibility + +This standard is compatible with the SRC-20 and SRC-8 standards. + +# Example ABI + +```sway +abi SRC10 { + fn register_token(token_address: b256, gateway_contract: b256); + fn process_message(message_index: u64); + fn withdraw(to_address: b256, sub_id: SubId, gateway_contract: b256); + fn claim_refund(to_address: b256, token_address: b256, token_id: b256, gateway_contract: b256); +} +``` \ No newline at end of file diff --git a/standards/src10-native-bridge/src/src10.sw b/standards/src10-native-bridge/src/src10.sw new file mode 100644 index 0000000..7eedfa1 --- /dev/null +++ b/standards/src10-native-bridge/src/src10.sw @@ -0,0 +1,114 @@ +library; + +/// Enscapsultes metadata sent between the canonical chain and Fuel. +struct MessageData { + /// The number of tokens. + amount: b256, + /// The user's address on the canonical chain. + from: b256, + /// The number of deposit messages. + len: u16, + /// The bridging target destination on the Fuel chain. + to: Identity, + /// The bridged token's address on the canonical chain. + token_address: b256, + /// The token's ID on the canonical chain. + token_id: b256, +} + +abi SRC10 { + /// Compiles a message to be sent back to the canonical chain. + /// + /// # Additional Information + /// + /// * The `gateway` contract on the canonical chain receives the `token_address` ID in the message such that when assets are deposited they are reported to prevent loss of funds. + /// + /// # Arguments + /// + /// * `token_address`: [b256] - The token's address on the canonical chain. + /// * `gateway_contract`: [b256] - The contract that accepts deposits on the canonical chain. + /// + /// # Examples + /// + /// ```sway + /// use src10::SRC10; + /// + /// fn foo(gateway_contract: b256, token_address: b256, bridge: ContractId) { + /// let bridge_abi = abi(SRC10, bridge.value); + /// bridge_abi.register_token(token_address, gateway_contract); + /// } + /// ``` + #[storage(read, write)] + fn register_token(token_address: b256, gateway_contract: b256); + + /// Accepts incoming deposit messages from the canonical chain and issues the corresponding bridged asset. + /// + /// # Arguments + /// + /// * `message_index`: [u64] - The index of the message to parse. + /// + /// # Examples + /// + /// ```sway + /// use src10::SRC10; + /// + /// fn foo(message_index: u64, bridge: ContractId) { + /// let bridge_abi = abi(SRC10, bridge.value); + /// bridge_abi.process_message(message_index); + /// } + /// ``` + #[storage(read, write)] + fn process_message(message_index: u64); + + /// Accepts and burns a bridged asset and sends a messages to the canonical chain to release the original deposited token. + /// + /// # Arguments + /// + /// * `to_address`: [b256] - The address on the canonical chain to send the released tokens to. + /// * `sub_id`: [SubId] - The SubId of the asset sent in the transaction. + /// * `gateway_contract`: [b256] - The contract that holds the deposited tokens on the canonical chain. + /// + /// # Examples + /// + /// ```sway + /// use src10::SRC10; + /// + /// fn foo(to_address: b256, asset_sub_id: SubId, gateway_contract: b256, bridge: ContractId, bridged_asset: AssetId) { + /// let bridge_abi = abi(SRC10, bridge.value); + /// bridge_abi { + /// gas: 10000, + /// coins: 100, + /// asset_id: bridged_asset, + /// }.withdraw(to_address, asset_sub_id, gateway_contract); + /// } + /// ``` + #[storage(read, write)] + fn withdraw(to_address: b256, sub_id: SubId, gateway_contract: b256); + + /// Returns a refund on the canonical chain if an error occurs while bridging. + /// + /// # Arguments + /// + /// * `to_address`: [b256] - The address on the canonical chain to send the refunded tokens to. + /// * `token_address`: [b256] - The token on the canonical chain to be refunded. + /// * `token_id`: [b256] - The token id of the token on the canonical chain to be refunded. + /// * `gateway_contract`: [b256] - The contract that holds the deposited tokens on the canonical chain. + /// + /// # Examples + /// + /// ```sway + /// use src10::SRC10; + /// + /// fn foo(to_address: b256, token_address: b256, token_id: b256, gateway_contract: b256, bridge: ContractId) { + /// let bridge_abi = abi(SRC10, bridge.value); + /// bridge_abi.claim_refund(to_address, token_address, token_id, gateway_contract); + /// } + /// ``` + #[storage(read, write)] + fn claim_refund( + to_address: b256, + token_address: b256, + token_id: b256, + gateway_contract: b256, + ); +}