Skip to main content

Overview

NEAR Intents (also known as 1-Click) is an intent-based cross-chain bridge that enables seamless asset transfers between StarkNet and 10+ blockchains including Solana, Ethereum, Base, Arbitrum, and NEAR.
Sable’s bridge feature uses NEAR 1-Click to support bi-directional transfers between StarkNet and external chains.

What is NEAR Intents?

NEAR Intents (1-Click) is a solver-based bridging protocol:
  • Intent-driven: Users express intent to swap/bridge, solvers fulfill it
  • No wrapped tokens: Direct native asset transfers
  • Fast settlement: Typically completes in 2-10 minutes
  • Multi-chain support: 15+ blockchains including EVM, Solana, NEAR, Bitcoin

Key Features

  • Bi-directional bridging: Bridge IN (external → StarkNet) or OUT (StarkNet → external)
  • Native assets: No synthetic tokens, receive actual STRK/SOL/ETH/etc.
  • Competitive rates: Solver competition ensures best prices
  • Simple UX: One deposit address, automatic execution

How Sable Integrates with NEAR Intents

Sable’s Bridge page (/bridge) uses 1-Click for all cross-chain transfers.

Bridge Architecture

┌──────────────────────────────────────────────────┐
│                BRIDGE IN FLOW                       │
│         (External Chain → StarkNet)                 │
├──────────────────────────────────────────────────┤
│  1. User selects source chain + token              │
│  2. Sable fetches 1-Click quote                    │
│  3. User gets deposit address                      │
│  4. User sends funds to deposit address            │
│  5. 1-Click solver detects deposit                 │
│  6. Solver swaps + bridges → STRK on StarkNet     │
│  7. STRK arrives at user's StarkNet wallet         │
└──────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────┐
│                BRIDGE OUT FLOW                      │
│         (StarkNet → External Chain)                 │
├──────────────────────────────────────────────────┤
│  1. User selects destination chain + token         │
│  2. Sable fetches 1-Click quote                    │
│  3. User approves STRK to 1-Click deposit address  │
│  4. Sable executes STRK transfer                   │
│  5. 1-Click solver detects deposit                 │
│  6. Solver swaps + bridges → target token         │
│  7. Target token arrives at destination wallet     │
└──────────────────────────────────────────────────┘

Supported Chains

NEAR 1-Click supports bridging to/from 15+ blockchains:

Solana

Native SOL and SPL tokens

Ethereum

ETH and ERC-20 tokens

Base

Coinbase L2

Arbitrum

Arbitrum One and Arbitrum Nova

NEAR

NEAR Protocol

Polygon

Polygon PoS

Avalanche

AVAX C-Chain

Optimism

Optimism L2

Bitcoin

BTC (via wrapped assets)
StarkNet is always one side of the bridge. You bridge to or from StarkNet, not between two external chains.

API Integration

Sable integrates with the 1-Click REST API for quotes, execution, and status tracking.

Base URL

https://1click.chaindefuser.com

Fetch Available Tokens

// From: ~/workspace/source/src/lib/api/oneclick.ts

import { fetchOneClickTokens, getAvailableChains } from "@/lib/api/oneclick";

// Fetch all supported tokens
const tokens = await fetchOneClickTokens();

// Get list of chains (excluding StarkNet)
const chains = getAvailableChains(tokens);

console.log(chains);
// [
//   { id: "sol", name: "Solana", tokenCount: 45 },
//   { id: "eth", name: "Ethereum", tokenCount: 120 },
//   { id: "base", name: "Base", tokenCount: 38 },
//   { id: "arb", name: "Arbitrum", tokenCount: 67 },
//   ...
// ]

// Get tokens for a specific chain
const solTokens = tokens.filter(t => t.blockchain === "solana");
console.log(solTokens[0]);
// {
//   assetId: "nep141:sol.omft.near",
//   decimals: 9,
//   blockchain: "solana",
//   symbol: "SOL",
//   price: 150.23,
//   contractAddress: "So11111111111111111111111111111111111111112",
// }

Get Bridge Quote

import { getOneClickQuote, STRK_ASSET_ID, makeDeadline } from "@/lib/api/oneclick";

// Bridge OUT: 100 STRK → SOL
const quote = await getOneClickQuote({
  dry: false, // false = get real deposit address
  swapType: "EXACT_INPUT",
  slippageTolerance: 100, // 1% (in basis points)
  originAsset: STRK_ASSET_ID, // STRK on StarkNet
  depositType: "ORIGIN_CHAIN",
  destinationAsset: "nep141:sol.omft.near", // SOL
  amount: "100000000000000000000", // 100 STRK (18 decimals)
  refundTo: userStarknetAddress,
  refundType: "ORIGIN_CHAIN",
  recipient: userSolanaAddress, // User's Solana wallet
  recipientType: "DESTINATION_CHAIN",
  deadline: makeDeadline(), // 24 hours from now
});

console.log(quote);
// {
//   quote: {
//     amountIn: "100000000000000000000",
//     amountInFormatted: "100",
//     amountOut: "0.65 SOL",
//     amountOutFormatted: "0.65",
//     timeEstimate: 300, // 5 minutes
//     depositAddress: "0x1234...abcd", // Send STRK here
//   },
//   depositAddress: "0x1234...abcd",
// }

