> ## Documentation Index
> Fetch the complete documentation index at: https://docs.portalhq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Swap Tokens with 0x

> Learn how to swap tokens using Portal's Flutter SDK with 0x integration.

Portal's Flutter SDK provides token swapping functionality through the `portal.trading.zeroX` API. This integration allows you to retrieve swap quotes, inspect available liquidity sources, and execute token swaps using 0x.

## Overview

Using the 0x integration, you can:

* Fetch **indicative prices** for token swaps
* Fetch **swap quotes** between supported tokens
* Retrieve **available liquidity sources**
* **Execute swaps** by signing and submitting transactions

All swap execution is performed by submitting the transaction data returned by 0x using `portal.sendTransaction` (or the lower-level `portal.request`).

## Prerequisites

Before using the 0x API, make sure you have:

* A properly initialized Portal client
* An active wallet with sufficient balance on the source network (see [Create a wallet](./create-a-wallet))
* 0x integration enabled in your Portal Dashboard (see [0x Integration](../../../integrations/Trading/zerox)) OR have a 0x API Key available

***

## Using a Custom 0x API Key (Optional)

By default, Portal uses the 0x API Key configured in the Portal Dashboard to communicate with the 0x integration.

If you have a 0x API key that you want to test with locally, you can optionally include it via the named `zeroXApiKey` parameter on any of the three methods.

```dart theme={null}
import 'package:portal_flutter/portal_flutter.dart';

try {
  final sources = await portal.trading.zeroX.getSources(
    'eip155:1',
    zeroXApiKey: 'YOUR_0X_API_KEY',
  );
  print('Available sources: ${sources.sources}');
} on PortalException catch (e) {
  print('Error: [${e.code}] ${e.message}');
}
```

If you've configured your 0x API key in the Portal Dashboard, you can omit the `zeroXApiKey` parameter:

```dart theme={null}
final sources = await portal.trading.zeroX.getSources('eip155:1');
```

***

## Getting a Price (Indicative)

Use `portal.trading.zeroX.getPrice` to retrieve an **indicative price** for a token swap without generating executable transaction data.

This method is useful for displaying prices, estimating swap outcomes, or building preview experiences without committing to a quote.

```dart theme={null}
import 'package:portal_flutter/portal_flutter.dart';

try {
  final request = ZeroXPriceRequest(
    chainId: 'eip155:1',
    buyToken: 'USDC',
    sellToken: 'ETH',
    sellAmount: '100000000000000', // 0.0001 ETH
  );

  final price = await portal.trading.zeroX.getPrice(request);

  print('Buy Amount: ${price.buyAmount ?? "-"}');
  print('Sell Amount: ${price.sellAmount ?? "-"}');
  print('Liquidity Available: ${price.liquidityAvailable ?? false}');
  print('Estimated Gas: ${price.gas ?? "-"}');
} on PortalException catch (e) {
  print('Error: [${e.code}] ${e.message}');
}
```

***

## Getting a Swap Quote

Use `portal.trading.zeroX.getQuote` to fetch a swap quote from 0x with executable transaction data.

```dart theme={null}
import 'package:portal_flutter/portal_flutter.dart';

try {
  final request = ZeroXQuoteRequest(
    chainId: 'eip155:1',
    buyToken: 'USDC',
    sellToken: 'ETH',
    sellAmount: '100000000000000', // 0.0001 ETH
  );

  final quote = await portal.trading.zeroX.getQuote(request);

  print('Buy Amount: ${quote.buyAmount ?? "-"}');
  print('Min Buy Amount: ${quote.minBuyAmount ?? "-"}');
  print('Total Network Fee: ${quote.totalNetworkFee ?? "-"}');

  final tx = quote.transaction;
  if (tx != null) {
    print('Transaction → to: ${tx.to}, gas: ${tx.gas}, value: ${tx.value}');
  }
} on PortalException catch (e) {
  print('Error: [${e.code}] ${e.message}');
}
```

***

## Getting Liquidity Sources

You can query available liquidity sources supported by 0x using `portal.trading.zeroX.getSources`.

```dart theme={null}
import 'package:portal_flutter/portal_flutter.dart';

try {
  final response = await portal.trading.zeroX.getSources('eip155:1');

  // `sources` is List<String?> — filter out any nulls before use.
  final sources = response.sources.whereType<String>().toList();
  print('Available sources (${sources.length}): $sources');
  print('Request id (zid): ${response.zid ?? "-"}');
} on PortalException catch (e) {
  print('Error: [${e.code}] ${e.message}');
}
```

***

## Checking Quote Issues

When getting a quote, you may encounter issues related to allowances, balances, or simulation. Always check for these before executing a swap:

```dart theme={null}
final quote = await portal.trading.zeroX.getQuote(request);

final issues = quote.issues;
if (issues != null) {
  final allowance = issues.allowance;
  if (allowance != null) {
    print('Allowance issue: actual=${allowance.actual}, '
        'spender=${allowance.spender}');
  }

  final balance = issues.balance;
  if (balance != null) {
    print('Balance issue: token=${balance.token}, '
        'actual=${balance.actual}, expected=${balance.expected}');
  }

  if (issues.simulationIncomplete == true) {
    print('Warning: Simulation incomplete');
  }

  final invalidSources = issues.invalidSourcesPassed;
  if (invalidSources != null && invalidSources.isNotEmpty) {
    print('Invalid sources: $invalidSources');
  }
}
```

