---
description: Use Stargate to transfer an asset across blockchains.
---

# How to Swap

## Overview

Stargate V2 allows for same-asset bridging only, which means that USDC on Ethereum can only be swapped with USDC on some other chain.

When performing a swap you have two main options for balancing speed and gas costs:

1. Taking a taxi: **Immediately** performs a swap and sends an omnichain message to the destination chain.
2. Riding the bus: Allows the user to take advantage of cheaper gas costs thanks to **transaction batching**. When you use this approach your swap will immediately be settled on the local chain with instant guaranteed finality. However, you may need to wait before you receive the target asset on the destination chain. The message will be sent to destination chain when a "bus" reaches a set number of passengers (between 2-10). An impatient user can also choose to `driveBus`, by buying up the remaining bus tickets.&#x20;

Instant guaranteed finality ensures that your swap will be executed, even when taking the bus.

### OFT standard

As a reminder Stargate V2 interfaces are built upon the IOFT interface for OFTs on LayerZero V2. IOFT interface is [available here](https://github.com/LayerZero-Labs/LayerZero-v2/blob/main/packages/layerzero-v2/evm/oapp/contracts/oft/interfaces/IOFT.sol). Documentation for building on IOFT is [here](https://docs.layerzero.network/v2/developers/evm/oft/composing#sending-token).

This means that when you execute a swap on Stargate you are actually calling `OFT.send()`. Lets take a look at [`IStargate`](https://github.com/stargate-protocol/stargate-v2/blob/main/packages/stg-evm-v2/src/interfaces/IStargate.sol) interface that extends `OFT` standard:

{% code fullWidth="true" %}
```solidity
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import { IOFT, SendParam, MessagingFee, MessagingReceipt, OFTReceipt } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/interfaces/IOFT.sol";

enum StargateType {
    Pool,
    OFT
}

struct Ticket {
    uint56 ticketId;
    bytes passenger;
}

/// @title Interface for Stargate.
/// @notice Defines an API for sending tokens to destination chains.
interface IStargate is IOFT {
    /// @dev This function is same as `send` in OFT interface but returns the ticket data if in the bus ride mode,
    /// which allows the caller to ride and drive the bus in the same transaction.
    function sendToken(
        SendParam calldata _sendParam,
        MessagingFee calldata _fee,
        address _refundAddress
    ) external payable returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt, Ticket memory ticket);

    /// @notice Returns the Stargate implementation type.
    function stargateType() external pure returns (StargateType);
}
```
{% endcode %}

As you can see above the Stargate interface isn't much different from an OFT. The most important piece of the interface is:

{% code fullWidth="true" %}
```solidity
SendParam calldata _sendParam
```
{% endcode %}

Let's explain it:

{% code fullWidth="true" %}
```solidity
/**
 * @dev Struct representing token parameters for the OFT send() operation.
 */
struct SendParam {
    uint32 dstEid; // Destination endpoint ID.
    bytes32 to; // Recipient address.
    uint256 amountLD; // Amount to send in local decimals.
    uint256 minAmountLD; // Minimum amount to send in local decimals.
    bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.
    bytes composeMsg; // The composed message for the send() operation.
    bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.
}
```
{% endcode %}

The biggest Stargate specific difference is the use of last three properties of the struct above.

### SendParam.extraOptions

`extraOptions`&#x20;

**If you use taxi mode** then these options are LayerZero's [execution options](https://docs.layerzero.network/v2/developers/evm/gas-settings/options). You can use [`OptionsBuilder`](https://docs.layerzero.network/v2/developers/evm/gas-settings/options) to prepare them. The exception to the above is that you don't need to put `addExecutorLzReceiveOption()` in them, because it is handled automatically by Stargate.

* The practical example of using `extraOptions` can be found in [Composability](composability) section where [`addExecutorLzComposeOption()`](https://docs.layerzero.network/v2/developers/evm/gas-settings/options#lzcompose-option) is used to enable composing functionality
* Another interesting option is [`addExecutorNativeDropOption()`](https://docs.layerzero.network/v2/developers/evm/gas-settings/options#lznativedrop-option) which can be used to drop native tokens to the address you specify.

**If you use bus mode** these are `options` from `RideBusParams`:

{% code fullWidth="true" %}
```solidity
struct RideBusParams {
    address sender;
    uint32 dstEid;
    bytes32 receiver;
    uint64 amountSD;
    bool nativeDrop;
}
```
{% endcode %}

### SendParam.composeMsg

Check the [Composability](composability) page to learn more about it. If you don't plan to use any destination logic, feel free to just use: `new bytes(0)`.

### SendParam.oftCmd

The OFT command to be executed, unused in default OFT implementation, but in Stargate it is used to indicate the transportation mode.

{% code fullWidth="true" %}
```solidity
pragma solidity ^0.8.22;

library OftCmdHelper {
    function taxi() internal pure returns (bytes memory) {
        return "";
    }

    function bus() internal pure returns (bytes memory) {
        return new bytes(1);
    }

    function drive(bytes memory _passengers) internal pure returns (bytes memory) {
        return _passengers;
    }
}
```
{% endcode %}

Empty bytes are "taxi", `bytes(1)` is riding a bus. If you pass a bytes array of `_passengers` it indicates you want to drive a bus.

## Take a taxi

Let's start with the first high-level example.

Users can opt to pay for their transactions to be bridged immediately by taking a taxi. Take a look at `prepareTakeTaxi()` in the code example below:

{% code fullWidth="true" %}
```solidity
pragma solidity ^0.8.19;

import { IStargate } from "@stargatefinance/stg-evm-v2/src/interfaces/IStargate.sol";
import { MessagingFee, OFTReceipt, SendParam } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/interfaces/IOFT.sol";

contract StargateIntegration {
    function prepareTakeTaxi(
        address _stargate,
        uint32 _dstEid,
        uint256 _amount,
        address _receiver
    ) external view returns (uint256 valueToSend, SendParam memory sendParam, MessagingFee memory messagingFee) {
        sendParam = SendParam({
            dstEid: _dstEid,
            to: addressToBytes32(_receiver),
            amountLD: _amount,
            minAmountLD: _amount,
            extraOptions: new bytes(0),
            composeMsg: new bytes(0),
            oftCmd: ""
        });

        IStargate stargate = IStargate(_stargate);

        (, , OFTReceipt memory receipt) = stargate.quoteOFT(sendParam);
        sendParam.minAmountLD = receipt.amountReceivedLD;

        messagingFee = stargate.quoteSend(sendParam, false);
        valueToSend = messagingFee.nativeFee;

        if (stargate.token() == address(0x0)) {
            valueToSend += sendParam.amountLD;
        }
    }

    function addressToBytes32(address _addr) internal pure returns (bytes32) {
        return bytes32(uint256(uint160(_addr)));
    }
}
```
{% endcode %}

...and the following code initiates an omnichain transaction using the example Alice account:

{% code fullWidth="true" %}
```solidity
StargateIntegration integration = new StargateIntegration();

// as Alice
ERC20(sourceChainPoolToken).approve(stargate, amount);

(uint256 valueToSend, SendParam memory sendParam, MessagingFee memory messagingFee) =
    integration.prepareTakeTaxi(stargate, destinationEndpointId, amount, alice);

IStargate(stargate).sendToken{ value: valueToSend }(sendParam, messagingFee, ALICE);
```
{% endcode %}

The above code executes a swap and requests an immediate taxi ride of the assets to the destination chain.

## Ride the bus

Here is an example of how to perform an omnichain swap using Solidity and bus mode:

{% code fullWidth="true" %}
```solidity
pragma solidity ^0.8.19;

import { IStargate } from "@stargatefinance/stg-evm-v2/src/interfaces/IStargate.sol";
import { MessagingFee, OFTReceipt, SendParam } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/interfaces/IOFT.sol";

contract StargateIntegration {
    function prepareRideBus(
        address _stargate,
        uint32 _dstEid,
        uint256 _amount,
        address _receiver
    ) external view returns (uint256 valueToSend, SendParam memory sendParam, MessagingFee memory messagingFee) {
        sendParam = SendParam({
            dstEid: _dstEid,
            to: addressToBytes32(_receiver),
            amountLD: _amount,
            minAmountLD: _amount,
            extraOptions: new bytes(0),
            composeMsg: new bytes(0),
            oftCmd: new bytes(1)
        });

        IStargate stargate = IStargate(_stargate);

        (, , OFTReceipt memory receipt) = stargate.quoteOFT(sendParam);
        sendParam.minAmountLD = receipt.amountReceivedLD;

        messagingFee = stargate.quoteSend(sendParam, false);
        valueToSend = messagingFee.nativeFee;

        if (stargate.token() == address(0x0)) {
            valueToSend += sendParam.amountLD;
        }
    }

    function addressToBytes32(address _addr) internal pure returns (bytes32) {
        return bytes32(uint256(uint160(_addr)));
    }
}
```
{% endcode %}

The function `prepareRideBus` contains logic to interact with Stargate and prepare arguments for token swap. You can send the swap transaction using code below:

<pre class="language-solidity" data-full-width="true"><code class="lang-solidity"><strong>StargateIntegration integration = new StargateIntegration();
</strong>
// as Alice
ERC20(sourceChainPoolToken).approve(stargate, amount);

(uint256 valueToSend, SendParam memory sendParam, MessagingFee memory messagingFee) =
    integration.prepareRideBus(stargate, destinationEndpointId, amount, alice);

IStargate(stargate).sendToken{ value: valueToSend }(sendParam, messagingFee, ALICE);
</code></pre>

The bus ride isn't instant. The swap is locally settled instantly, but you need to wait to receive tokens on the destination chain because bus transactions are batched together.

#### Bus Ticket

When you board a bus, you will receive a `Ticket` for your journey to the destination chain. This ticket can be acquired by capturing parameters from the `sendToken()` function.

{% code fullWidth="true" %}
```solidity
(, , Ticket memory ticket) = stargate.sendToken{ value: valueToSend }(sendParam, messagingFee, ALICE);
```
{% endcode %}

`Ticket` is a struct:

{% code fullWidth="true" %}
```solidity
struct Ticket {
    uint56 ticketId;
    bytes passenger;
}
```
{% endcode %}

#### Checking if the Bus has already left

If you want to check whether your bus has already left, compare your `Ticket.ticketId` with `busQueues[dstEid].nextTicketId`. If the `nextTicketId` is greater than your `ticketId` it means that your tokens were sent to the destination chain.
