Universal cross-chain swap & bridge skill for OpenClaw using the NEAR Intents 1Click SDK. Supports 14+ blockchains including NEAR, Base, Ethereum, Solana, an...
---
name: near-intents
description: Universal cross-chain swap & bridge skill for OpenClaw using the NEAR Intents 1Click SDK. Supports 14+ blockchains including NEAR, Base, Ethereum, Solana, and Bitcoin.
---
# NEAR Intents Skill — 1Click SDK
## 📋 TL;DR
**What it does**: Universal cross-chain swap & bridge tool powered by the [1Click API](https://docs.near-intents.org/near-intents/integration/distribution-channels/1click-api) and the [`@defuse-protocol/one-click-sdk-typescript`](https://github.com/defuse-protocol/one-click-sdk-typescript) SDK.
**Supported chains**: NEAR, Base, Ethereum, Arbitrum, Solana, BSC, Gnosis, Starknet, Bitcoin, Dogecoin, Zcash, Litecoin.
**How it works**:
1. Get a quote from the 1Click API → receive a deposit address
2. Send tokens to the deposit address on the origin chain
3. 1Click handles the swap/bridge automatically
4. Tokens arrive at the recipient address on the destination chain
**Key facts**:
- Minimum ~$0.10 USD per swap
- NEAR account only required when the *origin* asset is on NEAR
- No NEAR account needed for cross-chain swaps from other chains (e.g., Arb USDC → Sol USDC)
- JWT authentication optional but avoids 0.2% fee → register at [partners.near-intents.org](https://partners.near-intents.org/)
---
## Core Concept
All swaps go through the **1Click API** (`https://1click.chaindefuser.com`):
```
User Wallet ──► [Deposit to 1Click address] ──► Market Makers ──► Recipient on Destination Chain
```
There is **no need to interact with `intents.near` directly** — the 1Click API abstracts everything.
---
## `executeIntent()` API
The single entry point exported by `index.ts`:
```typescript
import { executeIntent } from './index';
const result = await executeIntent({
assetIn: 'NEAR', // Origin token (see Asset Naming below)
assetOut: 'base:USDC', // Destination token
amount: '1.0', // Human-readable amount
recipient: '0x...', // Destination address (optional if same-chain NEAR)
mode: 'auto', // 'auto' (default) or 'manual'
swapType: 'EXACT_INPUT', // 'EXACT_INPUT' (default) or 'EXACT_OUTPUT'
});
```
### Parameters
| Parameter | Type | Required | Description |
|-------------|--------|----------|-------------|
| `assetIn` | string | ✅ | Origin asset symbol (e.g., `'NEAR'`, `'base:USDC'`, `'arb:ARB'`) |
| `assetOut` | string | ✅ | Destination asset symbol |
| `amount` | string | ✅ | Human-readable amount (e.g., `'1.0'`, `'0.5'`) |
| `recipient` | string | ❌ | Destination address. Required for cross-chain. Defaults to NEAR account |
| `refundAddress` | string | ⚠️ | **REQUIRED for non-NEAR origins**. Address on origin chain for refunds if swap fails. **CRITICAL for fund safety!** |
| `mode` | string | ❌ | `'auto'` sends deposit automatically from NEAR account. `'manual'` returns quote + deposit address for user to send manually |
| `swapType` | string | ❌ | `'EXACT_INPUT'` (amount = input), `'EXACT_OUTPUT'` (amount = desired output) |
### ⚠️ CRITICAL: Refund Address Safety
**When `assetIn` is on a non-NEAR chain** (e.g., `'base:USDC'`, `'arb:ARB'`, `'btc:BTC'`):
1. **`refundAddress` is REQUIRED** — the function will throw an error if missing
2. **ALWAYS ask the user** for their wallet address on the origin chain
3. **NEVER assume or guess** — using the wrong address = permanent fund loss
4. **Explain to the user**: "If the swap fails, your tokens will be refunded to this address"
**Example**:
```typescript
// ❌ WRONG - Missing refundAddress for non-NEAR origin
await executeIntent({
assetIn: 'base:USDC', // Base origin
assetOut: 'NEAR',
amount: '0.5',
recipient: 'user.near',
mode: 'manual'
});
// → Error: Cross-chain swap from base:USDC requires refundAddress parameter
// ✅ CORRECT - User's Base wallet address provided
await executeIntent({
assetIn: 'base:USDC',
assetOut: 'NEAR',
amount: '0.5',
recipient: 'user.near',
refundAddress: '0x123...', // User's Base address
mode: 'manual'
});
```
**Why this matters**:
- Swaps can fail due to market conditions, liquidity issues, or timing
- Failed swaps trigger automatic refunds to `refundAddress` on the **origin chain**
- If `refundAddress` is wrong or belongs to someone else, **funds are permanently lost**
### Returns
- **Auto mode**: `"Swap Successful! 1.0 NEAR → 0.97 USDC\nTransaction: https://nearblocks.io/txns/...\nExplorer: https://explorer.near-intents.org/transactions/..."`
- **Manual mode**: Formatted instructions with deposit address, amounts, tracking URL, and deadline
---
## Asset Naming
Use `chain:SYMBOL` format. Omit chain prefix for NEAR-native tokens.
| Chain | Prefix | Examples |
|-----------|------------|-----------------------------------|
| NEAR | *(none)* | `NEAR`, `USDC`, `USDT`, `wNEAR` |
| Base | `base:` | `base:USDC` |
| Ethereum | `eth:` | `eth:ETH`, `eth:USDC` |
| Arbitrum | `arb:` | `arb:USDC`, `arb:ARB` |
| Solana | `sol:` | `sol:SOL`, `sol:USDC` |
| BSC | `bsc:` | `bsc:USDC` |
| Bitcoin | `btc:` | `btc:BTC` *(native only)* |
| Dogecoin | `doge:` | `doge:DOGE` *(native only)* |
| Zcash | `zec:` | `zec:ZEC` *(native only)* |
| Litecoin | `ltc:` | `ltc:LTC` *(native only)* |
- **Case-insensitive**: `near`, `NEAR`, `Near` all work
- **UTXO chains** (BTC, DOGE, ZEC, LTC): **native tokens only** — no wrapped/ERC-20 equivalents
---
## Modes
### Auto Mode (default)
Automatically sends the deposit from the configured NEAR account.
**Use when**: Origin asset is on NEAR and agent has NEAR credentials in `.env`.
```typescript
await executeIntent({
assetIn: 'NEAR',
assetOut: 'base:USDC',
amount: '1.0',
recipient: '0xYourBaseAddress',
});
```
### Manual Mode
Returns a quote with deposit address — the user (or agent) sends tokens separately.
**Use when**: Origin is on a non-NEAR chain, or you want to show the user a quote first.
```typescript
const quote = await executeIntent({
assetIn: 'arb:USDC',
assetOut: 'sol:USDC',
amount: '5.0',
recipient: 'YourSolanaAddress',
mode: 'manual',
});
// Returns deposit address + instructions
```
### EXACT_OUTPUT
Specify the desired output amount; the 1Click API tells you how much to send.
```typescript
const quote = await executeIntent({
assetIn: 'USDT',
assetOut: 'base:USDC',
amount: '10.0', // Want exactly 10 USDC out
recipient: '0x...',
mode: 'manual',
swapType: 'EXACT_OUTPUT',
});
```
---
## Examples
### 1. Swap NEAR → USDC on Base (Auto)
```typescript
await executeIntent({
assetIn: 'NEAR',
assetOut: 'base:USDC',
amount: '1.0',
recipient: '0xYourBaseAddress',
});
```
### 2. Bridge USDC from Arbitrum → Solana (Manual)
```typescript
const quote = await executeIntent({
assetIn: 'arb:USDC',
assetOut: 'sol:USDC',
amount: '5.0',
recipient: 'YourSolanaAddress',
refundAddress: '0xYourArbitrumAddress', // REQUIRED for refunds
mode: 'manual',
});
// User sends 5 USDC to the deposit address on Arbitrum
```
### 3. Swap NEAR → USDC (same chain)
```typescript
await executeIntent({
assetIn: 'NEAR',
assetOut: 'USDC',
amount: '2.0',
});
```
### 4. Get a quote: How much NEAR for 10 USDC on Arbitrum?
```typescript
const quote = await executeIntent({
assetIn: 'NEAR',
assetOut: 'arb:USDC',
amount: '10.0',
recipient: '0xYourArbAddress',
mode: 'manual',
swapType: 'EXACT_OUTPUT',
});
```
### 5. Send BTC to NEAR address
```typescript
const quote = await executeIntent({
assetIn: 'btc:BTC',
assetOut: 'NEAR',
amount: '0.01',
recipient: 'yourname.near',
refundAddress: 'bc1q...', // REQUIRED - Your Bitcoin address
mode: 'manual',
});
// User sends 0.01 BTC to the deposit address on Bitcoin
```
---
## Configuration
### `.env` file (only needed for auto mode with NEAR origin):
```env
NEAR_ACCOUNT_ID=your-account.near
NEAR_PRIVATE_KEY=ed25519:your_private_key_here
NEAR_RPC_URL=https://rpc.mainnet.fastnear.com
NEAR_NETWORK_ID=mainnet
ONE_CLICK_JWT=optional_jwt_token
```
- `ONE_CLICK_JWT`: Register at [partners.near-intents.org](https://partners.near-intents.org/) to avoid the 0.2% fee.
- Skip `.env` entirely if only using manual mode for non-NEAR-origin swaps.
⚠️ **Never commit `.env` to version control!**
---
## Under the Hood — 1Click SDK Flow
```
executeIntent()
│
├─ 1. resolveToken(assetIn) → { symbol, decimals, assetId }
├─ 2. resolveToken(assetOut) → { symbol, decimals, assetId }
├─ 3. toSmallestUnit(amount, decimals)
│
├─ 4. OneClickService.getQuote({
│ originAsset, destinationAsset, amount,
│ refundTo, recipient, deadline, ...
│ })
│ → Returns { depositAddress, amountIn, amountOut }
│
├─ [manual mode] → return quote instructions
│
├─ 5. account.transfer() → send deposit to depositAddress
├─ 6. OneClickService.submitDepositTx() → (optional, speeds up)
└─ 7. OneClickService.getExecutionStatus() → poll until SUCCESS
```
### Swap Statuses
| Status | Meaning |
|---------------------|---------|
| `PENDING_DEPOSIT` | Waiting for deposit |
| `PROCESSING` | Deposit detected, market makers executing |
| `SUCCESS` | Tokens delivered to recipient |
| `INCOMPLETE_DEPOSIT`| Deposit below required amount |
| `REFUNDED` | Swap failed, tokens returned to refund address |
| `FAILED` | Swap failed due to error |
---
## Token Map (built-in)
The code includes a static `TOKEN_MAP` for common tokens:
| Key | Asset ID (NEP-141) | Decimals |
|--------------|-----------------------------------------------------------------|----------|
| `NEAR` | `nep141:wrap.near` | 24 |
| `USDC` | `nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1` | 6 |
| `USDT` | `nep141:usdt.tether-token.near` | 6 |
| `base:USDC` | `nep141:base-0x833589fcd6edb6e08f4c7c32d4f71b54bda02913.omft.near` | 6 |
| `arb:USDC` | `nep141:arb-0xaf88d065e77c8cc2239327c5edb3a432268e5831.omft.near` | 6 |
| `arb:ARB` | `nep141:arb-0x912ce59144191c1204e64559fe8253a0e49e6548.omft.near` | 18 |
| `eth:ETH` | `nep141:eth.omft.near` | 18 |
| `eth:USDC` | `nep141:eth-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.omft.near` | 6 |
| `sol:SOL` | `nep141:sol.omft.near` | 9 |
| `sol:USDC` | `nep141:sol-5ce3bf3a31af18be40ba30f721101b4341690186.omft.near` | 6 |
To add more tokens: use `OneClickService.getTokens()` or check `TOKENS.md`.
---
## Decimals Quick Reference
| Token | Decimals | `"1.0"` → smallest unit |
|-------|----------|------------------------|
| NEAR | 24 | `1000000000000000000000000` |
| USDC | 6 | `1000000` |
| USDT | 6 | `1000000` |
| ETH | 18 | `1000000000000000000` |
| SOL | 9 | `1000000000` |
| BTC | 8 | `100000000` |
Decimal conversion is handled automatically by `index.ts` using `decimal.js`.
---
## Tracking & Explorer
- **NEAR transactions**: `https://nearblocks.io/txns/<txHash>`
- **1Click swap status**: `https://explorer.near-intents.org/transactions/<depositAddress>`
---
## Troubleshooting
| Error | Solution |
|-------|----------|
| `Token not found: X` | Check symbol and chain prefix. See `TOKEN_MAP` or `TOKENS.md` |
| `No deposit address in quote response` | Solver couldn't match the pair/amount. Try a different amount or pair |
| `NEAR_ACCOUNT_ID and NEAR_PRIVATE_KEY must be set` | Configure `.env` or use `mode: 'manual'` |
| `Swap failed with status: REFUNDED` | Tokens returned to refund address. Retry with different amount |
| `Status polling timed out` | Check explorer URL manually. Swap may still complete |
| 401 Authentication error | JWT is invalid or expired. Register at partners.near-intents.org |
---
## Dependencies
```json
{
"@defuse-protocol/one-click-sdk-typescript": "0.1.1",
"@near-js/accounts": "^2.2.4",
"@near-js/crypto": "^2.2.4",
"@near-js/providers": "^2.2.4",
"@near-js/signers": "^2.2.4",
"@near-js/tokens": "^2.2.4",
"decimal.js": "^10.4.3",
"dotenv": "^16.3.1"
}
```
---
## Files Overview
| File | Purpose |
|------|---------|
| `index.ts` | Main entry — exports `executeIntent()` |
| `lib-1click/` | Step-by-step 1Click SDK examples (get tokens, get quote, send deposit, etc.) |
| `SKILL.md` | This file — **primary AI agent reference** |
| `AI-AGENT-GUIDE.md` | Detailed agent workflow guide |
| `TOKENS.md` | Full token reference with decimals and asset IDs |
| `manifest.json` | Skill manifest for OpenClaw |
| `README.md` | Project documentation |
| `USAGE_GUIDE.md` | Usage patterns and troubleshooting |
---
## Version
**v2.0.0** — Powered by [1Click SDK](https://github.com/defuse-protocol/one-click-sdk-typescript) and [NEAR Intents](https://docs.near-intents.org)
## License
MIT
don't have the plugin yet? install it then click "run inline in claude" again.
formalized intent statement, separated environment variables from function parameters, added decision branches for mode selection and refund address validation, added edge cases for network timeouts and liquidity failures, specified exact output contract format with error handling, defined success signals for both auto and manual modes, moved author to frontmatter.
execute cross-chain token swaps and bridges across 12+ blockchains (NEAR, Base, Ethereum, Arbitrum, Solana, BSC, Gnosis, Starknet, Bitcoin, Dogecoin, Zcash, Litecoin) using the NEAR Intents 1Click API. use this skill when you need to move tokens between chains, convert one asset to another across chain boundaries, or get a quote for a cross-chain swap. minimum swap size is roughly $0.10 USD. no NEAR account required unless the origin asset lives on NEAR.
environment variables (required only for auto mode with NEAR origin):
NEAR_ACCOUNT_ID: your NEAR account (e.g., user.near)NEAR_PRIVATE_KEY: ed25519 private key for signing (format: ed25519:...)NEAR_RPC_URL: NEAR RPC endpoint (default: https://rpc.mainnet.fastnear.com)NEAR_NETWORK_ID: network identifier (default: mainnet)ONE_CLICK_JWT: optional JWT token from partners.near-intents.org to avoid 0.2% fee (register first)function parameters for executeIntent():
assetIn (required, string): origin token symbol using chain:SYMBOL format or bare symbol for NEAR-native tokens (e.g., NEAR, base:USDC, arb:USDC, btc:BTC)assetOut (required, string): destination token symbol in same formatamount (required, string): human-readable amount as decimal string (e.g., 1.0, 0.5)recipient (optional, string): destination wallet address on target chain; defaults to NEAR account if not providedrefundAddress (conditionally required, string): wallet address on origin chain for automatic refunds if swap fails; mandatory when assetIn is on a non-NEAR chain (e.g., base:USDC, btc:BTC); function throws error if missing for cross-chain swapsmode (optional, string): auto (default, auto-sends from NEAR account) or manual (returns quote + deposit address for user to send manually)swapType (optional, string): EXACT_INPUT (default, input amount is fixed) or EXACT_OUTPUT (desired output is fixed, function returns required input)external API connection:
https://1click.chaindefuser.comedge cases to handle:
refundAddress for non-NEAR origins (function validates and throws)validate inputs: check that assetIn, assetOut, and amount are non-empty strings; confirm amount is a valid decimal number (e.g., not NaN or infinite). if mode is manual and assetIn is not NEAR-native, verify refundAddress is provided and belongs to the correct chain.
resolve origin and destination tokens: call internal resolveToken(assetIn) to look up token metadata (symbol, decimals, asset ID) using the built-in TOKEN_MAP; repeat for assetOut. if either token is not found, return error with list of supported tokens from TOKENS.md.
convert amount to smallest unit: use decimal.js to convert human-readable amount string to smallest unit (wei/satoshi/lamport depending on decimals). for example, 1.0 NEAR becomes 1000000000000000000000000 (24 zeros). store result as amountSmallestUnit.
call 1Click API getQuote: send HTTP POST request to 1Click API with parameters: origin asset ID, destination asset ID, amount in smallest units, refund address (if applicable), recipient address, optional deadline (30 minutes from now), optional JWT token. response includes: depositAddress (where user sends tokens on origin chain), amountIn (exact input required), amountOut (exact output received).
decision branch on mode:
mode === 'manual': format and return quote as human-readable string with deposit address, amounts, tracking URL (https://explorer.near-intents.org/transactions/<depositAddress>), and deadline. skip step 6-8 and return.mode === 'auto': proceed to step 6.auto mode: load NEAR account credentials: read NEAR_ACCOUNT_ID and NEAR_PRIVATE_KEY from environment; initialize NEAR account object using @near-js/accounts and @near-js/signers. if either is missing, throw error "NEAR_ACCOUNT_ID and NEAR_PRIVATE_KEY must be set".
auto mode: send deposit transaction: call account.transfer() to send amountIn tokens to the depositAddress returned from quote. wait for transaction confirmation on NEAR blockchain. if transfer fails, return error with transaction hash. if successful, capture transaction hash.
auto mode: poll for swap completion: repeatedly call 1Click API getExecutionStatus() with deposit address every 2 seconds for up to 5 minutes. check response status field: if SUCCESS, extract final amounts and recipient confirmation; if PROCESSING or PENDING_DEPOSIT, continue polling; if REFUNDED, INCOMPLETE_DEPOSIT, or FAILED, return error with status and reason. if polling times out, return message to check explorer manually.
return result: format success response with original amounts, destination amounts, transaction hashes (NEAR tx if auto mode, 1Click swap ID), and explorer URLs for both NEAR (https://nearblocks.io/txns/<txHash>) and 1Click (https://explorer.near-intents.org/transactions/<depositAddress>).
if assetIn is on a non-NEAR chain (e.g., base:USDC, arb:USDC, btc:BTC, eth:ETH):
refundAddress parameter; throw error if not providedrefundAddress is the only way to recover funds if swap failsif mode === 'manual':
if mode === 'auto' (default):
assetIn is NEAR or NEAR-native token.env credentials (NEAR_ACCOUNT_ID and NEAR_PRIVATE_KEY)if swapType === 'EXACT_OUTPUT':
amount parameter specifies desired output (e.g., "10 USDC out")if 1Click API returns no deposit address:
if swap status is REFUNDED:
refundAddress on origin chainif polling times out (no completion status after 5 minutes):
manual mode returns a formatted string:
NEAR Intents Quote
Origin: 5.0 arb:USDC
Destination: 4.97 sol:USDC
Deposit Address: <address on Arbitrum>
Deadline: <ISO 8601 timestamp, typically 30 min from now>
Tracking: https://explorer.near-intents.org/transactions/<depositAddress>
Instructions:
1. Send 5.0 USDC to the deposit address on Arbitrum
2. Wait for confirmation (typically 2-5 minutes)
3. Your USDC will arrive at YourSolanaAddress on Solana
auto mode (success) returns a formatted string:
Swap Successful! 1.0 NEAR → 0.97 base:USDC
NEAR Transaction: https://nearblocks.io/txns/<nearTxHash>
1Click Swap: https://explorer.near-intents.org/transactions/<depositAddress>
Recipient: 0x<yourBaseAddress>
Status: SUCCESS
auto mode (failure) returns error object:
{
"error": "Swap failed with status: REFUNDED",
"status": "REFUNDED",
"depositAddress": "...",
"refundAddress": "...",
"explorer": "https://explorer.near-intents.org/transactions/..."
}
error responses are thrown as exceptions with message string and optional code field (e.g., TOKEN_NOT_FOUND, MISSING_REFUND_ADDRESS, SWAP_FAILED, POLLING_TIMEOUT).
data format is JSON where applicable; all numeric amounts are strings (to preserve precision); all URLs are HTTPS; all timestamps are ISO 8601 UTC.
user knows the skill worked when:
credits: original skill by cuongdcdev; powered by 1Click SDK and NEAR Intents.