NEAR Intents
Move USDC between chains for NEAR Intents with Circle's CCTP. Ask Xebra first, keep your usual path when it says no.
This guide is for teams building on NEAR Intents: the 1Click API, solvers and wallets. It shows how to hand the USDC part of a cross-chain swap to Xebra, and how to fall back to your existing path when Xebra does not cover a route.
Why
NEAR Intents settles trades between balances in its Verifier contract and moves money on and off NEAR through bridges (Omni, POA, HOT). It does not move USDC from one chain to another: when a user's USDC is on one chain and the liquidity is on another, a solver has to carry it across, or there is no quote at all.
Two things follow from that, both visible from the public API:
- Some chains get no quotes. On 12 November 2026, 1Click answered
Quoting for this pair is not availablefor Stellar USDC into USDC on Base, NEAR and Solana, at every amount we tried. Stellar USDC enters NEAR Intents through the HOT bridge, and no solver quotes it. - Some chains are not listed at all. 1Click has no Circle USDC on Arc, Sonic, Ink, XDC, Sei, Unichain, World Chain or Codex. With Stellar, Circle counts about 2.05 billion USDC on those nine chains (the numbers).
- USDC is split by bridge. The same USDC on Base is a different asset in NEAR Intents depending on which bridge brought it in, and solvers convert between them by withdrawing and depositing again (Move HOT liquidity to Omni tokens).
Circle's CCTP moves USDC between chains one for one: it burns on the source chain and Circle mints native USDC on the destination. There is no pool, so there is no slippage and nothing to rebalance. Xebra runs CCTP between Stellar, Sui, Solana and the EVM chains, and is adding every chain CCTP supports.
How it fits
User's USDC on Stellar
-> Xebra: burn on Stellar, Circle mints on Base (one signature, on Stellar)
-> straight into the 1Click deposit address on Base
-> NEAR Intents swaps it and delivers anything, anywhere (unchanged)NEAR Intents changes nothing on its side. The deposit address is the one 1Click already issues; Xebra is only how the USDC gets there. If NEAR Intents cannot fill the swap, its refund goes where the quote says.
Install
npm install @xebra/sdk@xebra/sdk/intents holds everything on this page. It runs in Node and browsers.
Ask before you route
isRouteAvailable(from, to) answers from Xebra's live route list, cached for 30 seconds. Chains can
be named the way NEAR Intents names them (sol, arb, base, stellar) or the way Xebra does. When
Xebra switches on a new route, the answer changes with no code change on your side.
import { } from "@xebra/sdk";
import { } from "@xebra/sdk/intents";
const = ({ : () });
// Your existing path, unchanged.
declare function (: string, : string): <void>;
declare function (: string, : string): <void>;
async function (: string, : string) {
// Unknown chains answer false; an unreachable Xebra rejects. Both mean: keep your usual path.
const = await .(, ).(() => false);
return ? (, ) : (, );
}pickHub(from) returns the first of Base, Arbitrum and Solana that Xebra reaches from from right
now, or null. Use it when the user's own chain has no quotes and you need a chain that does.
Recognise USDC
Only Circle's own USDC can move over CCTP. usdcChainOf(token) takes an entry from 1Click's
/v0/tokens and returns the chain on which it is Circle's USDC, or null. It compares the contract
address, never the symbol, so a token that only calls itself USDC is refused.
import { type IntentsToken, } from "@xebra/sdk/intents";
const = (await ("https://1click.chaindefuser.com/v0/tokens").(() =>
.(),
)) as IntentsToken[];
const = .(() => () === "stellar");
const = .(() => () === "base");Build the deposit
Get a real (non-dry) 1Click quote whose originAsset is USDC on the hub chain, then have Xebra send
the user's USDC straight to its depositAddress. buildDeposit refuses a route Xebra does not offer
before building anything, so you can fall back before showing the user a signature.
import { } from "@xebra/sdk";
import { , } from "@xebra/sdk/intents";
declare const : string; // the user's Stellar account, G...
declare const : string; // where NEAR Intents delivers
declare const : string; // a 1Click asset id
declare const : string; // from usdcChainOf, above
const = ();
const = ({ : });
// 1. What Xebra delivers on Base for 25 USDC from Stellar, after its fee and Circle's.
const = await .({ : "stellar", : "base", : "25" });
// 2. A real 1Click quote for that amount, from USDC on Base. It issues the deposit address.
const = await ("https://1click.chaindefuser.com/v0/quote", {
: "POST",
: { "content-type": "application/json" },
: .({
: false,
: "FLEX_INPUT",
: 100,
: ,
: "ORIGIN_CHAIN",
,
: (.((.) * 1e6)),
,
: "DESTINATION_CHAIN",
// The user has no wallet on Base. Their Stellar key already owns a NEAR Intents account.
: (),
: "INTENTS",
: new (.() + 3_600_000).(),
}),
});
const { } = (await .()) as { : { : string } };
// 3. The Stellar transaction that sends the USDC there. The user's wallet signs it.
const { } = await .({
: "stellar",
: "base",
: .,
,
: "25",
: .., // refuse if Xebra's or Circle's fee rose since the quote
});steps is one Stellar transaction; see Wallet integration for
signing it. Then follow the two legs: Xebra's with xebra.getTransfer("stellar", hash) and NEAR
Intents' with GET /v0/status?depositAddress=....
FLEX_INPUT lets the deposit land at whatever Xebra delivers. It is exact in practice, since Circle
charges the delivery fee the quote shows.
A Stellar wallet already has a NEAR Intents account
NEAR Intents verifies signatures made with Stellar's signed-message standard (SEP-53) and names the
signer's account after its ed25519 public key in hex. intentsAccountFor("G...") returns that
account. Refunds sent there belong to the user, who withdraws them by signing with the same Stellar
wallet. Nothing has to be created first.
Start from Arc or another EVM chain
NEAR Intents does not list Circle's USDC on Arc, Unichain, Sonic, World Chain, Sei, XDC, Ink or Codex (1Click token list, 13 November 2026). Xebra reaches Base from those chains over CCTP, so the same three steps work with an EVM sender. Only two things change:
fromnames the EVM chain ("arc","unichain", ...), andstepsholds up to two EVM transactions on it: an approval for exactly the amount, when the allowance is short, then the burn. Send them in order and wait for the approval's receipt before the burn.- The refund goes back to the user's own address on the hub chain: an EVM account is the same
address on every EVM chain, so set
refundTo: senderandrefundType: "ORIGIN_CHAIN". A smart contract wallet may not exist at that address on the hub; refund those users toINTENTSinstead.
import { } from "@xebra/sdk";
import { } from "@xebra/sdk/intents";
declare const : `0x${string}`; // the user's address on Arc
declare const : string; // from a non-dry 1Click quote, refundTo: sender
const = ();
const = ({ : });
const = await .("arc"); // "base" today
if () {
const = await .({ : "arc", : "base", : "25" });
const { } = await .({
: "arc",
: ,
,
,
: "25",
: ..,
});
// steps: [approve?, burn], each { tx: { kind: "evm", chainId, to, data, value: "0" } }
}If the user's chain is one NEAR Intents already lists (Base, Arbitrum, Solana...), price both paths and let the better one win: depositing straight into Intents usually pays more there, and Xebra adds nothing. xebra.exchange/near-intents does exactly this.
Proven on mainnet
The first swap through this path ran on 12 November 2026: USDC on Stellar into USDC on NEAR, delivered
to that-guy.near. Every step is on chain.
| Step | When (UTC) | Transaction |
|---|---|---|
| Burned on Stellar: Xebra's contract took its fee and Circle burned the rest | 23:24:27 | 95efc874...842b |
Minted on Base by Circle, into the 1Click deposit address 0x4137...c9e5 | 23:24:38 | 0x8d16acc8...940a |
Delivered on NEAR by a NEAR Intents solver (1Click status SUCCESS) | 23:25:03 | Hv1xrVs5...VAg7 |
36 seconds from the burn on Stellar to USDC in the NEAR wallet: 11 for Circle to mint on Base, 25 for NEAR Intents to take the deposit and deliver. It also settled the one thing the code could not: NEAR Intents credits a deposit that arrives as a Circle mint, not only as an ordinary transfer.
Costs and timing
Prices for 25 USDC from Stellar, from live quotes on 12 November 2026 with Xebra's fee as cut on 13 November:
| Leg | Cost | Time |
|---|---|---|
| Xebra, Stellar to Base | 0.10 USDC: Xebra's 0.05% (at least 0.10), Circle's delivery fee paid out of it | 11 s on the first run |
| NEAR Intents, USDC on Base to USDC on NEAR | about 0.003 USDC | 25 s on the first run (quoted about 35 s) |
| Total | about 24.897 USDC arrives on NEAR | 36 s on the first run |
| Straight into NEAR Intents from Stellar | No quote |
Xebra's fee and limits come from its contracts and can change through a 48-hour timelock; read them
live with xebra.quote, never from this table.
Trust model
- Xebra never holds funds. The user signs one Stellar transaction that pays Xebra's fee and burns the rest with Circle in the same transaction; either all of it happens or none of it does. See Security.
- Circle mints the USDC on the destination chain. Xebra adds no wrapped token and no pool.
- The deposit address is NEAR Intents'. From the moment the USDC lands there, NEAR Intents' usual rules apply, including its refunds.
Try it
xebra.exchange/near-intents does all of the above with a
Stellar wallet: it prices both paths side by side, asks for one signature, and follows both legs.
Its source is in apps/web/lib/near-intents.