From 9ca314a23c059f5128bbfedf27a05305ab3ff91d Mon Sep 17 00:00:00 2001 From: Derek Cofausper <256792747+decofe@users.noreply.github.com> Date: Mon, 23 Mar 2026 15:14:47 +0000 Subject: [PATCH 1/4] docs: add Bridge USDC via Stargate guide Adds a comprehensive guide covering: - USDC.e and Stargate contract addresses on Tempo - Source chain contracts (Ethereum, Arbitrum, Base, Optimism, Polygon, Avalanche) - Bridging TO and FROM Tempo with cast (Foundry) and TypeScript (viem) examples - EndpointDollar explanation (no msg.value on Tempo) - Bus vs Taxi mode Also updates getting-funds.mdx to link to the new guide and adds sidebar entry. Co-Authored-By: Uddhav <255779543+letstokenize@users.noreply.github.com> --- src/pages/guide/bridge-usdc-stargate.mdx | 399 +++++++++++++++++++++++ src/pages/guide/getting-funds.mdx | 2 +- vocs.config.ts | 4 + 3 files changed, 404 insertions(+), 1 deletion(-) create mode 100644 src/pages/guide/bridge-usdc-stargate.mdx diff --git a/src/pages/guide/bridge-usdc-stargate.mdx b/src/pages/guide/bridge-usdc-stargate.mdx new file mode 100644 index 0000000..075209b --- /dev/null +++ b/src/pages/guide/bridge-usdc-stargate.mdx @@ -0,0 +1,399 @@ +--- +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. +--- + +import { Tabs, Tab } from 'vocs' + +# Bridge USDC via Stargate + +[Stargate](https://stargate.finance/) uses [LayerZero](https://layerzero.network) to enable 1:1 USDC bridging between Tempo and other chains. When you bridge USDC to Tempo, Stargate locks your USDC on the source chain and mints **USDC.e** (Bridged USDC) on Tempo. + +## 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 + +Use the Stargate pool contract on the source chain to bridge USDC to Tempo, and the StargateOFTUSDC contract on Tempo to bridge back. + +| Chain | LZ Endpoint ID | Stargate USDC Pool | +|-------|---------------:|--------------------| +| Ethereum | `30101` | [`0xc026395860Db2d07ee33e05fE50ed7bD583189C7`](https://etherscan.io/address/0xc026395860Db2d07ee33e05fE50ed7bD583189C7) | +| Arbitrum | `30110` | [`0xe8CDF27AcD73a434D661C84887215F7598e7d0d3`](https://arbiscan.io/address/0xe8CDF27AcD73a434D661C84887215F7598e7d0d3) | +| Base | `30184` | [`0x27a16dc786820B16E5c9028b75B99F6f604b5d26`](https://basescan.org/address/0x27a16dc786820B16E5c9028b75B99F6f604b5d26) | +| Optimism | `30111` | [`0xcE8CcA271Ebc0533920C83d39F417ED6A0abB7D0`](https://optimistic.etherscan.io/address/0xcE8CcA271Ebc0533920C83d39F417ED6A0abB7D0) | +| Polygon | `30109` | [`0x9Aa02D4Fae7F58b8E8f34c66E756cC734DAc7fe4`](https://polygonscan.com/address/0x9Aa02D4Fae7F58b8E8f34c66E756cC734DAc7fe4) | +| Avalanche | `30106` | [`0x5634c4a5FEd09819E3c46D86A965Dd9447d86e47`](https://snowtrace.io/address/0x5634c4a5FEd09819E3c46D86A965Dd9447d86e47) | + +## Bridge to Tempo + +### Using the Stargate app + +1. Go to [stargate.finance](https://stargate.finance/) +2. Select your source chain (e.g. Ethereum) and USDC +3. Set **Tempo** as the destination chain +4. Enter the amount, approve, and send + +USDC.e arrives in your wallet on Tempo once the LayerZero message is delivered (typically under 2 minutes). + +### Using cast (Foundry) + +Bridge USDC from Ethereum to Tempo using `cast send`. This calls `sendToken` on the Stargate pool on the source chain. + +**Step 1 — Quote the fee:** + +```bash +# Encode the SendParam tuple +SEND_PARAM=$(cast abi-encode \ + "(uint32,bytes32,uint256,uint256,bytes,bytes,bytes)" \ + 30410 \ + $(cast --to-bytes32 $YOUR_ADDRESS) \ + 1000000 \ + 990000 \ + 0x \ + 0x \ + 0x) + +# Quote the messaging fee (returns nativeFee, lzTokenFee) +cast call 0xc026395860Db2d07ee33e05fE50ed7bD583189C7 \ + "quoteSend((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),bool)(uint256,uint256)" \ + "$SEND_PARAM" \ + false \ + --rpc-url https://eth.llamarpc.com +``` + +**Step 2 — Approve USDC spend:** + +```bash +# Approve the Stargate pool to spend your USDC (Ethereum USDC) +cast send 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \ + "approve(address,uint256)" \ + 0xc026395860Db2d07ee33e05fE50ed7bD583189C7 \ + 1000000 \ + --rpc-url https://eth.llamarpc.com \ + --private-key $PRIVATE_KEY +``` + +**Step 3 — Send the bridge transaction:** + +```bash +# Bridge 1 USDC to Tempo (taxi mode — immediate delivery) +# Replace $NATIVE_FEE with the nativeFee from the quote +cast send 0xc026395860Db2d07ee33e05fE50ed7bD583189C7 \ + "sendToken((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),(uint256,uint256),address)" \ + "(30410,$(cast --to-bytes32 $YOUR_ADDRESS),1000000,990000,0x,0x,0x)" \ + "($NATIVE_FEE,0)" \ + $YOUR_ADDRESS \ + --value $NATIVE_FEE \ + --rpc-url https://eth.llamarpc.com \ + --private-key $PRIVATE_KEY +``` + +:::info +`amountLD` and `minAmountLD` are in USDC's local decimals (6). `1000000` = 1 USDC. Set `minAmountLD` slightly below `amountLD` to account for Stargate fees (e.g. 1% slippage → `990000`). +::: + +### Using TypeScript (viem) + +```typescript +import { createWalletClient, http, parseUnits, pad } from 'viem' +import { mainnet } from 'viem/chains' +import { privateKeyToAccount } from 'viem/accounts' + +const account = privateKeyToAccount('0x...') + +const walletClient = createWalletClient({ + account, + chain: mainnet, + transport: http(), +}) + +// Stargate pool on Ethereum +const stargatePool = '0xc026395860Db2d07ee33e05fE50ed7bD583189C7' as const +// USDC on Ethereum +const usdc = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' as const + +const amount = parseUnits('1', 6) // 1 USDC +const minAmount = parseUnits('0.99', 6) // 1% slippage tolerance + +const sendParam = { + dstEid: 30410, // Tempo + to: pad(account.address), + amountLD: amount, + minAmountLD: minAmount, + extraOptions: '0x' as const, + composeMsg: '0x' as const, + oftCmd: '0x' as const, // taxi mode (immediate) +} + +const stargateAbi = [ + { + name: 'quoteSend', + type: 'function', + stateMutability: 'view', + inputs: [ + { + name: '_sendParam', + type: 'tuple', + components: [ + { name: 'dstEid', type: 'uint32' }, + { name: 'to', type: 'bytes32' }, + { name: 'amountLD', type: 'uint256' }, + { name: 'minAmountLD', type: 'uint256' }, + { name: 'extraOptions', type: 'bytes' }, + { name: 'composeMsg', type: 'bytes' }, + { name: 'oftCmd', type: 'bytes' }, + ], + }, + { name: '_payInLzToken', type: 'bool' }, + ], + outputs: [ + { + name: 'msgFee', + type: 'tuple', + components: [ + { name: 'nativeFee', type: 'uint256' }, + { name: 'lzTokenFee', type: 'uint256' }, + ], + }, + ], + }, + { + name: 'sendToken', + type: 'function', + stateMutability: 'payable', + inputs: [ + { + name: '_sendParam', + type: 'tuple', + components: [ + { name: 'dstEid', type: 'uint32' }, + { name: 'to', type: 'bytes32' }, + { name: 'amountLD', type: 'uint256' }, + { name: 'minAmountLD', type: 'uint256' }, + { name: 'extraOptions', type: 'bytes' }, + { name: 'composeMsg', type: 'bytes' }, + { name: 'oftCmd', type: 'bytes' }, + ], + }, + { + name: '_fee', + type: 'tuple', + components: [ + { name: 'nativeFee', type: 'uint256' }, + { name: 'lzTokenFee', type: 'uint256' }, + ], + }, + { name: '_refundAddress', type: 'address' }, + ], + outputs: [], + }, +] as const + +const erc20Abi = [ + { + name: 'approve', + type: 'function', + stateMutability: 'nonpayable', + inputs: [ + { name: 'spender', type: 'address' }, + { name: 'amount', type: 'uint256' }, + ], + outputs: [{ type: 'bool' }], + }, +] as const + +// 1. Quote the fee +const { createPublicClient } = await import('viem') +const publicClient = createPublicClient({ chain: mainnet, transport: http() }) + +const msgFee = await publicClient.readContract({ + address: stargatePool, + abi: stargateAbi, + functionName: 'quoteSend', + args: [sendParam, false], +}) + +// 2. Approve USDC +await walletClient.writeContract({ + address: usdc, + abi: erc20Abi, + functionName: 'approve', + args: [stargatePool, amount], +}) + +// 3. Send the bridge transaction +await walletClient.writeContract({ + address: stargatePool, + abi: stargateAbi, + functionName: 'sendToken', + args: [sendParam, msgFee, account.address], + value: msgFee.nativeFee, +}) +``` + +## 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. + +### Using cast (Foundry) + +```bash +# Bridge 1 USDC.e from Tempo to Ethereum +# StargateOFTUSDC on Tempo: 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 +# USDC.e on Tempo: 0x20C000000000000000000000b9537d11c60E8b50 + +# Step 1 — Quote the fee +SEND_PARAM=$(cast abi-encode \ + "(uint32,bytes32,uint256,uint256,bytes,bytes,bytes)" \ + 30101 \ + $(cast --to-bytes32 $YOUR_ADDRESS) \ + 1000000 \ + 990000 \ + 0x \ + 0x \ + 0x) + +cast call 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 \ + "quoteSend((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),bool)(uint256,uint256)" \ + "$SEND_PARAM" \ + false \ + --rpc-url https://rpc.tempo.xyz + +# Step 2 — Approve USDC.e spend +cast send 0x20C000000000000000000000b9537d11c60E8b50 \ + "approve(address,uint256)" \ + 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 \ + 1000000 \ + --rpc-url https://rpc.tempo.xyz \ + --private-key $PRIVATE_KEY + +# Step 3 — Send (use destination EID for the target chain) +# Ethereum = 30101, Arbitrum = 30110, Base = 30184, etc. +cast send 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 \ + "sendToken((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),(uint256,uint256),address)" \ + "(30101,$(cast --to-bytes32 $YOUR_ADDRESS),1000000,990000,0x,0x,0x)" \ + "($NATIVE_FEE,0)" \ + $YOUR_ADDRESS \ + --rpc-url https://rpc.tempo.xyz \ + --private-key $PRIVATE_KEY +``` + +:::info +When bridging **from** Tempo, the messaging fee is paid via `LZEndpointDollar` in a TIP-20 stablecoin instead of a native gas token. See [EndpointDollar](#endpointdollar) below. +::: + +### Using TypeScript (viem) + +```typescript +import { createWalletClient, createPublicClient, http, parseUnits, pad } from 'viem' +import { tempo } from 'viem/chains' +import { privateKeyToAccount } from 'viem/accounts' + +const account = privateKeyToAccount('0x...') + +const publicClient = createPublicClient({ chain: tempo, transport: http() }) +const walletClient = createWalletClient({ + account, + chain: tempo, + transport: http(), +}) + +const stargateOFT = '0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392' as const +const usdce = '0x20C000000000000000000000b9537d11c60E8b50' as const + +const amount = parseUnits('1', 6) +const minAmount = parseUnits('0.99', 6) + +// Use the destination chain's EID +const dstEid = 30101 // Ethereum (change for other chains) + +const sendParam = { + dstEid, + to: pad(account.address), + amountLD: amount, + minAmountLD: minAmount, + extraOptions: '0x' as const, + composeMsg: '0x' as const, + oftCmd: '0x' as const, +} + +// 1. Quote the fee +const msgFee = await publicClient.readContract({ + address: stargateOFT, + abi: stargateAbi, // same ABI as above + functionName: 'quoteSend', + args: [sendParam, false], +}) + +// 2. Approve USDC.e +await walletClient.writeContract({ + address: usdce, + abi: erc20Abi, + functionName: 'approve', + args: [stargateOFT, amount], +}) + +// 3. Send +await walletClient.writeContract({ + address: stargateOFT, + abi: stargateAbi, + functionName: 'sendToken', + args: [sendParam, msgFee, account.address], +}) +``` + +:::info +Note that no `value` is passed when sending from Tempo — Tempo has no native gas token. The LayerZero messaging fee is handled automatically via `LZEndpointDollar`. +::: + +## 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 + +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 | + +All examples above use taxi mode for immediate delivery. To use bus mode, set `oftCmd` to `0x00`: + +```bash +# cast — bus mode +oftCmd=0x00 +``` + +```typescript +// viem — bus mode +const sendParam = { + // ... + oftCmd: '0x00' as const, // bus mode +} +``` + +## Further reading + +- [Stargate documentation](https://stargateprotocol.gitbook.io/stargate/v2-developer-docs) +- [LayerZero V2 documentation](https://docs.layerzero.network/v2) +- [Bridges & Exchanges on Tempo](/ecosystem/bridges) +- [Getting Funds on Tempo](/guide/getting-funds) diff --git a/src/pages/guide/getting-funds.mdx b/src/pages/guide/getting-funds.mdx index 28c3800..d847ad0 100644 --- a/src/pages/guide/getting-funds.mdx +++ b/src/pages/guide/getting-funds.mdx @@ -51,7 +51,7 @@ codex exec "Read https://tempo.xyz/SKILL.md and fund my Tempo Wallet" Use one of these supported bridges to move assets to Tempo: -- **[LayerZero](https://stargate.finance/)**: Bridge supported assets from other chains to Tempo. +- **[LayerZero (Stargate)](/guide/bridge-usdc-stargate)**: Bridge USDC to and from Tempo via Stargate. See the [full bridging guide](/guide/bridge-usdc-stargate) for contract addresses, code examples, and EndpointDollar details. - **[Squid](https://app.squidrouter.com/)**: Swap and bridge assets to Tempo in one flow. - **[Relay](https://relay.link/)**: Bridge assets to Tempo with low fees. - **[Across](https://app.across.to/)**: Bridge assets to Tempo quickly with competitive fees. diff --git a/vocs.config.ts b/vocs.config.ts index 94840ad..112dd8a 100644 --- a/vocs.config.ts +++ b/vocs.config.ts @@ -84,6 +84,10 @@ export default defineConfig({ text: 'Getting Funds on Tempo', link: '/guide/getting-funds', }, + { + text: 'Bridge USDC via Stargate', + link: '/guide/bridge-usdc-stargate', + }, { text: 'Create & Use Accounts', collapsed: true, From e1223436d1cb5e1c160dde5ce93137f351fc7b4a Mon Sep 17 00:00:00 2001 From: Uddhav <255779543+letstokenize@users.noreply.github.com> Date: Mon, 23 Mar 2026 12:54:29 -0700 Subject: [PATCH 2/4] fix: correct cast command syntax in Stargate bridge guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix quoteSend return type: (uint256,uint256) → ((uint256,uint256)) - Replace broken pre-encoded $SEND_PARAM with inline tuple args - Rename $NATIVE_FEE → $LZ_FEE for bridge-from-Tempo (stablecoin units) - Add bus mode caveat warning (not available on all routes) Co-Authored-By: Uddhav <255779543+letstokenize@users.noreply.github.com> --- src/pages/guide/bridge-usdc-stargate.mdx | 42 ++++++++---------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/src/pages/guide/bridge-usdc-stargate.mdx b/src/pages/guide/bridge-usdc-stargate.mdx index 075209b..5b63b09 100644 --- a/src/pages/guide/bridge-usdc-stargate.mdx +++ b/src/pages/guide/bridge-usdc-stargate.mdx @@ -51,21 +51,10 @@ Bridge USDC from Ethereum to Tempo using `cast send`. This calls `sendToken` on **Step 1 — Quote the fee:** ```bash -# Encode the SendParam tuple -SEND_PARAM=$(cast abi-encode \ - "(uint32,bytes32,uint256,uint256,bytes,bytes,bytes)" \ - 30410 \ - $(cast --to-bytes32 $YOUR_ADDRESS) \ - 1000000 \ - 990000 \ - 0x \ - 0x \ - 0x) - # Quote the messaging fee (returns nativeFee, lzTokenFee) cast call 0xc026395860Db2d07ee33e05fE50ed7bD583189C7 \ - "quoteSend((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),bool)(uint256,uint256)" \ - "$SEND_PARAM" \ + "quoteSend((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),bool)((uint256,uint256))" \ + "(30410,$(cast --to-bytes32 $YOUR_ADDRESS),1000000,990000,0x,0x,0x)" \ false \ --rpc-url https://eth.llamarpc.com ``` @@ -251,20 +240,10 @@ To bridge USDC.e from Tempo back to another chain, call `sendToken` on the **Sta # StargateOFTUSDC on Tempo: 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 # USDC.e on Tempo: 0x20C000000000000000000000b9537d11c60E8b50 -# Step 1 — Quote the fee -SEND_PARAM=$(cast abi-encode \ - "(uint32,bytes32,uint256,uint256,bytes,bytes,bytes)" \ - 30101 \ - $(cast --to-bytes32 $YOUR_ADDRESS) \ - 1000000 \ - 990000 \ - 0x \ - 0x \ - 0x) - +# Step 1 — Quote the fee (returns fee in stablecoin units, not ETH) cast call 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 \ - "quoteSend((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),bool)(uint256,uint256)" \ - "$SEND_PARAM" \ + "quoteSend((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),bool)((uint256,uint256))" \ + "(30101,$(cast --to-bytes32 $YOUR_ADDRESS),1000000,990000,0x,0x,0x)" \ false \ --rpc-url https://rpc.tempo.xyz @@ -276,12 +255,13 @@ cast send 0x20C000000000000000000000b9537d11c60E8b50 \ --rpc-url https://rpc.tempo.xyz \ --private-key $PRIVATE_KEY -# Step 3 — Send (use destination EID for the target chain) -# Ethereum = 30101, Arbitrum = 30110, Base = 30184, etc. +# Step 3 — Send (no --value needed on Tempo) +# $LZ_FEE = nativeFee from the quote step (in stablecoin units) +# Destination EIDs: Ethereum = 30101, Arbitrum = 30110, Base = 30184, etc. cast send 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 \ "sendToken((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),(uint256,uint256),address)" \ "(30101,$(cast --to-bytes32 $YOUR_ADDRESS),1000000,990000,0x,0x,0x)" \ - "($NATIVE_FEE,0)" \ + "($LZ_FEE,0)" \ $YOUR_ADDRESS \ --rpc-url https://rpc.tempo.xyz \ --private-key $PRIVATE_KEY @@ -391,6 +371,10 @@ const sendParam = { } ``` +:::warning +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. +::: + ## Further reading - [Stargate documentation](https://stargateprotocol.gitbook.io/stargate/v2-developer-docs) From 692d0d2ae6244909e8f8e109848400238b6d6ce8 Mon Sep 17 00:00:00 2001 From: Uddhav <255779543+letstokenize@users.noreply.github.com> Date: Mon, 23 Mar 2026 23:15:05 -0700 Subject: [PATCH 3/4] docs: update bridge USDC guide to use Base, improve cast/viem examples - Switch cast and viem examples from Ethereum to Base as source/dest chain - Restructure cast steps with separate headings, generic placeholders - Use cast abi-encode instead of cast --to-bytes32 - Add Step 4 (verify transaction status) with LayerZero API link - Move sidebar entry from Build on Tempo to Integrate Tempo > Ecosystem - Merge delivery time into intro paragraph, update to under 1 minute - Clean up viem imports (static createPublicClient), consistent step ordering - Add guides/tmp/ to .gitignore for test scratch files Co-authored-by: Uddhav <255779543+letstokenize@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d1d7c-102d-764e-807a-8155d5803e40 Co-authored-by: Amp --- .gitignore | 3 + src/pages/guide/bridge-usdc-stargate.mdx | 164 +++++++++++++---------- vocs.config.ts | 8 +- 3 files changed, 99 insertions(+), 76 deletions(-) diff --git a/.gitignore b/.gitignore index b03dc1a..ab58711 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,9 @@ src/pages.gen.ts .env*.local src/pages/protocol/tips/tip-* +# Test scratch files +guides/tmp/ + # Playwright playwright-report/ test-results/ diff --git a/src/pages/guide/bridge-usdc-stargate.mdx b/src/pages/guide/bridge-usdc-stargate.mdx index 5b63b09..090a480 100644 --- a/src/pages/guide/bridge-usdc-stargate.mdx +++ b/src/pages/guide/bridge-usdc-stargate.mdx @@ -7,7 +7,7 @@ import { Tabs, Tab } from 'vocs' # Bridge USDC via Stargate -[Stargate](https://stargate.finance/) uses [LayerZero](https://layerzero.network) to enable 1:1 USDC bridging between Tempo and other chains. When you bridge USDC to Tempo, Stargate locks your USDC on the source chain and mints **USDC.e** (Bridged USDC) on Tempo. +[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. ## Contracts on Tempo @@ -42,73 +42,82 @@ Use the Stargate pool contract on the source chain to bridge USDC to Tempo, and 3. Set **Tempo** as the destination chain 4. Enter the amount, approve, and send -USDC.e arrives in your wallet on Tempo once the LayerZero message is delivered (typically under 2 minutes). - ### Using cast (Foundry) -Bridge USDC from Ethereum to Tempo using `cast send`. This calls `sendToken` on the Stargate pool on the source chain. +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. + +#### Step 1 — Get a quote fee estimate from Stargate router -**Step 1 — Quote the fee:** +Use the same parameters you will pass to `sendToken`. Returns `(nativeFee, lzTokenFee)`. +Take the first returned number as ``. ```bash -# Quote the messaging fee (returns nativeFee, lzTokenFee) -cast call 0xc026395860Db2d07ee33e05fE50ed7bD583189C7 \ - "quoteSend((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),bool)((uint256,uint256))" \ - "(30410,$(cast --to-bytes32 $YOUR_ADDRESS),1000000,990000,0x,0x,0x)" \ +cast call 0x27a16dc786820B16E5c9028b75B99F6f604b5d26 \ + 'quoteSend((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),bool)((uint256,uint256))' \ + "(30410,$(cast abi-encode 'f(address)' ),,,0x,0x,0x)" \ false \ - --rpc-url https://eth.llamarpc.com + --rpc-url https://mainnet.base.org ``` -**Step 2 — Approve USDC spend:** +#### Step 2 — Approve USDC on Base + +Approve the Stargate router to spend USDC. ```bash -# Approve the Stargate pool to spend your USDC (Ethereum USDC) -cast send 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \ - "approve(address,uint256)" \ - 0xc026395860Db2d07ee33e05fE50ed7bD583189C7 \ - 1000000 \ - --rpc-url https://eth.llamarpc.com \ +cast send 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 \ + 'approve(address,uint256)' \ + 0x27a16dc786820B16E5c9028b75B99F6f604b5d26 \ + \ + --rpc-url https://mainnet.base.org \ --private-key $PRIVATE_KEY ``` -**Step 3 — Send the bridge transaction:** +#### Step 3 — Send bridge transaction ```bash -# Bridge 1 USDC to Tempo (taxi mode — immediate delivery) -# Replace $NATIVE_FEE with the nativeFee from the quote -cast send 0xc026395860Db2d07ee33e05fE50ed7bD583189C7 \ - "sendToken((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),(uint256,uint256),address)" \ - "(30410,$(cast --to-bytes32 $YOUR_ADDRESS),1000000,990000,0x,0x,0x)" \ - "($NATIVE_FEE,0)" \ - $YOUR_ADDRESS \ - --value $NATIVE_FEE \ - --rpc-url https://eth.llamarpc.com \ +cast send 0x27a16dc786820B16E5c9028b75B99F6f604b5d26 \ + 'sendToken((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),(uint256,uint256),address)' \ + "(30410,$(cast abi-encode 'f(address)' ),,,0x,0x,0x)" \ + "(,0)" \ + \ + --value \ + --rpc-url https://mainnet.base.org \ --private-key $PRIVATE_KEY ``` -:::info -`amountLD` and `minAmountLD` are in USDC's local decimals (6). `1000000` = 1 USDC. Set `minAmountLD` slightly below `amountLD` to account for Stargate fees (e.g. 1% slippage → `990000`). -::: +#### Step 4 — Verify transaction status + +Track the bridge with the transaction hash returned by `sendToken`: + +```text +https://scan.layerzero-api.com/v1/messages/tx/ +``` + +Once delivered, view the destination transaction on Tempo: + +```text +https://explore.tempo.xyz/tx/ +``` ### Using TypeScript (viem) ```typescript -import { createWalletClient, http, parseUnits, pad } from 'viem' -import { mainnet } from 'viem/chains' +import { createWalletClient, createPublicClient, http, parseUnits, pad } from 'viem' +import { base } from 'viem/chains' import { privateKeyToAccount } from 'viem/accounts' const account = privateKeyToAccount('0x...') const walletClient = createWalletClient({ account, - chain: mainnet, + chain: base, transport: http(), }) -// Stargate pool on Ethereum -const stargatePool = '0xc026395860Db2d07ee33e05fE50ed7bD583189C7' as const -// USDC on Ethereum -const usdc = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' as const +// Stargate pool on Base +const stargatePool = '0x27a16dc786820B16E5c9028b75B99F6f604b5d26' as const +// USDC on Base +const usdc = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' as const const amount = parseUnits('1', 6) // 1 USDC const minAmount = parseUnits('0.99', 6) // 1% slippage tolerance @@ -201,8 +210,7 @@ const erc20Abi = [ ] as const // 1. Quote the fee -const { createPublicClient } = await import('viem') -const publicClient = createPublicClient({ chain: mainnet, transport: http() }) +const publicClient = createPublicClient({ chain: base, transport: http() }) const msgFee = await publicClient.readContract({ address: stargatePool, @@ -235,41 +243,57 @@ To bridge USDC.e from Tempo back to another chain, call `sendToken` on the **Sta ### Using cast (Foundry) -```bash -# Bridge 1 USDC.e from Tempo to Ethereum -# StargateOFTUSDC on Tempo: 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 -# USDC.e on Tempo: 0x20C000000000000000000000b9537d11c60E8b50 +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. + +#### Step 1 — Quote the fee + +Use the same parameters you will pass to `sendToken`. Returns `(nativeFee, lzTokenFee)`. +Take the first returned number as `` (in stablecoin units, not ETH). -# Step 1 — Quote the fee (returns fee in stablecoin units, not ETH) +```bash cast call 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 \ - "quoteSend((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),bool)((uint256,uint256))" \ - "(30101,$(cast --to-bytes32 $YOUR_ADDRESS),1000000,990000,0x,0x,0x)" \ + 'quoteSend((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),bool)((uint256,uint256))' \ + "(30184,$(cast abi-encode 'f(address)' ),,,0x,0x,0x)" \ false \ --rpc-url https://rpc.tempo.xyz +``` + +#### Step 2 — Approve USDC.e on Tempo + +Approve the StargateOFTUSDC contract to spend USDC.e. -# Step 2 — Approve USDC.e spend +```bash cast send 0x20C000000000000000000000b9537d11c60E8b50 \ - "approve(address,uint256)" \ + 'approve(address,uint256)' \ 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 \ - 1000000 \ + \ --rpc-url https://rpc.tempo.xyz \ --private-key $PRIVATE_KEY +``` + +#### Step 3 — Send bridge transaction -# Step 3 — Send (no --value needed on Tempo) -# $LZ_FEE = nativeFee from the quote step (in stablecoin units) -# Destination EIDs: Ethereum = 30101, Arbitrum = 30110, Base = 30184, etc. +No `--value` is needed on Tempo — the messaging fee is paid in a TIP-20 stablecoin via [EndpointDollar](#endpointdollar). + +```bash cast send 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 \ - "sendToken((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),(uint256,uint256),address)" \ - "(30101,$(cast --to-bytes32 $YOUR_ADDRESS),1000000,990000,0x,0x,0x)" \ - "($LZ_FEE,0)" \ - $YOUR_ADDRESS \ + 'sendToken((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),(uint256,uint256),address)' \ + "(30184,$(cast abi-encode 'f(address)' ),,,0x,0x,0x)" \ + "(,0)" \ + \ --rpc-url https://rpc.tempo.xyz \ --private-key $PRIVATE_KEY ``` -:::info -When bridging **from** Tempo, the messaging fee is paid via `LZEndpointDollar` in a TIP-20 stablecoin instead of a native gas token. See [EndpointDollar](#endpointdollar) below. -::: +#### Step 4 — Verify transaction status + +Track the bridge with the transaction hash returned by `sendToken`: + +```text +https://scan.layerzero-api.com/v1/messages/tx/ +``` + +Once delivered, view the destination transaction on the destination chain explorer. ### Using TypeScript (viem) @@ -280,33 +304,33 @@ import { privateKeyToAccount } from 'viem/accounts' const account = privateKeyToAccount('0x...') -const publicClient = createPublicClient({ chain: tempo, transport: http() }) const walletClient = createWalletClient({ account, chain: tempo, transport: http(), }) +// StargateOFTUSDC on Tempo const stargateOFT = '0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392' as const +// USDC.e on Tempo const usdce = '0x20C000000000000000000000b9537d11c60E8b50' as const -const amount = parseUnits('1', 6) -const minAmount = parseUnits('0.99', 6) - -// Use the destination chain's EID -const dstEid = 30101 // Ethereum (change for other chains) +const amount = parseUnits('1', 6) // 1 USDC.e +const minAmount = parseUnits('0.99', 6) // 1% slippage tolerance const sendParam = { - dstEid, + dstEid: 30184, // Base to: pad(account.address), amountLD: amount, minAmountLD: minAmount, extraOptions: '0x' as const, composeMsg: '0x' as const, - oftCmd: '0x' as const, + oftCmd: '0x' as const, // taxi mode (immediate) } // 1. Quote the fee +const publicClient = createPublicClient({ chain: tempo, transport: http() }) + const msgFee = await publicClient.readContract({ address: stargateOFT, abi: stargateAbi, // same ABI as above @@ -322,7 +346,7 @@ await walletClient.writeContract({ args: [stargateOFT, amount], }) -// 3. Send +// 3. Send the bridge transaction (no value — fee handled via EndpointDollar) await walletClient.writeContract({ address: stargateOFT, abi: stargateAbi, @@ -331,10 +355,6 @@ await walletClient.writeContract({ }) ``` -:::info -Note that no `value` is passed when sending from Tempo — Tempo has no native gas token. The LayerZero messaging fee is handled automatically via `LZEndpointDollar`. -::: - ## 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. diff --git a/vocs.config.ts b/vocs.config.ts index 112dd8a..ac304c0 100644 --- a/vocs.config.ts +++ b/vocs.config.ts @@ -84,10 +84,6 @@ export default defineConfig({ text: 'Getting Funds on Tempo', link: '/guide/getting-funds', }, - { - text: 'Bridge USDC via Stargate', - link: '/guide/bridge-usdc-stargate', - }, { text: 'Create & Use Accounts', collapsed: true, @@ -297,6 +293,10 @@ export default defineConfig({ text: 'Bridges & Exchanges', link: '/ecosystem/bridges', }, + { + text: 'Bridge USDC via Stargate', + link: '/guide/bridge-usdc-stargate', + }, { text: 'Data & Analytics', link: '/ecosystem/data-analytics', From bf17eb1cb050729a5a7708813923455791b40d26 Mon Sep 17 00:00:00 2001 From: Uddhav <255779543+letstokenize@users.noreply.github.com> Date: Mon, 23 Mar 2026 23:26:08 -0700 Subject: [PATCH 4/4] docs: move bridge guide to top-level under Integrate Tempo Co-Authored-By: Uddhav <255779543+letstokenize@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d1d7c-102d-764e-807a-8155d5803e40 Co-authored-by: Amp --- vocs.config.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vocs.config.ts b/vocs.config.ts index ac304c0..9c4b1db 100644 --- a/vocs.config.ts +++ b/vocs.config.ts @@ -281,6 +281,10 @@ export default defineConfig({ text: 'Contract Verification', link: '/quickstart/verify-contracts', }, + { + text: 'Bridge USDC via Stargate', + link: '/guide/bridge-usdc-stargate', + }, { text: 'Ecosystem', collapsed: true, @@ -293,10 +297,6 @@ export default defineConfig({ text: 'Bridges & Exchanges', link: '/ecosystem/bridges', }, - { - text: 'Bridge USDC via Stargate', - link: '/guide/bridge-usdc-stargate', - }, { text: 'Data & Analytics', link: '/ecosystem/data-analytics',