Execute Bridge Transaction

import { Contract } from "starknet";

const STRK_ADDRESS = "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d";

// Approve STRK to 1-Click deposit address
const strkContract = new Contract(ERC20_ABI, STRK_ADDRESS, account);
await strkContract.approve(quote.depositAddress, amount);

// Transfer STRK to deposit address
await strkContract.transfer(quote.depositAddress, amount);

// 1-Click solver will detect the deposit and fulfill the intent

Track Bridge Status

import { getOneClickStatus } from "@/lib/api/oneclick";

// Poll status every 10 seconds
const interval = setInterval(async () => {
  const status = await getOneClickStatus(quote.depositAddress);
  
  console.log(status.status);
  // "PENDING_DEPOSIT" → "KNOWN_DEPOSIT_TX" → "PROCESSING" → "SUCCESS"
  
  if (status.status === "SUCCESS" || status.status === "COMPLETED") {
    clearInterval(interval);
    console.log("Bridge complete!");
    console.log("Destination TX:", status.swapDetails?.destinationChainTxHashes);
  }
}, 10_000);

Status Flow

Bridge transactions progress through multiple states:
┌───────────────────┐
│ PENDING_DEPOSIT    │  Waiting for user to send funds
└─────────┬──────────┘


┌─────────┼──────────┐
│ KNOWN_DEPOSIT_TX   │  Deposit detected on-chain
└─────────┬──────────┘


┌─────────┼──────────┐
│   PROCESSING      │  Solver executing swap + bridge
└─────────┬──────────┘


┌─────────┼──────────┐
│    SUCCESS        │  Funds arrived at destination
└─────────┴──────────┘
Other possible states:
  • FAILED: Transaction failed (user receives refund)
  • REFUNDED: Funds returned to refund address
  • INCOMPLETE_DEPOSIT: User sent wrong amount

STRK Asset ID

All StarkNet bridging operations use the STRK asset ID:
nep141:starknet.omft.near
This is the 1-Click identifier for STRK on StarkNet.

Which Sable Features Use NEAR Intents?

Bridge Page

Bi-Directional Cross-Chain Bridge
  • Bridge IN: External chain → STRK on StarkNet
  • Bridge OUT: STRK on StarkNet → External chain
  • Supported chains: Solana, Ethereum, Base, Arbitrum, NEAR, Polygon, Avalanche, Optimism, etc.
  • UI: /bridge
  • Status tracking: Real-time progress updates

Frontend Integration

Sable’s bridge page provides a user-friendly interface for cross-chain transfers.
// From: ~/workspace/source/src/hooks/use-bridge.ts

import { getOneClickQuote, getOneClickStatus } from "@/lib/api/oneclick";

async function executeBridge(
  direction: "in" | "out",
  sourceChain: string,
  sourceToken: string,
  destinationChain: string,
  destinationToken: string,
  amount: string,
  userAddress: string,
) {
  // 1. Get quote
  const quote = await getOneClickQuote({
    dry: false,
    swapType: "EXACT_INPUT",
    slippageTolerance: 100,
    originAsset: sourceToken,
    depositType: "ORIGIN_CHAIN",
    destinationAsset: destinationToken,
    amount,
    refundTo: userAddress,
    refundType: "ORIGIN_CHAIN",
    recipient: userAddress,
    recipientType: "DESTINATION_CHAIN",
    deadline: makeDeadline(),
  });
  
  // 2. Display deposit address to user
  console.log(`Send ${quote.quote.amountInFormatted} to ${quote.depositAddress}`);
  
  // 3. If bridge OUT, execute STRK transfer
  if (direction === "out") {
    await strkContract.transfer(quote.depositAddress, amount);
  }
  
  // 4. Poll status
  const interval = setInterval(async () => {
    const status = await getOneClickStatus(quote.depositAddress);
    updateUI(status);
    
    if (status.status === "SUCCESS") {
      clearInterval(interval);
      showSuccessMessage();
    }
  }, 10_000);
}

Error Handling

1-Click API returns user-friendly error messages:
try {
  const quote = await getOneClickQuote(params);
} catch (error) {
  if (error.message.includes("try at least")) {
    // Amount too low for bridge
    alert("Amount too low. Minimum ~10 STRK required.");
  } else if (error.message.includes("timeout")) {
    // Solvers couldn't fulfill in time
    alert("Bridge quote timed out. Please try again.");
  } else if (error.message.includes("not valid")) {
    // Invalid address format
    alert("Invalid destination address for selected chain.");
  }
}

External Resources

NEAR 1-Click Documentation

Official 1-Click protocol documentation

NEAR Intents

Learn more about NEAR’s intent-based architecture

1-Click API Reference

REST API for quotes and status tracking

Integration Source Code:
  • API integration: ~/workspace/source/src/lib/api/oneclick.ts
  • Bridge UI: ~/workspace/source/src/app/bridge/page.tsx
  • Bridge hooks: ~/workspace/source/src/hooks/use-bridge.ts

Build docs developers (and LLMs) love