Send USDC to another network

Move USDC from a Gravv wallet on one network to an outside address or another Gravv wallet on a different network.

Use this recipe to move USDC from a Gravv wallet on one network to an outside address, or to another Gravv wallet, on a different network.

Who it is for. A payroll platform that holds USDC on Polygon and pays a contractor who wants USDC on Solana.

Status. Not yet available. Ask support to confirm it is switched on for your business. Sending USDT across networks is not supported.

Money example

  1. The Polygon wallet holds 2,000.00 USDC.
  2. You send 1,000.00 USDC from it to the contractor's Solana address.
  3. The USDC moves between networks without a swap, so no price applies. Read any fee in the fee field of the transfer response.

Before you start

  • The source wallet is a Gravv wallet (internal_crypto_wallet) on polygon, ethereum, solana or stellar, and it holds USDC (GET /v1/wallets/{wallet_id}).
  • The receiving side is on polygon, ethereum, solana, stellar or avalanche, and receives USDC.
  • A Stellar receiver must already accept USDC. This is a one-time "trustline" setting in the receiving wallet.

Find out what is required

Gravv supports only the networks listed above. The address must match its network:

  • EVM networks (polygon, ethereum, avalanche) take 0x and 40 hex characters.
  • Solana takes a base58 address.
  • Stellar takes a G… account. Gravv refuses a muxed M… address.

If the format is wrong, the transfer call refuses it and names the network.

Steps

1. Send

curl --request POST \
 --url https://api.gravv.xyz/v1/transfer \
 --header 'Api-Key: <API_KEY>' \
 --header 'Content-Type: application/json' \
 --header 'Idempotency-Key: f207b686-1dd1-4bf2-8b4e-6442681aa37f' \
 --data '
{
"customer_id": "c7ae0522-6a4d-476f-a43c-5a9aac863c31",
"source": {
"source_type": "internal_crypto_wallet",
"id": "b6705e3a-3c6c-49b0-b24f-d55aafd03635",
"crypto_asset": "USDC"
},
"destination": {
"destination_type": "crypto_wallet",
"wallet_address": "76YbQbwcPcp8MTy6pP8efkicPhTUfYYxeEPuCQmo46K1",
"blockchain_network": "solana"
},
"amount": "1000.00",
"description": "October contract",
"client_reference": "pay-ctr-1009"
}
'
const response = await fetch("https://api.gravv.xyz/v1/transfer", {
method: "POST",
headers: {
"Api-Key": process.env.GRAVV_API_KEY,
"Content-Type": "application/json",
"Idempotency-Key": "f207b686-1dd1-4bf2-8b4e-6442681aa37f",
},
body: JSON.stringify({
"customer_id": "c7ae0522-6a4d-476f-a43c-5a9aac863c31",
"source": {
  "source_type": "internal_crypto_wallet",
  "id": "b6705e3a-3c6c-49b0-b24f-d55aafd03635",
  "crypto_asset": "USDC"
},
"destination": {
  "destination_type": "crypto_wallet",
  "wallet_address": "76YbQbwcPcp8MTy6pP8efkicPhTUfYYxeEPuCQmo46K1",
  "blockchain_network": "solana"
},
"amount": "1000.00",
"description": "October contract",
"client_reference": "pay-ctr-1009"
}),
});
console.log(response.status, await response.json());
import os

import requests

response = requests.post(
"https://api.gravv.xyz/v1/transfer",
headers={
    "Api-Key": os.environ["GRAVV_API_KEY"],
    "Content-Type": "application/json",
    "Idempotency-Key": "f207b686-1dd1-4bf2-8b4e-6442681aa37f",
},
json={
    "customer_id": "c7ae0522-6a4d-476f-a43c-5a9aac863c31",
    "source": {
        "source_type": "internal_crypto_wallet",
        "id": "b6705e3a-3c6c-49b0-b24f-d55aafd03635",
        "crypto_asset": "USDC",
    },
    "destination": {
        "destination_type": "crypto_wallet",
        "wallet_address": "76YbQbwcPcp8MTy6pP8efkicPhTUfYYxeEPuCQmo46K1",
        "blockchain_network": "solana",
    },
    "amount": "1000.00",
    "description": "October contract",
    "client_reference": "pay-ctr-1009",
},
)
print(response.status_code, response.json())
{
  "data": {
    "amount": "1000",
    "amount_currency": "USD",
    "date_created": "2026-10-13T15:10:00.000Z",
    "fee": "0",
    "fee_currency": "USD",
    "local_amount": null,
    "local_currency": null,
    "reference": "657b085b-096a-4779-9cbb-6d7f0689cc65",
    "requires_approval": false,
    "transaction_id": "ea2bfda0-e733-4245-befe-2ca64e43cfcc",
    "transfer_status": "pending"
  },
  "error": null
}

To send to another Gravv wallet instead, which can belong to any of your customers, send "destination": {"destination_type": "internal_crypto_wallet", "id": "<wallet id>"}. For an outside Stellar address, you can add blockchain_memo.

2. Wait for it

The move has several on-chain steps, so several transfer.status.pending events arrive first. Wait for transfer.status.completed (with tx_hash), or poll GET /v1/transactions/ea2bfda0-e733-4245-befe-2ca64e43cfcc. A cross-network transfer can stay pending for a long time. If it does, contact support with the reference instead of sending again.

What can go wrong

Response Why Fix
400 transfers between different blockchain networks support USDC only, on avalanche, ethereum, polygon, solana and stellar A network is outside the list (such as base or tron), or one side uses USDT Use USDC on a listed network. Convert USDT first
400 destination.wallet_address is not a valid solana address (or ethereum, stellar, …) The address does not match the network, or it is a muxed Stellar M… address Get the right address for that network
400 transfers from avalanche accounts are not currently supported Avalanche can receive but not send Send from another network
400 the source account (…) and destination (…) must be on the same blockchain network The source is an account rather than a wallet, so it cannot move across networks Use an internal_crypto_wallet source
400 insufficient balance The amount plus the fee is more than the balance Fund the wallet
The receiver got the amount rounded to 2 decimal places You sent more than 2 decimal places Send at most 2 decimal places
transfer.status.failed, but the transaction is still pending For example, the Stellar receiver has no USDC trustline. The last step cannot complete, so a person handles it Do not send it again. Contact support with the reference, and ask the receiver to enable USDC

Webhooks you will receive

  1. transfer.status.pending, several times, with workflow_type cross_chain_self_transfer.
  2. transfer.status.completed with tx_hash, or transfer.status.failed.

Testing in sandbox

In sandbox, the request checks for USDT, an unlisted network and a malformed address behave as described. The move itself does not complete in sandbox:

  • A send to an outside Solana address goes to manual review within seconds. Gravv sends transfer.status.failed with transfer_status manual_processing_required, and the transaction stays pending.
  • A send to the customer's own Stellar wallet stays pending.

Do not use sandbox to fund a wallet on another network.

Reference

Was this page helpful?