Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 103 additions & 30 deletions docs/4-write-ten-dapp/5-bridging.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,123 @@
---
sidebar_position: 5
---
# Cross-Chain Bridging
# Cross-Chain Messaging

The TEN Bridge facilitates the seamless transfer of assets between the Ethereum blockchain and the TEN blockchain. This documentation provides an overview of the bridge, its purpose, supported tokens, and usage instructions.
`CrossChainEnabledTEN` is a convenience base contract that applications inherit to get cross-chain capabilities. During initialisation it must be configured with the address of the `CrossChainMessenger` on the same chain — e.g. `TenBridge` on L1 points to the L1 messenger, `EthereumBridge` on L2 points to the L2 messenger. This is the trusted authority that authenticates incoming cross-chain calls:

## Purpose
```solidity
CrossChainEnabledTEN.configure(messengerAddress);
```

The primary goal of the TEN Bridge is to enable users to move assets between Ethereum and TEN efficiently and securely. It serves as a vital infrastructure component for users who wish to interact with assets on both blockchains.
The `configure` call derives the `MessageBus` address from the messenger and initialises the nonce counter. Once configured, the contract provides the ability to send and receive cross-chain messages.

## Supported Tokens
## Sending Messages

To provide flexibility and utility to users, the bridge supports the following tokens:
- Ethereum (ETH)
- TEN token (TEN)
- Tether (USDT)
- USD Coin (USDC)
```solidity
function queueMessage(
address remoteTarget,
bytes memory payload,
uint32 topic,
uint256 gas,
uint8 consistencyLevel
) internal;
```

## Usage
This encodes a `CrossChainCall` and publishes it through the MessageBus. The bridge uses this to send function calls like `receiveAssets(...)` to its counterpart on the other chain.

### Prerequisites
For raw data (not function calls):

Before using the TEN Bridge, ensure you have the following:
- MetaMask or any Ethereum-compatible wallet
- Navigate to the [TEN Bridge UI](https://testnet-bridge.ten.xyz/)
```solidity
function publishRawMessage(bytes memory data, uint32 topic, uint8 consistencyLevel) internal;
```

### Steps to Transfer Assets
## Receiving Messages

1. Connect your Ethereum wallet (e.g. MetaMask) to the TEN Bridge interface.
2. Select the asset you wish to transfer and specify the destination blockchain (Ethereum or TEN).
3. Enter the amount of the asset you want to transfer.
4. Review the transaction details and confirm the transfer.
5. Once confirmed, wait for the transaction to be processed. The transfer may take some time depending on network congestion.
6. Once the transfer is complete, you will receive a confirmation notification.
The `onlyCrossChainSender` modifier verifies that the current call originated from a cross-chain relay and came from the expected sender:

### Notes
```solidity
modifier onlyCrossChainSender(address expectedSender) {
require(_isCrossChain());
require(_crossChainSender() == expectedSender);
require(_crossChainTarget() == address(this));
_;
}
```

- Please ensure you have sufficient gas fees (for Ethereum transactions) and TEN network fees to cover the transaction costs.
- Transactions may take varying amounts of time to process depending on network congestion and other factors.
- Always double-check the recipient address before confirming the transfer to avoid any potential loss of assets.
This is how the reference bridge ensures that only its counterpart on the other chain can call `receiveAssets`.

## Support
## Reference Bridge

For any enquiries or assistance regarding the TEN Bridge, please contact our team at [team@obscu.ro](mailto:team@obscu.ro).
TEN includes a reference bridge that demonstrates `CrossChainEnabledTEN` in practice. It handles ERC-20 transfers, native ETH, and WETH wrapping/unwrapping across L1 and L2.

## Conclusion
### L1 TenBridge

The TEN Bridge provides a convenient and secure way for users to transfer assets between Ethereum and TEN blockchains. By supporting multiple tokens, including ETH, TEN, USDT and USDC, the bridge enhances interoperability and accessibility for users within the ecosystem.
[`TenBridge.sol`](https://github.com/ten-protocol/go-ten/blob/main/contracts/src/reference_bridge/L1/contracts/TenBridge.sol) sits on Ethereum. It locks assets on L1 and uses `queueMessage` to instruct the L2 bridge to mint or release the equivalent on TEN:

```solidity
contract TenBridge is CrossChainEnabledTEN, IBridge, ... {

function initialize(address messenger, address _owner) public initializer {
CrossChainEnabledTEN.configure(messenger);
// ...
}

function sendERC20(address asset, uint256 amount, address receiver) external payable {
SafeERC20.safeTransferFrom(IERC20(asset), msg.sender, address(this), amount);

bytes memory data = abi.encodeWithSelector(
IBridge.receiveAssets.selector, asset, amount, receiver
);
queueMessage(remoteBridgeAddress, data, uint32(Topics.TRANSFER), 0, 0, 0);
}

function receiveAssets(address asset, uint256 amount, address receiver)
external
onlyCrossChainSender(remoteBridgeAddress)
{
// unlock tokens or native ETH back to the receiver
}
}
```

### L2 EthereumBridge

[`EthereumBridge.sol`](https://github.com/ten-protocol/go-ten/blob/main/contracts/src/reference_bridge/L2/contracts/EthereumBridge.sol) sits on TEN. It mints wrapped tokens when assets arrive from L1 and burns them when a user bridges back:

```solidity
contract EthereumBridge is IBridge, ITokenFactory, CrossChainEnabledTEN, ... {

function initialize(address messenger, address remoteBridge, address localWeth)
public initializer
{
CrossChainEnabledTEN.configure(messenger);
remoteBridgeAddress = remoteBridge;
// ...
}

function sendERC20(address asset, uint256 amount, address receiver) external payable {
WrappedERC20 token = wrappedTokens[asset];
token.burnFor(msg.sender, amount);

bytes memory data = abi.encodeWithSelector(
IBridge.receiveAssets.selector, localToRemoteToken[asset], amount, receiver
);
queueMessage(remoteBridgeAddress, data, uint32(Topics.TRANSFER), 0, 0, msg.value);
}

function receiveAssets(address asset, uint256 amount, address receiver)
external
onlyCrossChainSender(remoteBridgeAddress)
{
wrappedTokens[remoteToLocalToken[asset]].issueFor(receiver, amount);
}
}
```

Both contracts follow the same pattern:
* **Configure** the messenger
* **Send** via `queueMessage`
* **Receive** behind `onlyCrossChainSender`

Use them as a starting point for your own cross-chain application.