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
- The Polygon wallet holds 2,000.00 USDC.
- You send 1,000.00 USDC from it to the contractor's Solana address.
- The USDC moves between networks without a swap, so no price applies. Read any fee in the
feefield 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
0xand 40 hex characters. - Solana takes a base58 address.
- Stellar takes a
G…account. Gravv refuses a muxedM…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
transfer.status.pending, several times, withworkflow_typecross_chain_self_transfer.transfer.status.completedwithtx_hash, ortransfer.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.failedwithtransfer_statusmanual_processing_required, and the transaction stayspending. - A send to the customer's own Stellar wallet stays
pending.
Do not use sandbox to fund a wallet on another network.