diff --git a/src/pages/guide/bridge-layerzero.mdx b/src/pages/guide/bridge-layerzero.mdx index af1cac87..c47d876b 100644 --- a/src/pages/guide/bridge-layerzero.mdx +++ b/src/pages/guide/bridge-layerzero.mdx @@ -260,9 +260,7 @@ await walletClient.writeContract({ ## Bridge from Tempo -To bridge from Tempo back to another chain, call `sendToken` on the Stargate OFT contract on Tempo. The process is similar to bridging in - quote, approve, send - but includes additional steps to prepare the messaging fee. - -Because Tempo has no native gas token, LayerZero messaging fees are paid in a TIP-20 stablecoin via [LZEndpointDollar](#endpointdollar). Before sending a bridge transaction, you must wrap your USDC.e into an LZD (LayerZero Dollar) token that the endpoint can consume as a fee. This involves approving USDC.e to the LZD wrapper contract, wrapping it, and then approving the resulting LZD to the Stargate pool. +Use [`TempoOFTWrapper`](https://explore.tempo.xyz/address/0xbb95daF376cd63F258d7c37a4eFe57c10055E8E0) (`0xbb95daF376cd63F258d7c37a4eFe57c10055E8E0`). It pulls your stablecoin, wraps the fee portion into LZD, and calls `send()` on the OFT in a single transaction. Pay fees in `USDC.e`, `USDT0`, or `pathUSD`. #### Using cast (Foundry) @@ -280,77 +278,42 @@ cast call 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 \ --rpc-url https://rpc.tempo.xyz ``` -Take the first returned number as `` (in stablecoin units, not ETH). - -### Approve USDC.e to the LZD wrapper +The first returned number is `` in stablecoin units. -Approve the `LZEndpointDollar` wrapper contract to spend `` of your USDC.e. This is the amount needed to cover the LayerZero messaging fee. +### Approve USDC.e to the wrapper ```bash cast send 0x20C000000000000000000000b9537d11c60E8b50 \ - "approve(address,uint256)" \ - 0x0cEb237E109eE22374a567c6b09F373C73FA4cBb \ - \ - --rpc-url https://rpc.tempo.xyz \ - --private-key $PRIVATE_KEY -``` - -### Wrap USDC.e into LZD - -Wrap your USDC.e into the LZD token so it can be used as a messaging fee by the LayerZero endpoint. - -```bash -cast send 0x0cEb237E109eE22374a567c6b09F373C73FA4cBb \ - "wrap(address,address,uint256)" \ - 0x20C000000000000000000000b9537d11c60E8b50 \ - \ - \ + 'approve(address,uint256)' \ + 0xbb95daF376cd63F258d7c37a4eFe57c10055E8E0 \ + $(( + )) \ --rpc-url https://rpc.tempo.xyz \ --private-key $PRIVATE_KEY ``` -### Approve LZD to Stargate +One approval covers both the bridge amount and the fee since they share the same token. Use a max approval if you bridge frequently. -Approve the Stargate OFT contract to spend your LZD so it can pay the messaging fee when sending. +### Send ```bash -cast send 0x0cEb237E109eE22374a567c6b09F373C73FA4cBb \ - "approve(address,uint256)" \ +cast send 0xbb95daF376cd63F258d7c37a4eFe57c10055E8E0 \ + 'sendOFT(address,address,(uint32,bytes32,uint256,uint256,bytes,bytes,bytes),uint256)' \ 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 \ + 0x20C000000000000000000000b9537d11c60E8b50 \ + "(30184,$(cast abi-encode 'f(address)' ),,,0x,0x,0x)" \ \ --rpc-url https://rpc.tempo.xyz \ --private-key $PRIVATE_KEY ``` -### Approve token on Tempo - -```bash -cast send 0x20C000000000000000000000b9537d11c60E8b50 \ - 'approve(address,uint256)' \ - 0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392 \ - \ - --rpc-url https://rpc.tempo.xyz \ - --private-key $PRIVATE_KEY -``` - -### Send bridge transaction - -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)' \ - "(30184,$(cast abi-encode 'f(address)' ),,,0x,0x,0x)" \ - "(,0)" \ - \ - --rpc-url https://rpc.tempo.xyz \ - --private-key $PRIVATE_KEY -``` +`` here is `maxNativeFee` — the call reverts if the fee at execution exceeds this. ### Verify transaction status +Track delivery to the destination chain via the LayerZero scan API: + ```text -https://scan.layerzero-api.com/v1/messages/tx/ +https://scan.layerzero-api.com/v1/messages/tx/ ``` ::::: @@ -358,28 +321,20 @@ https://scan.layerzero-api.com/v1/messages/tx/ #### Using TypeScript (viem) ```typescript -import { createWalletClient, createPublicClient, http, parseUnits, pad } from 'viem' +import { createWalletClient, createPublicClient, http, parseUnits, pad, parseAbi } from 'viem' import { tempo } from 'viem/chains' import { privateKeyToAccount } from 'viem/accounts' const account = privateKeyToAccount('0x...') +const walletClient = createWalletClient({ account, chain: tempo, transport: http() }) +const publicClient = createPublicClient({ chain: tempo, transport: http() }) -const walletClient = createWalletClient({ - account, - chain: tempo, - transport: http(), -}) - -// Stargate OFT for USDC.e on Tempo -const stargateOFT = '0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392' as const -// USDC.e on Tempo +const oft = '0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392' as const // Stargate USDC.e const usdce = '0x20C000000000000000000000b9537d11c60E8b50' as const -// LZEndpointDollar wrapper -const lzd = '0x0cEb237E109eE22374a567c6b09F373C73FA4cBb' as const - -const amount = parseUnits('1', 6) // 1 USDC.e -const minAmount = parseUnits('0.99', 6) // 1% slippage tolerance +const wrapper = '0xbb95daF376cd63F258d7c37a4eFe57c10055E8E0' as const +const amount = parseUnits('1', 6) +const minAmount = parseUnits('0.99', 6) const sendParam = { dstEid: 30184, // Base to: pad(account.address), @@ -387,74 +342,35 @@ const sendParam = { minAmountLD: minAmount, extraOptions: '0x' as const, composeMsg: '0x' as const, - oftCmd: '0x' as const, // taxi mode (immediate) + oftCmd: '0x' as const, } -const wrapAbi = [ - { - name: 'wrap', - type: 'function', - stateMutability: 'nonpayable', - inputs: [ - { name: 'token', type: 'address' }, - { name: 'to', type: 'address' }, - { name: 'amount', type: 'uint256' }, - ], - outputs: [], - }, -] as const - -// 1. Quote the fee -const publicClient = createPublicClient({ chain: tempo, transport: http() }) +const wrapperAbi = parseAbi([ + 'function sendOFT(address oft, address feeToken, (uint32 dstEid, bytes32 to, uint256 amountLD, uint256 minAmountLD, bytes extraOptions, bytes composeMsg, bytes oftCmd) sendParam, uint256 maxNativeFee)', +]) +// 1. Quote const msgFee = await publicClient.readContract({ - address: stargateOFT, - abi: stargateAbi, // same ABI as above - functionName: 'quoteSend', - args: [sendParam, false], + address: oft, abi: stargateAbi, functionName: 'quoteSend', args: [sendParam, false], }) -// 2. Approve USDC.e to LZD wrapper (for the messaging fee) +// 2. Approve USDC.e to wrapper (bridge amount + fee) await walletClient.writeContract({ - address: usdce, - abi: erc20Abi, - functionName: 'approve', - args: [lzd, msgFee.nativeFee], -}) - -// 3. Wrap USDC.e into LZD -await walletClient.writeContract({ - address: lzd, - abi: wrapAbi, - functionName: 'wrap', - args: [usdce, account.address, msgFee.nativeFee], -}) - -// 4. Approve LZD to Stargate (for the messaging fee) -await walletClient.writeContract({ - address: lzd, - abi: erc20Abi, - functionName: 'approve', - args: [stargateOFT, msgFee.nativeFee], -}) - -// 5. Approve USDC.e to Stargate (for the bridge amount) -await walletClient.writeContract({ - address: usdce, - abi: erc20Abi, - functionName: 'approve', - args: [stargateOFT, amount], + address: usdce, abi: erc20Abi, functionName: 'approve', + args: [wrapper, amount + msgFee.nativeFee], }) -// 6. Send the bridge transaction (no value - fee handled via EndpointDollar) +// 3. Send (wrap + approve + send happen atomically inside) await walletClient.writeContract({ - address: stargateOFT, - abi: stargateAbi, - functionName: 'sendToken', - args: [sendParam, msgFee, account.address], + address: wrapper, abi: wrapperAbi, functionName: 'sendOFT', + args: [oft, usdce, sendParam, msgFee.nativeFee], }) ``` +:::warning +**Do not use the wrapper for compose messages that refund `msg.sender`.** Refunds go to the wrapper, not your wallet, and are lost. For that case call the OFT directly — see [LayerZero's direct flow](https://docs.layerzero.network/v2/developers/tempo/how-to/support-ofts-and-oapps#direct-flow-1-view-call-5-transactions). +::: + ### Bus vs. Taxi mode Stargate offers two delivery modes: