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

# Supported Networks

> Stablecoin networks supported for deposits and withdrawals. USDC and USDT each settle on 10 chains; EURC settles on 4.

**What this is.** The authoritative list of blockchains OpenFX supports per stablecoin. Each stablecoin × network combination is a logically distinct routing target. A USDC balance on Ethereum and a USDC balance on Solana are tracked separately, and addresses are not interchangeable across chains.

**When it matters.** Any time you're verifying a stablecoin withdrawal account, reading a deposit's on-chain origin from `network`, or building a UI that lets users pick which chain to receive funds on.

**What you'll learn.** Which chains each stablecoin supports, the `network` value the API returns for each chain, and per-network request bodies for `POST /v3/fx/withdrawals`.

## Networks by stablecoin

### USDC

| Network             | API value   |
| ------------------- | ----------- |
| Aptos               | `APTOS`     |
| Arbitrum            | `ARBITRUM`  |
| Avalanche           | `AVALANCHE` |
| Base                | `BASE`      |
| Binance Smart Chain | `BSC`       |
| Ethereum            | `ETHEREUM`  |
| Optimism            | `OPTIMISM`  |
| Polygon             | `POLYGON`   |
| Solana              | `SOLANA`    |
| Tron                | `TRON`      |

### USDT

| Network             | API value   |
| ------------------- | ----------- |
| Aptos               | `APTOS`     |
| Arbitrum            | `ARBITRUM`  |
| Avalanche           | `AVALANCHE` |
| Base                | `BASE`      |
| Binance Smart Chain | `BSC`       |
| Ethereum            | `ETHEREUM`  |
| Optimism            | `OPTIMISM`  |
| Polygon             | `POLYGON`   |
| Solana              | `SOLANA`    |
| Tron                | `TRON`      |

### EURC

| Network   | API value   |
| --------- | ----------- |
| Avalanche | `AVALANCHE` |
| Base      | `BASE`      |
| Ethereum  | `ETHEREUM`  |
| Solana    | `SOLANA`    |

<Warning>
  **USDC on Ethereum and USDC on Polygon are different ledgers.** Even when two
  EVM chains share the same address format (e.g. `0x…` on Ethereum and Polygon),
  tokens sent on Polygon settle on the Polygon ledger and aren't visible on
  Ethereum, and vice versa. On non-EVM chains (Solana, Tron, Aptos), the address
  formats are entirely different too. Either way, the verified withdrawal
  account pins both the currency and the network; OpenFX routes the withdrawal
  using the account's specified network, and the resulting Withdrawal reports
  the chain at `network`.
</Warning>

## How the API exposes the network

* **`POST /v3/fx/withdrawals`**: You don't specify a network on the request. The withdrawal account (identified by `withdrawalAccountId`) carries the network, and OpenFX infers it from there. See [Trade Settlement](/v3/trade-settlement).
* **`GET /v3/fx/withdrawals/{id}`, `GET /v3/fx/deposits`**: the chain a stablecoin movement settled on lives at `network` (e.g. `ETHEREUM`). A fiat movement carries `network: null` instead (`assetType: "FIAT"` explains why). See [Deposit lifecycle](/v3/deposit-lifecycle) and [Withdrawal lifecycle](/v3/withdrawal-lifecycle).
* **`GET /v3/fx/withdrawal-accounts`**: The `rail` field identifies which chain a stablecoin account targets. Use this to pick the right `withdrawalAccountId` for a given chain.

## Examples by network

Each example is a complete `POST /v3/fx/withdrawals` request body. Pick the verified `withdrawalAccountId` whose `currency` and `network` match the chain you want to send on.