***

## Executing the Swap

Once you receive a quote containing transaction data, execute the swap by submitting the transaction through the Portal SDK.

<Note>
  The transaction data returned by 0x may include gas parameters such as `gas` or `gasPrice`. These fields are optional — you can omit them and let Portal estimate gas automatically, or include them if you prefer to use 0x's suggested values.
</Note>

The simplest path uses `portal.sendTransaction`, which forwards the call as `eth_sendTransaction` and returns the transaction hash:

```dart theme={null}
import 'package:portal_flutter/portal_flutter.dart';

try {
  // Step 1: Get the quote
  final quote = await portal.trading.zeroX.getQuote(
    ZeroXQuoteRequest(
      chainId: 'eip155:1',
      buyToken: 'USDC',
      sellToken: 'ETH',
      sellAmount: '100000000000000', // 0.0001 ETH
    ),
  );

  final tx = quote.transaction;
  if (tx == null) {
    print('No transaction in quote response');
    return;
  }

  // Step 2: Submit the transaction
  final txHash = await portal.sendTransaction(
    chainId: 'eip155:1',
    to: tx.to,
    data: tx.data,
    value: tx.value,
  );

  print('Transaction submitted: $txHash');
} on PortalException catch (e) {
  print('Swap failed: [${e.code}] ${e.message}');
}
```

If you need full control over the request envelope, you can use `portal.request` directly:

```dart theme={null}
final result = await portal.request(
  chainId: 'eip155:1',
  method: 'eth_sendTransaction',
  params: [
    {
      'to': tx.to,
      'from': tx.from,
      'data': tx.data,
      'value': tx.value,
      'gas': tx.gas,        // optional
      'gasPrice': tx.gasPrice, // optional
    }
  ],
);

print('Transaction submitted: ${result.result}');
```

***

## Error Handling

All three methods throw a `PortalException` when the native SDK or the 0x service returns an error. Catch it the same way you would any other Portal call:

```dart theme={null}
try {
  final sources = await portal.trading.zeroX.getSources('eip155:1');
  print('Success: ${sources.sources.length} sources');
} on PortalException catch (e) {
  // e.code is one of:
  //   ZEROX_GET_SOURCES_ERROR
  //   ZEROX_GET_PRICE_ERROR
  //   ZEROX_GET_QUOTE_ERROR
  // and the underlying native error message is preserved in e.message.
  print('0x error: [${e.code}] ${e.message}');
} catch (e) {
  print('Unexpected error: $e');
}
```

***

## Complete Swap Flow Example

Here's a complete example of executing a token swap end-to-end:

```dart theme={null}
import 'package:portal_flutter/portal_flutter.dart';

Future<void> performZeroXSwap(Portal portal) async {
  const chainId = 'eip155:1';

  try {
    // Step 1: Discover available sources for the chain (optional)
    final sourcesResponse = await portal.trading.zeroX.getSources(chainId);
    final sources = sourcesResponse.sources.whereType<String>().toList();
    print('Sources (${sources.length}): ${sources.take(5).join(", ")}...');

    // Step 2: Get an executable quote
    final quote = await portal.trading.zeroX.getQuote(
      ZeroXQuoteRequest(
        chainId: chainId,
        buyToken: 'USDC',
        sellToken: 'ETH',
        sellAmount: '100000000000000', // 0.0001 ETH
      ),
    );

    // Step 3: Inspect issues before submitting
    final balance = quote.issues?.balance;
    if (balance != null && balance.actual == '0') {
      print('Insufficient balance — aborting');
      return;
    }

    // Step 4: Submit the transaction
    final tx = quote.transaction;
    if (tx == null) {
      print('No transaction in quote response');
      return;
    }

    final txHash = await portal.sendTransaction(
      chainId: chainId,
      to: tx.to,
      data: tx.data,
      value: tx.value,
    );

    print('Transaction submitted: $txHash');
  } on PortalException catch (e) {
    print('Swap failed: [${e.code}] ${e.message}');
  }
}
```

***

## Supported Networks

The `portal.trading.zeroX` API supports a predefined set of EIP-155 networks. Requests using unsupported chains will fail.

| Network    | EIP-155 Chain ID |
| ---------- | ---------------- |
| Ethereum   | `eip155:1`       |
| Optimism   | `eip155:10`      |
| BSC        | `eip155:56`      |
| Unichain   | `eip155:130`     |
| Polygon    | `eip155:137`     |
| Worldchain | `eip155:480`     |
| Mantle     | `eip155:5000`    |
| Base       | `eip155:8453`    |
| Monad      | `eip155:143`     |
| Mode       | `eip155:34443`   |
| Arbitrum   | `eip155:42161`   |
| Avalanche  | `eip155:43114`   |
| Ink        | `eip155:57073`   |
| Linea      | `eip155:59144`   |
| Berachain  | `eip155:80094`   |
| Blast      | `eip155:81457`   |
| Scroll     | `eip155:534352`  |

<Note>
  0x supports mainnet networks only — calls against testnets surface as `PortalException(ZEROX_GET_QUOTE_ERROR)` from the native SDK.
</Note>

***

## Next Steps

* Learn how to [bridge across chains with Li.Fi](./lifi)
* Learn how to [sign Ethereum transactions](./sign-a-transaction)
* Explore how to [send tokens](./send-tokens)
