Skip to content
Open
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
@@ -1,28 +1,56 @@
---
title: Bridge USDC via Stargate
description: Bridge USDC to and from Tempo using Stargate and LayerZero. Includes source chain addresses, cast commands, and TypeScript examples with viem.
title: Bridge via LayerZero
description: Bridge tokens to and from Tempo using LayerZero. Covers Stargate pools and standard OFT adapters with cast commands and TypeScript examples.
---

import { Tabs, Tab } from 'vocs'
# Bridge via LayerZero

# Bridge USDC via Stargate
[LayerZero](https://layerzero.network) is the omnichain messaging protocol that powers token bridging on Tempo. Tokens are bridged using the [OFT (Omnichain Fungible Token)](https://docs.layerzero.network/v2/developers/evm/oft/quickstart) standard - the source chain locks or burns tokens and the destination chain mints the bridged equivalent.

[Stargate](https://stargate.finance/) uses [LayerZero](https://layerzero.network) to enable 1:1 USDC bridging between Tempo and other chains. Stargate locks USDC on the source chain and mints **USDC.e** (Bridged USDC) on Tempo, typically in under a minute.
There are two flavors of OFT on Tempo:

## Contracts on Tempo
- **Stargate** - an application built on LayerZero that manages liquidity pools. Tokens like USDC.e and EURC.e use Stargate's `sendToken()` interface.
- **Standard OFT** - token issuers (e.g. Tether for USDT0) deploy their own OFT adapters using LayerZero's `send()` interface directly.

Both use the same underlying LayerZero endpoint on Tempo.

## Bridged tokens on Tempo

| Token | Address | Bridge |
|-------|---------|--------|
| **USDC.e** (Bridged USDC) | [`0x20C000000000000000000000b9537d11c60E8b50`](https://explore.tempo.xyz/address/0x20C000000000000000000000b9537d11c60E8b50) | Stargate |
| **EURC.e** (Bridged EURC) | [`0x20c0000000000000000000001621e21F71CF12fb`](https://explore.tempo.xyz/address/0x20c0000000000000000000001621e21F71CF12fb) | Stargate |
| **USDT0** | [`0x20c00000000000000000000014f22ca97301eb73`](https://explore.tempo.xyz/address/0x20c00000000000000000000014f22ca97301eb73) | OFT |
| **frxUSD** | [`0x20c0000000000000000000003554d28269e0f3c2`](https://explore.tempo.xyz/address/0x20c0000000000000000000003554d28269e0f3c2) | OFT |
| **cUSD** | [`0x20c0000000000000000000000520792dcccccccc`](https://explore.tempo.xyz/address/0x20c0000000000000000000000520792dcccccccc) | OFT |
| **stcUSD** | [`0x20c0000000000000000000008ee4fcff88888888`](https://explore.tempo.xyz/address/0x20c0000000000000000000008ee4fcff88888888) | OFT |
| **GUSD** | [`0x20c0000000000000000000005c0bac7cef389a11`](https://explore.tempo.xyz/address/0x20c0000000000000000000005c0bac7cef389a11) | OFT |
| **rUSD** | [`0x20c0000000000000000000007f7ba549dd0251b9`](https://explore.tempo.xyz/address/0x20c0000000000000000000007f7ba549dd0251b9) | OFT |
| **wsrUSD** | [`0x20c000000000000000000000aeed2ec36a54d0e5`](https://explore.tempo.xyz/address/0x20c000000000000000000000aeed2ec36a54d0e5) | OFT |

See the full token list at [tokenlist.tempo.xyz](https://tokenlist.tempo.xyz/list/4217).

## LayerZero contracts on Tempo

| Contract | Address |
|----------|---------|
| **USDC.e** (Bridged USDC) | [`0x20C000000000000000000000b9537d11c60E8b50`](https://explore.tempo.xyz/address/0x20C000000000000000000000b9537d11c60E8b50) |
| **StargateOFTUSDC** | [`0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392`](https://explore.tempo.xyz/address/0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392) |
| **EndpointV2** | [`0x20Bb7C2E2f4e5ca2B4c57060d1aE2615245dCc9C`](https://explore.tempo.xyz/address/0x20Bb7C2E2f4e5ca2B4c57060d1aE2615245dCc9C) |
| **LZEndpointDollar** | [`0x0cEb237E109eE22374a567c6b09F373C73FA4cBb`](https://explore.tempo.xyz/address/0x0cEb237E109eE22374a567c6b09F373C73FA4cBb) |

Tempo's LayerZero Endpoint ID is **`30410`**.

## Source chain contracts
## Stargate tokens

[Stargate](https://stargate.finance/) manages liquidity pools for USDC.e and EURC.e. Use the Stargate `sendToken()` interface for these tokens.

### Stargate contracts on Tempo

| Token | Stargate OFT Contract |
|-------|----------------------|
| **USDC.e** | [`0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392`](https://explore.tempo.xyz/address/0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392) |
| **EURC.e** | [`0x7753Dc8d4bd48Db599Da21E08b1Ab1D6FDFfdC71`](https://explore.tempo.xyz/address/0x7753Dc8d4bd48Db599Da21E08b1Ab1D6FDFfdC71) |

Use the Stargate pool contract on the source chain to bridge USDC to Tempo, and the StargateOFTUSDC contract on Tempo to bridge back.
### Source chain Stargate pools

| Chain | LZ Endpoint ID | Stargate USDC Pool |
|-------|---------------:|--------------------|
Expand All @@ -33,23 +61,20 @@ Use the Stargate pool contract on the source chain to bridge USDC to Tempo, and
| Polygon | `30109` | [`0x9Aa02D4Fae7F58b8E8f34c66E756cC734DAc7fe4`](https://polygonscan.com/address/0x9Aa02D4Fae7F58b8E8f34c66E756cC734DAc7fe4) |
| Avalanche | `30106` | [`0x5634c4a5FEd09819E3c46D86A965Dd9447d86e47`](https://snowtrace.io/address/0x5634c4a5FEd09819E3c46D86A965Dd9447d86e47) |

## Bridge to Tempo
### Bridge to Tempo via Stargate

### Using the Stargate app
#### Using the Stargate app

1. Go to [stargate.finance](https://stargate.finance/)
2. Select your source chain (e.g. Ethereum) and USDC
2. Select your source chain and token (USDC or EURC)
3. Set **Tempo** as the destination chain
4. Enter the amount, approve, and send

### Using cast (Foundry)
#### Using cast (Foundry)

Bridge USDC from Base to Tempo using `cast`. This calls `sendToken` on the Stargate pool on the source chain. This example uses [taxi mode](#bus-vs-taxi-mode) for immediate delivery.
This example bridges USDC from Base to Tempo. Replace addresses for other tokens or source chains.

#### Step 1 — Get a quote fee estimate from Stargate router

Use the same parameters you will pass to `sendToken`. Returns `(nativeFee, lzTokenFee)`.
Take the first returned number as `<NATIVE_FEE>`.
##### Step 1 - Get a quote

```bash
cast call 0x27a16dc786820B16E5c9028b75B99F6f604b5d26 \
Expand All @@ -59,9 +84,9 @@ cast call 0x27a16dc786820B16E5c9028b75B99F6f604b5d26 \
--rpc-url https://mainnet.base.org
```

#### Step 2 — Approve USDC on Base
Take the first returned number as `<NATIVE_FEE>`.

Approve the Stargate router to spend USDC.
##### Step 2 - Approve token on source chain

```bash
cast send 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 \
Expand All @@ -72,34 +97,26 @@ cast send 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 \
--private-key $PRIVATE_KEY
```

#### Step 3 Send bridge transaction
##### Step 3 - Send bridge transaction

```bash
cast send 0x27a16dc786820B16E5c9028b75B99F6f604b5d26 \
'sendToken((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),(uint256,uint256),address)' \
"(30410,$(cast abi-encode 'f(address)' <TEMPO_WALLET_ADDRESS>),<AMOUNT>,<MIN_AMOUNT>,0x,0x,0x)" \
"(<NATIVE_FEE>,0)" \
<SOURCE_BASE_ADDRESS> \
<SOURCE_ADDRESS> \
--value <NATIVE_FEE> \
--rpc-url https://mainnet.base.org \
--private-key $PRIVATE_KEY
```

#### Step 4 — Verify transaction status

Track the bridge with the transaction hash returned by `sendToken`:
##### Step 4 - Verify transaction status

```text
https://scan.layerzero-api.com/v1/messages/tx/<SOURCE_TX_HASH>
```

Once delivered, view the destination transaction on Tempo:

```text
https://explore.tempo.xyz/tx/<DESTINATION_TX_HASH>
```

### Using TypeScript (viem)
#### Using TypeScript (viem)

```typescript
import { createWalletClient, createPublicClient, http, parseUnits, pad } from 'viem'
Expand Down Expand Up @@ -219,7 +236,7 @@ const msgFee = await publicClient.readContract({
args: [sendParam, false],
})

// 2. Approve USDC
// 2. Approve token
await walletClient.writeContract({
address: usdc,
abi: erc20Abi,
Expand All @@ -237,18 +254,15 @@ await walletClient.writeContract({
})
```

## Bridge from Tempo

To bridge USDC.e from Tempo back to another chain, call `sendToken` on the **StargateOFTUSDC** contract on Tempo. The process is the same — quote, approve, send — but the source contract and destination EID are swapped.
### Bridge from Tempo via Stargate

### Using cast (Foundry)
To bridge from Tempo back to another chain, call `sendToken` on the Stargate OFT contract on Tempo. The process is the same - quote, approve, send - but the source contract and destination EID are swapped.

Bridge USDC.e from Tempo to Base using `cast`. This calls `sendToken` on the **StargateOFTUSDC** contract on Tempo. This example uses [taxi mode](#bus-vs-taxi-mode) for immediate delivery.
#### Using cast (Foundry)

#### Step 1 — Quote the fee
This example bridges USDC.e from Tempo to Base.

Use the same parameters you will pass to `sendToken`. Returns `(nativeFee, lzTokenFee)`.
Take the first returned number as `<NATIVE_FEE>` (in stablecoin units, not ETH).
##### Step 1 - Quote the fee

```bash
cast call 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 \
Expand All @@ -258,9 +272,9 @@ cast call 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 \
--rpc-url https://rpc.tempo.xyz
```

#### Step 2 — Approve USDC.e on Tempo
Take the first returned number as `<NATIVE_FEE>` (in stablecoin units, not ETH).

Approve the StargateOFTUSDC contract to spend USDC.e.
##### Step 2 - Approve token on Tempo

```bash
cast send 0x20C000000000000000000000b9537d11c60E8b50 \
Expand All @@ -271,9 +285,9 @@ cast send 0x20C000000000000000000000b9537d11c60E8b50 \
--private-key $PRIVATE_KEY
```

#### Step 3 Send bridge transaction
##### Step 3 - Send bridge transaction

No `--value` is needed on Tempo the messaging fee is paid in a TIP-20 stablecoin via [EndpointDollar](#endpointdollar).
No `--value` is needed on Tempo - the messaging fee is paid in a TIP-20 stablecoin via [EndpointDollar](#endpointdollar).

```bash
cast send 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 \
Expand All @@ -285,17 +299,13 @@ cast send 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 \
--private-key $PRIVATE_KEY
```

#### Step 4 — Verify transaction status

Track the bridge with the transaction hash returned by `sendToken`:
##### Step 4 - Verify transaction status

```text
https://scan.layerzero-api.com/v1/messages/tx/<SOURCE_TX_HASH>
```

Once delivered, view the destination transaction on the destination chain explorer.

### Using TypeScript (viem)
#### Using TypeScript (viem)

```typescript
import { createWalletClient, createPublicClient, http, parseUnits, pad } from 'viem'
Expand All @@ -310,7 +320,7 @@ const walletClient = createWalletClient({
transport: http(),
})

// StargateOFTUSDC on Tempo
// Stargate OFT for USDC.e on Tempo
const stargateOFT = '0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392' as const
// USDC.e on Tempo
const usdce = '0x20C000000000000000000000b9537d11c60E8b50' as const
Expand Down Expand Up @@ -338,15 +348,15 @@ const msgFee = await publicClient.readContract({
args: [sendParam, false],
})

// 2. Approve USDC.e
// 2. Approve token
await walletClient.writeContract({
address: usdce,
abi: erc20Abi,
functionName: 'approve',
args: [stargateOFT, amount],
})

// 3. Send the bridge transaction (no value fee handled via EndpointDollar)
// 3. Send the bridge transaction (no value - fee handled via EndpointDollar)
await walletClient.writeContract({
address: stargateOFT,
abi: stargateAbi,
Expand All @@ -355,36 +365,24 @@ await walletClient.writeContract({
})
```

## EndpointDollar

Tempo has no native gas token, so there is no `msg.value`. Standard LayerZero endpoints require `msg.value` to pay messaging fees, which doesn't work on Tempo.

**LZEndpointDollar** ([`0x0cEb237E109eE22374a567c6b09F373C73FA4cBb`](https://explore.tempo.xyz/address/0x0cEb237E109eE22374a567c6b09F373C73FA4cBb)) is an adapter contract that routes LayerZero messaging fees through a TIP-20 stablecoin instead of `msg.value`. It wraps the standard `EndpointV2` so that Stargate's OFT contracts can function on Tempo without modification.

This is transparent for end users:

- **Bridging to Tempo** — fees are paid in native gas on the source chain (ETH, MATIC, AVAX, etc.) as normal.
- **Bridging from Tempo** — `LZEndpointDollar` automatically deducts the messaging fee from a TIP-20 stablecoin. No `msg.value` is needed.
- **Developers** don't need to interact with `LZEndpointDollar` directly. The StargateOFTUSDC contract handles it internally.

## Bus vs. Taxi mode
### Bus vs. Taxi mode

Stargate offers two delivery modes:

| Mode | `oftCmd` | Delivery | Cost |
|------|----------|----------|------|
| **Taxi** | `0x` (empty) | Immediate message sent right away | Higher gas cost |
| **Bus** | `0x00` (1 byte) | Batched waits for other passengers | Lower gas cost |
| **Taxi** | `0x` (empty) | Immediate - message sent right away | Higher gas cost |
| **Bus** | `0x00` (1 byte) | Batched - waits for other passengers | Lower gas cost |

All examples above use taxi mode for immediate delivery. To use bus mode, set `oftCmd` to `0x00`:
All examples above use taxi mode. To use bus mode, set `oftCmd` to `0x00`:

```bash
# cast bus mode
# cast - bus mode
oftCmd=0x00
```

```typescript
// viem bus mode
// viem - bus mode
const sendParam = {
// ...
oftCmd: '0x00' as const, // bus mode
Expand All @@ -395,9 +393,60 @@ const sendParam = {
Bus mode is not available on all routes. If a bus route is not configured for your source/destination pair, the transaction will revert. Use taxi mode (`0x`) for guaranteed delivery.
:::

## Standard OFT tokens

Tokens like USDT0, frxUSD, cUSD, and others are bridged using the standard LayerZero OFT `send()` interface. Each token issuer deploys their own OFT adapter contract. The `send()` interface uses the same `SendParam` struct as Stargate but calls `send()` instead of `sendToken()`.

To bridge a standard OFT token, you need the OFT adapter contract address on the source chain. Refer to the token issuer's documentation for their deployment addresses:

- **USDT0** - [Tether](https://tether.io)
- **frxUSD** - [Frax](https://docs.frax.com)
- **cUSD** - [Coinbase](https://docs.cdp.coinbase.com)

The flow is the same as Stargate - quote, approve, send - but you call `send()` on the OFT adapter instead of `sendToken()` on a Stargate pool:

```bash
# Quote
cast call <OFT_ADAPTER> \
'quoteSend((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),bool)((uint256,uint256))' \
"(30410,$(cast abi-encode 'f(address)' <TEMPO_ADDRESS>),<AMOUNT>,<MIN_AMOUNT>,0x,0x,0x)" \
false \
--rpc-url <SOURCE_RPC>

# Approve
cast send <TOKEN_ADDRESS> \
'approve(address,uint256)' \
<OFT_ADAPTER> \
<AMOUNT> \
--rpc-url <SOURCE_RPC> \
--private-key $PRIVATE_KEY

# Send
cast send <OFT_ADAPTER> \
'send((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),(uint256,uint256),address)' \
"(30410,$(cast abi-encode 'f(address)' <TEMPO_ADDRESS>),<AMOUNT>,<MIN_AMOUNT>,0x,0x,0x)" \
"(<NATIVE_FEE>,0)" \
<REFUND_ADDRESS> \
--value <NATIVE_FEE> \
--rpc-url <SOURCE_RPC> \
--private-key $PRIVATE_KEY
```

## EndpointDollar

Tempo has no native gas token, so there is no `msg.value`. Standard LayerZero endpoints require `msg.value` to pay messaging fees, which doesn't work on Tempo.

**LZEndpointDollar** ([`0x0cEb237E109eE22374a567c6b09F373C73FA4cBb`](https://explore.tempo.xyz/address/0x0cEb237E109eE22374a567c6b09F373C73FA4cBb)) is an adapter contract that routes LayerZero messaging fees through a TIP-20 stablecoin instead of `msg.value`. It wraps the standard `EndpointV2` so that OFT contracts can function on Tempo without modification.

This is transparent for end users:

- **Bridging to Tempo** - fees are paid in native gas on the source chain (ETH, MATIC, AVAX, etc.) as normal.
- **Bridging from Tempo** - `LZEndpointDollar` automatically deducts the messaging fee from a TIP-20 stablecoin. No `msg.value` is needed.
- **Developers** don't need to interact with `LZEndpointDollar` directly. The OFT contracts on Tempo handle it internally.

## Further reading

- [Stargate documentation](https://stargateprotocol.gitbook.io/stargate/v2-developer-docs)
- [LayerZero V2 documentation](https://docs.layerzero.network/v2)
- [Stargate documentation](https://stargateprotocol.gitbook.io/stargate/v2-developer-docs)
- [Bridges & Exchanges on Tempo](/ecosystem/bridges)
- [Getting Funds on Tempo](/guide/getting-funds)
Loading
Loading