<CodeGroup>
  ```javascript Node theme={null}
  // POST /v3/fx/withdrawals — the request body shape is identical across
  // every chain; only withdrawalAccountId/withdrawalAmount/currency change.
  // The pattern from /v3/supported-networks#examples-by-network.
  // signRequest is the real helper from /v3/authentication — adjust this path
  // if you've copied it somewhere else in your own project.
  const { randomUUID } = require("node:crypto");
  const { signRequest } = require("../../authentication/node/sign-request");
  // Builds the POST /v3/fx/withdrawals body. Kept separate from the network
  // call so it's testable without a real request.
  function buildWithdrawalBody(withdrawalAccountId, withdrawalAmount, currency) {
    return { withdrawalAccountId, withdrawalAmount, currency };
  }
  async function withdraw(withdrawalAccountId, withdrawalAmount, currency) {
    const url = "https://api.openfx.com/v3/fx/withdrawals";
    const body = JSON.stringify(buildWithdrawalBody(withdrawalAccountId, withdrawalAmount, currency));
    const { headers } = signRequest({ method: "POST", url, body });
    headers["Idempotency-Key"] = randomUUID();
    const res = await fetch(url, { method: "POST", headers, body });
    return res.json();
  }
  module.exports = { buildWithdrawalBody, withdraw };
  if (require.main === module) {
    // USDC on Ethereum — see the table on /v3/supported-networks#examples-by-network
    // for the withdrawalAccountId per network.
    withdraw("8ff46b97-5742-4196-88dd-db7b0b46ab7a", "1000.00", "USDC").catch((err) => {
      console.error("Error:", err.message);
      process.exitCode = 1;
    });
  }
  ```

  ```python Python theme={null}
  """POST /v3/fx/withdrawals — the request body shape is identical across
  every chain; only withdrawal_account_id/withdrawal_amount/currency change.
  The pattern from /v3/supported-networks#examples-by-network."""
  import json
  import os
  import sys
  import uuid
  # sign_request is the real helper from /v3/authentication — adjust this path
  # if you've copied it somewhere else in your own project.
  sys.path.insert(
      0, os.path.join(os.path.dirname(__file__), "..", "..", "authentication", "python")
  )
  from sign_request import sign_request  # noqa: E402
  import requests  # noqa: E402
  def build_withdrawal_body(withdrawal_account_id, withdrawal_amount, currency):
      """Builds the POST /v3/fx/withdrawals body. Kept separate from the
      network call so it's testable without a real request."""
      return {
          "withdrawalAccountId": withdrawal_account_id,
          "withdrawalAmount": withdrawal_amount,
          "currency": currency,
      }
  def withdraw(withdrawal_account_id, withdrawal_amount, currency):
      url = "https://api.openfx.com/v3/fx/withdrawals"
      body = json.dumps(build_withdrawal_body(withdrawal_account_id, withdrawal_amount, currency)).encode("utf-8")
      headers, _ = sign_request("POST", url, body)
      headers["Idempotency-Key"] = str(uuid.uuid4())
      return requests.post(url, headers=headers, data=body).json()
  if __name__ == "__main__":
      # USDC on Ethereum — see the table on
      # /v3/supported-networks#examples-by-network for the
      # withdrawal_account_id per network.
      print(withdraw("8ff46b97-5742-4196-88dd-db7b0b46ab7a", "1000.00", "USDC"))
  ```

  ```java Java theme={null}
  // POST /v3/fx/withdrawals — the request body shape is identical across
  // every chain; only withdrawalAccountId/withdrawalAmount/currency change.
  // The pattern from /v3/supported-networks#examples-by-network. Uses only
  // the built-in JDK APIs — no external dependency, so JSON serialization
  // below is intentionally minimal (a flat object serializer), not a
  // general JSON library.
  import java.net.HttpURLConnection;
  import java.net.URI;
  import java.nio.charset.StandardCharsets;
  import java.util.LinkedHashMap;
  import java.util.Map;
  import java.util.UUID;
  public class Withdraw {
      // Builds the POST /v3/fx/withdrawals body. Kept separate from the
      // network call so it's testable without a real request.
      static Map<String, String> buildWithdrawalBody(String withdrawalAccountId, String withdrawalAmount, String currency) {
          Map<String, String> body = new LinkedHashMap<>();
          body.put("withdrawalAccountId", withdrawalAccountId);
          body.put("withdrawalAmount", withdrawalAmount);
          body.put("currency", currency);
          return body;
      }
      // Escapes a string for embedding in a JSON string literal. Sufficient
      // for this page's field values; not a general JSON serializer.
      static String escapeJson(String value) {
          StringBuilder out = new StringBuilder();
          for (int i = 0; i < value.length(); i++) {
              char c = value.charAt(i);
              switch (c) {
                  case '"': out.append("\\\""); break;
                  case '\\': out.append("\\\\"); break;
                  case '\n': out.append("\\n"); break;
                  case '\r': out.append("\\r"); break;
                  case '\t': out.append("\\t"); break;
                  default:
                      if (c < 0x20) out.append(String.format("\\u%04x", (int) c));
                      else out.append(c);
              }
          }
          return out.toString();
      }
      // Serializes a flat string-valued map to a JSON object. Sufficient for
      // this page's request body; not a general JSON serializer.
      static String toJsonObject(Map<String, String> fields) {
          StringBuilder json = new StringBuilder("{");
          boolean first = true;
          for (Map.Entry<String, String> entry : fields.entrySet()) {
              if (!first) json.append(",");
              first = false;
              json.append("\"").append(entry.getKey()).append("\":\"").append(escapeJson(entry.getValue())).append("\"");
          }
          return json.append("}").toString();
      }
      static void withdraw(String withdrawalAccountId, String withdrawalAmount, String currency) throws Exception {
          String url = "https://api.openfx.com/v3/fx/withdrawals";
          byte[] body = toJsonObject(buildWithdrawalBody(withdrawalAccountId, withdrawalAmount, currency))
                  .getBytes(StandardCharsets.UTF_8);
          // SignRequest is the real helper from /v3/authentication, compiled
          // on the classpath alongside this file — see that page's example.
          Map<String, String> headers = SignRequest.signRequest("POST", url, body);
          headers.put("Idempotency-Key", UUID.randomUUID().toString());
          HttpURLConnection conn = (HttpURLConnection) URI.create(url).toURL().openConnection();
          conn.setRequestMethod("POST");
          for (Map.Entry<String, String> h : headers.entrySet()) {
              conn.setRequestProperty(h.getKey(), h.getValue());
          }
          conn.setDoOutput(true);
          conn.getOutputStream().write(body);
          conn.getResponseCode();
      }
      public static void main(String[] args) throws Exception {
          // USDC on Ethereum — see the table on
          // /v3/supported-networks#examples-by-network for the
          // withdrawalAccountId per network.
          withdraw("8ff46b97-5742-4196-88dd-db7b0b46ab7a", "1000.00", "USDC");
      }
  }
  ```

  ```cpp C++ theme={null}
  // POST /v3/fx/withdrawals — the request body shape is identical across
  // every chain; only withdrawalAccountId/withdrawalAmount/currency change.
  // The pattern from /v3/supported-networks#examples-by-network. Requires
  // OpenSSL and libcurl. No JSON library was added, so serialization below
  // is intentionally minimal (a flat object serializer), not a general
  // JSON library.
  #include <curl/curl.h>
  #include <openssl/rand.h>
  #include <iomanip>
  #include <map>
  #include <sstream>
  #include <string>
  // Matches the definition in ../../authentication/cpp/sign_request.cpp,
  // compiled together with this file — see that file for the real
  // implementation of signRequest.
  struct SignedRequest {
      std::string authHeader;
      std::string sigHeader;
  };
  SignedRequest signRequest(const std::string& method, const std::string& urlPath,
                             const std::string& query, const std::string& body);
  // Builds the POST /v3/fx/withdrawals body. Kept separate from the network
  // call so it's testable without a real request.
  std::map<std::string, std::string> buildWithdrawalBody(const std::string& withdrawalAccountId,
                                                           const std::string& withdrawalAmount,
                                                           const std::string& currency) {
      return {
          {"withdrawalAccountId", withdrawalAccountId},
          {"withdrawalAmount", withdrawalAmount},
          {"currency", currency},
      };
  }
  // Escapes a string for embedding in a JSON string literal. Sufficient for
  // this page's field values; not a general JSON serializer.
  std::string escapeJson(const std::string& value) {
      std::ostringstream out;
      for (unsigned char c : value) {
          switch (c) {
              case '"': out << "\\\""; break;
              case '\\': out << "\\\\"; break;
              case '\n': out << "\\n"; break;
              case '\r': out << "\\r"; break;
              case '\t': out << "\\t"; break;
              default:
                  if (c < 0x20) out << "\\u" << std::hex << std::setfill('0') << std::setw(4) << (int)c << std::dec;
                  else out << (char)c;
          }
      }
      return out.str();
  }
  // Serializes a flat string-valued map to a JSON object. Sufficient for
  // this page's request body; not a general JSON serializer.
  std::string toJsonObject(const std::map<std::string, std::string>& fields) {
      std::ostringstream json;
      json << "{";
      bool first = true;
      for (const auto& [key, value] : fields) {
          if (!first) json << ",";
          first = false;
          json << "\"" << key << "\":\"" << escapeJson(value) << "\"";
      }
      json << "}";
      return json.str();
  }
  // Generates a random per-call idempotency key. Not RFC 4122 UUID format —
  // idempotency keys only need to be unique client-generated strings — but
  // uses the same RAND_bytes source as the JWT nonce in sign_request.cpp.
  std::string randomIdempotencyKey() {
      unsigned char bytes[16];
      RAND_bytes(bytes, sizeof(bytes));
      std::ostringstream hex;
      for (unsigned char b : bytes) hex << std::hex << std::setfill('0') << std::setw(2) << (int)b;
      return hex.str();
  }
  static size_t discard(char*, size_t s, size_t n, void*) { return s * n; }
  void withdraw(const std::string& withdrawalAccountId, const std::string& withdrawalAmount,
                const std::string& currency) {
      std::string url = "https://api.openfx.com/v3/fx/withdrawals";
      std::string body = toJsonObject(buildWithdrawalBody(withdrawalAccountId, withdrawalAmount, currency));
      SignedRequest req = signRequest("POST", "/v3/fx/withdrawals", "", body);
      CURL* curl = curl_easy_init();
      curl_slist* headers = nullptr;
      headers = curl_slist_append(headers, ("Authorization: " + req.authHeader).c_str());
      headers = curl_slist_append(headers, ("X-Request-Signature: " + req.sigHeader).c_str());
      headers = curl_slist_append(headers, ("Idempotency-Key: " + randomIdempotencyKey()).c_str());
      headers = curl_slist_append(headers, "Content-Type: application/json");
      curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
      curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
      curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, discard);
      curl_easy_perform(curl);
      curl_slist_free_all(headers);
      curl_easy_cleanup(curl);
  }
  #ifndef WITHDRAW_NO_MAIN
  #include <iostream>
  int main() {
      try {
          // USDC on Ethereum — see the table on
          // /v3/supported-networks#examples-by-network for the
          // withdrawalAccountId per network.
          withdraw("8ff46b97-5742-4196-88dd-db7b0b46ab7a", "1000.00", "USDC");
          return 0;
      } catch (const std::exception& e) {
          std::cerr << "Error: " << e.what() << std::endl;
          return 1;
      }
  }
  #endif
  ```

  ```go Go theme={null}
  // POST /v3/fx/withdrawals — the request body shape is identical across
  // every chain; only WithdrawalAccountId/WithdrawalAmount/Currency change.
  // The pattern from /v3/supported-networks#examples-by-network.
  package main
  import (
  	"bytes"
  	"crypto/rand"
  	"encoding/json"
  	"fmt"
  	"net/http"
  	// auth is the real signRequest helper from /v3/authentication — see
  	// go.mod's replace directive if you've copied it somewhere else.
  	auth "openfx-v3-authentication-example"
  )
  type WithdrawalBody struct {
  	WithdrawalAccountID string `json:"withdrawalAccountId"`
  	WithdrawalAmount    string `json:"withdrawalAmount"`
  	Currency            string `json:"currency"`
  }
  // Builds the POST /v3/fx/withdrawals body. Kept separate from the network
  // call so it's testable without a real request.
  func buildWithdrawalBody(withdrawalAccountID, withdrawalAmount, currency string) WithdrawalBody {
  	return WithdrawalBody{WithdrawalAccountID: withdrawalAccountID, WithdrawalAmount: withdrawalAmount, Currency: currency}
  }
  func newIdempotencyKey() (string, error) {
  	b := make([]byte, 16)
  	if _, err := rand.Read(b); err != nil {
  		return "", err
  	}
  	b[6] = (b[6] & 0x0f) | 0x40
  	b[8] = (b[8] & 0x3f) | 0x80
  	return fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:16]), nil
  }
  func withdraw(withdrawalAccountID, withdrawalAmount, currency string) error {
  	url := "https://api.openfx.com/v3/fx/withdrawals"
  	body, err := json.Marshal(buildWithdrawalBody(withdrawalAccountID, withdrawalAmount, currency))
  	if err != nil {
  		return err
  	}
  	signed, err := auth.SignRequest("POST", url, body)
  	if err != nil {
  		return err
  	}
  	key, err := newIdempotencyKey()
  	if err != nil {
  		return err
  	}
  	req, err := http.NewRequest("POST", url, bytes.NewReader(body))
  	if err != nil {
  		return err
  	}
  	for k, v := range signed.Headers {
  		req.Header.Set(k, v)
  	}
  	req.Header.Set("Idempotency-Key", key)
  	res, err := http.DefaultClient.Do(req)
  	if err != nil {
  		return err
  	}
  	defer res.Body.Close()
  	return nil
  }
  func main() {
  	// USDC on Ethereum — see the table on
  	// /v3/supported-networks#examples-by-network for the
  	// withdrawalAccountId per network.
  	if err := withdraw("8ff46b97-5742-4196-88dd-db7b0b46ab7a", "1000.00", "USDC"); err != nil {
  		fmt.Println("Error:", err)
  	}
  }
  ```

  ```bash cURL theme={null}
  #!/usr/bin/env bash
  # POST /v3/fx/withdrawals — the request body shape is identical across
  # every chain; only withdrawalAccountId/withdrawalAmount/currency change.
  # The pattern from /v3/supported-networks#examples-by-network. Illustrative
  # only: cURL can't compute the ES256 request signature itself, so
  # $OPENFX_JWT/$OPENFX_SIGNATURE below are placeholders — get real values
  # from signRequest, in any supported language, via /v3/authentication.
  set -euo pipefail
  # USDC on Ethereum — see the table on
  # /v3/supported-networks#examples-by-network for the withdrawalAccountId
  # per network.
  export OPENFX_JWT="<jwt-from-signRequest>"
  export OPENFX_SIGNATURE="<signature-from-signRequest>"
  curl -X POST https://api.openfx.com/v3/fx/withdrawals \
    -H "Authorization: Bearer $OPENFX_JWT" \
    -H "X-Request-Signature: $OPENFX_SIGNATURE" \
    -H "Idempotency-Key: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d '{
      "withdrawalAccountId": "8ff46b97-5742-4196-88dd-db7b0b46ab7a",
      "withdrawalAmount": "1000.00",
      "currency": "USDC"
    }'
  ```
</CodeGroup>

<Tip>
  The request body shape is **identical across every chain** — only
  `withdrawalAccountId`, `withdrawalAmount`, and `currency` change. The network
  is bound to the verified withdrawal account, so swapping chains is a matter of
  selecting a different `withdrawalAccountId` from `GET
      /v3/fx/withdrawal-accounts`. The examples above use `USDC` on Ethereum; here's
  the same request for every other network on this page:
</Tip>

| Network          | `currency` | `withdrawalAccountId`                  | `withdrawalAmount` |
| ---------------- | ---------- | -------------------------------------- | ------------------ |
| USDC on Ethereum | `USDC`     | `8ff46b97-5742-4196-88dd-db7b0b46ab7a` | `1000.00`          |
| USDC on Solana   | `USDC`     | `a2c7d4f1-3b18-4f6a-9c35-2e8a1b4d9f02` | `1000.00`          |
| USDT on Tron     | `USDT`     | `b9e1d5a7-7c2f-49b8-8d04-4ac6e3f50d83` | `2500.00`          |
| USDT on BSC      | `USDT`     | `c4f8e2b0-5d63-4a17-bf90-1e7c3a92e441` | `500.00`           |
| EURC on Base     | `EURC`     | `d7a3b8c1-9f45-4e26-a812-6b4d5e90f378` | `1500.00`          |

