-
Verifier:
intents.nearon NEAR mainnet (there is no public testnet deployment). Internal ledger token ids look likenep141:wrap.nearornep141:eth-0xdac…ec7.omft.nearfor bridged assets. - 30+ chains via three bridge routes (PoA, Omni, HOT) — Bitcoin, Ethereum and its L2s, Solana, TON, Tron, XRP, Stellar, and more — all settling through the same NEAR contract.
-
Signing standards: NEP-413 for NEAR wallets and raw keys, plus
erc191(EVM),raw_ed25519(Solana),tip191,webauthn,ton_connect, andsep53— accounts from other ecosystems trade without holding NEAR. -
Two integration surfaces: the hosted 1Click API
(
1click.chaindefuser.com) that wraps quoting, bridging, and settlement behind a deposit address, and the solver relay for direct flows.
Swap anything, anywhere, with NEAR Intents.
NEAR Intents inverts how trading works: instead of scripting an execution path ("bridge X,
then swap on Y"), you sign a declarative statement of the outcome you want, and a competitive
network of off-chain solvers races to fulfill it. Settlement is anchored in
one contract on NEAR — the verifier at intents.near — which holds every
participating asset as a NEP-245 multi-token balance and applies each matched trade
atomically: the whole bundle settles, or nothing does. @fastnear/intents makes
the protocol usable from the browser (IIFE global nearIntents), Node.js, and AI
agents.
Quote, fund, sign, settle — most of it is one REST API.
Each step is a REST call, an on-chain transaction, or an off-chain signature — never a bespoke bridge script.
GET /v0/tokens lists every swappable asset. POST /v0/quote with
dry: true previews real pricing for free; dry: false commits and
returns a unique depositAddress.
Send the input tokens to the deposit address on the origin chain — a plain transfer.
Funds already inside intents.near skip this: sign a server-generated intent
instead (generate-intent → submit-intent).
Intents are authorized by a NEP-413 signed message — no gas, no transaction. Solvers
co-sign the mirror token_diff; the verifier requires all diffs to net to
zero per token.
The relay submits one execute_intents transaction; output is delivered to
your recipient (withdrawing cross-chain via the bridges). Poll
GET /v0/status to SUCCESS, REFUNDED, or
FAILED — failures auto-refund.
NEP-413 under the hood — and the one encoding gotcha.
The signed digest is sha256( borsh(u32 231+413) ‖ borsh({ message, nonce[32],
recipient, callbackUrl? }) ), signed by a full-access key
(ed25519 for nearly all NEAR accounts; secp256k1 also works).
For intents, message is the JSON intent body
({ signer_id, deadline, intents: [...] }), recipient is
intents.near, and the 32-byte nonce is the verifier's replay
nonce.
- Full-access keys only. NEP-413 forbids FunctionCall access keys — the session keys this demo site uses for silent signing cannot authorize intents. Wallets sign with the account's own key.
-
Re-encode the signature. Wallets return NEP-413 signatures as base64;
the verifier wants a curve-prefixed base58 string —
ed25519:<base58>, orsecp256k1:<base58>for 65-byte signatures. The signers in@fastnear/intentsown that conversion — skipping it is the most common integration bug. -
Zero-sum diffs. A
token_difflists per-token deltas (negative = you pay). Your intent alone is unbalanced; the verifier executes it together with a solver's mirror diff so every token nets to zero.
What actually gets submitted — to 1Click, the solver relay, or
intents.near execute_intents directly.
{
"standard": "nep413",
"payload": {
"message": "{\"signer_id\":\"alice.near\",\"deadline\":\"2026-08-01T00:00:00.000Z\",\"intents\":[{\"intent\":\"token_diff\",\"diff\":{\"nep141:usdc.near\":\"-1000000\",\"nep141:usdt.near\":\"1000000\"}}]}",
"nonce": "Vij2xgAlKBKzgNPJFViEQRgYyS7p2NEiYTTY4XmT8go=",
"recipient": "intents.near"
},
"public_key": "ed25519:Bn7CwuFHG8XfbcW6RFucMjZsKjnff14oscA57prVuryX",
"signature": "ed25519:3JT69jSwZL3sv8skfnpy9PnEn5feBHppfgfRakoQs3A5vqJ6gXmW7VJuUnv2n8wounesFi7UwAkaQy1DFEuBWpVm"
}
Quote in the browser console; swap from a wallet or an agent.
The full set — quote, wallet signing, Node agent swap, deposits and balances — lives in the
intents key of
recipes.json.
Dry quotes are free, keyless, and commitment-free.
IIFE global from intents.js. Works unauthenticated — quotes then carry a
0.2% platform fee; a key from partners.near-intents.org waives it.
<script src="https://js.fastnear.com/intents.js"></script>
<script type="module">
const oneClick = nearIntents.createOneClickClient();
const tokens = await oneClick.tokens();
const quote = await oneClick.quote({
dry: true, // free preview; dry:false commits and returns a depositAddress
swapType: "EXACT_INPUT",
slippageTolerance: 100, // bps
originAsset: "nep141:wrap.near",
destinationAsset:
"nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1", // USDC on NEAR
amount: "1000000000000000000000000", // 1 wNEAR
depositType: "ORIGIN_CHAIN",
refundTo: "you.near",
refundType: "ORIGIN_CHAIN",
recipient: "you.near",
recipientType: "DESTINATION_CHAIN",
deadline: new Date(Date.now() + 10 * 60_000).toISOString(),
});
console.log(quote.quote.amountOutFormatted, "USDC for 1 wNEAR");
</script>
The INTENTS deposit type: 1Click builds the payload, the local signer signs it verbatim — no deposit transaction needed. The key stays server-side.
import { createOneClickClient } from "@fastnear/intents";
import { createLocalIntentSigner } from "@fastnear/intents/node";
const { NEAR_ACCOUNT_ID, NEAR_PRIVATE_KEY } = process.env;
const oneClick = createOneClickClient();
const signer = createLocalIntentSigner({
accountId: NEAR_ACCOUNT_ID,
privateKey: NEAR_PRIVATE_KEY, // full-access, server-side only
});
// Quote with depositType/refundType/recipientType "INTENTS" — the input
// funds already sit inside intents.near, so no deposit transaction is needed.
const quote = await oneClick.quote({
dry: false,
swapType: "EXACT_INPUT",
slippageTolerance: 100,
originAsset: "nep141:wrap.near",
destinationAsset:
"nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
amount: "1000000000000000000000000",
depositType: "INTENTS",
refundTo: NEAR_ACCOUNT_ID,
refundType: "INTENTS",
recipient: NEAR_ACCOUNT_ID,
recipientType: "INTENTS",
deadline: new Date(Date.now() + 10 * 60_000).toISOString(),
});
const { intent } = await oneClick.generateIntent({
signerId: NEAR_ACCOUNT_ID,
depositAddress: quote.quote.depositAddress,
});
// signPayload pins the recipient to intents.near and signs verbatim.
const signed = await signer.signPayload(intent);
const { intentHash } = await oneClick.submitIntent({ signedData: signed });
console.log(intentHash);
Browser signing uses createWalletIntentSigner({ wallet: nearWallet }) over
nearWallet.signMessage (NEP-413), which every wallet in the connector's
manifest implements. The solver relay
(@fastnear/intents/relay) is wired and typed, but its quotes require a
partner API key in practice — the 1Click path is the default. The funded end-to-end
swap path is verified against the mainnet smoke runbook
(packages/intents/MAINNET_QA.md) before being documented further.
Hard constraints and safe defaults.
These rules come from the machine-readable catalog — agents and humans should apply the same ones.
- The verifier is
intents.nearon NEAR mainnet; there is no public testnet deployment of the intents stack. - NEP-413 intent signatures require a full-access key, and the verifier checks the key is authorized for
signer_id. - Submitted signatures use
ed25519:<base58>encoding — not the base64 NEAR wallets return; the signers own that conversion. - Native NEAR is not a verifier asset: wrap to wNEAR before depositing, and exit native NEAR only via the
native_withdrawintent. - Amounts are base-unit strings, and
token_diffdiffs must net to zero per token across the executed batch.
- Quote with
dry: truefirst; commit withdry: falseonly when ready to fund the deposit address before it expires. - Keep local signer private keys in server-side secret storage, never browser code.
- Use a partner API key from partners.near-intents.org to remove the 0.2% keyless platform fee.
- Poll
/v0/statusto a terminal state (SUCCESS,REFUNDED,FAILED) and surfaceswapDetailson non-success. - Omit
msgonft_withdrawso failed withdrawals stay refundable.
AI agents: read the intents key in
recipes.json
and the NEAR Intents section of
llms.txt
before editing an app. Humans: the package guide is the
@fastnear/intents README,
the protocol docs live at
docs.near-intents.org,
and the verifier source is
near/intents.