## Common mistakes

* **Sending to the wrong-chain address.** A USDC withdrawal sent to a Polygon address using an Ethereum-network `withdrawalAccountId` will not be recoverable. Always confirm the verified withdrawal account's `rail` matches your intent before withdrawing.
* **Treating `USDC` as one balance.** Your `availableBalance` for `USDC` spans all chains, but withdrawals are pinned to one chain via the `withdrawalAccountId`. There is no "move from Ethereum USDC to Solana USDC" inside OpenFX. That requires a bridge.
* **Looking for a chain on a fiat resource.** A fiat deposit or withdrawal carries `network: null` (its `assetType` is `"FIAT"`) — there is no chain to read. Use [Settlement Times](/v3/settlement-times) for fiat-rail information.

## What's next

<CardGroup cols={2}>
  <Card title="Trade Settlement" icon="wallet" href="/v3/trade-settlement">
    Deposits in, withdrawals out. The unified fiat + stablecoin endpoint.
  </Card>

  <Card title="Settlement times" icon="clock" href="/v3/settlement-times">
    Fiat-rail submission cut-offs by currency.
  </Card>

  <Card title="Withdrawal lifecycle" icon="arrow-up-from-bracket" href="/v3/withdrawal-lifecycle">
    pending → processing → completed state machine.
  </Card>

  <Card title="Deposit lifecycle" icon="arrow-down-to-bracket" href="/v3/deposit-lifecycle">
    How deposits are detected and credited.
  </Card>
</CardGroup>
