# OKX Web3 Documentation
## OKX OnchainOS Developer Documentation
- [What is Onchain OS](https://web3.okx.com/onchainos/dev-docs/home/what-is-onchainos.md)
# What is Onchain OS
## Built for AI. Ready for Web3.
Onchain OS is the Web3 infrastructure for the AI era — reshaping the next generation of finance.
Agentic Wallet sets a new benchmark for Agent-native wallets — private key generation and signing are fully within a Trusted Execution Environment (TEE), with Agents driving onchain transactions directly through natural language, backed by OKX's Web3 infrastructure and security system serving tens of millions of users worldwide. All capabilities are packaged as Skills and MCP Server, installable with a single command.
## Two Integration Methods
- Skills: Drive Onchain OS directly through natural language conversation with your Agent.
- Open API: Programmatically access capabilities of Onchain OS with precision.
## Core Capabilities
- **Wallet** — Agentic Wallet serves as the dedicated wallet for AI Agents. Private keys are generated and signed within a Trusted Execution Environment (TEE), untouchable by anyone. Every Agent operation undergoes intent verification and real-time monitoring, purpose-built for autonomous operation.
- **Trade** — Powered by OKX DEX aggregation engine, scanning liquidity across the entire network in real time, with intelligent routing to find the best price and maximize the received amount on every swap. Native multi-chain architecture covering major EVM chains and Solana, battle-tested by millions of traders and developers.
- **Market** — Real-time multi-chain market data, covering token prices, transaction records, and comprehensive data support for advanced strategies.
- **Payments** — Built on the x402 protocol, pay-as-you-go, purpose-built for autonomous Agent payment scenarios.
- [Supported Networks](https://web3.okx.com/onchainos/dev-docs/home/supported-chain.md)
# Supported Networks
## Agentic Wallet
### Popular Networks
| Chain | Status | ChainIndex |
|---|---|---|
| X Layer | [✓](../wallet/agentic-wallet) | 196 |
| Ethereum | [✓](../wallet/agentic-wallet) | 1 |
| Solana | [✓](../wallet/agentic-wallet) | 501 |
| BNB Chain | [✓](../wallet/agentic-wallet) | 56 |
| Base | [✓](../wallet/agentic-wallet) | 8453 |
### EVM Networks
| Chain | Status | ChainIndex |
|---|---|---|
| Arbitrum One | [✓](../wallet/agentic-wallet) | 42161 |
| Avalanche C | [✓](../wallet/agentic-wallet) | 43114 |
| Blast | [✓](../wallet/agentic-wallet) | 81457 |
| Conflux | [✓](../wallet/agentic-wallet) | 1030 |
| Fantom | [✓](../wallet/agentic-wallet) | 250 |
| Linea | [✓](../wallet/agentic-wallet) | 59144 |
| Monad | [✓](../wallet/agentic-wallet) | - |
| Optimism | [✓](../wallet/agentic-wallet) | 10 |
| Polygon | [✓](../wallet/agentic-wallet) | 137 |
| Scroll | [✓](../wallet/agentic-wallet) | 534352 |
| Sonic | [✓](../wallet/agentic-wallet) | 146 |
| zkSync Era | [✓](../wallet/agentic-wallet) | 324 |
## Open API
### Popular Networks
| Chain | Wallet API | Trade | Market | Payments | Dapp Connect | ChainIndex |
|---|---|---|---|---|---|---|
| X Layer | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | [✓](../payments/x402-introduction) | [✓](../sdks/okx-wallet-integration-introduction) | 196 |
| Bitcoin | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 0 |
| Ethereum | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 1 |
| Solana | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | Coming Soon | [✓](../sdks/okx-wallet-integration-introduction) | 501 |
| Tron | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 195 |
| BNB Chain | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | Coming Soon | [✓](../sdks/okx-wallet-integration-introduction) | 56 |
| Base | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | Coming Soon | [✓](../sdks/okx-wallet-integration-introduction) | 8453 |
| SUI | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 784 |
### EVM Networks
| Chain | Wallet API | Trade | Market | Payments | Dapp Connect | ChainIndex |
|---|---|---|---|---|---|---|
| Arbitrum One | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 42161 |
| Avalanche C | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 43114 |
| Base | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | Coming Soon | [✓](../sdks/okx-wallet-integration-introduction) | 8453 |
| Blast | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 81457 |
| BNB Chain | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | Coming Soon | [✓](../sdks/okx-wallet-integration-introduction) | 56 |
| Conflux | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 1030 |
| Cronos | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 25 |
| Ethereum | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 1 |
| Fantom | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 250 |
| Linea | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 59144 |
| Manta Pacific | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 169 |
| Mantle | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 5000 |
| Merlin | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 4200 |
| Metis | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 1088 |
| Optimism | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 10 |
| Polygon | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 137 |
| Polygon zkEVM | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 1101 |
| Scroll | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 534352 |
| Sonic | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 146 |
| Uni Chain | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 130 |
| X Layer | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | [✓](../payments/x402-introduction) | [✓](../sdks/okx-wallet-integration-introduction) | 196 |
| ZetaChain | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 7000 |
| zkSync Era | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 324 |
### Other Networks
| Chain | Wallet API | Trade | Market | Payments | Dapp Connect | ChainIndex |
|---|---|---|---|---|---|---|
| Bitcoin | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 0 |
| Solana | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | Coming Soon | [✓](../sdks/okx-wallet-integration-introduction) | 501 |
| SUI | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 784 |
| Ton | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 607 |
| Tron | [✓](../wallet/what-is-wallet) | [✓](../trade/dex-api-introduction) | [✓](../market/market-api-introduction) | - | [✓](../sdks/okx-wallet-integration-introduction) | 195 |
- [Authentication](https://web3.okx.com/onchainos/dev-docs/home/api-access-and-usage.md)
# Authentication
## If you use Agentic Wallet
### Email Authentication
Create or access your wallet with just an email — no developer account or API key required. Ideal for getting started quickly.
**Step 1**
Tell your Agent:
```plain text
Log in to Agentic Wallet with email
```
**Step 2**
Enter your email and wait for the verification code:
```plain text
```
**Step 3**
Enter the verification code:
```plain text
```
Once verified, the Agent will automatically create a wallet on first login:
```plain text
Wallet created successfully!
EVM Address:
Solana Address:
```
Private keys are generated and stored in a TEE environment and are never
exposed to anyone, including your Agent. Logging in again with the same email
will restore your existing wallet — no need to recreate it.
### API Key Authentication
The built-in sandbox keys are for testing only and may be subject to rate limits. For stable production usage, set up your own API credentials:
1. Go to the [OKX Developer Portal](https://web3.okx.com/onchainos/dev-portal/project).
2. Generate your **API Key**, **Secret Key**, and **Passphrase**.
Tell your agent:
```plain text
Create a .env file with these variables: OKX_API_KEY, OKX_SECRET_KEY, and OKX_PASSPHRASE. Add .env to .gitignore.
And open it, tell me what to do next, and remind me to save after I filled out the passkeys.
```
Your agent will create the file and open it — paste your keys there.
Never commit `.env` to git (add it to `.gitignore`) and never expose these
credentials in logs, screenshots, or chat messages
## If you use Open API
You can also authenticate using an API Key for Onchain OS Skills/Open API. You'll need to create a project and generate an API key in the developer management portal first. For detailed steps and resources, refer to [here](./developer-portal).
All API requests must include the following headers for authentication:
- OK-ACCESS-KEY: API key
- OK-ACCESS-TIMESTAMP: Request timestamp (UTC), in ISO format, e.g. 2020-12-08T09:08:57.715Z
- OK-ACCESS-PASSPHRASE: The passphrase specified when creating the API key
- OK-ACCESS-SIGN: Signature
Signing steps:
- Step 1: Concatenate timestamp, method, requestPath, and body into a single string
- Step 2: Sign the pre-hash string (result of Step 1) using HMAC SHA256 with the secret key (generated when creating the API key)
- Step 3: Encode the signature using Base64
For example, sign=CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(timestamp
+ 'GET' + '/api/v6/dex/aggregator/swap', SecretKey)). The timestamp must match
OK-ACCESS-TIMESTAMP. GET is the method (HTTP request method, all uppercase).
/api/v6/dex/aggregator/swap is the requestPath. The body is empty — it can be
omitted if there is no request body (typically for GET requests).
The timestamp must not differ from the server time by more than 30 seconds.
POST requests must include the raw request body in the signature calculation.
The secret key is only visible at creation time — store it through a secure
channel.
**Postman Example**
Postman is a popular API development and testing tool that allows developers to design, test, and document APIs. It provides a user-friendly graphical interface for sending HTTP requests to APIs.
If you haven't installed Postman yet, you can download it for free from the Postman website: https://www.postman.com/
This example requires a basic understanding of Postman.
### Add Parameters
- This typically applies to GET requests.
- If your request requires query parameters, you can add them under the **Params** tab. Here you can add key-value pairs for query parameters.

### Set Headers
Under the **Headers** tab, add the following key-value pairs:
- `OK-ACCESS-KEY`
- `OK-ACCESS-PASSPHRASE`

### Add Body
- This typically applies to POST requests.
- If your request requires a request body, you can add it under the **Body** tab.
- Select **raw** and **JSON** from the dropdown menu.
- Enter your request body in JSON format.

### Set Pre-request Script
- Used to generate the required signature (`OK-ACCESS-SIGN`) and timestamp (`OK-ACCESS-TIMESTAMP`).
- Under the **Pre-request Script** tab, insert the script corresponding to your request type.
- When generating the pre-hash string, GET requests exclude the request body.
- Edit the secret key as needed.
GET request:
```javascript
var method = pm.request.method;
var now = new Date();
var isoString = now.toISOString();
var path = pm.request.url.getPathWithQuery();
var sign = CryptoJS.enc.Base64.stringify(
CryptoJS.HmacSHA256(
isoString + method + path,
pm.variables.replaceIn('{{secret_key}}')
)
);
pm.request.headers.add({
key: 'OK-ACCESS-SIGN',
value: sign,
});
pm.request.headers.add({
key: 'OK-ACCESS-TIMESTAMP',
value: isoString,
});
```
POST request:
```javascript
var method = pm.request.method;
var now = new Date();
var isoString = now.toISOString();
var path = pm.request.url.getPathWithQuery();
var bodyStr = pm.request.body.raw;
var sign = CryptoJS.enc.Base64.stringify(
CryptoJS.HmacSHA256(
isoString + method + path + bodyStr,
pm.variables.replaceIn('{{secret_key}}')
)
);
pm.request.headers.add({
key: 'OK-ACCESS-SIGN',
value: sign,
});
pm.request.headers.add({
key: 'OK-ACCESS-TIMESTAMP',
value: isoString,
});
```
**JavaScript Example**
To call the API via a JavaScript script, refer to the following code example:
```javascript
const https = require('https');
const crypto = require('crypto');
const querystring = require('querystring');
// Define API credentials
const api_config = {
api_key: '',
secret_key: '',
passphrase: '',
};
function preHash(timestamp, method, request_path, params) {
// Create pre-sign string from parameters
let query_string = '';
if (method === 'GET' && params) {
query_string = '?' + querystring.stringify(params);
}
if (method === 'POST' && params) {
query_string = JSON.stringify(params);
}
return timestamp + method + request_path + query_string;
}
function sign(message, secret_key) {
// Sign the pre-hash string using HMAC-SHA256
const hmac = crypto.createHmac('sha256', secret_key);
hmac.update(message);
return hmac.digest('base64');
}
function createSignature(method, request_path, params) {
// Get ISO 8601 formatted timestamp
const timestamp = new Date().toISOString().slice(0, -5) + 'Z';
// Generate signature
const message = preHash(timestamp, method, request_path, params);
const signature = sign(message, api_config['secret_key']);
return { signature, timestamp };
}
function sendGetRequest(request_path, params) {
// Generate signature
const { signature, timestamp } = createSignature('GET', request_path, params);
// Build request headers
const headers = {
'OK-ACCESS-KEY': api_config['api_key'],
'OK-ACCESS-SIGN': signature,
'OK-ACCESS-TIMESTAMP': timestamp,
'OK-ACCESS-PASSPHRASE': api_config['passphrase'],
};
const options = {
hostname: 'web3.okx.com',
path: request_path + (params ? `?${querystring.stringify(params)}` : ''),
method: 'GET',
headers: headers,
};
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log(data);
});
});
req.end();
}
function sendPostRequest(request_path, params) {
// Generate signature
const { signature, timestamp } = createSignature(
'POST',
request_path,
params
);
// Build request headers
const headers = {
'OK-ACCESS-KEY': api_config['api_key'],
'OK-ACCESS-SIGN': signature,
'OK-ACCESS-TIMESTAMP': timestamp,
'OK-ACCESS-PASSPHRASE': api_config['passphrase'],
'Content-Type': 'application/json',
};
const options = {
hostname: 'web3.okx.com',
path: request_path,
method: 'POST',
headers: headers,
};
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log(data);
});
});
if (params) {
req.write(JSON.stringify(params));
}
req.end();
}
// GET request example
const getRequestPath = '/api/v6/dex/aggregator/quote';
const getParams = {
chainIndex: 42161,
amount: 1000000000000,
toTokenAddress: '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8',
fromTokenAddress: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
};
sendGetRequest(getRequestPath, getParams);
// POST request example
const postRequestPath = '/api/v5/mktplace/nft/ordinals/listings';
const postParams = {
slug: 'sats',
};
sendPostRequest(postRequestPath, postParams);
```
- [Developer Portal](https://web3.okx.com/onchainos/dev-docs/home/developer-portal.md)
# Developer Portal
This section covers the features of the development portal and best practices which will help you to build your own apps efficiently.
## Sign up/Log in
1. Click on the [**Developer portal**](https://web3.okx.com/build/dev-portal).
2. Click **Connect Wallet** to create or log in to an account. We recommend to use OKX Wallet for a seamless experience.You can also choose other wallets for your project.
3. Click **Verify** in the developer portal, then confirm **Signature** request in the extension.
You have successfully created your developer account with OKX API. A default project is also created for your convenience.
## Link E-mail and Phone number to your account
1. Click the **Get started** button.
2. Input your E-mail and Phone number correctly.
If you find you cannot find your country in the Phone number country selection part, that's mainly because some regions are restricted to OKX API services.
3. Complete the verification code process.
You have successfully linked your E-mail and phone number to your developer account. You can now immediately create OKX API keys on the developer portal and start embedding our advanced trading capability into your dApps.
## Generate a New API Key
1. Switch to the project that you want to create an API key. Note, you can create up to three projects per developer account, and three API keys per project.
2. Navigate to the **Home** page and click the **Create API key** button.
3. Enter the name of the API key and passphrase, and click **Create** to generate the corresponding API Key.
Please keep your passphrase safe and accessible. You cannot access your API key in your project without the passphrase.
## View the Secret Key
When making API calls with a generated API key, you need to provide both the API passphrase and the secret key. The secret key is system generated, and can be viewed by clicking the **View details** button on the Home page.
## General Terms
- API key: a unique identifier used to authenticate and authorize an application or user when making requests to OKX API.
- Secret key: a system generated security token to provide additional security for your API key.
- Passphrase: a phrase supplied by the developer when creating an API key, used to encrypt the secret key on the server to provide additional security. The passphrase is also needed to view the secret key and modify the API information.
- [Agentic Wallet](https://web3.okx.com/onchainos/dev-docs/home/agentic-wallet-overview.md)
# Agentic Wallet
**Your AI Agent now has its own wallet.**
The most powerful AI Agentic wallet in the industry — key generation, storage, and signing all happen inside TEE. No one can touch the private key, not even OKX. Agents autonomously scan the market, assess timing, and execute trades, backed by OKX's full-chain liquidity and security infrastructure. Install with a single command, sign in with email to get started.
## Why Your Agent Needs Agentic Wallet
- **Closed-Loop Execution** — From signal to onchain settlement, Agents handle everything autonomously. No profit lost to manual delays.
- **24/7 nonstop** — The market never sleeps, neither does your Agent. Always first in when opportunity strikes.
- **Automatic risk protection** — Malicious approvals and abnormal transfers identified in real time. Assets remain fully in your control.
- **Multi-Strategy in Parallel** — Up to 50 sub-wallets, isolated positions, multiple strategies running simultaneously for maximum returns.
- **Autonomous payments** — Built on the x402 protocol, Agents automatically pay for the data and services they need, keeping the execution chain uninterrupted.
## What Agentic Wallet Can Do
- **Automated trading** — Describe your strategy, and your Agent scans the market 7×24 and executes autonomously.
- **Assets & security** — Multi-chain balances, risk detection, and one-click revocation of malicious approvals. Protect your profits.
- **Multi-wallet** — Up to 50 sub-wallets, batch transfers, parallel position management.
- **Auto-payments** — Built on the x402 protocol, Agents pay onchain automatically when calling external APIs.
- **Market monitoring** — Token prices, smart money movements, onchain anomalies — tracked in real time.
Start exploring Agentic Wallet
- [Skills/MCP Services](https://web3.okx.com/onchainos/dev-docs/home/skills-mcp-services.md)
# Skills/MCP Services
**Give your Agent the power to trade, access market data, and make payments.**
Trade, Market, and Payment Skills/MCP are now live. Agents can swap tokens, fetch real-time prices, query onchain data, and stream market updates.
- [Install Your Agentic Wallet](https://web3.okx.com/onchainos/dev-docs/home/install-your-agentic-wallet.md)
# Install Your Agentic Wallet
Give your AI Agent its first onchain wallet — private keys protected by TEE, automatic risk detection before transactions, from wallet setup to onchain trading all through conversation.
## Preparation: Install an AI Agent
This guide uses [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) as an example. Onchain OS supports all mainstream agents ([Cursor](https://docs.cursor.com/get-started/installation), [OpenClaw](https://docs.openclaw.ai/), etc.).
## Step 1: Add Onchain OS to Your Agent
Tell your Agent:
```plain text
Run npx skills add okx/onchainos-skills to install Onchain OS skills.
Note: Please install to the skill directory corresponding to your current Agent.
```
The Agent will complete the installation automatically. For more details, see [Github](https://github.com/okx/onchainos-skills).
## Step 2: Create a Wallet
Tell your Agent:
```plain text
Log in to Agentic Wallet with email
```
Enter your email and wait for the verification code:
```plain text
```
Enter the verification code:
```plain text
```
Once verified, the Agent will automatically create a wallet on first login:
```plain text
Wallet created successfully!
EVM Address:
Solana Address:
```
Private keys are generated and stored entirely within the TEE — never exposed to anyone, including your Agent. Logging in with the same email restores the existing wallet automatically.
- [Run Your First AI Agent](https://web3.okx.com/onchainos/dev-docs/home/run-your-first-ai-agent.md)
# Run Your First AI Agent
You've installed Agentic Wallet — now let your AI Agent start trading. Share your strategy, and your Agent will run it around the clock with your private keys secured at all times.
Here's a full walkthrough from setting your strategy to viewing your returns:
## 1. Describe Your Strategy
```plain text
Monitor what smart money is buying on Solana, with these conditions:
- Market cap under $5M
- At least 2 smart money wallets buying
- Auto-buy 1 USDT after security check passes
- Take profit at +50% with trailing stop (sell on 10% pullback), stop loss at -20%
```
Agent breaks down the strategy and confirms:
```plain text
Got it, let me break this down:
1. Monitor: Solana smart money wallet activity
2. Filters: Market cap < $5M, ≥ 2 smart money wallets buying, security check passed
3. Buy: 1 USDT per trade
4. Take profit: Trailing stop after +50%, sell on 10% pullback
5. Stop loss: -20%
Confirm and I'll start executing.
```
## 2. Auto Trading
Agent detects a signal and executes buy:
```plain text
Signal detected: meets criteria
- Smart money buys: 3 wallets
- Current market cap: $2.9M
- Security check: Passed
Executing buy : 1 USDT
Trade complete ✓
Buy price: $0.00999
Tx hash:
```
## 3. Auto Take-Profit / Stop-Loss
```plain text
current price: $0.01699 (+70.2%)
Trailing take-profit triggered, tracking...
pulled back to $0.01539 (10.1% from high)
Trailing take-profit triggered, executing sell.
Sell complete ✓
Profit: +0.54 USDT (+54.1%)
Tx hash:
```
## 4. View Report
Ask your Agent:
```plain text
How did I do today?
```
Agent generates a report:
```plain text
Today's report:
Closed: 3 trades | Open: 2 trades
1. +54.1% ✓ Trailing take-profit
2. -20.0% ✗ Stop loss
3. +10.0% ✓ Timeout exit
Today's realized profit: +0.44 USDT
```
The above example is for demonstration purposes only and does not constitute any investment advice. Please make trading decisions based on your own judgment.
- [Build Your DApp](https://web3.okx.com/onchainos/dev-docs/home/run-your-first-dapp.md)
# Build Your DApp
If you're a DApp developer, this demo shows you how to build your DApp.
Example: Using the Trade API endpoint for token swaps. You'll swap USDC for ETH on the Ethereum network.
## 1. Set Up Your Environment
```typescript
// --------------------- npm package ---------------------
import { Web3 } from 'web3';
import axios from 'axios';
import * as dotenv from 'dotenv';
import CryptoJS from 'crypto-js';
// The URL for the Ethereum node you want to connect to
const web3 = new Web3('https://......com');
// --------------------- environment variable ---------------------
// Load hidden environment variables
dotenv.config();
// Your wallet information - REPLACE WITH YOUR OWN VALUES
const WALLET_ADDRESS: string = process.env.EVM_WALLET_ADDRESS || '0xYourWalletAddress';
const PRIVATE_KEY: string = process.env.EVM_PRIVATE_KEY || 'YourPrivateKey';
// Token addresses for swap on Base Chain
const ETH_ADDRESS: string = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; // Native ETH
const USDC_ADDRESS: string = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC on Base
// Chain ID for Base Chain
const chainIndex: string = '8453';
// API URL
const baseUrl: string = 'https://web3.okx.com/api/v6/';
// Amount to swap in smallest unit (0.0005 ETH)
const SWAP_AMOUNT: string = '500000000000000'; // 0.0005 ETH
const SLIPPAGEPERCENT: string = '0.5'; // 0.5% slippagePercent tolerance
// --------------------- util function ---------------------
export function getHeaders(timestamp: string, method: string, requestPath: string, queryString = "") {
// Check https://web3.okx.com/zh-hans/web3/build/docs/waas/rest-authentication for api-key
const apiKey = process.env.OKX_API_KEY;
const secretKey = process.env.OKX_SECRET_KEY;
const apiPassphrase = process.env.OKX_API_PASSPHRASE;
const projectId = process.env.OKX_PROJECT_ID;
if (!apiKey || !secretKey || !apiPassphrase || !projectId) {
throw new Error("Missing required environment variables");
}
const stringToSign = timestamp + method + requestPath + queryString;
return {
"Content-Type": "application/json",
"OK-ACCESS-KEY": apiKey,
"OK-ACCESS-SIGN": CryptoJS.enc.Base64.stringify(
CryptoJS.HmacSHA256(stringToSign, secretKey)
),
"OK-ACCESS-TIMESTAMP": timestamp,
"OK-ACCESS-PASSPHRASE": apiPassphrase,
"OK-ACCESS-PROJECT": projectId,
};
};
```
## 2. Check Allowance
You need to check if the token has been approved for the DEX to spend. This step is only needed for ERC20 tokens, not for native tokens like ETH.
```typescript
/**
* Check token allowance for DEX
* @param tokenAddress - Token contract address
* @param ownerAddress - Your wallet address
* @param spenderAddress - DEX spender address
* @returns Allowance amount
*/
async function checkAllowance(
tokenAddress: string,
ownerAddress: string,
spenderAddress: string
): Promise {
const tokenABI = [
{
"constant": true,
"inputs": [
{ "name": "_owner", "type": "address" },
{ "name": "_spender", "type": "address" }
],
"name": "allowance",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
}
];
const tokenContract = new web3.eth.Contract(tokenABI, tokenAddress);
try {
const allowance = await tokenContract.methods.allowance(ownerAddress, spenderAddress).call();
return BigInt(String(allowance));
} catch (error) {
console.error('Failed to query allowance:', error);
throw error;
}
}
```
## 3. Check the Approval Parameters and Initiate the Approval
If the allowance is lower than the amount you want to swap, you need to approve the token.
### 3.1 Define your transaction approval parameters
```typescript
const getApproveTransactionParams = {
chainIndex: chainIndex,
tokenContractAddress: tokenAddress,
approveAmount: amount
};
```
### 3.2 Define helper functions
```typescript
async function getApproveTransaction(
tokenAddress: string,
amount: string
): Promise {
try {
const path = 'dex/aggregator/approve-transaction';
const url = `${baseUrl}${path}`;
const params = {
chainIndex: chainIndex,
tokenContractAddress: tokenAddress,
approveAmount: amount
};
// Prepare authentication
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await axios.get(url, { params, headers });
if (response.data.code === '0') {
return response.data.data[0];
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get approval transaction data:', (error as Error).message);
throw error;
}
}
```
### 3.3 Create Compute gasLimit utility function
Using the Onchain gateway API to get the gas limit.
```typescript
/**
* Get transaction gas limit from Onchain gateway API
* @param fromAddress - Sender address
* @param toAddress - Target contract address
* @param txAmount - Transaction amount (0 for approvals)
* @param inputData - Transaction calldata
* @returns Estimated gas limit
*/
async function getGasLimit(
fromAddress: string,
toAddress: string,
txAmount: string = '0',
inputData: string = ''
): Promise {
try {
const path = 'dex/pre-transaction/gas-limit';
const url = `https://web3.okx.com/api/v6/${path}`;
const body = {
chainIndex: chainIndex,
fromAddress: fromAddress,
toAddress: toAddress,
txAmount: txAmount,
extJson: {
inputData: inputData
}
};
// Prepare authentication with body included in signature
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
if (response.data.code === '0') {
return response.data.data[0].gasLimit;
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get gas limit:', (error as Error).message);
throw error;
}
}
```
Using RPC to get the gas limit.
```typescript
const gasLimit = await web3.eth.estimateGas({
from: WALLET_ADDRESS,
to: tokenAddress,
value: '0',
data: approveData.data
});
// Add 20% buffer
const gasLimit = (BigInt(gasLimit) * BigInt(12) / BigInt(10)).toString();
```
### 3.4 Get transaction information and send approveTransaction
```typescript
/**
* Sign and send approve transaction
* @param tokenAddress - Token to approve
* @param amount - Amount to approve
* @returns Transaction hash of the approval transaction
*/
async function approveToken(tokenAddress: string, amount: string): Promise {
const spenderAddress = '0x3b3ae790Df4F312e745D270119c6052904FB6790'; // Ethereum Mainnet DEX spender
// See Router addresses at: https://web3.okx.com/build/docs/waas/dex-smart-contract
const currentAllowance = await checkAllowance(tokenAddress, WALLET_ADDRESS, spenderAddress);
if (currentAllowance >= BigInt(amount)) {
console.log('Sufficient allowance already exists');
return null;
}
console.log('Insufficient allowance, approving tokens...');
// Get approve transaction data from OKX DEX API
const approveData = await getApproveTransaction(tokenAddress, amount);
// Get accurate gas limit using RPC
const gasLimit = await web3.eth.estimateGas({
from: WALLET_ADDRESS,
to: tokenAddress,
value: '0',
data: approveData.data
});
// Get accurate gas limit using Onchain gateway API
// const gasLimit = await getGasLimit(WALLET_ADDRESS, tokenAddress, '0', approveData.data);
// Get current gas price
const gasPrice = await web3.eth.getGasPrice();
const adjustedGasPrice = BigInt(gasPrice) * BigInt(15) / BigInt(10); // 1.5x for faster confirmation
// Get current nonce
const nonce = await web3.eth.getTransactionCount(WALLET_ADDRESS, 'latest');
// Create transaction object
const txObject = {
from: WALLET_ADDRESS,
to: tokenAddress,
data: approveData.data,
value: '0',
gas: gasLimit,
gasPrice: adjustedGasPrice.toString(),
nonce: nonce
};
// Sign and broadcast transaction
const signedTx = await web3.eth.accounts.signTransaction(txObject, PRIVATE_KEY);
const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);
console.log(`Approval transaction successful: ${receipt.transactionHash}`);
return receipt.transactionHash;
}
```
## 4. Get Quote Data
### 4.1 Define quote parameters
```typescript
const quoteParams = {
amount: fromAmount,
chainIndex: chainIndex,
toTokenAddress: toTokenAddress,
fromTokenAddress: fromTokenAddress,
};
```
### 4.2 Define helper functions
```typescript
/**
* Get swap quote from DEX API
* @param fromTokenAddress - Source token address
* @param toTokenAddress - Destination token address
* @param amount - Amount to swap
* @param slippagePercent - Maximum slippagePercent (e.g., "0.5" for 0.5%)
* @returns Swap quote
*/
async function getSwapQuote(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
slippagePercent: string = '0.5'
): Promise {
try {
const path = 'dex/aggregator/quote';
const url = `${baseUrl}${path}`;
const params = {
chainIndex: chainIndex,
fromTokenAddress,
toTokenAddress,
amount,
slippagePercent
};
// Prepare authentication
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await axios.get(url, { params, headers });
if (response.data.code === '0') {
return response.data.data[0];
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get swap quote:', (error as Error).message);
throw error;
}
}
```
## 5. Prepare Transaction
### 5.1 Define swap parameters
```typescript
const swapParams = {
chainIndex: chainIndex,
fromTokenAddress,
toTokenAddress,
amount,
userWalletAddress: userAddress,
slippagePercent
};
```
### 5.2 Request swap transaction data
```typescript
/**
* Get swap transaction data from DEX API
* @param fromTokenAddress - Source token address
* @param toTokenAddress - Destination token address
* @param amount - Amount to swap
* @param userAddress - User wallet address
* @param slippagePercent - Maximum slippagePercent (e.g., "0.5" for 0.5%)
* @returns Swap transaction data
*/
async function getSwapTransaction(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
userAddress: string,
slippagePercent: string = '0.5'
): Promise {
try {
const path = 'dex/aggregator/swap';
const url = `${baseUrl}${path}`;
const params = {
chainIndex: chainIndex,
fromTokenAddress,
toTokenAddress,
amount,
userWalletAddress: userAddress,
slippagePercent
};
// Prepare authentication
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await axios.get(url, { params, headers });
if (response.data.code === '0') {
return response.data.data[0];
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get swap transaction data:', (error as Error).message);
throw error;
}
}
```
## 6. Simulate Transaction
Before executing the actual swap, it's crucial to simulate the transaction to ensure it will succeed and to identify any potential issues:
The Simulate API is available to our whitelisted customers only. Please reach out to dexapi@okx.com to request access.
```typescript
async function simulateTransaction(swapData: any) {
try {
if (!swapData.tx) {
throw new Error('Invalid swap data format - missing transaction data');
}
const tx = swapData.tx;
const params: any = {
fromAddress: tx.from,
toAddress: tx.to,
txAmount: tx.value || '0',
chainIndex: chainIndex,
extJson: {
inputData: tx.data
},
includeDebug: true
};
const timestamp = new Date().toISOString();
const requestPath = "/api/v6/dex/pre-transaction/simulate";
const requestBody = JSON.stringify(params);
const headers = getHeaders(timestamp, "POST", requestPath, "", requestBody);
console.log('Simulating transaction...');
const response = await axios.post(
`https://web3.okx.com${requestPath}`,
params,
{ headers }
);
if (response.data.code !== "0") {
throw new Error(`Simulation failed: ${response.data.msg || "Unknown simulation error"}`);
}
const simulationResult = response.data.data[0];
// Check simulation success
if (simulationResult.success === false) {
console.error('Transaction simulation failed:', simulationResult.error);
throw new Error(`Transaction would fail: ${simulationResult.error}`);
}
console.log('Transaction simulation successful');
console.log(`Estimated gas used: ${simulationResult.gasUsed || 'N/A'}`);
if (simulationResult.logs) {
console.log('Simulation logs:', simulationResult.logs);
}
return simulationResult;
} catch (error) {
console.error("Error simulating transaction:", error);
throw error;
}
}
```
## 7. Broadcast Transaction
Use the Transaction API for gas estimation. This approach leverages Transaction API, which provides more accurate gas estimations than standard methods.
```typescript
/**
* Get transaction gas limit from Onchain gateway API
* @param fromAddress - Sender address
* @param toAddress - Target contract address
* @param txAmount - Transaction amount (0 for approvals)
* @param inputData - Transaction calldata
* @returns Estimated gas limit
*/
async function getGasLimit(
fromAddress: string,
toAddress: string,
txAmount: string = '0',
inputData: string = ''
): Promise {
try {
const path = 'dex/pre-transaction/gas-limit';
const url = `https://web3.okx.com/api/v6/${path}`;
const body = {
chainIndex: chainIndex,
fromAddress: fromAddress,
toAddress: toAddress,
txAmount: txAmount,
extJson: {
inputData: inputData
}
};
// Prepare authentication with body included in signature
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
if (response.data.code === '0') {
return response.data.data[0].gasLimit;
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get gas limit:', (error as Error).message);
throw error;
}
}
```
- [Support](https://web3.okx.com/onchainos/dev-docs/home/support.md)
# Support
Welcome to API Support. Here you'll find resources and guidance for getting help with our products and services.
## Technical Support
- Technical inquiries: [Discord Developer Community](https://discord.gg/mUqMWaFGyW).
- Developers are encouraged to join the real-time technical discussion channel.
## Business Support
- Business cooperation: onchainos@okx.com
- For enterprise service requests, please include your company name and contact information.
- [Change Log](https://web3.okx.com/onchainos/dev-docs/home/change-log.md)
# Change Log
### March 30, 2026
- The dex router address for EVM chains under the Trade API has been updated. The API will automatically construct transaction calldata based on the latest address. If you have whitelist configurations, please update them accordingly.
- The Token API under the Market API now supports pagination.
### March 26, 2026
- The Market API has added WebSocket push channels, enabling more customized and more real-time market capabilities.
### March 20, 2026
- The Market API has added address subscription and tracking, position cluster analysis, and new push channels, enabling more customized and more real-time market capabilities.
### March 10, 2026
- The Market API has added new Portfolio APIs to support PnL analysis and track.
### March 6, 2026
- The Market API has added new Trenches APIs to support meme trade and golden dog track.
### March 5, 2026
- The Market API has added new Strategy APIs to support more advanced indicators and tools.
### March 3, 2026
- The Trading API now supports AI-native tools.
- The Market Data API now supports AI-native tools.
### January 28, 2026
Update
- DEX API supports directly returning the approved contract address and approved calldata via the /swap endpoint.
- RFQ has added support for an EVM EIP-712 signature troubleshooting tool, including the testSignOrder.js example.
### January 22, 2026
Update
- DEX API now supports `useTokenLedger` feature in Solana `/swap-instruction` endpoint to support more customized and specific trading scenario
### December 19, 2025
Update
- Update Firm-order API Request Parameters
### December 11, 2025
Update
- Update Settlement Contract Address
### December 9, 2025
Update
- Update Dexrouter Contract Address
### December 2, 2025
Update
- Update Settlement Contract Address
- Update EVM Signature Example
### November 26, 2025
Update
- Update firm- order API Parameter: beneficiaryAddress
- Remove dex-get-quote API Parameter: quoteCompareList
### November 21, 2025
Update
- DEX API: Support Monad Chain
### October 30, 2025
Update
- DEX API: corrected the parameter types for market, market firm order, makerAmount, and rfqId.
- Added exactOut functionality (currently supported only in V5 DEX API).
### October 30, 2025
Update
- DEX API Upgraded EVM routing algorithm for more intelligent order routing and splitting with new smart contract addresses.
### October 9, 2025
Update
- Swap API supported Plasma chain from now.
### September 25, 2025
Update
- Upgraded DEX API to V6 with major updates including an updated routing algorithm with Directed Acyclic Graphs for more intelligent order routing and splitting.
- Updated a few paramaters in Get Quotes API, Get Solana Swap Instructions API, and Swap API including `chainIndex`, `slippagePercent`, `priceImpactProtectionPercent`, `maxAutoSlippagePercent`, `priceImpactPercent`, `toTokenIndex`, `fromTokenIndex`.
### September 11, 2025
Update
- Standard market maker integration guidelines are published for market makers to easily integrate with OKX DEX RFQ system and provide liquidity to our users and partners.
### September 4, 2025
Update
- The Market API now supports **Token API services**, allowing you to search for the tokens you need, access basic and detailed information, check holder statistics, or retrieve token ranking lists with a single request.
### August 28, 2025
Update
- We are excited to announce that our paid plan will officially launch in September 2025!
- The DEX API now supports multiple service tiers, designed to give you greater flexibility and scalability.
- Higher tiers unlock enhanced features, increased rate limits, and priority support — empowering your business to build faster and scale with confidence.
### August 27, 2025
Update
- The trading API swap endpoint now **supports RFQ quotes by default**, enabling your large orders to receive more favorable pricing. Since RFQ quotes may introduce some latency, you can disable it by adding the `disableRFQ` parameter in the `/swap` endpoint request.
### August 19, 2025
Update
- DEX API does not support OKT Chain any more.
- The `/swap API` now supports excluding specific liquidity sources when assembling calldata. You can use the `excludeDexIds` parameter to specify the liquidity pools you don’t want to use.
### July 1, 2025
Update
- The `feePercent` parameter in the swap API now supports 9 decimal.
- Trade API now supports MEV protection feature on SOL,ETH,BSC,BASE.
### June 6, 2025
Update
- The `swapReceiverAddress` parameter in the swap API now supports Sui and Ton chains. You can now specify a different receiving address for a swap transaction on Sui and Ton chains.
### May 30, 2025
Update
- Trade API now supports transaction simulate. You can take a look at "Onchain gateway API" for reference.
- The Trade API now supports the `exactOut` reverse quoting function. You can specify the amount you want to receive in the swap to query the required input token amount.
{' '}
ExactOut feature currently only support Ethereum、Base、BSC 、Arbitrum chain and
Uni v3 protocols
### May 22, 2025
Update
- Market API now supports to returen up to 100 tokens' 5m、1h、4h、24h trading volume and 5m、1h、4h、24h price change,you can check in the /price-info endpoint
### May 16, 2025
Update
- Trade API now supports Uni chain.
- Trade API now supports four tier priority fee for the Solana chain.
### May 13, 2025
Update
- Trade API now supports setting positive slippage gains for the Solana chain.
This feature is only available to **whitelisted or enterprise users**. If you would like to use it, please contact dexapi@okx.com.
### May 5, 2025
Update
- Trade API now supports setting the referral fee **for Solana chain up to 10%**.
### May 2, 2025
Update
- Trade API now supports the Onchain Gateway API function, offering gas price estimation and on-chain transaction broadcasting services.
### May 1, 2025
Update
- Trade API now supports setting the referral fee for **Ton chain**.
- Trade API now supports setting referral fees for **wrapped tokens on EVM chains**.
### March 27, 2025
Update
- OKX DEX API now supports the **Market API**.
### March 12, 2025
Update
- Single-chain swaps now support the Sonic chain.
### January 23, 2025
Update
- The single-chain swap API now supports specifying a single liquidity pool for swaps. You can achieve this by setting the `directRoute` parameter.
- The single-chain swap API now supports the Transaction querying feature.
### December 26, 2024
Update
- The single-chain swap API now supports an automatic slippage feature.
### November 25, 2024
Update
- The single-chain swap API parameter `swapReceiverAddress` now supports the Solana chain. It is now possible to specify a different recipient address for a swap transaction on the Solana chain.
### November 12, 2024
Update
- Added a new referral wallet address parameter for the single-chain swap API. Both Sol and SPL tokens now support direct use of wallet addresses for referral fee collection.
- APIs for retrieving the single-chain liquidity list and the cross-chain bridge list now support protocol logos.
- Single-chain quote and swap APIs now recognize and return risk tokens with honeypot mechanisms or tokens with 100% buy/sell tax.
### October 29, 2024
Update
- Single-chain swaps now support the Sui chain.
### July 1, 2024
Update
- The ETH dexRouter contract 0xf3de3c0d654fda23dad170f0f320a92172509127 will no longer function normally after August 1, 2024. Please switch to the latest version.
### June 19, 2024
Update
- Swap API DEX Router eth contract address update
### May 7, 2024
Update
- Solana mainnet supports commission-sharing
### April 19, 2024
Update
- Cross-chain swap support SUI chain
### April 19, 2024
Update
- Cross-chain swap support X Layer chain
### April 12, 2024
Update
- Cross-chain slippage request parameter range adjusted to 0.002-0.5
- Cross-chain swap support Merlin chain
### April 12, 2024
Update
- Added new response parameters (quoteCompareList) to DEX swap endpoint
### April 8, 2024
Update
- Added new response parameters (toTokenReferrerAddress) to DEX swap endpoint
### April 2, 2024
Update
- Updated DEX swap trading support Merlin Chain
### March 22, 2024
Update
- Added new response parameters (callDataMemo) to DEX swap endpoint
### March 22, 2024
Update
- Added new request parameters (sort) to cross-chain Get route information and Cross-chain swap endpoint
- DEX XBridge contract address update
### March 13, 2024
Update
- Added new response parameters (sourceChainGasfee, destinationChainGasfee, crossChainFee) to cross-chain Get transaction status endpoint
- Added new request parameters (onlyBridge) and new response parameters (minmumReceive,maxPriorityFeePerGas)
- Cross-chain API Smart contract part added cross-chain contract address (DEX XBridge contract address)
- Cross-chain API added new Get the list of tokens supported by the cross-chain bridges endpoint
### March 07, 2024
Update
- Added new request parameters (priceImpactProtectionPercentage) to Cross-chain & Single-chain swap endpoint
### February 29, 2024
Update
- Added new response parameters (decimal) to DEX swap endpoint
### February 22, 2024
Update
- Added new response parameters (maxPriorityFeePerGas) to DEX swap endpoint
### February 01, 2024
Update
- Updated DEX swap trading support solana, added new request parameters (solTokenAccountAddress)
- Added DEX swap trading support solana quick start
### January 17, 2024
Update
- Added new request parameters (feePercent, referrerAddress) to Cross-chain swap endpoint
### January 11, 2024
Update
- Cross-chain slippage range adjusted to 0.5-0.5
### January 02, 2024
Update
- Added new request parameters (receiveAddress) to Cross-chain swap endpoint
- Added new request parameters (chainld) to cross-chain Get transaction status endpoint
### December 28, 2023
Update
- Launched the first edition of OKX Web3 DeFi API documentation
- Added functionality for investment, redemption, and reward claiming processes via DeFi API
- Added methods for querying information, calculating estimated data, generating transaction data, and querying user information
### December 20, 2023
Update
- Added new response parameters (crossChainFeeTokenAddress) to cross-chain Get route information endpoint and Cross-chain swap endpoint
- Added new response parameters (status) to cross-chain Get transaction status endpoint
### December 14, 2023
Update
- Added DEX Iframe
### December 14, 2023
Update
- Added new optional parameter (swapReceiverAddress) to DEX swap interface
### December 08, 2023
Update
- Added DEX limit order list query
- Added DEX limit order get cancellation calldata
### December 06, 2023
Update
- Added new response parameters (toDexRouterList) to cross-chain Get route information endpoint
- Added new response parameters (toAmount, errorMsg) to cross-chain Get transaction status endpoint
- Added cross-chain refund issues to cross-chain FAQ
### November 24, 2023
Update
- The Order API has been expanded with a new listing module, enabling listings on OpenSea and OKX.
- Added more detailed description to order structure
### November 23, 2023
Update
- Added DEX cross-chain API quick start guidelines
### November 21, 2023
Update- Added DEX cross-chain API
quick start guidelines
### November 15, 2023
Update
- Added new response parameters (crossChainFee) to cross chain Get route information endpoint cross-chain/quote
- Added new response parameters (crossChainFee) to cross chain Cross-chain swap endpoint cross-chain/quote
### November 10, 2023
Update
- Added DEX API quick start guidelines
### November 7, 2023
Update
- Added DEX cross-chain aggregator API
### October 31, 2023
Update
- Added DEX limit order API
### October 23, 2023
Update
- Added smart contract information, including contract addresses and ABI
### September 27, 2023
Update
- Updated the content of integrating DApps with OKX Wallet on the mobile app
- Added guide and sub pages for integrating DApps with OKX Wallet on the mobile app
### August 17, 2023
Update
- Updated the API key authentication mechanism. Moving forward, all endpoints will require an API key generated from the Web3 developer portal.
- Updated the URI of all endpoints to reflect the latest versioning
- Updated error codes
Update
- Added API key authentication. Moving forward, all endpoints will require an API key generated from the Web3 developer portal
- Updated error codes
Update
- Updated the API key authentication mechanism. Moving forward, all Web3 API endpoints will require an API key generated from the Web3 developer portal
- Updated the URI of all endpoints to reflect the latest versioning
### May 29, 2023
Update- Added new BRC-20
endpoint: ``brc20/token-list`` - Added new response parameters (index, location)
to BRC-20 endpoint ``brc20/transaction-list`` - Added new response parameters
(location) to BRC-20 endpoint ``brc20/inscriptions-list``
- [Overview](https://web3.okx.com/onchainos/dev-docs/wallet/product-and-service.md)
# Overview
Onchain OS offers two services: Agentic Wallet and Wallet API.
## Agentic Wallet
The most powerful Agentic wallet — private keys are fully protected by TEE at all times, AI Agents trade directly via natural language, backed by OKX's trading infrastructure and security systems serving tens of millions of users worldwide.
### Core Capabilities
- **Security Architecture** — Private key generation, storage, and signing are completed entirely within a TEE secure environment. Automatic identity verification, blacklisted address blocking, and risky token alerts before every transaction — anomalies are blocked instantly.
- **Multi-Chain Coverage** — Supports X Layer, Ethereum, Solana, and other major chains, with more being added.
- **Multi-Address Management** — Create a wallet with just an email login. Derive up to 50 sub-wallets per account for parallel operations.
- **Zero Gas on X Layer** — 0 Gas fees on X Layer, ideal for high-frequency trading scenarios.
## Wallet API
Wallet API provides AI Agents, trading bots, and Web3 applications with complete on-chain asset querying and transaction execution — check balances, send transactions, and track on-chain activity, all through one unified interface.
- **Check Balance** — Query total asset value, multi-chain token balances, and specific token holdings for any wallet in real time.
- **Broadcast Transactions** — Estimate gas, simulate execution, broadcast transactions, and track order status in real time.
- **Check Transaction History** — Query complete transaction records by address, with filters for asset type, transaction direction, and time range.
- [Supported Networks](https://web3.okx.com/onchainos/dev-docs/wallet/supported-networks.md)
# Supported Networks
## EVM-Compatible Networks
| Chain | Agentic Wallet | Wallet API | ChainIndex |
| ------------- | -------------- | ---------- | ---------- |
| Arbitrum One | ✓ | ✓ | 42161 |
| Avalanche C | ✓ | ✓ | 43114 |
| Base | ✓ | ✓ | 8453 |
| Blast | ✓ | ✓ | 81457 |
| BNB Chain | ✓ | ✓ | 56 |
| Conflux | ✓ | ✓ | 1030 |
| Cronos | - | ✓ | 25 |
| Ethereum | ✓ | ✓ | 1 |
| Fantom | ✓ | ✓ | 250 |
| Linea | ✓ | ✓ | 59144 |
| Manta Pacific | - | ✓ | 169 |
| Mantle | - | ✓ | 5000 |
| Merlin | - | ✓ | 4200 |
| Metis | - | ✓ | 1088 |
| Monad | ✓ | ✓ | 143 |
| Optimism | ✓ | ✓ | 10 |
| Plasma | - | ✓ | 9745 |
| Polygon | ✓ | ✓ | 137 |
| Polygon zkEVM | - | ✓ | 1101 |
| Scroll | ✓ | ✓ | 534352 |
| Sonic | ✓ | ✓ | 146 |
| Uni Chain | - | ✓ | 130 |
| X Layer | ✓ | ✓ | 196 |
| ZetaChain | - | ✓ | 7000 |
| zkSync Era | ✓ | ✓ | 324 |
## Non-EVM Networks
| Chain | Agentic Wallet | Wallet API | ChainIndex |
| ------- | -------------- | ---------- | ---------- |
| Bitcoin | - | ✓ | 0 |
| Solana | ✓ | ✓ | 501 |
| SUI | Coming Soon | ✓ | 784 |
| Ton | Coming Soon | ✓ | 607 |
| Tron | Coming Soon | ✓ | 195 |
- [Agentic Wallet](https://web3.okx.com/onchainos/dev-docs/wallet/agentic-wallet.md)
# Agentic Wallet
Agentic Wallet is a dedicated onchain wallet for AI Agents — turning them from query assistants into onchain executors that can hold assets, sign, and submit transactions.
## Why OKX Agentic Wallet
- **Quick setup** — Log in with email to create a wallet instantly — no seed phrase, no key configuration needed.
- **Private keys are untouchable** — Generated and stored within a TEE secure environment, AI Agents can trade but cannot access the keys — including OKX.
- **OKX-level execution capability** — Backed by the trading engine and security system serving tens of millions of users worldwide.
- **Security controls** — Transactions go through risk simulation and scoring before execution, keeping everything safe and auditable.
## Core Capabilities
- **Security Architecture** — Private key generation, storage, and signing are all completed within a TEE secure environment. Automatic identity verification before transactions, blacklisted address interception, risky token alerts, and immediate blocking upon anomaly detection.
- **Multi-chain Coverage** — Covers nearly 20 chains, supporting transactions and transfers on X Layer, Ethereum, Solana, and other major chains, with continuous expansion.
- **Multi-address Management** — A single wallet can derive up to 50 sub-wallets, supporting parallel multi-address operations.
- **Zero Gas on X Layer** — Zero gas fees for operations on X Layer, ideal for high-frequency trading scenarios.

- [Quickstart](https://web3.okx.com/onchainos/dev-docs/wallet/agentic-quickstart.md)
# Quickstart
AI Agents need a wallet to truly interact with the chain securely — holding assets, signing, and submitting transactions. With Agentic Wallet installed, your AI Agent can swap tokens, check balances, and transfer funds directly in conversation, no tool-switching, no manual signing.
Don't have a wallet yet? Start with setup:
Already have a wallet? Jump right in:
- [Install Your Agentic Wallet](https://web3.okx.com/onchainos/dev-docs/wallet/install-your-agentic-wallet.md)
# Install Your Agentic Wallet
Provide your AI Agent with onchain wallet capabilities — private keys protected by TEE, automatic risk detection before transactions, from wallet setup to onchain trading all through conversation.
## Preparation: Install an AI Agent
This guide uses [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) as an example. Onchain OS supports all mainstream agents ([Cursor](https://docs.cursor.com/get-started/installation), [OpenClaw](https://docs.openclaw.ai/), etc.).
## Step 1: Add Onchain OS to Your Agent
Tell your Agent:
```plain text
Run npx skills add okx/onchainos-skills to install Onchain OS skills.
Note: Please install to the skill directory corresponding to your current Agent.
```
The Agent will complete the installation automatically. For more details, see [Github](https://github.com/okx/onchainos-skills).
## Step 2: Create a Wallet
Tell your Agent:
```plain text
Log in to Agentic Wallet with email
```
Enter your email and wait for the verification code:
```plain text
```
Enter the verification code:
```plain text
```
Once verified, the Agent will automatically create a wallet on first login:
```plain text
Wallet created successfully!
EVM Address:
Solana Address:
```
Private keys are generated and stored entirely within the TEE — never exposed to anyone, including your Agent. Logging in with the same email restores the existing wallet automatically.
- [Run Your First AI Agent](https://web3.okx.com/onchainos/dev-docs/wallet/run-your-first-ai-agent.md)
# Run Your First AI Agent
After installing and creating your wallet, fund your Agentic Wallet, then use natural language to have your Agent execute onchain trades.
## Fund Your Wallet
Transfer assets to your Agentic Wallet address via an on-chain wallet or exchange.
## Check Balance
```plain text
Check my wallet balance.
```
Once the funds arrive, you can start onchain trading! Try using natural language to have your Agent execute trades!
## Trading Example
User tells the Agent to filter tokens and buy:
```plain text
Find tokens on Solana that KOLs are buying, market cap under $5M, at least 2 KOL wallets buying, and buy 1 USDT worth
```
Agent filters tokens and recommends the best pick:
```plain text
Token Market Cap Smart Money Wallets Sell Ratio
$9.99M 9 wallets (×9 signals) 9.99%
$9.99M 9 wallets (×9 signals) 99.99%
is the best choice — lowest sell ratio and most signal occurrences.
Buying 1 USDT worth.
```
Agent gets trade quote and asks for confirmation:
```plain text
Trade quote:
Details Value
Sell 1 USDT
Buy ~99.99
Price 1 ≈ $0.0099
Route USDT → ... →
Price Impact -0.99%
Honeypot No
Tax 0%
Gas Fee ~$0.009
Confirm trade?
```
User confirms the trade:
```plain text
Confirm
```
Agent broadcasts transaction:
```plain text
Trade successful!
Details Value
Sell 1 USDT
Buy ~99.99
Route USDT → ... →
Price Impact -0.99%
Slippage 1%
Order ID
Wallet (Wallet 1)
Transaction broadcast on Solana!
```
The above example is for demonstration purposes only and does not constitute investment advice. Please make trading decisions based on your own judgment.
- [Skills](https://web3.okx.com/onchainos/dev-docs/wallet/agentic-wallet-skills.md)
# Skills
## Overview
Agentic Wallet provides the following features, covering the full workflow from login authentication to onchain transaction execution. Tell the Agent what you want to do in natural language, and it will automatically select and execute the corresponding feature.
## Features at a Glance
| Feature | Description |
|---|---|
| Wallet Login Authentication | Email OTP or API Key authentication, with verification code validation, status queries, and logout |
| Wallet Management | Create up to 50 sub-wallets, with default wallet configuration |
| Portfolio Query | Multi-chain balance queries, total asset valuation, single token balance, supporting 17 chains |
| Security Detection | Token honeypot detection, DApp phishing scanning, transaction risk interception, approval management |
| Transfer | Send tokens to specified addresses, batch transfers supported, automatic security detection |
| Transaction History | Transaction record queries, filterable by chain / token / direction |
## Wallet Login Authentication
Log in to your wallet via email OTP or API Key. This is a prerequisite for all wallet operations.
```shell
Log in to my wallet with email
# Calls wallet login → wallet verify
```
```shell
Create a wallet with API Key
# Calls wallet login (apikey mode)
```
```shell
Am I logged in?
# Calls wallet status
```
```shell
Log out of my wallet
# Calls wallet logout
```
## Wallet Management
Create, derive, and switch wallets. Each login method supports up to 50 sub-wallets, with each sub-wallet generating both EVM and Solana addresses simultaneously.
```shell
Check wallet status
# Calls wallet status
```
```shell
Show all wallets
# Calls wallet balance --all
```
```shell
Create a new sub-wallet for me
# Calls wallet add
```
```shell
Switch to wallet 2
# Calls wallet switch
```
```shell
Show my deposit address
# Calls wallet balance
```
```shell
Operate on Solana
# Calls wallet chains
```
## Portfolio Query
Query balances and assets. Query your own wallet after logging in, or query any address without logging in.
```shell
Check my balance
# Calls wallet balance
```
```shell
How much OKB do I have?
# Calls wallet balance --token-address
```
```shell
Check the assets of this address 0x1234...
# Calls wallet balance
```
```shell
How much are the total assets of this address worth?
# Calls wallet balance
```
## Security Detection
Comprehensive security protection to ensure every operation is safe.
```shell
Is this token safe?
# Calls security token-scan
```
```shell
Is this website safe?
# Calls security dapp-scan
```
```shell
Check my approvals
# Calls security approvals
```
```shell
Is this transaction safe?
# Calls security tx-scan
```
```shell
Is this signature request safe?
# Calls security sig-scan
```
## Transfer
Send tokens to a specified address, with batch consolidation support.
```shell
Send 0.1 ETH to 0x1234...
# Calls wallet send
```
```shell
Consolidate all wallets' ETH to 0x1234...
# Calls wallet balance --all → wallet send for each
```
## Transaction History
View transaction records, filterable by chain, token, and direction. Shows the most recent 20 entries by default, with pagination support.
```shell
Show me my recent transactions
# Calls wallet history
```
```shell
Show transactions on Arbitrum
# Calls wallet history --chain
```
```shell
Show USDC transfer history
# Calls wallet history
```
```shell
Show only incoming transfers
# Calls wallet history
```
```shell
Look up this transaction 0xabc123...
# Calls wallet history --tx-hash
```
## Cross-Feature Composite Workflows
A single instruction can automatically chain multiple features together. The Agent plans and executes in order — no need to break down steps manually.
```shell
Check my holdings and sell the ones that dropped
# Check holdings → Analyze → Trade
```
```shell
Find a promising meme coin on Solana and buy some
# Search tokens → Check security → Buy
```
```shell
Execute this transaction safely
# Check Gas → Simulate → Security scan → Execute → Track
```
```shell
Transfer 10 USDC from my main wallet to wallet 3, then use wallet 3 to buy ETH
# Multi-wallet collaboration
```
```shell
Run a comprehensive security check and summarize the report
# Full security audit workflow
```
```shell
I want to trade on uniswap.org, is it safe?
# DApp security + interaction
```
- [Wallet API](https://web3.okx.com/onchainos/dev-docs/wallet/wallet-api-introduction.md)
# Wallet API
Wallet API is the core infrastructure module of OnchainOS, built for DApp developers — providing complete onchain asset querying and transaction execution capabilities. Check balances, send transactions, and track onchain activity, all through a unified multi-chain interface.
- **Check Balance** — Query any wallet's total asset value, multi-chain token balances, and specific token holdings in real time. Automatically fetch the latest onchain state before trading.
- **Transaction Gateway** — Estimate gas, simulate execution, broadcast transactions, and track order status in real time. Combines OKX's proprietary nodes with third-party nodes for smart multi-path broadcasting that improves onchain success rates.
- **Transaction History** — Query complete transaction records by address or view specific transaction details. Supports multiple chains and transaction types, with filtering by asset type, direction, and time range.
- [Check Balance](https://web3.okx.com/onchainos/dev-docs/wallet/balance-api-overview.md)
# Check Balance
Check Balance API retrieves asset balances for any wallet address. It supports querying total portfolio value, all token balances, or a specific token balance across multiple chains. Use it to build wallet dashboards, portfolio trackers, and asset management tools.
## Key Capabilities
### 1. Flexible Balance Query
* Query total asset value across all supported chains by wallet address.
* Retrieve a full list of token balances or query a specific token directly.
* Supports native tokens and ERC-20/SPL tokens.
### 2. Real-Time Data
* Returns up-to-date balances with current token prices.
* Covers 130+ chains in a single integration.
* Structured responses for easy parsing and display.
- [API Reference](https://web3.okx.com/onchainos/dev-docs/wallet/balance-api-reference.md)
# API Reference
- [Get Supported Chains](https://web3.okx.com/onchainos/dev-docs/wallet/balance-api-chains.md)
{/* api-page */}
# Get Supported Chains
Retrieve information on chains supported by the DEX Balance endpoint
### Request URL
GET `https://web3.okx.com/api/v6/dex/balance/supported/chain`
## Request Parameters
None
## Response Parameters
| Parameter | Type | Description |
|-----------|--------|-------------------|
| name | String | Chain name |
| logoUrl | String | Chain logo URL |
| shortName | String | Chain short name |
| chainIndex| String | Chain unique identifier |
## Request Example
``` shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/balance/supported/chain' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"name": "Ethereum",
"logoUrl": "http://www.eth.org/eth.png",
"shortName": "ETH",
"chainIndex": "1"
}
],
"msg": ""
}
```
- [Get Total Value](https://web3.okx.com/onchainos/dev-docs/wallet/balance-api-total-value.md)
{/* api-page */}
# Get Total Value
Retrieve the total balance of all tokens and DeFi assets under an account.
### Request URL
GET `https://web3.okx.com/api/v6/dex/balance/total-value-by-address`
## Request Parameters
| Parameter | Type | Required | Description |
| ------------------| ------- | -------- | ------------------------------------------------------------------ |
| address | String | Yes | Get the total valuation for the address |
| chains | String | Yes | Filter chains for which to query total assets, separated by ",". Supports up to 50 chains.. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| assetType | String | No | Query balance type. Default is to query all asset balances. `0`: Query total balance for all assets, including tokens and DeFi assets. `1`: Query only token balance. `2`: Query only DeFi balance. |
| excludeRiskToken | Boolean | No | Option to filter out risky airdrop & honeypot tokens. Default is to filter. `true`: filter out, `false`: do not filter out It supports only `ETH`、`BSC`、`SOL`、`BASE` for honeypot tokens, more chains will be supported soon. |
## Response Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| totalValue | String | Total asset balance based on the query type, returned in USD |
## Request Example
``` shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/balance/total-value-by-address?address=0x0b32aa5c1e71715206fe29b7badb21ad95f272c0&chains=1&assetType=0' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
``` json
{
"code": "0",
"msg": "success",
"data": [
{
"totalValue": "1172.895057177065864522056725546579939398"
}
]
}
```
- [Get Total Token Balances](https://web3.okx.com/onchainos/dev-docs/wallet/balance-api-all-token-balances.md)
{/* api-page */}
# Get Total Token Balances
Retrieve the list of token balances for an address across multiple chains or specified chains.
### Request URL
GET `https://web3.okx.com/api/v6/dex/balance/all-token-balances-by-address`
## Request Parameters
| Parameter | Type | Required | Description |
|----------------|--------|----------|-----------------------------------------|
| address | String | Yes | Address |
| chains | Array | Yes | When filtering the chains for querying asset details, multiple chains should be separated by commas (`,`). A maximum of 50 chains is supported. e.g., `1`: Ethereum. See more [here](../home/supported-chain).|
| excludeRiskToken | String | No | Option to filter out risky airdrop & honeypot tokens. Default is to filter. `0`: Filter out `1`: Do not filter out It supports only `ETH`、`BSC`、`SOL`、`BASE` for honeypot tokens, more chains will be supported soon. |
## Response Parameters
| Parameter | Type | Description |
|---------------|--------|------------------------------------------|
| tokenAssets | Array | List of token balances |
| >chainIndex | String | Unique identifier for the chain |
| >tokenContractAddress | String | Contract address |
| >address | String | Address |
| >symbol | String | Token symbol |
| >balance | String | Token balance |
| >rawBalance | String | Raw balance of token address. For unsupported chains, this field is empty. More chains will be supported soon. |
| >tokenPrice | String | Token unit value, priced in USD |
| >isRiskToken | Boolean| `true`: flagged as a risky airdrop & honeypot token `false`: not flagged as a risky airdrop & honeypot token |
## Request Example
``` shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/balance/all-token-balances-by-address?address=0xEd0C6079229E2d407672a117c22b62064f4a4312&chains=1' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
``` json
{
"code": "0",
"msg": "success",
"data": [
{
"tokenAssets": [
{
"chainIndex": "1",
"tokenContractAddress": "0x386ae941d4262b0ee96354499df2ab8442734ec0",
"symbol": "PT-sUSDE-27FEB2025",
"balance": "47042180.520700015",
"tokenPrice": "0.968391562089677097",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0",
"symbol": "wstETH",
"balance": "7565.892480395067",
"tokenPrice": "4321.611627695311",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
"symbol": "WBTC",
"balance": "329.10055205",
"tokenPrice": "98847.8",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x23878914efe38d27c4d67ab83ed1b93a74d4086a",
"symbol": "aEthUSDT",
"balance": "30057379.938443",
"tokenPrice": "0.99978",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x657e8c867d8b37dcc18fa4caead9c45eb088c642",
"symbol": "eBTC",
"balance": "271.94970471",
"tokenPrice": "99094.345321371",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x4d5f47fa6a74757f35c14fd3a6ef8e3c9bc514e8",
"symbol": "aEthWETH",
"balance": "6080.001975381972",
"tokenPrice": "3634.32",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xe00bd3df25fb187d6abbb620b3dfd19839947b81",
"symbol": "PT-sUSDE-27MAR2025",
"balance": "19016580.895408865",
"tokenPrice": "0.952031186961110727",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xa17581a9e3356d9a858b789d68b4d866e593ae94",
"symbol": "cWETHv3",
"balance": "3000.000734740809",
"tokenPrice": "3663.74",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x9d39a5de30e57443bff2a8307a4256c8797a3497",
"symbol": "sUSDe",
"balance": "4863500.628333919",
"tokenPrice": "1.144688569528375454",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xec5a52c685cc3ad79a6a347abace330d69e0b1ed",
"symbol": "PT-LBTC-27MAR2025",
"balance": "46.02912324",
"tokenPrice": "97165.169717785655331396",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x8236a87084f8b84306f72007f36f2618a5634494",
"symbol": "LBTC",
"balance": "38.09998",
"tokenPrice": "99187.19184268864",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xbeef047a543e45807105e51a8bbefcc5950fcfba",
"symbol": "steakUSDT",
"balance": "482651.8612595832",
"tokenPrice": "1.063",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x4c9edd5852cd905f086c759e8383e09bff1e68b3",
"symbol": "USDe",
"balance": "69564",
"tokenPrice": "0.99977",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x8be3460a480c80728a8c4d7a5d5303c85ba7b3b9",
"symbol": "sENA",
"balance": "42294.989425",
"tokenPrice": "1.19",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "",
"symbol": "ETH",
"balance": "8.135546539084933",
"tokenPrice": "3638.63",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xbf5495efe5db9ce00f80364c8b423567e58d2110",
"symbol": "ezETH",
"balance": "5.270854886240325",
"tokenPrice": "3763.152404188635320082",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x6b175474e89094c44da98b954eedeac495271d0f",
"symbol": "DAI",
"balance": "1196.2693184870445",
"tokenPrice": "1.0002",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xc00e94cb662c3520282e6f5717214004a7f26888",
"symbol": "COMP",
"balance": "0.007643",
"tokenPrice": "84.43345772756197",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x9abfc0f085c82ec1be31d30843965fcc63053ffe",
"symbol": "Q*",
"balance": "900",
"tokenPrice": "0.000419255747329174",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xa1290d69c65a6fe4df752f95823fae25cb99e5a7",
"symbol": "rsETH",
"balance": "0.00007090104120006",
"tokenPrice": "3765.640772858747921444",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x56015bbe3c01fe05bc30a8a9a9fd9a88917e7db3",
"symbol": "CAT",
"balance": "0.42",
"tokenPrice": "0.06242994543936436",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xec53bf9167f50cdeb3ae105f56099aaab9061f83",
"symbol": "EIGEN",
"balance": "0.002496149915967488",
"tokenPrice": "4.018538365202288",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x58d97b57bb95320f9a05dc918aef65434969c2b2",
"symbol": "MORPHO",
"balance": "0.001409373661132556",
"tokenPrice": "3.3568669630371337",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xaf5191b0de278c7286d6c7cc6ab6bb8a73ba2cd6",
"symbol": "STG",
"balance": "0.000009547670354338",
"tokenPrice": "0.49707759500034454",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xba3335588d9403515223f109edc4eb7269a9ab5d",
"symbol": "GEAR",
"balance": "0.000009005734110189",
"tokenPrice": "0.012329598382413718",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x35fa164735182de50811e8e2e824cfb9b6118ac2",
"symbol": "eETH",
"balance": "0.000000000000000001",
"tokenPrice": "3637.93",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xae7ab96520de3a18e5e111b5eaab095312d7fe84",
"symbol": "stETH",
"balance": "0.000000000000000001",
"tokenPrice": "3637.93",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xa3931d71877c0e7a3148cb7eb4463524fec27fbd",
"symbol": "sUSDS",
"balance": "67435907.43236613",
"tokenPrice": "0",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xa8705a14c79fa1cded70875510211fec822b3c30",
"symbol": "BEEX",
"balance": "5000000",
"tokenPrice": "0",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xabc0abace9fb9625fcefbedc423e8f94225bd251",
"symbol": "TANUKI",
"balance": "3548102.746002181",
"tokenPrice": "0",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x356b8d89c1e1239cbbb9de4815c39a1474d5ba7d",
"symbol": "syrupUSDT",
"balance": "1750000",
"tokenPrice": "0",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
}
]
}
]
}
```
- [Get Specific Token Balance](https://web3.okx.com/onchainos/dev-docs/wallet/balance-api-token-balances.md)
{/* api-page */}
# Get Specific Token Balance
Query the balance of a specific token under an address.
### Request URL
POST `https://web3.okx.com/api/v6/dex/balance/token-balances-by-address`
## Request Parameters
| Parameter | Type | Required | Description |
|----------------|--------|----------|-----------------------------------------|
| address | String | Yes | Address |
| tokenContractAddresses | Array | Yes | List of tokens addresses to query. Maximum of 20 items. |
| >chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| >tokenContractAddress | String | Yes | Token address. `1`: Pass an empty string `""` to query the native token of the corresponding chain. `2`: Pass the specific token contract address to query the corresponding token. |
| excludeRiskToken | String | No | Option to filter out risky airdrop & honeypot tokens. Default is to filter `0`: Filter out `1`: Do not filter out It supports only `ETH`、`BSC`、`SOL`、`BASE` for honeypot tokens, more chains will be supported soon. |
## Response Parameters
| Parameter | Type | Description |
|--------------|--------|-----------------------------------------|
| tokenAssets | Array | List for token balances |
| >chainIndex | String | Unique identifier for the chain |
| >tokenContractAddress | String | Token address.If the return is an empty string `""`, it means the query is for the native token of the corresponding blockchain. |
| >address | String | Address |
| >symbol | String | Token symbol |
| >balance | String | Token balance. |
| >rawBalance | String | Raw balance of token address. For unsupported chains, this field is empty. More chains will be supported soon. |
| >tokenPrice | String | Token price in USD |
| >isRiskToken | Boolean| `true`: flagged as a risky airdrop & honeypot token `false`: not flagged as a risky airdrop & honeypot token |
## Request Example
```shell
curl --location --request POST 'https://web3.okx.com/api/v6/dex/balance/token-balances' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z' \
--data-raw '{
"address": "0x50c476a139aab23fdaf9bca12614cdd54a4244e3",
"tokenContractAddresses": [
{
"chainIndex": "1",
"tokenContractAddress": ""
}
]
}'
```
## Response Example
``` json
{
"code": "0",
"msg": "success",
"data": [
{
"tokenAssets": [
{
"chainIndex": "1",
"tokenContractAddress": "",
"symbol": "eth",
"balance": "0",
"tokenPrice": "3640.43",
"isRiskToken": false,
"rawBalance": "",
"address": ""
}
]
}
]
}
```
- [Error Codes](https://web3.okx.com/onchainos/dev-docs/wallet/balance-error-code.md)
# Error Codes
| Code | HTTP status | Message |
|-------|-------------|-----------------------------------------------------------------|
| 50014 | 400 | param \{param0\} is invalid |
| 50001 | 200 | Service temporarily unavailable. Try again later |
| 81001 | 200 | Incorrect parameter: : \{param0\} |
| 50011 | 429 | Too Many Requests |
| 81104 | 200 | Chain not support |
| 81001 | 200 | Required request body is missing |
- [Broadcast Transactions](https://web3.okx.com/onchainos/dev-docs/wallet/onchain-gateway-api-overview.md)
# Broadcast Transactions
Transaction API supports onchain transaction simulation and broadcasting. It combines OKX Web3's proprietary RPC nodes with premium third-party nodes to enable intelligent broadcasting, lower failure rates, and faster confirmation speeds. Pair it with the Swap and Cross-Chain APIs to build a complete experience — no extra external resources needed.
## Key Capabilities
### 1. High-Availability Hybrid Node Architecture
* Proprietary multi-chain node clusters for stable, high-performance service.
* Third-party premium nodes integrated to form a redundant, resilient network.
* Real-time health monitoring, dynamic load balancing, and sub-second failover.
### 2. Intelligent Multi-Broadcasting Engine
* Broadcasts transactions across multiple node networks simultaneously.
* Distributed propagation algorithms that raise onchain success rates.
* Priority block-packaging for Ethereum, BNB Chain, Solana, and more.
- [API Reference](https://web3.okx.com/onchainos/dev-docs/wallet/onchain-gateway-api-reference.md)
# API Reference
- [Get Supported Chains](https://web3.okx.com/onchainos/dev-docs/wallet/onchain-gateway-api-chains.md)
{/* api-page */}
# Get Supported Chains
Retrieve information on chains supported by Onchain gateway API
### Request URL
GET `https://web3.okx.com/api/v6/dex/pre-transaction/supported/chain`
## Request Parameters
None
## Response Parameters
| Parameter | Type | Description |
|-----------|--------|-------------------|
| name | String | Chain name |
| logoUrl | String | Chain logo URL |
| shortName | String | Chain short name |
| chainIndex| String | Chain unique identifier |
## Request Example
``` shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/pre-transaction/supported/chain' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"name": "Ethereum",
"logoUrl": "http://www.eth.org/eth.png",
"shortName": "ETH",
"chainIndex": "1"
}
],
"msg": ""
}
```
- [Get Gas Price](https://web3.okx.com/onchainos/dev-docs/wallet/onchain-gateway-api-gas-price.md)
{/* api-page */}
# Get Gas Price
Dynamically obtain estimated gas prices for various chains.
### Request URL
GET `https://web3.okx.com/api/v6/dex/pre-transaction/gas-price`
## Request Parameters
| Parameter | Type | Required | Description |
|------------|--------|----------|-----------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
## Response Parameters
### EVM & Tron
| Parameter | Type | Description |
|------------------|---------|-----------------------------------|
| normal | String | Medium gas price. For EVM, it is in wei. For Tron,it is in SUN |
| min | String | Low gas price. For EVM, it is in wei. For Tron,it is in SUN |
| max | String | High gas price. For EVM, it is in wei. For Tron,it is in SUN |
| supporteip1559 | Boolean | Whether supports 1559 |
| eip1559Protocol | Object | 1559 protocol |
### eip1559 Protocol
| Parameter | Type | Description |
|---------------------|--------|--------------------------------------|
| eip1559Protocol | Object | Structure of 1559 protocol |
| >suggestBaseFee | String | Suggested base fee = base fee * 1.25, in wei |
| >baseFee | String | Base fee, in wei |
| >proposePriorityFee | String | Medium priority fee, in wei |
| >safePriorityFee | String | Low priority fee, in wei |
| >fastPriorityFee | String | High priority fee, in wei |
### Solana
| Parameter | Type | Description |
|------------------|---------|-------------------|
| priorityFee | String | Priority fee per compute unit. Only applicable to Solana |
| >proposePriorityFee | String | Medium priority fee in microlamports.( it is also called Medium compute unit price ) 80th percentile|
| >safePriorityFee | String | Low priority fee in microlamports.( it is also called Low compute unit price ) 60th percentile|
| >fastPriorityFee | String | High priority fee in microlamports.( it is also called High compute unit price ) 95th percentile|
| >extremePriorityFee | String | Extreme High priority fee in microlamports.( it is also called Extreme High compute unit price ) 99th percentile |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/pre-transaction/gas-price?chainIndex=1' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"normal" : "21289500000", // Medium gas price
"min" : "15670000000", // Low gas price
"max" : "29149000000", // High gas price
"supportEip1559" : true, // Whether supports 1559
"eip1599Protocol": {
"suggestBaseFee" : "15170000000", // Suggested base fee
"baseFee" : "15170000000", // Base fee
"proposePriorityFee" : "810000000", // Medium priority fee
"safePriorityFee" : "500000000", // Low priority fee
"fastPriorityFee" : "3360000000" // High priority fee
},
"priorityFee":{}
}
],
"msg": ""
}
```
- [Get Gas Limit](https://web3.okx.com/onchainos/dev-docs/wallet/onchain-gateway-api-gas-limit.md)
{/* api-page */}
# Get Gas Limit
Retrieve estimated Gas Limit consumption through pre-execution of transaction information.
### Request URL
POST `https://web3.okx.com/api/v6/dex/pre-transaction/gas-limit`
## Request Parameters
| Parameter | Type | Required | Description |
|------------|--------|----------|-----------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| fromAddress| String | Yes | From address. For `transfer`,`Swap`, `Approve`, it is a wallet address |
| toAddress | String | Yes | To address. For `transfer`, it can be a token address or wallet address. For `Swap`, it should be OKX DEX router address. For `Approve`, it is a token address |
| txAmount | String | No | Transaction amount. Default value: `0`. 1. For **Native token transactions** ( where the `fromToken` is native token. e.g., Ethereum), the txAmount can be set to the native token quantity, or retrieved from [/swap](./dex-swap) api(e.g., `txAmount = swapResponse.tx.value`). 2.For **non-native token transactions**, set `txAmount` to `0`. The valle must use base unit of the native token, e.g., wei for ETH |
| extJson | Object | No | Additional parameters for calldata and other information |
extJson
| Parameter | Type | Required | Description |
|-----------|--------|----------|-------------|
| inputData | String | No | Calldata |
## Response Parameters
| Parameter | Type | Description |
|-----------|--------|-------------------|
| gasLimit | String | Estimated gas limit |
## Request Example
``` shell
curl --location --request POST 'https://web3.okx.com/api/v6/dex/pre-transaction/gas-limit' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z' \
--data-raw '{
"fromAddress": "0x383c8208b4711256753b70729ba0cf0cda55efad",
"toAddress": "0x4ad041bbc6fa102394773c6d8f6d634320773af4",
"txAmount": "31600000000000000",
"chainIndex": "1",
"extJson": {
"inputData":"041bbc6fa102394773c6d8f6d634320773af4"
}
}'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"gasLimit": "652683"
}
],
"msg": ""
}
```
- [Simulate Transactions](https://web3.okx.com/onchainos/dev-docs/wallet/onchain-gateway-api-simulate-transaction.md)
{/* api-page */}
# Simulate Transactions
Simulate a blockchain transaction before executing it to see the expected outcomes and potential risks.
Transaction simulate API is available to our whitelisted customers only. If you are interested, please contact us dexapi@okx.com.
### Request URL
GET `https://web3.okx.com/api/v6/dex/pre-transaction/simulate`
## Request Parameters
| Parameter | Type | Required | Description |
|--------------|--------|----------|---------------------------------------------------------------------------------------|
| fromAddress | String | Yes | Source address. For `Swap`, `Approve`, it is a wallet address |
| toAddress | String | Yes | Destination address. For `Swap`, it should be OKX DEX router address. For `Approve`, it is a token address |
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum See [Supported Chains](../home/supported-chain) for more. It supports EVM、SOL、SUI, more chains will be supported soon. |
| txAmount | String | No | Transaction amount. Default value: `0`. 1. For **Native token transactions** ( where the `fromToken` is native token. e.g., Ethereum), the txAmount can be set to the native token quantity, or retrieved from [/swap](./dex-swap) api(e.g., `txAmount = swapResponse.tx.value`). 2.For **non-native token transactions**, set `txAmount` to `0`. The valle must use base unit of the native token, e.g., wei for ETH |
| extJson | Object | Yes | Extended information object containing the following fields: |
| > inputData | String | Yes | Call data for the transaction. The encoding rule require `base58`. |
| priorityFee | String | No | Priority fee. Only applicable to Solana. |
| gasPrice | String | No | Gas price for the transaction. |
## Response Parameters
| Parameter | Type | Description |
|--------------|--------|--------------------------------------------------------------------|
| intention | String | Transaction purpose. Valid values: "Swap", "Token Approval" |
| assetChange | Array | Details of asset changes resulting from the transaction |
| > assetType | String | Asset type. Valid values: "NATIVE", "ERC20", "SPLTOKEN","SUITOKEN"|
| > name | String | Asset name (e.g., "Ethereum") |
| > symbol | String | Asset symbol (e.g., "ETH") |
| > decimals | Number | Asset decimal precision |
| > address | String | Asset contract address |
| > imageUrl | String | URL to the asset's image |
| > rawValue | String | Asset amount. Positive values indicate receiving assets, negative values indicate sending assets. |
| gasUsed | String | Gas consumed by the transaction |
| failReason | String | Human-friendly explanation if the transaction would fail |
| risks | Array | Potential risks identified in the transaction |
| > address | String | Address associated with the risk |
| > addressType| String | Type of address. Valid values: "contract", "eoa" |
## Request Example
```shell
curl --location --request POST 'https://web3.okx.com/api/v6/dex/pre-transaction/simulate' \
--header 'OK-ACCESS-KEY: your-access-key' \
--header 'OK-ACCESS-SIGN: your-access-sign' \
--header 'OK-ACCESS-PASSPHRASE: your-passphrase' \
--header 'OK-ACCESS-TIMESTAMP: 2025-05-19T10:00:00.000Z' \
--header 'Content-Type: application/json' \
--data-raw '{
"fromAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"toAddress": "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
"chainIndex": "1",
"txAmount": "0",
"extJson": {
"inputData": "0x38ed1739000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000042ab52c000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000742d35cc6634c0532925a3b844bc454e4438f44e0000000000000000000000000000000000000000000000000000000064794b4b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
},
"gasPrice": "12000000000"
}'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"intention": "SWAP",
"assetChange": [
{
"assetType": "NATIVE",
"name": "Ether",
"symbol": "ETH",
"decimals": 18,
"address": "",
"imageUrl": "",
"rawValue": "-1000000000000000"
},
{
"assetType": "ERC20",
"name": "USD Coin",
"symbol": "USDC",
"decimals": 6,
"address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"imageUrl": "",
"rawValue": "1000000000000000"
}
],
"gasUsed": "180000",
"failReason": "",
"risks": []
}
],
"msg": "success"
}
```
- [Broadcast Transactions](https://web3.okx.com/onchainos/dev-docs/wallet/onchain-gateway-api-broadcast-transaction.md)
{/* api-page */}
# Broadcast Transactions
Broadcast transactions to the specified blockchain.
Your end-user's transaction can only be covered by the MEV protection feature if you actually utilise OKX Build's API services for that particular transaction. MEV protection is currently an experimental feature provided by third-parties and OKX Build does not guarantee the effectiveness and quality of such MEV protection.
### Request URL
POST `https://web3.okx.com/api/v6/dex/pre-transaction/broadcast-transaction`
## Request Parameters
| Parameter | Type | Required | Description |
|------------ |-------- |----------|------------------------------------------------------------------------|
| signedTx | String | Yes | The transaction string after being signed |
| chainIndex | String | Yes | Unique identifier for the chain. e.g., ETH=1. See more [here](../home/supported-chain). |
| address | String | Yes | Address. |
| extraData | String | No | Additional parameters for calldata and other information |
| > enableMevProtection | Boolean | No | Enable MEV protection. Not enabled by default. Valid values: `false`:not enabled, `true`:enabled It supports only `ETH`、`BSC`、`SOL`、`BASE`, more chains will be supported soon. |
| > jitoSignedTx | String | No | The transaction string after being signed that will send to Jito. The encoding rule require `base58`, applicable to `SOL`. For SOL, `signedTx` and `jitoSignedTx` must be passed at the same time |
## Response Parameters
| Parameter | Type | Description |
|-----------|--------|--------------------|
| orderId | String | Unique transaction identifier |
| txHash | String | Transaction Hash. It supports only `ETH`、`BSC`、`SOL`、`BASE`, more chains will be supported soon. |
## Request Example
``` shell
curl --location --request POST 'https://web3.okx.com/api/v6/dex/pre-transaction/broadcast-transaction' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z' \
--data-raw '{
"signedTx":"0x08b47112567534ad041bbc6fa102394773c6d8f6d634320773af4da55efa",
"address": "0x383c8208b4711256753b70729ba0cf0cda55efad",
"chainIndex": "1",
"extraData":"{\"enableMevProtection\":true,\"jitoSignedTx\":\"0x123456\"}"
}'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"orderId": "0x383c8208b4711256753b70729ba0cf0cda55efad",
"txHash": "0xd394f356a16b618ed839c66c935c9cccc5dde0af832ff9b468677eea38759db5"
}
],
"msg": ""
}
```
- [Get Transaction Orders](https://web3.okx.com/onchainos/dev-docs/wallet/onchain-gateway-api-orders.md)
{/* api-page */}
# Get Transaction Orders
Get the list of orders sent from transaction broadcasting API. This supports querying transactions sorted in descending order by time.
### Request URL
GET `https://web3.okx.com/api/v6/dex/post-transaction/orders`
## Request Parameters
| Parameter | Type | Required | Description |
|------------ |-------- |----------|----------------------------------------------------|
| address | String | Yes | Address |
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| txStatus | String | No | Transaction status: `1`: Pending `2`: Success `3`: Failed |
| orderId | String | No | Unique identifier for the transaction order |
| cursor | String | No | Cursor |
| limit | String | No | Number of records returned, default is the most recent 20, maximum is 100 |
## Response Parameters
| Parameter | Type | Description |
|------------ |-------- |-----------------------------------------------------|
| chainIndex | String | Unique identifier for the chain |
| address | String | Address |
| orderId | String | Order ID |
| txStatus | String | Transaction status: `1`: Pending `2`: Success `3`: Failed |
| failReason | String | The reason for failed transaction |
| txHash | String | Transaction hash |
## Request Example
``` shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/post-transaction/orders?address=0x238193be9e80e68eace3588b45d8cf4a7eae0fa3&chainIndex=1' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
``` json
{
"code": "0",
"msg": "success",
"data": [
{
"cursor": "1",
"orders":[
{
"chainIndex": "1",
"orderId": "016cf21d020be6c2f071dad9bbd8ec5cb9342fa8",
"address": "0x238193be9e80e68eace3588b45d8cf4a7eae0fa3",
"txHash": "0xb240e65dd9156b4a450be72f6c9fe41be6f72397025bb465b21a96ee9871a589",
"failReason": "",
"txstatus": "2"
},
{
"chainIndex": "1",
"orderId": "592051a92a744627022955be929ecb5c9e777705",
"address": "0x238193be9e80e68eace3588b45d8cf4a7eae0fa3",
"txHash": "0xc401ffcd2a2b4b1db42ce68dfde8e63c0a1e9653484efb2873dbf5d0cbeb227a",
"txstatus": "1",
"failReason": "",
}
]
}
]
}
```
- [Error Codes](https://web3.okx.com/onchainos/dev-docs/wallet/onchain-gateway-error-code.md)
# Error Codes
| Code | HTTP status | Message |
|-------|-------------|-----------------------------------------------------------------------------------------|
| 50001 | 200 | Service temporarily unavailable. Try again later |
| 81001 | 200 | Incorrect parameter |
| 81108 | 200 | Wallet type does not match the required type |
| 81104 | 200 | Chain not support |
| 81152 | 200 | Coin not exist |
| 81451 | 200 | node return failed |
- [Check Transaction History](https://web3.okx.com/onchainos/dev-docs/wallet/tx-history-api-overview.md)
# Check Transaction History
Transaction History API retrieves onchain transaction records for any wallet address. It supports multiple chains and returns structured data covering transfers, contract interactions, and token activity. Use it to build portfolio trackers, wallet dashboards, and onchain analytics tools.
## Key Capabilities
### 1. Multi-Chain Transaction Query
* Query transaction history across all supported chains by wallet address.
* Filter by asset type, transaction type, or time range.
* Paginated responses for efficient data handling.
### 2. Detailed Transaction Data
* Returns transaction hash, timestamp, status, gas fee, and block number.
* Covers native token transfers, ERC-20/SPL token transfers, and contract calls.
* Supports both real-time and historical data access.
- [API Reference](https://web3.okx.com/onchainos/dev-docs/wallet/tx-history-api-reference.md)
# API Reference
- [Get Supported Chains](https://web3.okx.com/onchainos/dev-docs/wallet/tx-history-api-chains.md)
{/* api-page */}
# Get Supported Chains
Retrieve information on chains supported by Transaction history API
### Request URL
GET `https://web3.okx.com/api/v6/dex/balance/supported/chain`
## Request Parameters
None
## Response Parameters
| Parameter | Type | Description |
|-----------|--------|-------------------|
| name | String | Chain name |
| logoUrl | String | Chain logo URL |
| shortName | String | Chain short name |
| chainIndex| String | Chain unique identifier |
## Request Example
``` shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/balance/supported/chain' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"name": "Ethereum",
"logoUrl": "http://www.eth.org/eth.png",
"shortName": "ETH",
"chainIndex": "1"
}
],
"msg": ""
}
```
- [Get History by Address](https://web3.okx.com/onchainos/dev-docs/wallet/tx-history-api-history.md)
{/* api-page */}
# Get History by Address
Query the transaction history under the address dimension for 6 months, sorted in descending chronological order.
### Request URL
GET `https://web3.okx.com/api/v6/dex/post-transaction/transactions-by-address`
## Request Parameters
| Parameter | Type | Required | Description |
|-------------- |-------- |---------- |--------------------------------------------------------------------------------------------------------------- |
| address | String | Yes | Address to query the transaction history for |
| chains | String | No | Filter the chains whose transaction history needs to be queried. Multiple chains are separated by ",". A maximum of 50 chains are supported. |
| tokenContractAddress | String | No | Token contract address; if empty, query addresses with main chain currency balance;if not pass, query all |
| begin | String | No | Start time, queries transactions after this time. Unix timestamp, in milliseconds |
| end | String | No | End time, queries transactions before this time. If both begin and end are not provided, queries transactions before the current time. Unix timestamp, in milliseconds |
| cursor | String | No | Cursor |
| limit | String | No | Number of records to return, defaults to the most recent 20 records. Up to a maximum of 20 records for query on single chain. Up to a maximum of 100 records for query on multiple chain. | |
## Response Parameters
| Parameter | Type | Description |
|----------------- |--------------------------------- |----------------------------------------------------- |
| transactions | Array | List of transactions |
| >chainIndex | String | Chain ID |
| >txHash | String | Transaction hash |
| >itype | String | Transaction tier type `0`: Outer main chain coin transfer `1`: Contract inner main chain coin transfer `2`: Token transfer |
| >methodId | String | Contract Function Call |
| >nonce | String | The nth transaction initiated by the sender address |
| >txTime | String | Transaction time in Unix timestamp format, in milliseconds, e.g., 1597026383085 |
| >from | Array | Transaction input |
| >>address | String | Sending/input address, comma-separated for multi-signature transactions |
| >>amount | String | Input amount |
| >to | Array | Transaction output |
| >>address | String | Receiving/output address, comma-separated for multiple addresses |
| >>amount | String | Output amount
| >tokenContractAddress | String | Token contract address |
| >amount | String | Transaction amount |
| >symbol | String | Currency symbol corresponding to the transaction amount |
| >txFee | String | Transaction fee |
| >txStatus | String | Transaction status: `success` for successful transactions, `fail` for failed transactions, `pending` for pending transactions |
| >hitBlacklist | Boolean | `false`: Not in blacklist, `true`: In blacklist |
| cursor | String | Cursor |
## Request Example
``` shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/post-transaction/transactions-by-address?addresses=0x50c476a139aab23fdaf9bca12614cdd54a4244e4&chains=1' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
``` json
{
"code": "0",
"msg": "success",
"data": [
{
"cursor": "1706197403",
"transactionList": [
{
"chainIndex": "1",
"txHash": "0x963767695543cfb7804039c470b110b87adf9ab69ebc002b571523b714b828ca",
"methodId": "",
"nonce": "",
"txTime": "1724213411000",
"from": [
{
"address":
"0xae7ab96520de3a18e5e111b5eaab095312d7fe84"
"amount": ""
}
],
"to": [
{
"address":
"0x50c476a139aab23fdaf9bca12614cdd54a4244e4"
"amount": ""
}
],
"tokenContractAddress": "0xe13c851c331874028cd8f681052ad3367000fb13",
"amount": "1",
"symbol": "claim rewards on stethdao.net",
"txFee": "",
"txStatus": "success",
"hitBlacklist": true,
"itype": "2"
}
]
}
]
}
```
- [Get Specific Transaction](https://web3.okx.com/onchainos/dev-docs/wallet/tx-history-api-detail.md)
{/* api-page */}
# Get Specific Transaction
Retrieve details of a transaction based on `txHash` for 6 months. It decomposes a transaction and its internal transactions into sub-transactions based on asset type: `0`: Outer layer mainnet coin transfer `1`: Inner layer mainnet coin transfer in a contract `2`: Token transfer
It decomposes a transaction into sub-transactions based on asset type. For EVM transactions, different sub-transaction types include: `0`: Outer layer mainnet coin transfer `1`: Inner layer mainnet coin transfer in a contract `2`: Token transfer
### Request URL
GET `https://web3.okx.com/api/v6/dex/post-transaction/transaction-detail-by-txhash`
## Request Parameters
| Parameter | Type | Required | Description |
|--------------------|----------------|------------------|--------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier for the chain |
| txHash | String | Yes | Transaction hash |
| itype | String | No | Layer type for transactions `0`: Outer layer mainnet coin transfer `1`: Inner layer mainnet coin transfer `2`: Token transfer |
## Response Parameters
| Parameter | Type | Description |
|------------------------------------|----------------|----------------------------------------------------------------|
| chainIndex | String | Unique identifier for the chain |
| height | String | Block height where the transaction occurred |
| txTime | String | Transaction time; Unix timestamp in milliseconds |
| txhash | String | Transaction hash |
| txStatus | String | Transaction status: `1`: pending `2`: success `3`: fail |
| gasLimit | String | Gas limit |
| gasUsed | String | Gas used |
| gasPrice | String | Gas price |
| txFee | String | Transaction fee. |
| nonce | String | Nonce |
| amount | String | Transaction amount |
| symbol | String | Currency symbol for the transaction amount |
| methodId | String | Contract method ID |
| fromDetails | Array | Details of transaction inputs |
| >address | String | Sender/input address |
| >vinIndex | String | Index of the input in the current transaction |
| >preVoutIndex | String | Index of the output in the previous transaction |
| >txhash | String | Transaction hash, used with `preVoutIndex` to uniquely identify the UTXO |
| >isContract | Boolean | Whether the sender address is a contract (true: yes; false: no) |
| >amount | String | Transaction amount |
| toDetails | Array | Details of transaction outputs |
| >address | String | Receiver/output address |
| >voutIndex | String | Output index |
| >isContract | Boolean | Whether the receiver address is a contract (true: yes; false: no) |
| >amount | String | Transaction amount |
| internalTransactionDetails | Array | Internal transaction details |
| >from | String | Sender address for the internal transaction |
| >to | String | Receiver address for the internal transaction |
| >isFromContract | Boolean | Whether the sender address is a contract (true: yes; false: no) |
| >isToContract | Boolean | Whether the receiver address is a contract (true: yes; false: no) |
| >amount | String | Transaction amount |
| >txStatus | String | Transaction status |
| tokenTransferDetails | Array | Token transfer details |
| >from | String | Sender address for token transfer |
| >to | String | Receiver address for token transfer |
| >isFromContract | Boolean | Whether the sender address is a contract (true: yes; false: no) |
| >isToContract | Boolean | Whether the receiver address is a contract (true: yes; false: no) |
| >tokenContractAddress | String | Token contract address |
| >symbol | String | Token symbol |
| >amount | String | Token amount |
| l1OriginHash | String | Hash of the L1 transaction executed |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/post-transaction/transaction-detail-by-txhash?txHash=0x9ab8ccccc9f778ea91ce4c0f15517672c4bd06d166e830da41ba552e744d29a5&chainIndex=42161' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
``` json
{
"code": "0",
"msg": "success",
"data": [
{
"chainIndex": "42161",
"height": "245222398",
"txTime": "1724253417000",
"txhash": "0x9ab8ccccc9f778ea91ce4c0f15517672c4bd06d166e830da41ba552e744d29a5",
"gasLimit": "2000000",
"gasUsed": "2000000",
"gasPrice": "10000000",
"txFee":"",
"nonce": "0",
"symbol": "ETH",
"amount": "0",
"txStatus": "success",
"methodId": "0xc9f95d32",
"l1OriginHash": "0xa6a87ba2f18cc32bbae8f3b2253a29a9617ed1eb0940d80443f6e3bf9873dbad",
"fromDetails": [
{
"address": "0xd297fa914353c44b2e33ebe05f21846f1048cfeb",
"vinIndex": "",
"preVoutIndex": "",
"txHash": "",
"isContract": false,
"amount": ""
}
],
"toDetails": [
{
"address": "0x000000000000000000000000000000000000006e",
"voutIndex": "",
"isContract": false,
"amount": ""
}
],
"internalTransactionDetails": [
{
"from": "0x0000000000000000000000000000000000000000",
"to": "0xd297fa914353c44b2e33ebe05f21846f1048cfeb",
"isFromContract": false,
"isToContract": false,
"amount": "0.02",
"txStatus": "success"
},
{
"from": "0xd297fa914353c44b2e33ebe05f21846f1048cfeb",
"to": "0x428ab2ba90eba0a4be7af34c9ac451ab061ac010",
"isFromContract": false,
"isToContract": false,
"amount": "0.00998",
"txStatus": "success"
},
{
"from": "0xd297fa914353c44b2e33ebe05f21846f1048cfeb",
"to": "0x428ab2ba90eba0a4be7af34c9ac451ab061ac010",
"isFromContract": false,
"isToContract": false,
"amount": "0.009977946366846017",
"txStatus": "success"
}
],
"tokenTransferDetails": []
}
]
}
```
- [Error Codes](https://web3.okx.com/onchainos/dev-docs/wallet/tx-history-error-code.md)
# Error Codes
| Code | HTTP status | Message |
|-------|-------------|-----------------------------------------------------------------------------------------|
| 81001 | 200 | Incorrect parameter |
- [Introduction](https://web3.okx.com/onchainos/dev-docs/wallet/defi-api-overview.md)
# Introduction
DeFi API is a core infrastructure module of OnchainOS, providing DeFi users with comprehensive DeFi investment capabilities — product discovery, trade execution, and position management, all through a single interface that covers mainstream DeFi protocols.
- **Product Discovery** — Query investment products across dozens of DeFi protocols such as Aave, Lido, and more. Retrieve detailed product information including APY, fees, total value locked, etc.
- **Trade Execution** — Subscribe to or redeem investment products, lend or repay assets, and claim related investment rewards.
- **User Holdings** — Query DeFi investment position details across chains and protocols, with real-time tracking of principal, earnings, APY, and other relevant data.
- [Investment Product Query](https://web3.okx.com/onchainos/dev-docs/wallet/defi-product-introduction.md)
# Investment Product Query
The Investment Product Query API aggregates investment product information from dozens of DeFi protocols. Developers can search or filter by keywords to retrieve detailed product parameters (APY, TVL, underlying assets, fees, etc.) to prepare for subsequent transaction execution.
## Core Capabilities
### 1. Product Search and Filtering
- Search investment products by token keywords, protocol names, chain ID, investment type, and other criteria
- Filter investment products by key metrics such as APY, TVL, and more
### 2. Query Product Details
- Retrieve product details including underlying assets, APY breakdowns, fee structures, and other data.
- Query which operations a DeFi investment product supports (such as subscribe, redeem, claim rewards, etc.)
- [API Reference](https://web3.okx.com/onchainos/dev-docs/wallet/defi-product-api-reference.md)
# API Reference
- [Get Supported Chains](https://web3.okx.com/onchainos/dev-docs/wallet/defi-product-supported-chains.md)
# Get Supported Chains
Query all chains currently covered by DeFi investment products. The chain list is aggregated from the database by a scheduled task and cached in Redis, containing chain IDs and network names. It can be used to display chain filters on the frontend, or as a source for the `chainIndex` parameter in other endpoints (such as `/api/v6/defi/product/search` and `/api/v6/defi/product/supported-platforms`).
**URL**: GET `/api/v6/defi/product/supported-chains`
## Request Parameters
No request parameters.
## Request Example
```
GET /api/v6/defi/product/supported-chains
```
## Response Parameters
**data Array Elements**
| Field | Type | Explanation |
| --- | --- | --- |
| chainIndex | String | Chain ID (e.g., "1"=Ethereum, "56"=BSC, "137"=Polygon) |
| network | String | Network identifier (e.g., "ETH", "BSC", "POLYGON") |
## Response Example
```json
{
"code": 0,
"data": [
{
"chainIndex": "1",
"network": "ETH"
},
{
"chainIndex": "56",
"network": "BSC"
},
{
"chainIndex": "137",
"network": "POLYGON"
},
{
"chainIndex": "42161",
"network": "ARBITRUM"
},
{
"chainIndex": "8453",
"network": "BASE"
}
]
}
```
> The API returns chains based on which ones currently have listed investment products. The list is updated by a scheduled task, typically refreshed once per hour.
- [Get Supported Protocols](https://web3.okx.com/onchainos/dev-docs/wallet/defi-product-supported-platforms.md)
# Get Supported Protocols
Query all protocols currently covered by DeFi investment products along with their statistics. The response includes protocol name, protocol ID, and the number of investment products under each protocol. It can be used to display protocol filters on the frontend or for protocol overview pages.
**URL**: GET `/api/v6/defi/product/supported-platforms`
## Request Parameters
No request parameters.
## Request Example
```
GET /api/v6/defi/product/supported-platforms
```
## Response Parameters
**data Array Elements**
| Field | Type | Explanation |
| --- | --- | --- |
| analysisPlatformId | String | Protocol ID |
| platformName | String | Protocol name (e.g., "Aave V3", "Lido", "PancakeSwap V3") |
| investmentCount | Long | Number of investment products under this protocol |
## Response Example
```json
{
"code": 0,
"data": [
{
"analysisPlatformId": "10",
"platformName": "Aave V3",
"investmentCount": 68
},
{
"analysisPlatformId": "20",
"platformName": "Lido",
"investmentCount": 1
},
{
"analysisPlatformId": "30",
"platformName": "PancakeSwap V3",
"investmentCount": 120
}
]
}
```
> The actual number of protocols and investment products depends on what is currently live.
- [Investment Product Search](https://web3.okx.com/onchainos/dev-docs/wallet/defi-product-search.md)
# Investment Product Search
This is the first step in the DeFi investment workflow. When a user wants to find DeFi investment opportunities, use this endpoint to search for available investment products by token name (e.g. "USDC", "ETH"). The response includes investment product ID, name, protocol, APY, TVL, and other information. Once you have the `investmentId`, you can call `/product/detail` to retrieve details, or call `/product/detail/prepare` to prepare transaction parameters.
**URL**: POST `/api/v6/defi/product/search`
## Request Parameters
| Field | Type | Required | Explanation |
| --- | --- | --- | --- |
| tokenKeywordList | Array | Yes | Token keyword list (e.g. ["USDC", "ETH"]) |
| platformKeywordList | Array | No | Platform keyword list (e.g. ["AAVE V3"]) |
| pageNum | Integer | No | Page number, minimum 1; current fixed pageSize=20 |
| chainIndex | String | No | Chain ID (e.g. 1=ETH, 56=BSC, etc.) |
| productGroup | String | No | Investment type filter, default SINGLE\_EARN; includes: SINGLE\_EARN, DEX\_POOL, LENDING |
## Request Examples
### Example 1: Search USDC Token Only
```json
{
"tokenKeywordList": ["USDC"],
"pageNum": 1
}
```
### Example 2: Search Multiple Tokens + Specified Chain
```json
{
"tokenKeywordList": ["USDC", "ETH"],
"chainIndex": "1",
"pageNum": 1
}
```
### Example 3: Search + Filter by Protocol + Investment Type
```json
{
"tokenKeywordList": ["USDC"],
"platformKeywordList": ["Aave"],
"productGroup": "SINGLE_EARN",
"pageNum": 1
}
```
```json
{
"tokenKeywordList": ["USDC"],
"platformKeywordList": ["Uniswap"],
"productGroup": "DEX_POOL",
"pageNum": 1
}
```
```json
{
"tokenKeywordList": ["USDC"],
"platformKeywordList": ["AAVE V3"],
"productGroup": "LENDING",
"pageNum": 1
}
```
### Example 4: Search Multi-Chain Data
```json
{
"tokenKeywordList": ["USDT"],
"pageNum": 1
}
```
## Response Parameters
| Field | Type | Explanation |
| --- | --- | --- |
| total | Integer | Total count |
| list | Array | Product list |
| > investmentId | String | Investment product ID |
| > name | String | Investment product name |
| > platformName | String | Protocol name |
| > rate | String | APY |
| > tvl | String | TVL |
| > chainIndex | String | Chain ID |
| > detailPath | String | Detail page path |
| > feeRate | BigDecimal | Fee rate |
| > productGroup | String | Investment product type |
## Response Example
**Search USDC + Protocol Aave**
```json
{
"code": 0,
"msg": "",
"data": {
"total": 8,
"list": [
{
"investmentId": 9502,
"name": "USDC",
"platformName": "Aave V3",
"rate": "0.02140",
"tvl": "3423591587.48413",
"detailPath": null,
"feeRate": null,
"productGroup": "SINGLE_EARN",
"chainIndex": "1"
},
{
"investmentId": 378532533,
"name": "USDC",
"platformName": "Aave V3",
"rate": "0.02590",
"tvl": "370145418.65238",
"detailPath": null,
"feeRate": null,
"productGroup": "SINGLE_EARN",
"chainIndex": "8453"
},
{
"investmentId": 124,
"name": "USDC",
"platformName": "Aave V3",
"rate": "0.03510",
"tvl": "100947767.28590",
"detailPath": null,
"feeRate": null,
"productGroup": "SINGLE_EARN",
"chainIndex": "43114"
}
]
}
}
```
> The actual response returns 8 results; only the first 3 are shown here. `rate` is in decimal format (0.02140 = 2.14%), and `tvl` is in USD.
- [Get Product Details](https://web3.okx.com/onchainos/dev-docs/wallet/defi-product-detail.md)
# Get Product Details
When the user has selected a specific investment product (i.e., `investmentId` has been obtained), use this endpoint to retrieve the full product information, including APY breakdown, underlying assets, and whether subscribe/redeem/claim operations are supported. This information is used to display product details to the user and to determine which subsequent operations are available.
**URL**: GET `/api/v6/defi/product/detail`
## Request Parameters
| Field | Type | Required | Explanation |
| --- | --- | --- | --- |
| investmentId | String | Yes | Investment product ID |
## Response Parameters
| Field | Type | Explanation |
| --- | --- | --- |
| investmentId | String | Investment product ID |
| investmentName | String | Investment product name |
| platformName | String | Protocol name |
| platformLogo | String | Protocol logo |
| investType | Integer | Investment type |
| chainIndex | String | Chain ID string |
| network | String | Network name |
| rate | String | Yield rate |
| tvl | String | TVL |
| feeRate | String | Fee rate |
| isSupportClaim | Boolean | Whether claim is supported |
| isInvestable | Boolean | Whether investable |
| isSupportRedeem | Boolean | Whether redemption is supported |
| analysisPlatformId | String | Protocol ID |
| subscriptionMethod | Integer | Subscription method |
| redeemMethod | Integer | Redemption method |
| underlyingToken | Array | Underlying asset tokens |
| > tokenSymbol | String | Token symbol |
| > tokenAddress | String | Contract address |
| > chainIndex | String | Chain ID |
| > tokenPrecision | Integer | Precision |
| > tokenLogo | String | Logo |
| aboutToken | Array | Related tokens |
| > tokenSymbol | String | Token symbol |
| > tokenAddress | String | Contract address |
| > chainIndex | String | Chain ID |
| > tokenPrecision | Integer | Precision |
| > tokenLogo | String | Logo |
| > marketCap | String | Market cap |
| > price | String | Price |
| rateDetails | Array | Yield rate details |
| qaList | Array | Q&A list |
## Response Example
**Aave V3 USDC (Ethereum, investmentId=9502)**
```json
{
"code": 0,
"msg": "",
"data": {
"investmentId": 9502,
"investmentName": "USDC",
"platformName": "Aave V3",
"platformLogo": "https://static.coinall.ltd/cdn/web3/protocol/logo/aave-v3.png/type=png_350_0?v=1774409445039",
"investType": 1,
"tvl": "3423591587.48413",
"rate": "0.02140",
"rateType": 0,
"rateTypeDesc": "APY",
"network": "Ethereum",
"networkLogo": "https://static.coinall.ltd/cdn/wallet/logo/ETH-20220328.png",
"underlyingToken": [
{
"tokenSymbol": "USDC",
"tokenLogo": "https://static.coinall.ltd/cdn/wallet/logo/USDC.png",
"tokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"isBaseToken": false
}
],
"rateDetails": [
{
"tokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"tokenSymbol": "USDC",
"rate": "0.0214",
"title": "Supply APY",
"type": 1
}
],
"aboutToken": [
{
"tokenSymbol": "USDC",
"tokenLogo": "https://static.coinall.ltd/cdn/wallet/logo/USDC.png",
"tokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"isBaseToken": false,
"marketCap": "55468602892.82830569743700036",
"price": "0.99988"
}
],
"isSupportClaim": false,
"isInvestable": true,
"isSupportRedeem": true,
"analysisPlatformId": "10",
"chainIndex": "1",
"detailPath": "aave-v3-ethereum-usdc-9502",
"platformUrl": "https://app.aave.com",
"utilizationRate": "0.755200",
"hasRateChart": true,
"hasTvlChart": false
}
}
```
- [Historical APY Line Chart](https://web3.okx.com/onchainos/dev-docs/wallet/defi-product-rate-chart.md)
# Historical APY Line Chart
When a user wants to view the historical yield trend of an investment product, use this endpoint. Pass in the product ID and time range (WEEK / MONTH / SEASON / YEAR), and it returns APY data at each time point, which can be used to plot a line chart showing the yield trend.
**URL**: GET `/api/v6/defi/product/rate/chart`
## Request Parameters
| Field | Type | Required | Default | Explanation |
| --- | --- | --- | --- | --- |
| investmentId | String | Yes | — | Investment Product ID |
| timeRange | String | No | WEEK | Time range: WEEK (one week), MONTH (one month), SEASON (three months), YEAR (one year) |
## Request Examples
### Example 1: Query APY Line Chart for Past Week (Default)
```
GET /api/v6/defi/product/rate/chart?investmentId=124
```
### Example 2: Query APY Line Chart for Past Three Months
```
GET /api/v6/defi/product/rate/chart?investmentId=124&timeRange=SEASON
```
## Response Parameters
| Field | Type | Explanation |
| --- | --- | --- |
| timestamp | String | Statistical timestamp (milliseconds) |
| rate | String | Investment product yield (including base interest rate + mining coin reward) |
| bonusRate | String | Additional bonus yield (OKX Bonus/Merkl, etc.) |
| limitValue | Integer | Limit value marking: 1=highest point, -1=lowest point, null=normal point |
| totalReward | String | Total fee + bonus reward during the statistical period |
## Response Example
```json
{
"code": 0,
"msg": "",
"data": [
{
"timestamp": 1741737600000,
"rate": "0.0312",
"bonusRate": "0.0045",
"limitValue": 1,
"totalReward": "0.0357"
},
{
"timestamp": 1741651200000,
"rate": "0.0298",
"bonusRate": "0.0045",
"limitValue": null,
"totalReward": "0.0343"
}
]
}
```
- [Historical TVL Line Chart](https://web3.okx.com/onchainos/dev-docs/wallet/defi-product-tvl-chart.md)
# Historical TVL Line Chart
Use this endpoint when a user wants to view the historical TVL (Total Value Locked) trend of an investment product. Pass the investment product ID and time range, and it returns TVL data at each time point, which can be used to assess the product's scale changes and market popularity.
**URL**: GET `/api/v6/defi/product/tvl/chart`
## Request Parameters
| Field | Type | Required | Default | Explanation |
| --- | --- | --- | --- | --- |
| investmentId | String | Yes | — | Investment product ID (Query parameter) |
| timeRange | String | No | WEEK | Time range enum: WEEK (one week), MONTH (one month), SEASON (three months), YEAR (one year) |
## Request Examples
### Example 1: Query the past week's TVL line chart (default)
```
GET /api/v6/defi/product/tvl/chart?investmentId=124
```
### Example 2: Query the past year's TVL line chart
```
GET /api/v6/defi/product/tvl/chart?timeRange=YEAR&investmentId=124
```
## Response Parameters
| Field | Type | Explanation |
| --- | --- | --- |
| chartVos | Array | TVL line chart data list |
| > timestamp | String | Statistics timestamp (milliseconds) |
| > tvl | String | TVL value (USD) |
| > limitValue | Integer | Extreme value marker: 1=highest point, -1=lowest point, null=regular point |
| text | String | Line chart description text (may be empty) |
## Response Example
```json
{
"code": 0,
"msg": "",
"data": {
"chartVos": [
{
"timestamp": 1741737600000,
"tvl": "523847291.45",
"limitValue": 1
},
{
"timestamp": 1741651200000,
"tvl": "498312044.78",
"limitValue": null
},
{
"timestamp": 1741564800000,
"tvl": "480125367.22",
"limitValue": -1
}
],
"text": null
}
}
```
- [V3 Depth Price History Chart](https://web3.okx.com/onchainos/dev-docs/wallet/defi-product-depth-price-chart.md)
# V3 Depth Price History Chart
Use this endpoint when you want to view the liquidity depth distribution or historical price trends of a V3 Pool. Pass in the investment product ID, chart type (depth chart or price history chart), and time range. Returns the corresponding depth/price data list for rendering liquidity distribution charts or price candlestick charts. **Only applicable to V3 Pool type investment products**.
**URL**: GET `/api/v6/defi/product/depth-price/chart`
## Request Parameters
| Field | Type | Required | Default | Explanation |
| --- | --- | --- | --- | --- |
| investmentId | String | Yes | — | Investment product ID (integer string) |
| chartType | String | No | DEPTH | Chart type: `DEPTH`=depth chart, `PRICE`=price history chart |
| timeRange | String | No | DAY | Only used when chartType=`PRICE`. Time range: `DAY`=24h, `WEEK`=1W |
## Request Examples
### Example 1: Query Depth Chart (default, last 24h)
```
GET /api/v6/defi/product/depth-price/chart?investmentId=1589649169
```
### Example 2: Query Price History Chart (last 1 week)
```
GET /api/v6/defi/product/depth-price/chart?investmentId=1589649169&chartType=PRICE&timeRange=WEEK
```
## Response Parameters
Returns an `Array`, each element has the following structure:
| Field | Type | Explanation |
| --- | --- | --- |
| tick | Integer | Tick index (has value in depth chart, empty in price history chart) |
| liquidity | String | Liquidity at this tick (has value in depth chart) |
| liquidityNet | String | Net liquidity at this tick (has value in depth chart) |
| token0Price | String | Depth chart: token0 price at this tick; Price history chart: historical token0 price at this timestamp |
| token1Price | String | Depth chart: token1 price at this tick; Price history chart: historical token1 price at this timestamp |
| timestamp | Long | Timestamp in milliseconds (only has value in price history chart) |
## Response Examples
### Depth Chart (chartType=DEPTH)
```json
{
"code": 0,
"msg": "",
"data": [
{
"tick": -32932,
"liquidity": "1234567890123456",
"liquidityNet": "500000000000000",
"token0Price": "0.9985",
"token1Price": "1.0015"
},
{
"tick": -32931,
"liquidity": "1234567890123456",
"liquidityNet": "0",
"token0Price": "0.9986",
"token1Price": "1.0014"
}
]
}
```
### Price History Chart (chartType=PRICE)
```json
{
"code": 0,
"msg": "",
"data": [
{
"token0Price": "0.9985",
"token1Price": "1.0015",
"timestamp": 1741737600000
},
{
"token0Price": "0.9990",
"token1Price": "1.0010",
"timestamp": 1741651200000
}
]
}
```
### Error Example: Non-V3 Pool Investment Product
```json
{
"code": 84032,
"msg": "This api is only supported for V3 DEX Pool products",
"data": null
}
```
- [Trade Execution](https://web3.okx.com/onchainos/dev-docs/wallet/defi-transaction-introduction.md)
# Trade Execution
The Trade Execution API provides subscribe (deposit/borrow), redeem (withdraw/repay), and claim DeFi rewards capabilities. It supports DeFi investment product operations across multiple chains including EVM (Ethereum, BSC, Avalanche, etc.), Solana, Sui, Aptos, and more.
- **Subscribe/Deposit/Borrow**: Deposit assets into DeFi protocols to earn yield, or borrow assets.
- **Redeem/Withdraw/Repay**: Redeem assets from DeFi protocols, or repay loans.
- **Claim Rewards**: Claim various rewards generated by your investments (protocol rewards, investment yields, bonus incentives, etc.) with a single call.
- [API Reference](https://web3.okx.com/onchainos/dev-docs/wallet/defi-transaction-api-reference.md)
# API Reference
- [Prepare Transaction Parameters](https://web3.okx.com/onchainos/dev-docs/wallet/defi-product-prepare.md)
# Prepare Transaction Parameters
This is a required preparatory step before initiating a subscription transaction. Call this endpoint to obtain the "available input token list", "receipt token", "yield tokens", and other information. These are required inputs when subsequently calling `/transaction/enter` or `/transaction/exit` to construct calldata.
**URL**: POST `/api/v6/defi/product/detail/prepare`
## Request Parameters
| Field | Type | Required | Explanation |
| --- | --- | --- | --- |
| investmentId | String | Yes | Investment product ID |
## Request Example
### Example 1: Subscription Initialization
```json
{
"investmentId": 12345
}
```
## Response Parameters
| Field | Type | Explanation |
| --- | --- | --- |
| investWithTokenList | Array | Available input token list, used for the subscription enter endpoint |
| > tokenId | String | NFT TokenId |
| > tokenSymbol | String | Token symbol |
| > tokenName | String | Token name |
| > tokenAddress | String | Contract address |
| > tokenPrecision | String | Precision |
| > chainIndex | String | Chain ID |
| > network | String | Network |
| > coinAmount | String | Token amount |
| > currencyAmount | String | USD value |
| receiveTokenInfo | Object | Received receipt token |
| > tokenId | String | NFT TokenId |
| > tokenSymbol | String | Token symbol |
| > tokenName | String | Token name |
| > tokenAddress | String | Contract address |
| > tokenPrecision | String | Precision |
| > chainIndex | String | Chain ID |
| > network | String | Network |
| > coinAmount | String | Token amount |
| > currencyAmount | String | USD value |
| gainsTokenList | Array | Yield token list |
| > tokenId | String | NFT TokenId |
| > tokenSymbol | String | Token symbol |
| > tokenName | String | Token name |
| > tokenAddress | String | Contract address |
| > tokenPrecision | String | Precision |
| > chainIndex | String | Chain ID |
| > network | String | Network |
| > coinAmount | String | Token amount |
| > currencyAmount | String | USD value |
| isAllowSubscribe | Boolean | Whether subscription is allowed (has value when type=1) |
| feeRate | String | Fee rate (dex_pool exclusive) |
| currentTick | String | Current tick (corresponding to current price exchange rate) (dex_pool exclusive) |
| currentPrice | String | token0 current price exchange rate (dex_pool exclusive) |
| lowerPrice | String | Lower price bound when entering the initial page for adding positions (dex_pool exclusive) |
| upperPrice | String | Upper price bound when entering the initial page for adding positions (dex_pool exclusive) |
| tickSpacing | String | Tick spacing (dex_pool exclusive) |
| underlyingTokenList | Array | Underlying asset list, index0=token0, index1=token1 (dex_pool exclusive) |
| > tokenId | String | NFT Token ID (tokenId of the V3 position NFT) |
| > tokenSymbol | String | Token symbol, e.g. USDC, ETH |
| > tokenName | String | Token full name |
| > tokenLogo | String | Token logo image URL |
| > tokenAddress | String | Token contract address |
| > network | String | Network identifier, e.g. eth, bsc |
| > chainIndex | String | Chain ID, e.g. 1 (ETH), 56 (BSC) |
| > tokenPrecision | String | Token precision (i.e. decimals), e.g. 18, 6 |
| > isBaseToken | Boolean | Whether it is the chain's native token (e.g. ETH, BNB) |
## Response Example
**Single Earn: Aave V3 USDC (Ethereum, investmentId=9502)**
```json
{
"code": 0,
"msg": "",
"data": {
"investWithTokenList": [
{
"tokenSymbol": "USDC",
"tokenName": "USD Coin",
"tokenLogo": "https://static.coinall.ltd/cdn/wallet/logo/USDC.png",
"tokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"network": "ETH",
"chainIndex": "1",
"tokenPrecision": "6",
"isBaseToken": false,
"coinAmount": "0",
"currencyAmount": "0",
"browserUrl": "https://web3.okx.com/explorer/eth/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
}
],
"receiveTokenInfo": {
"tokenSymbol": "aEthUSDC",
"tokenName": "Aave Ethereum USDC",
"tokenLogo": "https://static.coinall.ltd/cdn/web3/currency/token/1-0x98c23e9d8f34fefb1b7bd6a91b7ff122f4e16f5c-97.png/type=png_350_0",
"tokenAddress": "0x98c23e9d8f34fefb1b7bd6a91b7ff122f4e16f5c",
"network": "ETH",
"chainIndex": "1",
"tokenPrecision": "6",
"coinAmount": "0"
},
"gainsTokenList": [
{
"tokenSymbol": "USDC",
"tokenName": "USD Coin",
"tokenLogo": "https://static.coinall.ltd/cdn/wallet/logo/USDC.png",
"tokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"network": "ETH",
"tokenPrecision": "6",
"coinAmount": "0",
"dataType": "0"
}
]
}
}
```
- [Subscription](https://web3.okx.com/onchainos/dev-docs/wallet/defi-transaction-enter.md)
# Subscription
Call this endpoint when a user wants to deposit assets into a DeFi protocol or borrow assets. Key parameters: `investmentId` (investment product ID), `address` (user wallet address), `userInputList` (input token address, chain ID, and amount). The returned `dataList` contains transaction steps to be executed in order (e.g., APPROVE followed by DEPOSIT), each of which must be signed and broadcast on-chain sequentially. For V3 Pool operations, additional parameters such as `tickLower` and `tickUpper` are required.
**URL**: POST `/api/v6/defi/transaction/enter`
## Request Parameters
> Enter and exit share the same request model, and some fields are only valid under specific operations.
| Field | Type | Required | Default | Explanation |
| --- | --- | --- | --- | --- |
| investmentId | String | Yes | — | Investment product ID |
| address | String | Yes | — | User wallet address |
| tickLower | String | No | — | V3 tick lower bound. Only required when creating a new position for Dex Pool type investments |
| tickUpper | String | No | — | V3 tick upper bound. Only required when creating a new position for Dex Pool type investments |
| tokenId | String | No | — | V3 Pool NFT position token ID. When isV3Pool=true: required for adding liquidity (appending to an existing position); required for redemption |
| userInputList | Array | No | — | Input tokens and amounts. For subscription, this is the token or token list information to invest |
| > tokenAddress | String | No | — | Required when `userInputList` is provided; Token contract address |
| > chainIndex | String | No | — | Required when `userInputList` is provided; Chain ID |
| > coinAmount | String | No | — | Required when `userInputList` is provided; Amount (human-readable, e.g., "0.2") |
| > tokenSymbol | String | No | — | Token symbol |
| > tokenPrecision | Integer | No | — | Precision |
| slippage | String | No | "0.01" | Transaction slippage (effective for adapter/Zap routing). "0.01"=1%, "0.1"=10% |
## Request Examples
### Example 1: BSC V3 Pool subscription
Investment product: PancakeSwapV3 USDT-RIVER (id=1589649169, chainIndex=56)
```json
{
"investmentId": "1589649169",
"address": "0x1ae68a40b9f903a469aed01574f3a9ab6d45c563",
"tickLower": "-32150",
"tickUpper": "-31350",
"userInputList": [
{
"tokenAddress": "0x55d398326f99059fF775485246999027B3197955",
"chainIndex": "56",
"coinAmount": "0.2"
}
],
"slippage": "0.1"
}
```
### Example 2: Avalanche Aave V3 deposit (Single Earn)
Investment product: Aave V3 USDC (id=124, chainIndex=43114)
```json
{
"investmentId": "124",
"address": "0x1ae68a40b9f903a469aed01574f3a9ab6d45c563",
"userInputList": [
{
"tokenAddress": "0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e",
"chainIndex": "43114",
"coinAmount": "0.05"
}
]
}
```
### Example 3: Avalanche Aave V3 borrow
Investment product: Aave V3 USDC Borrow (id=33901, chainIndex=43114)
```json
{
"investmentId": "33901",
"address": "0x1ae68a40b9f903a469aed01574f3a9ab6d45c563",
"userInputList": [
{
"tokenAddress": "0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e",
"chainIndex": "43114",
"coinAmount": "0.01"
}
]
}
```
### Example 4: Sui NAVI borrow
Investment product: NAVI SUI Borrow (id=40047, chainIndex=784)
```json
{
"investmentId": "40047",
"address": "0x2791c11545a2fef7d8b3188002c80343bf6dc64130a603914238d8660b3bddde",
"userInputList": [
{
"tokenAddress": "0x2::sui::SUI",
"chainIndex": "784",
"coinAmount": "0.02"
}
]
}
```
### Example 5: Solana Kamino borrow
Investment product: Kamino USDC Borrow (id=29130, chainIndex=501)
```json
{
"investmentId": "29130",
"address": "4GK2VMnznuPpg8gG9vqD5MM6889pjJ8WS2HqzktaBfSo",
"userInputList": [
{
"tokenAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"chainIndex": "501",
"coinAmount": "0.05"
}
]
}
```
## Response Parameters
| Field | Type | Explanation |
| --- | --- | --- |
| code | String | "0"=success, non-"0"=failure |
| msg | String | Error message |
| data.dataList | Array | Calldata result list (execute in order, e.g., APPROVE → DEPOSIT) |
| > callDataType | String | Operation type (approve, subscribe, redeem, claim), see enum table below |
| > from | String | From address (user wallet address) |
| > to | String | To address (target contract address). Depending on product type and operation, this may be a Zap contract or a direct protocol contract |
| > value | String | Transfer amount (native token quantity). Empty string or "0x0" when no native token transfer is needed |
| > serializedData | String | Serialized transaction data. EVM: hex calldata (0x prefix); Solana: base58 encoded; Sui: base64 encoded BCS bytes |
| > originalData | String | Auxiliary metadata (JSON string). EVM chains include function ABI (methodId/methodDefine/methodParams); Aptos chains include module ABI JSON |
| > transactionPayload | String | Transaction template, only returned for Aptos chains. Contains payload JSON; the client needs to supplement the sequence\_number and build the complete transaction via SDK |
| > signatureData | String | Signature data. EVM chains: Zap contract permit signature; non-EVM chains: server-side signature credential |
| > gas | String | Gas limit, only returned for non-EVM chains such as Aptos. EVM chains require client-side estimation or a fixed value |
### callDataType Enum Values
| Value | Explanation |
| --- | --- |
| APPROVE | ERC20 authorization (approve spender) |
| DEPOSIT | Deposit into protocol |
| SWAP,DEPOSIT | Swap then deposit (V3 Pool scenario, single token into dual-token pool) |
| WITHDRAW | Withdraw from protocol |
| WITHDRAW,SWAP | Withdraw then swap back to target token (V3 Pool scenario) |
> **Note**: Aave Borrow returns callDataType=WITHDRAW, and Aave Repay returns callDataType=DEPOSIT. This follows Aave's internal method semantics (borrow=withdraw assets from the pool, repay=deposit assets into the pool) and does not affect actual business operations.
### serializedData Processing by Chain
| Chain | Encoding | Client Processing Flow |
| --- | --- | --- |
| EVM (BSC/AVAX/ETH) | Hex (0x prefix) | Use directly as tx.data, with to as the target address |
| Sui | Base64 BCS | base64 decode → prepend intent [0,0,0] → blake2b-256 hash → Ed25519 sign → submit sui\_executeTransactionBlock |
| Solana | Base58 | bs58 decode → skip first 65 bytes (signature placeholder) → VersionedMessage.deserialize() → sign → send immediately (blockhash expires in ~60s) |
| Aptos | — | Use the payload JSON from the transactionPayload field, build transaction via SDK build.simple() → sign → submit |
## Response Examples
**EVM chain (BSC V3 Enter, APPROVE + SWAP,DEPOSIT two steps)**:
```json
{
"code": 0,
"msg": "",
"data": {
"dataList": [
{
"callDataType": "APPROVE",
"from": "0x1ae68a40b9f903a469aed01574f3a9ab6d45c563",
"to": "0xda7ad9dea9397cffddae2f8a052b82f1484252b3",
"value": "0x0",
"serializedData": "0x095ea7b3000000000000000000000000...ffffffff",
"originalData": "{\"callDataType\":\"APPROVE\",\"methodId\":\"0x095ea7b3\",\"methodDefine\":\"approve(address,uint256)\",...}",
"signatureData": "..."
},
{
"callDataType": "SWAP,DEPOSIT",
"from": "0x1ae68a40b9f903a469aed01574f3a9ab6d45c563",
"to": "0x7251FEbEABB01eC9dE53ECe7a96f1C951F886Dd2",
"value": "0x0",
"serializedData": "0xec5b999d000000000000000000000000...",
"originalData": "{\"callDataType\":\"SWAP,DEPOSIT\",\"methodDefine\":\"{...executeWithPermit...}\",...}",
"signatureData": "..."
}
]
}
}
```
**Sui chain (NAVI Deposit, single step)**:
```json
{
"code": 0,
"msg": "",
"data": {
"dataList": [
{
"serializedData": "",
"from": "0x2791c11545a2fef7d8b3188002c80343bf6dc64130a603914238d8660b3bddde",
"to": "...",
"value": "0"
}
]
}
}
```
**Solana chain (Kamino Deposit, single step)**:
```json
{
"code": 0,
"msg": "",
"data": {
"dataList": [
{
"serializedData": "",
"from": "4GK2VMnznuPpg8gG9vqD5MM6889pjJ8WS2HqzktaBfSo",
"to": "...",
"value": "0"
}
]
}
}
```
## V3 Pool Dual-Token Position Ratio Calculator
Note: This endpoint only needs to be called when investing in V3 Pool related products. Before subscribing to a V3 Pool, call this endpoint to calculate the required dual-token input ratio — the user only needs to provide a single token amount, and the endpoint intelligently computes how much of each token is needed based on the current pool price and selected price range. The returned `investWithTokenList` can be passed directly as `userInputList` to the subscription endpoint above.
**URL**: POST `/api/v6/defi/calculator/enter/info`
### Request Parameters
| Field | Type | Required | Explanation |
| --- | --- | --- | --- |
| inputAmount | String | Yes | Single-token amount entered by the user (human-readable format, e.g. "0.05") |
| inputTokenAddress | String | Yes | Contract address of the token entered by the user. Can be token0 or token1 of the V3 Pool |
| tokenDecimal | String | Yes | Decimals of the input token (e.g. "18", "6") |
| investmentId | String | Yes | Investment product ID (investmentId corresponding to the V3 Pool) |
| address | String | Yes | User wallet address |
| tickLower | String | Yes | Lower tick bound of the V3 position (e.g. "-33500") |
| tickUpper | String | Yes | Upper tick bound of the V3 position (e.g. "-30450") |
### Request Example
**Example: BSC V3 Pool (USDT → Dual-Token Allocation)**
Investment product: PancakeSwapV3 USDT-RIVER (id=1589649169, chainIndex=56)
```json
{
"inputAmount": "0.05",
"inputTokenAddress": "0x55d398326f99059fF775485246999027B3197955",
"tokenDecimal": "18",
"investmentId": 1589649169,
"address": "0x1ae68a40b9f903a469aed01574f3a9ab6d45c563",
"tickLower": "-33500",
"tickUpper": "-30450"
}
```
### Response Parameters
**data Object**
| Field | Type | Explanation |
| --- | --- | --- |
| investWithTokenList | Array | Dual-token allocation result list, typically containing 2 elements (token0 and token1) |
| > tokenAddress | String | Token contract address (lowercase format) |
| > chainIndex | String | Chain ID (e.g. "56" = BSC) |
| > coinAmount | String | Allocated amount (human-readable format, e.g. "0.05"). High-precision decimal, can be used directly as userInputList in transaction/enter |
### Response Example
**Success: USDT Input → USDT + RIVER Dual-Token Allocation**
```json
{
"code": 0,
"data": {
"investWithTokenList": [
{
"tokenAddress": "0x55d398326f99059ff775485246999027b3197955",
"chainIndex": "56",
"coinAmount": "0.05"
},
{
"tokenAddress": "0xda7ad9dea9397cffddae2f8a052b82f1484252b3",
"chainIndex": "56",
"coinAmount": "0.000275606738038671"
}
]
}
}
```
Explanation: The user invests 0.05 USDT. Based on the current tick ≈ -32932 price ratio, 0.05 USDT + 0.000275 RIVER are needed to form the dual-token entry.
### Complete Invocation Flow
1. **POST `/api/v6/defi/calculator/enter/info`** (current endpoint) — Input: single-token amount + tick range → Output: investWithTokenList
2. **Verify wallet balance** — Ensure both token balances meet the coinAmount specified in investWithTokenList
3. **POST `/api/v6/defi/transaction/enter`** — Pass in userInputList (= investWithTokenList) + tickLower + tickUpper + slippage → Output: calldata (DEPOSIT)
4. **Sign & send on-chain transaction** — Dual-token entry is a pure DEPOSIT (no on-chain swap)
- [Redemption](https://web3.okx.com/onchainos/dev-docs/wallet/defi-transaction-exit.md)
# Redemption
Call this endpoint when a user wants to withdraw assets from a DeFi protocol or repay a loan. Key parameters: `investmentId`, `address`. **Critical note**: always pass `redeemPercent` when redeeming (e.g., "1" for 100%) — otherwise, for tokens with dynamic balances such as aTokens, the on-chain execution may revert due to balance changes between API response and transaction confirmation. For V3 Pool redemptions, `tokenId` is required.
**URL**: POST `/api/v6/defi/transaction/exit`
## Request Parameters
| Field | Type | Required | Default | Explanation |
| --- | --- | --- | --- | --- |
| investmentId | String | Yes | — | Investment product ID |
| address | String | Yes | — | User wallet address |
| tokenId | String | No | — | V3 Pool NFT position token ID. Required for redemption when isV3Pool=true |
| redeemPercent | String | Recommended | — | Redemption ratio, e.g., "1"=100%, "0.5"=50%. When not provided, the Zap calldata's tokenIn amount uses the exact value from userInputList.coinAmount; when provided, MAX\_UINT256 is used to represent the full balance |
| userInputList | Array | No | — | For Farm and V2 Pool, input LP Token information; for other cases, input the target receiving token information |
| > tokenAddress | String | No | — | Required when `userInputList` is provided; Token contract address |
| > chainIndex | String | No | — | Required when `userInputList` is provided; Chain ID |
| > coinAmount | String | No | — | Required when `userInputList` is provided; Amount (human-readable, e.g., "0.05") |
| > tokenSymbol | String | No | — | Token symbol |
| > tokenPrecision | Integer | No | — | Precision |
| > tokenId | String | No | — | NFT token ID (V3 Pool scenario) |
| slippage | String | No | "0.01" | Transaction slippage (effective for adapter/Zap routing). "0.01"=1%, "0.1"=10% |
## Key Notes
> **redeemPercent is required**: When type="REDEEM", if `redeemPercent` is not provided, the Zap calldata generated by the API will use an exact value for `tokenIn.amount` (e.g., 60009) instead of MAX\_UINT256. For tokens like aToken whose interest grows dynamically, the balance may change between the API response and on-chain execution, causing `SafeERC20: low-level call failed`. When `redeemPercent: "1"` is provided, the amount is set to MAX\_UINT256, and the contract dynamically retrieves the actual balance.
## Request Examples
### Example 1: BSC V3 Pool pure redemption (50%, without userInputList)
Investment product: PancakeSwapV3 USDT-RIVER (id=1589649169)
```json
{
"investmentId": "1589649169",
"address": "0x1ae68a40b9f903a469aed01574f3a9ab6d45c563",
"tokenId": "6632738",
"redeemPercent": "0.5"
}
```
### Example 2: Avalanche Aave V3 redemption (redeemPercent="1")
Investment product: Aave V3 USDC (id=124, chainIndex=43114)
```json
{
"investmentId": "124",
"address": "0x1ae68a40b9f903a469aed01574f3a9ab6d45c563",
"userInputList": [
{
"tokenAddress": "0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e",
"chainIndex": "43114",
"coinAmount": "0.05"
}
],
"redeemPercent": "1"
}
```
### Example 3: Sui NAVI redemption
Investment product: NAVI USDC (id=32202, chainIndex=784)
```json
{
"investmentId": "32202",
"address": "0x2791c11545a2fef7d8b3188002c80343bf6dc64130a603914238d8660b3bddde",
"userInputList": [
{
"tokenAddress": "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC",
"chainIndex": "784",
"coinAmount": "0.3"
}
]
}
```
### Example 4: Solana Kamino repayment
Investment product: Kamino USDC Borrow (id=29130, chainIndex=501)
```json
{
"investmentId": "29130",
"address": "4GK2VMnznuPpg8gG9vqD5MM6889pjJ8WS2HqzktaBfSo",
"userInputList": [
{
"tokenAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"chainIndex": "501",
"coinAmount": "0.05"
}
]
}
```
## Response Parameters
| Field | Type | Explanation |
| --- | --- | --- |
| data.dataList | Array | Calldata result list (execute in order) |
| > callDataType | String | Operation type (approve, subscribe, redeem, claim) |
| > from | String | From address (user wallet address) |
| > to | String | To address (target contract address) |
| > value | String | Transfer amount (native token quantity). Empty string or "0x0" when no native token transfer is needed |
| > serializedData | String | Serialized transaction data. EVM: hex calldata (0x prefix); Solana: base58 encoded; Sui: base64 encoded BCS bytes |
| > originalData | String | Auxiliary metadata (JSON string) |
| > transactionPayload | String | Transaction template, only returned for Aptos chains |
| > signatureData | String | Signature data |
| > gas | String | Gas limit, only returned for non-EVM chains such as Aptos |
Common dataList combinations during redemption:
- Single step: `[WITHDRAW]` — pure redemption/repayment
- Two steps: `[APPROVE, WITHDRAW]` — requires prior aToken authorization to Zap
- Two steps: `[WITHDRAW, SWAP]` — V3 withdraw then swap back to target token (actually combined into a single callDataType=WITHDRAW,SWAP)
## Response Example
**EVM chain (Avalanche Aave V3 USDC redemption, APPROVE + WITHDRAW two steps)**
```json
{
"code": 0,
"msg": "",
"data": {
"dataList": [
{
"callDataType": "APPROVE",
"from": "0x1ae68a40b9f903a469aed01574f3a9ab6d45c563",
"to": "0x625e7708f30ca75bfd92586e17077590c60eb4cd",
"value": "0x0",
"serializedData": "0x095ea7b3000000000000000000000000...ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"originalData": "{\"callDataType\":\"APPROVE\",\"methodId\":\"0x095ea7b3\",\"methodDefine\":\"approve(address,uint256)\",...}",
"signatureData": ""
},
{
"callDataType": "WITHDRAW",
"from": "0x1ae68a40b9f903a469aed01574f3a9ab6d45c563",
"to": "0x794a61358d6845594f94dc1db02a252b5b4814ad",
"value": "0x0",
"serializedData": "0x69328dec000000000000000000000000...0000000000000000000000001ae68a40b9f903a469aed01574f3a9ab6d45c563",
"originalData": "{\"callDataType\":\"WITHDRAW\",\"methodId\":\"0x69328dec\",\"methodDefine\":\"withdraw(address,uint256,address)\",...}",
"signatureData": ""
}
]
}
}
```
> **Note**: When `redeemPercent="1"`, the amount parameter in `serializedData` is MAX_UINT256, and the contract dynamically retrieves the actual balance for redemption, avoiding insufficient balance issues caused by aToken interest growth.
- [Claim DeFi Protocol Rewards](https://web3.okx.com/onchainos/dev-docs/wallet/defi-transaction-claim.md)
# Claim DeFi Protocol Rewards
Call this endpoint when a user wants to claim rewards generated from DeFi investments. Key parameters: `address` (wallet address), `rewardType` (reward type). Common `rewardType` values include: `REWARD_INVESTMENT` (investment product mining rewards, most common), `REWARD_PLATFORM` (protocol rewards, requires `analysisPlatformId`), `V3_FEE` (V3 fees, requires `tokenId`), `REWARD_OKX_BONUS` (OKX Bonus), `REWARD_MERKLE_BONUS` (Merkle Bonus, requires `expectOutputList`), `UNLOCKED_PRINCIPAL` (matured principal, requires `principalIndex`).
**URL**: POST `/api/v6/defi/transaction/claim`
## Request Parameters
| Field | Type | Required | Explanation |
| --- | --- | --- | --- |
| address | String | Yes | User wallet address |
| rewardType | String | Yes | Reward type (see below) |
| investmentId | String | No | Investment product ID (required for all reward types except protocol rewards) |
| analysisPlatformId | String | No | Protocol ID (required when claiming protocol rewards) |
| expectOutputList | Array | No | Expected output token list |
| > chainIndex | String | No | Required when `rewardType=REWARD_MERKLE_BONUS` and `expectOutputList` is provided; Chain ID |
| > tokenAddress | String | No | Required when `rewardType=REWARD_MERKLE_BONUS` and `expectOutputList` is provided; Token contract address |
| > coinAmount | String | No | Required when `rewardType=REWARD_MERKLE_BONUS` and `expectOutputList` is provided; Amount (human-readable, e.g., "0.001") |
| tokenId | String | No | Position tokenId required when claiming V3 fees |
| principalIndex | String | No | Matured order index required when claiming matured principal |
**Supported rewardType values:**
| Value | Explanation |
| --- | --- |
| REWARD\_INVESTMENT | Investment product mining rewards (most common) |
| REWARD\_PLATFORM | Protocol rewards (requires `analysisPlatformId`) |
| V3\_FEE | V3 fees (requires `tokenId`) |
| REWARD\_OKX\_BONUS | OKX Bonus |
| REWARD\_MERKLE\_BONUS | Merkle Bonus (requires `expectOutputList`) |
| UNLOCKED\_PRINCIPAL | Matured principal (requires `principalIndex`) |
## Request Examples
### Example 1: Claim protocol rewards
```json
{
"rewardType": "REWARD_PLATFORM",
"analysisPlatformId": "144",
"address": "0x46e3420d02d628d3781fa16149e741bcb97da055",
"expectOutputList": [
{
"chainIndex": "1",
"tokenAddress": "0xc00e94Cb662C3520282E6f5717214004A7f26888",
"coinAmount": "0.001"
}
]
}
```
### Example 2: Claim investment product mining rewards
```json
{
"rewardType": "REWARD_INVESTMENT",
"investmentId": "27100",
"address": "0x46e3420d02d628d3781fa16149e741bcb97da055"
}
```
### Example 3: Claim V3 fees
```json
{
"rewardType": "V3_FEE",
"investmentId": "42101",
"address": "0x46e3420d02d628d3781fa16149e741bcb97da055",
"tokenId": "64219"
}
```
### Example 4: Claim OKX Bonus
```json
{
"rewardType": "REWARD_OKX_BONUS",
"investmentId": "42101",
"address": "0x46e3420d02d628d3781fa16149e741bcb97da055"
}
```
### Example 5: Claim Merkle Bonus
```json
{
"rewardType": "REWARD_MERKLE_BONUS",
"investmentId": "43304",
"address": "0x46e3420d02d628d3781fa16149e741bcb97da055",
"expectOutputList": [
{
"chainIndex": "747474",
"coinAmount": "10",
"tokenAddress": "0x2dca96907fde857dd3d816880a0df407eeb2d2f2"
}
]
}
```
### Example 6: Claim matured principal
For example, LSD: Lido requires a lock-up period after redemption. Once the lock-up period expires, claim the principal:
```json
{
"rewardType": "UNLOCKED_PRINCIPAL",
"investmentId": "10002",
"address": "0x46e3420d02d628d3781fa16149e741bcb97da055",
"principalIndex": "107580"
}
```
## Response Parameters
| Field | Type | Explanation |
| --- | --- | --- |
| dataList | Array | Calldata result list |
| > callDataType | String | Operation type (approve, subscribe, redeem, claim) |
| > from | String | From address (user wallet address) |
| > to | String | To address (target contract address) |
| > value | String | Transfer amount (native token quantity). Empty string or "0x0" when no native token transfer is needed |
| > serializedData | String | Serialized transaction data. EVM: hex calldata (0x prefix); Solana: base58 encoded; Sui: base64 encoded BCS bytes |
| > originalData | String | Auxiliary metadata (JSON string). EVM chains include function ABI; Aptos chains include module ABI JSON |
| > transactionPayload | String | Transaction template, only returned for Aptos chains |
| > signatureData | String | Signature data. EVM chains: Zap contract permit signature; non-EVM chains: server-side signature credential |
| > gas | String | Gas limit, only returned for non-EVM chains such as Aptos |
## Response Example
**Claim investment product mining rewards (REWARD_INVESTMENT)**
```json
{
"code": 0,
"msg": "",
"data": {
"dataList": [
{
"callDataType": "CLAIM",
"from": "0x46e3420d02d628d3781fa16149e741bcb97da055",
"to": "0x197e90f9fad81970ba7976f33cbd77088e5d7cf7",
"value": "0x0",
"serializedData": "0x3111e7b3000000000000000000000000...",
"originalData": "{\"callDataType\":\"CLAIM\",\"methodId\":\"0x3111e7b3\",...}",
"signatureData": ""
}
]
}
}
```
- [User Holdings](https://web3.okx.com/onchainos/dev-docs/wallet/defi-user-asset-overview.md)
# User Holdings
Query the user's current positions in DeFi protocols. Supports aggregated queries across multiple wallets, chains, and protocols, returning structured data including asset values and quantities for investment products within each protocol. Suitable for building DeFi portfolio management dashboards and other analytical tools.
- **Position Distribution**: Pass the user's wallet addresses (supporting multiple chains and wallets) to retrieve position overview across DeFi protocols.
- **Position Details**: Returns summary information including protocol names, total assets, and chain-level distribution. Also supports retrieving claimable rewards lists and lending market supply and borrow information.
- [API Reference](https://web3.okx.com/onchainos/dev-docs/wallet/defi-user-asset-api-reference.md)
# API Reference
- [Holdings Overview](https://web3.okx.com/onchainos/dev-docs/wallet/defi-user-asset-platform-list.md)
# Holdings Overview
Call this endpoint when a user wants to view their overall holdings across DeFi protocols. Pass the user's wallet address list (one wallet per chain, supports multiple chains simultaneously), and it returns the holdings overview for each DeFi protocol (protocol name, total assets in USD, number of investment products, etc.). After obtaining the `analysisPlatformId`, you can call `/api/v6/defi/user/asset/platform/detail` to view detailed holdings.
**URL**: POST `/api/v6/defi/user/asset/platform/list`
## Request Parameters
| Field | Type | Required | Explanation |
| --- | --- | --- | --- |
| walletAddressList | Array | Yes | Wallet address list |
| > chainIndex | String | Yes | Chain ID |
| > walletAddress | String | Yes | Wallet address |
| > pubKey | String | No | Public key (only used for BTC investment products) |
| tag | String | No | Custom tag |
## Request Examples
### Example 1: Query a single wallet's protocol holdings on ETH
```json
{
"walletAddressList": [
{
"chainIndex": "1",
"walletAddress": "0x7f429edeff8afc7bb3a2cf7db832fc86f6fa99da"
}
]
}
```
### Example 2: Query multiple wallets' protocol holdings across multiple chains
```json
{
"walletAddressList": [
{
"chainIndex": "1",
"walletAddress": "0x1234567890123456789012345678901234567890"
},
{
"chainIndex": "56",
"walletAddress": "0x1234567890123456789012345678901234567890"
},
{
"chainIndex": "137",
"walletAddress": "0x1234567890123456789012345678901234567890"
}
],
"tag": "portfolio_check"
}
```
## Response Parameters
| Field | Type | Explanation |
| --- | --- | --- |
| walletIdPlatformList | Array | Protocol list by wallet |
| > platformList | Array | Protocol list |
| > > platformName | String | Protocol name |
| > > analysisPlatformId | String | Protocol ID |
| > > platformLogo | String | Protocol Logo |
| > > currencyAmount | String | Protocol total assets (USD) |
| > > isSupportInvest | Boolean | Whether investment is supported |
| > > platformUrl | String | Protocol URL |
| > > networkBalanceList | Array | Assets by chain |
| > > > network | String | Chain name |
| > > > networkLogo | String | Chain logo |
| > > > chainIndex | String | Chain chainId |
| > > > currencyAmount | String | Protocol total assets on this chain (USD) |
| > > > investmentCount | Integer | Number of investment products |
| > > investmentCount | Integer | Number of investment products |
| > walletId | String | Wallet ID |
| > accountId | String | Account ID |
| > totalAssets | String | Wallet total assets (USD) |
| updateAt | String | Update timestamp (milliseconds) |
| assetStatus | int | Asset status: 1=updated, 2=updating |
## Response Example
**Base chain single wallet query**
```json
{
"code": 0,
"msg": "",
"data": {
"walletIdPlatformList": [
{
"platformList": [
{
"platformName": "Aave V3",
"analysisPlatformId": "10",
"platformLogo": "https://static.coinall.ltd/cdn/web3/protocol/logo/aave-v3.png/type=png_350_0?v=1774419400100",
"currencyAmount": "1.842485290389950657798412062008333407",
"isSupportInvest": true,
"platformUrl": "https://app.aave.com",
"networkBalanceList": [
{
"network": "BASE",
"networkLogo": "https://static.coinall.ltd/cdn/web3/invest/network/logo/base_new.png",
"chainIndex": "8453",
"currencyAmount": "1.842485290389950657798412062008333407",
"investmentCount": 3
}
],
"investmentCount": 3
},
{
"platformName": "Morpho",
"analysisPlatformId": "119170",
"platformLogo": "https://static.coinall.ltd/cdn/web3/protocol/logo/morphoblue-none.png/type=png_350_0?v=1774419195442",
"currencyAmount": "1.01000616616",
"isSupportInvest": true,
"platformUrl": "https://app.morpho.org",
"networkBalanceList": [
{
"network": "BASE",
"networkLogo": "https://static.coinall.ltd/cdn/web3/invest/network/logo/base_new.png",
"chainIndex": "8453",
"currencyAmount": "1.01000616616",
"investmentCount": 1
}
],
"investmentCount": 1
},
{
"platformName": "Compound V3",
"analysisPlatformId": "144",
"platformLogo": "https://static.coinall.ltd/cdn/web3/protocol/logo/compound-v3.png/type=png_350_0?v=1774418057245",
"currencyAmount": "0.358024146564053025",
"isSupportInvest": true,
"platformUrl": "https://v3-app.compound.finance",
"networkBalanceList": [
{
"network": "BASE",
"networkLogo": "https://static.coinall.ltd/cdn/web3/invest/network/logo/base_new.png",
"chainIndex": "8453",
"currencyAmount": "0.358024146564053025",
"investmentCount": 3
}
],
"investmentCount": 3
}
],
"walletId": "a11a9be9-00b9-43de-b450-6e090a58297a",
"accountId": "a11a9be9-00b9-43de-b450-6e090a58297a",
"totalAssets": "3.216207599599296551158095653123634461"
}
],
"updateAt": 1774429492000,
"assetStatus": 1
}
}
```
> The actual response returns 4 protocols; only the first 3 are shown here. `currencyAmount` is denominated in USD, and `assetStatus=1` indicates that data has been fully updated.
- [Holdings Detail](https://web3.okx.com/onchainos/dev-docs/wallet/defi-user-asset-platform-detail.md)
# Holdings Detail
Call this endpoint when a user wants to view detailed holdings in a specific DeFi protocol. Requires wallet address list and target protocol information (`analysisPlatformId` obtained from the previous protocol list endpoint). The response includes detailed asset information for each investment product, V3 position information, claimable rewards, etc. Based on the returned `investmentId` and position information, you can further call the transaction execution API to redeem or claim rewards.
**URL**: POST `/api/v6/defi/user/asset/platform/detail`
## Request Parameters
| Field | Type | Required | Explanation |
| --- | --- | --- | --- |
| walletAddressList | Array | Yes | Wallet list |
| > chainIndex | String | Yes | Chain ID |
| > walletAddress | String | Yes | Wallet address |
| > pubKey | String | No | Public key (only used for BTC investment products) |
| platformList | Array | Yes | DeFi protocols to query |
| > chainIndex | String | No | Chain ID |
| > analysisPlatformId | String | No | Protocol ID |
## Request Example
### Example 1: Query by protocol ID
```json
{
"walletAddressList": [
{
"chainIndex": "1",
"walletAddress": "0x7f429edeff8afc7bb3a2cf7db832fc86f6fa99da"
}
],
"platformList": [
{
"chainIndex": "1",
"analysisPlatformId": "44"
}
]
}
```
## Response Parameters
| Field | Type | Explanation |
| --- | --- | --- |
| walletIdPlatformDetailList | Array | Holdings by wallet |
| > networkHoldVoList | Array | Holdings by network |
| > > network | String | Network name |
| > > chainIndex | String | Chain ID |
| > > isSupportInvest | Boolean | Whether investment is supported |
| > > totalAssert | String | Total assets (USD) |
| > > investTokenBalanceVoList | Array | Holdings by investment product |
| > > > investmentName | String | Investment product name |
| > > > validatorName | String | Validator name |
| > > > currentPrice | String | Current price (e.g., 1 DAI - 1.0393 USDC) |
| > > > investmentId | String | Investment product ID |
| > > > specialPositionAssetKey | String | Special position asset key |
| > > > sourceInvestmentId | String | Source investment product ID |
| > > > feeRate | String | Fee rate (used by Uni V3, e.g., 0.0003) |
| > > > aggregateProductId | String | Aggregate product ID |
| > > > isInvestTypeSupport | Boolean | Whether the investment type is supported |
| > > > investType | Integer | Investment type (1:save, 2:pool, 3:farm, 4:vaults, 5:stake, 6:borrow, 7:staking, 8:locked, 9:deposit, 10:vesting) |
| > > > investName | String | Investment type description |
| > > > investLogo | Object | Investment product logo information |
| > > > positionList | Array | Position detail list |
| > > > > range | String | Price range (e.g., 0.892 - 0.992 USDC per DAI) |
| > > > > reverseRange | String | Reverse price range |
| > > > > rangeInfo | Object | Price range details |
| > > > > tokenId | String | NFT tokenId (e.g., 93828) |
| > > > > positionName | String | Position name |
| > > > > nftLogo | String | NFT Logo |
| > > > > positionStatus | String | Position status (ACTIVE, INACTIVE) |
| > > > > assetsTokenList | Array | Asset token list |
| > > > > showIncreaseLiquidity | boolean | Whether to show increase liquidity |
| > > > > rewardDefiTokenInfo | Array | Compound rewards (same structure as availableRewards element) |
| > > > > unclaimFeesDefiTokenInfo | Array | Uni V3 fees (same structure as availableRewards element) |
| > > > > totalValue | String | Total value (USD) |
| > > > > isNarrow | Boolean | Whether the range is too narrow |
| > > > > needInvest | Boolean | Whether investment is needed (true: needs investment, false: already invested) |
| > > > > settlementTime | String | Settlement time (second-level timestamp) |
| > > > > assetPositionType | String | Position type (0: Uni V3 position, 1: position with expiration) |
| > > > > positionExtInfoList | Array | Position extended information list |
| > > > assetsTokenList | Array | Asset token list |
| > > > borrowTokenList | Array | Borrow token list (LSDFI) |
| > > > rewardDefiTokenInfo | Array | Compound rewards (same structure as availableRewards element) |
| > > > fundsInfo | Array | Funds information (same structure as availableRewards element) |
| > > > extraData | Object | Detail page extended data |
| > > > totalValue | String | Total value (USD) |
| > > > overflowTotalValue | String | Overflow total value (USD) |
| > > > collateralRatioInfo | Object | Collateral ratio information (LSDFI) |
| > > > rewardAddress | String | BTC reward receiving address |
| > > > maturityTime | String | Pendle maturity time |
| > > > fixedApy | String | Pendle fixed APY |
| > > > browserUrl | String | OKLink explorer URL |
| > > > poolId | String | Pool ID |
| > > > poolAddress | String | Pool address |
| > > > tagList | Array | Tag list |
| > > > investNameTagList | Array | Investment name tag list |
| > > > extraFieldList | Array | Extra display field list |
| > > > subTitle | String | Subtitle (e.g., ID: 205c8e01aa#12) |
| > > > investmentCategory | Integer | Investment category (0: Earn, 1: Brc20, 2: LSDFI) |
| > > > investmentClassify | String | Investment classification |
| > > > nonPoolPositionList | Array | Non-UniV3 investment product position list |
| > > > investmentKey | String | Investment key |
| > > > marketId | String | Lending market ID |
| > > > perpetual | Object | Perpetual contract information |
| > > > detailPath | String | Web detail page redirect path |
| > > > yieldYesterday | BigDecimal | Yesterday's yield (USD) |
| > > > totalEarnings | BigDecimal | Total earnings (USD) |
| > > investMarketTokenBalanceVoList | Array | Holdings by lending market |
| > > > assetMap | Object | Asset data mapping (includes supply and borrow, value structure same as investTokenBalanceVoList element) |
| > > > marketId | String | Market identifier |
| > > > healthRate | Object | Market health rate (non-null when there are borrows) |
| > > > marketRewards | Array | Market rewards (same structure as availableRewards element) |
| > > > totalValue | String | Total value (USD) |
| > > availableRewards | Array | Claimable rewards |
| > > > baseDefiTokenInfos | Array | Reward token element list |
| > > > buttonType | Integer | Button type (0: hidden, 1: requires authorization, 2: disabled, 3: enabled) |
| > > > callDataExtJson | String | CallData extended JSON |
| > > > claimMode | Integer | Claim mode (null=normal, 0=stake OKT, 1=redirect to secondary page) |
| > > > extraData | Array | Reward detail extended data |
| > > > > coinAmount | String | Claimable amount (string precision value) |
| > > > > principalIndex | String | Principal batch/index identifier; can be passed in the claim API when rewardType is UNLOCKED_PRINCIPAL |
| > > > > claimable | Boolean | Whether claimable (true/false) |
| > > > > status | String | Claim status (e.g., CLAIMABLE means claimable) |
| > > > rewardType | String | Reward type string (REWARD\_INVESTMENT, REWARD\_PLATFORM, V3\_FEE, REWARD\_OKX\_BONUS, REWARD\_MERKLE\_BONUS, UNLOCKED\_PRINCIPAL) |
| > > > unclaimedTokenList | Array | Merkl bonus unclaimed token list (by chain) |
| > > > network | String | Network name |
| > > > chainIndex | String | Chain ID |
| > > > urlInfo | Object | URL redirect information |
| > > > currencyAmount | String | Currency amount |
| > > fundsInfo | Array | Funds information (same structure as availableRewards element) |
| > > airDropRewardInfo | Array | Airdrop rewards (same structure as availableRewards element) |
| > > extraData | Object | Extended data |
| > walletId | String | Wallet ID |
| platformName | String | Protocol name |
| analysisPlatformId | String | Analysis group protocol ID |
| platformLogo | String | Protocol Logo |
| platformUrl | String | Protocol URL |
## Response Example
```json
{
"code": "0",
"msg": "",
"data": [
{
"walletIdPlatformDetailList": [
{
"networkHoldVoList": [
{
"network": "Ethereum",
"chainIndex": "1",
"isSupportInvest": true,
"totalAssert": "0.8152800000000020382",
"investTokenBalanceVoList": [
{
"investmentName": "ETH",
"investmentId": 22850,
"specialPositionAssetKey": "1-0x308861a430be4cce5502d0a12724771fc6daf216-0x35fa164735182de50811e8e2e824cfb9b6118ac25",
"aggregateProductId": 73676,
"isInvestTypeSupport": true,
"investType": 5,
"investName": "Stake",
"investLogo": {
"middleLogoList": [
{
"tokenLogo": "https://static.coinall.ltd/cdn/wallet/logo/ETH-20220328.png",
"tokenName": "ETH"
}
],
"bottomRightLogoList": [
{
"tokenLogo": "https://static.coinall.ltd/cdn/invest/platform/EtherFi.png",
"tokenName": "ether.fi"
}
],
"topRightLogoList": [
{
"tokenLogo": "https://static.coinall.ltd/cdn/wallet/logo/ETH-20220328.png",
"tokenName": "ETH"
}
],
"topLeftLogoList": []
},
"assetsTokenList": [
{
"tokenSymbol": "ETH",
"tokenLogo": "https://static.coinall.ltd/cdn/wallet/logo/ETH-20220328.png",
"coinAmount": "0.000000000000000001",
"currencyAmount": "0.0000000000000020382",
"tokenPrecision": 18,
"tokenAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"network": "ETH"
}
],
"rewardDefiTokenInfo": [],
"fundsInfo": [
{
"baseDefiTokenInfos": [
{
"tokenSymbol": "ETH",
"tokenLogo": "https://static.coinall.ltd/cdn/wallet/logo/ETH-20220328.png",
"coinAmount": "0.0002",
"currencyAmount": "0.40764",
"tokenPrecision": 18,
"tokenAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"network": "ETH",
"buttonType": 3,
"callDataExtJson": "{\"isExtraReward\":true}"
}
],
"callDataExtJson": "{\"isExtraReward\":true}",
"extraData": {
"claimDetails": [
{
"coinAmount": "0.000100000000000000",
"principalIndex": "72021",
"claimable": true,
"status": "CLAIMABLE"
},
{
"coinAmount": "0.000100000000000000",
"principalIndex": "72452",
"claimable": true,
"status": "CLAIMABLE"
}
]
},
"rewardType": "UNLOCKED_PRINCIPAL"
}
],
"extraData": {},
"totalValue": "0.4076400000000020382",
"investmentCategory": 0,
"investmentKey": "1-0x308861a430be4cce5502d0a12724771fc6daf216-0x35fa164735182de50811e8e2e824cfb9b6118ac2",
"marketId": "",
"detailPath": "ether-fi-ethereum-eth-22850"
},
{
"investmentName": "ETH",
"specialPositionAssetKey": "1-0x7d5706f6ef3f89b3951e23e557cdfbc3239d4e2c-72021-08",
"isInvestTypeSupport": false,
"investType": 8,
"investName": "Locked Staking",
"investLogo": {
"middleLogoList": [
{
"tokenLogo": "https://static.coinall.ltd/cdn/wallet/logo/ETH-20220328.png",
"tokenName": "ETH"
}
]
},
"assetsTokenList": [
{
"tokenSymbol": "ETH",
"tokenLogo": "https://static.coinall.ltd/cdn/wallet/logo/ETH-20220328.png",
"coinAmount": "0.0001",
"currencyAmount": "0.20382",
"tokenPrecision": 18,
"tokenAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"network": "ETH"
}
],
"rewardDefiTokenInfo": [],
"extraData": {},
"totalValue": "0.20382",
"investmentCategory": 0,
"investmentKey": "1-0x7d5706f6ef3f89b3951e23e557cdfbc3239d4e2c-72021-0",
"marketId": ""
},
{
"investmentName": "ETH",
"specialPositionAssetKey": "1-0x7d5706f6ef3f89b3951e23e557cdfbc3239d4e2c-72452-08",
"isInvestTypeSupport": false,
"investType": 8,
"investName": "Locked Staking",
"investLogo": {
"middleLogoList": [
{
"tokenLogo": "https://static.coinall.ltd/cdn/wallet/logo/ETH-20220328.png",
"tokenName": "ETH"
}
]
},
"assetsTokenList": [
{
"tokenSymbol": "ETH",
"tokenLogo": "https://static.coinall.ltd/cdn/wallet/logo/ETH-20220328.png",
"coinAmount": "0.0001",
"currencyAmount": "0.20382",
"tokenPrecision": 18,
"tokenAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"network": "ETH"
}
],
"rewardDefiTokenInfo": [],
"extraData": {},
"totalValue": "0.20382",
"investmentCategory": 0,
"investmentKey": "1-0x7d5706f6ef3f89b3951e23e557cdfbc3239d4e2c-72452-0",
"marketId": ""
}
],
"investMarketTokenBalanceVoList": [],
"availableRewards": [],
"airDropRewardInfo": [],
"extraData": {}
}
],
"walletId": "1a33c32f-2b8d-426e-9be2-33445f0fcd87"
}
],
"platformName": "ether.fi",
"analysisPlatformId": "260",
"platformLogo": "https://static.coinall.ltd/cdn/invest/platform/EtherFi.png",
"platformUrl": "https://app.ether.fi"
}
]
}
```
- [Error Codes](https://web3.okx.com/onchainos/dev-docs/wallet/defi-error-code.md)
# Error Codes
| Error Code | HTTP Status Code | Explanation |
| --- | --- | --- |
| 0 | 200 | Success |
| 50011 | 429 | Rate limit exceeded. Please refer to the API documentation and reduce your request frequency |
| 50014 | 400 | Parameter 0 cannot be empty |
| 50026 | 500 | System error. Please try again later |
| 50103 | 401 | Request header "OK-ACCESS-KEY" cannot be empty |
| 50104 | 401 | Request header "OK-ACCESS-PASSPHRASE" cannot be empty |
| 50105 | 401 | Request header "OK-ACCESS-PASSPHRASE" is incorrect |
| 50106 | 401 | Request header "OK-ACCESS-SIGN" cannot be empty |
| 50107 | 401 | Request header "OK-ACCESS-TIMESTAMP" cannot be empty |
| 50111 | 401 | Invalid OK-ACCESS-KEY |
| 50112 | 401 | Invalid OK-ACCESS-TIMESTAMP |
| 50113 | 401 | Invalid signature |
| 84000 | 400 | Parameter error |
| 84001 | 200 | Protocol not supported |
| 84003 | 200 | Protocol not supported |
| 84007 | 200 | Investment product not supported |
| 84010 | 200 | Token not supported |
| 84011 | 200 | Protocol logo is empty |
| 84013 | 200 | Swap not supported |
| 84014 | 200 | Balance validation failed |
| 84016 | 200 | Smart contract execution failed |
| 84017 | 200 | Staking rate validation failed |
| 84018 | 200 | Rebalancing failed |
| 84019 | 200 | Address format mismatch |
| 84021 | 200 | Syncing assets |
| 84022 | 200 | Authorization type not supported |
| 84023 | 200 | External service call error |
| 84024 | 200 | Investment product does not exist |
| 84025 | 200 | No claimable rewards |
| 84026 | 200 | Investment product status abnormal |
| 84027 | 200 | Transaction data construction failed |
| 84028 | 200 | Asset query failed |
| 84029 | 200 | Principal is still in lock-up period and cannot be claimed |
| 84030 | 200 | Principal claim has expired |
| 84031 | 200 | This product type does not support this time range |
| 84032 | 200 | This API is only supported for V3 DEX Pool products |
| 84998 | 200 | Investment error |
| 84999 | 500 | Default error |
- [Features and Services](https://web3.okx.com/onchainos/dev-docs/payments/x402-introduction.md)
# Features and Services
Onchain OS Payment provides protocol-native on-chain payment capabilities for APIs, MCP tools, and AI Agents. Built on the x402 open protocol, running on the X Layer network.
## Who Is It For?
**Seller**
Can be an API service provider, an MCP tool provider, or an Agent that offers external services.
Typical scenarios:
- Paid APIs such as weather, exchange rates, and on-chain data: charged per call, deducted upon invocation
- MCP tools such as code generation, translation, and image recognition: automatic payment when called by an Agent
- Agent services providing strategy signals and risk scoring: Agent-to-Agent transactions with automatic pricing and settlement
**Buyer**
Can be an ordinary user's Agent, or a developer providing App services on behalf of the Buyer.
Typical scenarios:
- Purchase paid data sources on demand through an Agent, with automatic payment and data retrieval
- Call and purchase on-chain analytics APIs through an Agent, making autonomous decisions based on results
- Integrate paid services in your App backend; users initiate payment, and the backend handles payment automatically
## How It Works
The core interaction of Onchain OS Payment is a four-step flow:
1. Buyer requests a resource
2. Seller returns payment requirements (amount, token, network)
3. Buyer re-requests with a signed credential attached
4. Seller verifies and returns the resource

No account registration required, no manual intervention.
## Payment Methods
The only difference between payment methods lies in the settlement rhythm: Instant Payment settles on-chain immediately after each signature, while Batch Payment allows the Buyer to sign multiple times to access resources first, and then the Seller batches multiple transactions into a single on-chain settlement.
Choose the payment method that fits your business scenario:
| **Payment Method** | **Use Case** | **Settlement Method** | **Compatible Protocol** |
|:---:|:---:|:---:|:---:
| **Instant Payment** | Single API call, content purchase, low-frequency high-value | Independent on-chain settlement per request |x402 |
| **Batch Payment** | High-frequency micro-payments, micropayments, Agent autonomous transactions | Buyer signs multiple times, Seller batches and settles on-chain | x402 |
> Not sure which one to choose? Start with [One-time Payment](./methods-onetime) -- integration takes just 5 minutes.
## Why Choose Onchain OS Payment
**Flexible Payment Methods** -- Instant Payment and Batch Payment cover the vast majority of payment scenarios.
**Zero Gas** -- Transfers and payments using USDG / USDT on X Layer incur zero Gas fees. Off-chain signing in Batch Payment mode also means zero Gas.
**Multi-Token Support** -- Supports major stablecoins including USDT and USDG.
**SDK** -- SDKs available in Node.js, Rust, and Go.
**Built-in Compliance** -- KYT (Know Your Transaction) risk identification is built into every payment, with no additional integration required.
**Open Standard** -- Built on the x402 protocol, not locked into any single platform.
- [Core Concepts](https://web3.okx.com/onchainos/dev-docs/payments/core-concept.md)
# Core Concepts
## HTTP 402
**What is HTTP 402?**
HTTP 402 is a reserved status code in the HTTP specification, meaning "Payment Required" -- the client must pay to access the resource. It was defined in the HTTP/1.1 specification (RFC 2616) back in 1999, but since there was no accompanying payment mechanism at the time, it remained in a "reserved but unused" state for over twenty years.
**Why has 402 never been put to use?**
Because the HTTP specification only defined the semantics of the status code ("payment required"), but did not define how to pay, whom to pay, or how to verify payment. Without a standardized payment flow, browsers and clients had no idea what to do upon receiving a 402. The x402 protocol was created to fill this gap -- it defines a complete payment interaction flow based on 402. See the x402 Protocol FAQ for details.
**Why use 402 instead of a custom status code?**
Because 402's semantics already mean "payment required" -- this is part of the HTTP standard, and all HTTP clients, proxies, CDNs, and load balancers can correctly transmit this status code. Using a standard status code means no modifications are needed to any intermediate layer of network infrastructure, and the payment protocol can run directly on top of the existing HTTP ecosystem.
## x402 Protocol
**What is x402?**
x402 is an open payment protocol standard based on the HTTP 402 status code. It defines a complete request-pay-deliver flow: the server returns 402 + payment requirements -> the client pays on-chain -> the client retries with a payment credential -> the server verifies and returns the resource.
**What are the roles in the x402 protocol?**
- **Buyer**: The party that initiates the request and makes the payment, typically an AI Agent or application client. The Buyer signs and constructs a payment credential, submitting it to the Seller via an HTTP request header.
- **Seller**: The party that provides paid APIs or resources. The Seller returns 402 + payment requirements on unpaid requests, and delivers the resource upon receiving a valid payment credential.
- **Facilitator**: A third-party service responsible for payment Verification and on-chain Settlement. The Facilitator frees the Seller from maintaining blockchain nodes, managing Gas, and submitting transactions -- all of which are handled by the Facilitator. The core role of Onchain OS Payment is the Facilitator.
**Who proposed x402?**
x402 was proposed and open-sourced by Coinbase, and is currently being advanced by the x402 Foundation (co-governed by Coinbase and Cloudflare). It is an open standard that anyone can implement.
**Is x402 only for AI Agents?**
No. x402 is a general-purpose HTTP payment protocol that can be used by browsers, mobile apps, CLIs, and automation scripts. AI Agents are currently the most typical use case, because Agents need to autonomously complete paid API calls without human intervention.
## Facilitator
**What is a Facilitator?**
A Facilitator is a third-party role in the x402 protocol, responsible for payment Verification and on-chain Settlement. In x402's Buyer-Seller-Facilitator three-party architecture, the Facilitator serves as the trust layer in between: after the Buyer signs a payment, the Facilitator verifies the validity of the payment credential and submits the on-chain transaction, so the Seller does not need to maintain blockchain infrastructure on its own.
**Why is a Facilitator needed? Can't the Buyer just transfer directly to the Seller?**
Technically yes, but the Facilitator solves several practical problems:
**Seller doesn't need to run nodes**: The Facilitator handles on-chain interactions for the Seller (RPC connections, Gas management, transaction submission), and the Seller only needs a receiving address.
**Verification and Settlement are separated**: The Facilitator first performs cryptographic verification (~100ms, off-chain), and only submits the on-chain Settlement after confirming the payment credential is valid. This makes verification extremely fast while ensuring reliable settlement.
**Unified Compliance entry point**: Compliance checks such as KYT screening can be executed uniformly at the Facilitator layer, rather than each Seller implementing them individually.
**Is Onchain OS a Facilitator?**
Yes. The core role of Onchain OS Payment is the Facilitator -- receiving the Buyer's payment credential, verifying its validity, performing KYT screening, submitting on-chain Settlement, and confirming payment completion to the Seller. Coinbase also provides its own Facilitator service (CDP Facilitator); both are different implementations of the same role.
**Does the Facilitator have access to my funds?**
The Facilitator submits on-chain transactions, but funds transfer directly from the Buyer to the Seller's receiving address. The Facilitator does not custody funds, nor does it act as an intermediary account. Its role is more like a "notary" -- it verifies and executes, but does not hold funds.
**Can I build my own Facilitator?**
x402 is an open protocol, and in theory anyone can implement their own Facilitator. However, building your own means you need to maintain the full suite of infrastructure: blockchain node connections, Gas management, transaction signing and broadcasting, KYT Compliance, and more. Using the Onchain OS Payment Facilitator service can save you from all of this.
## Settlement
**What is Settlement?**
Settlement is the on-chain execution process where, after the Buyer's payment credential is verified by the Facilitator, funds are actually transferred from the Buyer's address to the Seller's receiving address. In the x402 flow, Settlement is handled by the Facilitator -- the Seller does not need to submit on-chain transactions on its own.
In simple terms: Verification answers "is this payment valid?", while Settlement answers "when do the funds arrive?". Onchain OS supports both Synchronous Settlement and Asynchronous Settlement modes, which differ in whether the Seller waits for fund confirmation before delivering the resource, or delivers first with the funds arriving shortly after.
**What is Synchronous Settlement?**
Synchronous Settlement (`syncSettle = true`) means the Facilitator submits the on-chain transaction and waits for final confirmation before notifying the Seller to deliver the resource. "Confirm first, deliver second" -- no bad debt risk, but requires waiting for on-chain block confirmation.
**What is Asynchronous Settlement?**
Asynchronous Settlement (`syncSettle = false`) means the Facilitator returns a transaction hash immediately after verifying the payment credential, and the Seller delivers the resource right away, while on-chain confirmation completes asynchronously in the background. "Deliver first, confirm later" -- near-zero latency, suitable for high-frequency, low-value scenarios.
**Is Asynchronous Settlement safe? Is there bad debt risk?**
Extremely low. Once a transaction is broadcast to the chain, it is virtually impossible to roll back (especially under X Layer's finality mechanism). Onchain OS checks transaction parameters and broadcast status during the verification step, leaving an extremely narrow window for fraud. For high-frequency, low-value calls (e.g., an inference API at $0.001 per call), the risk-reward ratio of Asynchronous Settlement is far better than the latency cost of Synchronous Settlement.
**Which mode should I choose for my use case?**
A simple rule of thumb: if the value per call is less than $1 and the call frequency is high, choose asynchronous; if the value per call is high or you have strict requirements for fund confirmation, choose synchronous. Most AI Agent scenarios are suitable for Asynchronous Settlement.
**What happens if Asynchronous Settlement fails?**
If an on-chain transaction ultimately fails to confirm (extremely rare), the Seller can track it through Onchain OS's settlement status API. At the protocol level, an unconfirmed transaction means the Buyer was not actually charged, and the loss is borne by the Seller -- but the probability is extremely low.
## Gas Subsidy
**What is Gas Subsidy?**
Gas Subsidy is the fee waiver provided by OKX for payment transactions on X Layer. Normally, on-chain transactions require paying Gas fees (miner/validator processing fees), but when making x402 payments with USDG or USDT on X Layer, the Gas fee is zero -- covered by OKX.
**Does Gas Subsidy apply to all chains?**
No. Gas Subsidy currently only applies to payment transactions using USDG and USDT on X Layer. When paying on other chains, Gas fees are borne by the Buyer at the standard rate.
**What does zero Gas mean for micropayments?**
It means any amount, no matter how small, is economically viable. On chains with Gas fees, a $0.001 payment with a $0.01 Gas fee is clearly unreasonable. With zero Gas on X Layer, the Buyer's actual cost = the Seller's listed price, with no additional overhead. This is a foundational requirement for high-frequency, low-value AI Agent call scenarios.
**Will Gas Subsidy be discontinued?**
The Gas Subsidy policy depends on OKX's operational strategy. It is recommended to follow Onchain OS official announcements for the latest information. Even if the policy is adjusted in the future, X Layer's Gas fee rate is still far lower than high-cost chains like Ethereum mainnet.
## KYT
**What is KYT?**
KYT (Know Your Transaction) is a transaction-level Compliance screening technology. It analyzes on-chain fund flows to identify transactions associated with high-risk entities such as sanctioned addresses, mixers, darknet markets, and stolen funds.
**What is the difference between KYT and KYC?**
KYC (Know Your Customer) is identity verification of "people" -- who you are, where you live, your ID number. KYT is risk assessment of "transactions" -- where the funds came from, and whether they are associated with high-risk activities. The x402 protocol does not require user registration or KYC, but Onchain OS achieves Compliance at the transaction level through KYT, filtering illicit funds while preserving anonymity.
**Does KYT screening slow down payments?**
Not noticeably. KYT screening is executed in parallel with transaction verification, and its impact on end-to-end latency is on the order of milliseconds.
**What if my transaction is flagged by KYT?**
A flagged transaction will not pass verification, the server will not return the paid resource, and the Buyer will not be charged. The specific rejection reason will be returned via an error code. If you believe it is a false positive, you can contact the Onchain OS support team.
**As a Seller, do I need to perform KYT myself?**
No. Onchain OS automatically performs KYT checks during the transaction verification step, completely transparent to the Seller. This is one of the core differentiators of Onchain OS compared to the bare x402 protocol.
- [Supported Networks and Currencies](https://web3.okx.com/onchainos/dev-docs/payments/supported-networks.md)
# Supported Networks and Currencies
Currently supported network: **X Layer** (ChainIndex: 196).
Supported currencies:
| Currency | Contract Address |
|----------|--------------------------------------------------------|
| USDG | 0x4ae46a509f6b1d9056937ba4500cb143933d2dc8 |
| USDT | 0x779ded0c9e1022225f8e0630b35a9b54be713736 |
- [Overview](https://web3.okx.com/onchainos/dev-docs/payments/quickstart-overview.md)
# Overview
Onchain OS Payment enables any Buyer and Seller to initiate and receive on-chain payments. The Seller declares a charge requirement, the Buyer completes the payment, and the Seller returns the resource immediately after verification — the entire process is completed within a single HTTP request.
This quick start guide uses a single payment as an example to help you complete your first on-chain payment in 5 minutes.
**Choose your starting point:**
- [Seller Quickstart](https://web3.okx.com/onchainos/dev-docs/payments/payment-use-seller.md)
# Seller Quickstart
If you are a seller, you can quickly get set up via an AI agent (coming soon) or via API. Once complete, your service will be able to charge buyers and AI Agents for access through the x402 protocol.
- [Build with Agent](https://web3.okx.com/onchainos/dev-docs/payments/payment-use-seller-ai.md)
# Build with Agent
Configure payment collection capabilities for Onchain OS Payment through AI. Simply send the prompt to an AI to generate the complete 402 payment integration code -- no need to manually implement the 402 response or transaction verification.
## Prerequisites
- Payment Wallet: Any EVM-compatible wallet (e.g., OKX Wallet, [OKX Agentic Wallet](https://web3.okx.com/zh-hans/onchainos/dev-docs/wallet/agentic-wallet))
- API Key: Apply through the [OKX Developer Portal](https://web3.okx.com/zh-hans/onchainos/dev-portal)
- Business Backend: Your own API service or backend server
## Configuration
Send the following prompt to an AI to generate the complete integration code. The weather query API below is for reference only:
```
// TS version
Reference https://github.com/okx/payments/blob/main/typescript/SELLER.md
// Rust version
Reference https://github.com/okx/payments/blob/main/rust/SELLER.md
// Go version
Reference https://github.com/okx/payments/blob/main/go/SELLER.md
I have a weather query API (/weather). Help me deploy it to localhost:4021 and add payment functionality using onchain-payment-sdk.
Charge 0.1 USDT per call, use X Layer network (eip155:196), and the payment address is 0xMyWalletAddress.
```
> During the process, the AI will prompt you to configure your recipient wallet and API Key.
## Verification
Use the following code to request your paid resource:
```shell
curl -i http://localhost:4021/weather
```
After the request, you will see a `402` + `PAYMENT-REQUIRED` response header, indicating that the integration was successful:
```http
HTTP/1.1 402 Payment Required // 402状态码
content-type: application/json
payment-required: eyJ4NDAyVmVy.....jAifX1dfQ== // 支付信息(base64 encode编码)
```
The base64-decoded `payment-required` payment information will also be returned:
```json
{
"x402Version": 2,
"resource": {
"url": "/weather",
"description": "Get current weather data for any location",
"mimeType": "application/json"
},
"accepts": [
{
"scheme": "exact",
"network": "eip155:196",
"asset": "0x779ded0c9e1022225f8e0630b35a9b54be713736",
"amount": "1000",
"payTo": "0xb483abdb92f8061e9a3a082a4aaaa6b88c381308",
"maxTimeoutSeconds": 600000,
"extra": {
"name": "USD₮0",
"version": "1"
}
},
{
"scheme": "aggr_deferred",
"network": "eip155:196",
"asset": "0x779ded0c9e1022225f8e0630b35a9b54be713736",
"amount": "1000",
"payTo": "0xb483abdb92f8061e9a3a082a4aaaa6b88c381308",
"maxTimeoutSeconds": 600000,
"extra": {
"version": "1",
"name": "USD₮0"
}
}
]
}
```
- [Build with SDK](https://web3.okx.com/onchainos/dev-docs/payments/payment-use-seller-api.md)
# Build with SDK
Integrate Onchain OS Payment collection capabilities via SDK. Built-in 402 protocol response and on-chain transaction verification — no need to implement it yourself through the API.
## Prerequisites
- Payment Wallet: Any EVM-compatible wallet (e.g., OKX Wallet, [OKX Agentic Wallet](https://web3.okx.com/zh-hans/onchainos/dev-docs/wallet/agentic-wallet))
- API Key: Apply through the [OKX Developer Portal](https://web3.okx.com/zh-hans/onchainos/dev-portal)
- Business Backend: Your own API service or backend server
## Install SDK
**Node.js**
```shell
npm install express @okxweb3/x402-express @okxweb3/x402-core @okxweb3/x402-evm
npm install -D typescript tsx @types/express @types/node
```
**Go**
```shell
go get github.com/okx/payments/go
```
**Rust**
```toml
# Cargo.toml configuration
[dependencies]
x402-axum = { git = "https://github.com/okx/payments" }
x402-core = { git = "https://github.com/okx/payments" }
x402-evm = { git = "https://github.com/okx/payments" }
```
## Integrate Service
**Node.js**
```typescript
import express from "express";
import {
paymentMiddleware,
x402ResourceServer,
} from "@okxweb3/x402-express";
import { ExactEvmScheme } from "@okxweb3/x402-evm/exact/server";
import { OKXFacilitatorClient } from "@okxweb3/x402-core";
const app = express();
const NETWORK = "eip155:196"; // X Layer Mainnet
const PAY_TO = process.env.PAY_TO_ADDRESS || "0xYourWalletAddress";
// OKX Facilitator Client (requires OKX API credentials)
const facilitatorClient = new OKXFacilitatorClient({
apiKey: "OKX_API_KEY",
secretKey: "OKX_SECRET_KEY",
passphrase: "OKX_PASSPHRASE",
});
// Create resource server and register EVM exact scheme
const resourceServer = new x402ResourceServer(facilitatorClient);
resourceServer.register(NETWORK, new ExactEvmScheme());
// Mount x402 payment middleware
app.use(
paymentMiddleware(
{
"GET /generateImg": {
accepts: [{
scheme: "exact",
network: NETWORK,
payTo: PAY_TO,
price: "$0.01", // USDT 0.01
}],
description: "AI Image Generation Service",
mimeType: "application/json",
},
},
resourceServer,
),
);
// Protected image generation route (only executed after payment is verified)
app.get("/generateImg", (_req, res) => {
console.log("[Seller] Payment verified, generating image...");
res.json({
success: true,
imageUrl: "https://placehold.co/512x512/png?text=AI+Generated",
prompt: "a sunset over mountains",
timestamp: new Date().toISOString(),
});
});
app.listen(4000, () => {
console.log("[Seller] Image generation service listening at http://localhost:4000");
console.log("[Seller] Protected route: GET http://localhost:4000/generateImg");
console.log(`[Seller] Network: ${NETWORK}, PayTo: ${PAY_TO}`);
});
```
**Go**
```go
package main
import (
"net/http"
"os"
"time"
ginfw "github.com/gin-gonic/gin"
x402http "github.com/okx/payments/go/http"
ginmw "github.com/okx/payments/go/http/gin"
evm "github.com/okx/payments/go/mechanisms/evm/exact/server"
)
func main() {
// 1. Create OKX Facilitator client
facilitator, _ := x402http.NewOKXFacilitatorClient(&x402http.OKXFacilitatorConfig{
Auth: x402http.OKXAuthConfig{
APIKey: os.Getenv("OKX_API_KEY"),
SecretKey: os.Getenv("OKX_SECRET_KEY"),
Passphrase: os.Getenv("OKX_PASSPHRASE"),
},
})
// 2. Define payment-protected routes
routes := x402http.RoutesConfig{
"GET /generateImg": {
Accepts: x402http.PaymentOptions{
{
Scheme: "exact",
Price: "$0.01",
Network: "eip155:196",
PayTo: "0xYourWalletAddress",
},
},
Description: "AI-generated image",
MimeType: "image/png",
},
}
// 3. Create Gin router with payment middleware
r := ginfw.Default()
r.Use(ginmw.X402Payment(ginmw.Config{
Routes: routes,
Facilitator: facilitator,
Schemes: []ginmw.SchemeConfig{
{Network: "eip155:196", Server: evm.NewExactEvmScheme()},
},
Timeout: 30 * time.Second,
}))
r.GET("/genarateImg", func(c *ginfw.Context) {
c.JSON(http.StatusOK, ginfw.H{"image": "https://placehold.co/512x512/png?text=AI+Generated"})
})
r.Run(":4000")
}
```
**Rust**
```rust
use std::collections::HashMap;
use axum::{routing::get, Json, Router};
use serde_json::{json, Value};
use x402_axum::{payment_middleware, AcceptConfig, RoutePaymentConfig};
use x402_core::http::OkxHttpFacilitatorClient;
use x402_core::server::X402ResourceServer;
use x402_evm::{AggrDeferredEvmScheme, ExactEvmScheme};
#[tokio::main]
async fn main() {
// Initialize tracing (see Facilitator request/response logs in terminal)
tracing_subscriber::fmt::init();
// Read configuration from environment variables
let api_key = std::env::var("OKX_API_KEY").expect("OKX_API_KEY is required");
let secret_key = std::env::var("OKX_SECRET_KEY").expect("OKX_SECRET_KEY is required");
let passphrase = std::env::var("OKX_PASSPHRASE").expect("OKX_PASSPHRASE is required");
let pay_to = std::env::var("PAY_TO_ADDRESS")
.unwrap_or_else(|_| "0xb483abdb92...aa6b88c381308".to_string());
// 1. Configure OKX Facilitator
let facilitator = match std::env::var("FACILITATOR_URL") {
Ok(url) => OkxHttpFacilitatorClient::with_url(&url, &api_key, &secret_key, &passphrase),
Err(_) => OkxHttpFacilitatorClient::new(&api_key, &secret_key, &passphrase),
}
.expect("Failed to create facilitator client");
// 2. Create Server and register payment schemes
let mut server = X402ResourceServer::new(facilitator)
.register("eip155:196", ExactEvmScheme::new())
.register("eip155:196", AggrDeferredEvmScheme::new());
// 3. Must initialize (fetches supported schemes from facilitator)
server
.initialize()
.await
.expect("Failed to initialize: check facilitator connectivity");
// 4. Configure which routes require payment
let routes = HashMap::from([(
"GET /api/joke".to_string(),
RoutePaymentConfig {
accepts: vec![AcceptConfig {
scheme: "exact".into(),
price: "$0.001".into(),
network: "eip155:196".into(),
pay_to: pay_to.clone(),
max_timeout_seconds: None,
extra: None,
}],
description: "Get a random joke".into(),
mime_type: "application/json".into(),
sync_settle: None,
},
)]);
// 5. Build Axum router
let app = Router::new()
.route("/health", get(health))
.route("/api/joke", get(joke))
.layer(payment_middleware(routes, server));
// 6. Start server
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
.await
.unwrap();
println!("========================================");
println!(" Your paid API is running!");
println!(" Free endpoint: http://localhost:3000/health");
println!(" Paid endpoint: http://localhost:3000/api/joke ($0.001)");
println!("========================================");
axum::serve(listener, app).await.unwrap();
}
// Free endpoint
async fn health() -> Json {
Json(json!({ "status": "ok" }))
}
// Paid endpoint - requires $0.001 payment to access
async fn joke() -> Json {
Json(json!({
"joke": "Why do programmers always confuse Halloween and Christmas? Because Oct 31 = Dec 25",
"price": "$0.001"
}))
}
```
## Test Service
1. Use Onchain OS to request your service endpoint. You can refer to [Buyer Quickstart](https://web3.okx.com/onchainos/dev-docs/payments/payment-use-buyer).
```
Use onchainOS to access http://localhost:4000/genarateImg
```
2. The server returns a 402 status code with `PAYMENT-REQUIRED` in the response header.
3. Complete the payment using Agentic Wallet.
4. Agentic Wallet automatically retries the request.
5. The server verifies the payment result with the Facilitator and returns the corresponding resource.
- [Buyer Quickstart](https://web3.okx.com/onchainos/dev-docs/payments/payment-use-buyer.md)
# Buyer Quickstart
Integrate Onchain OS payment capabilities to enable your AI Agent to directly purchase services and resources.
- [Build with Agent](https://web3.okx.com/onchainos/dev-docs/payments/payment-use-buyer-ai.md)
# Build with Agent
After installing the payment Skill for your Agent, it can recognize 402 payment requests, sign transactions, and complete payments without human intervention.
## Configuration
Send the following prompt to the AI to install Onchain OS Skills and configure an Agentic Wallet for your Agent:
```
Run npx skills add okx/onchainos-skills to install Onchain OS skills
Note: Please install to the skill directory corresponding to the current Agent
Also, help me install the latest version of the CLI based on this documentation https://github.com/okx/onchainos-skills
```
## Verification
The following example uses "query weather" as a demonstration. Please test with an actual online x402 paid service as needed.
Log in:
```
onchainos wallet login
```
Request the seller's service/resource:
```
// Not a production service, for reference only
Access the service at http://localhost:4021/weather
```
If payment information is returned, the request was successful:
```
The service returned an x402 payment requirement response (HTTP 402). Details:
- x402 Version: 2
- Resource: /weather — Get current weather data for any location, returns application/json
- Payment methods (two schemes):
- exact — One-time payment
- aggr_deferred — Deferred payment
- Network: eip155:196 (OKX X Layer)
- Token: USD₮0 (0x779ded0c9e1022225f8e0630b35a9b54be713736)
- Amount: 1000 (smallest unit)
- Pay to: 0xb483abdb92f8061e9a3a082a4aaaa6b88c381308
- Timeout: 600,000 seconds
This endpoint requires payment via the x402 protocol to access. Would you like me to initiate payment to get the weather data?
```
Confirm the payment, sign the transaction data, and replay the request to the seller:
```
Signature successful, replaying request:
Bash(PAYMENT_PAYLOAD=$(python3 -c "
import json, base64
payload = {
'x402Version': 2,
'resource': {'url': '/weather', 'description': 'Get current weather data for any location', 'mimeType':
'application/json'},
'accepted': {'scheme': 'exact', 'network': 'eip155:196', 'asset':
'0x779ded0...4be713736', 'amount': '1000', 'payTo':
'0xb483abd....aa6b88c381308', 'maxTimeoutSeconds': 600000, 'extra': {'name': 'USD₮0',
'version': '1'}},
'payload': {
'signature': '0x3d1907a6ba9f592c...35868ea11b',
'authorization': {'from': '0xbda9fde...99763', 'nonce':
'0x20862cc...06e48', 'to':
'0xb483abd...81308', 'validAfter': '0', 'validBefore': '1775837508', 'value': '1000'}
}
}
print(base64.b64encode(json.dumps(payload, separators=(',', ':')).encode()).decode())
")
curl -s -D - http://localhost:4021/weather -H "PAYMENT-SIGNATURE: $PAYMENT_PAYLOAD")
⎿ HTTP/1.1 200 OK
```
The resource returned by the seller is received:
```
⏺ Payment successful! Weather data returned:
{"report": {"temperature": 70, "weather": "sunny"}}
- Temperature: 70°F
- Weather: Sunny ☀️
```
- [Overview](https://web3.okx.com/onchainos/dev-docs/payments/methods-overview.md)
# Overview
Onchain OS Payment offers two payment methods: One-Time Payment and Batch Payment.
- **One-Time Payment**: One request, one settlement, each transaction submitted on-chain individually. Suitable for high-value scenarios that require immediate on-chain confirmation.
- **Batch Payment**: Confirmed immediately upon request; on-chain settlement is completed in batches in the background. Suitable for high-frequency, low-value continuous calls by AI Agents.
Both methods support the X Layer network, accept USDG and USDT, with zero Gas fees.
## How It Works
Both payment methods share the same x402 protocol framework and API interface. The difference lies in the signature method and settlement timing.
### One-Time Payment
Each request independently completes a full payment:

- The Buyer's payment signature is an EOA signature (converted via signConvert), which can be directly accepted by on-chain contracts
- After the Seller calls `/settle`, the Facilitator submits the transaction on-chain
- Supports both synchronous settlement (returns after on-chain confirmation) and asynchronous settlement (returns immediately after submission)
→ For detailed integration guide, see
### Batch Payment
The Seller receives confirmation immediately after the Buyer signs; on-chain settlement is completed asynchronously in batches in the background:

- The Buyer signs directly with a session key (skipping signConvert); the signature cannot be directly accepted by on-chain contracts
- After the Seller calls `/settle`, the Facilitator stores the signature in the database and **immediately returns `status: "success"`**
- In the background, the TEE (Trusted Execution Environment) compresses multiple signatures into a single on-chain transaction, significantly reducing Gas costs
- Security guarantee: session key signatures are settled collectively by the TEE; Sellers cannot bypass the Facilitator to submit on-chain independently
→ For detailed integration guide, see
## Use Cases
Choose the appropriate method based on your business scenario:
| **Your Scenario** | **Recommended Method** |
|:---:|:---:|
| Each call has a clear price; delivery is irrevocable once completed (e.g., report generation, file download) | **One-Time Payment** |
| Resources should only be delivered after receiving on-chain confirmation | **One-Time Payment** |
| API is called at high frequency by AI Agents continuously, with very small per-call amounts (e.g., tool-chain orchestration, streaming billing) | **Batch Payment** |
| Latency-sensitive; millisecond-level response takes priority over immediate settlement confirmation | **Batch Payment** |
If your business covers multiple scenarios above, you do not need to choose one over the other. Return both the `exact` and `aggr_deferred` schemes in the 402 response, and the system will automatically match based on the Buyer's capabilities.
- [One-Time Payment](https://web3.okx.com/onchainos/dev-docs/payments/methods-onetime.md)
# One-Time Payment
## Overview
One-Time Payment is the most fundamental payment mode in Onchain OS Payment: each time a buyer requests a paid resource, a complete payment flow is executed independently. One request, instant settlement, instant delivery.
## Use Cases
- Each API call has a clear price and delivery is irreversible once completed (e.g., report generation, file download)
- Resources should only be delivered after on-chain confirmation (txHash) is received
- High-value API calls that require the certainty of immediate on-chain settlement
## Seller Integration
**Node.js**
```typescript
import { x402ResourceServer } from "@okxweb3/x402-core/server";
import { ExactEvmScheme } from "@okxweb3/x402-evm/exact/server";
import { OKXFacilitatorClient } from "@okxweb3/x402-core";
const facilitator = new OKXFacilitatorClient({
apiKey: "xxx",
secretKey: "xxx",
passphrase: "xxx",
syncSettle: false,
});
const server = new x402ResourceServer(facilitator);
// Register one-time payment scheme
server.register("eip155:196", new ExactEvmScheme());
// Configure payment routes
const routes = {
"GET /api/data": {
accepts: [{
scheme: "exact",
network: "eip155:196",
payTo: "0xSellerWallet",
price: "$0.01",
}],
description: "Premium data endpoint",
mimeType: "application/json",
},
};
app.use(paymentMiddleware(routes, server));
```
**Go**
```go
package main
import (
"fmt"
"net/http"
"os"
"time"
x402http "github.com/okx/payments/go/http"
ginmw "github.com/okx/payments/go/http/gin"
exact "github.com/okx/payments/go/mechanisms/evm/exact/server"
ginfw "github.com/gin-gonic/gin"
)
func main() {
syncClient, err := x402http.NewOKXFacilitatorClient(&x402http.OKXFacilitatorConfig{
Auth: x402http.OKXAuthConfig{
APIKey: "OKX_API_KEY",
SecretKey: "OKX_SECRET_KEY",
Passphrase: "OKX_PASSPHRASE",
},
BaseURL: "https://web3.okx.com",
})
if err != nil {
fmt.Printf("Failed to create client: %v\n", err)
os.Exit(1)
}
routes := x402http.RoutesConfig{
"GET /api/data": {
Accepts: x402http.PaymentOptions{
{Scheme: "exact", Price: "$0.01", Network: "eip155:196", PayTo: "SellerWallet"},
},
Description: "Premium data endpoint",
MimeType: "application/json",
},
}
schemes := []ginmw.SchemeConfig{
{Network: "eip155:196", Server: exact.NewExactEvmScheme()},
}
r := ginfw.Default()
apiGroup := r.Group("/")
apiGroup.Use(ginmw.X402Payment(ginmw.Config{
Routes: routes,
Facilitator: syncClient,
Schemes: schemes,
Timeout: 30 * time.Second,
}))
apiGroup.GET("/api/data", func(c *ginfw.Context) {
c.JSON(http.StatusOK, ginfw.H{
"data": "premium content",
"price": "$0.01",
})
})
r.Run(":3000")
}
```
**Rust**
```rust
use std::collections::HashMap;
use axum::{routing::get, Json, Router};
use serde_json::{json, Value};
use x402_axum::{payment_middleware, AcceptConfig, RoutePaymentConfig};
use x402_core::http::OkxHttpFacilitatorClient;
use x402_core::server::X402ResourceServer;
use x402_evm::ExactEvmScheme;
#[tokio::main]
async fn main() {
// 1. Configure OKX Facilitator client with API credentials
let facilitator = OkxHttpFacilitatorClient::new(
"OKX_API_KEY",
"OKX_SECRET_KEY",
"OKX_PASSPHRASE",
)
.expect("Failed to create facilitator client");
// 2. Create resource server and register the "exact" payment scheme on X Layer
let mut server = X402ResourceServer::new(facilitator)
.register("eip155:196", ExactEvmScheme::new());
// 3. Initialize server (fetches supported schemes from facilitator)
server
.initialize()
.await
.expect("Failed to initialize: check facilitator connectivity");
// 4. Define which routes require payment and their pricing
let routes = HashMap::from([(
"GET /api/data".to_string(),
RoutePaymentConfig {
accepts: vec![AcceptConfig {
scheme: "exact".into(),
price: "$0.01".into(), // $0.01 per request
network: "eip155:196".into(), // X Layer network
pay_to: "SellerWallet".into(), // Seller's wallet address
max_timeout_seconds: None, // Default: 300s (5 min)
extra: None,
}],
description: "Premium data endpoint".into(),
mime_type: "application/json".into(),
sync_settle: None, // Default: async settle
},
)]);
// 5. Build Axum router with payment middleware
let app = Router::new()
.route("/api/data", get(|| async { Json(json!({"data": "premium"})) }))
.layer(payment_middleware(routes, server));
// 6. Start the server
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
```
### Synchronous Settlement vs Asynchronous Settlement
After calling `/settle`, the Facilitator submits the transaction on-chain. The `syncSettle` field in the route configuration controls the settlement behavior:
| Mode | Parameter Value | Facilitator Behavior | Use Case |
|:---:|:---:|:---:|:---:|
| Synchronous | `true` | Submits the transaction and waits for on-chain confirmation before returning txHash | High-value transactions where resources should only be delivered after payment is confirmed |
| Asynchronous | `false` | Returns txHash immediately after submitting the transaction, without waiting for confirmation | Medium-value transactions where response speed matters |
> Asynchronous Settlement carries timing risk: resources may be delivered before on-chain payment is finalized. Synchronous Settlement is recommended for high-value transactions.
## Buyer Integration
After installing the OKX Agentic Wallet, the Agent automatically handles the 402 negotiation and payment signing. For details, see [Buyer Quickstart](./payment-use-buyer).
- [Batch Payment](https://web3.okx.com/onchainos/dev-docs/payments/methods-batch.md)
# Batch Payment
## Overview
Batch Payment is a payment mode designed by Onchain OS Payment for high-frequency, low-value scenarios. Unlike One-Time Payment, Batch Payment **does not require each transaction to be settled on-chain immediately** -- after the buyer signs, the seller receives confirmation and delivers the resource right away, while on-chain settlement is completed asynchronously in batches by OKX in the background.
## Why Batch Payment
In One-Time Payment, every transaction must go through the full "verify -> on-chain settlement -> delivery" flow. This is reasonable for high-value transactions, but in scenarios where AI Agents make high-frequency calls, settling each transaction individually on-chain introduces two problems:
- **Latency**: Even with asynchronous settlement, the Facilitator still needs extra time to submit transactions
- **Throughput**: In high-frequency scenarios, per-transaction on-chain settlement is limited by on-chain confirmation speed, and a large number of concurrent requests creates a settlement bottleneck
Batch Payment specifically addresses both issues: the settle call returns confirmation immediately (solving the latency problem), and multiple transactions are compressed into a single on-chain transaction (solving the throughput problem).
## Prerequisites
> Batch Payment requires the buyer to install and use the [OKX Agentic Wallet](https://web3.okx.com/zh-hans/onchainos/dev-docs/wallet/agentic-wallet). Regular EVM wallets cannot use this mode.
This is because Batch Payment relies on two key pieces of infrastructure:
- **Session Key**: A temporary signing key generated by the Agentic Wallet that allows the Agent to sign autonomously within the authorized scope, without invoking the primary private key each time
- **TEE (Trusted Execution Environment)**: A hardware-level security isolation technology. OKX performs batch settlement inside a TEE, ensuring that signing data cannot be tampered with or stolen -- even if the server is compromised, settlement transactions cannot be forged
## Use Cases
- **Continuous AI Agent calls**: An Agent frequently calls multiple paid APIs during a single task (e.g., search -> analyze -> generate)
- **Streaming billing**: High-frequency micropayments charged per token, per character, or per request
- **Bulk data queries**: A large volume of on-chain data or market data queries in a short period
- **Tool-chain orchestration**: An Agent automatically orchestrates multiple tool calls, each with a very small amount
## Seller Integration
**Node.js**
```typescript
import { OKXFacilitatorClient } from "@okxweb3/x402-core";
import { x402ResourceServer } from "@okxweb3/x402-core/server";
import { AggrDeferredEvmScheme } from "@okxweb3/x402-evm/deferred/server";
const facilitator = new OKXFacilitatorClient({
apiKey: "xxx",
secretKey: "xxx",
passphrase: "xxx",
});
const server = new x402ResourceServer(facilitator);
server.register("eip155:196", new AggrDeferredEvmScheme());
const routes = {
"GET /api/data": {
accepts: [{
scheme: "aggr_deferred",
network: "eip155:196",
payTo: "0xSellerWallet",
price: "$0.001",
}],
description: "Data endpoint with deferred settlement",
mimeType: "application/json",
},
};
app.use(paymentMiddleware(routes, server));
```
**Go**
```go
package main
import (
"fmt"
"net/http"
"os"
"time"
x402http "github.com/okx/payments/go/http"
ginmw "github.com/okx/payments/go/http/gin"
deferred "github.com/okx/payments/go/mechanisms/evm/deferred/server"
ginfw "github.com/gin-gonic/gin"
)
func main() {
syncClient, err := x402http.NewOKXFacilitatorClient(&x402http.OKXFacilitatorConfig{
Auth: x402http.OKXAuthConfig{
APIKey: "OKX_API_KEY",
SecretKey: "OKX_SECRET_KEY",
Passphrase: "OKX_PASSPHRASE",
},
BaseURL: "https://beta.okex.org",
})
if err != nil {
fmt.Printf("Failed to create client: %v\n", err)
os.Exit(1)
}
routes := x402http.RoutesConfig{
"GET /api/data": {
Accepts: x402http.PaymentOptions{
{Scheme: "aggr_deferred", Price: "$0.01", Network: "eip155:196", PayTo: "SellerWallet"},
},
Description: "Premium data endpoint",
MimeType: "application/json",
},
}
schemes := []ginmw.SchemeConfig{
{Network: "eip155:196", Server: deferred.NewAggrDeferredEvmScheme()},
}
r := ginfw.Default()
apiGroup := r.Group("/")
apiGroup.Use(ginmw.X402Payment(ginmw.Config{
Routes: routes,
Facilitator: syncClient,
Schemes: schemes,
Timeout: 30 * time.Second,
}))
apiGroup.GET("/api/data", func(c *ginfw.Context) {
c.JSON(http.StatusOK, ginfw.H{
"data": "premium content",
"price": "$0.01",
})
})
r.Run(":3000")
}
```
**Rust**
```rust
use axum::{Router, Json, routing::get};
use serde_json::json;
use std::collections::HashMap;
use x402_axum::{payment_middleware, RoutePaymentConfig, AcceptConfig};
use x402_core::http::OkxHttpFacilitatorClient;
use x402_core::server::X402ResourceServer;
use x402_evm::AggrDeferredEvmScheme;
#[tokio::main]
async fn main() {
// 1. Configure OKX Facilitator client with API credentials
let facilitator = OkxHttpFacilitatorClient::new(
&std::env::var("OKX_API_KEY").unwrap(),
&std::env::var("OKX_SECRET_KEY").unwrap(),
&std::env::var("OKX_PASSPHRASE").unwrap(),
)
.expect("Failed to create facilitator client");
// 2. Create resource server and register aggr_deferred scheme on X Layer
let mut server = X402ResourceServer::new(facilitator)
.register("eip155:196", AggrDeferredEvmScheme::new());
// 3. Initialize server (fetches supported schemes from facilitator)
server
.initialize()
.await
.expect("Failed to initialize: check facilitator connectivity");
// 4. Define which routes require payment and their pricing
let routes = HashMap::from([(
"GET /api/data".to_string(),
RoutePaymentConfig {
accepts: vec![AcceptConfig {
scheme: "aggr_deferred".into(),
price: "$0.01".into(), // $0.01 per request
network: "eip155:196".into(), // X Layer network
pay_to: "0xSellerWallet".into(), // Seller's wallet address
max_timeout_seconds: None, // Default: 300s (5 min)
extra: None,
}],
description: "Premium data endpoint".into(),
mime_type: "application/json".into(),
sync_settle: None, // Default: async settle
},
)]);
// 5. Build Axum router with payment middleware
let app = Router::new()
.route("/api/data", get(|| async { Json(json!({"data": "premium content"})) }))
.layer(payment_middleware(routes, server));
// 6. Start the server
let listener = tokio::net::TcpListener::bind("0.0.0.0:4021").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
```
## Buyer Integration
After the buyer installs the OKX Agentic Wallet, the Agent automatically handles the 402 negotiation and signed payment. For details, please refer to [Buyer Quickstart](.payment-use-buyer).
- [Node SDK Reference](https://web3.okx.com/onchainos/dev-docs/payments/sdk-nodejs.md)
# Node SDK Reference
## **Packages**
| Package | Description |
|:---:|:---:|
| `@okxweb3/x402-core` | Core: server, facilitator, types |
| `@okxweb3/x402-evm` | EVM mechanisms: exact, aggr_deferred |
| `@okxweb3/x402-express` | Express middleware (seller) |
| `@okxweb3/x402-next` | Next.js middleware (seller) |
| `@okxweb3/x402-hono` | Hono middleware (seller) |
| `@okxweb3/x402-fastify` | Fastify middleware (seller) |
---
## **Core Types**
### **Network**
```typescript
type Network = `${string}:${string}`;
// CAIP-2 format, e.g., "eip155:196"
```
### **Money / Price / AssetAmount**
```typescript
type Money = string | number;
// User-friendly amount, e.g., "$0.01", "0.01", 0.01
type AssetAmount = {
asset: string; // Token contract address
amount: string; // Amount in token's smallest unit (e.g., "10000" for 0.01 USDC)
extra?: Record; // Scheme-specific data (e.g., EIP-712 domain)
};
type Price = Money | AssetAmount;
// Either a user-friendly amount or a specific token amount
```
### **ResourceInfo**
```typescript
interface ResourceInfo {
url: string; // Resource URL path
description?: string; // Human-readable description
mimeType?: string; // Response content type (e.g., "application/json")
}
```
### **PaymentRequirements**
Describes what the seller accepts for payment.
```
type PaymentRequirements = {
scheme: string; // Payment scheme: "exact" | "aggr_deferred"
network: Network; // CAIP-2 network identifier
asset: string; // Token contract address
amount: string; // Price in token's smallest unit
payTo: string; // Recipient wallet address
maxTimeoutSeconds: number; // Payment authorization validity window
extra: Record; // Scheme-specific data
};
```
**`extra` fields by scheme:**
| Scheme | Extra Field | Type | Description |
|:---:|:---:|:---:|:---:|
| `exact` (EIP-3009) | `extra.eip712.name` | string | EIP-712 domain name (e.g., "USD Coin") |
| `exact` (EIP-3009) | `extra.eip712.version` | string | EIP-712 domain version (e.g., "2") |
### **PaymentRequired**
The HTTP 402 response body sent to clients.
```typescript
type PaymentRequired = {
x402Version: number; // Protocol version (currently 2)
error?: string; // Optional error message
resource: ResourceInfo; // Protected resource metadata
accepts: PaymentRequirements[]; // List of accepted payment options
};
```
### **PaymentPayload**
The client's signed payment submitted in the retry request.
```typescript
type PaymentPayload = {
x402Version: number; // Must match server's version
resource?: ResourceInfo; // Optional resource reference
accepted: PaymentRequirements; // The chosen payment option from `accepts`
payload: Record; // Scheme-specific signed data (see below)
extensions?: Record; // Extension data
};
```
**`payload` fields by scheme:**
For **exact (EIP-3009)**:
```typescript
{
signature: `0x${string}`; // EIP-712 signature
authorization: {
from: `0x${string}`; // Buyer wallet address
to: `0x${string}`; // Seller wallet address
value: string; // Amount in smallest unit
validAfter: string; // Unix timestamp (start validity)
validBefore: string; // Unix timestamp (end validity)
nonce: `0x${string}`; // 32-byte unique nonce
};
}
```
For **aggr_deferred**:
```typescript
{
signature: `0x${string}`; // Session key signature
authorization: { /* same as EIP-3009 */ };
}
```
### **VerifyResponse**
```typescript
type VerifyResponse = {
isValid: boolean; // Whether signature is valid
invalidReason?: string; // Machine-readable reason code
invalidMessage?: string; // Human-readable error message
payer?: string; // Recovered payer address
extensions?: Record;
};
```
### **SettleResponse**
```typescript
type SettleResponse = {
success: boolean; // Whether settlement succeeded
status?: "pending" | "success" | "timeout"; // OKX extension
errorReason?: string; // Machine-readable error code
errorMessage?: string; // Human-readable error message
payer?: string; // Payer address
transaction: string; // On-chain transaction hash (empty for aggr_deferred)
network: Network; // Settlement network
amount?: string; // Actual settled amount (may differ for "upto")
extensions?: Record;
};
```
### **SupportedKind / SupportedResponse**
```typescript
type SupportedKind = {
x402Version: number;
scheme: string;
network: Network;
extra?: Record;
};
type SupportedResponse = {
kinds: SupportedKind[];
extensions: string[]; // Supported extension keys
signers: Record; // CAIP family → signer addresses
};
```
---
## **Server API (`x402ResourceServer`)**
### **Constructor**
```typescript
import { x402ResourceServer } from "@okxweb3/x402-core/server";
const server = new x402ResourceServer(facilitatorClients?);
// facilitatorClients: FacilitatorClient | FacilitatorClient[]
// Can pass a URL string shorthand: new x402ResourceServer("https://x402.org/facilitator")
```
### **register(network, server)**
Register a server-side scheme. Chainable.
```typescript
server
.register("eip155:196", new ExactEvmScheme())
.register("eip155:196", new AggrDeferredEvmScheme());
```
### **registerExtension(extension)**
```typescript
interface ResourceServerExtension {
key: string;
enrichDeclaration?: (declaration: unknown, transportContext: unknown) => unknown;
enrichPaymentRequiredResponse?: (
declaration: unknown,
context: PaymentRequiredContext,
) => Promise;
enrichSettlementResponse?: (
declaration: unknown,
context: SettleResultContext,
) => Promise;
}
```
### **initialize()**
Fetch supported kinds from the facilitator. Call once at startup.
```typescript
await server.initialize();
```
### **buildPaymentRequirements(config) → PaymentRequirements[]
```typescript
interface ResourceConfig {
scheme: string; // "exact" | "aggr_deferred" | "upto"
payTo: string; // Recipient wallet address
price: Price; // "$0.01" or AssetAmount
network: Network; // "eip155:196"
maxTimeoutSeconds?: number; // Default: 60
extra?: Record;
}
const reqs = await server.buildPaymentRequirements({
scheme: "exact",
payTo: "0xSeller",
price: "$0.01",
network: "eip155:196",
});
```
### **buildPaymentRequirementsFromOptions(options, context) → PaymentRequirements[]
Dynamic pricing and payTo. Functions receive the context parameter.
```typescript
const reqs = await server.buildPaymentRequirementsFromOptions(
[
{
scheme: "exact",
network: "eip155:196",
payTo: (ctx) => ctx.sellerId === "A" ? "0xWalletA" : "0xWalletB",
price: (ctx) => ctx.premium ? "$0.10" : "$0.01",
},
],
requestContext
);
```
### **verifyPayment(payload, requirements) → VerifyResponse
```typescript
const result = await server.verifyPayment(paymentPayload, requirements);
// result.isValid: boolean
```
### **settlePayment(payload, requirements, ...) → SettleResponse
```typescript
const result = await server.settlePayment(
paymentPayload,
requirements,
declaredExtensions?, // Extension data from 402 response
transportContext?, // HTTP transport context
settlementOverrides?, // { amount: "$0.05" } for upto scheme
);
```
### **Server Lifecycle Hooks**
| Hook | Context | Can Abort/Recover |
|:---:|:---:|:---:|
| `onBeforeVerify` | `{ paymentPayload, requirements }` | `{ abort: true, reason, message? }` |
| `onAfterVerify` | `{ paymentPayload, requirements, result }` | No |
| `onVerifyFailure` | `{ paymentPayload, requirements, error }` | `{ recovered: true, result }` |
| `onBeforeSettle` | `{ paymentPayload, requirements }` | `{ abort: true, reason, message? }` |
| `onAfterSettle` | `{ paymentPayload, requirements, result, transportContext? }` | No |
| `onSettleFailure` | `{ paymentPayload, requirements, error }` | `{ recovered: true, result }` |
```typescript
server.onBeforeVerify(async (ctx) => {
// Log or gate verification
});
server.onAfterSettle(async (ctx) => {
console.log(`Settled: ${ctx.result.transaction} on ${ctx.result.network}`);
});
server.onSettleFailure(async (ctx) => {
if (ctx.error.message.includes("timeout")) {
return { recovered: true, result: { success: true, transaction: "", network: "eip155:196" } };
}
});
```
---
## **HTTP Resource Server (`x402HTTPResourceServer`)**
Higher-level wrapper that handles route matching, paywall, and HTTP-specific logic.
### **Constructor**
```typescript
import { x402HTTPResourceServer } from "@okxweb3/x402-core/http";
const httpServer = new x402HTTPResourceServer(resourceServer, routes);
```
### **RoutesConfig**
```typescript
type RoutesConfig = Record | RouteConfig;
interface RouteConfig {
accepts: PaymentOption | PaymentOption[]; // Accepted payment methods
resource?: string; // Override resource name
description?: string; // Human-readable description
mimeType?: string; // Response MIME type
customPaywallHtml?: string; // Custom HTML for browser 402 page
unpaidResponseBody?: (ctx: HTTPRequestContext) => HTTPResponseBody | Promise;
settlementFailedResponseBody?: (ctx, result) => HTTPResponseBody | Promise;
extensions?: Record;
}
interface PaymentOption {
scheme: string; // "exact" | "aggr_deferred" | "upto"
payTo: string | DynamicPayTo; // Static or dynamic recipient
price: Price | DynamicPrice; // Static or dynamic price
network: Network;
maxTimeoutSeconds?: number;
extra?: Record;
}
// Dynamic functions receive HTTPRequestContext
type DynamicPayTo = (context: HTTPRequestContext) => string | Promise;
type DynamicPrice = (context: HTTPRequestContext) => Price | Promise;
```
### **PaywallConfig**
```typescript
interface PaywallConfig {
appName?: string; // Application name for paywall UI
appLogo?: string; // Logo URL
sessionTokenEndpoint?: string;
currentUrl?: string;
testnet?: boolean; // Show testnet branding
}
```
### **onSettlementTimeout(hook)**
```typescript
type OnSettlementTimeoutHook = (txHash: string, network: string) => Promise<{ confirmed: boolean }>;
httpServer.onSettlementTimeout(async (txHash, network) => {
// Custom recovery logic
return { confirmed: false };
});
```
### onProtectedRequest(hook)
```typescript
type ProtectedRequestHook = (
context: HTTPRequestContext,
routeConfig: RouteConfig,
) => Promise;
httpServer.onProtectedRequest(async (ctx, config) => {
// Grant free access for certain users
if (ctx.adapter.getHeader("x-api-key") === "internal") {
return { grantAccess: true };
}
});
```
---
## Middleware Reference
### Express (`@okxweb3/x402-express`)
```typescript
import {
paymentMiddleware,
paymentMiddlewareFromConfig,
paymentMiddlewareFromHTTPServer,
setSettlementOverrides,
} from "@okxweb3/x402-express";
// From pre-configured server (recommended)
app.use(paymentMiddleware(routes, server, paywallConfig?, paywall?, syncFacilitatorOnStart?));
// From config (creates server internally)
app.use(paymentMiddlewareFromConfig(routes, facilitatorClients?, schemes?, paywallConfig?, paywall?, syncFacilitatorOnStart?));
// From HTTP server (most control)
app.use(paymentMiddlewareFromHTTPServer(httpServer, paywallConfig?, paywall?, syncFacilitatorOnStart?));
// Settlement override in handler (for "upto" scheme)
app.post("/api/generate", (req, res) => {
setSettlementOverrides(res, { amount: "$0.05" });
res.json({ result: "..." });
});
```
| Parameter | Type | Default | Description |
|:---:|:---:|:---:|:---:|
| `routes` | `RoutesConfig` | required | Route → payment config mapping |
| `server` | `x402ResourceServer` | required | Pre-configured resource server |
| `paywallConfig` | `PaywallConfig` | `undefined` | Browser paywall settings |
| `paywall` | `PaywallProvider` | `undefined` | Custom paywall renderer |
| `syncFacilitatorOnStart` | `boolean` | `true` | Fetch supported kinds on first request |
### Next.js (`@okxweb3/x402-next`)
```typescript
import {
paymentProxy,
paymentProxyFromConfig,
paymentProxyFromHTTPServer,
withX402,
withX402FromHTTPServer,
} from "@okxweb3/x402-next";
// As global middleware (middleware.ts)
const proxy = paymentProxy(routes, server, paywallConfig?, paywall?, syncFacilitatorOnStart?);
export async function middleware(request: NextRequest) { return proxy(request); }
export const config = { matcher: ["/api/:path*"] };
// Per-route wrapper (app/api/data/route.ts)
export const GET = withX402(handler, routeConfig, server, paywallConfig?, paywall?, syncFacilitatorOnStart?);
export const GET = withX402FromHTTPServer(handler, httpServer, paywallConfig?, paywall?, syncFacilitatorOnStart?);
```
### Hono (`@okxweb3/x402-hono`)
```typescript
import { paymentMiddleware, paymentMiddlewareFromConfig, paymentMiddlewareFromHTTPServer } from "@okxweb3/x402-hono";
app.use("/*", paymentMiddleware(routes, server, paywallConfig?, paywall?, syncFacilitatorOnStart?));
```
### Fastify (`@okxweb3/x402-fastify`)
```typescript
import { paymentMiddleware, paymentMiddlewareFromConfig, paymentMiddlewareFromHTTPServer } from "@okxweb3/x402-fastify";
// NOTE: Fastify registers hooks directly, returns void
paymentMiddleware(app, routes, server, paywallConfig?, paywall?, syncFacilitatorOnStart?);
```
---
## EVM Mechanism Types
### ExactEvmScheme (Server)
```typescript
import { ExactEvmScheme } from "@okxweb3/x402-evm/exact/server";
const scheme = new ExactEvmScheme(); // No constructor args for server-side
scheme.scheme; // "exact"
// Automatically handles price parsing, EIP-712 domain injection
```
### AggrDeferredEvmScheme (Server)
```typescript
import { AggrDeferredEvmScheme } from "@simongtest/x402-evm/deferred/server";
const scheme = new AggrDeferredEvmScheme();
scheme.scheme; // "aggr_deferred"
// Delegates to ExactEvmScheme for price parsing
```
---
## Error Classes
```typescript
class VerifyError extends Error {
readonly invalidReason?: string;
readonly invalidMessage?: string;
readonly payer?: string;
readonly statusCode: number;
}
class SettleError extends Error {
readonly errorReason?: string;
readonly errorMessage?: string;
readonly payer?: string;
readonly transaction: string;
readonly network: Network;
readonly statusCode: number;
}
class FacilitatorResponseError extends Error {}
class RouteConfigurationError extends Error {
readonly errors: RouteValidationError[];
}
interface RouteValidationError {
routePattern: string;
scheme: string;
network: Network;
reason: "missing_scheme" | "missing_facilitator";
message: string;
}
```
- [Rust SDK Reference](https://web3.okx.com/onchainos/dev-docs/payments/sdk-rust.md)
# Rust SDK Reference
## Crates
| Crate | Description |
|:---:|:---:|
| `x402-core` | Core: server, facilitator client, types, HTTP utilities, HMAC auth |
| `x402-axum` | Axum middleware (Tower Layer/Service) |
| `x402-evm` | EVM mechanisms: exact, aggr_deferred |
> The Rust SDK currently provides **server-side (seller)** and **facilitator client** functionality. Buyer-side payment signing is planned.
---
## Core Types
### Network / Money / Price
```rust
pub type Network = String;
// CAIP-2 format, e.g., "eip155:196"
pub type Money = String;
// User-friendly amount, e.g., "$0.01", "0.01"
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Price {
Money(Money),
Asset(AssetAmount),
}
```
### AssetAmount
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AssetAmount {
pub asset: String, // Token contract address
pub amount: String, // Amount in token's smallest unit
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extra: Option>,
}
```
### ResourceInfo
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ResourceInfo {
pub url: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mime_type: Option,
}
```
### PaymentRequirements
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentRequirements {
pub scheme: String, // "exact" | "aggr_deferred"
pub network: Network, // CAIP-2 identifier
pub asset: String, // Token contract address
pub amount: String, // Price in token's smallest unit
pub pay_to: String, // Recipient wallet address
pub max_timeout_seconds: u64, // Authorization validity window
#[serde(default)]
pub extra: HashMap, // Scheme-specific data
}
```
### PaymentRequired
402 response body.
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentRequired {
#[serde(rename = "x402Version")]
pub x402_version: u32, // Protocol version (2)
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option,
pub resource: ResourceInfo,
pub accepts: Vec,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extensions: Option>,
}
```
### PaymentPayload
Client's signed payment.
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentPayload {
#[serde(rename = "x402Version")]
pub x402_version: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub resource: Option,
pub accepted: PaymentRequirements,
pub payload: HashMap, // Scheme-specific signed data
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extensions: Option>,
}
```
---
## Facilitator Types
### VerifyRequest / VerifyResponse
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VerifyRequest {
#[serde(rename = "x402Version")]
pub x402_version: u32,
pub payment_payload: PaymentPayload,
pub payment_requirements: PaymentRequirements,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VerifyResponse {
pub is_valid: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub invalid_reason: Option,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub invalid_message: Option,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub payer: Option,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extensions: Option>,
}
```
### SettleRequest / SettleResponse
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SettleRequest {
#[serde(rename = "x402Version")]
pub x402_version: u32,
pub payment_payload: PaymentPayload,
pub payment_requirements: PaymentRequirements,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sync_settle: Option, // OKX extension: wait for on-chain confirmation
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SettleResponse {
pub success: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error_reason: Option,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error_message: Option,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub payer: Option,
pub transaction: String, // Tx hash (empty for aggr_deferred)
pub network: Network,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub amount: Option,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status: Option, // OKX: "pending" | "success" | "timeout"
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extensions: Option>,
}
```
### SupportedKind / SupportedResponse
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SupportedKind {
#[serde(rename = "x402Version")]
pub x402_version: u32,
pub scheme: String,
pub network: Network,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extra: Option>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SupportedResponse {
pub kinds: Vec,
pub extensions: Vec,
pub signers: HashMap>,
}
```
### SettleStatusResponse
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SettleStatusResponse {
pub success: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error_reason: Option,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error_message: Option,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub payer: Option,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub transaction: Option,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub network: Option,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status: Option, // "pending" | "success" | "failed"
}
```
---
## Traits
### SchemeNetworkServer
Server-side scheme implementation.
```rust
#[async_trait]
pub trait SchemeNetworkServer: Send + Sync {
fn scheme(&self) -> &str;
async fn parse_price(
&self,
price: &Price,
network: &Network,
) -> Result;
async fn enhance_payment_requirements(
&self,
payment_requirements: PaymentRequirements,
supported_kind: &SupportedKind,
facilitator_extensions: &[String],
) -> Result;
}
```
### FacilitatorClient
Network boundary for communicating with a remote facilitator.
```rust
#[async_trait]
pub trait FacilitatorClient: Send + Sync {
async fn get_supported(&self) -> Result;
async fn verify(&self, request: &VerifyRequest) -> Result;
async fn settle(&self, request: &SettleRequest) -> Result;
async fn get_settle_status(&self, tx_hash: &str) -> Result;
}
```
### ResourceServerExtension
```rust
#[async_trait]
pub trait ResourceServerExtension: Send + Sync {
fn key(&self) -> &str;
async fn enrich_payment_required(
&self,
payment_required: PaymentRequired,
context: &PaymentRequiredContext,
) -> PaymentRequired;
async fn enrich_verify_extensions(
&self,
extensions: HashMap,
payment_payload: &PaymentPayload,
payment_requirements: &PaymentRequirements,
) -> HashMap;
async fn enrich_settle_extensions(
&self,
extensions: HashMap,
payment_payload: &PaymentPayload,
payment_requirements: &PaymentRequirements,
) -> HashMap;
}
pub struct PaymentRequiredContext {
pub url: String,
pub method: String,
}
pub struct SettleResultContext {
pub url: String,
pub method: String,
pub payment_payload: PaymentPayload,
pub payment_requirements: PaymentRequirements,
pub settle_response: SettleResponse,
}
```
### FacilitatorExtension
```rust
#[async_trait]
pub trait FacilitatorExtension: Send + Sync {
fn key(&self) -> &str;
fn supported_networks(&self) -> Vec;
}
```
---
## Server API (`X402ResourceServer`)
### Constructor and Registration
```rust
use x402_core::server::X402ResourceServer;
use x402_evm::{ExactEvmScheme, AggrDeferredEvmScheme};
// register() uses builder pattern (consumes self, returns Self)
let mut server = X402ResourceServer::new(facilitator)
.register("eip155:196", ExactEvmScheme::new())
.register("eip155:196", AggrDeferredEvmScheme::new());
```
### Methods
```rust
use std::collections::HashMap;
use std::time::Duration;
use x402_core::error::X402Error;
use x402_core::facilitator::FacilitatorClient;
use x402_core::http::{PollResult, SettlementOverrides};
use x402_core::server::X402ResourceServer;
use x402_core::types::{
PaymentPayload, PaymentRequirements, ResourceInfo, SchemeNetworkServer,
SettleResponse, SupportedResponse, VerifyResponse,
};
impl X402ResourceServer {
pub fn new(facilitator: impl FacilitatorClient + 'static) -> Self;
pub fn register(
mut self,
network: &str,
scheme: impl SchemeNetworkServer + 'static,
) -> Self;
pub async fn initialize(&mut self) -> Result<(), X402Error>;
pub fn supported(&self) -> Option<&SupportedResponse>;
pub fn facilitator(&self) -> &dyn FacilitatorClient;
pub async fn build_payment_requirements(
&self,
scheme: &str, // "exact"
network: &str, // "eip155:196"
price: &str, // "$0.01"
pay_to: &str, // "0xSeller"
max_timeout_seconds: u64,
resource: &ResourceInfo,
config_extra: Option<&HashMap>,
) -> Result;
pub async fn verify_payment(
&self,
payment_payload: &PaymentPayload,
payment_requirements: &PaymentRequirements,
) -> Result;
pub async fn settle_payment(
&self,
payment_payload: &PaymentPayload,
payment_requirements: &PaymentRequirements,
sync_settle: Option,
settlement_overrides: Option<&SettlementOverrides>,
) -> Result;
pub async fn poll_settle_status(
&self,
tx_hash: &str,
poll_interval: Duration, // DEFAULT_POLL_INTERVAL = 1s
poll_deadline: Duration, // DEFAULT_POLL_DEADLINE = 5s
) -> PollResult;
}
```
### PollResult
```rust
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PollResult {
Success, // Transaction confirmed on-chain
Failed, // Transaction failed on-chain
Timeout, // Polling deadline exceeded
}
```
---
## OKX Facilitator Client (`OkxHttpFacilitatorClient`)
```rust
use x402_core::http::OkxHttpFacilitatorClient;
// Default URL (https://web3.okx.com)
let client = OkxHttpFacilitatorClient::new(
"your-api-key",
"your-secret-key",
"your-passphrase",
)?;
```
Implements `FacilitatorClient` trait. All requests include HMAC-SHA256 authentication.
**Endpoints called:**
| Method | OKX Path |
|:---:|:---:|
| `get_supported()` | `GET /api/v6/pay/x402/supported` |
| `verify(request)` | `POST /api/v6/pay/x402/verify` |
| `settle(request)` | `POST /api/v6/pay/x402/settle` |
| `get_settle_status(tx_hash)` | `GET /api/v6/pay/x402/settle/status?txHash=...` |
OKX responses are wrapped in `{"code": 0, "data": {...}, "msg": ""}` -- the client automatically unwraps.
---
## HMAC Authentication
```rust
use x402_core::http::hmac::{sign_request, build_auth_headers};
// Sign a request
let signature = sign_request(secret_key, timestamp, method, request_path, body);
// Message = timestamp + METHOD + requestPath + body
// Signature = Base64(HMAC-SHA256(secretKey, message))
// Build all auth headers
let headers = build_auth_headers(api_key, secret_key, passphrase, method, request_path, body);
// Returns: OK-ACCESS-KEY, OK-ACCESS-SIGN, OK-ACCESS-TIMESTAMP, OK-ACCESS-PASSPHRASE
```
---
## HTTP Utilities
### Header Encoding/Decoding
```rust
use x402_core::http::{
encode_payment_signature_header,
decode_payment_signature_header,
encode_payment_required_header,
decode_payment_required_header,
encode_payment_response_header,
decode_payment_response_header,
};
let encoded = encode_payment_required_header(&payment_required)?; // -> base64 string
let decoded = decode_payment_required_header(&header_value)?; // -> PaymentRequired
```
### Constants
```rust
pub const DEFAULT_POLL_INTERVAL: Duration = Duration::from_secs(1);
pub const DEFAULT_POLL_DEADLINE: Duration = Duration::from_secs(5);
pub const PAYMENT_SIGNATURE_HEADER: &str = "PAYMENT-SIGNATURE";
pub const PAYMENT_REQUIRED_HEADER: &str = "PAYMENT-REQUIRED";
pub const PAYMENT_RESPONSE_HEADER: &str = "PAYMENT-RESPONSE";
```
---
## Route Configuration
```rust
use x402_core::http::{RoutesConfig, RoutePaymentConfig, AcceptConfig};
use std::collections::HashMap;
pub type RoutesConfig = HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RoutePaymentConfig {
pub accepts: Vec, // Accepted payment options
pub description: String, // Resource description
pub mime_type: String, // Response MIME type
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sync_settle: Option, // Enable sync settlement
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AcceptConfig {
pub scheme: String, // "exact" | "aggr_deferred"
pub price: String, // "$0.01" or token amount
pub network: String, // "eip155:196"
pub pay_to: String, // Recipient wallet
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_timeout_seconds: Option, // Default: 300s (5 min)
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extra: Option>, // Scheme-specific metadata
}
```
**Example:**
```rust
use std::collections::HashMap;
use x402_axum::{AcceptConfig, RoutePaymentConfig};
let mut routes = HashMap::new();
routes.insert("GET /api/data".to_string(), RoutePaymentConfig {
accepts: vec![
AcceptConfig {
scheme: "exact".to_string(),
price: "$0.01".to_string(),
network: "eip155:196".to_string(),
pay_to: "0xSeller".to_string(),
max_timeout_seconds: None,
extra: None,
},
AcceptConfig {
scheme: "aggr_deferred".to_string(),
price: "$0.001".to_string(),
network: "eip155:196".to_string(),
pay_to: "0xSeller".to_string(),
max_timeout_seconds: None,
extra: None,
},
],
description: "Premium data".to_string(),
mime_type: "application/json".to_string(),
sync_settle: Some(true),
});
```
---
## Axum Middleware (`x402-axum`)
### Basic Usage
```rust
use std::time::Duration;
use axum::{Router, routing::get};
use x402_axum::{
payment_middleware, payment_middleware_with_poll_deadline,
payment_middleware_with_timeout_hook, payment_middleware_with_timeout_hook_and_deadline,
OnSettlementTimeoutHook, RoutesConfig,
};
use x402_core::server::X402ResourceServer;
let app = Router::new()
.route("/api/data", get(handler))
.layer(payment_middleware(routes, server));
```
### Construction Functions
```rust
use std::time::Duration;
use x402_axum::{
OnSettlementTimeoutHook, PaymentLayer, RoutesConfig,
};
use x402_core::server::X402ResourceServer;
// Basic middleware
pub fn payment_middleware(
routes: RoutesConfig,
server: X402ResourceServer,
) -> PaymentLayer;
// With custom poll deadline
pub fn payment_middleware_with_poll_deadline(
routes: RoutesConfig,
server: X402ResourceServer,
poll_deadline: Duration,
) -> PaymentLayer;
// With settlement timeout recovery hook
pub fn payment_middleware_with_timeout_hook(
routes: RoutesConfig,
server: X402ResourceServer,
timeout_hook: OnSettlementTimeoutHook,
) -> PaymentLayer;
// With both
pub fn payment_middleware_with_timeout_hook_and_deadline(
routes: RoutesConfig,
server: X402ResourceServer,
timeout_hook: OnSettlementTimeoutHook,
poll_deadline: Duration,
) -> PaymentLayer;
```
### OnSettlementTimeoutHook
```rust
use std::pin::Pin;
use std::future::Future;
use x402_axum::{OnSettlementTimeoutHook, SettlementTimeoutResult};
pub struct SettlementTimeoutResult {
pub confirmed: bool,
}
pub type OnSettlementTimeoutHook = Box<
dyn Fn(String, String) -> Pin + Send>>
+ Send + Sync,
>;
// Usage: arguments are (tx_hash, network), not (route, tx_hash)
let hook: OnSettlementTimeoutHook = Box::new(|tx_hash, network| {
Box::pin(async move {
tracing::warn!("Timeout for tx={} network={}", tx_hash, network);
SettlementTimeoutResult { confirmed: false }
})
});
```
### Middleware Flow
1. Check if route requires payment (`find_route_config`)
2. If no `payment-signature` header -> return 402 with `PAYMENT-REQUIRED` header
3. Decode and validate payment payload
4. Match payload against route's accepted requirements
5. Verify via facilitator (`POST /verify`)
6. Call inner handler and buffer response
7. Settle via facilitator (`POST /settle`)
8. If async (`status: "pending"`) -> poll with deadline
9. If timeout -> call timeout hook if configured
10. Add `PAYMENT-RESPONSE` header to response
### Re-exports
```rust
pub use x402_core::http::{
AcceptConfig,
BeforeHookResult,
OnAfterSettleHook,
OnAfterVerifyHook,
OnBeforeSettleHook,
OnBeforeVerifyHook,
OnProtectedRequestHook,
OnSettleFailureHook,
OnSettlementTimeoutHook,
OnVerifyFailureHook,
PaymentResolverFn,
PollResult,
ProtectedRequestResult,
RequestContext,
ResolvedAccept,
RoutePaymentConfig,
RoutesConfig,
SettleContext,
SettleRecoveryResult,
SettleResultContext,
SettlementOverrides,
SettlementTimeoutResult,
VerifyContext,
VerifyRecoveryResult,
VerifyResultContext,
DEFAULT_POLL_DEADLINE,
DEFAULT_POLL_INTERVAL,
SETTLEMENT_OVERRIDES_HEADER,
};
pub use x402_core::server::X402ResourceServer;
```
---
## EVM Mechanisms (`x402-evm`)
### ExactEvmScheme
```rust
use x402_evm::ExactEvmScheme;
let scheme = ExactEvmScheme::new();
scheme.scheme(); // "exact"
```
Handles:
- Price parsing: `"$0.01"`, `"0.01"`, `AssetAmount`
- Token amount conversion with correct decimals
- Default asset lookup per network (USDC on Base, USDT on X Layer)
- EIP-712 domain injection into `extra` for EIP-3009 tokens
### DeferredEvmScheme
```rust
use x402_evm::AggrDeferredEvmScheme;
let scheme = AggrDeferredEvmScheme::new();
scheme.scheme(); // "aggr_deferred"
```
Delegates to `ExactEvmScheme` for all price/requirement logic. Identical from the seller's perspective.
### EVM Payload Types
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AssetTransferMethod {
#[serde(rename = "eip3009")]
Eip3009,
Permit2,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EIP3009Authorization {
pub from: String,
pub to: String,
pub value: String,
pub valid_after: String,
pub valid_before: String,
pub nonce: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExactEIP3009Payload {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub signature: Option,
pub authorization: EIP3009Authorization,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExactPermit2Payload {
pub signature: String,
pub permit2_authorization: Permit2Authorization,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ExactEvmPayloadV2 {
EIP3009(ExactEIP3009Payload),
Permit2(ExactPermit2Payload),
}
impl ExactEvmPayloadV2 {
pub fn is_permit2(&self) -> bool;
pub fn is_eip3009(&self) -> bool;
}
```
### Asset Configuration
```rust
#[derive(Debug, Clone)]
pub struct DefaultAssetInfo {
pub address: &'static str, // Token contract address
pub name: &'static str, // EIP-712 domain name
pub version: &'static str, // EIP-712 domain version
pub decimals: u8, // Token decimal places
pub asset_transfer_method: Option<&'static str>, // "permit2" override
pub supports_eip2612: bool, // EIP-2612 permit() support
}
#[derive(Debug, Clone)]
pub struct ChainConfig {
pub network: &'static str, // CAIP-2 identifier
pub chain_id: u64,
}
// Pre-registered assets:
// BASE_MAINNET_USDC: eip155:8453, 0x833589..., 6 decimals
// BASE_SEPOLIA_USDC: eip155:84532, 0x036CbD..., 6 decimals
// XLAYER_MAINNET_USDT: eip155:196, 0x779ded..., 6 decimals, name: "USD₮0"
// XLAYER_TESTNET_USDT: eip155:195, TBD
pub fn default_stablecoins() -> HashMap<&'static str, DefaultAssetInfo>;
pub fn get_default_asset(network: &str) -> Option;
```
---
## Error Types
```rust
#[derive(Debug, thiserror::Error)]
pub enum X402Error {
#[error(transparent)]
Verify(#[from] VerifyError),
#[error(transparent)]
Settle(#[from] SettleError),
#[error(transparent)]
FacilitatorResponse(#[from] FacilitatorResponseError),
#[error("configuration error: {0}")]
Config(String),
#[error("route configuration error: {0}")]
RouteConfig(String),
#[error("unsupported scheme: {0}")]
UnsupportedScheme(String),
#[error("unsupported network: {0}")]
UnsupportedNetwork(String),
#[error("price parse error: {0}")]
PriceParse(String),
#[error("not initialized: {0}")]
NotInitialized(String),
#[error("http error: {0}")]
Http(#[from] reqwest::Error),
#[error("serialization error: {0}")]
Serialization(#[from] serde_json::Error),
#[error("base64 decode error: {0}")]
Base64Decode(#[from] base64::DecodeError),
#[error("{0}")]
Other(String),
}
#[derive(Debug, Clone, thiserror::Error)]
pub struct VerifyError {
pub status_code: u16,
pub invalid_reason: Option,
pub invalid_message: Option,
pub payer: Option,
}
#[derive(Debug, Clone, thiserror::Error)]
pub struct SettleError {
pub status_code: u16,
pub error_reason: Option,
pub error_message: Option,
pub payer: Option,
pub transaction: String,
pub network: Network,
}
#[derive(Debug, Clone, thiserror::Error)]
pub struct FacilitatorResponseError(pub String);
```
---
## Utility Functions
```rust
pub fn safe_base64_encode(data: &str) -> String;
pub fn safe_base64_decode(data: &str) -> Result;
// Network pattern matching: "eip155:*" matches "eip155:196"
pub fn network_matches_pattern(network: &str, pattern: &str) -> bool;
pub fn find_schemes_by_network<'a, T>(
map: &'a HashMap>,
network: &str,
) -> Option<&'a HashMap>;
pub fn find_by_network_and_scheme<'a, T>(
map: &'a HashMap>,
scheme: &str,
network: &str,
) -> Option<&'a T>;
pub fn deep_equal(obj1: &serde_json::Value, obj2: &serde_json::Value) -> bool;
```
---
## Schema Validation
```rust
pub fn validate_payment_requirements(req: &PaymentRequirements) -> Result<(), X402Error>;
pub fn validate_payment_payload(payload: &PaymentPayload) -> Result<(), X402Error>;
pub fn validate_payment_required(required: &PaymentRequired) -> Result<(), X402Error>;
```
Validates that all required fields are non-empty.
- [Go SDK Reference](https://web3.okx.com/onchainos/dev-docs/payments/sdk-go.md)
# Go SDK Reference
## Packages
| Package | Description |
|:---:|:---:|
| `github.com/anthropics/x402/go` | Core: client, server, facilitator, types |
| `.../go/mechanisms/evm` | EVM mechanisms: exact, aggr_deferred |
| `.../go/mechanisms/svm` | Solana mechanisms: exact |
| `.../go/http/gin` | Gin middleware (seller) |
| `.../go/http/echo` | Echo middleware (seller) |
| `.../go/http/nethttp` | net/http middleware (seller) |
| `.../go/http` | HTTP client wrapper (buyer) |
---
## Core Types
### Network
```go
type Network string
// CAIP-2 format, e.g., "eip155:196", "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
func (n Network) Parse() (namespace, reference string, err error)
func (n Network) Match(pattern Network) bool
```
### Price / AssetAmount
```go
type Price interface{}
// Can be string ("$0.01"), number (0.01), or map (AssetAmount)
type AssetAmount struct {
Asset string `json:"asset"` // Token contract address
Amount string `json:"amount"` // Smallest unit amount
Extra map[string]interface{} `json:"extra,omitempty"`
}
```
### ResourceConfig
```go
type ResourceConfig struct {
Scheme string `json:"scheme"` // "exact" | "aggr_deferred" | "upto"
PayTo string `json:"payTo"` // Recipient wallet
Price Price `json:"price"` // "$0.01" or AssetAmount
Network Network `json:"network"` // "eip155:196"
MaxTimeoutSeconds int `json:"maxTimeoutSeconds,omitempty"` // Default: 60
Extra map[string]interface{} `json:"extra,omitempty"`
}
```
### VerifyResponse
```go
type VerifyResponse struct {
IsValid bool `json:"isValid"`
InvalidReason string `json:"invalidReason,omitempty"`
InvalidMessage string `json:"invalidMessage,omitempty"`
Payer string `json:"payer,omitempty"`
}
```
### SettleResponse
```go
type SettleResponse struct {
Success bool `json:"success"`
ErrorReason string `json:"errorReason,omitempty"`
ErrorMessage string `json:"errorMessage,omitempty"`
Payer string `json:"payer,omitempty"`
Transaction string `json:"transaction"`
Network Network `json:"network"`
Status string `json:"status,omitempty"` // OKX: "pending" | "success" | "timeout"
}
```
### SupportedResponse
```go
type SupportedKind struct {
X402Version int `json:"x402Version"`
Scheme string `json:"scheme"`
Network Network `json:"network"`
Extra map[string]interface{} `json:"extra,omitempty"`
}
type SupportedResponse struct {
Kinds []SupportedKind `json:"kinds"`
Extensions []string `json:"extensions"`
Signers map[string][]string `json:"signers"` // CAIP family -> addresses
}
```
### SettlementOverrides
```go
type SettlementOverrides struct {
Amount string `json:"amount,omitempty"` // Atomic units, percentage, or dollar
}
```
---
## View Interfaces
Unified view for V1/V2 payment data:
```go
type PaymentRequirementsView interface {
GetScheme() string
GetNetwork() string
GetAsset() string
GetAmount() string
GetPayTo() string
GetMaxTimeoutSeconds() int
GetExtra() map[string]interface{}
}
type PaymentPayloadView interface {
GetVersion() int
GetScheme() string
GetNetwork() string
GetPayload() map[string]interface{}
}
```
---
## Client API (`x402Client`)
### Constructor
```go
import x402 "github.com/anthropics/x402/go"
client := x402.Newx402Client(opts ...ClientOption)
```
ClientOption functions:
| Option | Description |
|:---:|:---:|
| `WithPaymentSelector(selector)` | Custom payment requirements selector |
| `WithPolicy(policy)` | Add a payment filtering policy |
### Register Methods (Chainable)
```go
func (c *x402Client) Register(network Network, client SchemeNetworkClient) *x402Client
func (c *x402Client) RegisterV1(network Network, client SchemeNetworkClientV1) *x402Client
func (c *x402Client) RegisterPolicy(policy PaymentPolicy) *x402Client
func (c *x402Client) RegisterExtension(ext ClientExtension) *x402Client
```
### Payment Creation
```go
// Select best payment option from requirements list
func (c *x402Client) SelectPaymentRequirements(
requirements []types.PaymentRequirements,
) (types.PaymentRequirements, error)
// Create signed payment payload
func (c *x402Client) CreatePaymentPayload(
ctx context.Context,
requirements types.PaymentRequirements,
resource *types.ResourceInfo,
extensions map[string]interface{},
) (types.PaymentPayload, error)
```
### Lifecycle Hooks (Chainable)
```go
func (c *x402Client) OnBeforePaymentCreation(hook BeforePaymentCreationHook) *x402Client
func (c *x402Client) OnAfterPaymentCreation(hook AfterPaymentCreationHook) *x402Client
func (c *x402Client) OnPaymentCreationFailure(hook OnPaymentCreationFailureHook) *x402Client
```
### Policy and Selector Types
```go
type PaymentRequirementsSelector func(requirements []PaymentRequirementsView) PaymentRequirementsView
type PaymentPolicy func(requirements []PaymentRequirementsView) []PaymentRequirementsView
func DefaultPaymentSelector(requirements []PaymentRequirementsView) PaymentRequirementsView
```
---
## Server API (`x402ResourceServer`)
### Constructor
```go
server := x402.Newx402ResourceServer(opts ...ResourceServerOption)
```
ResourceServerOption functions:
| Option | Description |
|:---:|:---:|
| `WithFacilitatorClient(client)` | Set facilitator client |
| `WithSchemeServer(network, server)` | Register a scheme |
| `WithCacheTTL(ttl)` | Set supported-kinds cache TTL |
### Register Methods (Chainable)
```go
func (s *x402ResourceServer) Register(network Network, server SchemeNetworkServer) *x402ResourceServer
func (s *x402ResourceServer) RegisterExtension(ext types.ResourceServerExtension) *x402ResourceServer
```
### Initialization
```go
func (s *x402ResourceServer) Initialize(ctx context.Context) error
```
### Payment Operations
```go
// Build requirements from config
func (s *x402ResourceServer) BuildPaymentRequirementsFromConfig(
ctx context.Context,
config ResourceConfig,
) ([]types.PaymentRequirements, error)
// Build single requirement
func (s *x402ResourceServer) BuildPaymentRequirements(
ctx context.Context,
config ResourceConfig,
supportedKind types.SupportedKind,
extensions []string,
) (types.PaymentRequirements, error)
// Create 402 response
func (s *x402ResourceServer) CreatePaymentRequiredResponse(
requirements []types.PaymentRequirements,
resourceInfo *types.ResourceInfo,
errorMsg string,
extensions map[string]interface{},
) types.PaymentRequired
// Find matching requirements for a payment payload
func (s *x402ResourceServer) FindMatchingRequirements(
available []types.PaymentRequirements,
payload types.PaymentPayload,
) *types.PaymentRequirements
// Verify payment signature
func (s *x402ResourceServer) VerifyPayment(
ctx context.Context,
payload types.PaymentPayload,
requirements types.PaymentRequirements,
) (*VerifyResponse, error)
// Settle payment on-chain
func (s *x402ResourceServer) SettlePayment(
ctx context.Context,
payload types.PaymentPayload,
requirements types.PaymentRequirements,
overrides *SettlementOverrides,
) (*SettleResponse, error)
// Check registered support
func (s *x402ResourceServer) HasRegisteredScheme(network Network, scheme string) bool
func (s *x402ResourceServer) HasFacilitatorSupport(network Network, scheme string) bool
```
### Server Lifecycle Hooks (Chainable)
```go
func (s *x402ResourceServer) OnBeforeVerify(hook BeforeVerifyHook) *x402ResourceServer
func (s *x402ResourceServer) OnAfterVerify(hook AfterVerifyHook) *x402ResourceServer
func (s *x402ResourceServer) OnVerifyFailure(hook OnVerifyFailureHook) *x402ResourceServer
func (s *x402ResourceServer) OnBeforeSettle(hook BeforeSettleHook) *x402ResourceServer
func (s *x402ResourceServer) OnAfterSettle(hook AfterSettleHook) *x402ResourceServer
func (s *x402ResourceServer) OnSettleFailure(hook OnSettleFailureHook) *x402ResourceServer
```
---
## Scheme Interfaces
### SchemeNetworkClient (Buyer)
```go
type SchemeNetworkClient interface {
Scheme() string
CreatePaymentPayload(ctx context.Context, requirements types.PaymentRequirements) (types.PaymentPayload, error)
}
type ExtensionAwareClient interface {
SchemeNetworkClient
CreatePaymentPayloadWithExtensions(ctx context.Context, requirements types.PaymentRequirements, extensions map[string]interface{}) (types.PaymentPayload, error)
}
```
### SchemeNetworkServer (Seller)
```go
type SchemeNetworkServer interface {
Scheme() string
ParsePrice(price Price, network Network) (AssetAmount, error)
EnhancePaymentRequirements(ctx context.Context, requirements types.PaymentRequirements, supportedKind types.SupportedKind, extensions []string) (types.PaymentRequirements, error)
}
```
### SchemeNetworkFacilitator
```go
type SchemeNetworkFacilitator interface {
Scheme() string
CaipFamily() string
GetExtra(network Network) map[string]interface{}
GetSigners(network Network) []string
Verify(ctx context.Context, payload types.PaymentPayload, requirements types.PaymentRequirements, fctx *FacilitatorContext) (*VerifyResponse, error)
Settle(ctx context.Context, payload types.PaymentPayload, requirements types.PaymentRequirements, fctx *FacilitatorContext) (*SettleResponse, error)
}
```
### FacilitatorClient (Network Boundary)
```go
type FacilitatorClient interface {
Verify(ctx context.Context, payloadBytes []byte, requirementsBytes []byte) (*VerifyResponse, error)
Settle(ctx context.Context, payloadBytes []byte, requirementsBytes []byte) (*SettleResponse, error)
GetSupported(ctx context.Context) (SupportedResponse, error)
}
```
### ClientExtension
```go
type ClientExtension interface {
Key() string
EnrichPaymentPayload(ctx context.Context, payload types.PaymentPayload, required types.PaymentRequired) (types.PaymentPayload, error)
}
```
### MoneyParser
```go
type MoneyParser func(amount float64, network Network) (*AssetAmount, error)
```
---
## Middleware Reference
All Go middleware packages provide three construction levels plus builder shortcuts.
### Gin (`go/http/gin`)
```go
import x402gin "github.com/anthropics/x402/go/http/gin"
// Standard (recommended)
r.Use(x402gin.PaymentMiddleware(routes, server, opts ...MiddlewareOption))
// From HTTP server
r.Use(x402gin.PaymentMiddlewareFromHTTPServer(httpServer, opts ...MiddlewareOption))
// From config
r.Use(x402gin.PaymentMiddlewareFromConfig(routes, opts ...MiddlewareOption))
// Builder shortcut
r.Use(x402gin.X402Payment(Config{ Routes: routes, Facilitator: facilitator, Schemes: schemes }))
// Simple one-liner
r.Use(x402gin.SimpleX402Payment(payTo, price, network, facilitatorURL))
```
MiddlewareOption functions:
| Option | Type | Description |
|:---:|:---:|:---:|
| `WithFacilitatorClient(client)` | `FacilitatorClient` | Facilitator for verification/settlement |
| `WithScheme(network, server)` | `Network, SchemeNetworkServer` | Register a scheme |
| `WithPaywallConfig(config)` | `*PaywallConfig` | Browser paywall settings |
| `WithSyncFacilitatorOnStart(bool)` | `bool` | Fetch supported kinds at startup |
| `WithErrorHandler(fn)` | `func(*gin.Context, error)` | Custom error handler |
| `WithSettlementHandler(fn)` | `func(*gin.Context, *SettleResponse)` | Custom settlement handler |
| `WithTimeout(duration)` | `time.Duration` | Request timeout |
Config struct (builder pattern):
```go
type Config struct {
Routes x402http.RoutesConfig
Facilitator x402.FacilitatorClient
Facilitators []x402.FacilitatorClient
Schemes []SchemeConfig
PaywallConfig *x402http.PaywallConfig
SyncFacilitatorOnStart bool
Timeout time.Duration
ErrorHandler func(*gin.Context, error)
SettlementHandler func(*gin.Context, *x402.SettleResponse)
}
type SchemeConfig struct {
Network x402.Network
Server x402.SchemeNetworkServer
}
```
### Echo (`go/http/echo`)
Same API surface as Gin but with `echo.Context` types:
```go
import x402echo "github.com/anthropics/x402/go/http/echo"
e.Use(x402echo.PaymentMiddleware(routes, server, opts ...MiddlewareOption))
e.Use(x402echo.X402Payment(Config{ ... }))
e.Use(x402echo.SimpleX402Payment(payTo, price, network, facilitatorURL))
```
### net/http (`go/http/nethttp`)
Returns `func(http.Handler) http.Handler`:
```go
import x402nethttp "github.com/anthropics/x402/go/http/nethttp"
handler := x402nethttp.PaymentMiddleware(routes, server, opts ...MiddlewareOption)(yourHandler)
handler := x402nethttp.X402Payment(Config{ ... })(yourHandler)
handler := x402nethttp.SimpleX402Payment(payTo, price, network, facilitatorURL)(yourHandler)
http.ListenAndServe(":4021", handler)
```
---
## HTTP Client (Buyer)
```go
import x402http "github.com/anthropics/x402/go/http"
// Create HTTP client with payment support
httpClient := x402http.Newx402HTTPClient(x402Client)
// Wrap existing http.Client
wrappedClient := x402http.WrapHTTPClientWithPayment(http.DefaultClient, httpClient)
// Direct methods
resp, err := httpClient.GetWithPayment(ctx, url)
resp, err := httpClient.PostWithPayment(ctx, url, body)
resp, err := httpClient.DoWithPayment(ctx, req)
// Convenience functions
resp, err := x402http.Get(ctx, url, httpClient)
resp, err := x402http.Post(ctx, url, body, httpClient)
resp, err := x402http.Do(ctx, req, httpClient)
```
---
## EVM Mechanisms
### ExactEvmScheme (Client)
```go
import (
"github.com/anthropics/x402/go/mechanisms/evm"
evmsigners "github.com/anthropics/x402/go/mechanisms/evm/signers"
evmclient "github.com/anthropics/x402/go/mechanisms/evm/exact/client"
)
signer, err := evmsigners.NewClientSignerFromPrivateKey("0x...")
scheme := evmclient.NewExactEvmScheme(signer, config)
scheme.Scheme() // "exact"
```
ClientEvmSigner interface:
```go
type ClientEvmSigner interface {
Address() string
SignTypedData(ctx context.Context, domain TypedDataDomain, types map[string][]TypedDataField, primaryType string, message map[string]interface{}) ([]byte, error)
}
```
### ExactEvmScheme (Server)
```go
import evmserver "github.com/anthropics/x402/go/mechanisms/evm/exact/server"
scheme := evmserver.NewExactEvmScheme()
scheme.RegisterMoneyParser(customParser) // Optional: custom price parsing
scheme.Scheme() // "exact"
```
### ExactEvmScheme (Facilitator)
```go
import evmfacilitator "github.com/anthropics/x402/go/mechanisms/evm/exact/facilitator"
scheme := evmfacilitator.NewExactEvmScheme(signer, &ExactEvmSchemeConfig{
DeployERC4337WithEIP6492: false, // Auto-deploy smart wallets
SimulateInSettle: false, // Re-simulate during settle
})
```
### EVM Types
```go
type TypedDataDomain struct {
Name string `json:"name"`
Version string `json:"version"`
ChainID *big.Int `json:"chainId"`
VerifyingContract string `json:"verifyingContract"`
}
type TypedDataField struct {
Name string `json:"name"`
Type string `json:"type"`
}
type AssetTransferMethod string
const (
AssetTransferMethodEIP3009 AssetTransferMethod = "eip3009"
AssetTransferMethodPermit2 AssetTransferMethod = "permit2"
)
type AssetInfo struct {
Address string
Name string
Version string
Decimals int
AssetTransferMethod AssetTransferMethod
SupportsEip2612 bool
}
```
---
## SVM Mechanisms
### ExactSvmScheme (Client)
```go
import (
svmsigners "github.com/anthropics/x402/go/mechanisms/svm/signers"
svmclient "github.com/anthropics/x402/go/mechanisms/svm/exact/client"
)
signer, err := svmsigners.NewClientSignerFromPrivateKey(solanaPrivateKey)
scheme := svmclient.NewExactSvmScheme(signer, config?)
```
### ExactSvmScheme (Server)
```go
import svmserver "github.com/anthropics/x402/go/mechanisms/svm/exact/server"
scheme := svmserver.NewExactSvmScheme()
scheme.RegisterMoneyParser(customParser)
```
---
## Facilitator Hooks
```go
type FacilitatorVerifyContext struct {
Ctx context.Context
Payload PaymentPayloadView
Requirements PaymentRequirementsView
PayloadBytes []byte
RequirementsBytes []byte
}
type FacilitatorBeforeHookResult struct {
Abort bool
Reason string
Message string
}
type FacilitatorBeforeVerifyHook func(FacilitatorVerifyContext) (*FacilitatorBeforeHookResult, error)
type FacilitatorAfterVerifyHook func(FacilitatorVerifyResultContext) error
type FacilitatorOnVerifyFailureHook func(FacilitatorVerifyFailureContext) (*FacilitatorVerifyFailureHookResult, error)
type FacilitatorBeforeSettleHook func(FacilitatorSettleContext) (*FacilitatorBeforeHookResult, error)
type FacilitatorAfterSettleHook func(FacilitatorSettleResultContext) error
type FacilitatorOnSettleFailureHook func(FacilitatorSettleFailureContext) (*FacilitatorSettleFailureHookResult, error)
```
- [Authentication](https://web3.okx.com/onchainos/dev-docs/payments/api-authentication.md)
# Authentication
All endpoints require API Key authentication. Include the following fields in the request header:
| Header | Required | Description |
| :---: | :---: | :---: |
| `OK-ACCESS-KEY` | Yes | API Key |
| `OK-ACCESS-SIGN` | Yes | Request signature |
| `OK-ACCESS-PASSPHRASE` | Yes | API passphrase |
| `OK-ACCESS-TIMESTAMP` | Yes | ISO 8601 timestamp |
| `Content-Type` | Yes | Must be set to `application/json` for POST requests |
- [Get Supported Info](https://web3.okx.com/onchainos/dev-docs/payments/get-supported-schemes-and-networks.md)
# Get Supported Info
Query the list of schemes, networks, and signers supported by the Facilitator. The Seller SDK calls this endpoint to build the `accepts` array for the `402` response.
## Request URL
GET `/api/v6/pay/x402/supported`
Request Parameters: None
### Response Parameters
| Parameter | Type | Description |
|:---:|:---:|:---:|
| kinds | Array\ | List of supported payment types |
| kinds[].x402Version | Integer | x402 protocol version, e.g. `2` |
| kinds[].scheme | String | Settlement scheme: `exact` (one-time payment with a fixed amount) / `aggr_deferred` (deferred aggregated settlement) |
| kinds[].network | String | CAIP-2 chain identifier, e.g. `eip155:196` (X Layer) |
| kinds[].extra | Object | Scheme-specific extended configuration (optional) |
| extensions | Array\ | List of supported extension identifiers |
| signers | Object | CAIP-2 wildcard to signer address array mapping |
### Request Example
```bash
curl --location --request GET 'https://web3.okx.com/api/v6/pay/x402/supported' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
### Response Example
```json
{
"code": "0",
"data": {
"kinds": [
{
"x402Version": 2,
"scheme": "exact",
"network": "eip155:196",
"extra": null
},
{
"x402Version": 2,
"scheme": "aggr_deferred",
"network": "eip155:196",
"extra": null
}
],
"extensions": [],
"signers": {
"eip155:*": ["0x...facilitatorSignerAddress"]
}
},
"msg": ""
}
```
- [Verify Transaction](https://web3.okx.com/onchainos/dev-docs/payments/payment-verification.md)
# Verify Transaction
Verify the validity of a payment authorization **without executing an on-chain transaction**. The Seller calls this endpoint to confirm the legitimacy of the Buyer's signature before releasing the resource.
## Request URL
POST `/api/v6/pay/x402/verify`
### Request Parameters
| Parameter | Type | Required | Description |
|:---:|:---:|:---:|:---:|
| x402Version | Integer | Yes | x402 protocol version, e.g. `2` |
| paymentPayload | Object | Yes | The x402 payment payload carried by the client with the protected request. See PaymentPayload for details |
| paymentRequirements | Object | Yes | Payment requirements defined by the Seller. See PaymentRequirements for details |
### Response Parameters
| Parameter | Type | Description |
|:---:|:---:|:---:|
| isValid | Boolean | `true` if verification passed, `false` if verification failed |
| invalidReason | String | Machine-readable reason for invalidity (returned on verification failure), e.g. `insufficient_funds` |
| invalidMessage | String | Human-readable description of invalidity (returned on verification failure) |
| payer | String | Payer wallet address |
### Request Example
```bash
curl --location --request POST 'https://web3.okx.com/api/v6/pay/x402/verify' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z' \
--data '{
"x402Version": 2,
"paymentPayload": {
"x402Version": 2,
"resource": {
"url": "https://api.example.com/premium-data",
"description": "Access to premium data",
"mimeType": "application/json"
},
"accepted": {
"scheme": "exact",
"network": "eip155:196",
"amount": "10000",
"asset": "0x4ae46a509f6b1d9056937ba4500cb143933d2dc8",
"payTo": "0xRecipientAddress",
"maxTimeoutSeconds": 60,
"extra": { "name": "USDG", "version": "2" }
},
"payload": {
"signature": "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f13480...",
"authorization": {
"from": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"to": "0xRecipientAddress",
"value": "10000",
"validAfter": "0",
"validBefore": "1740672154",
"nonce": "0xf374661..."
}
}
},
"paymentRequirements": {
"scheme": "exact",
"network": "eip155:196",
"amount": "10000",
"asset": "0x4ae46a509f6b1d9056937ba4500cb143933d2dc8",
"payTo": "0xRecipientAddress",
"maxTimeoutSeconds": 60,
"extra": { "name": "USDG", "version": "2" }
}
}'
```
### Response Example -- Verification Passed
```json
{
"code": "0",
"msg": "success",
"data": {
"isValid": true,
"invalidReason": null,
"invalidMessage": null,
"payer": "0xcb30ed083ad246b126a3aa1f414b44346e83e67d"
}
}
```
### Response Example -- Verification Failed
```json
{
"code": "0",
"msg": "success",
"data": {
"isValid": false,
"invalidReason": "insufficient_funds",
"invalidMessage": "Payer balance is below required amount",
"payer": "0xcb30ed083ad246b126a3aa1f414b44346e83e67d"
}
}
```
- [Settle Transaction](https://web3.okx.com/onchainos/dev-docs/payments/payment-settlement.md)
# Settle Transaction
After verification passes, submit on-chain settlement. Aligned with Coinbase V2 protocol + OKX extension field `syncSettle`.
## Request URL
POST `/api/v6/pay/x402/settle`
## Request Parameters
| Parameter | Type | Required | Description |
|:---:|:---:|:---:|:---:|
| x402Version | Integer | Yes | x402 protocol version, e.g. `2` |
| paymentPayload | Object | Yes | x402 payment payload carried by the client. See PaymentPayload for details |
| paymentRequirements | Object | Yes | Payment requirements defined by the seller. See PaymentRequirements for details |
| syncSettle | Boolean | No | OKX extension. `true` = wait synchronously for on-chain confirmation before returning; `false` (default) = asynchronous, returns immediately after broadcast. Only effective for the `exact` scheme |
## Response Parameters
| Parameter | Type | Description |
|:---:|:---:|:---:|
| success | Boolean | Whether the settlement succeeded |
| errorReason | String | Machine-readable failure reason (returned on failure) |
| errorMessage | String | Human-readable failure description (returned on failure) |
| payer | String | Payer wallet address |
| transaction | String | On-chain transaction hash (returned on `exact` success; empty string for `deferred`) |
| network | String | CAIP-2 chain identifier, e.g. `eip155:196` |
| status | String | OKX extension. Settlement status; see the status table below |
## status Values
| scheme | syncSettle | Result | status Value | transaction |
|:---:|:---:|:---:|:---:|:---:|
| exact | false (default) | Broadcast | `pending` | txHash |
| exact | true | On-chain confirmation succeeded | `success` | txHash |
| exact | true | Wait timed out | `timeout` | txHash |
| aggr_deferred | — | Stored | `success` | Empty string |
## Request Example
```bash
curl --location --request POST 'https://web3.okx.com/api/v6/pay/x402/settle' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z' \
--data '{
"x402Version": 2,
"paymentPayload": {
"x402Version": 2,
"resource": {
"url": "https://api.example.com/premium-data",
"description": "Access to premium data",
"mimeType": "application/json"
},
"accepted": {
"scheme": "exact",
"network": "eip155:196",
"amount": "10000",
"asset": "0x4ae46a509f6b1d9056937ba4500cb143933d2dc8",
"payTo": "0xRecipientAddress",
"maxTimeoutSeconds": 60,
"extra": { "name": "USDG", "version": "2" }
},
"payload": {
"signature": "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f13480...",
"authorization": {
"from": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"to": "0xRecipientAddress",
"value": "10000",
"validAfter": "0",
"validBefore": "1740672154",
"nonce": "0xf374661..."
}
}
},
"paymentRequirements": {
"scheme": "exact",
"network": "eip155:196",
"amount": "10000",
"asset": "0x4ae46a509f6b1d9056937ba4500cb143933d2dc8",
"payTo": "0xRecipientAddress",
"maxTimeoutSeconds": 60,
"extra": { "name": "USDG", "version": "2" }
},
"syncSettle": true
}'
```
## Response Example - Synchronous Settlement (syncSettle=true)
```json
{
"code": "0",
"msg": "success",
"data": {
"success": true,
"errorReason": null,
"errorMessage": null,
"payer": "0xcb30ed083ad246b126a3aa1f414b44346e83e67d",
"transaction": "0x4f46ed8eac92ddbccfb56a88ff827db3616c7beb191adabbeeded901340bd7d5",
"network": "eip155:196",
"status": "success"
}
}
```
## Response Example - Asynchronous Settlement (syncSettle=false)
```json
{
"code": "0",
"msg": "success",
"data": {
"success": true,
"errorReason": null,
"errorMessage": null,
"payer": "0xcb30ed083ad246b126a3aa1f414b44346e83e67d",
"transaction": "0x4f46ed8eac92ddbccfb56a88ff827db3616c7beb191adabbeeded901340bd7d5",
"network": "eip155:196",
"status": "pending"
}
}
```
## Response Example - Settlement Failed
```json
{
"code": "0",
"msg": "success",
"data": {
"success": false,
"errorReason": "insufficient_funds",
"errorMessage": "Transaction reverted",
"payer": "0xcb30ed083ad246b126a3aa1f414b44346e83e67d",
"transaction": "",
"network": "eip155:196",
"status": ""
}
}
```
- [Query Settlement Status](https://web3.okx.com/onchainos/dev-docs/payments/api-query-settlement-status.md)
# Query Settlement Status
Query settlement status by on-chain transaction hash. Applicable to `exact` (`syncSettle=false`) one-time payments asynchronous settlement scenarios and status tracking after `aggr_deferred` batch payments submission.
## Request URL
GET `/api/v6/pay/x402/settle/status`
## Request Parameters
| Parameter | Type | Required | Description |
|:---:|:---:|:---:|:---:|
| txHash | String | Yes | On-chain transaction hash (Query parameter) |
## Response Parameters
| Parameter | Type | Description |
|:---:|:---:|:---:|
| success | Boolean | Whether the query succeeded (`false` when txHash does not exist) |
| errorReason | String | Machine-readable failure reason (returned on failure) |
| errorMessage | String | Human-readable failure description (returned on failure) |
| payer | String | Payer wallet address |
| transaction | String | On-chain transaction hash |
| network | String | CAIP-2 chain identifier |
| status | String | Current settlement status |
## status Values
| status | Meaning |
|:---:|:---:|
| `pending` | Processing, not yet finalized |
| `success` | On-chain transaction succeeded |
| `failed` | On-chain transaction failed |
## Request Example
```bash
curl --location --request GET 'https://web3.okx.com/api/v6/pay/x402/settle/status?txHash=0x4f46ed8eac92ddbccfb56a88ff827db3616c7beb191adabbeeded901340bd7d5' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example -- Query Succeeded
```json
{
"code": "0",
"msg": "success",
"data": {
"success": true,
"errorReason": null,
"errorMessage": null,
"payer": "0xcb30ed083ad246b126a3aa1f414b44346e83e67d",
"transaction": "0x4f46ed8eac92ddbccfb56a88ff827db3616c7beb191adabbeeded901340bd7d5",
"network": "eip155:196",
"status": "success"
}
}
```
## Response Example -- Transaction Not Found
```json
{
"code": "0",
"msg": "success",
"data": {
"success": false,
"errorReason": "not_found",
"errorMessage": "Transaction not found for txHash: 0xabc123...",
"payer": null,
"transaction": null,
"network": null,
"status": null
}
}
```
- [Common Data Structures](https://web3.okx.com/onchainos/dev-docs/payments/api-common-data-structures.md)
# Common Data Structures
## PaymentPayload
After signing, the Buyer passes this to the Seller via the `X-PAYMENT` header (base64-encoded), and the Seller forwards it as-is to the Facilitator.
| Parameter | Type | Required | Description |
|:---:|:---:|:---:|:---:|
| x402Version | Integer | Yes | Protocol version, e.g. `2` |
| resource | Object | No | Protected resource description |
| resource.url | String | Yes | URL of the protected resource |
| resource.description | String | No | Resource description |
| resource.mimeType | String | No | Expected response MIME type |
| accepted | Object | Yes | Payment method selected by the Buyer (picked from the `accepts` array), structure identical to PaymentRequirements |
| payload | Object | Yes | Signed data |
| payload.signature | String | Yes | EIP-712 signature. `exact`: EOA signature; `aggr_deferred`: session key signature |
| payload.authorization | Object | Yes | EIP-3009 authorization parameters |
## Authorization
| Parameter | Type | Required | Description |
|:---:|:---:|:---:|:---:|
| from | String | Yes | Payer wallet address |
| to | String | Yes | Payee wallet address |
| value | String | Yes | Payment amount (atomic units, with precision) |
| validAfter | String | Yes | Unix timestamp when the authorization becomes valid |
| validBefore | String | Yes | Unix timestamp when the authorization expires |
| nonce | String | Yes | 32-byte random nonce (0x hex format, replay protection) |
## PaymentRequirements
Used both as an element of the 402 response `accepts` array and as `paymentPayload.accepted`.
| Parameter | Type | Required | Description |
|:---:|:---:|:---:|:---:|
| scheme | String | Yes | Settlement scheme: `exact` / `aggr_deferred` |
| network | String | Yes | CAIP-2 chain identifier, e.g. `eip155:196` |
| amount | String | Yes | Payment amount (atomic unit string) |
| asset | String | Yes | Token contract address |
| payTo | String | Yes | Payee wallet address |
| maxTimeoutSeconds | Integer | No | Maximum timeout for payment completion (seconds) |
| extra | Object | No | Scheme-specific extensions. For `aggr_deferred`, the Buyer includes the session key certificate in `accepted.extra.sessionCert` |
- [Error Codes](https://web3.okx.com/onchainos/dev-docs/payments/error-code.md)
# Error Codes
### Authentication Errors (HTTP 401)
| Error Code | Description |
|:---:|:---:|
| 50103 | Request header `OK-ACCESS-KEY` cannot be empty |
| 50104 | Request header `OK-ACCESS-PASSPHRASE` cannot be empty |
| 50105 | Request header `OK-ACCESS-PASSPHRASE` is incorrect |
| 50106 | Request header `OK-ACCESS-SIGN` cannot be empty |
| 50107 | Request header `OK-ACCESS-TIMESTAMP` cannot be empty |
| 50111 | Invalid `OK-ACCESS-KEY` |
| 50112 | Invalid `OK-ACCESS-TIMESTAMP` |
| 50113 | Invalid signature |
### Request Errors
| Error Code | HTTP Status | Description |
|:---:|:---:|:---:|
| 50011 | 429 | Request rate too high, exceeding the allowed limit for this endpoint |
| 50014 | 400 | Required parameter `{param}` cannot be empty |
### Business Errors
| Error Code | HTTP Status | Description |
|:---:|:---:|:---:|
| 50026 | 500 | System error, please try again later |
| 81001 | 200 | Invalid parameter `{param}` |
| 81004 | 200 | Unsupported chain |
| 80007 | 200 | Risky address |
- [Instant vs Batch Payment Request Differences](https://web3.okx.com/onchainos/dev-docs/payments/api-instant-vs-batch.md)
# Instant vs Batch Payment Request Differences
## Instant Payment (exact) vs Batch Payment (aggr_deferred) Request Differences
| Field | exact | aggr_deferred |
|:---:|:---:|:---:|
| `accepted.scheme` | `"exact"` | `"aggr_deferred"` |
| `accepted.extra.sessionCert` | None | Required: session key certificate (base64) |
| `payload.signature` | EOA signature | session key signature |
| `syncSettle` (settle endpoint) | Optional, defaults to false | Not applicable |
| settle response `transaction` | txHash | Empty string |
> **sessionCert flow**: Buyer includes it in `accepted.extra.sessionCert` → Seller passes it through as-is → Facilitator extracts it internally for TEE signature verification and signature conversion. Seller does not need to parse this field.
- [Trade API](https://web3.okx.com/onchainos/dev-docs/trade/dex-api-introduction.md)
# Trade API
Our Trade API provides developers a set of APIs to identify the best quotes and execute the most efficient swap routes through our smart routing algorithm and liquidity aggregation across 500+ DEXs across Solana, EVM, SUI and more.
It provides the critical multi-chain DEX aggregator backend for developers to build and scale trading experience within a variety of dApps such as
- DEX Dashboards and Aggregators: offering users a comprehensive view of prices and liquidity across multiple decentralized exchanges for optimized trading.
- Native Swap in Wallets: enabling wallets to incorporate built-in swap functionalities with access to the best liquidity pools.
- Multi-chain Trading Bots: allowing automation of trading strategies such as arbitrage, liquidity mining, and asset management by leveraging high liquidity and efficient trade routing across 20+ chains.
- DeFi Lending Platforms: supporting Defi platforms with liquidity tracking for optimized asset management and lending strategies.
- Arbitrage Bots: providing tools and infrastructure to enable developers to perform and profit from price differences across multiple DEXs or between DEXs and CEXs.
- [Build with AI](https://web3.okx.com/onchainos/dev-docs/trade/dex-ai-tools-introduction.md)
# Build with AI
OKX Onchain OS Trade give agents and developers a complete swap execution layer, from getting the best multi-chain quote to a transaction, without stitching together separate routing, signing, and RPC integrations. Whether you are building an automated trading agent or embedding swap execution into a product, the Skill and MCP Server handle the entire lifecycle with production-grade patterns already built in.
## Why Onchain OS Trade for AI
- **Broadest on-chain liquidity coverage.** The OKX DEX aggregator routes across 500+ DEXs on 20+ chains such as Solana, Ethereum, Base, BNB Chain, Arbitrum, Sui, TON, and more. At quote time, it splits orders across multiple pools and routes to minimize price impact and maximize the amount received, not just find the cheapest single route.
- **Full execution stack in one tool.** Onchain OS Trade AI tools cover every step from quote → swap → sign with multi-chain support. An Agent can execute a complete multi-step swap from a single high-level instruction, including Approve transactions where needed, chain-specific signing flows for EVM and Solana, and confirmation handling.
- **Agent-optimized interface Skill and MCP Server.** OKX Web3 Trade provides two integration paths depending on how your Agent is deployed. The Skill teaches your Agent how to call the OKX DEX API through structured instructions and code generation. The MCP Server exposes the same capabilities as directly callable tools via the Model Context Protocol.
## Quickstart
By adding the Skill files to the Agent’s skill directory, the Agent will automatically load the intent router, chain-specific execution playbooks, signing modes, and error-handling logic.
```shell
npx skills add okx/onchainos-skills
```
For more details, please refer to the [GitHub repository](https://github.com/okx/onchainos-skills).
For Claude Desktop, Cursor, and other MCP-compatible clients, add the following configuration:
**for General Claude code**
```shell
claude mcp add onchainos-mcp https://web3.okx.com/api/v1/onchainos-mcp -t http -H"OK-ACCESS-KEY: d573a84c-8e79-4a35-b0c6-427e9ad2478d"
```
**For Claude Desktop Installation**
1. Go to the `Settings` page and locate the `Connector` menu.
2. Scroll to the bottom, find `Add custom connector`, and enter the URL: https://web3.okx.com/api/v1/onchainos-mcp
You can also try a local installation. For detailed instructions, please consult your Claude client.
**for Claude code MCP settings**
```shell
claude mcp add-json onchainos-mcp '{
"type": "http",
"url": "https://web3.okx.com/api/v1/onchainos-mcp",
"headers": {
"OK-ACCESS-KEY": "d573a84c-8e79-4a35-b0c6-427e9ad2478d"
}
}'
```
One MCP server covers both Trade and Market capabilities. Restart your client after updating the config.
- [Skills](https://web3.okx.com/onchainos/dev-docs/trade/dex-ai-tools-skills.md)
# Skills
Beyond simply "retrieving documentation," Skills encapsulate domain knowledge and engineering workflows into stable capabilities.
They enable the Agent not only to answer "how to write the docs," but also to handle "which endpoint to choose, what the next step is, and how to deal with errors."
## Why Onchain OS Trade Skills for AI
- Covers the full DEX swap lifecycle: supported chains → liquidity sources → approve → quote → swap → sign → broadcast.
- Provides an Intent Router: maps user instructions ("swap 0.1 ETH for USDC", "get me a quote for 500 USDT → SOL", "what DEXs are available on Arbitrum?") to the correct API and the appropriate first action.
- Plug-and-play for AI agents: structured, framework-agnostic capability modules that directly encapsulate the OKX DEX API for AI Agents. Each Skill corresponds to a specific capability (e.g., generating transaction execution data, fetching quotes, querying token information) and defines clear input/output schemas, enabling seamless integration into any Agent architecture.
## Quickstart
```shell
skills:npx skills add okx/onchainos-skills
```
For more details, please refer to the [GitHub repository](https://github.com/okx/onchainos-skills).
## Example interactions
Once the Skills is uploaded to your agent, an Agent can respond to natural-language instructions like:
```shell
What chains support DEX swaps?
# Call dex-aggregator-supported-chains
```
```shell
Which DEXs are available on X-layer?
# Call dex-liquidity
```
```shell
How much USDC will I get for 1 OKB on X-layer?
# Call dex-quote
```
```shell
I need to approve USDT before swapping, generate the calldata
# Call dex-approve-transaction
```
```shell
Build a swap transaction: 100 USDT → ETH, wallet 0xd8dA..., slippage 0.5%
# Call dex-swap
```
```shell
Swap 2 SOL for USDC on Solana, wallet DYw8...
# Call dex-solana-swap-instruction
```
- [MCP Server](https://web3.okx.com/onchainos/dev-docs/trade/dex-ai-tools-mcp-server.md)
# MCP Server
MCP (Model Context Protocol) connects AI tools with developer resources. After adding the OKX DEX MCP Server, the Agent can get quotes, check DEX liquidity, construct Approve transactions, execute swaps, and signed transactions — all through standardized, directly callable tool interfaces, within a single conversation or editor session, without any additional integration code.
## What the MCP Server exposes
- **Chain & Liquidity Tools:** `dex-okx-dex-aggregator-supported-chains` lists all chains that support swaps; `dex-okx-dex-liquidity` lists active DEX liquidity sources on a specified chain.
- **Approve Tool:** `dex-okx-dex-approve-transaction` generates the ERC-20 Approve calldata required for a first-time token swap.
- **Quote Tool:** `dex-okx-dex-quote` retrieves the best aggregated quote across all liquidity sources, including expected output amount, price impact, and route details.
- **Swap Tool:** `dex-okx-dex-swap` constructs a complete swap transaction (calldata + value + gas estimation), ready for signing.
- **Solana Tool:** `dex-okx-dex-solana-swap-instruction` generates a Solana versioned transaction instruction set for swapping SPL tokens, ready to be signed by a wallet.
## Quickstart
**for General Claude code**
```shell
claude mcp add onchainos-mcp https://web3.okx.com/api/v1/onchainos-mcp -t http -H"OK-ACCESS-KEY: d573a84c-8e79-4a35-b0c6-427e9ad2478d"
```
**for Claude Desktop Installation**
1. Go to the `Settings` page and locate the `Connector` menu.
2. Scroll to the bottom, find `Add custom connector`, and enter the URL: https://web3.okx.com/api/v1/onchainos-mcp
You can also try a local installation. For detailed instructions, please consult your Claude client.
**for Claude code MCP settings**
```shell
claude mcp add-json onchainos-mcp '{
"type": "http",
"url": "https://web3.okx.com/api/v1/onchainos-mcp",
"headers": {
"OK-ACCESS-KEY": "d573a84c-8e79-4a35-b0c6-427e9ad2478d"
}
}'
```
## Example interactions
Once the MCP Server is active, an Agent can respond to natural-language instructions like:
```shell
What chains support DEX swaps?
# Call dex-aggregator-supported-chains
```
```shell
Which DEXs are available on X-layer?
# Call dex-liquidity
```
```shell
How much USDC will I get for 1 OKB on X-layer?
# Call dex-quote
```
```shell
I need to approve USDT for swap
# Call dex-approve-transaction
```
```shell
Swap 100 USDT to OKB
# Call dex-swap
```
```shell
Swap 2 SOL for USDC on Solana...
# Call dex-solana-swap-instruction
```
- [llms.txt](https://web3.okx.com/onchainos/dev-docs/trade/dex-ai-tools-llm.md)
# llms.txt
## Onchain OS Trade llms.txt Structure
`llms.txt` is a proposed standard designed to help AI language models efficiently understand and navigate documentation or websites. It enables Agents to quickly grasp the structure of documentation and locate relevant pages. Based on minimal context, it can precisely identify the appropriate module within shorter response times, reduce hallucinations, and lower token consumption—serving as a low-cost navigation layer.
A typical `llms.txt` file includes:
- Site title (H1)
- Sections organized by module (H2)
- A link to each page + a one-sentence description (used for routing and retrieval)
Please visit [OnchainOS.llms.txt](https://web3.okx.com/llms.txt) to view the detailed structure of `llms.txt`.
## Onchain OS Trade llms-full.txt Structure
In addition to `llms.txt`, we also provide `llms-full.txt`. Unlike a directory-style index, it aggregates the full site documentation in Markdown format (including more granular descriptions and examples) for deeper indexing and search.
Suitable for:
- AI tools that require full-context access (deep indexing / advanced search / offline knowledge base construction)
- Developers who want a comprehensive view of all pages and resources at once
- Building custom AI workflows (e.g., full indexing first, then chunk-based retrieval as needed)
Please visit [OnchainOS.llms-full.txt](https://web3.okx.com/llms-full.txt) to view the detailed structure of `llms-full.txt`.
- [Build Swap Applications](https://web3.okx.com/onchainos/dev-docs/trade/dex-use-swap.md)
# Build Swap Applications
- [Build Swap Applications on Solana](https://web3.okx.com/onchainos/dev-docs/trade/dex-use-swap-solana-quick-start.md)
# Build Swap Applications on Solana
There are two approaches to building swap applications with OKX DEX on Solana:
1. The API-first approach - directly interacting with OKX DEX API endpoints
2. The SDK approach - using the `@okx-dex/okx-dex-sdk` package for a simplified developer experience
This guide covers both methods to help you choose the approach that best fits your needs.
## Method 1: API-First Approach
This approach demonstrates a token swap using the OKX DEX API endpoints directly. You will swap SOL to USDC on Solana Mainnet.
## 1. Set Up Your Environment
Import the necessary Node.js libraries and set up your environment variables:
```typescript
// Required libraries
import base58 from "bs58";
import BN from "bn.js";
import * as solanaWeb3 from "@solana/web3.js";
import { Connection } from "@solana/web3.js";
import cryptoJS from "crypto-js";
import axios from "axios";
import dotenv from 'dotenv';
dotenv.config();
// Environment variables
const apiKey = process.env.OKX_API_KEY;
const secretKey = process.env.OKX_SECRET_KEY;
const apiPassphrase = process.env.OKX_API_PASSPHRASE;
const userAddress = process.env.WALLET_ADDRESS;
const userPrivateKey = process.env.PRIVATE_KEY;
const solanaRpcUrl = process.env.SOLANA_RPC_URL;
// Constants
const SOLANA_CHAIN_ID = "501";
const COMPUTE_UNITS = 300000;
const MAX_RETRIES = 3;
// Initialize Solana connection
const connection = new Connection(`${solanaRpcUrl}`, {
confirmTransactionInitialTimeout: 5000
});
```
```typescript
// Utility function for OKX API authentication
function getHeaders(timestamp: string, method: string, requestPath: string, queryString = "", body = "") {
const stringToSign = timestamp + method + requestPath + (queryString || body);
return {
"Content-Type": "application/json",
"OK-ACCESS-KEY": apiKey,
"OK-ACCESS-SIGN": cryptoJS.enc.Base64.stringify(
cryptoJS.HmacSHA256(stringToSign, secretKey)
),
"OK-ACCESS-TIMESTAMP": timestamp,
"OK-ACCESS-PASSPHRASE": apiPassphrase,
};
}
```
## 2. Get Swap Data
Solana's native token address is 11111111111111111111111111111111. Use the /swap endpoint to retrieve detailed swap information:
```typescript
async function getSwapData(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
slippagePercent = '0.5' // 0.5% slippagePercent
) {
const timestamp = new Date().toISOString();
const requestPath = "/api/v6/dex/aggregator/swap";
const params = {
amount: amount,
chainIndex: SOLANA_CHAIN_ID,
fromTokenAddress: fromTokenAddress,
toTokenAddress: toTokenAddress,
userWalletAddress: userAddress,
slippagePercent: slippagePercent
};
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, "GET", requestPath, queryString);
try {
const response = await axios.get(
`https://web3.okx.com${requestPath}${queryString}`,
{ headers }
);
if (response.data.code !== "0" || !response.data.data?.[0]) {
throw new Error(`API Error: ${response.data.msg || "Failed to get swap data"}`);
}
return response.data.data[0];
} catch (error) {
console.error("Error fetching swap data:", error);
throw error;
}
}
```
## 3. Prepare Transaction
```typescript
async function prepareTransaction(callData: string) {
try {
// Decode the base58 encoded transaction data
const decodedTransaction = base58.decode(callData);
// Get the latest blockhash
const recentBlockHash = await connection.getLatestBlockhash();
console.log("Got blockhash:", recentBlockHash.blockhash);
let tx;
// Try to deserialize as a versioned transaction first
try {
tx = solanaWeb3.VersionedTransaction.deserialize(decodedTransaction);
console.log("Successfully created versioned transaction");
tx.message.recentBlockhash = recentBlockHash.blockhash;
} catch (e) {
// Fall back to legacy transaction if versioned fails
console.log("Versioned transaction failed, trying legacy:", e);
tx = solanaWeb3.Transaction.from(decodedTransaction);
console.log("Successfully created legacy transaction");
tx.recentBlockhash = recentBlockHash.blockhash;
}
return {
transaction: tx,
recentBlockHash
};
} catch (error) {
console.error("Error preparing transaction:", error);
throw error;
}
}
```
## 4. Simulate Transaction
Before executing the actual swap, it's crucial to simulate the transaction to ensure it will succeed and to identify any potential issues:
The Simulate API is available to our whitelisted customers only. Please reach out to dexapi@okx.com to request access.
```typescript
async function simulateTransaction(swapData: any) {
try {
if (!swapData.tx) {
throw new Error('Invalid swap data format - missing transaction data');
}
const tx = swapData.tx;
const params: any = {
fromAddress: tx.from,
toAddress: tx.to,
txAmount: tx.value,
chainIndex: SOLANA_CHAIN_ID,
extJson: {
inputData: tx.data
},
includeDebug: true
};
const timestamp = new Date().toISOString();
const requestPath = "/api/v6/dex/pre-transaction/simulate";
const requestBody = JSON.stringify(params);
const headers = getHeaders(timestamp, "POST", requestPath, "", requestBody);
console.log('Simulating transaction...');
const response = await axios.post(
`https://web3.okx.com${requestPath}`,
params,
{ headers }
);
if (response.data.code !== "0") {
throw new Error(`Simulation failed: ${response.data.msg || "Unknown simulation error"}`);
}
const simulationResult = response.data.data[0];
// Check simulation success
if (simulationResult.success === false) {
console.error('Transaction simulation failed:', simulationResult.error);
throw new Error(`Transaction would fail: ${simulationResult.error}`);
}
console.log('Transaction simulation successful');
console.log(`Estimated gas used: ${simulationResult.gasUsed || 'N/A'}`);
if (simulationResult.logs) {
console.log('Simulation logs:', simulationResult.logs);
}
return simulationResult;
} catch (error) {
console.error("Error simulating transaction:", error);
throw error;
}
}
```
## 5. Broadcast Transaction
5.1 Create a Compute Unit Estimation Utility Function
Solana uses compute units instead of gas to measure transaction complexity. There are two approaches to estimate compute units for your transactions: using standard RPC calls or leveraging the Onchain Gateway API.
Method 1: Using the Onchain Gateway API for Compute Unit Estimation
The first approach leverages OKX's Onchain Gateway API, which provides more accurate compute unit estimations than standard methods.
```typescript
/**
* Get transaction compute units from Onchain Gateway API
* @param fromAddress - Sender address
* @param toAddress - Target program address
* @param inputData - Transaction data (base58 encoded)
* @returns Estimated compute units
*/
async function getComputeUnits(
fromAddress: string,
toAddress: string,
inputData: string
): Promise {
try {
const path = 'dex/pre-transaction/gas-limit';
const url = `https://web3.okx.com/api/v6/${path}`;
const body = {
chainIndex: "501", // Solana chain ID
fromAddress: fromAddress,
toAddress: toAddress,
txAmount: "0",
extJson: {
inputData: inputData
}
};
// Prepare authentication with body included in signature
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
if (response.data.code === '0') {
const computeUnits = parseInt(response.data.data[0].gasLimit);
console.log(`API estimated compute units: ${computeUnits}`);
return computeUnits;
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get compute units from API:', (error as Error).message);
throw error;
}
}
```
Method 2: Using RPC to Estimate Compute Units
The second approach utilizes standard Solana RPC calls to simulate and estimate the required compute units for your transaction.
```typescript
/**
* Estimate compute units for a transaction
*/
async function getComputeUnits(transaction: VersionedTransaction): Promise {
try {
// Simulate the transaction to get compute unit usage
const simulationResult = await connection.simulateTransaction(transaction, {
replaceRecentBlockhash: true,
commitment: 'processed'
});
if (simulationResult.value.err) {
throw new Error(`Simulation failed: ${JSON.stringify(simulationResult.value.err)}`);
}
// Get the compute units consumed from simulation
const computeUnitsConsumed = simulationResult.value.unitsConsumed || 200000;
// Add 20% buffer for safety
const computeUnitsWithBuffer = Math.ceil(computeUnitsConsumed * 1.2);
console.log(`Estimated compute units: ${computeUnitsConsumed}`);
console.log(`With 20% buffer: ${computeUnitsWithBuffer}`);
return computeUnitsWithBuffer;
} catch (error) {
console.warn('Failed to estimate compute units, using default:', error);
return 300000; // Default fallback
}
}
```
5.2 Transaction Preparation with Compute Units
Before broadcasting, prepare your transaction with the estimated compute units and latest blockhash:
Method 1: Transaction Using Compute Units from Gas-Limit API
prepare your transaction with compute units estimated from the Onchain Gateway API:
```typescript
/**
* Get transaction compute units from Onchain Gateway API
*/
async function getComputeUnitsFromAPI(
fromAddress: string,
inputData: string
): Promise {
try {
const path = 'dex/pre-transaction/gas-limit';
const url = `https://web3.okx.com/api/v6/${path}`;
const body = {
chainIndex: "501", // Solana chain ID
fromAddress: fromAddress,
toAddress: "", // Can be empty for Solana
txAmount: "0",
extJson: {
inputData: inputData
}
};
// Prepare authentication with body included in signature
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await fetch(url, {
method: 'POST',
headers,
body: bodyString
});
const data = await response.json();
if (data.code === '0') {
const computeUnits = parseInt(data.data[0].gasLimit);
console.log(`API estimated compute units: ${computeUnits}`);
return computeUnits;
} else {
throw new Error(`API Error: ${data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get compute units from API:', (error as Error).message);
throw error;
}
}
/**
* Prepare transaction with compute units from API
*/
async function prepareTransactionWithAPIComputeUnits(
transaction: VersionedTransaction,
fromAddress: string,
transactionData: string
): Promise<{
transaction: VersionedTransaction;
gasData: {
estimatedComputeUnits: number;
priorityFee: number;
blockhash: string;
};
}> {
try {
// Get fresh blockhash
const { blockhash } = await connection.getLatestBlockhash('confirmed');
console.log(`Using blockhash: ${blockhash}`);
// Update the transaction's blockhash
transaction.message.recentBlockhash = blockhash;
// Check if transaction already has compute budget instructions
const hasComputeBudgetIx = transaction.message.compiledInstructions.some(ix => {
const programId = transaction.message.staticAccountKeys[ix.programIdIndex];
return programId.equals(ComputeBudgetProgram.programId);
});
if (hasComputeBudgetIx) {
console.log('Transaction already contains compute budget instructions, skipping addition');
return {
transaction,
gasData: {
estimatedComputeUnits: 300000,
priorityFee: 1000,
blockhash
}
};
}
// Get compute units from API
const estimatedComputeUnits = await getComputeUnitsFromAPI(fromAddress, transactionData);
// Set priority fee
const priorityFee = 1000; // microLamports
const gasData = {
estimatedComputeUnits,
priorityFee,
blockhash
};
console.log(`Priority fee: ${gasData.priorityFee} microLamports`);
// Create compute unit limit instruction
const computeBudgetIx = ComputeBudgetProgram.setComputeUnitLimit({
units: gasData.estimatedComputeUnits
});
// Create compute unit price instruction for priority
const computePriceIx = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: gasData.priorityFee
});
// Get existing instructions and account keys
const existingInstructions = [...transaction.message.compiledInstructions];
const existingAccountKeys = [...transaction.message.staticAccountKeys];
// Add compute budget program to account keys if not present
let computeBudgetProgramIndex = existingAccountKeys.findIndex(
key => key.equals(ComputeBudgetProgram.programId)
);
if (computeBudgetProgramIndex === -1) {
computeBudgetProgramIndex = existingAccountKeys.length;
existingAccountKeys.push(ComputeBudgetProgram.programId);
}
// Create new instructions array with compute budget instructions
const newInstructions = [
{
programIdIndex: computeBudgetProgramIndex,
accountKeyIndexes: [],
data: computeBudgetIx.data
},
{
programIdIndex: computeBudgetProgramIndex,
accountKeyIndexes: [],
data: computePriceIx.data
},
...existingInstructions
];
// Create new versioned message with proper instruction mapping
const newMessage = new TransactionMessage({
payerKey: existingAccountKeys[0],
recentBlockhash: gasData.blockhash,
instructions: newInstructions.map(ix => ({
programId: existingAccountKeys[ix.programIdIndex],
keys: ix.accountKeyIndexes.map(idx => ({
pubkey: existingAccountKeys[idx],
isSigner: false,
isWritable: false
})).filter(key => key.pubkey),
data: Buffer.from(ix.data)
})).filter(ix => ix.programId)
}).compileToV0Message();
// Create and return new transaction
const preparedTransaction = new VersionedTransaction(newMessage);
return { transaction: preparedTransaction, gasData };
} catch (error) {
console.error('Error preparing transaction:', error);
throw error;
}
}
```
Method 2: Transaction Using Compute Units from RPC
```typescript
// Simple connection setup
const connection = new Connection(
process.env.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com"
);
/**
* Prepare transaction with compute units
*/
async function prepareTransactionWithComputeUnits(
transaction: VersionedTransaction
): Promise<{
transaction: VersionedTransaction;
gasData: {
estimatedComputeUnits: number;
priorityFee: number;
blockhash: string;
};
}> {
try {
// Get fresh blockhash
const { blockhash } = await connection.getLatestBlockhash('confirmed');
console.log(`Using blockhash: ${blockhash}`);
// Update the transaction's blockhash
transaction.message.recentBlockhash = blockhash;
// Check if transaction already has compute budget instructions
const hasComputeBudgetIx = transaction.message.compiledInstructions.some(ix => {
const programId = transaction.message.staticAccountKeys[ix.programIdIndex];
return programId.equals(ComputeBudgetProgram.programId);
});
if (hasComputeBudgetIx) {
console.log('Transaction already contains compute budget instructions, skipping addition');
return {
transaction,
gasData: {
estimatedComputeUnits: 300000,
priorityFee: 1000,
blockhash
}
};
}
// Estimate compute units
const estimatedComputeUnits = await getComputeUnits(transaction);
// Set priority fee
const priorityFee = 1000; // microLamports
const gasData = {
estimatedComputeUnits,
priorityFee,
blockhash
};
console.log(`Priority fee: ${gasData.priorityFee} microLamports`);
// Create compute unit limit instruction
const computeBudgetIx = ComputeBudgetProgram.setComputeUnitLimit({
units: gasData.estimatedComputeUnits
});
// Create compute unit price instruction for priority
const computePriceIx = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: gasData.priorityFee
});
// Get existing instructions and account keys
const existingInstructions = [...transaction.message.compiledInstructions];
const existingAccountKeys = [...transaction.message.staticAccountKeys];
// Add compute budget program to account keys if not present
let computeBudgetProgramIndex = existingAccountKeys.findIndex(
key => key.equals(ComputeBudgetProgram.programId)
);
if (computeBudgetProgramIndex === -1) {
computeBudgetProgramIndex = existingAccountKeys.length;
existingAccountKeys.push(ComputeBudgetProgram.programId);
}
// Create new instructions array with compute budget instructions
const newInstructions = [
{
programIdIndex: computeBudgetProgramIndex,
accountKeyIndexes: [],
data: computeBudgetIx.data
},
{
programIdIndex: computeBudgetProgramIndex,
accountKeyIndexes: [],
data: computePriceIx.data
},
...existingInstructions
];
// Create new versioned message with proper instruction mapping
const newMessage = new TransactionMessage({
payerKey: existingAccountKeys[0],
recentBlockhash: gasData.blockhash,
instructions: newInstructions.map(ix => ({
programId: existingAccountKeys[ix.programIdIndex],
keys: ix.accountKeyIndexes.map(idx => ({
pubkey: existingAccountKeys[idx],
isSigner: false,
isWritable: false
})).filter(key => key.pubkey),
data: Buffer.from(ix.data)
})).filter(ix => ix.programId)
}).compileToV0Message();
// Create and return new transaction
const preparedTransaction = new VersionedTransaction(newMessage);
return { transaction: preparedTransaction, gasData };
} catch (error) {
console.error('Error preparing transaction:', error);
throw error;
}
}
```
5.3 Broadcasting Transactions
Using Onchain Gateway API
For developers with access to the Onchain Gateway API, you can broadcast transactions directly through OKX's infrastructure. This method provides enhanced reliability and monitoring capabilities for high-volume trading operations.
The Broadcast API is available to our whitelisted customers only. Please reach out to dexapi@okx.com to request access.
```typescript
async function broadcastTransaction(
signedTx: solanaWeb3.Transaction | solanaWeb3.VersionedTransaction
) {
try {
const serializedTx = signedTx.serialize();
const encodedTx = base58.encode(serializedTx);
const path = "dex/pre-transaction/broadcast-transaction";
const url = `https://web3.okx.com/api/v6/${path}`;
const broadcastData = {
signedTx: encodedTx,
chainIndex: SOLANA_CHAIN_ID,
address: userAddress
// See [MEV Section](#8-mev-protection) for MEV protection settings
};
// Prepare authentication with body included in signature
const bodyString = JSON.stringify(broadcastData);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, broadcastData, { headers });
if (response.data.code === '0') {
const orderId = response.data.data[0].orderId;
console.log(`Transaction broadcast successfully, Order ID: ${orderId}`);
return orderId;
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to broadcast transaction:', error);
throw error;
}
}
```
Using Standard RPC
For developers who prefer using standard blockchain RPC methods or do not have yet requested API whitelisting, you can broadcast transactions directly to the network using Web3 RPC calls.
```typescript
async function signAndBroadcastTransaction(
tx: solanaWeb3.Transaction | solanaWeb3.VersionedTransaction,
connection: Connection
) {
if (!userPrivateKey) {
throw new Error("Private key not found");
}
const feePayer = solanaWeb3.Keypair.fromSecretKey(
base58.decode(userPrivateKey)
);
// Sign the transaction
if (tx instanceof solanaWeb3.VersionedTransaction) {
tx.sign([feePayer]);
} else {
tx.partialSign(feePayer);
}
// Send the transaction with retry logic
const maxRetries = 3;
let attempt = 0;
while (attempt < maxRetries) {
try {
const txId = await connection.sendRawTransaction(tx.serialize(), {
skipPreflight: false,
preflightCommitment: 'processed',
maxRetries: 0 // Handle retries manually
});
console.log(`Transaction sent: ${txId}`);
// Wait for confirmation with timeout
const confirmation = await connection.confirmTransaction({
signature: txId,
blockhash: tx instanceof solanaWeb3.VersionedTransaction
? tx.message.recentBlockhash
: tx.recentBlockhash!,
lastValidBlockHeight: tx instanceof solanaWeb3.VersionedTransaction
? undefined
: tx.lastValidBlockHeight!
}, 'confirmed');
if (confirmation.value.err) {
throw new Error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`);
}
console.log(`Transaction confirmed: https://solscan.io/tx/${txId}`);
return txId;
} catch (error) {
attempt++;
console.warn(`Attempt ${attempt} failed:`, error);
if (attempt >= maxRetries) {
throw new Error(`Transaction failed after ${maxRetries} attempts: ${error}`);
}
// Wait before retry
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
}
```
### Swap Transaction Using Compute Unit Data
Here's a complete example that demonstrates the full flow from getting swap data to preparing transactions with proper compute unit estimation:
```typescript
import { getHeaders } from '../../shared';
import {
Connection,
VersionedTransaction,
ComputeBudgetProgram,
TransactionMessage
} from "@solana/web3.js";
import base58 from 'bs58';
import dotenv from 'dotenv';
dotenv.config();
// Simple connection to one RPC endpoint
const connection = new Connection(
process.env.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com"
);
async function getQuote(params: any) {
const timestamp = new Date().toISOString();
const requestPath = "/api/v6/dex/aggregator/swap";
const queryString = "?" + new URLSearchParams({
...params,
}).toString();
const headers = getHeaders(timestamp, "GET", requestPath, queryString);
const response = await fetch(`https://web3.okx.com${requestPath}${queryString}`, {
method: "GET",
headers
});
const data = await response.json();
return data;
}
/**
* * Get compute units using Onchain Gateway API (API registration and whitelist required)
*/
async function getComputeUnitsFromAPI(
fromAddress: string,
inputData: string
): Promise {
try {
const path = 'dex/pre-transaction/gas-limit';
const url = `https://web3.okx.com/api/v6/${path}`;
const body = {
chainIndex: "501", // Solana chain ID
fromAddress: fromAddress,
toAddress: "", // Can be empty for Solana
txAmount: "0",
extJson: {
inputData: inputData
}
};
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await fetch(url, {
method: 'POST',
headers,
body: bodyString
});
const data = await response.json();
if (data.code === '0') {
const computeUnits = parseInt(data.data[0].gasLimit);
console.log(`API estimated compute units: ${computeUnits}`);
return computeUnits;
} else {
throw new Error(`API Error: ${data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get compute units from API, falling back to simulation:', error);
// Fallback to RPC simulation
return 300000;
}
}
/**
* Execute a complete Solana transaction with proper compute unit estimation
*/
async function executeTransaction(): Promise<{
quote: any;
gasData: {
estimatedComputeUnits: number;
priorityFee: number;
blockhash: string;
};
preparedTransaction: string;
}> {
try {
console.log('Getting Solana swap data...');
// Step 1: Get swap data from OKX DEX API
const quote = await getQuote({
chainIndex: '501', // Solana chain ID
amount: '10000000', // 0.01 SOL in lamports
fromTokenAddress: '11111111111111111111111111111111', // SOL
toTokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
userWalletAddress: "YOUR_WALLET_ADDRESS",
slippagePercent: '0.5',
autoSlippage: "true",
maxAutoSlippagePercent: "0.5"
});
console.log('Quote response:', JSON.stringify(quote, null, 2));
// Step 2: Process transaction data if available
if (quote.data && quote.data[0] && quote.data[0].tx && quote.data[0].tx.data) {
console.log('\nGetting gas data for transaction...');
// Step 3: Create transaction from the data
const decodedTransaction = base58.decode(quote.data[0].tx.data);
const transaction = VersionedTransaction.deserialize(decodedTransaction);
// Step 4: Get compute units using API (API whitelist required) or fallback to RPC
const userWalletAddress = "YOUR_WALLET_ADDRESS";
let estimatedComputeUnits: number;
try {
// Try API first (API whitelist required)
estimatedComputeUnits = await getComputeUnitsFromAPI(
userWalletAddress,
quote.data[0].tx.data
);
console.log('Using API estimate for compute units');
} catch (error) {
// Fallback to RPC simulation
estimatedComputeUnits = await getComputeUnits(transaction);
console.log('Using RPC simulation for compute units');
}
// Step 5: Prepare transaction with compute units
const { transaction: preparedTransaction, gasData } = await prepareTransactionWithComputeUnits(transaction);
// Override with API estimate if we got one
gasData.estimatedComputeUnits = estimatedComputeUnits;
console.log('\nGas Data Summary:');
console.log('Blockhash:', gasData.blockhash);
console.log('Estimated Compute Units:', gasData.estimatedComputeUnits);
console.log('Priority Fee:', gasData.priorityFee, 'microLamports');
console.log('Transaction prepared successfully');
// Return the complete result
const result = {
quote: quote.data[0],
gasData,
preparedTransaction: Buffer.from(preparedTransaction.serialize()).toString('base64')
};
console.log('\nFinal Result:', JSON.stringify(result, null, 2));
return result;
} else {
throw new Error('No transaction data received from swap API');
}
} catch (error) {
console.error("Error executing transaction:", error);
throw error;
}
}
// Example usage
async function main() {
try {
await executeTransaction();
} catch (error) {
console.error('Failed to prepare transaction:', error);
process.exit(1);
}
}
// Run if this file is executed directly
if (require.main === module) {
main();
}
export { executeTransaction, prepareTransactionWithComputeUnits, getComputeUnits, getComputeUnitsFromAPI };
```
## 6. Track Transaction
Finally, create a transaction tracking system, choose the first (section 6.1) when you need detailed information about the swap execution itself, or the second (section 6.2) for complete swap insight with token-level details.
6.1 With the Onchain gateway API
The Onchain gateway API provides transaction tracking capabilities through the `/dex/post-transaction/orders` endpoint. Use the order ID returned by the broadcast API to track transactions as they progress through OKX's systems with simple status codes (1: Pending, 2: Success, 3: Failed).
```typescript
// Define transaction status interface
interface TxErrorInfo {
error: string;
message: string;
action: string;
}
/**
* Tracking transaction confirmation status using the Onchain gateway API
* @param orderId - Order ID from broadcast response
* @param intervalMs - Polling interval in milliseconds
* @param timeoutMs - Maximum time to wait
* @returns Final transaction confirmation status
*/
async function trackTransaction(
orderId: string,
intervalMs: number = 5000,
timeoutMs: number = 300000
): Promise {
console.log(`Tracking transaction with Order ID: ${orderId}`);
const startTime = Date.now();
let lastStatus = '';
while (Date.now() - startTime < timeoutMs) {
// Get transaction status
try {
const path = 'dex/post-transaction/orders';
const url = `https://web3.okx.com/api/v6/${path}`;
const params = {
orderId: orderId,
chainIndex: SOLANA_CHAIN_ID,
address: userAddress,
limit: '1'
};
// Prepare authentication
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await axios.get(url, { params, headers });
if (response.data.code === '0' && response.data.data && response.data.data.length > 0) {
if (response.data.data[0].orders && response.data.data[0].orders.length > 0) {
const txData = response.data.data[0].orders[0];
// Use txStatus to match the API response
const status = txData.txStatus;
// Only log when status changes
if (status !== lastStatus) {
lastStatus = status;
if (status === '1') {
console.log(`Transaction pending: ${txData.txHash || 'Hash not available yet'}`);
} else if (status === '2') {
console.log(`Transaction successful: https://solscan.io/tx/${txData.txHash}`);
return txData;
} else if (status === '3') {
const failReason = txData.failReason || 'Unknown reason';
const errorMessage = `Transaction failed: ${failReason}`;
console.error(errorMessage);
const errorInfo = handleTransactionError(txData);
console.log(`Error type: ${errorInfo.error}`);
console.log(`Suggested action: ${errorInfo.action}`);
throw new Error(errorMessage);
}
}
} else {
console.log(`No orders found for Order ID: ${orderId}`);
}
}
} catch (error) {
console.warn('Error checking transaction status:', (error instanceof Error ? error.message : "Unknown error"));
}
// Wait before next check
await new Promise(resolve => setTimeout(resolve, intervalMs));
}
throw new Error('Transaction tracking timed out');
}
/**
* Comprehensive error handling with failReason
* @param txData - Transaction data from post-transaction/orders
* @returns Structured error information
*/
function handleTransactionError(txData: any): TxErrorInfo {
const failReason = txData.failReason || 'Unknown reason';
// Log the detailed error
console.error(`Transaction failed with reason: ${failReason}`);
// Default error info
let errorInfo: TxErrorInfo = {
error: 'TRANSACTION_FAILED',
message: failReason,
action: 'Try again or contact support'
};
// More specific error handling based on the failure reason
if (failReason.includes('insufficient funds')) {
errorInfo = {
error: 'INSUFFICIENT_FUNDS',
message: 'Your wallet does not have enough funds to complete this transaction',
action: 'Add more SOL to your wallet to cover the transaction'
};
} else if (failReason.includes('blockhash')) {
errorInfo = {
error: 'BLOCKHASH_EXPIRED',
message: 'The transaction blockhash has expired',
action: 'Try again with a fresh transaction'
};
} else if (failReason.includes('compute budget')) {
errorInfo = {
error: 'COMPUTE_BUDGET_EXCEEDED',
message: 'Transaction exceeded compute budget',
action: 'Increase compute units or simplify the transaction'
};
}
return errorInfo;
}
```
6.2 For more detailed swap-specific information, you can use the SWAP API:
SWAP API transaction tracking provides comprehensive swap execution details using the `/dex/aggregator/history` endpoint. It offers token-specific information (symbols, amounts), fees paid, and detailed blockchain data. Use this when you need complete swap insight with token-level details.
```typescript
/**
* Track transaction using SWAP API
* @param chainIndex - Chain ID (e.g., 501 for Solana)
* @param txHash - Transaction hash
* @returns Transaction details
*/
async function trackTransactionWithSwapAPI(
txHash: string
): Promise {
try {
const path = 'dex/aggregator/history';
const url = `https://web3.okx.com/api/v6/${path}`;
const params = {
chainIndex: SOLANA_CHAIN_ID,
txHash: txHash,
isFromMyProject: 'true'
};
// Prepare authentication
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await axios.get(url, { params, headers });
if (response.data.code === '0') {
const txData = response.data.data[0];
const status = txData.status;
if (status === 'pending') {
console.log(`Transaction is still pending: ${txHash}`);
return { status: 'pending', details: txData };
} else if (status === 'success') {
console.log(`Transaction successful!`);
console.log(`From: ${txData.fromTokenDetails.symbol} - Amount: ${txData.fromTokenDetails.amount}`);
console.log(`To: ${txData.toTokenDetails.symbol} - Amount: ${txData.toTokenDetails.amount}`);
console.log(`Transaction Fee: ${txData.txFee}`);
console.log(`Explorer URL: https://solscan.io/tx/${txHash}`);
return { status: 'success', details: txData };
} else if (status === 'failure') {
console.error(`Transaction failed: ${txData.errorMsg || 'Unknown reason'}`);
return { status: 'failure', details: txData };
}
return txData;
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to track transaction status:', (error instanceof Error ? error.message : "Unknown error"));
throw error;
}
}
```
## 7. Complete Implementation
Here's a complete implementation example:
```typescript
import { getHeaders } from '../../shared';
import { Connection, PublicKey, Transaction, Keypair, VersionedTransaction, SystemProgram } from '@solana/web3.js';
import * as axios from 'axios';
import bs58 from 'bs58';
// // Utility function for OKX API authentication
// function getHeaders(timestamp: string, method: string, requestPath: string, queryString = "", body = "") {
// const stringToSign = timestamp + method + requestPath + (queryString || body);
// return {
// "Content-Type": "application/json",
// "OK-ACCESS-KEY": apiKey,
// "OK-ACCESS-SIGN": cryptoJS.enc.Base64.stringify(
// cryptoJS.HmacSHA256(stringToSign, secretKey)
// ),
// "OK-ACCESS-TIMESTAMP": timestamp,
// "OK-ACCESS-PASSPHRASE": apiPassphrase,
// };
// Environment variables
const WALLET_ADDRESS = process.env.SOLANA_WALLET_ADDRESS;
const PRIVATE_KEY = process.env.SOLANA_PRIVATE_KEY;
const chainIndex = '501'; // Solana Mainnet
const rpcUrl = process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com';
// Constants
const SOL_ADDRESS = '11111111111111111111111111111111'; // Native SOL
const USDC_ADDRESS = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'; // USDC
// Initialize Solana connection
const connection = new Connection(rpcUrl, 'confirmed');
// Type definitions
interface GasLimitApiResponse {
code: string;
msg?: string;
data: Array<{
gasLimit: string;
}>;
}
interface SimulationApiResponse {
code: string;
msg?: string;
data: Array<{
intention: string;
gasUsed?: string;
failReason?: string;
assetChange?: Array<{
assetType: string;
name: string;
symbol: string;
decimals: number;
address: string;
imageUrl: string;
rawValue: string;
}>;
risks?: Array;
}>;
}
interface BroadcastApiResponse {
code: string;
msg?: string;
data: Array<{
orderId: string;
}>;
}
interface TxErrorInfo {
error: string;
message: string;
action: string;
}
// ============================================================================
// API Functions
// ============================================================================
/**
* Get gas limit from Onchain Gateway API
*/
async function getGasLimit(
fromAddress: string,
toAddress: string,
txAmount: string = '0',
inputData: string = ''
): Promise {
try {
console.log('Getting gas limit from Onchain Gateway API...');
const path = 'dex/pre-transaction/gas-limit';
const url = `https://web3.okx.com/api/v6/${path}`;
const body = {
chainIndex: chainIndex,
fromAddress: fromAddress,
toAddress: toAddress,
txAmount: txAmount,
extJson: {
inputData: inputData
}
};
// Prepare authentication with body included in signature
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
if (response.data.code === '0') {
const gasLimit = response.data.data[0].gasLimit;
console.log(`Gas Limit obtained: ${gasLimit}`);
return gasLimit;
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get gas limit:', (error as Error).message);
throw error;
}
}
/**
* Get swap data from OKX API
*/
async function getSwapData(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
slippagePercent = '0.5'
) {
try {
console.log('Getting swap data from OKX API...');
const timestamp = new Date().toISOString();
const requestPath = "/api/v6/dex/aggregator/swap";
const queryString = "?" + new URLSearchParams({
chainIndex: chainIndex,
fromTokenAddress,
toTokenAddress,
amount,
slippagePercent,
userWalletAddress: WALLET_ADDRESS!,
autoSlippage: "false",
maxAutoSlippagePercent: "0.5"
}).toString();
const headers = getHeaders(timestamp, "GET", requestPath, queryString);
const response = await fetch(`https://web3.okx.com${requestPath}${queryString}`, {
method: "GET",
headers
});
if (!response.ok) {
throw new Error(`Failed to get swap data: ${response.status} ${await response.text()}`);
}
const data = await response.json();
console.log('Swap data obtained');
return data.data[0]; // Return only the first swap data object
} catch (error) {
console.error('Failed to get swap data:', (error as Error).message);
throw error;
}
}
/**
* Simulate transaction using Onchain Gateway API
*/
async function simulateTransaction(swapData: any) {
try {
console.log('Simulating transaction with Onchain Gateway API...');
const path = 'dex/pre-transaction/simulate';
const url = `https://web3.okx.com/api/v6/${path}`;
const body = {
chainIndex: chainIndex,
fromAddress: swapData.tx.from,
toAddress: swapData.tx.to,
txAmount: swapData.tx.value,
extJson: {
inputData: swapData.tx.data
}
};
// Prepare authentication with body included in signature
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
if (response.data.code === '0') {
const simulationData = response.data.data[0];
if (simulationData.failReason) {
throw new Error(`Simulation failed: ${simulationData.failReason}`);
}
console.log(`Transaction simulation successful. Gas used: ${simulationData.gasUsed}`);
console.log('Simulation API Response:', simulationData);
return simulationData;
} else {
throw new Error(`Simulation API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Transaction simulation failed:', (error as Error).message);
throw error;
}
}
/**
* Broadcast transaction using Onchain Gateway API with RPC fallback
*/
async function broadcastTransaction(
signedTx: string,
chainIndex: string,
walletAddress: string
): Promise {
try {
console.log('Broadcasting transaction via Onchain Gateway API...');
const path = 'dex/pre-transaction/broadcast-transaction';
const url = `https://web3.okx.com/api/v6/${path}`;
const body = {
signedTx: signedTx,
chainIndex: chainIndex,
address: walletAddress
};
console.log('Broadcast request body:', JSON.stringify(body, null, 2));
// Prepare authentication with body included in signature
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
if (response.data.code === '0') {
const orderId = response.data.data[0].orderId;
console.log(`Transaction broadcast successful. Order ID: ${orderId}`);
return orderId;
} else {
throw new Error(`Broadcast API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('OKX API broadcast failed:', (error as Error).message);
// Fallback to direct RPC broadcast
try {
console.log('Attempting direct RPC broadcast as fallback...');
// Decode the signed transaction
const txBytes = bs58.decode(signedTx);
// Send directly to Solana RPC
const signature = await connection.sendRawTransaction(txBytes, {
skipPreflight: false,
preflightCommitment: 'processed'
});
console.log(`Direct RPC broadcast successful. Signature: ${signature}`);
// Wait for confirmation
const confirmation = await connection.confirmTransaction(signature, 'confirmed');
if (confirmation.value.err) {
throw new Error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`);
}
console.log(`Transaction confirmed: https://solscan.io/tx/${signature}`);
return signature;
} catch (rpcError) {
console.error('RPC broadcast also failed:', (rpcError as Error).message);
throw new Error(`Both OKX API and RPC broadcast failed. OKX Error: ${(error as Error).message}, RPC Error: ${(rpcError as Error).message}`);
}
}
}
/**
* Track transaction status using Onchain Gateway API
*/
async function trackTransaction(
orderId: string,
intervalMs: number = 5000,
timeoutMs: number = 180000 // Reduced timeout to 3 minutes
): Promise {
console.log(`Tracking transaction with Order ID: ${orderId}`);
const startTime = Date.now();
let lastStatus = '';
let pendingCount = 0;
while (Date.now() - startTime < timeoutMs) {
try {
const path = 'dex/post-transaction/orders';
const url = `https://web3.okx.com/api/v6/${path}`;
const params = {
orderId: orderId,
chainIndex: chainIndex,
address: WALLET_ADDRESS!,
limit: '1'
};
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await axios.get(url, { params, headers });
const responseData = response.data as any;
if (responseData.code === '0' && responseData.data && responseData.data.length > 0) {
if (responseData.data[0].orders && responseData.data[0].orders.length > 0) {
const txData = responseData.data[0].orders[0];
const status = txData.txStatus;
if (status !== lastStatus) {
lastStatus = status;
if (status === '1') {
pendingCount++;
console.log(`Transaction pending (${pendingCount}): ${txData.txHash || 'Hash not available yet'}`);
// If pending too long without a hash, something is wrong
if (pendingCount > 12 && !txData.txHash) { // 1 minute of pending without hash
console.warn('Transaction has been pending for too long without a transaction hash. This may indicate an issue.');
}
} else if (status === '2') {
console.log(`Transaction successful: https://web3.okx.com/explorer/solana/tx/${txData.txHash}`);
return txData;
} else if (status === '3') {
const failReason = txData.failReason || 'Unknown reason';
const errorMessage = `Transaction failed: ${failReason}`;
console.error(errorMessage);
const errorInfo = handleTransactionError(txData);
console.log(`Error type: ${errorInfo.error}`);
console.log(`Suggested action: ${errorInfo.action}`);
throw new Error(errorMessage);
}
} else if (status === '1') {
pendingCount++;
// Show progress for long pending transactions
if (pendingCount % 6 === 0) { // Every 30 seconds
const elapsed = Math.round((Date.now() - startTime) / 1000);
console.log(`Still pending... (${elapsed}s elapsed)`);
}
}
} else {
console.log(`No orders found for Order ID: ${orderId}`);
}
} else {
console.log('No response data from tracking API');
}
} catch (error) {
console.warn('Error checking transaction status:', (error as Error).message);
}
await new Promise(resolve => setTimeout(resolve, intervalMs));
}
throw new Error(`Transaction tracking timed out after ${timeoutMs/1000} seconds. The transaction may still be processing.`);
}
// ============================================================================
// Transaction Signing Functions
// ============================================================================
/**
* Sign transaction with private key - Fixed OKX approach with gas limit analysis
*/
async function signTransaction(swapData: any, gasLimit: string): Promise {
try {
console.log('Signing transaction...');
if (!PRIVATE_KEY) {
throw new Error('Private key not found in environment variables');
}
// Create keypair from private key
const privateKeyBytes = bs58.decode(PRIVATE_KEY);
const keypair = Keypair.fromSecretKey(privateKeyBytes);
if (!swapData.tx || !swapData.tx.data) {
throw new Error('No transaction data found in swap response');
}
const callData = swapData.tx.data;
console.log('Transaction data length:', callData.length);
console.log('Gas limit from API:', gasLimit);
try {
// Decode the base58 encoded transaction data (this is the correct approach)
const decodedTransaction = bs58.decode(callData);
console.log('Decoded transaction bytes length:', decodedTransaction.length);
// Get the latest blockhash (CRITICAL!)
const recentBlockHash = await connection.getLatestBlockhash();
console.log('Got recent blockhash:', recentBlockHash.blockhash);
let transaction: Transaction | VersionedTransaction;
// Try VersionedTransaction first (more common for modern Solana programs)
try {
transaction = VersionedTransaction.deserialize(decodedTransaction);
console.log('Successfully deserialized as VersionedTransaction');
// DEBUGGING: Let's see what instructions are already in the transaction
console.log('Number of instructions in OKX transaction:', transaction.message.compiledInstructions.length);
// Check if there are already ComputeBudget instructions
const computeBudgetProgram = new PublicKey('ComputeBudget111111111111111111111111111111');
const computeBudgetIndex = transaction.message.staticAccountKeys.findIndex(
key => key.equals(computeBudgetProgram)
);
if (computeBudgetIndex !== -1) {
console.log('ComputeBudget program found at index:', computeBudgetIndex);
// Check which instructions use the ComputeBudget program
const computeBudgetInstructions = transaction.message.compiledInstructions.filter(
ix => ix.programIdIndex === computeBudgetIndex
);
console.log('Number of ComputeBudget instructions:', computeBudgetInstructions.length);
// Analyze each ComputeBudget instruction
computeBudgetInstructions.forEach((ix, i) => {
const data = ix.data;
if (data.length > 0) {
const instructionType = data[0];
console.log(`ComputeBudget instruction ${i}: type ${instructionType}`);
if (instructionType === 0 && data.length >= 5) {
// SetComputeUnitLimit instruction
const computeUnits = new Uint32Array(data.slice(1, 5).buffer)[0];
console.log(` - Current compute unit limit: ${computeUnits}`);
console.log(` - Gas limit from API: ${gasLimit}`);
// Check if we need to update it
const apiGasLimit = parseInt(gasLimit);
if (computeUnits !== apiGasLimit) {
console.log(` - Compute units mismatch! OKX: ${computeUnits}, API: ${apiGasLimit}`);
// We could potentially update this here
}
} else if (instructionType === 1 && data.length >= 9) {
// SetComputeUnitPrice instruction
const microLamports = new BigUint64Array(data.slice(1, 9).buffer)[0];
console.log(` - Current compute unit price: ${microLamports} microlamports`);
}
}
});
} else {
console.log('No ComputeBudget program found - OKX transaction may not have compute budget instructions');
console.log('We should add ComputeBudget instruction with gas limit:', gasLimit);
// Add ComputeBudget instruction since OKX didn't include one
const setComputeUnitLimitData = Buffer.alloc(5);
setComputeUnitLimitData[0] = 0; // SetComputeUnitLimit instruction
setComputeUnitLimitData.writeUInt32LE(parseInt(gasLimit), 1);
// Add the ComputeBudget program to static accounts
transaction.message.staticAccountKeys.push(computeBudgetProgram);
const programIndex = transaction.message.staticAccountKeys.length - 1;
// Add the compute budget instruction at the beginning
transaction.message.compiledInstructions.unshift({
programIdIndex: programIndex,
accountKeyIndexes: [],
data: setComputeUnitLimitData
});
console.log('Added ComputeBudget instruction with gas limit:', gasLimit);
}
// CRITICAL: Update the blockhash in the transaction message
transaction.message.recentBlockhash = recentBlockHash.blockhash;
// Sign the versioned transaction
transaction.sign([keypair]);
console.log('Signed VersionedTransaction');
} catch (versionedError) {
console.log('VersionedTransaction failed, trying legacy Transaction');
try {
transaction = Transaction.from(decodedTransaction);
console.log('Successfully deserialized as legacy Transaction');
// DEBUGGING: Check legacy transaction instructions
console.log('Number of instructions in legacy transaction:', transaction.instructions.length);
// Check for ComputeBudget instructions in legacy format
const computeBudgetProgram = new PublicKey('ComputeBudget111111111111111111111111111111');
const computeBudgetInstructions = transaction.instructions.filter(
ix => ix.programId.equals(computeBudgetProgram)
);
if (computeBudgetInstructions.length === 0) {
console.log('No ComputeBudget instructions found in legacy transaction');
console.log('Adding ComputeBudget instruction with gas limit:', gasLimit);
// Add ComputeBudget instruction
const setComputeUnitLimitData = Buffer.alloc(5);
setComputeUnitLimitData[0] = 0; // SetComputeUnitLimit instruction
setComputeUnitLimitData.writeUInt32LE(parseInt(gasLimit), 1);
const computeBudgetIx = {
programId: computeBudgetProgram,
keys: [],
data: setComputeUnitLimitData
};
// Add at the beginning
transaction.instructions.unshift(computeBudgetIx);
console.log('Added ComputeBudget instruction to legacy transaction');
} else {
console.log('Found existing ComputeBudget instructions:', computeBudgetInstructions.length);
}
// CRITICAL: Update the blockhash in the transaction
transaction.recentBlockhash = recentBlockHash.blockhash;
// Sign the legacy transaction
transaction.sign(keypair);
console.log('Signed legacy Transaction');
} catch (legacyError) {
console.log('Both transaction types failed to deserialize');
console.log('VersionedTransaction error:', (versionedError as Error).message);
console.log('Legacy Transaction error:', (legacyError as Error).message);
// This should not happen with proper OKX data
throw new Error('Failed to deserialize OKX transaction data. Data may be corrupted.');
}
}
// Serialize and encode the signed transaction
const serializedTx = transaction.serialize();
const encodedTx = bs58.encode(serializedTx);
console.log('Transaction signed and encoded successfully');
return encodedTx;
} catch (error) {
console.log('Failed to process OKX transaction data:', (error as Error).message);
// If we reach here, the OKX data is not in expected format
throw new Error(`Cannot process OKX transaction data: ${(error as Error).message}`);
}
} catch (error) {
console.error('Failed to sign transaction:', (error as Error).message);
throw error;
}
}
// ============================================================================
// Error Handling
// ============================================================================
/**
* Comprehensive error handling with failReason
*/
function handleTransactionError(txData: any): TxErrorInfo {
const failReason = txData.failReason || 'Unknown reason';
console.error(`Transaction failed with reason: ${failReason}`);
return {
error: 'TRANSACTION_FAILED',
message: failReason,
action: 'Try again or contact support'
};
}
// ============================================================================
// Main Execution Functions
// ============================================================================
/**
* Execute swap with full transaction flow
*/
async function executeSwap(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
slippagePercent: string = '0.5'
): Promise {
try {
console.log('Starting swap execution...');
// Step 1: Get swap data
const swapData = await getSwapData(fromTokenAddress, toTokenAddress, amount, slippagePercent);
console.log('Swap data obtained');
// Step 2: Simulate transaction
const simulationResult = await simulateTransaction(swapData);
console.log('Transaction simulation completed');
console.log('Simulation result', simulationResult.intention);
// Step 3: Get gas limit
const gasLimit = await getGasLimit(
swapData.tx.from,
swapData.tx.to,
swapData.tx.value || '0',
swapData.tx.data
);
console.log('Gas limit obtained');
// Step 4: Check account balance
if (!(swapData.tx && swapData.tx.data)) {
throw new Error('No valid transaction data found in swap API response (tx.data missing)');
}
console.log('Checking account balance...');
const fromPubkey = new PublicKey(swapData.tx.from);
const balance = await connection.getBalance(fromPubkey);
console.log(`Account balance: ${balance / 1e9} SOL`);
// Check if we have enough balance for the transaction
const requiredAmount = parseInt(swapData.tx.value || '0');
console.log(`Required amount: ${requiredAmount / 1e9} SOL`);
if (balance < requiredAmount) {
throw new Error(`Insufficient balance. Required: ${requiredAmount / 1e9} SOL, Available: ${balance / 1e9} SOL`);
}
// Step 5: Sign the transaction with private key
console.log('Signing transaction with private key...');
const signedTx = await signTransaction(swapData, gasLimit);
console.log('Transaction signed successfully');
// Step 6: Broadcast transaction
console.log('Broadcasting signed transaction via Onchain Gateway API...');
const txHash = await broadcastTransaction(signedTx, chainIndex, WALLET_ADDRESS!);
console.log(`Transaction broadcast successful. Hash: ${txHash}`);
// Step 7: Track transaction
console.log('Tracking transaction status...');
const trackingResult = await trackTransaction(txHash);
console.log('Transaction tracking completed');
console.log('Tracking result', trackingResult);
return txHash;
} catch (error) {
console.error('Swap execution failed:', (error as Error).message);
throw error;
}
}
/**
* Execute swap with simulation and detailed logging
*/
async function executeSwapWithSimulation(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
slippagePercent: string = '0.5'
): Promise {
try {
console.log('Starting swap execution with simulation...');
const txHash = await executeSwap(fromTokenAddress, toTokenAddress, amount, slippagePercent);
console.log('Swap execution completed successfully!');
console.log(`Transaction Hash: ${txHash}`);
return { success: true, txHash };
} catch (error) {
console.error('Swap execution failed:', (error as Error).message);
return { success: false, error: (error as Error).message };
}
}
/**
* Simulation-only mode
*/
async function simulateOnly(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
slippagePercent: string = '0.5'
): Promise {
try {
console.log('Starting simulation-only mode...');
console.log(`Simulation Details:`);
console.log(` From Token: ${fromTokenAddress}`);
console.log(` To Token: ${toTokenAddress}`);
console.log(` Amount: ${amount}`);
console.log(` SlippagePercent: ${slippagePercent}%`);
// Step 1: Get swap data
const swapData = await getSwapData(fromTokenAddress, toTokenAddress, amount, slippagePercent);
console.log('Swap data obtained');
// Step 2: Simulate transaction
const simulationResult = await simulateTransaction(swapData);
console.log('Transaction simulation completed');
// Step 3: Get gas limit
const gasLimit = await getGasLimit(
swapData.tx.from,
swapData.tx.to,
swapData.tx.value || '0',
swapData.tx.data
);
console.log('Gas limit obtained');
return {
success: true,
swapData,
simulationResult,
gasLimit,
estimatedGasUsed: simulationResult.gasUsed,
};
} catch (error) {
console.error('Simulation failed:', (error as Error).message);
return { success: false, error: (error as Error).message };
}
}
// ============================================================================
// Main Entry Point
// ============================================================================
async function main() {
try {
console.log('Solana Swap Tools with Onchain Gateway API');
console.log('=====================================');
// Validate environment variables
if (!WALLET_ADDRESS || !PRIVATE_KEY) {
throw new Error('Missing wallet address or private key in environment variables');
}
console.log(`Wallet Address: ${WALLET_ADDRESS}`);
console.log(`Chain ID: ${chainIndex}`);
console.log(`RPC URL: ${rpcUrl}`);
// Parse command line arguments
const args = process.argv.slice(2);
const mode = args[0] || 'simulate'; // Default to simulate mode
// Example parameters
const fromToken = SOL_ADDRESS;
const toToken = USDC_ADDRESS;
const amount = '10000000'; // 0.01 SOL in lamports
const slippagePercent = '0.5'; // 0.5%
console.log('\nConfiguration:');
console.log(` From: ${fromToken} (SOL)`);
console.log(` To: ${toToken} (USDC)`);
console.log(` Amount: ${parseInt(amount) / 1e9} SOL`);
console.log(` SlippagePercent: ${slippagePercent}%`);
console.log(` Mode: ${mode}`);
let result;
switch (mode.toLowerCase()) {
case 'simulate':
case 'sim':
result = await simulateOnly(fromToken, toToken, amount, slippagePercent);
break;
case 'execute':
case 'exec':
result = await executeSwapWithSimulation(fromToken, toToken, amount, slippagePercent);
break;
default:
console.log('\nAvailable modes:');
console.log(' simulate/sim - Only simulate the transaction');
console.log(' execute/exec - Execute the full swap');
console.log('\nExample: npm run solana-swap simulate');
return;
}
if (result.success) {
console.log('\nOperation completed successfully!');
if (mode === 'simulate' || mode === 'sim') {
console.log(`Gas Limit: ${result.gasLimit}`);
} else {
console.log(`Transaction Hash: ${result.txHash}`);
}
} else {
console.log('\nOperation failed!');
console.log(`Error: ${result.error}`);
}
} catch (error) {
console.error('Main execution failed:', (error as Error).message);
process.exit(1);
}
}
// Run the script
if (require.main === module) {
main();
}
// ============================================================================
// Exports
// ============================================================================
export {
executeSwap,
executeSwapWithSimulation,
simulateOnly,
getSwapData,
simulateTransaction,
getGasLimit,
broadcastTransaction,
trackTransaction,
signTransaction
};
```
You can run this script using `solana-swap-executor.ts sim` or `solana-swap-executor.ts exec`.
`sim` simulates a transaction using swap data using the transaction simulation API and retruns `gasLimit` info
`exec` executes a transaction using the broadcast API
## 8. MEV Protection
### MEV Protection with Broadcast Transaction API
The OKX Broadcast Transaction API provides built-in MEV protection capabilities to help safeguard your transactions from front-running and sandwich attacks. The Broadcast API is available to our whitelisted customers only. Please reach out to dexapi@okx.com to request access.
**Disclaimer:** Your end-user's transaction can only be covered by the MEV protection feature if you actually utilise OKX Build's API services for that particular transaction. MEV protection is currently an experimental feature provided by third-parties and OKX Build does not guarantee the effectiveness and quality of such MEV protection.
#### Basic MEV Protection
To enable MEV protection, add the `extraData` field to your broadcast transaction request with `enableMevProtection: true`:
```typescript
/**
* Broadcast transaction with MEV protection enabled
*/
async function broadcastTransactionWithMEV(
signedTx: string,
chainIndex: string = "501",
walletAddress: string,
enableMevProtection: boolean = true
): Promise {
try {
console.log('Broadcasting transaction with MEV protection...');
const path = 'dex/pre-transaction/broadcast-transaction';
const url = `https://web3.okx.com/api/v6/${path}`;
const body = {
signedTx: signedTx,
chainIndex: chainIndex,
address: walletAddress,
extraData: JSON.stringify({
enableMevProtection: enableMevProtection
})
};
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
if (response.data.code === '0') {
const orderId = response.data.data[0].orderId;
console.log(`Transaction broadcast with MEV protection. Order ID: ${orderId}`);
return orderId;
} else {
throw new Error(`Broadcast API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('MEV-protected broadcast failed:', error);
throw error;
}
}
```
#### Jito Integration for Enhanced Protection
For additional MEV protection on Solana, you can include Jito-specific parameters:
```typescript
/**
* Broadcast transaction with Jito MEV protection
*/
async function broadcastTransactionWithJito(
signedTx: string,
jitoSignedTx: string,
chainIndex: string = "501",
walletAddress: string
): Promise {
try {
console.log('Broadcasting transaction with Jito MEV protection...');
const path = 'dex/pre-transaction/broadcast-transaction';
const url = `https://web3.okx.com/api/v6/${path}`;
const body = {
signedTx: signedTx,
chainIndex: chainIndex,
address: walletAddress,
extraData: JSON.stringify({
enableMevProtection: true,
jitoSignedTx: jitoSignedTx
})
};
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
if (response.data.code === '0') {
const orderId = response.data.data[0].orderId;
console.log(`Transaction broadcast with Jito protection. Order ID: ${orderId}`);
return orderId;
} else {
throw new Error(`Broadcast API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Jito-protected broadcast failed:', error);
throw error;
}
}
```
#### Updated Broadcast Function with MEV Parameters
Here's the updated `broadcastTransaction` function that includes MEV protection parameters:
```typescript
/**
* Enhanced broadcast transaction with MEV protection parameters
*/
async function broadcastTransaction(
signedTx: string,
chainIndex: string = "501",
walletAddress: string,
enableMevProtection: boolean = false,
jitoSignedTx: string = ""
): Promise {
try {
console.log(`Broadcasting transaction${enableMevProtection ? ' with MEV protection' : ''}...`);
const path = 'dex/pre-transaction/broadcast-transaction';
const url = `https://web3.okx.com/api/v6/${path}`;
const body: any = {
signedTx: signedTx,
chainIndex: chainIndex,
address: walletAddress
};
// Add MEV protection parameters if enabled
if (enableMevProtection || jitoSignedTx) {
const extraData: any = {};
if (enableMevProtection) {
extraData.enableMevProtection = true;
}
if (jitoSignedTx) {
extraData.jitoSignedTx = jitoSignedTx;
}
body.extraData = JSON.stringify(extraData);
}
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
if (response.data.code === '0') {
const orderId = response.data.data[0].orderId;
console.log(`Transaction broadcast successful. Order ID: ${orderId}`);
return orderId;
} else {
throw new Error(`Broadcast API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Broadcast failed:', error);
throw error;
}
}
```
#### Usage Examples
**Basic swap without MEV protection:**
```typescript
// Standard broadcast (no MEV protection)
const orderId = await broadcastTransaction(signedTx, "501", walletAddress);
```
**Swap with MEV protection enabled:**
```typescript
// With MEV protection
const orderId = await broadcastTransaction(signedTx, "501", walletAddress, true);
```
**Swap with Jito MEV protection:**
```typescript
// With Jito protection
const orderId = await broadcastTransaction(signedTx, "501", walletAddress, true, jitoSignedTransaction);
```
**Swap with only Jito (no general MEV protection):**
```typescript
// Only Jito protection
const orderId = await broadcastTransaction(signedTx, "501", walletAddress, false, jitoSignedTransaction);
```
#### Integration with Complete Swap Flow
Here's how to integrate MEV protection into your complete swap execution:
```typescript
/**
* Execute swap with MEV protection
*/
async function executeSwapWithMEVProtection(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
slippagePercent: string = '0.5',
enableMevProtection: boolean = true
): Promise {
try {
// Step 1: Get swap data
const swapData = await getSwapData(fromTokenAddress, toTokenAddress, amount, slippagePercent);
// Step 2: Prepare and sign transaction
const { transaction } = await prepareTransaction(swapData.tx.data);
const signedTx = await signTransaction(transaction);
// Step 3: Broadcast with MEV protection
const orderId = await broadcastTransaction(signedTx, "501", userAddress, enableMevProtection);
// Step 4: Track transaction
const result = await trackTransaction(orderId);
return result.txHash;
} catch (error) {
console.error("MEV-protected swap failed:", error);
throw error;
}
}
```
The MEV protection feature integrates seamlessly with your existing EVM and Solana swap implementation and provides an additional layer of security against MEV attacks across Solana, Base, Ethereum, and BSC.
## Method 2: SDK approach
Using the OKX DEX SDK provides a much simpler developer experience while retaining all the functionality of the API-first approach. The SDK handles many implementation details for you, including retry logic, error handling, and transaction management.
## 1. Install the SDK
```typescript
npm install @okx-dex/okx-dex-sdk
# or
yarn add @okx-dex/okx-dex-sdk
# or
pnpm add @okx-dex/okx-dex-sdk
```
## 2. Setup Your Environment
Create a .env file with your API credentials and wallet information:
```typescript
# OKX API Credentials
OKX_API_KEY=your_api_key
OKX_SECRET_KEY=your_secret_key
OKX_API_PASSPHRASE=your_passphrase
# Solana Configuration
SOLANA_RPC_URL=your_solana_rpc_url
SOLANA_WALLET_ADDRESS=your_solana_wallet_address
SOLANA_PRIVATE_KEY=your_solana_private_key
# Ethereum Configuration
EVM_RPC_URL=your_evm_rpc_url
EVM_WALLET_ADDRESS=your_evm_wallet_address
EVM_PRIVATE_KEY=your_evm_private_key
```
## 3. Initialize the Client
Create a file for your DEX client (e.g., DexClient.ts):
```typescript
import { OKXDexClient } from '@okx-dex/okx-dex-sdk';
import { createEVMWallet } from '@okx-dex/okx-dex-sdk/core/evm-wallet';
import { createWallet } from '@okx-dex/okx-dex-sdk/core/wallet';
import { Connection } from '@solana/web3.js';
import { ethers } from 'ethers';
import dotenv from 'dotenv';
dotenv.config();
// EVM setup (Ethereum, Base, Arbitrum, etc.)
const evmProvider = new ethers.JsonRpcProvider(process.env.EVM_RPC_URL!);
const evmWallet = createEVMWallet(process.env.EVM_PRIVATE_KEY!, evmProvider);
// Solana setup
const solanaConnection = new Connection(process.env.SOLANA_RPC_URL!);
const solanaWallet = createWallet(process.env.SOLANA_PRIVATE_KEY!, solanaConnection);
// Initialize the client
const client = new OKXDexClient({
// API credentials (get from OKX Developer Portal)
apiKey: process.env.OKX_API_KEY!,
secretKey: process.env.OKX_SECRET_KEY!,
apiPassphrase: process.env.OKX_API_PASSPHRASE!,
// EVM configuration (works for all EVM chains)
evm: {
wallet: evmWallet
},
// Solana configuration
solana: {
wallet: solanaWallet,
computeUnits: 300000, // Optional
maxRetries: 3 // Optional
},
})
```
## 4. Execute a Swap With the SDK
Create a swap execution file:
```typescript
// swap.ts
import { client } from './DexClient';
/**
* Example: Execute a swap from SOL to USDC
*/
async function executeSwap() {
try {
if (!process.env.SOLANA_PRIVATE_KEY) {
throw new Error('Missing SOLANA_PRIVATE_KEY in .env file');
}
// Get quote to fetch token information
console.log("Getting token information...");
const quote = await client.dex.getQuote({
chainIndex: '501',
fromTokenAddress: '11111111111111111111111111111111', // SOL
toTokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
amount: '1000000', // Small amount for quote
slippagePercent: '0.5' // 0.5% slippagePercent
});
const tokenInfo = {
fromToken: {
symbol: quote.data[0].fromToken.tokenSymbol,
decimals: parseInt(quote.data[0].fromToken.decimal),
price: quote.data[0].fromToken.tokenUnitPrice
},
toToken: {
symbol: quote.data[0].toToken.tokenSymbol,
decimals: parseInt(quote.data[0].toToken.decimal),
price: quote.data[0].toToken.tokenUnitPrice
}
};
// Convert amount to base units (for display purposes)
const humanReadableAmount = 0.1; // 0.1 SOL
const rawAmount = (humanReadableAmount * Math.pow(10, tokenInfo.fromToken.decimals)).toString();
console.log("\nSwap Details:");
console.log("--------------------");
console.log(`From: ${tokenInfo.fromToken.symbol}`);
console.log(`To: ${tokenInfo.toToken.symbol}`);
console.log(`Amount: ${humanReadableAmount} ${tokenInfo.fromToken.symbol}`);
console.log(`Amount in base units: ${rawAmount}`);
console.log(`Approximate USD value: $${(humanReadableAmount * parseFloat(tokenInfo.fromToken.price)).toFixed(2)}`);
// Execute the swap
console.log("\nExecuting swap...");
const swapResult = await client.dex.executeSwap({
chainIndex: '501', // Solana chain ID
fromTokenAddress: '11111111111111111111111111111111', // SOL
toTokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
amount: rawAmount,
slippagePercent: '0.5', // 0.5% slippagePercent
userWalletAddress: process.env.SOLANA_WALLET_ADDRESS!
});
console.log('Swap executed successfully:');
console.log(JSON.stringify(swapResult, null, 2));
return swapResult;
} catch (error) {
if (error instanceof Error) {
console.error('Error executing swap:', error.message);
// API errors include details in the message
if (error.message.includes('API Error:')) {
const match = error.message.match(/API Error: (.*)/);
if (match) console.error('API Error Details:', match[1]);
}
}
throw error;
}
}
// Run if this file is executed directly
if (require.main === module) {
executeSwap()
.then(() => process.exit(0))
.catch((error) => {
console.error('Error:', error);
process.exit(1);
});
}
export { executeSwap };
```
## 5. Additional SDK Functionality
The SDK provides additional methods that simplify development:
Get a quote for a token pair
```typescript
const quote = await client.dex.getQuote({
chainIndex: '501', // Solana
fromTokenAddress: '11111111111111111111111111111111', // SOL
toTokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
amount: '100000000', // 0.1 SOL (in lamports)
slippagePercent: '0.5' // 0.5% slippagePercent
});
```
- [Advanced Control With Swap-Instructions on Solana](https://web3.okx.com/onchainos/dev-docs/trade/dex-use-swap-solana-advance-control.md)
# Advanced Control With Swap-Instructions on Solana
The `swap-instruction` endpoint offers more control over the swap process than the standard `/swap` endpoint. While `/swap` gives you a pre-built transaction ready to sign, `swap-instruction` lets you:
- Build custom transaction signing flows
- Handle instruction processing your own way
- Add your own instructions to the transaction if needed
- Work with lookup tables directly for optimizing transaction size
## 1. Set Up Your Environment
Import the necessary libraries:
```javascript
// Required Solana dependencies for DEX interaction
import {
Connection, // Handles RPC connections to Solana network
Keypair, // Manages wallet keypairs for signing
PublicKey, // Handles Solana public key conversion and validation
TransactionInstruction, // Core transaction instruction type
TransactionMessage, // Builds transaction messages (v0 format)
VersionedTransaction, // Supports newer transaction format with lookup tables
RpcResponseAndContext, // RPC response wrapper type
SimulatedTransactionResponse, // Simulation result type
AddressLookupTableAccount, // For transaction size optimization
PublicKeyInitData // Public key input type
} from "@solana/web3.js";
import base58 from "bs58"; // Required for private key decoding
```
## 2. Initialize Your Connection and Wallet
Set up your connection and wallet instance:
```javascript
// Note: Consider using a reliable RPC endpoint with high rate limits for production
const connection = new Connection(
process.env.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com"
);
// Initialize wallet for signing
// This wallet will be the fee payer and transaction signer
const wallet = Keypair.fromSecretKey(
Uint8Array.from(base58.decode(userPrivateKey))
);
```
## 3. Configure Swap Parameters
Set up the parameters for your swap:
```javascript
// Configure swap parameters
const baseUrl = "https://web3.okx.com/api/v6/dex/aggregator/swap-instruction";
const params = {
chainIndex: "501", // Solana mainnet chain ID
feePercent: "1", // Platform fee percentage
amount: "1000000", // Amount in smallest denomination (lamports for SOL)
fromTokenAddress: "11111111111111111111111111111111", // SOL mint address
toTokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC mint address
slippagePercent: "0.5", // SlippagePercent tolerance 0.5%
userWalletAddress: userAddress, // Wallet performing the swap
autoSlippage: "false", // Use fixed slippage instead of auto
pathNum: "3" // Maximum routes to consider
};
```
## 4. Process Swap Instructions
Fetch and process the swap instructions:
```javascript
// Helper function to convert DEX API instructions to Solana format
function createTransactionInstruction(instruction) {
return new TransactionInstruction({
programId: new PublicKey(instruction.programId), // DEX program ID
keys: instruction.accounts.map((key) => ({
pubkey: new PublicKey(key.pubkey), // Account address
isSigner: key.isSigner, // True if account must sign tx
isWritable: key.isWritable // True if instruction modifies account
})),
data: Buffer.from(instruction.data, 'base64') // Instruction parameters
});
}
// Fetch optimal swap route and instructions from DEX
const timestamp = new Date().toISOString();
const requestPath = "/api/v6/dex/aggregator/swap-instruction";
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, "GET", requestPath, queryString);
const response = await fetch(
`https://web3.okx.com${requestPath}${queryString}`,
{ method: 'GET', headers }
);
const { data } = await response.json();
const { instructionLists, addressLookupTableAccount } = data;
// Process DEX instructions into Solana-compatible format
const instructions = [];
// Remove duplicate lookup table addresses returned by DEX
const uniqueLookupTables = Array.from(new Set(addressLookupTableAccount));
console.log("Lookup tables to load:", uniqueLookupTables);
// Convert each DEX instruction to Solana format
if (instructionLists?.length) {
instructions.push(...instructionLists.map(createTransactionInstruction));
}
```
## 5. Handle Address Lookup Tables
Process the address lookup tables for transaction optimization:
```javascript
// Process lookup tables for transaction optimization
// Lookup tables are crucial for complex swaps that interact with many accounts
// They significantly reduce transaction size and cost
const addressLookupTableAccounts = [];
if (uniqueLookupTables?.length > 0) {
console.log("Loading address lookup tables...");
// Fetch all lookup tables in parallel for better performance
const lookupTableAccounts = await Promise.all(
uniqueLookupTables.map(async (address) => {
const pubkey = new PublicKey(address);
// Get lookup table account data from Solana
const account = await connection
.getAddressLookupTable(pubkey)
.then((res) => res.value);
if (!account) {
throw new Error(`Could not fetch lookup table account ${address}`);
}
return account;
})
);
addressLookupTableAccounts.push(...lookupTableAccounts);
}
```
## 6. Create and Sign Transaction
Create the transaction message and sign it:
```javascript
// Get recent blockhash for transaction timing and uniqueness
const latestBlockhash = await connection.getLatestBlockhash('finalized');
// Create versioned transaction message (V0 format required for lookup table support)
const messageV0 = new TransactionMessage({
payerKey: wallet.publicKey, // Fee payer address
recentBlockhash: latestBlockhash.blockhash, // Transaction timing
instructions // Swap instructions from DEX
}).compileToV0Message(addressLookupTableAccounts); // Include lookup tables
// Create new versioned transaction with optimizations
const transaction = new VersionedTransaction(messageV0);
// Simulate transaction to check for errors
// This helps catch issues before paying fees
const result = await connection.simulateTransaction(transaction);
// Sign transaction with fee payer wallet
transaction.sign([wallet]);
```
## 7. Execute Transaction
Finally, simulate and send the transaction:
```javascript
// Send transaction to Solana
// skipPreflight=false ensures additional validation
// maxRetries helps handle network issues
const txId = await connection.sendRawTransaction(transaction.serialize(), {
skipPreflight: false, // Run preflight validation
maxRetries: 5 // Retry on failure
});
// Log transaction results
console.log("Transaction ID:", txId);
console.log("Explorer URL:", `https://solscan.io/tx/${txId}`);
// Wait for confirmation
await connection.confirmTransaction({
signature: txId,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight
});
console.log("Transaction confirmed!");
```
## Best Practices and Considerations
When implementing swap instructions, keep these key points in mind:
- Error Handling: Always implement proper error handling for API responses and transaction simulation results.
- Slippage Protection: Choose appropriate slippagePercent parameters based on your use case and market conditions.
- Gas Optimization: Use address lookup tables when available to reduce transaction size and costs.
- Transaction Simulation: Always simulate transactions before sending them to catch potential issues early.
- Retry Logic: Implement proper retry mechanisms for failed transactions with appropriate backoff strategies.
MEV Protection
Trading on Solana comes with MEV (Maximal Extractable Value) risks. While the MEV protection is not directly included in the SDK, you can implement it yourself using the API-first approach.
- [Build Swap Applications on EVM](https://web3.okx.com/onchainos/dev-docs/trade/dex-use-swap-quick-start.md)
# Build Swap Applications on EVM
There are two approaches to building swap applications with OKX DEX on EVM networks:
1. The API-first approach - directly interacting with OKX DEX API endpoints
2. The SDK approach - using the @okx-dex/okx-dex-sdk package for a simplified developer experience
This guide covers both methods to help you choose the approach that best fits your needs.
## Method 1: API-First Approach
This approach demonstrates a token swap using the OKX DEX API endpoints directly. You will swap USDC to ETH on the Ethereum network.
## 1. Set Up Your Environment
```typescript
// --------------------- npm package ---------------------
import { Web3 } from 'web3';
import axios from 'axios';
import * as dotenv from 'dotenv';
import CryptoJS from 'crypto-js';
// The URL for the Ethereum node you want to connect to
const web3 = new Web3('https://......com');
// --------------------- environment variable ---------------------
// Load hidden environment variables
dotenv.config();
// Your wallet information - REPLACE WITH YOUR OWN VALUES
const WALLET_ADDRESS: string = process.env.EVM_WALLET_ADDRESS || '0xYourWalletAddress';
const PRIVATE_KEY: string = process.env.EVM_PRIVATE_KEY || 'YourPrivateKey';
// Token addresses for swap on Base Chain
const ETH_ADDRESS: string = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; // Native ETH
const USDC_ADDRESS: string = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC on Base
// Chain ID for Base Chain
const chainIndex: string = '8453';
// API URL
const baseUrl: string = 'https://web3.okx.com/api/v6/';
// Amount to swap in smallest unit (0.0005 ETH)
const SWAP_AMOUNT: string = '500000000000000'; // 0.0005 ETH
const SLIPPAGEPERCENT: string = '0.5'; // 0.5% slippagePercent tolerance
// --------------------- util function ---------------------
export function getHeaders(timestamp: string, method: string, requestPath: string, queryString = "") {
// Check https://web3.okx.com/zh-hans/web3/build/docs/waas/rest-authentication for api-key
const apiKey = process.env.OKX_API_KEY;
const secretKey = process.env.OKX_SECRET_KEY;
const apiPassphrase = process.env.OKX_API_PASSPHRASE;
if (!apiKey || !secretKey || !apiPassphrase ) {
throw new Error("Missing required environment variables");
}
const stringToSign = timestamp + method + requestPath + queryString;
return {
"Content-Type": "application/json",
"OK-ACCESS-KEY": apiKey,
"OK-ACCESS-SIGN": CryptoJS.enc.Base64.stringify(
CryptoJS.HmacSHA256(stringToSign, secretKey)
),
"OK-ACCESS-TIMESTAMP": timestamp,
"OK-ACCESS-PASSPHRASE": apiPassphrase,
};
};
```
## 2. Check Allowance
You need to check if the token has been approved for the DEX to spend. This step is only needed for ERC20 tokens, not for native tokens like ETH.
```typescript
/**
* Check token allowance for DEX
* @param tokenAddress - Token contract address
* @param ownerAddress - Your wallet address
* @param spenderAddress - DEX spender address
* @returns Allowance amount
*/
async function checkAllowance(
tokenAddress: string,
ownerAddress: string,
spenderAddress: string
): Promise {
const tokenABI = [
{
"constant": true,
"inputs": [
{ "name": "_owner", "type": "address" },
{ "name": "_spender", "type": "address" }
],
"name": "allowance",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
}
];
const tokenContract = new web3.eth.Contract(tokenABI, tokenAddress);
try {
const allowance = await tokenContract.methods.allowance(ownerAddress, spenderAddress).call();
return BigInt(String(allowance));
} catch (error) {
console.error('Failed to query allowance:', error);
throw error;
}
}
```
## 3. Check the Approval Parameters and Initiate the Approval
If the allowance is lower than the amount you want to swap, you need to approve the token.
3.1 Define your transaction approval parameters
```typescript
const getApproveTransactionParams = {
chainIndex: chainIndex,
tokenContractAddress: tokenAddress,
approveAmount: amount
};
```
3.2 Define helper functions
```typescript
async function getApproveTransaction(
tokenAddress: string,
amount: string
): Promise {
try {
const path = 'dex/aggregator/approve-transaction';
const url = `${baseUrl}${path}`;
const params = {
chainIndex: chainIndex,
tokenContractAddress: tokenAddress,
approveAmount: amount
};
// Prepare authentication
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await axios.get(url, { params, headers });
if (response.data.code === '0') {
return response.data.data[0];
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get approval transaction data:', (error as Error).message);
throw error;
}
}
```
3.3 Create Compute gasLimit utility function
Using the Onchain gateway API to get the gas limit.
```typescript
/**
* Get transaction gas limit from Onchain gateway API
* @param fromAddress - Sender address
* @param toAddress - Target contract address
* @param txAmount - Transaction amount (0 for approvals)
* @param inputData - Transaction calldata
* @returns Estimated gas limit
*/
async function getGasLimit(
fromAddress: string,
toAddress: string,
txAmount: string = '0',
inputData: string = ''
): Promise {
try {
const path = 'dex/pre-transaction/gas-limit';
const url = `https://web3.okx.com/api/v6/${path}`;
const body = {
chainIndex: chainIndex,
fromAddress: fromAddress,
toAddress: toAddress,
txAmount: txAmount,
extJson: {
inputData: inputData
}
};
// Prepare authentication with body included in signature
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
if (response.data.code === '0') {
return response.data.data[0].gasLimit;
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get gas limit:', (error as Error).message);
throw error;
}
}
```
Using RPC to get the gas limit.
```typescript
const gasLimit = await web3.eth.estimateGas({
from: WALLET_ADDRESS,
to: tokenAddress,
value: '0',
data: approveData.data
});
// Add 20% buffer
const gasLimit = (BigInt(gasLimit) * BigInt(12) / BigInt(10)).toString();
```
3.4 Get transaction information and send approveTransaction
```typescript
/**
* Sign and send approve transaction
* @param tokenAddress - Token to approve
* @param amount - Amount to approve
* @returns Transaction hash of the approval transaction
*/
async function approveToken(tokenAddress: string, amount: string): Promise {
const spenderAddress = '0x3b3ae790Df4F312e745D270119c6052904FB6790'; // Ethereum Mainnet DEX spender
// See Router addresses at: https://web3.okx.com/build/docs/waas/dex-smart-contract
const currentAllowance = await checkAllowance(tokenAddress, WALLET_ADDRESS, spenderAddress);
if (currentAllowance >= BigInt(amount)) {
console.log('Sufficient allowance already exists');
return null;
}
console.log('Insufficient allowance, approving tokens...');
// Get approve transaction data from OKX DEX API
const approveData = await getApproveTransaction(tokenAddress, amount);
// Get accurate gas limit using RPC
const gasLimit = await web3.eth.estimateGas({
from: WALLET_ADDRESS,
to: tokenAddress,
value: '0',
data: approveData.data
});
// Get accurate gas limit using Onchain gateway API
// const gasLimit = await getGasLimit(WALLET_ADDRESS, tokenAddress, '0', approveData.data);
// Get current gas price
const gasPrice = await web3.eth.getGasPrice();
const adjustedGasPrice = BigInt(gasPrice) * BigInt(15) / BigInt(10); // 1.5x for faster confirmation
// Get current nonce
const nonce = await web3.eth.getTransactionCount(WALLET_ADDRESS, 'latest');
// Create transaction object
const txObject = {
from: WALLET_ADDRESS,
to: tokenAddress,
data: approveData.data,
value: '0',
gas: gasLimit,
gasPrice: adjustedGasPrice.toString(),
nonce: nonce
};
// Sign and broadcast transaction
const signedTx = await web3.eth.accounts.signTransaction(txObject, PRIVATE_KEY);
const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);
console.log(`Approval transaction successful: ${receipt.transactionHash}`);
return receipt.transactionHash;
}
```
## 4. Get Quote Data
4.1 Define quote parameters
```typescript
const quoteParams = {
amount: fromAmount,
chainIndex: chainIndex,
toTokenAddress: toTokenAddress,
fromTokenAddress: fromTokenAddress,
};
```
4.2 Define helper functions
```typescript
/**
* Get swap quote from DEX API
* @param fromTokenAddress - Source token address
* @param toTokenAddress - Destination token address
* @param amount - Amount to swap
* @param slippagePercent - Maximum slippagePercent (e.g., "0.5" for 0.5%)
* @returns Swap quote
*/
async function getSwapQuote(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
slippagePercent: string = '0.5'
): Promise {
try {
const path = 'dex/aggregator/quote';
const url = `${baseUrl}${path}`;
const params = {
chainIndex: chainIndex,
fromTokenAddress,
toTokenAddress,
amount,
slippagePercent
};
// Prepare authentication
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await axios.get(url, { params, headers });
if (response.data.code === '0') {
return response.data.data[0];
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get swap quote:', (error as Error).message);
throw error;
}
}
```
## 5. Prepare Transaction
5.1 Define swap parameters
```typescript
const swapParams = {
chainIndex: chainIndex,
fromTokenAddress,
toTokenAddress,
amount,
userWalletAddress: userAddress,
slippagePercent
};
```
5.2 Request swap transaction data
```typescript
/**
* Get swap transaction data from DEX API
* @param fromTokenAddress - Source token address
* @param toTokenAddress - Destination token address
* @param amount - Amount to swap
* @param userAddress - User wallet address
* @param slippagePercent - Maximum slippagePercent (e.g., "0.5" for 0.5%)
* @returns Swap transaction data
*/
async function getSwapTransaction(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
userAddress: string,
slippagePercent: string = '0.5'
): Promise {
try {
const path = 'dex/aggregator/swap';
const url = `${baseUrl}${path}`;
const params = {
chainIndex: chainIndex,
fromTokenAddress,
toTokenAddress,
amount,
userWalletAddress: userAddress,
slippagePercent
};
// Prepare authentication
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await axios.get(url, { params, headers });
if (response.data.code === '0') {
return response.data.data[0];
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get swap transaction data:', (error as Error).message);
throw error;
}
}
```
## 6. Simulate Transaction
Before executing the actual swap, it's crucial to simulate the transaction to ensure it will succeed and to identify any potential issues:
The Simulate API is available to our whitelisted customers only. Please reach out to dexapi@okx.com to request access.
```typescript
async function simulateTransaction(swapData: any) {
try {
if (!swapData.tx) {
throw new Error('Invalid swap data format - missing transaction data');
}
const tx = swapData.tx;
const params: any = {
fromAddress: tx.from,
toAddress: tx.to,
txAmount: tx.value || '0',
chainIndex: chainIndex,
extJson: {
inputData: tx.data
},
includeDebug: true
};
const timestamp = new Date().toISOString();
const requestPath = "/api/v6/dex/pre-transaction/simulate";
const requestBody = JSON.stringify(params);
const headers = getHeaders(timestamp, "POST", requestPath, "", requestBody);
console.log('Simulating transaction...');
const response = await axios.post(
`https://web3.okx.com${requestPath}`,
params,
{ headers }
);
if (response.data.code !== "0") {
throw new Error(`Simulation failed: ${response.data.msg || "Unknown simulation error"}`);
}
const simulationResult = response.data.data[0];
// Check simulation success
if (simulationResult.success === false) {
console.error('Transaction simulation failed:', simulationResult.error);
throw new Error(`Transaction would fail: ${simulationResult.error}`);
}
console.log('Transaction simulation successful');
console.log(`Estimated gas used: ${simulationResult.gasUsed || 'N/A'}`);
if (simulationResult.logs) {
console.log('Simulation logs:', simulationResult.logs);
}
return simulationResult;
} catch (error) {
console.error("Error simulating transaction:", error);
throw error;
}
}
```
## 7. Broadcast Transaction
Creating a Compute Gas Limit Utility Function
There are two approaches to obtain the gas limit for your transactions: using standard RPC calls or leveraging the Onchain Gateway API.
Method 1: Using the Onchain Gateway API for Gas Estimation
The first approach leverages OKX's Onchain Gateway API, which provides more accurate gas estimations than standard methods.
```typescript
/**
* Get transaction gas limit from Onchain gateway API
* @param fromAddress - Sender address
* @param toAddress - Target contract address
* @param txAmount - Transaction amount (0 for approvals)
* @param inputData - Transaction calldata
* @returns Estimated gas limit
*/
async function getGasLimit(
fromAddress: string,
toAddress: string,
txAmount: string = '0',
inputData: string = ''
): Promise {
try {
const path = 'dex/pre-transaction/gas-limit';
const url = `https://web3.okx.com/api/v6/${path}`;
const body = {
chainIndex: chainIndex,
fromAddress: fromAddress,
toAddress: toAddress,
txAmount: txAmount,
extJson: {
inputData: inputData
}
};
// Prepare authentication with body included in signature
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
if (response.data.code === '0') {
return response.data.data[0].gasLimit;
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get gas limit:', (error as Error).message);
throw error;
}
}
```
Method 2: Using RPC to Estimate Gas Limit
The second approach utilizes standard Web3 RPC calls to estimate the required gas for your transaction.
```typescript
const gasLimit = await web3.eth.estimateGas({
from: WALLET_ADDRESS,
to: tokenAddress,
value: '0',
data: swapData.data
});
// Add 20% buffer
const gasLimit = (BigInt(gasLimit) * BigInt(12) / BigInt(10)).toString();
```
Broadcasting Transactions with the Onchain Gateway API
For developers with access to the Onchain Gateway API, you can broadcast transactions directly through OKX's infrastructure. This method provides enhanced reliability and monitoring capabilities for high-volume trading operations.
The Broadcast API requires API is available to our whitelisted customers only. Please reach out to dexapi@okx.com to request access.
```typescript
import { Web3 } from 'web3';
import axios from 'axios';
import * as dotenv from 'dotenv';
import CryptoJS from 'crypto-js';
// Load environment variables
dotenv.config();
// Connect to Base network
const web3 = new Web3(process.env.EVM_RPC_URL || 'https://mainnet.base.org');
// Your wallet information - REPLACE WITH YOUR OWN VALUES
const WALLET_ADDRESS = process.env.EVM_WALLET_ADDRESS || '';
const PRIVATE_KEY = process.env.EVM_PRIVATE_KEY || '';
// Token addresses for swap on Base Chain
const ETH_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; // Native ETH
// Chain ID for Base Chain
const chainIndex = '8453';
// API URL
const baseUrl = 'https://web3.okx.com/api/v6/';
// Define interfaces
interface GasLimitApiResponse {
code: string;
msg?: string;
data: Array<{
gasLimit: string;
}>;
}
// Interface for broadcast API response
interface BroadcastApiResponse {
code: string;
msg?: string;
data: Array<{
orderId: string;
}>;
}
/**
* Generate API authentication headers
*/
function getHeaders(timestamp: string, method: string, requestPath: string, queryString = "", body = "") {
const apiKey = process.env.OKX_API_KEY;
const secretKey = process.env.OKX_SECRET_KEY;
const apiPassphrase = process.env.OKX_API_PASSPHRASE;
if (!apiKey || !secretKey || !apiPassphrase ) {
throw new Error("Missing required environment variables for API authentication");
}
const stringToSign = timestamp + method + requestPath + (queryString || body);
return {
"Content-Type": "application/json",
"OK-ACCESS-KEY": apiKey,
"OK-ACCESS-SIGN": CryptoJS.enc.Base64.stringify(
CryptoJS.HmacSHA256(stringToSign, secretKey)
),
"OK-ACCESS-TIMESTAMP": timestamp,
"OK-ACCESS-PASSPHRASE": apiPassphrase,
};
}
/**
* Get transaction gas limit from Onchain gateway API
* @param fromAddress - Sender address
* @param toAddress - Target contract address
* @param txAmount - Transaction amount (0 for approvals)
* @param inputData - Transaction calldata
* @returns Estimated gas limit
*/
async function getGasLimit(
fromAddress: string,
toAddress: string,
txAmount: string = '0',
inputData: string = ''
): Promise {
const path = 'dex/pre-transaction/gas-limit';
const url = `${baseUrl}${path}`;
const body = { chainIndex: chainIndex, fromAddress, toAddress, txAmount, extJson: { inputData } };
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const headers = getHeaders(timestamp, 'POST', `/api/v6/${path}`, "", bodyString);
const response = await axios.post(url, body, { headers });
if (response.data.code === '0') {
return response.data.data[0].gasLimit;
}
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
/**
* Get swap data from OKX API
*/
async function getSwapData(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
slippagePercent = '0.5'
) {
const path = 'dex/aggregator/swap';
const url = `${baseUrl}${path}`;
const params = { chainIndex: chainIndex, fromTokenAddress, toTokenAddress, amount, slippagePercent, userWalletAddress: WALLET_ADDRESS };
const queryString = "?" + new URLSearchParams(params).toString();
const timestamp = new Date().toISOString();
const headers = getHeaders(timestamp, 'GET', `/api/v6/${path}`, queryString);
const response = await axios.get(`${url}${queryString}`, { headers });
const responseData = response.data as any;
if (responseData.code === '0') {
return responseData.data[0];
}
throw new Error(`Swap API Error: ${responseData.msg || 'Unknown error'}`);
}
/**
* Build and sign transaction using gas limit
*/
async function buildAndSignTransaction(swapData: any, gasLimit: string): Promise {
const gasPrice = await web3.eth.getGasPrice();
const nonce = await web3.eth.getTransactionCount(WALLET_ADDRESS, 'pending');
const transaction = {
from: swapData.tx.from,
to: swapData.tx.to,
data: swapData.tx.data,
value: swapData.tx.value || '0x0',
gas: gasLimit,
gasPrice: gasPrice.toString(),
nonce: Number(nonce),
chainIndex: parseInt(chainIndex)
};
return await web3.eth.accounts.signTransaction(transaction, PRIVATE_KEY);
}
/**
* Broadcast transaction using Onchain Gateway API
*/
async function broadcastTransaction(signedTx: any, chainIndex: string, walletAddress: string): Promise {
const path = 'dex/pre-transaction/broadcast-transaction';
const url = `${baseUrl}${path}`;
const rawTxHex = typeof signedTx.rawTransaction === 'string' ? signedTx.rawTransaction : web3.utils.bytesToHex(signedTx.rawTransaction);
const body = { signedTx: rawTxHex, chainIndex: chainIndex, address: walletAddress };
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const headers = getHeaders(timestamp, 'POST', `/api/v6/${path}`, "", bodyString);
const response = await axios.post(url, body, { headers });
if (response.data.code === '0') {
return response.data.data[0].orderId;
}
throw new Error(`Broadcast API Error: ${response.data.msg || 'Unknown error'}`);
}
async function main() {
try {
console.log('EVM Gas Limit and Broadcast');
console.log('================================');
// Validate environment variables
if (!WALLET_ADDRESS || !PRIVATE_KEY) {
throw new Error('Missing wallet address or private key in environment variables');
}
console.log(`Wallet Address: ${WALLET_ADDRESS}`);
console.log(`Chain ID: ${chainIndex}`);
console.log(`RPC URL: ${process.env.EVM_RPC_URL || 'https://mainnet.base.org'}`);
// Example parameters
const fromToken = ETH_ADDRESS;
const toToken = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC on Base
const amount = '100000000000000'; // 0.0001 ETH in wei
const slippagePercent = '0.5'; // 0.5%
// Step 1: Get swap data
const swapData = await getSwapData(fromToken, toToken, amount, slippagePercent);
console.log('Swap data obtained');
// Step 2: Get gas limit
const gasLimit = await getGasLimit(
swapData.tx.from,
swapData.tx.to,
swapData.tx.value || '0',
swapData.tx.data
);
console.log('Gas limit obtained', gasLimit);
// Step 3: Build and sign transaction
const signedTx = await buildAndSignTransaction(swapData, gasLimit);
console.log('Transaction built and signed');
// Step 4: Broadcast transaction
try {
const orderId = await broadcastTransaction(signedTx, chainIndex, swapData.tx.from);
console.log(`Transaction broadcast successful. Order ID: ${orderId}`);
} catch (broadcastError: any) {
if (broadcastError.message.includes('API registration and whitelist required')) {
console.log('Broadcast failed - API registration and whitelist required');
console.log('Gas limit obtained successfully:', gasLimit);
} else {
throw broadcastError;
}
}
} catch (error) {
console.error('Main execution failed:', (error as Error).message);
process.exit(1);
}
}
// Run the script
if (require.main === module) {
main();
}
export {
getSwapData,
getGasLimit,
broadcastTransaction
};
```
Alternative: Broadcasting Transactions Using Standard RPC
For developers who prefer using standard blockchain RPC methods or do not have yet requested API whitelisting, you can broadcast transactions directly to the network using Web3 RPC calls.
```typescript
/**
* Execute token swap
* @param fromTokenAddress - Source token address
* @param toTokenAddress - Destination token address
* @param amount - Amount to swap
* @param slippagePercent - Maximum slippagePercent
* @returns Transaction hash
*/
async function executeSwap(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
slippagePercent: string = '0.5'
): Promise {
// 1. Check allowance and approve if necessary (skip for native token)
if (fromTokenAddress !== ETH_ADDRESS) {
await approveToken(fromTokenAddress, amount);
}
// 2. Get swap transaction data
const swapData = await getSwapTransaction(fromTokenAddress, toTokenAddress, amount, WALLET_ADDRESS, slippagePercent);
const txData = swapData.tx;
console.log("Swap TX data received");
// 3. Get accurate gas limit
const gasLimit = await getGasLimit(
WALLET_ADDRESS,
txData.to,
txData.value || '0',
txData.data
);
console.log("Gas limit received");
// 4. Get current nonce
const nonce = await web3.eth.getTransactionCount(WALLET_ADDRESS, 'latest');
console.log("Nonce received");
// 5. Get current gas price and adjust for faster confirmation
const gasPrice = await web3.eth.getGasPrice();
const adjustedGasPrice = BigInt(gasPrice) * BigInt(15) / BigInt(10); // 1.5x for faster confirmation
console.log("Gas price received");
// 6. Create transaction object
const txObject = {
from: WALLET_ADDRESS,
to: txData.to,
data: txData.data,
value: txData.value || '0',
gas: gasLimit,
gasPrice: adjustedGasPrice.toString(),
nonce: nonce
};
console.log("TX build complete");
// 7. Sign and broadcast transaction using RPC
const signedTx = await web3.eth.accounts.signTransaction(txObject, PRIVATE_KEY);
console.log("TX signed");
const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);
console.log(`Transaction successful: ${receipt.transactionHash}`);
return receipt.transactionHash;
}
```
## 8. Track Transaction
Choose the first(section 8.1) for basic transaction confirmation status, and the second(section 8.2) when you need detailed information about the swap execution itself.
8.1 Using Onchain gateway API
The Onchain gateway API provides transaction tracking capabilities through the `/dex/post-transaction/orders` endpoint. Use the order ID returned by the broadcast API to track transactions as they progress through OKX's systems with simple status codes (1: Pending, 2: Success, 3: Failed).
```typescript
// Define error info interface
interface TxErrorInfo {
error: string;
message: string;
action: string;
}
/**
* Tracking transaction confirmation status using the Onchain gateway API
* @param orderId - Order ID from broadcast response
* @param intervalMs - Polling interval in milliseconds
* @param timeoutMs - Maximum time to wait
* @returns Final transaction confirmation status
*/
async function trackTransaction(
orderId: string,
intervalMs: number = 5000,
timeoutMs: number = 300000
): Promise {
console.log(`Tracking transaction with Order ID: ${orderId}`);
const startTime = Date.now();
let lastStatus = '';
while (Date.now() - startTime < timeoutMs) {
// Get transaction status
try {
const path = 'dex/post-transaction/orders';
const url = `https://web3.okx.com/api/v6/${path}`;
const params = {
orderId: orderId,
chainIndex: chainIndex,
address: WALLET_ADDRESS,
limit: '1'
};
// Prepare authentication
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await axios.get(url, { params, headers });
if (response.data.code === '0' && response.data.data && response.data.data.length > 0) {
if (response.data.data[0].orders && response.data.data[0].orders.length > 0) {
const txData = response.data.data[0].orders[0];
// Use txStatus to match the API response
const status = txData.txStatus;
// Only log when status changes
if (status !== lastStatus) {
lastStatus = status;
if (status === '1') {
console.log(`Transaction pending: ${txData.txHash || 'Hash not available yet'}`);
} else if (status === '2') {
console.log(`Transaction successful: https://web3.okx.com/explorer/base/tx/${txData.txHash}`);
return txData;
} else if (status === '3') {
const failReason = txData.failReason || 'Unknown reason';
const errorMessage = `Transaction failed: ${failReason}`;
console.error(errorMessage);
const errorInfo = handleTransactionError(txData);
console.log(`Error type: ${errorInfo.error}`);
console.log(`Suggested action: ${errorInfo.action}`);
throw new Error(errorMessage);
}
}
} else {
console.log(`No orders found for Order ID: ${orderId}`);
}
}
} catch (error) {
console.warn('Error checking transaction status:', (error as Error).message);
}
// Wait before next check
await new Promise(resolve => setTimeout(resolve, intervalMs));
}
throw new Error('Transaction tracking timed out');
}
/**
* Comprehensive error handling with failReason
* @param txData - Transaction data from post-transaction/orders
* @returns Structured error information
*/
function handleTransactionError(txData: any): TxErrorInfo {
const failReason = txData.failReason || 'Unknown reason';
// Log the detailed error
console.error(`Transaction failed with reason: ${failReason}`);
// Default error handling
return {
error: 'TRANSACTION_FAILED',
message: failReason,
action: 'Try again or contact support'
};
}
```
8.2 Track transaction using SWAP API:
SWAP API transaction tracking provides comprehensive swap execution details using the `/dex/aggregator/history` endpoint. It offers token-specific information (symbols, amounts), fees paid, and detailed blockchain data. Use this when you need complete swap insight with token-level details.
```typescript
/**
* Track transaction using SWAP API
* @param chainIndex - Chain ID (e.g., 1 for Ethereum Mainnet)
* @param txHash - Transaction hash
* @returns Transaction details
*/
async function trackTransactionWithSwapAPI(chainIndex: string, txHash: string): Promise {
try {
const path = 'dex/aggregator/history';
const url = `${baseUrl}${path}`;
const params = {
chainIndex: chainIndex,
txHash: txHash,
isFromMyProject: 'true'
};
// Prepare authentication
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await axios.get(url, { params, headers });
if (response.data.code === '0') {
const txData = response.data.data[0];
const status = txData.status;
if (status === 'pending') {
console.log(`Transaction is still pending: ${txHash}`);
return { status: 'pending', details: txData };
} else if (status === 'success') {
console.log(`Transaction successful!`);
console.log(`From: ${txData.fromTokenDetails.symbol} - Amount: ${txData.fromTokenDetails.amount}`);
console.log(`To: ${txData.toTokenDetails.symbol} - Amount: ${txData.toTokenDetails.amount}`);
console.log(`Transaction Fee: ${txData.txFee}`);
console.log(`Explorer URL: https://basescan.org/tx/${txHash}`);
return { status: 'success', details: txData };
} else if (status === 'failure') {
console.error(`Transaction failed: ${txData.errorMsg || 'Unknown reason'}`);
return { status: 'failure', details: txData };
}
return txData;
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to track transaction status:', (error as Error).message);
throw error;
}
}
```
## 9. Complete Implementation
Here's a complete implementation example:
```typescript
import { Web3 } from 'web3';
import * as axios from 'axios';
import * as dotenv from 'dotenv';
import * as CryptoJS from 'crypto-js';
// Load environment variables
dotenv.config();
// Connect to Base network
const web3 = new Web3(process.env.EVM_RPC_URL || 'https://mainnet.base.org');
// Your wallet information - REPLACE WITH YOUR OWN VALUES
const WALLET_ADDRESS: string = process.env.EVM_WALLET_ADDRESS || '';
const PRIVATE_KEY: string = process.env.EVM_PRIVATE_KEY || '';
// Token addresses for swap on Base Chain
const ETH_ADDRESS: string = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; // Native ETH
// Chain ID for Base Chain
const chainIndex: string = '8453';
// API URL
const baseUrl: string = 'https://web3.okx.com/api/v6/';
// Define interfaces
interface TokenInfo {
tokenSymbol: string;
decimal: string;
tokenUnitPrice: string;
}
// Interface for gas limit API response
interface GasLimitApiResponse {
code: string;
msg?: string;
data: Array<{
gasLimit: string;
}>;
}
// Interface for simulation API response
interface SimulationApiResponse {
code: string;
msg?: string;
data: Array<{
intention: string;
gasUsed?: string;
failReason?: string;
assetChange?: Array<{
assetType: string;
name: string;
symbol: string;
decimals: number;
address: string;
imageUrl: string;
rawValue: string;
}>;
risks?: Array;
}>;
}
// Interface for broadcast API response
interface BroadcastApiResponse {
code: string;
msg?: string;
data: Array<{
orderId: string;
}>;
}
// Define error info interface
interface TxErrorInfo {
error: string;
message: string;
action: string;
}
/**
* Generate API authentication headers
*/
function getHeaders(timestamp: string, method: string, requestPath: string, queryString = "", body = "") {
const apiKey = process.env.OKX_API_KEY;
const secretKey = process.env.OKX_SECRET_KEY;
const apiPassphrase = process.env.OKX_API_PASSPHRASE;
if (!apiKey || !secretKey || !apiPassphrase ) {
throw new Error("Missing required environment variables for API authentication");
}
const stringToSign = timestamp + method + requestPath + (queryString || body);
return {
"Content-Type": "application/json",
"OK-ACCESS-KEY": apiKey,
"OK-ACCESS-SIGN": CryptoJS.enc.Base64.stringify(
CryptoJS.HmacSHA256(stringToSign, secretKey)
),
"OK-ACCESS-TIMESTAMP": timestamp,
"OK-ACCESS-PASSPHRASE": apiPassphrase,
};
}
/**
* Get transaction gas limit from Onchain gateway API
* @param fromAddress - Sender address
* @param toAddress - Target contract address
* @param txAmount - Transaction amount (0 for approvals)
* @param inputData - Transaction calldata
* @returns Estimated gas limit
*/
async function getGasLimit(
fromAddress: string,
toAddress: string,
txAmount: string = '0',
inputData: string = ''
): Promise {
try {
console.log('Getting gas limit from Onchain Gateway API...');
const path = 'dex/pre-transaction/gas-limit';
const url = `${baseUrl}${path}`;
const body = {
chainIndex: chainIndex,
fromAddress: fromAddress,
toAddress: toAddress,
txAmount: txAmount,
extJson: {
inputData: inputData
}
};
// Prepare authentication with body included in signature
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
console.log('Gas Limit API Response:');
console.log(JSON.stringify(response.data, null, 2));
if (response.data.code === '0') {
const gasLimit = response.data.data[0].gasLimit;
console.log(`Gas Limit obtained: ${gasLimit}`);
return gasLimit;
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get gas limit:', (error as Error).message);
throw error;
}
}
/**
* Get swap data from OKX API
*/
async function getSwapData(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
slippagePercent = '0.5'
) {
try {
console.log('Getting swap data from OKX API...');
const path = 'dex/aggregator/swap';
const url = `${baseUrl}${path}`;
const params = {
chainIndex: chainIndex,
fromTokenAddress: fromTokenAddress,
toTokenAddress: toTokenAddress,
amount: amount,
slippagePercent: slippagePercent,
userWalletAddress: WALLET_ADDRESS
};
console.log('Swap API Request Parameters:');
console.log(JSON.stringify(params, null, 2));
// Prepare authentication with query string
const queryString = "?" + new URLSearchParams(params).toString();
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await axios.get(`${url}${queryString}`, { headers });
console.log('Swap API Response:');
console.log(JSON.stringify(response.data, null, 2));
const responseData = response.data as any;
if (responseData.code === '0') {
return responseData.data[0];
} else {
throw new Error(`Swap API Error: ${responseData.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get swap data:', (error as Error).message);
throw error;
}
}
/**
* Simulate transaction using Onchain Gateway API
*/
async function simulateTransaction(swapData: any) {
try {
console.log('Simulating transaction with Onchain Gateway API...');
const path = 'dex/pre-transaction/simulate';
const url = `${baseUrl}${path}`;
const body = {
chainIndex: chainIndex,
fromAddress: swapData.tx.from,
toAddress: swapData.tx.to,
txAmount: swapData.tx.value || '0',
extJson: {
inputData: swapData.tx.data
}
};
// Prepare authentication with body included in signature
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
console.log('Simulation API Response:');
console.log(JSON.stringify(response.data, null, 2));
if (response.data.code === '0') {
const simulationResult = response.data.data[0];
// Check if simulation was successful (no failReason or empty failReason)
if (!simulationResult.failReason || simulationResult.failReason === '') {
console.log(`Transaction simulation successful. Gas used: ${simulationResult.gasUsed}`);
return simulationResult;
} else {
throw new Error(`Simulation failed: ${simulationResult.failReason}`);
}
} else {
throw new Error(`Simulation API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Transaction simulation failed:', (error as Error).message);
throw error;
}
}
/**
* Broadcast transaction using Onchain Gateway API with RPC fallback
*/
async function broadcastTransaction(signedTx: any, chainIndex: string, walletAddress: string): Promise {
try {
console.log('Broadcasting transaction via Onchain Gateway API...');
const path = 'dex/pre-transaction/broadcast-transaction';
const url = `${baseUrl}${path}`;
// Convert rawTransaction to hex string
const rawTxHex = typeof signedTx.rawTransaction === 'string'
? signedTx.rawTransaction
: web3.utils.bytesToHex(signedTx.rawTransaction);
const body = {
signedTx: rawTxHex,
chainIndex: chainIndex,
address: walletAddress
// See [MEV Section](#10-mev-protection) for MEV protection settings
};
console.log('Broadcast API Request Body:');
console.log(JSON.stringify(body, null, 2));
// Prepare authentication with body included in signature
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
console.log('Broadcast API Response:');
console.log(JSON.stringify(response.data, null, 2));
if (response.data.code === '0') {
const orderId = response.data.data[0].orderId;
console.log(`Transaction broadcast successful. Order ID: ${orderId}`);
return orderId;
} else {
throw new Error(`Broadcast API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('API broadcast failed, trying RPC fallback:', (error as Error).message);
// Fallback to RPC broadcast
try {
console.log('Broadcasting via RPC fallback...');
const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);
console.log(`RPC broadcast successful. Transaction hash: ${receipt.transactionHash}`);
return receipt.transactionHash.toString();
} catch (rpcError) {
console.error('RPC broadcast also failed:', (rpcError as Error).message);
throw new Error(`Both API and RPC broadcast failed. API Error: ${(error as Error).message}, RPC Error: ${(rpcError as Error).message}`);
}
}
}
/**
* Execute swap with full transaction flow
*/
async function executeSwap(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
slippagePercent: string = '0.5'
): Promise {
try {
console.log('Starting swap execution...');
// Step 1: Get swap data
const swapData = await getSwapData(fromTokenAddress, toTokenAddress, amount, slippagePercent);
console.log('Swap data obtained');
// Step 2: Simulate transaction
const simulationResult = await simulateTransaction(swapData);
console.log('Transaction simulation completed');
console.log('Simulation result', simulationResult.intention);
// Step 3: Get gas limit
const gasLimit = await getGasLimit(
swapData.tx.from,
swapData.tx.to,
swapData.tx.value || '0',
swapData.tx.data
);
// Step 4: Get current gas price
const gasPrice = await web3.eth.getGasPrice();
console.log(`Current gas price: ${web3.utils.fromWei(gasPrice, 'gwei')} gwei`);
// Step 5: Get nonce
const nonce = await web3.eth.getTransactionCount(WALLET_ADDRESS, 'pending');
console.log(`Nonce: ${nonce}`);
// Step 6: Build transaction
const transaction = {
from: swapData.tx.from,
to: swapData.tx.to,
data: swapData.tx.data,
value: swapData.tx.value || '0x0',
gas: gasLimit,
gasPrice: gasPrice.toString(),
nonce: Number(nonce),
chainIndex: parseInt(chainIndex)
};
console.log('Transaction object:');
console.log(JSON.stringify(transaction, null, 2));
// Step 7: Sign transaction
console.log('Signing transaction...');
const signedTx = await web3.eth.accounts.signTransaction(transaction, PRIVATE_KEY);
console.log('Transaction signed');
// Step 8: Broadcast transaction
const txHash = await broadcastTransaction(signedTx, chainIndex, WALLET_ADDRESS);
console.log(`Transaction broadcast successful. Hash: ${txHash}`);
// Step 9: Track transaction
console.log('Tracking transaction status...');
const trackingResult = await trackTransaction(txHash);
console.log('Transaction tracking completed');
console.log('Tracking result', trackingResult);
return txHash;
} catch (error) {
console.error('Swap execution failed:', (error as Error).message);
throw error;
}
}
/**
* Execute swap with simulation and detailed logging
*/
async function executeSwapWithSimulation(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
slippagePercent: string = '0.5'
): Promise {
try {
console.log('Starting swap execution with simulation...');
const txHash = await executeSwap(fromTokenAddress, toTokenAddress, amount, slippagePercent);
console.log('Swap execution completed successfully!');
console.log(`Transaction Hash: ${txHash}`);
return { success: true, txHash };
} catch (error) {
console.error('Swap execution failed:', (error as Error).message);
return { success: false, error: (error as Error).message };
}
}
/**
* Tracking transaction confirmation status using the Onchain gateway API
* @param orderId - Order ID from broadcast response
* @param intervalMs - Polling interval in milliseconds
* @param timeoutMs - Maximum time to wait
* @returns Final transaction confirmation status
*/
async function trackTransaction(
orderId: string,
intervalMs: number = 5000,
timeoutMs: number = 300000
): Promise {
console.log(`Tracking transaction with Order ID: ${orderId}`);
const startTime = Date.now();
let lastStatus = '';
while (Date.now() - startTime < timeoutMs) {
try {
const path = 'dex/post-transaction/orders';
const url = `https://web3.okx.com/api/v6/${path}`;
const params = {
orderId: orderId,
chainIndex: chainIndex,
address: WALLET_ADDRESS,
limit: '1'
};
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await axios.get(url, { params, headers });
const responseData = response.data as any;
if (responseData.code === '0' && responseData.data && responseData.data.length > 0) {
if (responseData.data[0].orders && responseData.data[0].orders.length > 0) {
const txData = responseData.data[0].orders[0];
const status = txData.txStatus;
if (status !== lastStatus) {
lastStatus = status;
if (status === '1') {
console.log(`Transaction pending: ${txData.txHash || 'Hash not available yet'}`);
} else if (status === '2') {
console.log(`Transaction successful: https://web3.okx.com/explorer/base/tx/${txData.txHash}`);
return txData;
} else if (status === '3') {
const failReason = txData.failReason || 'Unknown reason';
const errorMessage = `Transaction failed: ${failReason}`;
console.error(errorMessage);
const errorInfo = handleTransactionError(txData);
console.log(`Error type: ${errorInfo.error}`);
console.log(`Suggested action: ${errorInfo.action}`);
throw new Error(errorMessage);
}
}
} else {
console.log(`No orders found for Order ID: ${orderId}`);
}
}
} catch (error) {
console.warn('Error checking transaction status:', (error as Error).message);
}
await new Promise(resolve => setTimeout(resolve, intervalMs));
}
throw new Error('Transaction tracking timed out');
}
/**
* Comprehensive error handling with failReason
* @param txData - Transaction data from post-transaction/orders
* @returns Structured error information
*/
function handleTransactionError(txData: any): TxErrorInfo {
const failReason = txData.failReason || 'Unknown reason';
console.error(`Transaction failed with reason: ${failReason}`);
return {
error: 'TRANSACTION_FAILED',
message: failReason,
action: 'Try again or contact support'
};
}
// ======== Main Execution ========
async function simulateOnly(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
slippagePercent: string = '0.5'
): Promise {
try {
console.log('Starting simulation-only mode...');
console.log(`Simulation Details:`);
console.log(` From Token: ${fromTokenAddress}`);
console.log(` To Token: ${toTokenAddress}`);
console.log(` Amount: ${amount}`);
console.log(` SlippagePercent: ${slippagePercent}%`);
// Step 1: Get swap data
const swapData = await getSwapData(fromTokenAddress, toTokenAddress, amount, slippagePercent);
console.log('Swap data obtained');
// Step 2: Simulate transaction
const simulationResult = await simulateTransaction(swapData);
console.log('Transaction simulation completed');
// Step 3: Get gas limit
const gasLimit = await getGasLimit(
swapData.tx.from,
swapData.tx.to,
swapData.tx.value || '0',
swapData.tx.data
);
return {
success: true,
swapData,
simulationResult,
gasLimit,
estimatedGasUsed: simulationResult.gasUsed,
};
} catch (error) {
console.error('Simulation failed:', (error as Error).message);
return { success: false, error: (error as Error).message };
}
}
async function main() {
try {
console.log('EVM Swap Tools with Onchain Gateway API');
console.log('=====================================');
// Validate environment variables
if (!WALLET_ADDRESS || !PRIVATE_KEY) {
throw new Error('Missing wallet address or private key in environment variables');
}
console.log(`Wallet Address: ${WALLET_ADDRESS}`);
console.log(`Chain ID: ${chainIndex}`);
console.log(`RPC URL: ${process.env.EVM_RPC_URL || 'https://mainnet.base.org'}`);
// Parse command line arguments
const args = process.argv.slice(2);
const mode = args[0] || 'simulate'; // Default to simulate mode
// Example parameters
const fromToken = ETH_ADDRESS;
const toToken = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC on Base
const amount = '100000000000000'; // 0.0001 ETH in wei
const slippagePercent = '0.5'; // 0.5%
console.log('\nConfiguration:');
console.log(` From: ${fromToken} (ETH)`);
console.log(` To: ${toToken} (USDC)`);
console.log(` Amount: ${web3.utils.fromWei(amount, 'ether')} ETH`);
console.log(` SlippagePercent: ${slippagePercent}%`);
console.log(` Mode: ${mode}`);
let result;
switch (mode.toLowerCase()) {
case 'simulate':
case 'sim':
result = await simulateOnly(fromToken, toToken, amount, slippagePercent);
break;
case 'execute':
case 'exec':
result = await executeSwapWithSimulation(fromToken, toToken, amount, slippagePercent);
break;
default:
console.log('\nAvailable modes:');
console.log(' simulate/sim - Only simulate the transaction');
console.log(' execute/exec - Execute the full swap');
console.log('\nExample: npm run evm-swap simulate');
return;
}
if (result.success) {
console.log('\nOperation completed successfully!');
if (mode === 'simulate' || mode === 'sim') {
console.log(`Gas Limit: ${result.gasLimit}`);
} else {
console.log(`Transaction Hash: ${result.txHash}`);
}
} else {
console.log('\nOperation failed!');
console.log(`Error: ${result.error}`);
}
} catch (error) {
console.error('Main execution failed:', (error as Error).message);
process.exit(1);
}
}
// Run the script
if (require.main === module) {
main();
}
export {
executeSwap,
executeSwapWithSimulation,
simulateOnly,
getSwapData,
simulateTransaction,
getGasLimit,
broadcastTransaction,
trackTransaction
};
```
You can run this script using `evm-swap.ts sim` or `evm-swap.ts exec`.
`sim` simulates a transaction using swap data using the transaction simulation API and retruns `gasLimit` info
`exec` executes a transaction using the broadcast API
## 10. MEV Protection
### MEV Protection with Broadcast Transaction API
The OKX Broadcast Transaction API provides built-in MEV protection capabilities to help safeguard your transactions from front-running and sandwich attacks. The Broadcast API is available to our whitelisted customers only. Please reach out to dexapi@okx.com to request access.
**Disclaimer:** Your end-user's transaction can only be covered by the MEV protection feature if you actually utilise OKX Build's API services for that particular transaction. MEV protection is currently an experimental feature provided by third-parties and OKX Build does not guarantee the effectiveness and quality of such MEV protection.
#### Adding MEV Protection
To enable MEV protection on EVM chains, add the `extraData` field to your broadcast transaction request with `enableMevProtection: true`:
```tsx
/**
* Broadcast transaction with MEV protection enabled for EVM chains
*/
async function broadcastTransactionWithMEV(
signedTx: string,
chainIndex: string = "8453", // Base chain
walletAddress: string,
enableMevProtection: boolean = true
): Promise {
try {
console.log('Broadcasting transaction with MEV protection...');
const path = 'dex/pre-transaction/broadcast-transaction';
const url = `https://web3.okx.com/api/v6/${path}`;
const body = {
signedTx: signedTx,
chainIndex: chainIndex,
address: walletAddress,
extraData: JSON.stringify({
enableMevProtection: enableMevProtection
})
};
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
if (response.data.code === '0') {
const orderId = response.data.data[0].orderId;
console.log(`Transaction broadcast with MEV protection. Order ID: ${orderId}`);
return orderId;
} else {
throw new Error(`Broadcast API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('MEV-protected broadcast failed:', error);
throw error;
}
}
```
#### Usage Examples
**Basic swap without MEV protection:**
```tsx
// Standard broadcast (no MEV protection) for Base chain
const orderId = await broadcastTransaction(signedTx, "8453", walletAddress);
```
**Swap with MEV protection enabled:**
```tsx
// With MEV protection on Base chain
const orderId = await broadcastTransactionWithMEVOptions(signedTx, "8453", walletAddress, true);
```
#### Integration with Complete Swap Flow
Here's how to integrate MEV protection into your complete EVM swap execution:
```tsx
/**
* Execute EVM swap with MEV protection
*/
async function executeSwapWithMEVProtection(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
slippagePercent: string = '0.5',
enableMevProtection: boolean = true,
chainIndex: string = "8453" // Base chain
): Promise {
try {
// Step 1: Check allowance and approve if necessary (skip for native token)
if (fromTokenAddress !== ETH_ADDRESS) {
await approveToken(fromTokenAddress, amount);
}
// Step 2: Get swap transaction data
const swapData = await getSwapTransaction(fromTokenAddress, toTokenAddress, amount, WALLET_ADDRESS, slippagePercent);
const txData = swapData.tx;
console.log("Swap TX data received");
// Step 3: Get current gas price and nonce
const gasPrice = await web3.eth.getGasPrice();
const adjustedGasPrice = BigInt(gasPrice) * BigInt(15) / BigInt(10); // 1.5x for faster confirmation
const nonce = await web3.eth.getTransactionCount(WALLET_ADDRESS, 'latest');
// Step 4: Create and sign transaction object
const txObject = {
from: WALLET_ADDRESS,
to: txData.to,
data: txData.data,
value: txData.value || '0',
gas: '300000', // Default gas limit
gasPrice: adjustedGasPrice.toString(),
nonce: nonce
};
const signedTx = await web3.eth.accounts.signTransaction(txObject, PRIVATE_KEY);
console.log("Transaction signed");
// Step 5: Broadcast with MEV protection
const orderId = await broadcastTransactionWithMEVOptions(
signedTx.rawTransaction,
chainIndex,
WALLET_ADDRESS,
enableMevProtection
);
// Step 6: Track transaction
const result = await trackTransaction(orderId);
return result.txHash;
} catch (error) {
console.error("MEV-protected swap failed:", error);
throw error;
}
}
```
The MEV protection feature integrates seamlessly with your existing EVM and Solana swap implementation and provides an additional layer of security against MEV attacks across Solana, Base, Ethereum and BSC.
## Method 2: SDK approach
Using the OKX DEX SDK provides a much simpler developer experience while retaining all the functionality of the API-first approach. The SDK handles many implementation details for you, including retry logic, error handling, and transaction management.
## 1. Install the SDK
```typescript
npm install @okx-dex/okx-dex-sdk
# or
yarn add @okx-dex/okx-dex-sdk
# or
pnpm add @okx-dex/okx-dex-sdk
```
## 2. Setup Your Environment
Create a .env file with your API credentials and wallet information:
```typescript
# OKX API Credentials
OKX_API_KEY=your_api_key
OKX_SECRET_KEY=your_secret_key
OKX_API_PASSPHRASE=your_passphrase
# EVM Configuration
EVM_RPC_URL=your_evm_rpc_url
EVM_WALLET_ADDRESS=your_evm_wallet_address
EVM_PRIVATE_KEY=your_evm_private_key
```
## 3. Initialize the Client
Create a file for your DEX client (e.g., DexClient.ts):
```typescript
// DexClient.ts
import { OKXDexClient } from '@okx-dex/okx-dex-sdk';
import { createEVMWallet } from '@okx-dex/okx-dex-sdk/core/evm-wallet';
import { createWallet } from '@okx-dex/okx-dex-sdk/core/wallet';
import { Connection } from '@solana/web3.js';
import { ethers } from 'ethers';
import dotenv from 'dotenv';
dotenv.config();
// EVM setup (Ethereum, Base, Arbitrum, etc.)
const evmProvider = new ethers.JsonRpcProvider(process.env.EVM_RPC_URL!);
const evmWallet = createEVMWallet(process.env.EVM_PRIVATE_KEY!, evmProvider);
// Initialize the client
const client = new OKXDexClient({
// API credentials (get from OKX Developer Portal)
apiKey: process.env.OKX_API_KEY!,
secretKey: process.env.OKX_SECRET_KEY!,
apiPassphrase: process.env.OKX_API_PASSPHRASE!,
// EVM configuration (works for all EVM chains)
evm: {
wallet: evmWallet
},
})
```
## 4. Token Approval With the SDK
Create an approval utility function:
```typescript
// approval.ts
import { client } from './DexClient';
// Helper function to convert human-readable amounts to base units
export function toBaseUnits(amount: string, decimals: number): string {
// Remove any decimal point and count the decimal places
const [integerPart, decimalPart = ''] = amount.split('.');
const currentDecimals = decimalPart.length;
// Combine integer and decimal parts, removing the decimal point
let result = integerPart + decimalPart;
// Add zeros if you need more decimal places
if (currentDecimals < decimals) {
result = result + '0'.repeat(decimals - currentDecimals);
}
// Remove digits if you have too many decimal places
else if (currentDecimals > decimals) {
result = result.slice(0, result.length - (currentDecimals - decimals));
}
// Remove leading zeros
result = result.replace(/^0+/, '') || '0';
return result;
}
/**
* Example: Approve a token for swapping
*/
async function executeApproval(tokenAddress: string, amount: string) {
try {
// Get token information using quote
console.log("Getting token information...");
const tokenInfo = await client.dex.getQuote({
chainIndex: '8453', // Base Chain
fromTokenAddress: tokenAddress,
toTokenAddress: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', // Native token
amount: '1000000', // Use a reasonable amount for quote
slippagePercent: '0.5'
});
const tokenDecimals = parseInt(tokenInfo.data[0].fromToken.decimal);
const rawAmount = toBaseUnits(amount, tokenDecimals);
console.log(`\nApproval Details:`);
console.log(`--------------------`);
console.log(`Token: ${tokenInfo.data[0].fromToken.tokenSymbol}`);
console.log(`Amount: ${amount} ${tokenInfo.data[0].fromToken.tokenSymbol}`);
console.log(`Amount in base units: ${rawAmount}`);
// Execute the approval
console.log("\nExecuting approval...");
const result = await client.dex.executeApproval({
chainIndex: '8453', // Base Chain
tokenContractAddress: tokenAddress,
approveAmount: rawAmount
});
if ('alreadyApproved' in result) {
console.log("\nToken already approved for the requested amount!");
return { success: true, alreadyApproved: true };
} else {
console.log("\nApproval completed successfully!");
console.log("Transaction Hash:", result.transactionHash);
console.log("Explorer URL:", result.explorerUrl);
return result;
}
} catch (error) {
if (error instanceof Error) {
console.error('Error executing approval:', error.message);
}
throw error;
}
}
// Run if this file is executed directly
if (require.main === module) {
// Example usage: ts-node approval.ts 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 1000
const args = process.argv.slice(2);
if (args.length !== 2) {
console.log("Usage: ts-node approval.ts ");
console.log("\nExamples:");
console.log(" # Approve 1000 USDC");
console.log(` ts-node approval.ts 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 1000`);
process.exit(1);
}
const [tokenAddress, amount] = args;
executeApproval(tokenAddress, amount)
.then(() => process.exit(0))
.catch((error) => {
console.error('Error:', error);
process.exit(1);
});
}
export { executeApproval };
```
## 5. Execute a Swap With the SDK
Create a swap execution file:
```typescript
// swap.ts
import { client } from './DexClient';
/**
* Example: Execute a swap from ETH to USDC on Base chain
*/
async function executeSwap() {
try {
if (!process.env.EVM_PRIVATE_KEY) {
throw new Error('Missing EVM_PRIVATE_KEY in .env file');
}
// You can change this to any EVM chain
// For example, for Base, use chainIndex: '8453'
// For example, for baseSepolia, use chainIndex: '84532'
// You can also use SUI, use chainIndex: '784'
// When using another Chain, you need to change the fromTokenAddress and toTokenAddress to the correct addresses for that chain
const swapResult = await client.dex.executeSwap({
chainIndex: '8453', // Base chain ID
fromTokenAddress: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', // Native ETH
toTokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
amount: String(10 * 10 ** 14), // .0001 ETH
slippagePercent: '0.5', // 0.5% slippagePercent
userWalletAddress: process.env.EVM_WALLET_ADDRESS!
});
console.log('Swap executed successfully:');
console.log(JSON.stringify(swapResult, null, 2));
return swapResult;
} catch (error) {
if (error instanceof Error) {
console.error('Error executing swap:', error.message);
// API errors include details in the message
if (error.message.includes('API Error:')) {
const match = error.message.match(/API Error: (.*)/);
if (match) console.error('API Error Details:', match[1]);
}
}
throw error;
}
}
// Run if this file is executed directly
if (require.main === module) {
executeSwap()
.then(() => process.exit(0))
.catch((error) => {
console.error('Error:', error);
process.exit(1);
});
}
export { executeSwap };
```
## 6. Additional SDK Functionality
The SDK provides additional methods that simplify development:
Get a quote for a token pair
```typescript
const quote = await client.dex.getQuote({
chainIndex: '8453', // Base Chain
fromTokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC
toTokenAddress: '0x4200000000000000000000000000000000000006', // WETH
amount: '1000000', // 1 USDC (in smallest units)
slippagePercent: '0.5' // 0.5%
});
```
- [Build Swap Applications on Sui](https://web3.okx.com/onchainos/dev-docs/trade/dex-use-swap-sui-quick-start.md)
# Build Swap Applications on Sui
There are two approaches to building swap applications with OKX DEX on Sui:
- The API-first approach directly interacting with OKX DEX API endpoints
- The SDK approach using the @okx-dex/okx-dex-sdk package for a simplified developer experience
This guide covers both methods to help you choose the approach that best fits your needs.
## Method 1: API-first Approach
In this guide, we will provide a use case for Sui token exchange through the OKX DEX.
## 1. Set Up Your Environment
Import the necessary Node.js libraries and set up your environment variables:
```javascript
// Required libraries
import { SuiWallet } from "@okxweb3/coin-sui";
import { getFullnodeUrl, SuiClient } from '@mysten/sui/client';
import { Transaction } from '@mysten/sui/transactions';
import cryptoJS from "crypto-js";
// Install dependencies
// npm i @okxweb3/coin-sui
// npm i @mysten/sui
// npm i crypto-js
// Set up environment variables
const apiKey = 'your_api_key';
const secretKey = 'your_secret_key';
const apiPassphrase = 'your_passphrase';
const userAddress = 'your_sui_wallet_address';
const userPrivateKey = 'your_sui_wallet_private_key';
// Constants
const SUI_CHAIN_ID = "784";
const DEFAULT_GAS_BUDGET = 50000000;
const MAX_RETRIES = 3;
// Initialize Sui client
const wallet = new SuiWallet();
const client = new SuiClient({
url: getFullnodeUrl('mainnet')
});
// For Sui, you need to use the hexWithoutFlag format of your private key
// You can convert your key using sui keytool:
// sui keytool convert
```
## 2. Obtain Token Information and Swap Quote
First, create a utility function to handle API authentication headers:
```javascript
function getHeaders(timestamp, method, requestPath, queryString = "") {
if (!apiKey || !secretKey || !apiPassphrase ) {
throw new Error("Missing required environment variables");
}
const stringToSign = timestamp + method + requestPath + queryString;
return {
"Content-Type": "application/json",
"OK-ACCESS-KEY": apiKey,
"OK-ACCESS-SIGN": cryptoJS.enc.Base64.stringify(
cryptoJS.HmacSHA256(stringToSign, secretKey)
),
"OK-ACCESS-TIMESTAMP": timestamp,
"OK-ACCESS-PASSPHRASE": apiPassphrase,
};
}
```
Then, create a function to get token information:
```javascript
async function getTokenInfo(fromTokenAddress, toTokenAddress) {
const timestamp = new Date().toISOString();
const requestPath = "/api/v6/dex/aggregator/quote";
const params = {
chainIndex: SUI_CHAIN_ID,
fromTokenAddress,
toTokenAddress,
amount: "1000000",
slippagePercent: "0.5",// 0.5% slippagePercent
};
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, "GET", requestPath, queryString);
const response = await fetch(
`https://web3.okx.com${requestPath}${queryString}`,
{ method: "GET", headers }
);
if (!response.ok) {
throw new Error(`Failed to get quote: ${await response.text()}`);
}
const data = await response.json();
if (data.code !== "0" || !data.data?.[0]) {
throw new Error("Failed to get token information");
}
const quoteData = data.data[0];
return {
fromToken: {
symbol: quoteData.fromToken.tokenSymbol,
decimals: parseInt(quoteData.fromToken.decimal),
price: quoteData.fromToken.tokenUnitPrice
},
toToken: {
symbol: quoteData.toToken.tokenSymbol,
decimals: parseInt(quoteData.toToken.decimal),
price: quoteData.toToken.tokenUnitPrice
}
};
}
```
Create a function to convert human-readable amounts to base units:
```javascript
function convertAmount(amount, decimals) {
try {
if (!amount || isNaN(parseFloat(amount))) {
throw new Error("Invalid amount");
}
const value = parseFloat(amount);
if (value <= 0) {
throw new Error("Amount must be greater than 0");
}
return (BigInt(Math.floor(value * Math.pow(10, decimals)))).toString();
} catch (err) {
console.error("Amount conversion error:", err);
throw new Error("Invalid amount format");
}
}
```
## 3. Get Swap Data
3.1 Define swap parameters
```typescript
const swapParams = {
chainIndex: chainIndex,
fromTokenAddress,
toTokenAddress,
amount,
userWalletAddress: userAddress,
slippagePercent
};
```
3.2 Request swap transaction data
```typescript
/**
* Get swap transaction data from DEX API
* @param fromTokenAddress - Source token address
* @param toTokenAddress - Destination token address
* @param amount - Amount to swap
* @param userAddress - User wallet address
* @param slippagePercent - Maximum slippagePercent (e.g., "0.5" for 0.5%)
* @returns Swap transaction data
*/
async function getSwapTransaction(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
userAddress: string,
slippagePercent: string = '0.5'
): Promise {
try {
const path = 'dex/aggregator/swap';
const url = `${baseUrl}${path}`;
const params = {
chainIndex: chainIndex,
fromTokenAddress,
toTokenAddress,
amount,
userWalletAddress: userAddress,
slippagePercent
};
// Prepare authentication
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await axios.get(url, { params, headers });
if (response.data.code === '0') {
return response.data.data[0];
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get swap transaction data:', (error as Error).message);
throw error;
}
}
```
## 4. Simulate Transaction
Before executing the actual swap, it's crucial to simulate the transaction to ensure it will succeed and to identify any potential issues:
The Simulate API is available to our whitelisted customers only. Please reach out to dexapi@okx.com to request access.
```javascript
async function simulateTransaction(txData) {
try {
if (!txData) {
throw new Error('Invalid transaction data format');
}
const params = {
chainIndex: SUI_CHAIN_ID,
txData: txData,
includeDebug: true
};
const timestamp = new Date().toISOString();
const requestPath = "/api/v6/dex/pre-transaction/simulate";
const requestBody = JSON.stringify(params);
const headers = getHeaders(timestamp, "POST", requestPath, "", requestBody);
console.log('Simulating transaction...');
const response = await fetch(
`https://web3.okx.com${requestPath}`,
{
method: 'POST',
headers,
body: requestBody
}
);
const data = await response.json();
if (data.code !== "0") {
throw new Error(`Simulation failed: ${data.msg || "Unknown simulation error"}`);
}
const simulationResult = data.data[0];
// Check simulation success
if (simulationResult.success === false) {
console.error('Transaction simulation failed:', simulationResult.error);
throw new Error(`Transaction would fail: ${simulationResult.error}`);
}
console.log('Transaction simulation successful');
console.log(`Estimated gas used: ${simulationResult.gasUsed || 'N/A'}`);
if (simulationResult.logs) {
console.log('Simulation logs:', simulationResult.logs);
}
return simulationResult;
} catch (error) {
console.error("Error simulating transaction:", error);
throw error;
}
}
```
## 5. Execute the Transaction
First, prepare and sign the transaction:
```javascript
async function executeSwap(txData, privateKey) {
// Create transaction block
const txBlock = Transaction.from(txData);
txBlock.setSender(normalizedWalletAddress);
// Set gas parameters
const referenceGasPrice = await client.getReferenceGasPrice();
txBlock.setGasPrice(BigInt(referenceGasPrice));
txBlock.setGasBudget(BigInt(DEFAULT_GAS_BUDGET));
// Build and sign transaction
const builtTx = await txBlock.build({ client });
const txBytes = Buffer.from(builtTx).toString('base64');
const signedTx = await wallet.signTransaction({
privateKey,
data: {
type: 'raw',
data: txBytes
}
});
if (!signedTx?.signature) {
throw new Error("Failed to sign transaction");
}
return { builtTx, signature: signedTx.signature };
}
```
Then, send the transaction using RPC method calls
Using RPC:
```javascript
async function sendTransaction(builtTx, signature) {
// Execute transaction
const result = await client.executeTransactionBlock({
transactionBlock: builtTx,
signature: [signature],
options: {
showEffects: true,
showEvents: true,
}
});
// Wait for confirmation
const confirmation = await client.waitForTransaction({
digest: result.digest,
options: {
showEffects: true,
showEvents: true,
}
});
console.log("\nSwap completed successfully!");
console.log("Transaction ID:", result.digest);
console.log("Explorer URL:", `https://suiscan.xyz/mainnet/tx/${result.digest}`);
return result.digest;
}
```
## 6. Track Transaction
Track transaction using SWAP API:
SWAP API transaction tracking provides comprehensive swap execution details using the `/dex/aggregator/history` endpoint. It offers token-specific information (symbols, amounts), fees paid, and detailed blockchain data. Use this when you need complete swap insight with token-level details.
```javascript
async function trackTransactionWithSwapAPI(txHash) {
try {
const path = 'dex/aggregator/history';
const url = `${baseUrl}${path}`;
const params = {
chainIndex: SUI_CHAIN_ID,
txHash: txHash,
isFromMyProject: 'false'
};
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
console.log('Fetching transaction status...');
const response = await fetch(`${url}${queryString}`, { headers });
const data = await response.json();
if (!data) {
throw new Error('No response data received from API');
}
if (data.code !== '0') {
throw new Error(`API Error: ${data.msg || 'Unknown error'}`);
}
if (!data.data || !Array.isArray(data.data) || data.data.length === 0) {
console.log('Transaction not found in history yet, might be too recent');
return { status: 'pending', details: null };
}
const txData = data.data[0];
if (!txData) {
console.log('Transaction data not available yet');
return { status: 'pending', details: null };
}
const status = txData.status;
console.log(`Transaction status: ${status}`);
if (status === 'pending') {
console.log(`Transaction is still pending: ${txHash}`);
return { status: 'pending', details: txData };
} else if (status === 'success') {
console.log(`Transaction successful!`);
console.log(`From: ${txData.fromTokenDetails.symbol} - Amount: ${txData.fromTokenDetails.amount}`);
console.log(`To: ${txData.toTokenDetails.symbol} - Amount: ${txData.toTokenDetails.amount}`);
console.log(`Transaction Fee: ${txData.txFee}`);
console.log(`Explorer URL: https://suiscan.xyz/mainnet/tx/${txHash}`);
return { status: 'success', details: txData };
} else if (status === 'fail') {
const errorMsg = txData.errorMsg || 'Unknown reason';
console.error(`Transaction failed: ${errorMsg}`);
return { status: 'failure', details: txData, error: errorMsg };
}
return { status: 'unknown', details: txData };
} catch (error) {
console.error('Failed to track transaction status:', error.message);
return { status: 'pending', details: null, error: error.message };
}
}
```
## 7. Complete Implementation
Here's a complete implementation putting it all together:
```javascript
// swap.ts
import { SuiWallet } from "@okxweb3/coin-sui";
import { getFullnodeUrl, SuiClient } from '@mysten/sui/client';
import { Transaction } from '@mysten/sui/transactions';
import cryptoJS from "crypto-js";
import dotenv from 'dotenv';
dotenv.config();
// Environment variables
const apiKey = process.env.OKX_API_KEY;
const secretKey = process.env.OKX_SECRET_KEY;
const apiPassphrase = process.env.OKX_API_PASSPHRASE;
const userAddress = process.env.WALLET_ADDRESS;
const userPrivateKey = process.env.PRIVATE_KEY;
// Constants
const SUI_CHAIN_ID = "784";
const DEFAULT_GAS_BUDGET = 50000000;
const MAX_RETRIES = 3;
// Initialize clients
const wallet = new SuiWallet();
const client = new SuiClient({
url: getFullnodeUrl('mainnet')
});
// Normalize wallet address
const normalizedWalletAddress = userAddress;
function getHeaders(timestamp: string, method: string, requestPath: string, queryString: string = "", requestBody: string = "") {
if (!apiKey || !secretKey || !apiPassphrase) {
throw new Error("Missing required environment variables");
}
const stringToSign = timestamp + method + requestPath + queryString + requestBody;
return {
"Content-Type": "application/json",
"OK-ACCESS-KEY": apiKey,
"OK-ACCESS-SIGN": cryptoJS.enc.Base64.stringify(
cryptoJS.HmacSHA256(stringToSign, secretKey)
),
"OK-ACCESS-TIMESTAMP": timestamp,
"OK-ACCESS-PASSPHRASE": apiPassphrase,
};
}
async function getTokenInfo(fromTokenAddress: string, toTokenAddress: string) {
const timestamp = new Date().toISOString();
const requestPath = "/api/v6/dex/aggregator/quote";
const params = {
chainIndex: SUI_CHAIN_ID,
fromTokenAddress,
toTokenAddress,
amount: "1000000",
slippagePercent: "0.5",// 0.5% slippagePercent
};
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, "GET", requestPath, queryString);
const response = await fetch(
`https://web3.okx.com${requestPath}${queryString}`,
{ method: "GET", headers }
);
if (!response.ok) {
throw new Error(`Failed to get quote: ${await response.text()}`);
}
const data = await response.json();
if (data.code !== "0" || !data.data?.[0]) {
throw new Error("Failed to get token information");
}
const quoteData = data.data[0];
return {
fromToken: {
symbol: quoteData.fromToken.tokenSymbol,
decimals: parseInt(quoteData.fromToken.decimal),
price: quoteData.fromToken.tokenUnitPrice
},
toToken: {
symbol: quoteData.toToken.tokenSymbol,
decimals: parseInt(quoteData.toToken.decimal),
price: quoteData.toToken.tokenUnitPrice
}
};
}
function convertAmount(amount: string | number, decimals: number) {
try {
if (!amount || isNaN(parseFloat(amount.toString()))) {
throw new Error("Invalid amount");
}
const value = parseFloat(amount.toString());
if (value <= 0) {
throw new Error("Amount must be greater than 0");
}
return (BigInt(Math.floor(value * Math.pow(10, decimals)))).toString();
} catch (err) {
console.error("Amount conversion error:", err);
throw new Error("Invalid amount format");
}
}
async function trackTransactionWithSwapAPI(txHash: string) {
try {
const path = 'dex/aggregator/history';
const url = `https://web3.okx.com/api/v6/${path}`;
const params = {
chainIndex: SUI_CHAIN_ID,
txHash: txHash,
isFromMyProject: 'false'
};
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await fetch(`${url}${queryString}`, { headers });
const data = await response.json();
if (data.code !== '0') {
throw new Error(`API Error: ${data.msg || 'Unknown error'}`);
}
return data.data?.[0] || { status: 'pending' };
} catch (error) {
console.error('Failed to track transaction:', error);
return { status: 'error', error: error instanceof Error ? error.message : 'Unknown error' };
}
}
async function main() {
try {
const args = process.argv.slice(2);
if (args.length < 3) {
console.log("Usage: ts-node swap.ts ");
console.log("Example: ts-node swap.ts 1.5 0x2::sui::SUI 0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC");
process.exit(1);
}
const [amount, fromTokenAddress, toTokenAddress] = args;
if (!userPrivateKey || !userAddress) {
throw new Error("Private key or user address not found");
}
// Get token information
console.log("Getting token information...");
const tokenInfo = await getTokenInfo(fromTokenAddress, toTokenAddress);
console.log(`From: ${tokenInfo.fromToken.symbol} (${tokenInfo.fromToken.decimals} decimals)`);
console.log(`To: ${tokenInfo.toToken.symbol} (${tokenInfo.toToken.decimals} decimals)`);
// Convert amount using fetched decimals
const rawAmount = convertAmount(amount, tokenInfo.fromToken.decimals);
console.log(`Amount in ${tokenInfo.fromToken.symbol} base units:`, rawAmount);
// Get swap quote
const quoteParams = {
chainIndex: SUI_CHAIN_ID,
amount: rawAmount,
fromTokenAddress,
toTokenAddress,
slippagePercent: "0.5",// 0.5% slippagePercent
userWalletAddress: normalizedWalletAddress || "",
};
// Get swap data
const timestamp = new Date().toISOString();
const requestPath = "/api/v6/dex/aggregator/swap";
const queryString = "?" + new URLSearchParams(quoteParams).toString();
const headers = getHeaders(timestamp, "GET", requestPath, queryString);
console.log("Requesting swap quote...");
const response = await fetch(
`https://web3.okx.com${requestPath}${queryString}`,
{ method: "GET", headers }
);
const data = await response.json();
if (data.code !== "0") {
throw new Error(`API Error: ${data.msg}`);
}
const swapData = data.data[0];
// Show estimated output and price impact
const outputAmount = parseFloat(swapData.routerResult.toTokenAmount) / Math.pow(10, tokenInfo.toToken.decimals);
console.log("\nSwap Quote:");
console.log(`Input: ${amount} ${tokenInfo.fromToken.symbol} ($${(parseFloat(amount) * parseFloat(tokenInfo.fromToken.price)).toFixed(2)})`);
console.log(`Output: ${outputAmount.toFixed(tokenInfo.toToken.decimals)} ${tokenInfo.toToken.symbol} ($${(outputAmount * parseFloat(tokenInfo.toToken.price)).toFixed(2)})`);
if (swapData.priceImpactPercent) {
console.log(`Price Impact: ${swapData.priceImpactPercent}%`);
}
console.log("\nExecuting swap transaction...");
let retryCount = 0;
while (retryCount < MAX_RETRIES) {
try {
// Create transaction block
const txBlock = Transaction.from(swapData.tx.data);
if (!normalizedWalletAddress) {
throw new Error("Wallet address is not defined");
}
txBlock.setSender(normalizedWalletAddress);
// Set gas parameters
const referenceGasPrice = await client.getReferenceGasPrice();
txBlock.setGasPrice(BigInt(referenceGasPrice));
txBlock.setGasBudget(BigInt(DEFAULT_GAS_BUDGET));
// Build and sign transaction
const builtTx = await txBlock.build({ client });
const txBytes = Buffer.from(builtTx).toString('base64');
const signedTx = await wallet.signTransaction({
privateKey: userPrivateKey,
data: {
type: 'raw',
data: txBytes
}
});
if (!signedTx?.signature) {
throw new Error("Failed to sign transaction");
}
// Execute transaction
const result = await client.executeTransactionBlock({
transactionBlock: builtTx,
signature: [signedTx.signature],
options: {
showEffects: true,
showEvents: true,
}
});
// Wait for confirmation
const confirmation = await client.waitForTransaction({
digest: result.digest,
options: {
showEffects: true,
showEvents: true,
}
});
console.log("\nSwap completed successfully!");
console.log("Transaction ID:", result.digest);
console.log("Explorer URL:", `https://suiscan.xyz/mainnet/tx/${result.digest}`);
// Track transaction
const txStatus = await trackTransactionWithSwapAPI(result.digest);
console.log("Transaction Status:", txStatus);
process.exit(0);
} catch (error) {
console.error(`Attempt ${retryCount + 1} failed:`, error);
retryCount++;
if (retryCount === MAX_RETRIES) {
throw error;
}
await new Promise(resolve => setTimeout(resolve, 2000 * retryCount));
}
}
} catch (error) {
console.error("Error:", error instanceof Error ? error.message : "Unknown error");
process.exit(1);
}
}
if (require.main === module) {
main();
}
```
## Method 2: SDK Approach
Using the OKX DEX SDK provides a much simpler developer experience while retaining all the functionality of the API-first approach. The SDK handles many implementation details for you, including retry logic, error handling, and transaction management.
## 1. Install the SDK
```javascript
npm install @okx-dex/okx-dex-sdk
# or
yarn add @okx-dex/okx-dex-sdk
# or
pnpm add @okx-dex/okx-dex-sdk
```
## 2. Setup Your Environment
Create a .env file with your API credentials and wallet information:
```javascript
# OKX API Credentials
OKX_API_KEY=your_api_key
OKX_SECRET_KEY=your_secret_key
OKX_API_PASSPHRASE=your_passphrase
# Sui Configuration
SUI_WALLET_ADDRESS=your_sui_wallet_address
SUI_PRIVATE_KEY=your_sui_private_key
```
Remember that you need to use the hexWithoutFlag format of your SUI private key, which you can obtain using the SUI CLI:
```javascript
sui keytool convert
```
## 3. Initialize the Client
Create a file for your DEX client (e.g., DexClient.ts):
```javascript
// DexClient.ts
import { OKXDexClient } from '@okx-dex/okx-dex-sdk';
import 'dotenv/config';
// Validate environment variables
const requiredEnvVars = [
'OKX_API_KEY',
'OKX_SECRET_KEY',
'OKX_API_PASSPHRASE',
'SUI_WALLET_ADDRESS',
'SUI_PRIVATE_KEY'
];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
throw new Error(`Missing required environment variable: ${envVar}`);
}
}
// Initialize the client
export const client = new OKXDexClient({
apiKey: process.env.OKX_API_KEY!,
secretKey: process.env.OKX_SECRET_KEY!,
apiPassphrase: process.env.OKX_API_PASSPHRASE!,
sui: {
privateKey: process.env.SUI_PRIVATE_KEY!,
walletAddress: process.env.SUI_WALLET_ADDRESS!,
connection: {
rpcUrl: 'https://sui-mainnet.blockvision.org'
}
}
});
```
## 4. Create a Token Helper (Optional)
You can create a token list helper for easy reference:
```javascript
// Common tokens on Sui mainnet
export const TOKENS = {
SUI: "0x2::sui::SUI",
USDC: "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC"
} as const;
```
## 5. Execute a Swap With the SDK
Create a swap execution file:
```javascript
// swap.ts
import { client } from './DexClient';
import { TOKENS } from './Tokens'; // Optional, if you created the token helper
/**
* Example: Execute a swap from SUI to USDC
*/
async function executeSwap() {
try {
if (!process.env.SUI_PRIVATE_KEY) {
throw new Error('Missing SUI_PRIVATE_KEY in .env file');
}
// First, get token information using a quote
console.log("Getting token information...");
const fromTokenAddress = TOKENS.SUI; // Or use directly: "0x2::sui::SUI"
const toTokenAddress = TOKENS.USDC; // Or use directly: "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC"
const quote = await client.dex.getQuote({
chainIndex: '784', // Sui chain ID
fromTokenAddress,
toTokenAddress,
amount: '1000000', // Small amount for quote
slippagePercent: '0.5' // 0.5% slippagePercent
});
const tokenInfo = {
fromToken: {
symbol: quote.data[0].fromToken.tokenSymbol,
decimals: parseInt(quote.data[0].fromToken.decimal),
price: quote.data[0].fromToken.tokenUnitPrice
},
toToken: {
symbol: quote.data[0].toToken.tokenSymbol,
decimals: parseInt(quote.data[0].toToken.decimal),
price: quote.data[0].toToken.tokenUnitPrice
}
};
// Convert amount to base units
const humanReadableAmount = 1.5; // 1.5 SUI
const rawAmount = (humanReadableAmount * Math.pow(10, tokenInfo.fromToken.decimals)).toString();
console.log("\nSwap Details:");
console.log("--------------------");
console.log(`From: ${tokenInfo.fromToken.symbol}`);
console.log(`To: ${tokenInfo.toToken.symbol}`);
console.log(`Amount: ${humanReadableAmount} ${tokenInfo.fromToken.symbol}`);
console.log(`Amount in base units: ${rawAmount}`);
console.log(`Approximate USD value: $${(humanReadableAmount * parseFloat(tokenInfo.fromToken.price)).toFixed(2)}`);
// Execute the swap
console.log("\nExecuting swap...");
const swapResult = await client.dex.executeSwap({
chainIndex: '784', // Sui chain ID
fromTokenAddress,
toTokenAddress,
amount: rawAmount,
slippagePercent: '0.5', // 0.5% slippagePercent
userWalletAddress: process.env.SUI_WALLET_ADDRESS!
});
console.log('Swap executed successfully:');
console.log("\nTransaction ID:", swapResult.transactionId);
console.log("Explorer URL:", swapResult.explorerUrl);
if (swapResult.details) {
console.log("\nDetails:");
console.log(`Input: ${swapResult.details.fromToken.amount} ${swapResult.details.fromToken.symbol}`);
console.log(`Output: ${swapResult.details.toToken.amount} ${swapResult.details.toToken.symbol}`);
if (swapResult.details.priceImpact) {
console.log(`Price Impact: ${swapResult.details.priceImpact}%`);
}
}
return swapResult;
} catch (error) {
if (error instanceof Error) {
console.error('Error executing swap:', error.message);
// API errors include details in the message
if (error.message.includes('API Error:')) {
const match = error.message.match(/API Error: (.*)/);
if (match) console.error('API Error Details:', match[1]);
}
}
throw error;
}
}
// Run if this file is executed directly
if (require.main === module) {
executeSwap()
.then(() => process.exit(0))
.catch((error) => {
console.error('Error:', error);
process.exit(1);
});
}
export { executeSwap };
```
## 6. Additional SDK functionality
The SDK provides additional methods that simplify development:
Get a quote for a token pair
```javascript
const quote = await client.dex.getQuote({
chainIndex: '784', // Sui
fromTokenAddress: '0x2::sui::SUI', // SUI
toTokenAddress: '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC', // USDC
amount: '100000000', // In base units
slippagePercent: '0.5' // 0.5% slippagePercent
});
```
- [Build Swap Applications on Ton](https://web3.okx.com/onchainos/dev-docs/trade/dex-use-swap-ton-quick-start.md)
# Build Swap Applications on Ton
In this guide, we’ll provide an example token swap through OKX DEX, using Ton from the Ton network to purchase JETTON. The process is as follows:
1. Set up your environment
2. Request the /quote endpoint and get the quote data
3. Request the /swap endpoint send the swap transaction
## 1. Set Up Your Environment
```javascript
# --------------------- npm package ---------------------
npm install @ton/ton @ton/crypto @ton/core buffer @orbs-network/ton-access
```
```js
const cryptoJS = require('crypto-js'); // Import encryption modules for subsequent encryption calculations
const { TonClient, WalletContractV4, internal } = require("@ton/ton");
const { toNano, Cell } = require("@ton/core");
const { mnemonicToPrivateKey } = require("@ton/crypto");
const { getHttpEndpoint } = require("@orbs-network/ton-access");
// --------------------- environment variable ---------------------
const apiBaseUrl = 'https://web3.okx.com/api/v6/dex/aggregator';
const chainIndex = '607';
// Native token contract address
const fromTokenAddress = 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c';
// JETTON token contract address
const toTokenAddress = 'EQAQXlWJvGbbFfE8F3oS8s87lIgdovS455IsWFaRdmJetTon';
// your wallet address
const user = 'UQDoI2kiSNQZxxxxxxxxxxxx6lM2ZSxKkEw3k1'
const fromAmount = '1000000'
// user wallet private key
const privateKey = 'xxxxx';
// open api Secret key
const secretkey = 'xxxxx'
// Get the current time
const date = new Date();
// --------------------- util function ---------------------
function getAggregatorRequestUrl(methodName, queryParams) {
return apiBaseUrl + methodName + '?' + (new URLSearchParams(queryParams)).toString();
}
// Check https://web3.okx.com/zh-hans/web3/build/docs/waas/rest-authentication for api-key
const headersParams = {
'Content-Type': 'application/json',
// The api Key obtained from the previous application
'OK-ACCESS-KEY': 'xxxxx',
'OK-ACCESS-SIGN': cryptoJS.enc.Base64.stringify(
// The field order of headersParams should be consistent with the order of quoteParams.
// example : quote ==> cryptoJS.HmacSHA256(date.toISOString() + 'GET' + '/api/v6/dex/aggregator/quote?amount=1000000&chainIndex=607&toTokenAddress=EQAQXlWJvGbbFfE8F3oS8s87lIgdovS455IsWFaRdmJetTon&fromTokenAddress=EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', secretKey)
cryptoJS.HmacSHA256(date.toISOString() + 'GET' + '/api/v6/dex/aggregator/xxx/xxx/xxx', secretKey)
),
// Convert the current time to the desired format
'OK-ACCESS-TIMESTAMP': date.toISOString(),
// The password created when applying for the key
'OK-ACCESS-PASSPHRASE': 'xxxxxxx',
};
```
**Additional receiving addresses aren’t supported.**
## 2. Request the /quote Endpoint and Get the Quote Data
### 2.1 Define Quote Parameters
- Next, define the parameters to get basic information of the quote and the router list.
```js
const quoteParams = {
amount: fromAmount,
chainIndex: chainIndex,
toTokenAddress: toTokenAddress,
fromTokenAddress: fromTokenAddress,
};
```
### 2.2 Define helper functions
- Define helper functions to interact with the DEX API.
```js
const getQuote = async () => {
const apiRequestUrl = getAggregatorRequestUrl('/quote', quoteParams);
return fetch(apiRequestUrl, {
method: 'get',
headers: headersParams,
})
.then((res) => res.json())
.then((res) => {
return res;
});
};
```
## 3. Request the /swap Endpoint and Send the Transaction
### 3.1 Define Swap Parameters
- Next, define the parameters of the swap, and get the tx information.
```js
const swapParams = {
chainIndex: 1,
fromTokenAddress: 'fromTokenAddress',
toTokenAddress: 'toTokenAddress',
amount: '1000000',
slippagePercent: '0.5', // 0.5% slippagePercent
userWalletAddress: user
};
```
### 3.2 Define Helper Functions
Define helper functions to interact with the DEX API
```js
const getSwapData = async () => {
const apiRequestUrl = getAggregatorRequestUrl('/swap', swapParams);
return fetch(apiRequestUrl, {
method: 'get',
headers: headersParams,
})
.then((res) => res.json())
.then((res) => {
return res;
});
};
```
### 3.3 Request Swap Data and Send the Transaction
```js
let tx = {
"data": "te6cckEBBAEAwAABsA+KfqUAALgW1FkYQkBfXhAIAK3+NxydEq8Qc4csyQ7botOnBqxp3L54Fn7Zof9EjDx5ADoI2kiSNQZdnOIVsRSLrVMtiBySHg0Lt6lM2ZSxKkEwyC592wEBAZ8RMwAAAvrwgIALyzu3/eo7h8wFCa+0XsOg6z0IG/43fUuMnumWS8xS91AD0F/w35CTWUxTWRjefoV+400KRA2jX51X4ezIgmUUY/0AX5sDCAIBAQwDABgAAAABAAAAAAAAA+cKUcDO",
"from": "UQDoI2kiSNQZdnOIVsRSLrVMtiBySHg0Lt6lM2ZSxKkEw3k1",
"gas": "80234000",
"gasPrice": "5000",
"maxPriorityFeePerGas": "",
"minReceiveAmount": "25062412",
"to": "UQBXp1W7_UJWvsBrbaO8s-9i8O53s7hNNeZ0XqEEz12i0oDS",
"value": "440000000"
}
// This is the response of the /swap endpoint
async function sendTx() {
const endpoint = await getHttpEndpoint();
const client = new TonClient({ endpoint });
const mnemonic = ['range', 'xxxxxx']; // Your mnemonic words Decimal conversion
const keyPair = await mnemonicToPrivateKey(mnemonic);
const wallet = WalletContractV4.create({workchain: 0, publicKey: keyPair.publicKey});
const contract = client.open(wallet)
let seqno = await contract.getSeqno();
const body = Cell.fromBase64(tx.data);
const value = tx.value / Math.pow(10, 9); // Decimal conversion
const to = tx.to;
await contract.sendTransfer({
seqno,
secretKey: keyPair.secretKey,
messages: [internal({
value: toNano(value),
to,
body: body,
})]
});
}
```
- [DEX SDK ](https://web3.okx.com/onchainos/dev-docs/trade/dex-sdk-introduction.md)
# DEX SDK
The OKX DEX SDK is a typescript toolkit for developers to integrate OKX DEX API functionalities into their applications.
GitHub Repository https://github.com/okx/okx-dex-sdk
To get started, follow the guide here: https://github.com/okx/okx-dex-sdk?tab=readme-ov-file#usage
## Install the SDK
```bash
npm install @okx-dex/okx-dex-sdk
# or
yarn add @okx-dex/okx-dex-sdk
# or
pnpm add @okx-dex/okx-dex-sdk
```
## Setup Your Environment
Create a .env file with your API credentials and wallet information.
```bash
# OKX API Credentials
OKX_API_KEY=your_api_key
OKX_SECRET_KEY=your_secret_key
OKX_API_PASSPHRASE=your_passphrase
OKX_PROJECT_ID=your_project_id
# Solana Configuration
SOLANA_RPC_URL=your_solana_rpc_url
SOLANA_WALLET_ADDRESS=your_solana_wallet_address
SOLANA_PRIVATE_KEY=your_solana_private_key
# EVM Configuration
EVM_RPC_URL=your_evm_rpc_url
EVM_PRIVATE_KEY=your_evm_private_key
```
## Initialize the Client
Create a file for your DEX client (e.g., DexClient.ts):
```typescript
// example.ts or test.ts
import { OKXDexClient } from '@okx-dex/okx-dex-sdk';
import { Connection } from '@solana/web3.js';
import { createWallet } from '@okx-dex/okx-dex-sdk/core/wallet';
import 'dotenv/config';
import { createEVMWallet } from '@okx-dex/okx-dex-sdk/core/evm-wallet';
import { ethers } from 'ethers';
// Validate environment variables
const requiredEnvVars = [
'OKX_API_KEY',
'OKX_SECRET_KEY',
'OKX_API_PASSPHRASE',
'OKX_PROJECT_ID',
'SOLANA_RPC_URL',
'SOLANA_PRIVATE_KEY',
'EVM_RPC_URL',
'EVM_PRIVATE_KEY'
];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
throw new Error(`Missing required environment variable: ${envVar}`);
}
}
// Create Solana connection and wallet
const connection = new Connection(process.env.SOLANA_RPC_URL!);
const wallet = createWallet(process.env.SOLANA_PRIVATE_KEY!, connection);
// Create EVM provider and wallet
const provider = new ethers.JsonRpcProvider(process.env.EVM_RPC_URL!);
const evmWallet = createEVMWallet(process.env.EVM_PRIVATE_KEY!, provider);
// Initialize the client with both Solana and EVM support
export const client = new OKXDexClient({
apiKey: process.env.OKX_API_KEY!,
secretKey: process.env.OKX_SECRET_KEY!,
apiPassphrase: process.env.OKX_API_PASSPHRASE!,
projectId: process.env.OKX_PROJECT_ID!,
solana: {
wallet: wallet
},
evm: {
wallet: evmWallet
}
});
```
## Using the Client
Once initialized, you can use the client to make DEX API calls:
```typescript
async function main() {
try {
// Get tokens for Solana (chainIndex: 501)
const tokens = await client.dex.getTokens("501");
console.log('Supported tokens:', JSON.stringify(tokens, null, 2));
// Get tokens for Ethereum (chainIndex: 1)
const ethTokens = await client.dex.getTokens("1");
console.log('Ethereum tokens:', JSON.stringify(ethTokens, null, 2));
} catch (error) {
console.error('Error:', error);
}
}
main();
```
- [EVM Example](https://web3.okx.com/onchainos/dev-docs/trade/dex-sdk-evm.md)
# EVM Example
## Create an Approval
```javaScript
// approval.ts
import { client } from './DexClient';
// Helper function to convert human-readable amounts to base units
export function toBaseUnits(amount: string, decimals: number): string {
// Remove any decimal point and count the decimal places
const [integerPart, decimalPart = ''] = amount.split('.');
const currentDecimals = decimalPart.length;
// Combine integer and decimal parts, removing the decimal point
let result = integerPart + decimalPart;
// Add zeros if we need more decimal places
if (currentDecimals < decimals) {
result = result + '0'.repeat(decimals - currentDecimals);
}
// Remove digits if we have too many decimal places
else if (currentDecimals > decimals) {
result = result.slice(0, result.length - (currentDecimals - decimals));
}
// Remove leading zeros
result = result.replace(/^0+/, '') || '0';
return result;
}
/**
* Example: Approve a token for swapping
*/
async function executeApproval(tokenAddress: string, amount: string) {
try {
// Get token information using quote
console.log("Getting token information...");
const tokenInfo = await client.dex.getQuote({
chainIndex: '8453', // Base Chain
fromTokenAddress: tokenAddress,
toTokenAddress: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', // Native token
amount: '1000000', // Use a reasonable amount for quote
slippagePercent: '0.5'// 0.5% slippagePercent
});
const tokenDecimals = parseInt(tokenInfo.data[0].fromToken.decimal);
const rawAmount = toBaseUnits(amount, tokenDecimals);
console.log(`\nApproval Details:`);
console.log(`--------------------`);
console.log(`Token: ${tokenInfo.data[0].fromToken.tokenSymbol}`);
console.log(`Amount: ${amount} ${tokenInfo.data[0].fromToken.tokenSymbol}`);
console.log(`Amount in base units: ${rawAmount}`);
// Execute the approval
console.log("\nExecuting approval...");
const result = await client.dex.executeApproval({
chainIndex: '8453', // Base Chain
tokenContractAddress: tokenAddress,
approveAmount: rawAmount
});
if ('alreadyApproved' in result) {
console.log("\nToken already approved for the requested amount!");
return { success: true, alreadyApproved: true };
} else {
console.log("\nApproval completed successfully!");
console.log("Transaction Hash:", result.transactionHash);
console.log("Explorer URL:", result.explorerUrl);
return result;
}
} catch (error) {
if (error instanceof Error) {
console.error('Error executing approval:', error.message);
}
throw error;
}
}
// Run if this file is executed directly
if (require.main === module) {
// Example usage: ts-node approval.ts 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 1000
const args = process.argv.slice(2);
if (args.length !== 2) {
console.log("Usage: ts-node approval.ts ");
console.log("\nExamples:");
console.log(" # Approve 1000 USDC");
console.log(` ts-node approval.ts 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 1000`);
process.exit(1);
}
const [tokenAddress, amount] = args;
executeApproval(tokenAddress, amount)
.then(() => process.exit(0))
.catch((error) => {
console.error('Error:', error);
process.exit(1);
});
}
export { executeApproval };
```
## Create a Swap
```javaScript
// swap.ts
import { client } from './DexClient';
/**
* Example: Execute a swap from ETH to USDC on Base chain
*/
async function executeSwap() {
try {
if (!process.env.EVM_PRIVATE_KEY) {
throw new Error('Missing EVM_PRIVATE_KEY in .env file');
}
// You can change this to any EVM chain
// For example, for Base, use chainIndex: '8453'
// For example, for baseSepolia, use chainIndex: '84532'
// You can also use SUI, use chainIndex: '784'
// When using another Chain, you need to change the fromTokenAddress and toTokenAddress to the correct addresses for that chain
const swapResult = await client.dex.executeSwap({
chainIndex: '8453', // Base chain ID
fromTokenAddress: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', // Native ETH
toTokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
amount: String(10 * 10 ** 14), // .0001 ETH
slippagePercent: '0.5', // 0.5% slippagePercent
userWalletAddress: process.env.EVM_WALLET_ADDRESS!
});
console.log('Swap executed successfully:');
console.log(JSON.stringify(swapResult, null, 2));
return swapResult;
} catch (error) {
if (error instanceof Error) {
console.error('Error executing swap:', error.message);
// API errors include details in the message
if (error.message.includes('API Error:')) {
const match = error.message.match(/API Error: (.*)/);
if (match) console.error('API Error Details:', match[1]);
}
}
throw error;
}
}
// Run if this file is executed directly
if (require.main === module) {
executeSwap()
.then(() => process.exit(0))
.catch((error) => {
console.error('Error:', error);
process.exit(1);
});
}
export { executeSwap };
```
## Get a Quote
```javaScript
const quote = await client.dex.getQuote({
chainIndex: '8453', // Base Chain
fromTokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC
toTokenAddress: '0x4200000000000000000000000000000000000006', // WETH
amount: '1000000', // 1 USDC (in smallest units)
slippagePercent: '0.5' // 0.5% slippagePercent
});
```
- [Solana Example](https://web3.okx.com/onchainos/dev-docs/trade/dex-sdk-solana.md)
# Solana Example
## Create a Swap
```javaScript
// swap.ts
import { client } from './DexClient';
/**
* Example: Execute a swap from SOL to USDC
*/
async function executeSwap() {
try {
if (!process.env.SOLANA_PRIVATE_KEY) {
throw new Error('Missing SOLANA_PRIVATE_KEY in .env file');
}
// Get quote to fetch token information
console.log("Getting token information...");
const quote = await client.dex.getQuote({
chainIndex: '501',
fromTokenAddress: '11111111111111111111111111111111', // SOL
toTokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
amount: '1000000', // Small amount for quote
slippagePercent: '0.5' // 0.5% slippagePercent
});
const tokenInfo = {
fromToken: {
symbol: quote.data[0].fromToken.tokenSymbol,
decimals: parseInt(quote.data[0].fromToken.decimal),
price: quote.data[0].fromToken.tokenUnitPrice
},
toToken: {
symbol: quote.data[0].toToken.tokenSymbol,
decimals: parseInt(quote.data[0].toToken.decimal),
price: quote.data[0].toToken.tokenUnitPrice
}
};
// Convert amount to base units (for display purposes)
const humanReadableAmount = 0.1; // 0.1 SOL
const rawAmount = (humanReadableAmount * Math.pow(10, tokenInfo.fromToken.decimals)).toString();
console.log("\nSwap Details:");
console.log("--------------------");
console.log(`From: ${tokenInfo.fromToken.symbol}`);
console.log(`To: ${tokenInfo.toToken.symbol}`);
console.log(`Amount: ${humanReadableAmount} ${tokenInfo.fromToken.symbol}`);
console.log(`Amount in base units: ${rawAmount}`);
console.log(`Approximate USD value: $${(humanReadableAmount * parseFloat(tokenInfo.fromToken.price)).toFixed(2)}`);
// Execute the swap
console.log("\nExecuting swap...");
const swapResult = await client.dex.executeSwap({
chainIndex: '501', // Solana chain ID
fromTokenAddress: '11111111111111111111111111111111', // SOL
toTokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
amount: rawAmount,
slippagePercent: '0.5', // 0.5% slippagePercent
userWalletAddress: process.env.SOLANA_WALLET_ADDRESS!
});
console.log('Swap executed successfully:');
console.log(JSON.stringify(swapResult, null, 2));
return swapResult;
} catch (error) {
if (error instanceof Error) {
console.error('Error executing swap:', error.message);
// API errors include details in the message
if (error.message.includes('API Error:')) {
const match = error.message.match(/API Error: (.*)/);
if (match) console.error('API Error Details:', match[1]);
}
}
throw error;
}
}
// Run if this file is executed directly
if (require.main === module) {
executeSwap()
.then(() => process.exit(0))
.catch((error) => {
console.error('Error:', error);
process.exit(1);
});
}
export { executeSwap };
```
## Get a Quote
```javaScript
const quote = await client.dex.getQuote({
chainIndex: '501', // Solana
fromTokenAddress: '11111111111111111111111111111111', // SOL
toTokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
amount: '100000000', // 0.1 SOL (in lamports)
slippagePercent: '0.5' // 0.5% slippagePercent
});
```
## Swap-Instructions Execution
Import the necessary libraries:
```javaScript
// Required Solana dependencies for DEX interaction
import {
Connection, // Handles RPC connections to Solana network
Keypair, // Manages wallet keypairs for signing
PublicKey, // Handles Solana public key conversion and validation
TransactionInstruction, // Core transaction instruction type
TransactionMessage, // Builds transaction messages (v0 format)
VersionedTransaction, // Supports newer transaction format with lookup tables
RpcResponseAndContext, // RPC response wrapper type
SimulatedTransactionResponse, // Simulation result type
AddressLookupTableAccount, // For transaction size optimization
PublicKeyInitData // Public key input type
} from "@solana/web3.js";
import base58 from "bs58"; // Required for private key decoding
```
Initialize your connection and wallet:
```javaScript
// Note: Consider using a reliable RPC endpoint with high rate limits for production
const connection = new Connection(
process.env.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com"
);
// Initialize wallet for signing
// This wallet will be the fee payer and transaction signer
const wallet = Keypair.fromSecretKey(
Uint8Array.from(base58.decode(userPrivateKey))
);
```
Set up the parameters for your swap:
```javaScript
// Configure swap parameters
const baseUrl = "https://web3.okx.com/api/v6/dex/aggregator/swap-instruction";
const params = {
chainIndex: "501", // Solana mainnet chain ID
feePercent: "1", // Platform fee percentage
amount: "1000000", // Amount in smallest denomination (lamports for SOL)
fromTokenAddress: "11111111111111111111111111111111", // SOL mint address
toTokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC mint address
slippagePercent: "0.5", // slippagePercent tolerance 0.5%
userWalletAddress: userAddress, // Wallet performing the swap
autoSlippage: "false", // Use fixed slippage instead of auto
pathNum: "3" // Maximum routes to consider
};
```
## Process the Swap Instructions:
```javaScript
// Helper function to convert DEX API instructions to Solana format
function createTransactionInstruction(instruction) {
return new TransactionInstruction({
programId: new PublicKey(instruction.programId), // DEX program ID
keys: instruction.accounts.map((key) => ({
pubkey: new PublicKey(key.pubkey), // Account address
isSigner: key.isSigner, // True if account must sign tx
isWritable: key.isWritable // True if instruction modifies account
})),
data: Buffer.from(instruction.data, 'base64') // Instruction parameters
});
}
// Fetch optimal swap route and instructions from DEX
const timestamp = new Date().toISOString();
const requestPath = "/api/v6/dex/aggregator/swap-instruction";
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, "GET", requestPath, queryString);
const response = await fetch(
`https://web3.okx.com${requestPath}${queryString}`,
{ method: 'GET', headers }
);
const { data } = await response.json();
const { instructionLists, addressLookupTableAccount } = data;
// Process DEX instructions into Solana-compatible format
const instructions = [];
// Remove duplicate lookup table addresses returned by DEX
const uniqueLookupTables = Array.from(new Set(addressLookupTableAccount));
console.log("Lookup tables to load:", uniqueLookupTables);
// Convert each DEX instruction to Solana format
if (instructionLists?.length) {
instructions.push(...instructionLists.map(createTransactionInstruction));
}
```
## Handle Address Lookup Tables
```javaScript
// Process lookup tables for transaction optimization
// Lookup tables are crucial for complex swaps that interact with many accounts
// They significantly reduce transaction size and cost
const addressLookupTableAccounts = [];
if (uniqueLookupTables?.length > 0) {
console.log("Loading address lookup tables...");
// Fetch all lookup tables in parallel for better performance
const lookupTableAccounts = await Promise.all(
uniqueLookupTables.map(async (address) => {
const pubkey = new PublicKey(address);
// Get lookup table account data from Solana
const account = await connection
.getAddressLookupTable(pubkey)
.then((res) => res.value);
if (!account) {
throw new Error(`Could not fetch lookup table account ${address}`);
}
return account;
})
);
addressLookupTableAccounts.push(...lookupTableAccounts);
}
```
## Create and Sign Transaction
```javaScript
// Get recent blockhash for transaction timing and uniqueness
const latestBlockhash = await connection.getLatestBlockhash('finalized');
// Create versioned transaction message (V0 format required for lookup table support)
const messageV0 = new TransactionMessage({
payerKey: wallet.publicKey, // Fee payer address
recentBlockhash: latestBlockhash.blockhash, // Transaction timing
instructions // Swap instructions from DEX
}).compileToV0Message(addressLookupTableAccounts); // Include lookup tables
// Create new versioned transaction with optimizations
const transaction = new VersionedTransaction(messageV0);
// Simulate transaction to check for errors
// This helps catch issues before paying fees
const result = await connection.simulateTransaction(transaction);
// Sign transaction with fee payer wallet
transaction.sign([wallet]);
```
## Execute Transaction
```javaScript
// Send transaction to Solana
// skipPreflight=false ensures additional validation
// maxRetries helps handle network issues
const txId = await connection.sendRawTransaction(transaction.serialize(), {
skipPreflight: false, // Run preflight validation
maxRetries: 5 // Retry on failure
});
// Log transaction results
console.log("Transaction ID:", txId);
console.log("Explorer URL:", `https://solscan.io/tx/${txId}`);
// Wait for confirmation
await connection.confirmTransaction({
signature: txId,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight
});
console.log("Transaction confirmed!");
```
- [Sui Example](https://web3.okx.com/onchainos/dev-docs/trade/dex-sdk-sui.md)
# Sui Example
## Create a Token Helper (Optional)
```javaScript
// Common tokens on Sui mainnet
export const TOKENS = {
SUI: "0x2::sui::SUI",
USDC: "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC"
} as const;
```
## Create a Swap
```javaScript
// swap.ts
import { client } from './DexClient';
import { TOKENS } from './Tokens'; // Optional, if you created the token helper
/**
Example: Execute a swap from SUI to USDC
*/
async function executeSwap() {
try {
if (!process.env.SUI_PRIVATE_KEY) {
throw new Error('Missing SUI_PRIVATE_KEY in .env file');
}
// First, get token information using a quote
console.log("Getting token information...");
const fromTokenAddress = TOKENS.SUI; // Or use directly: "0x2::sui::SUI"
const toTokenAddress = TOKENS.USDC; // Or use directly: "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC"
const quote = await client.dex.getQuote({
chainIndex: '784', // Sui chain ID
fromTokenAddress,
toTokenAddress,
amount: '1000000', // Small amount for quote
slippagePercent: '0.5' // 0.5% slippagePercent
});
const tokenInfo = {
fromToken: {
symbol: quote.data[0].fromToken.tokenSymbol,
decimals: parseInt(quote.data[0].fromToken.decimal),
price: quote.data[0].fromToken.tokenUnitPrice
},
toToken: {
symbol: quote.data[0].toToken.tokenSymbol,
decimals: parseInt(quote.data[0].toToken.decimal),
price: quote.data[0].toToken.tokenUnitPrice
}
};
// Convert amount to base units
const humanReadableAmount = 1.5; // 1.5 SUI
const rawAmount = (humanReadableAmount * Math.pow(10, tokenInfo.fromToken.decimals)).toString();
console.log("\nSwap Details:");
console.log("--------------------");
console.log(
From: ${tokenInfo.fromToken.symbol}
);
console.log(
To: ${tokenInfo.toToken.symbol}
);
console.log(
Amount: ${humanReadableAmount} ${tokenInfo.fromToken.symbol}
);
console.log(
Amount in base units: ${rawAmount}
);
console.log(
Approximate USD value: $${(humanReadableAmount * parseFloat(tokenInfo.fromToken.price)).toFixed(2)}
);
// Execute the swap
console.log("\nExecuting swap...");
const swapResult = await client.dex.executeSwap({
chainIndex: '784', // Sui chain ID
fromTokenAddress,
toTokenAddress,
amount: rawAmount,
slippagePercent: '0.5', // 0.5% slippagePercent
userWalletAddress: process.env.SUI_WALLET_ADDRESS!
});
console.log('Swap executed successfully:');
console.log("\nTransaction ID:", swapResult.transactionId);
console.log("Explorer URL:", swapResult.explorerUrl);
if (swapResult.details) {
console.log("\nDetails:");
console.log(
Input: ${swapResult.details.fromToken.amount} ${swapResult.details.fromToken.symbol}
);
console.log(
Output: ${swapResult.details.toToken.amount} ${swapResult.details.toToken.symbol}
);
if (swapResult.details.priceImpact) {
console.log(
Price Impact: ${swapResult.details.priceImpact}%
);
}
}
return swapResult;
} catch (error) {
if (error instanceof Error) {
console.error('Error executing swap:', error.message);
// API errors include details in the message
if (error.message.includes('API Error:')) {
const match = error.message.match(/API Error: (.*)/);
if (match) console.error('API Error Details:', match[1]);
}
}
throw error;
}
}
// Run if this file is executed directly
if (require.main === module) {
executeSwap()
.then(() => process.exit(0))
.catch((error) => {
console.error('Error:', error);
process.exit(1);
});
}
export { executeSwap };
```
## Get a quote
```javaScript
const quote = await client.dex.getQuote({
chainIndex: '784', // Sui
fromTokenAddress: '0x2::sui::SUI', // SUI
toTokenAddress: '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC', // USDC
amount: '100000000', // In base units
slippagePercent: '0.5' // 0.5% slippagePercent
});
```
- [Introduction](https://web3.okx.com/onchainos/dev-docs/trade/dex-swap-api-introduction.md)
# Introduction
OKX DEX is an aggregator of various decentralized exchanges (also known as DEXs, such as Uniswap, Curve, Balancer, etc.) on different blockchains, allowing multi-chain and cross-chain trading. Our goal is to provide users with the best possible prices through intelligent routing.
|OKX DEX inquiry and transaction process|
|:-|
||
- Comprehensively calculate price, slippage, and transaction costs
- Select the best quote for users based on a comprehensive comparison of quotes from various DEXs and PMMs through the smart order splitting algorithm
| OKX DEX Swap trading process |
|:--------------------------------------------|
|  |
1. Get information of the supported networks through /supported/chain.
2. Get information of the supported tokens through /aggregator/all-tokens.
3. Build the request for /quote data based on information of the supported networks and tokens.
4. Following the quote request, obtain the user’s authorization to allow the OKX DEX router to perform asset operations on their wallet.
5. Build the request for /approve-transaction to get the user’s wallet authorization.
6. Build /swap information based on the returned quote router data and obtain the transaction data required for the swap.
7. Broadcast the returned swap transaction information to the blockchain.
- [API Reference](https://web3.okx.com/onchainos/dev-docs/trade/dex-api-reference.md)
# API Reference
- [Get Supported Chains](https://web3.okx.com/onchainos/dev-docs/trade/dex-get-aggregator-supported-chains.md)
{/* api-page */}
# Get Supported Chains
Retrieve information on chains supported for single-chain exchanges. The request returns supported target chains for cross-chain transactions.
### Request URL
GET `https://web3.okx.com/api/v6/dex/aggregator/supported/chain`
## Request Parameters
| Parameter | Type | Required | Description |
|---------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------|
| chainIndex | String | No | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
## Response Parameters
| Parameter | Type | Description |
|-----------------------|---------|----------------------------------------------------------------------------------------------------------------------------|
| chainIndex | String | Unique identifier for the chain. |
| chainName | String | Chain name (e.g., `Optimism`). |
| dexTokenApproveAddress| String | DEX authorization contract address; if no authorization has been made, this field will be empty. |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/aggregator/supported/chain?chainIndex=1' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code":"0",
"data":[
{
"chainIndex":"1",
"chainName":"Ethereum",
"dexTokenApproveAddress": "0x40aA958dd87FC8305b97f2BA922CDdCa374bcD7f"
},
],
"msg":""
}
```
- [Get Tokens](https://web3.okx.com/onchainos/dev-docs/trade/dex-get-tokens.md)
{/* api-page */}
# Get Tokens
It fetches a list of tokens. This interface returns a list of tokens that belong to major platforms or are deemed significant enough by OKX. However, you can still quote and swap other tokens outside of this list on OKX DEX.
### Request URL
GET `https://web3.okx.com/api/v6/dex/aggregator/all-tokens`
## Request Parameters
| Parameter | Type | Required | Description |
|-----------|--------|----------|--------------------------------------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
## Response Parameters
| Parameter | Type | Description |
|----------------------|--------|--------------------------|
| decimals | String | The precision of tokens (e.g., `18`) |
| tokenContractAddress | String | Token contract address (e.g., `0x382bb369d343125bfb2117af9c149795c6c65c50`) |
| tokenLogoUrl | String | Token logo (e.g., `https://static.okx.com/cdn/wallet/logo/USDT-991ffed9-e495-4d1b-80c2-a4c5f96ce22d.png`) |
| tokenName | String | Token full name (e.g., `Tether`) |
| tokenSymbol | String | Token symbol (e.g., `USDT`) |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/aggregator/all-tokens?chainIndex=1' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"decimals": "18",
"tokenContractAddress": "0x382bb369d343125bfb2117af9c149795c6c65c50",
"tokenLogoUrl": "https://static.okx.com/cdn/wallet/logo/USDT-991ffed9-e495-4d1b-80c2-a4c5f96ce22d.png",
"tokenName": "Tether",
"tokenSymbol": "USDT"
},
{
"decimals": "18",
"tokenContractAddress": "0xc946daf81b08146b1c7a8da2a851ddf2b3eaaf85",
"tokenLogoUrl": "https://static.okx.com/cdn/explorer/okexchain/exchain_usdc.png",
"tokenName": "USD Coin",
"tokenSymbol": "USDC"
},
{
"decimals": "18",
"tokenContractAddress": "0xdf54b6c6195ea4d948d03bfd818d365cf175cfc2",
"tokenLogoUrl": "https://static.okx.com/cdn/wallet/logo/okb.png",
"tokenName": "OKB",
"tokenSymbol": "OKB"
},
{
"decimals": "18",
"tokenContractAddress": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
"tokenLogoUrl": "https://static.okx.com/cdn/wallet/logo/okt.png",
"tokenName": "OKTC",
"tokenSymbol": "OKT"
},
{
"decimals": "18",
"tokenContractAddress": "0x218c3c3d49d0e7b37aff0d8bb079de36ae61a4c0",
"tokenLogoUrl": "https://static.okx.com/cdn/wallet/logo/BNB-20220308.png",
"tokenName": "Binance Coin",
"tokenSymbol": "BNB"
},
{
"decimals": "18",
"tokenContractAddress": "0x332730a4f6e03d9c55829435f10360e13cfa41ff",
"tokenLogoUrl": "https://static.okx.com/cdn/wallet/logo/BUSD-20220308.png",
"tokenName": "Binance USD",
"tokenSymbol": "BUSD"
},
{
"decimals": "18",
"tokenContractAddress": "0xdcac52e001f5bd413aa6ea83956438f29098166b",
"tokenLogoUrl": "https://static.okx.com/cdn/wallet/logo/eth_usdk.png",
"tokenName": "USDK",
"tokenSymbol": "USDK"
}
],
"msg": ""
}
```
[//]: # ()
[//]: # (//移动端页面布局(注释迁移时忽略))
[//]: # ()
[//]: # (//下一行填写 API名称)
[//]: # (# 获取币种列表)
[//]: # ()
[//]: # (获取欧易DEX聚合器协议支持兑换的币种列表)
[//]: # ()
[//]: # ()
[//]: # ()
[//]: # ()
[//]: # (## 请求地址)
[//]: # (「GET」https://web3.okx.com/api/v6/dex/aggregator/all-tokens)
[//]: # ()
[//]: # (## 请求参数)
[//]: # (// 下方填写请求参数表格(如果存在path 和 query 则分别分点描述))
[//]: # ()
[//]: # (| 参数名 | 类型 | 是否必须 | 描述 |)
[//]: # (|---------|--------|------|---------------------------------|)
[//]: # (| chainIndex | String | 是 | 链 ID (如`1`: Ethereum,更多可查看数据字典) |)
[//]: # ()
[//]: # ()
[//]: # ()
[//]: # (## 请求示例)
[//]: # ()
[//]: # ()
[//]: # (//下方填写shell 代码)
[//]: # ()
[//]: # (```shell)
[//]: # (curl --location --request GET 'https://web3.okx.com/api/v6/dex/aggregator/all-tokens?chainIndex=1')
[//]: # (```)
[//]: # ()
[//]: # ()
[//]: # (//下方填写java 代码)
[//]: # ()
[//]: # ()
[//]: # ()
[//]: # (//此区域可写其他补充性内容)
[//]: # ()
[//]: # ()
[//]: # (## 响应参数)
[//]: # (// 下方填写响应参数表格)
[//]: # ()
[//]: # ()
[//]: # (## 响应示例)
[//]: # ()
[//]: # ()
[//]: # ()
[//]: # ()
[//]: # (```json)
[//]: # ({)
[//]: # ( "code": "0",)
[//]: # ( "data": [)
[//]: # ( {)
[//]: # ( "decimals": 18,)
[//]: # ( "tokenContractAddress": "0xdf54b6c6195ea4d948d03bfd818d365cf175cfc2",)
[//]: # ( "tokenLogoUrl": "https://static.coinall.ltd/cdn/wallet/logo/okb.png",)
[//]: # ( "tokenName": "OKB",)
[//]: # ( "tokenSymbol": "OKB")
[//]: # ( },)
[//]: # ( {)
[//]: # ( "decimals": 6,)
[//]: # ( "tokenContractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",)
[//]: # ( "tokenLogoUrl": "https://static.coinall.ltd/cdn/wallet/logo/usdt.png",)
[//]: # ( "tokenName": "Tether",)
[//]: # ( "tokenSymbol": "USDT")
[//]: # ( },)
[//]: # ( {)
[//]: # ( "decimals": 6,)
[//]: # ( "tokenContractAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",)
[//]: # ( "tokenLogoUrl": "https://static.oklink.com/cdn/explorer/okexchain/exchain_usdc.png",)
[//]: # ( "tokenName": "USD Coin",)
[//]: # ( "tokenSymbol": "USDC")
[//]: # ( },)
[//]: # ( {)
[//]: # ( "decimals": 18,)
[//]: # ( "tokenContractAddress": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",)
[//]: # ( "tokenLogoUrl": "https://static.coinall.ltd/cdn/wallet/logo/eth01.png",)
[//]: # ( "tokenName": "Ethereum",)
[//]: # ( "tokenSymbol": "ETH")
[//]: # ( })
[//]: # ( ],)
[//]: # ( "msg": "")
[//]: # (})
[//]: # (```)
[//]: # ()
[//]: # ()
[//]: # ()
[//]: # (//此区域可写其他补充性内容)
[//]: # ()
[//]: # ()
[//]: # ()
[//]: # ()
- [Get Liquidity Sources](https://web3.okx.com/onchainos/dev-docs/trade/dex-get-liquidity.md)
{/* api-page */}
# Get Liquidity Sources
Get a list of liquidity that are available for swap in the OKX aggregation protocol.
### Request URL
GET `https://web3.okx.com/api/v6/dex/aggregator/get-liquidity`
## Request Parameters
| Parameter | Type | Required | Description |
|-----------|--------|----------|--------------------------------------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
## Response Parameters
| Parameter | Type | Description |
|----------------------|--------|--------------------------|
| id | String | The id of the liquidity pool (e.g., `34`) |
| name | String | The name of the liquidity pool (e.g., `Uniswap V2`) |
| logo | String | Liquidity Logo URL (e.g., `https://static.okx.com/cdn/wallet/logo/UNI.png`) |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/aggregator/get-liquidity?chainIndex=1' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"id": "34",
"logo": "https://static.okx.com/cdn/wallet/logo/UNI.png",
"name": "Uniswap V2"
},
{
"id": "29",
"logo": "https://static.okx.com/cdn/wallet/logo/SUSHI.png",
"name": "SushiSwap"
},
{
"id": "47",
"logo": "https://static.okx.com/cdn/explorer/dex/logo/Dex_DefiSwap.png",
"name": "DeFi Swap"
},
{
"id": "49",
"logo": "https://static.okx.com/cdn/wallet/logo/convxswap.png",
"name": "Convergence"
},
{
"id": "48",
"logo": "https://static.okx.com/cdn/wallet/logo/luaswap.png",
"name": "LuaSwap"
},
{
"id": "40",
"logo": "https://static.okx.com/cdn/wallet/logo/SHIB.png",
"name": "ShibaSwap"
},
{
"id": "30",
"logo": "https://static.okx.com/cdn/wallet/logo/pancake.png",
"name": "PancakeSwap"
},
{
"id": "53",
"logo": "https://static.okx.com/cdn/wallet/logo/UNI.png",
"name": "Uniswap V3"
},
{
"id": "54",
"logo": "https://static.okx.com/cdn/wallet/logo/balancer.png",
"name": "Balancer V1"
},
{
"id": "51",
"logo": "https://static.okx.com/cdn/wallet/logo/balancer.png",
"name": "Balancer V2"
},
{
"id": "55",
"logo": "https://static.okx.com/cdn/wallet/logo/Curve.png",
"name": "Curve V1"
},
{
"id": "58",
"logo": "https://static.okx.com/cdn/wallet/logo/Curve.png",
"name": "Curve V2"
},
{
"id": "52",
"logo": "https://static.okx.com/cdn/wallet/logo/bancor.png",
"name": "Bancor"
},
{
"id": "59",
"logo": "https://static.okx.com/cdn/wallet/logo/Kyber.png",
"name": "Kyber"
},
{
"id": "81",
"logo": "https://static.okx.com/cdn/wallet/logo/Synapse.png",
"name": "Synapse"
},
{
"id": "83",
"logo": "https://static.okx.com/cdn/wallet/logo/Wombat.png",
"name": "Wombat"
},
{
"id": "80",
"logo": "https://static.okx.com/cdn/wallet/logo/DODO.png",
"name": "DODO"
},
{
"id": "82",
"logo": "https://static.okx.com/cdn/wallet/logo/Shell.png",
"name": "Shell"
},
{
"id": "88",
"logo": "https://static.okx.com/cdn/wallet/logo/DODO.png",
"name": "DODO V2"
},
{
"id": "91",
"logo": "https://static.okx.com/cdn/wallet/logo/Smoothy.png",
"name": "Smoothy"
},
{
"id": "92",
"logo": "https://static.okx.com/cdn/wallet/logo/RadioShack.png",
"name": "RadioShack"
},
{
"id": "90",
"logo": "https://static.okx.com/cdn/wallet/logo/ORION.png",
"name": "Orion"
},
{
"id": "89",
"logo": "https://static.okx.com/cdn/wallet/logo/FraxFinance.png",
"name": "FraxSwap"
},
{
"id": "99",
"logo": "https://static.okx.com/cdn/wallet/logo/okb.png",
"name": "OKX DEX"
},
{
"id": "101",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_Swapr.png",
"name": "Swapr"
},
{
"id": "351",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_DFX.png",
"name": "DFX Finance V3"
},
{
"id": "104",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_bancor.png",
"name": "Bancor V3"
},
{
"id": "105",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_PSM.png",
"name": "PSM"
},
{
"id": "108",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_Verse.png",
"name": "Verse"
},
{
"id": "248",
"logo": "https://static.okx.com/cdn/wallet/logo/okb.png",
"name": "OKX Limit Order"
},
{
"id": "132",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_defiplaza.png",
"name": "DefiPlaza"
},
{
"id": "114",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_Swerve.png",
"name": "Swerve"
},
{
"id": "113",
"logo": "https://static.okx.com/cdn/wallet/logo/Kyber.png",
"name": "Kyber Elastic"
},
{
"id": "131",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_defiplaza.png",
"name": "StablePlaza"
},
{
"id": "134",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_Lido.png",
"name": "Lido"
},
{
"id": "135",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_NOMISWAP.png",
"name": "Nomiswap Stable"
},
{
"id": "136",
"logo": "https://static.okx.com/cdn/explorer/dex/logo/solidly.png",
"name": "Solidly"
},
{
"id": "215",
"logo": "https://static.okx.com/cdn/wallet/logo/traderjoexyz.png",
"name": "Trader Joe V2.1"
},
{
"id": "153",
"logo": "https://static.okx.com/cdn/wallet/logo/Dex_Cafe_Swap.png",
"name": "Cafe Swap"
},
{
"id": "141",
"logo": "https://static.okx.com/cdn/wallet/logo/Dex_ELK.png",
"name": "ELK"
},
{
"id": "102",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_Unifi.png",
"name": "Unifi"
},
{
"id": "159",
"logo": "https://static.okx.com/cdn/wallet/logo/Dex_LINKSWAP.png",
"name": "LINKSWAP"
},
{
"id": "160",
"logo": "https://static.okx.com/cdn/wallet/logo/Dex_Sake_Swap.png",
"name": "Sake Swap"
},
{
"id": "27",
"logo": "https://static.okx.com/cdn/wallet/logo/Curve.png",
"name": "Curve 3CRV"
},
{
"id": "202",
"logo": "https://static.okx.com/cdn/wallet/logo/Dex_Aave.png",
"name": "Aave V2"
},
{
"id": "230",
"logo": "https://static.okx.com/cdn/wallet/logo/Dex_Aave.png",
"name": "Aave V3"
},
{
"id": "199",
"logo": "https://static.okx.com/cdn/wallet/logo/Dex_Compound.png",
"name": "Compound"
},
{
"id": "266",
"logo": "https://static.okx.com/cdn/wallet/logo/Dex_Compound.png",
"name": "Compound V3"
},
{
"id": "184",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_logo_Frax.png",
"name": "sfrxETH"
},
{
"id": "356",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_logo_Frax.png",
"name": "sFRAX"
},
{
"id": "186",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_Lido.png",
"name": "stMatic"
},
{
"id": "200",
"logo": "https://static.okx.com/cdn/wallet/logo/pancake.png",
"name": "PancakeSwap V3"
},
{
"id": "203",
"logo": "https://static.okx.com/cdn/wallet/logo/Dex_Rocketpool.png",
"name": "RocketPool"
},
{
"id": "204",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_1inch_limit_order.png",
"name": "1inch LP v1.1"
},
{
"id": "210",
"logo": "https://static.okx.com/cdn/wallet/logo/Curve.png",
"name": "Curve TNG"
},
{
"id": "330",
"logo": "https://static.okx.com/cdn/wallet/logo/Curve.png",
"name": "CurveNG"
},
{
"id": "214",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_Mooniswap.png",
"name": "Mooniswap"
},
{
"id": "213",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_Integral.png",
"name": "Integral"
},
{
"id": "218",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_Maverick.png",
"name": "Maverick V1"
},
{
"id": "226",
"logo": "https://static.okx.com/cdn/wallet/logo/Curve.png",
"name": "Curve LLAMMA"
},
{
"id": "234",
"logo": "https://static.okx.com/cdn/explorer/dex/logo/Dex_xSigma.png",
"name": "xSigma"
},
{
"id": "239",
"logo": "https://static.okx.com/cdn/explorer/dex/logo/Dex_Sushiswap_V3.png",
"name": "Sushiswap V3"
},
{
"id": "257",
"logo": "https://static.okx.com/cdn/explorer/dex/logo/Synthetix.png",
"name": "Wrapped Synthetix"
},
{
"id": "262",
"logo": "https://static.okx.com/cdn/explorer/dex/logo/solidly.png",
"name": "Solidly V3"
},
{
"id": "265",
"logo": "https://static.okx.com/cdn/explorer/dex/logo/SmarDex.png",
"name": "SmarDex"
},
{
"id": "323",
"logo": "https://static.okx.com/cdn/explorer/dex/logo/Synthetix.png",
"name": "sETH Wrapper"
},
{
"id": "476",
"logo": "https://static.okx.com/cdn/web3/dex/logo/Ekubo.png",
"name": "Ekubo ETH"
},
{
"id": "328",
"logo": "https://static.okx.com/cdn/web3/dex/logo/sDai.png",
"name": "sDai"
},
{
"id": "333",
"logo": "https://static.okx.com/cdn/web3/dex/logo/RingProtocol.png",
"name": "Ring Protocol"
},
{
"id": "365",
"logo": "https://static.okx.com/cdn/web3/dex/logo/Angle.png",
"name": "Angle"
},
{
"id": "352",
"logo": "https://static.okx.com/cdn/web3/dex/logo/Angle.png",
"name": "Angle Stake"
},
{
"id": "354",
"logo": "https://static.okx.com/cdn/web3/dex/logo/Origin.png",
"name": "Origin Wrapper"
},
{
"id": "355",
"logo": "https://static.okx.com/cdn/web3/dex/logo/Origin.png",
"name": "Origin"
},
{
"id": "379",
"logo": "https://static.okx.com/cdn/web3/dex/logo/Unicly.png",
"name": "Unicly"
},
{
"id": "380",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_Maverick.png",
"name": "Maverick V2"
},
{
"id": "394",
"logo": "https://static.okx.com/cdn/web3/dex/logo/novabits.png",
"name": "Novabits V3"
},
{
"id": "399",
"logo": "https://static.okx.com/cdn/wallet/logo/UNI.png",
"name": "Uniswap V1"
},
{
"id": "401",
"logo": "https://static.okx.com/cdn/web3/dex/logo/Fluid.png",
"name": "Fluid"
},
{
"id": "404",
"logo": "https://static.okx.com/cdn/wallet/logo/dex_PSM.png",
"name": "LitePSM"
},
{
"id": "409",
"logo": "https://static.okx.com/cdn/wallet/logo/balancer.png",
"name": "Balancer CoW AMM"
},
{
"id": "406",
"logo": "https://static.okx.com/cdn/web3/dex/logo/native.png",
"name": "Native"
},
{
"id": "427",
"logo": "https://static.okx.com/cdn/web3/dex/logo/Saving_USDS.png",
"name": "Saving USDS"
},
{
"id": "431",
"logo": "https://static.okx.com/cdn/wallet/logo/Curve.png",
"name": "Saving CRV Stake Factory"
},
{
"id": "428",
"logo": "https://static.okx.com/cdn/web3/dex/logo/Saving_GYD.png",
"name": "Saving GYD"
},
{
"id": "433",
"logo": "https://static.okx.com/cdn/web3/dex/logo/EtherFi.png",
"name": "EtherFi weETH"
},
{
"id": "437",
"logo": "https://static.okx.com/cdn/web3/dex/logo/EtherFi.png",
"name": "EtherFi eETH"
},
{
"id": "436",
"logo": "https://static.okx.com/cdn/web3/dex/logo/EtherFi.png",
"name": "EtherFi eBTC"
},
{
"id": "438",
"logo": "https://static.okx.com/cdn/wallet/logo/UNI.png",
"name": "Uniswap V4"
},
{
"id": "450",
"logo": "https://static.okx.com/cdn/web3/dex/logo/native.png",
"name": "NativeV3"
},
{
"id": "466",
"logo": "https://static.okx.com/cdn/web3/dex/logo/Gamma_Swap.png",
"name": "Gamma Swap"
},
{
"id": "482",
"logo": "https://static.okx.com/cdn/web3/dex/logo/Amber.png",
"name": "Amber"
},
{
"id": "522",
"logo": "https://static.okx.com/cdn/web3/dex/logo/Fluid_Lite.png",
"name": "Fluid Lite"
},
{
"id": "523",
"logo": "https://static.okx.com/cdn/web3/dex/logo/1010_Trading.png",
"name": "1010 Trading"
}
],
"msg": ""
}
```
- [Approve Transactions](https://web3.okx.com/onchainos/dev-docs/trade/dex-approve-transaction.md)
{/* api-page */}
# Approve Transactions
According to the [ERC-20 standard ](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/), we need to make sure that the OKX router has permission to spend funds with the user's wallet before making a transaction. This API will generate the relevant data for calling the contract.
### Request URL
GET `https://web3.okx.com/api/v6/dex/aggregator/approve-transaction`
## Request Parameters
| Parameter | Type | Required | Description |
|----------------------|--------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| tokenContractAddress | String | Yes | Token contract address (e.g., `0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48`) |
| approveAmount | String | Yes | The amount of token that needs to be permitted (set in minimal divisible units, e.g., `1.00` USDT set as `1000000`, `1.00` DAI set as `1000000000000000000`,you could get the minimal divisible units from [Token Basic Information](../market/market-token-basic-info).) |
## Response Parameters
| Parameter | Type | Description |
|--------------------|--------|----------------------------------------|
| data | String | Call data |
| dexContractAddress | String | The contract address of OKX DEX approver (e.g., `0x6f9ffea7370310cd0f890dfde5e0e061059dcfd9`) |
| gasLimit | String | Gas limit (e.g., `50000`). To get accurate data, please take a look at [/gas-limit](onchain-gateway-api-gas-limit) API |
| gasPrice | String | Gas price in wei (e.g., `110000000`) |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/aggregator/approve-transaction?chainIndex=1&tokenContractAddress=0x6f9ffea7370310cd0f890dfde5e0e061059dcfd9&approveAmount=1000000' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"data": "0x095ea7b3000000000000000000000000c67879f4065d3b9fe1c09ee990b891aa8e3a4c2f00000000000000000000000000000000000000000000000000000000000f4240",
"dexContractAddress": "0xc67879F4065d3B9fe1C09EE990B891Aa8E3a4c2f",
"gasLimit": "50000",
"gasPrice": "110000000"
}
],
"msg": ""
}
```
- [Get Quotes](https://web3.okx.com/onchainos/dev-docs/trade/dex-get-quote.md)
{/* api-page */}
# Get Quotes
Get the best quote for a swap through OKX DEX.
### Request URL
GET `https://web3.okx.com/api/v6/dex/aggregator/quote`
## Request Parameters
| Parameter | Type | Required | Description |
|------------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| amount | String | Yes | The input amount of a token to be sold (if swapMode=exactIn) or buy (if swapMode=exactOut), set in minimal divisible units, e.g., 1.00 USDT set as 1000000, 1.00 DAI set as 1000000000000000000, you could get the minimal divisible units from [Token Basic Information](../market/market-token-basic-info). |
| swapMode | String | Yes | Possible values: [`exactIn`, `exactOut`]. Default: `exactIn`. `exactOut` is for supporting use cases where you need an exact output amount.
Note: 1.ExactOut feature currently only support **Ethereum、Base、BSC 、Arbitrum chain**. 2.ExactOut feature currently support only **Uni v3 protocols** 3. In this case the slippage is on the input token. |
| fromTokenAddress | String | Yes | The contract address of a token to be sold (e.g., `0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`) |
| toTokenAddress | String | Yes | The contract address of a token to be bought (e.g., `0xa892e1fef8b31acc44ce78e7db0a2dc610f92d00`) |
| dexIds | String | No | DexId of the liquidity pool for limited quotes, multiple combinations separated by `,` (e.g.,`1,50,180`, see liquidity list for more) |
| directRoute | Boolean| No | The default setting is false. When enabled, Direct Routes restrict our routing to a single liquidity pool only. Currently, this feature is only active for Solana swaps. |
| singleRouteOnly | Boolean| No | Default is false. When enabled, routing is restricted to a single route.Multi-hop and multi-pool routes are allowed, but no parallel split routes will be constructed |
| singlePoolPerHop | Boolean| No | Default is false. When enabled, each hop in the route is restricted to a single pool. |
| priceImpactProtectionPercent | String | No | This is an optional feature. The default value is 90 (representing 90%). The priceImpactProtectionPercent parameter can be set between 0 and 100. When it’s set to 100, the feature is disabled and every transaction will be allowed to pass. If the estimated price impact is above the percentage indicated, an error will be returned. For example, if priceImpactProtectionPercent = 25 (25%), any quote with a price impact higher than 25% will return an error. Note: If we’re unable to calculate the price impact, we’ll return null, and the price impact protection will be disabled. |
| feePercent | String | No | The percentage of fromTokenAmount will be sent to the referrer's address, the rest will be set as the input amount to be sold. min percentage> 0 max percentage: 10 for Solana, 3 for all other chains. By configuring this parameter, you can obtain the final amount of totoken provided to the user after deducting the commission from fromtoken. A maximum of nine decimal places is allowed. If more decimals are entered, the system will automatically round up.|
## Response Parameters
| Parameter | Type | Description |
|-----------------|--------|-------------------------------------------------------------------------------------------------------------------------------|
| chainIndex | String | Unique identifier for the chain. |
| swapMode | String | Swap mode of this quote. |
| ***dexRouterList*** | ***Array*** | ***Quote path data set*** |
| fromTokenAmount | String | The input amount of a token to be sold (e.g., `500000000000000000000000`) |
| toTokenAmount | String | The resulting amount of a token to be bought (e.g., `168611907733361`) |
| tradeFee | String | Estimated network fee (USD) of the quote route |
| estimateGasFee | String | Estimated gas consumption is returned in the smallest units of each chain, such as wei. |
| router | String | Main path for the token swap |
| ***dexProtocol*** | ***Object*** | ***Liquidity protocols used on the main path*** (e.g., `Verse`) |
| percent | String | The percentage of assets handled by the protocol (e.g., `100`)|
| dexName | String | The name of the liquidity protocol |
| fromTokenIndex | String | Token index of fromToken in the swap path. |
| ***fromToken*** | ***Object*** | ***The information of a token to be sold*** |
| tokenContractAddress | String | Token contract address (e.g., `0xa892e1fef8b31acc44ce78e7db0a2dc610f92d00`) |
| tokenSymbol | String | Token symbol (e.g., `0xa892e1fef8b31acc44ce78e7db0a2dc610f92d00`) |
| tokenUnitPrice | String | The token unit price returned by this interface is a general USD real time price based on data from on-chain sources. Note: This price is only a recommended price. For some special cases, the token unit price may be 'null' |
| decimal | String | The decimal number defines the smallest unit into which a single currency token can be divided. For example, if the decimal number of a token is 8, it means that a single such token can be divided into 100,000,000 of its smallest units. ***Note: This parameter is for reference only. It may change due to reasons such as settings adjustments by the contract owner.*** |
| isHoneyPot | Boolean | If the token is a honeypot token. `yes:true` `no:false ` |
| taxRate | String | Token tax rate for selling: Applicable to tokens with configurable tax mechanisms (e.g., SafeMoon, SPL2022 tokens). Returns 0 for regular tokens without tax. The value ranges from 0 to 1, where 0.01 represents 1%. |
| toTokenIndex | String | Token index of toToken in the swap path. |
| ***toToken*** | ***Object*** | ***The information of a token to be bought*** |
| tokenContractAddress | String | Token contract address (e.g., `0xa892e1fef8b31acc44ce78e7db0a2dc610f92d00`) |
| tokenSymbol | String | Token symbol (e.g., `0xa892e1fef8b31acc44ce78e7db0a2dc610f92d00`) |
| tokenUnitPrice | String | The token unit price returned by this interface is a general USD price based on data from on-chain, exchange, and other third-party sources. Note: This price is only a recommended price. For some special cases, the token unit price may be 'null' |
| decimal | String | The decimal number defines the smallest unit into which a single currency token can be divided. For example, if the decimal number of a token is 8, it means that a single such token can be divided into 100,000,000 of its smallest units. ***Note: This parameter is for reference only. It may change due to reasons such as settings adjustments by the contract owner.*** |
| isHoneyPot | Boolean | If the token is a honeypot token. `yes:true` `no:false ` |
| taxRate | String | Token tax rate for buying: Applicable to tokens with configurable tax mechanisms (e.g., SafeMoon, SPL2022 tokens). Returns 0 for regular tokens without tax. The value ranges from 0 to 1, where 0.01 represents 1%. |
| dexName | String | DEX name of the quote route |
| dexLogo | String | DEX logo of the quote route |
| tradeFee | String | Estimated network fee (USD) of the quote route |
| amountOut | String | Received amount of the quote route |
| priceImpactPercent | String | Percentage = (Received value – Paid value) / Paid value. The swap amount will affect the depth of the liquidity pool, causing a value difference. This percentage can be positive if the received value exceeds the paid value, e.g., 5 represents 5%. |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/aggregator/quote?amount=10000000000000000000&chainIndex=1&toTokenAddress=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&fromTokenAddress=0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"chainIndex": "130",
"contextSlot": 0,
"dexRouterList": [
{
"dexProtocol": {
"dexName": "Uniswap V4",
"percent": "30"
},
"fromToken": {
"decimal": "18",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x4200000000000000000000000000000000000006",
"tokenSymbol": "WETH",
"tokenUnitPrice": "4191.043356462183138854"
},
"fromTokenIndex": "0",
"toToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x078d782b760474a361dda0af3839290b0ef57ad6",
"tokenSymbol": "USDC",
"tokenUnitPrice": "0.999692348812448693"
},
"toTokenIndex": "4"
},
{
"dexProtocol": {
"dexName": "Uniswap V4",
"percent": "5"
},
"fromToken": {
"decimal": "18",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x4200000000000000000000000000000000000006",
"tokenSymbol": "WETH",
"tokenUnitPrice": "4191.043356462183138854"
},
"fromTokenIndex": "0",
"toToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x9151434b16b9763660705744891fa906f660ecc5",
"tokenSymbol": "USDT",
"tokenUnitPrice": "1.000313193103130296"
},
"toTokenIndex": "3"
},
{
"dexProtocol": {
"dexName": "Uniswap V4",
"percent": "5"
},
"fromToken": {
"decimal": "18",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x4200000000000000000000000000000000000006",
"tokenSymbol": "WETH",
"tokenUnitPrice": "4191.043356462183138854"
},
"fromTokenIndex": "0",
"toToken": {
"decimal": "8",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x0555e30da8f98308edb960aa94c0db47230d2b9c",
"tokenSymbol": "WBTC",
"tokenUnitPrice": "112879.983830393508167243"
},
"toTokenIndex": "2"
},
{
"dexProtocol": {
"dexName": "Uniswap V4",
"percent": "5"
},
"fromToken": {
"decimal": "18",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x4200000000000000000000000000000000000006",
"tokenSymbol": "WETH",
"tokenUnitPrice": "4191.043356462183138854"
},
"fromTokenIndex": "0",
"toToken": {
"decimal": "8",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x927b51f251480a681271180da4de28d44ec4afb8",
"tokenSymbol": "WBTC",
"tokenUnitPrice": "112634.107075070841530997"
},
"toTokenIndex": "1"
},
{
"dexProtocol": {
"dexName": "Uniswap V4",
"percent": "55"
},
"fromToken": {
"decimal": "18",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x4200000000000000000000000000000000000006",
"tokenSymbol": "WETH",
"tokenUnitPrice": "4191.043356462183138854"
},
"fromTokenIndex": "0",
"toToken": {
"decimal": "8",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x927b51f251480a681271180da4de28d44ec4afb8",
"tokenSymbol": "WBTC",
"tokenUnitPrice": "112634.107075070841530997"
},
"toTokenIndex": "1"
},
{
"dexProtocol": {
"dexName": "Uniswap V4",
"percent": "3"
},
"fromToken": {
"decimal": "8",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x927b51f251480a681271180da4de28d44ec4afb8",
"tokenSymbol": "WBTC",
"tokenUnitPrice": "112634.107075070841530997"
},
"fromTokenIndex": "1",
"toToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x9151434b16b9763660705744891fa906f660ecc5",
"tokenSymbol": "USDT",
"tokenUnitPrice": "1.000313193103130296"
},
"toTokenIndex": "3"
},
{
"dexProtocol": {
"dexName": "Uniswap V4",
"percent": "97"
},
"fromToken": {
"decimal": "8",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x927b51f251480a681271180da4de28d44ec4afb8",
"tokenSymbol": "WBTC",
"tokenUnitPrice": "112634.107075070841530997"
},
"fromTokenIndex": "1",
"toToken": {
"decimal": "8",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x0555e30da8f98308edb960aa94c0db47230d2b9c",
"tokenSymbol": "WBTC",
"tokenUnitPrice": "112879.983830393508167243"
},
"toTokenIndex": "2"
},
{
"dexProtocol": {
"dexName": "Uniswap V4",
"percent": "48"
},
"fromToken": {
"decimal": "8",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x0555e30da8f98308edb960aa94c0db47230d2b9c",
"tokenSymbol": "WBTC",
"tokenUnitPrice": "112879.983830393508167243"
},
"fromTokenIndex": "2",
"toToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x078d782b760474a361dda0af3839290b0ef57ad6",
"tokenSymbol": "USDC",
"tokenUnitPrice": "0.999692348812448693"
},
"toTokenIndex": "4"
},
{
"dexProtocol": {
"dexName": "Uniswap V4",
"percent": "52"
},
"fromToken": {
"decimal": "8",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x0555e30da8f98308edb960aa94c0db47230d2b9c",
"tokenSymbol": "WBTC",
"tokenUnitPrice": "112879.983830393508167243"
},
"fromTokenIndex": "2",
"toToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x9151434b16b9763660705744891fa906f660ecc5",
"tokenSymbol": "USDT",
"tokenUnitPrice": "1.000313193103130296"
},
"toTokenIndex": "3"
},
{
"dexProtocol": {
"dexName": "Euler",
"percent": "73"
},
"fromToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x9151434b16b9763660705744891fa906f660ecc5",
"tokenSymbol": "USDT",
"tokenUnitPrice": "1.000313193103130296"
},
"fromTokenIndex": "3",
"toToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x078d782b760474a361dda0af3839290b0ef57ad6",
"tokenSymbol": "USDC",
"tokenUnitPrice": "0.999692348812448693"
},
"toTokenIndex": "4"
},
{
"dexProtocol": {
"dexName": "Euler",
"percent": "26"
},
"fromToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x9151434b16b9763660705744891fa906f660ecc5",
"tokenSymbol": "USDT",
"tokenUnitPrice": "1.000313193103130296"
},
"fromTokenIndex": "3",
"toToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x078d782b760474a361dda0af3839290b0ef57ad6",
"tokenSymbol": "USDC",
"tokenUnitPrice": "0.999692348812448693"
},
"toTokenIndex": "4"
},
{
"dexProtocol": {
"dexName": "Uniswap V4",
"percent": "1"
},
"fromToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x9151434b16b9763660705744891fa906f660ecc5",
"tokenSymbol": "USDT",
"tokenUnitPrice": "1.000313193103130296"
},
"fromTokenIndex": "3",
"toToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x078d782b760474a361dda0af3839290b0ef57ad6",
"tokenSymbol": "USDC",
"tokenUnitPrice": "0.999692348812448693"
},
"toTokenIndex": "4"
}
],
"estimateGasFee": "1002000",
"fromToken": {
"decimal": "18",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"tokenSymbol": "UNICHAIN_ETH",
"tokenUnitPrice": "4191.043356462183138854"
},
"fromTokenAmount": "10000000000000000000000",
"priceImpactPercent": "-67.53",
"router": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee--0x927b51f251480a681271180da4de28d44ec4afb8--0x0555e30da8f98308edb960aa94c0db47230d2b9c--0x9151434b16b9763660705744891fa906f660ecc5--0x078d782b760474a361dda0af3839290b0ef57ad6",
"swapMode": "exactIn",
"toToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x078d782b760474a361dda0af3839290b0ef57ad6",
"tokenSymbol": "USDC",
"tokenUnitPrice": "0.999692348812448693"
},
"toTokenAmount": "13614286937853",
"tradeFee": "0.00001607688116316"
}
],
"msg": ""
}
```
- [Get Solana Swap Instructions](https://web3.okx.com/onchainos/dev-docs/trade/dex-solana-swap-instruction.md)
{/* api-page */}
# Get Solana Swap Instructions
Obtain transaction instruction data for redemption or custom assembly in Solana.
### Request URL
GET `https://web3.okx.com/api/v6/dex/aggregator/swap-instruction`
## Request Parameters
| Parameter | Type | Required | Description |
|----------------------------------|---------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `501`: Solana. See more [here](../home/supported-chain). |
| amount | String | Yes | Token amount for the quote. (The amount must include its precision. For example, exchanging 1.00 USDT requires inputting `1000000`, while exchanging 1.00 DAI requires `1000000000000000000`. Token precision can be obtained from the [Token Basic Information](../market/market-token-basic-info).) |
| fromTokenAddress | String | Yes | Address of the token contract being swapped from (e.g., `0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`). |
| toTokenAddress | String | Yes | Address of the token contract being swapped to (e.g., `0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48`). |
| userWalletAddress | String | Yes | User’s wallet address (e.g., `0x3f6a3f57569358a512ccc0e513f171516b0fd42a`). |
| slippagePercent | String | Yes | Slippage limit.
Note: 1. For EVM networks, the slippage setting has a minimum value of `0` and a maximum value of `100`. 2. For Solana, the slippage setting has a minimum value of `0` and a maximum value of less than `100`. (For example: `0.5` means that the maximum slippage for this transaction is `0.5%`.) |
| autoSlippage | Boolean | No | Default is false. When set to true, the original slippage (if set) will be covered by the autoSlippage and the API will calculate and return auto slippage recommendations based on current market data. |
| maxAutoSlippagePercent | String | No | When autoSlippage is set to true, this value is the maximum auto slippage returned by the API(e.g., 0.5 represents 0.5%). We recommend that users adopt this value to ensure risk control. |
| swapReceiverAddress | String | No | Recipient address for the purchased asset. If not set, the asset will be sent to the `userWalletAddress`. (e.g., `0x3f6a3f57569358a512ccc0e513f171516b0fd42a`). |
| feePercent | String | No | The percentage of fromTokenAmount will be sent to the referrer's address, the rest will be set as the input amount to be sold. min percentage> 0 max percentage: 10 for Solana, 3 for all other chains. By configuring this parameter, you can obtain the final amount of totoken provided to the user after deducting the commission from fromtoken. A maximum of nine decimal places is allowed. If more decimals are entered, the system will automatically round up.|
| fromTokenReferrerWalletAddress | String | No | Wallet address receiving the referral fee in `fromToken`. Must be used with `feePercent`, and a single transaction can only apply either `fromToken` or `toToken` referral fees.
**Note:** **Solana:** The referral address must hold some SOL for activation. |
| toTokenReferrerWalletAddress | String | No | Wallet address receiving the referral fee in `toToken`. Must be used with `feePercent`, and a single transaction can only apply either `fromToken` or `toToken` referral fees.
**Note:** **Solana:** The referral address must hold some SOL for activation. |
| positiveSlippagePercent | String | No | This feature is open to **whitelist or enterprise clients** only. If you want to access it , please contact dexapi@okx.com
Once configured, a fee can be charged on the quote improvement portion, capped at 10% of the total trade amount.
The cap can be adjusted by specifying a custom percentage parameter.The default setting is 0. `Min percentage`: 0 ,`Max percentage`: 10 , Maximum of 1 decimal point. Currently, this parameter is only supported on the **Solana chain**. |
| positiveSlippageFeeAddress | String | No | This feature is open to **whitelist or enterprise clients** only. If you want to use it , please contact dexapi@okx.com
The wallet address that receives positive slippage. You must set positiveSlippagePercent parameter together to specify the proportion. If provided, all positive slippage earnings will be sent to this address; if not provided, the wallet address used for collecting referral fees will be used instead. |
| dexIds | String | No | Restrict the quote to specific liquidity pools by `dexId`. Multiple IDs should be comma-separated (e.g., `1,50,180`. See liquidity list for more details). |
| excludeDexIds | String | No | The dexId of the liquidity pool will not be used, multiple combinations separated by `,` (e.g.,`1,50,180`, see liquidity list for more) |
| disableRFQ | Boolean | No | Disable all liqudity source classified as RFQs that have dependencies on time-sensitive quotes. The default setting is false. |
| directRoute | Boolean | No | The default setting is false. When enabled, Direct Routes restrict our routing to a single liquidity pool only. Currently, this feature is only active for Solana swaps. |
| singleRouteOnly | Boolean| No | Default is false. When enabled, routing is restricted to a single route.Multi-hop and multi-pool routes are allowed, but no parallel split routes will be constructed |
| singlePoolPerHop | Boolean| No | Default is false. When enabled, each hop in the route is restricted to a single pool. |
| priceImpactProtectionPercent | String | No | This is an optional feature. The default value is 90 (representing 90%). The priceImpactProtectionPercent can be set between 0 and 100. When it’s set to 100 (representing 100%), the feature is disabled and every transaction will be allowed to pass. If the estimated price impact is above the percentage indicated, an error will be returned. For example, if priceImpactProtectionPercent = 25 (25%), any quote with a price impact higher than 25% will return an error. Note: If we’re unable to calculate the price impact, we’ll return null, and the price impact protection will be disabled. |
| useTokenLedger | Boolean | No | The Token Ledger records your token balance before the swap executes. This is useful when you don't know the exact input amount upfront. For example, when the input comes from a previous instruction in the same transaction. |
| computeUnitPrice | String | No | Used for transactions on the Solana network and similar to gasPrice on Ethereum. This price determines the priority level of the transaction. The higher the price, the more likely that the transaction can be processed faster. |
| computeUnitLimit | String | No | Used for transactions on the Solana network and analogous to gasLimit on Ethereum, which ensures that the transaction won’t take too much computing resource. |
## Response Parameters
| Parameter | Type | Description |
|------------------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| addressLookupTableAccount | Array | Address Lookup Table Account. A data structure in the Solana blockchain used to optimize the management and referencing of addresses in transactions. It allows developers to store a group of related addresses in a table and reference them in transactions via index values (instead of the full 32-byte address), significantly improving transaction efficiency and scalability. |
| instructionLists | Array | Detailed transaction instruction information |
| data | String | Instruction data |
| accounts | Array | Instruction account information |
| isSigner | Boolean | Whether the account is a signer |
| isWritable | Boolean | Whether the account is writable |
| pubkey | Boolean | Public key address of the account |
| programId | String | Program ID for instruction execution |
| ***routerResult*** | ***Object*** | ***Quote path data*** |
| chainIndex | String | Unique identifier for the chain. |
| swapMode | String | Swap mode of this quote. |
| fromTokenAmount | String | The input amount of a token to be sold ( e.g.,`500000000000000000000000`) |
| toTokenAmount | String | The resulting amount of a token to be bought ( e.g.,`168611907733361`) |
| tradeFee | String | Estimated network fee (USD) of the quote route |
| estimateGasFee | String | Estimated gas consumption is returned in the smallest units of each chain, such as wei. |
| ***dexRouterList*** | ***Array*** | ***Quote path data set*** |
| router | String | One of the main paths for the token swap |
| ***dexProtocol*** | ***Object*** | ***Liquidity protocols used on the main path*** |
| dexName | String | The name of the liquidity protocol (e.g.,`Verse`) |
| fromTokenIndex | String | Token index of fromToken in the swap path. |
| percent | String | The percentage of assets handled by the protocol (e.g.,`100`) |
| ***fromToken*** | ***Object*** | ***The information of a token to be sold*** |
| tokenContractAddress | String | Token contract address (e.g.,`0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48`) |
| tokenSymbol | String | Token symbol (e.g.,`USDC`) |
| tokenUnitPrice | String | The token unit price returned by this interface is a general USDis a general USD real time price based on data from on-chain sources. Note: This price is only a recommended price. For some special cases, the token unit price may be 'null' |
| decimal | String | The decimal number defines the smallest unit into which a single currency token can be divided. For example, if the decimal number of a token is 8, it means that a single such token can be divided into 100,000,000 of its smallest units. ***Note: This parameter is for reference only. It may change due to reasons such as settings adjustments by the contract owner.*** |
| isHoneyPot | Boolean | If the token is a honeypot token. `yes:true` `no:false ` |
| taxRate | String | Token tax rate for selling: Applicable to tokens with configurable tax mechanisms (e.g., SafeMoon, SPL2022 tokens). Returns 0 for regular tokens without tax. The value ranges from 0 to 1, where 0.01 represents 1%. |
| toTokenIndex | String | Token index of toToken in the swap path. |
| ***toToken*** | ***Object*** | ***The information of a token to be bought*** |
| tokenContractAddress | String | Token contract address (e.g.,`0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48`) |
| tokenSymbol | String | Token symbol (e.g.,`USDC`) |
| tokenUnitPrice | String | The token unit price returned by this interface is a general USD price based on data from on-chain, exchange, and other third-party sources. Note: This price is only a recommended price. For some special cases, the token unit price may be 'null' |
| decimal | String | The decimal number defines the smallest unit into which a single currency token can be divided. For example, if the decimal number of a token is 8, it means that a single such token can be divided into 100,000,000 of its smallest units. ***Note: This parameter is for reference only. It may change due to reasons such as settings adjustments by the contract owner.*** |
| isHoneyPot | Boolean | If the token is a honeypot token. `yes:true` `no:false ` |
| taxRate | String | Token tax rate for buying: Applicable to tokens with configurable tax mechanisms (e.g., SafeMoon, SPL2022 tokens). Returns 0 for regular tokens without tax. The value ranges from 0 to 1, where 0.01 represents 1%. |
| priceImpactPercent | String | Percentage = (Received value – Paid value) / Paid value. The swap amount will affect the depth of the liquidity pool, causing a value difference. This percentage can be positive if the received value exceeds the paid value. |
| ***tx*** | ***Object*** | ***contract data model*** |
| from | String | User's wallet address (e.g.,`0x3f6a3f57569358a512ccc0e513f171516b0fd42a`) |
| to | String | The contract address of OKX DEX router (e.g.,`0x3b3ae790Df4F312e745D270119c6052904FB6790`) |
| minReceiveAmount | String | The minimum amount of a token to buy when the price reaches the upper limit of slippage (e.g.,`900645839798`) |
| slippagePercent | String | The value of current transaction slippage |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/aggregator/swap-instruction?chainIndex=501&userWalletAddress=J5CBzXpcYn6WR2JBah8zU4Yxct985CAFGwXRcFaX2pbS&autoSlippage=true&toTokenReferrerWalletAddress=A9bBCSy9y4vggKgcT7jkUiN77Q95soLbLYhCcbWUpy3g&amount=100000&fromTokenAddress=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&toTokenAddress=11111111111111111111111111111111&feePercent=0.875&slippagePercent=0.05&useTokenLedger=true \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": {
"addressLookupTableAccount": [
"Ga7MuV4c198RzhFvvpEHFVLUHaEDAM1VqW2rr2sJqfxe",
"2WejwkssZt7cd2At71Cc4yiev5cAKc5iZt3cZdyL5tkQ"
],
"createTokenAccountList": [
"FZcMUbUG4qaptFXmpX8xB2ivFKkxHywoEWpqskM6Md6D",
"H1KZidz2CNt5VjNaoV2bpSDsspaPtrKCLoLKXZEZrEB9",
"8XrtGP8RG33AnrN2yJ6H3gnXPNQ3dYXKqM72bpzhcsd6",
"DT2krA8vSP96D68nH86eAztZHVM18YB5i4gDXjLBDXm7",
"A9bBCSy9y4vggKgcT7jkUiN77Q95soLbLYhCcbWUpy3g",
"D9GYt4W7VvteKCSvTgyjzuiBJyTy94Kmr6YiC9fbxGjW"
],
"instructionLists": [
{
"data": "AgY6BAA=",
"accounts": [],
"programId": "ComputeBudget111111111111111111111111111111"
},
{
"data": "Axm5AgAAAAAA",
"accounts": [],
"programId": "ComputeBudget111111111111111111111111111111"
},
{
"data": "AgAAAPAdHwAAAAAA",
"accounts": [
{
"isSigner": true,
"isWritable": true,
"pubkey": "J5CBzXpcYn6WR2JBah8zU4Yxct985CAFGwXRcFaX2pbS"
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "H1KZidz2CNt5VjNaoV2bpSDsspaPtrKCLoLKXZEZrEB9"
}
],
"programId": "11111111111111111111111111111111"
},
{
"data": "k/F7ZPSErnb9",
"accounts": [
{
"isSigner": true,
"isWritable": true,
"pubkey": "J5CBzXpcYn6WR2JBah8zU4Yxct985CAFGwXRcFaX2pbS"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "J5CBzXpcYn6WR2JBah8zU4Yxct985CAFGwXRcFaX2pbS"
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "H1KZidz2CNt5VjNaoV2bpSDsspaPtrKCLoLKXZEZrEB9"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "So11111111111111111111111111111111111111112"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "11111111111111111111111111111111"
}
],
"programId": "proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u"
},
{
"data": "5FW5cE5PTQI=",
"accounts": [
{
"isSigner": false,
"isWritable": true,
"pubkey": "H9sbECFAyQXYJpvy5a3REamkZbGx8MnvUQ9wUZEJgANb"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "FZcMUbUG4qaptFXmpX8xB2ivFKkxHywoEWpqskM6Md6D"
}
],
"programId": "proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u"
},
{
"data": "JFyT2xqwn1o3PQMAAAAAAJdQCQAAAAAAMgABAAAARRAnAbCDhUAAAGQ=",
"accounts": [
{
"isSigner": true,
"isWritable": true,
"pubkey": "J5CBzXpcYn6WR2JBah8zU4Yxct985CAFGwXRcFaX2pbS"
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "FZcMUbUG4qaptFXmpX8xB2ivFKkxHywoEWpqskM6Md6D"
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "H1KZidz2CNt5VjNaoV2bpSDsspaPtrKCLoLKXZEZrEB9"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "So11111111111111111111111111111111111111112"
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "A9bBCSy9y4vggKgcT7jkUiN77Q95soLbLYhCcbWUpy3g"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u"
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "ARu4n5mFdZogZAravu7CcizaojWnS6oqka37gdLT5SZn"
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "8XrtGP8RG33AnrN2yJ6H3gnXPNQ3dYXKqM72bpzhcsd6"
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "DT2krA8vSP96D68nH86eAztZHVM18YB5i4gDXjLBDXm7"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "11111111111111111111111111111111"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "H9sbECFAyQXYJpvy5a3REamkZbGx8MnvUQ9wUZEJgANb"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "Ag3hiK9svNixH9Vu5sD2CmK5fyDWrx9a1iVSbZW22bUS"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "goonERTdGsjnkZqWuVjs73BZ3Pb9qoCUdBUL17BnS5j"
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "ARu4n5mFdZogZAravu7CcizaojWnS6oqka37gdLT5SZn"
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "8XrtGP8RG33AnrN2yJ6H3gnXPNQ3dYXKqM72bpzhcsd6"
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "DT2krA8vSP96D68nH86eAztZHVM18YB5i4gDXjLBDXm7"
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "JAQxrJ2WuDF4APfSifurJJ4HzV5Z3FyBuBeSMj7mo9aw"
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "4ynTYgJK5ruYx3AZMRjCHrJk1qkm61fePF7dkbvRQD46"
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "AABxS823DPBxDkxEcdFSduUH1p3XmKjL5AuVgbH5qB3U"
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "CyCg79QpzH8MbnPDMEJEvbw3ugJXWisWYPHEE363eUuJ"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "2pA6DAAPdrHZd1knT75Dy3Q3ZwyVWQ9gL4zZJSYqSuSR"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "Sysvar1nstructions1111111111111111111111111"
},
{
"isSigner": false,
"isWritable": false,
"pubkey": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "D9GYt4W7VvteKCSvTgyjzuiBJyTy94Kmr6YiC9fbxGjW"
}
],
"programId": "proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u"
}
],
"routerResult": {
"chainIndex": "501",
"contextSlot": 395101989,
"dexRouterList": [
{
"dexProtocol": {
"dexName": "GoonFi",
"percent": "100"
},
"fromToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"tokenSymbol": "USDC",
"tokenUnitPrice": "0.99975"
},
"fromTokenIndex": "0",
"toToken": {
"decimal": "9",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "So11111111111111111111111111111111111111112",
"tokenSymbol": "wSOL",
"tokenUnitPrice": "129.9"
},
"toTokenIndex": "1"
}
],
"estimateGasFee": "276998",
"fromToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"tokenSymbol": "USDC",
"tokenUnitPrice": "0.99975"
},
"fromTokenAmount": "100000",
"priceImpactPercent": "0.02",
"router": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v--11111111111111111111111111111111",
"swapMode": "exactIn",
"toToken": {
"decimal": "9",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "11111111111111111111111111111111",
"tokenSymbol": "SOL",
"tokenUnitPrice": "129.9"
},
"toTokenAmount": "610455",
"tradeFee": "0.0006497"
},
"tx": {
"from": "J5CBzXpcYn6WR2JBah8zU4Yxct985CAFGwXRcFaX2pbS",
"minReceiveAmount": "607402",
"slippagePercent": "0.5",
"to": "proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u"
},
"wsolRentFee": 2039280
},
"msg": ""
}
```
- [Swap](https://web3.okx.com/onchainos/dev-docs/trade/dex-swap.md)
{/* api-page */}
# Swap
Generate the data to call the OKX DEX router to execute a swap.
In Uni v3 pools, the following scenario may occur: If the liquidity for the desired token pair in the pool is depleted, the pool will only consume part of the payment token, leaving a remainder. As a fully decentralized smart contract, the OKX DEX Router will automatically refund the remainder. During your integration, please ensure compatibility with this scenario by configuring your contract to support token refunds, thereby ensuring a smooth user experience.
### Request URL
GET `https://web3.okx.com/api/v6/dex/aggregator/swap`
## Request Parameters
| Parameter | Type | Required | Description |
|-------------------|--------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| chainIndex | String | No | Unique identifier for the chain. |
| amount | String | Yes | The input amount of a token to be sold (set in minimal divisible units, e.g., 1.00 USDT set as 1000000, 1.00 DAI set as 1000000000000000000), you could get the minimal divisible units from [Token Basic Information](../market../market/market-token-basic-info). |
| swapMode | String | Yes | Possible values: [`exactIn`, `exactOut`]. Default: `exactIn`. `exactOut` is for supporting use cases where you need an exact output amount.
Note: 1.ExactOut feature currently only support **Ethereum、Base、BSC 、Arbitrum chain**. 2.ExactOut feature currently support only **Uni v3 protocols** 3.In this case the slippage is on the input token.|
| fromTokenAddress | String | Yes | The contract address of a token you want to send (e.g.,`0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`) |
| toTokenAddress | String | Yes | The contract address of a token you want to receive (e.g.,`0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48`) |
| slippagePercent | String | Yes | Slippage limit.
Note: 1. For EVM networks, the slippage setting has a minimum value of `0` and a maximum value of `100`. 2. For Solana, the slippage setting has a minimum value of `0` and a maximum value of less than `100`. (For example: `0.5` means that the maximum slippage for this transaction is `0.5%`.) |
| userWalletAddress | String | Yes | User's wallet address (e.g.,`0x3f6a3f57569358a512ccc0e513f171516b0fd42a`) |
| approveTransaction | Boolean | No | Defaults to false. When enabled, the authorized address and the authorized calldata will be returned in `signatureData`. |
| approveAmount | String | No | The amount of token that needs to be permitted (set in minimal divisible units, e.g., `1.00` USDT set as `1000000`, you could get the minimal divisible units from [Token Basic Information](../market/market-token-basic-info).) |
| swapReceiverAddress | String | No | Recipient address of a purchased token if not set, `userWalletAddress` will receive a purchased token (e.g.,`0x3f6a3f57569358a512ccc0e513f171516b0fd42a`) |
| feePercent | String | No | The percentage of fromTokenAmount will be sent to the referrer's address, the rest will be set as the input amount to be sold. min percentage> 0 max percentage: 10 for Solana, 3 for all other chains. By configuring this parameter, you can obtain the final amount of totoken provided to the user after deducting the commission from fromtoken. A maximum of nine decimal places is allowed. If more decimals are entered, the system will automatically round up.|
| fromTokenReferrerWalletAddress | String | No | The wallet address that receives the commission fee for the fromToken. When using the API, you must set the commission rate together with feePercent, and for each transaction, you can only choose either fromToken commission or toToken commission.
Note: 1. For **Solana**: The referrer wallet must have some SOL deposited in advance to activate the address. 2. For **TON**: Only commission through liquidity pools of Stonfi V2 and Dedust is supported; commission through Stonfi V1 liquidity pools is not supported. 3.For **BSC Chain**: Commission split for swaps via Four.meme is not supported|
| toTokenReferrerWalletAddress | String | No | The wallet address that receives the commission fee for the toToken. When using the API, you must set the commission rate together with feePercent, and for each transaction, you can only choose either fromToken commission or toToken commission.
Note: 1. For **Solana**: The referrer wallet must have some SOL deposited in advance to activate the address. 2. For **TON**: Only commission through liquidity pools of Stonfi V2 is supported. 3.For **BSC Chain**: Commission split for swaps via Four.meme is not supported |
| positiveSlippagePercent | String | No | This feature is open to **whitelist or enterprise clients** only. If you want to access it , please contact dexapi@okx.com
Once configured, a fee can be charged on the quote improvement portion, capped at 10% of the total trade amount.
The cap can be adjusted by specifying a custom percentage parameter.The default setting is 0. `Min percentage`: 0 ,`Max percentage`: 10 , Maximum of 1 decimal point. Currently, this parameter is only supported on the **Solana chain**. |
| positiveSlippageFeeAddress | String | No | This feature is open to **whitelist or enterprise clients** only. If you want to use it , please contact dexapi@okx.com
The wallet address that receives positive slippage. You must set positiveSlippagePercent parameter together to specify the proportion. If provided, all positive slippage earnings will be sent to this address; if not provided, the wallet address used for collecting referral fees will be used instead. |
| gasLimit | String | No | The gas(In smallest units : wei) for the swap transaction. If the value is too low to achieve the quote, an error will be returned. Only applicable to EVM|
| gasLevel | String | No | ( defaults to `average`) The target gas price level for the swap transaction,set to `average` or `fast` or `slow` |
| computeUnitPrice | String | No | Used for transactions on the Solana network and similar to gasPrice on Ethereum. This price determines the priority level of the transaction. The higher the price, the more likely that the transaction can be processed faster. |
| computeUnitLimit | String | No | Used for transactions on the Solana network and analogous to gasLimit on Ethereum, which ensures that the transaction won’t take too much computing resource. If the parameter `tips` is not 0, then `computeUnitPrice` should be set to 0. Otherwise, the fee is wasted. |
| tips | String | No | Jito tips in SOL. The maximum is "2" and the minimum is "0.0000000001". This is used for MEV protection. Specify `tips` to obtain calldata and call the [broadcast transaction API](./onchain-gateway-api-broadcast-transaction)。
| dexIds | String | No | DexId of the liquidity pool for limited quotes, multiple combinations separated by `,` (e.g., `1,50,180`, see liquidity list for more) |
| excludeDexIds | String | No | The dexId of the liquidity pool will not be used, multiple combinations separated by `,` (e.g.,`1,50,180`, see liquidity list for more) |
| disableRFQ | Boolean| No | Disable all liqudity source classified as RFQs that have dependencies on time-sensitive quotes. The default setting is false. |
| directRoute | Boolean | No | The default setting is false. When enabled, Direct Routes restrict our routing to a single liquidity pool only. Currently, this feature is only active for Solana swaps. |
| singleRouteOnly | Boolean| No | Default is false. When enabled, routing is restricted to a single route.Multi-hop and multi-pool routes are allowed, but no parallel split routes will be constructed |
| singlePoolPerHop | Boolean| No | Default is false. When enabled, each hop in the route is restricted to a single pool. |
| priceImpactProtectionPercent | String | No | ( The default is 90%.) The percentage (between 0 - 100) of the price impact allowed.
When the priceImpactProtectionPercent is set, if the estimated price impact is above the percentage indicated, an error will be returned. For example, if priceImpactProtectionPercent = 25, any quote with a price impact higher than 25% will return an error. When it’s set to 100, the feature will be disabled, which means that every transaction will be allowed to pass. Note: If we’re unable to calculate the price impact, we’ll return null, and the price impact protection will be disabled. |
| callDataMemo | String | No | You can customize the parameters to be sent on the blockchain in callData by encoding the data into a 128-character 64-bytes hexadecimal string. For example, the string “0x...111” needs to keep the “0x” at its start. |
| autoSlippage | Boolean | No | Default is false. When set to true, the original slippage (if set) will be covered by the autoSlippage and the API will calculate and return auto slippage recommendations based on current market data. |
| maxAutoSlippagePercent | String | No | When autoSlippage is set to true, this value is the maximum auto slippage returned by the API(e.g., 0.5 represents 0.5%). We recommend that users adopt this value to ensure risk control. |
## Response Parameters
| Parameter | Type | Description |
|------------------|--------|---------------------------------------------------------------------------------------------------------------------------------------|
| ***routerResult*** | ***Object*** | ***Quote path data*** |
| chainIndex | String | Unique identifier for the chain. |
| swapMode | String | Swap mode of this quote. |
| fromTokenAmount | String | The input amount of a token to be sold ( e.g.,`500000000000000000000000`) |
| toTokenAmount | String | The resulting amount of a token to be bought ( e.g.,`168611907733361`) |
| tradeFee | String | Estimated network fee (USD) of the quote route |
| estimateGasFee | String | Estimated gas consumption is returned in the smallest units of each chain, such as wei. |
| ***dexRouterList*** | ***Array*** | ***Quote path data set*** |
| router | String | Main path for the token swap |
| ***dexProtocol*** | ***Object*** | ***Liquidity protocols used on the main path*** |
| dexName | String | The name of the liquidity protocol (e.g.,`Verse`) |
| percent | String | The percentage of assets handled by the protocol (e.g.,`100`) |
| ***fromTokeIndexn*** | ***String*** | ***Token Index represents the position of from token during routing.*** |
| ***fromToken*** | ***Object*** | ***The information of a token to be sold*** |
| tokenContractAddress | String | Token contract address (e.g.,`0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48`) |
| tokenSymbol | String | Token symbol (e.g.,`USDC`) |
| tokenUnitPrice | String | The token unit price returned by this interface is a general USDis a general USD real time price based on data from on-chain sources. Note: This price is only a recommended price. For some special cases, the token unit price may be 'null' |
| decimal | String | The decimal number defines the smallest unit into which a single currency token can be divided. For example, if the decimal number of a token is 8, it means that a single such token can be divided into 100,000,000 of its smallest units. ***Note: This parameter is for reference only. It may change due to reasons such as settings adjustments by the contract owner.*** |
| isHoneyPot | Boolean | If the token is a honeypot token. `yes:true` `no:false ` |
| taxRate | String | Token tax rate for selling: Applicable to tokens with configurable tax mechanisms (e.g., SafeMoon, SPL2022 tokens). Returns 0 for regular tokens without tax. The value ranges from 0 to 1, where 0.01 represents 1%. |
| ***toTokenIndex*** | ***String*** | ***Token Index represents the position of to token during routing.*** |
| ***toToken*** | ***Object*** | ***The information of a token to be bought*** |
| tokenContractAddress | String | Token contract address (e.g.,`0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48`) |
| tokenSymbol | String | Token symbol (e.g.,`USDC`) |
| tokenUnitPrice | String | The token unit price returned by this interface is a general USD price based on data from on-chain, exchange, and other third-party sources. Note: This price is only a recommended price. For some special cases, the token unit price may be 'null' |
| decimal | String | The decimal number defines the smallest unit into which a single currency token can be divided. For example, if the decimal number of a token is 8, it means that a single such token can be divided into 100,000,000 of its smallest units. ***Note: This parameter is for reference only. It may change due to reasons such as settings adjustments by the contract owner.*** |
| isHoneyPot | Boolean | If the token is a honeypot token. `yes:true` `no:false ` |
| taxRate | String | Token tax rate for buying: Applicable to tokens with configurable tax mechanisms (e.g., SafeMoon, SPL2022 tokens). Returns 0 for regular tokens without tax. The value ranges from 0 to 1, where 0.01 represents 1%. |
| priceImpactPercent | String | Percentage = (Received value – Paid value) / Paid value. The swap amount will affect the depth of the liquidity pool, causing a value difference. This percentage can be positive if the received value exceeds the paid value. |
| ***tx*** | ***Object*** | ***contract data model*** |
| signatureData | ***Array*** | If this parameter is returned, it indicates that the transaction requires additional signing data. Developers should use this parameter as one of the inputs for the transaction signature and ensure it is correctly applied during the signing process. When you specify the `tips` request parameter, this parameter value represents the calldata of the jito tips transfer. Use it to broadcast transactions, refer to [this API](./onchain-gateway-api-broadcast-transaction) |
| from | String | User's wallet address (e.g.,`0x3f6a3f57569358a512ccc0e513f171516b0fd42a`) |
| gas | String | estimated amount of the gas limit, increase this value by 50% (e.g.,`1173250`). To get accurate data, please take a look at [gas-limit](./onchain-gateway-api-gas-limit) API |
| gasPrice | String | Gas price in wei (e.g.,`58270000000`) |
| maxPriorityFeePerGas | String | EIP-1559: Recommended priority cost of gas per unit (e.g.,`500000000`) |
| to | String | The contract address of OKX DEX router (e.g.,`0x3b3ae790Df4F312e745D270119c6052904FB6790`) |
| value | String | The amount of native tokens (in wei) that will be sent to the contract address (e.g.,`0`) |
| maxSpendAmount | String | The maximum amount of a token to spend when the price reaches the upper limit of slippage (applies to the exactOut mode) |
| minReceiveAmount | String | The minimum amount of a token to buy when the price reaches the upper limit of slippage (e.g.,`900645839798`) |
| data | String | Call data |
| slippagePercent | String | The value of current transaction slippage |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/aggregator/swap?chainIndex=1&amount=100000000000&fromTokenAddress=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&toTokenAddress=0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599&approveAmount=10000000&approveTransaction=true&slippagePercent=0.1&userWalletAddress=0x77660f108043c9e300b4e30a35a61dd19f5ae28a' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"routerResult": {
"chainIndex": "1",
"contextSlot": 24331258,
"dexRouterList": [
{
"dexProtocol": {
"dexName": "Fluid",
"percent": "62"
},
"fromToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"tokenSymbol": "USDC",
"tokenUnitPrice": "0.99965"
},
"fromTokenIndex": "0",
"toToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"tokenSymbol": "USDT",
"tokenUnitPrice": "0.99861"
},
"toTokenIndex": "1"
},
{
"dexProtocol": {
"dexName": "DODO V2",
"percent": "5"
},
"fromToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"tokenSymbol": "USDC",
"tokenUnitPrice": "0.99965"
},
"fromTokenIndex": "0",
"toToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"tokenSymbol": "USDT",
"tokenUnitPrice": "0.99861"
},
"toTokenIndex": "1"
},
{
"dexProtocol": {
"dexName": "Uniswap V4",
"percent": "8"
},
"fromToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"tokenSymbol": "USDC",
"tokenUnitPrice": "0.99965"
},
"fromTokenIndex": "0",
"toToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"tokenSymbol": "USDT",
"tokenUnitPrice": "0.99861"
},
"toTokenIndex": "1"
},
{
"dexProtocol": {
"dexName": "Maverick V2",
"percent": "24"
},
"fromToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"tokenSymbol": "USDC",
"tokenUnitPrice": "0.99965"
},
"fromTokenIndex": "0",
"toToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"tokenSymbol": "USDT",
"tokenUnitPrice": "0.99861"
},
"toTokenIndex": "1"
},
{
"dexProtocol": {
"dexName": "CurveNG",
"percent": "1"
},
"fromToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"tokenSymbol": "USDC",
"tokenUnitPrice": "0.99965"
},
"fromTokenIndex": "0",
"toToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"tokenSymbol": "USDT",
"tokenUnitPrice": "0.99861"
},
"toTokenIndex": "1"
},
{
"dexProtocol": {
"dexName": "Native",
"percent": "100"
},
"fromToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"tokenSymbol": "USDT",
"tokenUnitPrice": "0.99861"
},
"fromTokenIndex": "1",
"toToken": {
"decimal": "8",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
"tokenSymbol": "WBTC",
"tokenUnitPrice": "88645.26492049586"
},
"toTokenIndex": "2"
}
],
"estimateGasFee": "1248837",
"fromToken": {
"decimal": "6",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"tokenSymbol": "USDC",
"tokenUnitPrice": "0.99965"
},
"fromTokenAmount": "100000000000",
"priceImpactPercent": "0.07",
"router": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48--0xdac17f958d2ee523a2206206994597c13d831ec7--0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
"swapMode": "exactIn",
"toToken": {
"decimal": "8",
"isHoneyPot": false,
"taxRate": "0",
"tokenContractAddress": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
"tokenSymbol": "WBTC",
"tokenUnitPrice": "88645.26492049586"
},
"toTokenAmount": "90281915",
"tradeFee": "1.352992935519650381"
},
"tx": {
"data": "0xf2c426960000000000000000000000000000000000000000000000000000000000033d06000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599000000000000000000000000000000000000000000000000000000174876e8000000000000000000000000000000000000000000000000000000000005603711000000000000000000000000000000000000000000000000000000006979aceb00000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000000500000000000000000000000097a7f8be1364759266cc5a619772458cc126b61200000000000000000000000056bd269db96a089295d742351ba459fb0c279fe20000000000000000000000005745050e787f693ed21e4418d528f78ad9c374a60000000000000000000000004e3bcce28caf98a143fd8bd9e4875ccab3e7bbe0000000000000000000000000ecd7eef15713997528896cb5db7ec316db4c2101000000000000000000000000000000000000000000000000000000000000000500000000000000000000000097a7f8be1364759266cc5a619772458cc126b61200000000000000000000000004571c32a4e1c5f39bc3a238cb95b215058c432c0000000000000000000000005745050e787f693ed21e4418d528f78ad9c374a60000000000000000000000004e3bcce28caf98a143fd8bd9e4875ccab3e7bbe0000000000000000000000000ecd7eef15713997528896cb5db7ec316db4c21010000000000000000000000000000000000000000000000000000000000000005000000000000000000011838667701e51b4d1ca244f17c78f7ab8744b4c99f9b8000000000000000000101f404571c32a4e1c5f39bc3a238cb95b215058c432c000000000000000000010320000000000000000000000000000000000000000000000000000000000001096031373595f40ea48a7aab6cbcb0d377c6066e2dca0000000000000000000100644f493b7de8aac7d55f71853688b1f7c8f0243c85000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000000000010000000000000000000000001d27ad3613e84e201bc87929590f95e75454cdc000000000000000000000000000000000000000000000000000000000000000010000000000000000000000001d27ad3613e84e201bc87929590f95e75454cdc00000000000000000000000000000000000000000000000000000000000000001800000000000000001022710a540ec8c73322200d68e1b86c471a5c850854f220000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003e00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005d1a34369686ae59ac97ae4e1df5635ffda9ee7c000000000000000000000000129b3d9a0a6e4beab88f5cb1e57995d72a6e24f10000000000000000000000001d27ad3613e84e201bc87929590f95e75454cdc0000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599000000000000000000000000000000000000000000000000000000174ef64fae0000000000000000000000000000000000000000000000000000000006b9fdaa0000000000000000000000000000000000000000000000000000000006b161840000000000000000000000000000000000000000000000000000000069799f170000000000000000000000000000000000000000000000002ae6d79d5b8581cf0000000000000000000000000000000000000000000000000000000069799ee50000000000000000000000000000000000000000000000000000000069799f030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002db2544f5d245c4492b8e58de6d17ff15700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002800000000000000000000000006044eef7179034319e2c8636ea885b37cbfa9aba000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000041f4a08483b0d36c2b107ddf05bc9596e63d1daf996633dae4bdc9d48bbebf5aec31c126225e0d705efafb952425103bf4ae5725c92f5289a88f31f3803a884fe31b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000416a640cb7bcd355ee971adbbb30efdbb640b612a8c418da8dee981562f9ff66d4063d9f119ca4f95e26a634fb485d46dba89e4d0f7fdafbd45545eb9813b43f2b1c0000000000000000000000000000000000000000000000000000000000000077777777111180000000000000000000000000000000000000000000056197bb777777771111000000000064fa00a9ed787f3793db668bff3e6e6e7db0f92a1b",
"from": "0x77660f108043c9e300b4e30a35a61dd19f5ae28a",
"gas": "1248837",
"gasPrice": "557703374",
"maxPriorityFeePerGas": "500000000",
"maxSpendAmount": "",
"minReceiveAmount": "90191633",
"signatureData": [
"{\"approveContract\":\"0x40aA958dd87FC8305b97f2BA922CDdCa374bcD7f\",\"approveTxCalldata\":\"0x095ea7b300000000000000000000000040aa958dd87fc8305b97f2ba922cddca374bcd7f0000000000000000000000000000000000000000000000000000000000989680\"}"
],
"slippagePercent": "0.1",
"to": "0x5E1f62Dac767b0491e3CE72469C217365D5B48cC",
"value": "0"
}
}
],
"msg": ""
}
```
- [Get Transaction Status](https://web3.okx.com/onchainos/dev-docs/trade/dex-swap-history.md)
{/* api-page */}
# Get Transaction Status
Get the final transaction status of a single-chain swap using `txhash`.
### Request URL
GET `https://web3.okx.com/api/v6/dex/aggregator/history`
## Request Parameters
| Parameter | Type | Required | Description |
|--------------------|--------|----------|----------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| txHash | String | Yes | Transaction hash for a swap initiated via OKX DEX API |
| isFromMyProject | Boolean| No | Set `true` to check if the transaction is under the current API Key. Set `false` or omit to query any OKX DEX API transaction. |
## Response Parameters
| Parameter | Type | Description |
|---------------------|---------|-----------------------------------------------------------------------------|
| chainIndex | String | Unique identifier for the chain. |
| txHash | String | Transaction hash. |
| height | String | Block height where the transaction occurred. |
| txTime | String | Transaction time in Unix timestamp (milliseconds). |
| status | String | Transaction status: `pending` (In Progress), `success` (Success), `fail` (Failure). |
| txType | String | Transaction action: `Approve`, `Wrap`, `Unwrap`, `Swap`. |
| fromAddress | String | Sender's address. |
| dexRouter | String | Interaction address. |
| toAddress | String | Receiver's address. |
| fromTokenDetails | Array | Details of the token being swapped. |
| >symbol | String | Symbol of the token being swapped. |
| >amount | String | Swap amount in the smallest unit (e.g., wei for Ethereum). |
| >tokenAddress | String | Contract address of the token being swapped (e.g., `0xEeeeeEeeeEeEee...`). |
| toTokenDetails | Array | Details of the token received in the swap. |
| >symbol | String | Symbol of the token received. |
| >amount | String | Amount received in the smallest unit. |
| >tokenAddress | String | Contract address of the received token (e.g., `0xa0b86991c6218b36...`). |
| referalAmount | String | Referral fee amount. |
| errorMsg | String | Error message. |
| gasLimit | String | Gas limit for the transaction. |
| gasUsed | String | Gas used in the transaction, in the smallest unit (e.g., wei). |
| gasPrice | String | Gas price in the smallest unit (e.g., wei). |
| txFee | String | Transaction fee, response in the native token amount.Applied in Solana and Sui chain |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/aggregator/history?chainIndex=784&txHash=5GePcvqEakoUtArW8PHULDSQds95vcgeiTznvbnb8hCV' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": {
"chainIndex": "784",
"dexRouter": "0x51159f25f262ae01e87532b673de3b38df8f0ecc2dc0581f1033df6b84b84955",
"errorMsg": "",
"fromAddress": "0x4b9df646075d8621e2578f14818427e4c708709744ea3b827136056f85f88da7",
"fromTokenDetails": {
"amount": "892919000000.000",
"symbol": "HIPPO",
"tokenAddress": "0x8993129d72e733985f7f1a00396cbd055bad6f817fee36576ce483c8bbb8b87b::sudeng::SUDENG"
},
"gasLimit": "",
"gasPrice": "",
"gasUsed": "",
"height": "99502953",
"referralAmount": "892919000",
"status": "success",
"toAddress": "0x4b9df646075d8621e2578f14818427e4c708709744ea3b827136056f85f88da7",
"toTokenDetails": {
"amount": "1532443840.00000000",
"symbol": "SUI",
"tokenAddress": "0x2::sui::SUI"
},
"txFee": "7976416",
"txHash": "5GePcvqEakoUtArW8PHULDSQds95vcgeiTznvbnb8hCV",
"txTime": "1736390263909",
"txType": "swap"
},
"msg": ""
}
```
- [Adding Fees ](https://web3.okx.com/onchainos/dev-docs/trade/dex-api-addfee.md)
# Adding Fees
The OKX Wallet API supports configuring referral fees and fee-receiving addresses for token swaps
You can extend the implementation to include fee parameters in your swap quotes and charge up to 3% per swap from your users for most supported networks; while for Solana you can charge up to 10% per swap.
The OKX DEX API introduces a few API tiers to better support our integration partners. For details, please visit the [API fee](../home/api-fee) page.
```json
// Extended quoteParams with fee support
const quoteParams = {
chainIndex: SOLANA_CHAIN_ID,
amount: rawAmount,
fromTokenAddress,
toTokenAddress,
slippagePercent: "0.5", // 0.5% slippagePercent
userWalletAddress: userAddress,
// Fee-related parameters
fromTokenReferrerWalletAddress: "Your_REFERRER_WALLET_ADDRESS", // Optional: fee receiving address based on fromToken
toTokenReferrerWalletAddress: "Your_REFERRER_WALLET_ADDRESS", // Optional: fee receiving address based on toToken
feePercent: "1.5", // Optional: referrer fee percentage (max 9 decimal points)
} as Record;
```
Important Fee Configuration Notes for feePercent parameter:
- Min percentage > 0. Max percentage: 10 for Solana, 3 for all other chains
- Maximum 9 decimal points, E.g. 1.3269018736% is the actual input, but the final calculation will only adopt 1.326901873%
- For Solana, the fee receiving address must have some SOL deposited for activation
- Each transaction can only choose referrer fee from either the fromToken or the toToken
Example Usage with Fees:
```json
// .. Previous code implementation
// Get swap quote
const quoteParams = {
chainIndex: SOLANA_CHAIN_ID,
amount: rawAmount,
fromTokenAddress,
toTokenAddress,
slippagePercent: "0.5", // 0.5% slippagePercent
userWalletAddress: userAddress,
// Additional Fee params
fromTokenReferrerWalletAddress: "fee-recipient-wallet-address",
feePercent: "1",
// The wallet addresses to receive the referrer fee (Each transaction can only choose referrer fee from either the fromToken or the toToken)
// toTokenReferrerWalletAddress: "Fee receiving address,
// fromTokenReferrerWalletAddress: "Fee receiving address",
} as Record;
const timestamp = new Date().toISOString();
const requestPath = "/api/v6/dex/aggregator/swap";
const queryString = "?" + new URLSearchParams(quoteParams).toString();
const headers = getHeaders(timestamp, "GET", requestPath, queryString);
const response = await fetch(
`https://web3.okx.com${requestPath}${queryString}`,
{ method: "GET", headers }
);
const data = await response.json();
// .. Continue code implementation
```
Command Line Usage with Fees:
```json
# Example: Swap .01 SOL to USDC with 1.5% referrer fee
npx ts-node swap.ts .01 11111111111111111111111111111111 EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v --referrer YOUR_FEE_RECEIVING_ADDRESS --fee 1.5
```
Fee Calculation Example:
For a trade of 100 USDC with a 1.5% fee:
- Fee amount: 1.5 USDC (1.5% of 100 USDC)
- Actual swap amount: 98.5 USDC
- The fee (1.5 USDC) will be sent to the fee receiving address
- [Market Maker Integration](https://web3.okx.com/onchainos/dev-docs/trade/dex-api-market-maker.md)
# Market Maker Integration
## Overview
OKX DEX’s RFQ system is built on-chain and designed for professional market makers to offer efficient, competitive pricing across supported chains. By leveraging off-chain quoting with on-chain settlement, we aim to deliver the best execution for traders in DeFi.
Market makers integrate with OKX DEX to continuously provide pricing data, which is then processed by our smart order router to help users achieve optimal trades.
## Market Making on OKX DEX
To operate as a market maker on OKX DEX, you must connect via our dedicated API suite and fulfill the following requirements:
- Able to stream live price levels once every 1 seconds
- Maintain a RFQ response time below 500 ms
- Return signature for the taker execution
## Order Execution Modes
OKX DEX only supports taker-executed orders. Users receive a firm and executable quote from the market maker. The taker signs and submits the transaction directly.
Taker-executed orders contain the following characteristics:
- The maker is not required to provide a gas fee estimation
- The quote and signature are submitted together
- The taker is responsible for the execution
## Price Levels
Makers are required to stream price levels to OKX DEX servers at least once every 1 seconds for each token pair. Any price level older than 1 seconds is considered a stale quote and will be ignored.
- Quotes must be non-cumulative and level-by-level
- Quotes must a cumulative value of over 200 USD. Otherwise, they will be ignored by the system
- OKX DEX will request price levels every second on each chain, so quotes must be updated frequently to ensure responsiveness and accuracy
## Aggregation and Smart Routing
OKX DEX aggregates liquidity across multiple makers, AMM pools and routes orders based on optimal price and fillability. Our router dynamically splits orders across sources to minimize cost and maximize execution quality.
- Unified order book across all makers
- Partial fills may be requested based on your streamed depth
## Approvals
OKX DEX enables asset transfers through user and market maker token allowances.
We support two approval methods: approving via Permit2 or approving the OKX DEX settlement contract directly.
Market makers must ensure they have granted sufficient approvals to the OKX DEX settlement contract or permit2 contract to cover the size of the levels they stream and the quotes they provide.
## Signing
OKX DEX on EVM supports 2 signature schemes:
- EIP-712
- EIP-1271
OKX DEX on Solana currently only supports Base58-encoded signatures.
## Transaction expiry time
We enforce a fixed transaction expiry timing: 36 seconds for Ethereum and 32 seconds for EVM L2. This simplifies integration by removing the need for market makers to specify custom expiry times in quote requests, providing consistent behavior across all quotes and transactions, and establishing clear timeout boundaries at different stages of the flow.
## Time Decaying
The contract supports a time-based slippage mechanism: if the user does not submit the transaction within a specified time, the price will slip by a defined number of bps per second, with a maximum slippage cap in place.
## Performance Requirements
1. You must maintain a rate of 200 RPS in firm-order, and 50 RPS in pricing
2. For the /pricing endpoint, market makers must update price quotes for each token pair within 1 seconds. We will ping your endpoint every second. If the 1-second window is exceeded, the quote for that token pair on the corresponding chain will be considered invalid.
3. The firm-order response time is 200 ms, and the maximum response time is 500 ms. Any responses above this will be dropped and marked as a failed response.
4. You must keep a successful response rate of over 90%, with at least 90% of orders successfully executed on-chain.
## Settlement Contract Address
```js
const contractAddress = {
ETH: '0x5035D128ef482276Aa3bCce4307ffF8961ba30F9'
ARB: '0xcdC09a6B5211bb51F18A1Af7691B6725bB024434'
Base: '0x4EFBd630205DD9B987c3BcbEe257600abC1e3C11'
BSC: '0xdD30339C4b2f7bac319Ef4Fa5c6963cc9F470B2d'
};
```
## Permit2 Contract Address
```js
const contractAddress = {
ETH: '0x000000000022D473030F116dDEE9F6B43aC78BA3'
ARB: '0x000000000022D473030F116dDEE9F6B43aC78BA3'
Base: '0x000000000022D473030F116dDEE9F6B43aC78BA3'
BSC: '0x000000000022D473030F116dDEE9F6B43aC78BA3'
};
```
## RFQ API Schema
To facilitate the integration into OKX DEX RFQ module, you will need to provide a endpoint for us to return the pricing and firm-order endpoints with the corresponding request and response format.
| Endpoint | Method | URL | Description|
|--------------|--------|--------------------------------------------------------------------|------------|
| Base URL | - | | Example URL that we will register into our API. |
| pricing | GET | | The pricing endpoint get prices from market makers. Your levels will be used to determine if there is a path for a user order |
| firm-order | POST | | Whenever a trader makes an quote request, the OKX DEX servers determine the best way to route that RFQ among the existing Market Makers. The winning Market Maker(s) receive messages and return the order and the signature. |
## API Key
We would require an API key to access your endpoints. Please provide it to us during the registration process. The API Key will be passed to the endpoint as a header X-API-KEY.
## DEX EVM PMM Contract
Please refer to: https://github.com/okxlabs/Web3-DEX-EVM-PMM
## Other
Please provide your project LOGO and official web URL to OKX team
- [Pricing](https://web3.okx.com/onchainos/dev-docs/trade/dex-api-market-maker-pricing.md)
{/* api-page */}
# Pricing
OKX DEX will request relevant data using the parameters below and requires the following token and pricing information to obtain complete quote data:
- Market makers must provide independent (non-cumulative) pricing
- Pricing consists of from/to token amounts and prices
- Includes: trading pair info, each price level, and depth
Example:
A taker wants to swap 1.26 WETH to USDT. The market maker would buy 0.2635658632112683 WETH at the price of 4257.065884207436. The remaining 0.9964341368 WETH would need to be fulfilled by other PMMs or AMMs.
Another taker wants to swap 2000 USDT to WETH. The market maker would buy 1277.8023761262712 USDT at the price of 0.00023477808901049835 and buy 722.1976238737 USDT at a price of 0.00023474489972208067.
## Request Parameters
| Parameter | Type | Required | Description|
|--------------|--------|---------------------------------------------------------------|-----|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
## Response Parameters
| Parameter | Type | Description |
|-------------------- |-------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| chainIndex | String | Unique identifier for the chain. e.g., 1: Ethereum. See more here. |
| levelData | Object | Leveldata are a list of how much quantity is available at what price, quotes must be non-cumulative, level-by-level |
| >takerTokenAddress | String | The contract address of the token being sold by the taker and purchased by the maker (e.g., 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2) |
| >makerTokenAddress | String | The contract address of the token being sold by the maker and purchased by the taker (e.g., 0xa892e1fef8b31acc44ce78e7db0a2dc610f92d00) |
| >levels | String | levels are used to distinguish depth, where the first value is the quantity and the second value is the takerTokenRate, representing the taker/maker token exchange rate. Note: We do not require a minimum liquidity. |
## Request Example
```shell
curl --location --request GET 'https://your-api-endpoint.com/OKXDEX/rfq/pricing?chainIndex=501' \
--header 'X-API-KEY: 37c541a1-****-****-****-10fe7a038418' \
```
## Response Example
```json
{
"code": "0",
"msg": "",
"data": {
"chainIndex": "1",
"levelData": [
{
"takerTokenAddress": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // WETH
"makerTokenAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7", // USDT
"levels": [
[
"0.2635658632112683", // Maker is willing to buy 0.2635658632112683 ETH
"4257.065884207436" // takerTokenRate 1 WETH = 4257.065884207436 USDT
]
]
},
{
"takerTokenAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7", // USDT
"makerTokenAddress": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // WETH
"levels": [
[
"1277.8023761262712", // Maker is willing to buy 1277.8023761262712 USDT
"0.00023477808901049835"//takerTokenRate 1 USDT = 0.00023477808901049835 WETH
],
[
"961.3366422965815",
"0.00023474489972208067"
]
]
}
]
}
}
```
- [Firm order](https://web3.okx.com/onchainos/dev-docs/trade/dex-api-market-maker-firm-order.md)
{/* api-page */}
# Firm order
OKX DEX will request relevant data using the parameters below.
## Request Parameters
| Parameter | Type | Required | Description|
|--------------|--------|---------------------------------------------------------------|-----|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| takerAsset | String | Yes | Address of takerToken |
| makerAsset | String | Yes | Address of makerToken |
| takerAmount | String | Yes | Trade quantity of takerToken |
| takerAddress | String | Yes | Address of Taker |
| rfqId | Long | Yes | A unique identifier assigned to each quote request. |
| expiryDuration | Integer | Yes | This parameter sets the validity duration of a quote or request, indicating the time interval from when the quote/request is generated until it expires. |
| callData | String | No | The transaction needs to be signed, this requirement applies to Solana only.
| beneficiaryAddress | String | No | Address of taker |
| orderId | String | No | |
| confidenceT | Long | No | Slippage start time (in seconds) |
| confidenceRate | Long | No | Slippage per second, 1e6 |
| confidenceCap | Long | No | Maximum slippage cap ,1e6 |
## Response Parameters
| Parameter | Type | Required | Description |
|--------------|--------|---------------------------------------------------------------|------|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| pmmProtocol | String | Yes | settlement contract address |
| makerAmount | String | Yes | Trade quantity of makerToken |
| makerAddress | String | Yes | Address of order signer |
| takerAsset | String | Yes | Address of takerToken |
| makerAsset | String | Yes | Address of makerToken |
| takerAmount | String | Yes | Trade quantity of takerToken |
| takerAddress | String | Yes | Address of Taker |
| rfqId | Long | Yes | A unique identifier assigned to each quote request. |
| expiry | Timestamp | Yes | Expiration time. |
| signature | String | Yes | The signature of market maker. |
| callData | String | No | The signed transaction and the modified calldata.(required for solana) |
| signatureScheme | String | No | EIP-712 or EIP-1271(required for EVM) |
| usePermit2 | Boolean | No | Use Permit2 to approve, default is false.(required for EVM) |
| permit2Signature | String | No | Optional inline Permit2 signature (65 bytes if present) |
| permit2Witness | String | No | Packed witness hash when using Permit2 witnesses |
| permit2WitnessType | String | No | Canonical witness type string for Permit2 |
| confidenceT | Long | No | Request timestamp plus offset |
| confidenceRate | Long | No | Slippage per second, 1e6 |
| confidenceCap | Long | No | Maximum slippage cap ,1e6 |
## Request Example
```json
`firm-order` Solana Example
{
"chainIndex" : "501",
"takerAsset": "11111111111111111111111111111111", // Address of takerToken
"makerAsset": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", // Address of makerToken
"takerAmount": "6000000000000000", // Quantity of takerToken
"takerAddress": "taker address", // Address of order taker
"rfqId": 12345678, // uniqueId for the rfq
"expiryDuration": 60,
"callData": "d1KVBN6xw6sF1YzS5gDGGEp64jSmoB54umQyZeuHU8Mgctmok5vVvekq8DNUoPHDnb7Ydr42CyiQHAgkr8TnGFjk1AVr7yYF5MadmGPuLLRrn7KgMd7VHXccReChopuK8iJ2Co7CNmKULx75VtcZj7UMN2qeSQAPeMeAS2deNny3qiKnHXDYKFZRDyeZWnQrRPeSiithSiqc2fLb3XsN7S82Ho2M2D2Y5VbZnGZrJ7XVuPmTQrA5VXwGZpEYZg9QsqR6biy811YFHhvHTMTzMVbUhG988xyJSdxmVRkXBwbvLM2WCfr1Dppewg9pej9sqTG3zKx4NYSW27H9n5fV6SjgLReZCsX1RosN6Wk9ZpkUUHoWDfiGBCQWNdfaMD1mk8eFYXcgReCG2JDwQw8VRoVMhpUAf31Xyt7Ec2e9ug2X6XpXCctk4Adh9UMEJRqs7agEEzZwx638Cm99WfnDh7scDLBMYp4UaAkSmDVvnT8UpQoehrSxJefdzawFXhxVifkfPxi1VNKc4uinyHc4UWdhn9FFp37qJq2WsiACbRwEBmV8SQrZ77AL7MwcUPD8RqyR57Pxezk8KpH9PgEj7g6yRQjUzowDmesP5U9uVPSywUtgKDbtWVJXq4SgQUuSXY5YGWwxkQ6HYkPe6ga7ntziGfBQFbb7t9z1MDw6KAZcP7YPwGC9biEjgQxMyEaCBWzXDHWSvpYdrcLX4HRRLp8cWA3QBPcTZ6JFLFnRHLtn9fBwhz9G8v2544VzdNoVCj3pMsS9UZapDSnzbTFi2xKcyNQtDnRnnumUdz7pS4XtGz8V4tnfNozWCRC4TRg77vcCk81r8dPGrXKZQa2vusp7EvjtUyinmEuEhJvbwuvmRc1YsbrqibrRD2r6XEWFKLFE5adWz8gSZWwTj6ZWZDZtBw2QqVd1cSkuR1tEEaDs91nNGcgRitXeTJTuRNpGJWhcgNotEmK3NWTSmJZCiL7qaZeoatnYcVd6X3axxnyr2Dz4SwQePnVi5wVwJPtNiaWZ1V9kqpNwjTfuuk1aRyaWT55LGL71wSBWBgVuNxUYu9jTQ6cpyXxQiHYMHdhEcpdxYPzwe4gEx2EbPVSgL5mazUJHJoTwwhj9CFSsaXeiMaH2QsBw1TZVvLJboMw7Sa7eeAF6S9Q4CYyh4YSyjL6oLMmutz1a3X4xw3HkJHeEn3M2syP7GVP1xHreS9sso92MWHo3v7PyNgUwm3HyzgMNjXd"
"beneficiaryAddress":"Es2vMFrzaCECmJfrF4H2FYD4KCoNkY11McCe9CenwUYB" // Address of taker
}
`firm-order` EVM Example
{
"chainIndex" : "1",
"takerAsset": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // Address of takerToken
"makerAsset": "0xdac17f958d2ee523a2206206994597c13d831ec7", // Address of makerToken
"takerAmount": "6000000000000000", // Quantity of takerToken
"takerAddress": "taker address", // Address of order taker
"rfqId": 12345678, // uniqueId for the rfq
"expiryDuration": 60
"beneficiaryAddress":"0xf921fb05ed9db87889f413d7fefb2cd4af03beb6" // Address of taker
"orderId": 1002142110, //
"confidenceT": 30, // Slippage start time (in seconds)
"confidenceRate": 75, // Slippage per second, 1e6
"confidenceCap": 1200, // Maximum slippage cap ,1e6
}
```
## Response Example
```json
`firm-order` Solana Example
{
"code": "0",
"msg": "",
"data": {
"chainIndex" : "501",
"rfqId" : 12345678,
"expiry": 172120120102,
"makerAsset": "11111111111111111111111111111111", //Address of makerToken
"takerAsset": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", //Address of takerToken
"makerAddress": "DmTcmrZ7Dz8asHuuvk2G419JMzqdx58brUBidAegaevp", //Address of order signer
"takerAddress": "3owrzVrYU5bpWH6LpTRiG5BQ8F4szDtFXavrBWXRohgW", //Address of trading user
"makerAmount": "100000000", //Trade quantity of makerToken. TakerAmount * takerTokenRate
"takerAmount": "6000000000000000", //Trade quantity of takerToken
"signature": "c64bf62b7619edda019fe491da256b9fbe892fbfeac91f9d1fce168478ad53053dde038584f063fe21e267fcb4e758bcf420036cd2838fe5cbd993ec6d3dde561b",
"callData": "d1KVBN6xw6sF1YzS5gDGGEp64jSmoB54umQyZeuHU8Mgctmok5vVvekq8DNUoPHDnb7Ydr42CyiQHAgkr8TnGFjk1AVr7yYF5MadmGPuLLRrn7KgMd7VHXccReChopuK8iJ2Co7CNmKULx75VtcZj7UMN2qeSQAPeMeAS2deNny3qiKnHXDYKFZRDyeZWnQrRPeSiithSiqc2fLb3XsN7S82Ho2M2D2Y5VbZnGZrJ7XVuPmTQrA5VXwGZpEYZg9QsqR6biy811YFHhvHTMTzMVbUhG988xyJSdxmVRkXBwbvLM2WCfr1Dppewg9pej9sqTG3zKx4NYSW27H9n5fV6SjgLReZCsX1RosN6Wk9ZpkUUHoWDfiGBCQWNdfaMD1mk8eFYXcgReCG2JDwQw8VRoVMhpUAf31Xyt7Ec2e9ug2X6XpXCctk4Adh9UMEJRqs7agEEzZwx638Cm99WfnDh7scDLBMYp4UaAkSmDVvnT8UpQoehrSxJefdzawFXhxVifkfPxi1VNKc4uinyHc4UWdhn9FFp37qJq2WsiACbRwEBmV8SQrZ77AL7MwcUPD8RqyR57Pxezk8KpH9PgEj7g6yRQjUzowDmesP5U9uVPSywUtgKDbtWVJXq4SgQUuSXY5YGWwxkQ6HYkPe6ga7ntziGfBQFbb7t9z1MDw6KAZcP7YPwGC9biEjgQxMyEaCBWzXDHWSvpYdrcLX4HRRLp8cWA3QBPcTZ6JFLFnRHLtn9fBwhz9G8v2544VzdNoVCj3pMsS9UZapDSnzbTFi2xKcyNQtDnRnnumUdz7pS4XtGz8V4tnfNozWCRC4TRg77vcCk81r8dPGrXKZQa2vusp7EvjtUyinmEuEhJvbwuvmRc1YsbrqibrRD2r6XEWFKLFE5adWz8gSZWwTj6ZWZDZtBw2QqVd1cSkuR1tEEaDs91nNGcgRitXeTJTuRNpGJWhcgNotEmK3NWTSmJZCiL7qaZeoatnYcVd6X3axxnyr2Dz4SwQePnVi5wVwJPtNiaWZ1V9kqpNwjTfuuk1aRyaWT55LGL71wSBWBgVuNxUYu9jTQ6cpyXxQiHYMHdhEcpdxYPzwe4gEx2EbPVSgL5mazUJHJoTwwhj9CFSsaXeiMaH2QsBw1TZVvLJboMw7Sa7eeAF6S9Q4CYyh4YSyjL6oLMmutz1a3X4xw3HkJHeEn3M2syP7GVP1xHreS9sso92MWHo3v7PyNgUwm3HyzgMNjXd"
}
}
`firm-order` EVM Example
{
"chainIndex" : "1",
"rfqId" : 12345678,
"expiry": 172120120102,
"pmmProtocol": "0x0Bdf246b4AEF9Cfe4DD6eEf153A1b645aC4BcBb6", //settlement contract address
"makerAsset": "0xdac17f958d2ee523a2206206994597c13d831ec7", //Address of makerToken
"takerAsset": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", //Address of takerToken
"makerAddress": "0xcfdfea67395c531249a9e1dc8d916c9158810509", //Address of order signer
"takerAddress": "0xF36bC73f9783539E1DAC8Cf6d2bfd74e0663699C", //Address of trading user
"makerAmount": "100000000", //Trade quantity of makerToken. TakerAmount * takerTokenRate
"takerAmount": "6000000000000000", //Trade quantity of takerToken
"signature": "0xc64bf62b7619edda019fe491da256b9fbe892fbfeac91f9d1fce168478ad53053dde038584f063fe21e267fcb4e758bcf420036cd2838fe5cbd993ec6d3dde561b",
"sign_scheme": "EIP-712" //EIP-712 or 1271,
"usePermit2": false
"confidenceT": 1769767048, // Request timestamp plus offset
"confidenceRate": 75, // Slippage per second, 1e6
"confidenceCap": 1200, // Maximum slippage cap ,1e6
}
```
- [EVM Signature](https://web3.okx.com/onchainos/dev-docs/trade/dex-api-market-maker-sdk.md)
# EVM Signature
## EVM EIP-712 Signature Debug Tool
https://okxlabs.github.io/DEX-Router-Tools-Suite/docs/
## EVM Signature SDK
```javascript
// signOrderRFQ.js
import { Wallet, ethers } from "ethers";
/**
* Sign an OrderRFQ-typed struct and return the signature string
*
* @param {string} privateKey - Signer's private key (EOA)
* @param {string} verifyingContract - Address of the contract used for signature verification
* @param {number} chainId - Current chain ID
* @param {object} order - Order object containing fields like rfqId, expiration, etc.
* @returns {Promise} - EIP-712 signature string
*/
export async function signOrderRFQ({ privateKey, verifyingContract, chainId, order }) {
const wallet = new Wallet(privateKey);
const domain = {
name: "OnChain Labs PMM Protocol",
version: "1.0",
chainId,
verifyingContract,
};
// OrderRFQ typehash from Solidity - must match exactly
const ORDER_RFQ_TYPEHASH = ethers.keccak256(ethers.toUtf8Bytes(
"OrderRFQ(uint256 rfqId,uint256 expiry,address makerAsset,address takerAsset,address makerAddress,uint256 makerAmount,uint256 takerAmount,bool usePermit2,bytes permit2Signature,bytes32 permit2Witness,string permit2WitnessType)"
));
// Domain separator calculation matching Solidity
const EIP712_DOMAIN_TYPEHASH = ethers.keccak256(ethers.toUtf8Bytes(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
));
const domainSeparator = ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(
["bytes32", "bytes32", "bytes32", "uint256", "address"],
[
EIP712_DOMAIN_TYPEHASH,
ethers.keccak256(ethers.toUtf8Bytes(domain.name)),
ethers.keccak256(ethers.toUtf8Bytes(domain.version)),
domain.chainId,
domain.verifyingContract
]
));
// Struct hash calculation matching Solidity OrderRFQLib.hash()
const structHash = ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(
["bytes32", "uint256", "uint256", "address", "address", "address", "uint256", "uint256", "bool", "bytes32", "bytes32", "bytes32"],
[
ORDER_RFQ_TYPEHASH,
order.rfqId,
order.expiry,
order.makerAsset,
order.takerAsset,
order.makerAddress,
order.makerAmount,
order.takerAmount,
order.usePermit2,
ethers.keccak256(order.permit2Signature), // Hashed like in Solidity
order.permit2Witness,
ethers.keccak256(ethers.toUtf8Bytes(order.permit2WitnessType)) // Hashed like in Solidity
]
));
// Final digest calculation matching ECDSA.toTypedDataHash
const digest = ethers.keccak256(ethers.concat([
"0x1901",
domainSeparator,
structHash
]));
// Sign the digest directly (EIP-712 signature, no Ethereum message prefix)
// Use signingKey.sign() to sign the raw digest without any prefixes
const sig = wallet.signingKey.sign(digest);
// Reconstruct signature as r + s + v to match Solidity abi.encodePacked(r, s, v)
const rearrangedSignature = ethers.concat([sig.r, sig.s, ethers.toBeHex(sig.v, 1)]);
return ethers.hexlify(rearrangedSignature);
}
export const EXAMPLE_WITNESS_TYPEHASH = ethers.keccak256(ethers.toUtf8Bytes("ExampleWitness(address user)"));
export const WITNESS_TYPE_STRING = "ExampleWitness witness)ExampleWitness(address user)TokenPermissions(address token,uint256 amount)"
export const TOKEN_PERMISSIONS_TYPEHASH = ethers.keccak256(ethers.toUtf8Bytes("TokenPermissions(address token,uint256 amount)"));
/**
* Calculate permit2 witness hash from witness data
*
* @param {object} witnessData - Witness data object (e.g., { user: address })
* @param {string} witnessTypehash - Keccak256 hash of the witness type string
* @returns {string} - Witness hash as bytes32
*/
export function calculateWitness(witnessData, witnessTypehash = EXAMPLE_WITNESS_TYPEHASH) {
// For ExampleWitness struct: { user: address }
const encodedWitness = ethers.AbiCoder.defaultAbiCoder().encode(
["bytes32", "address"],
[witnessTypehash, witnessData.user]
);
return ethers.keccak256(encodedWitness);
}
/**
* Sign Permit2 with witness support
*
* @param {object} permit - Permit2 PermitTransferFrom object with { permitted: { token, amount }, nonce, deadline }
* @param {string} spender - Spender address (usually the PMM contract)
* @param {string} witness - Witness hash (bytes32)
* @param {string} witnessTypeString - Full witness type string for EIP-712
* @param {string} privateKey - Signer's private key
* @param {string} permit2DomainSeparator - Permit2 contract's domain separator
* @returns {Promise} - Permit2 signature
*/
export async function signPermit2WithWitness({
permit,
spender,
witness,
witnessTypeString,
privateKey,
permit2DomainSeparator
}) {
const wallet = new Wallet(privateKey);
const TOKEN_PERMISSIONS_TYPEHASH = ethers.keccak256(
ethers.toUtf8Bytes("TokenPermissions(address token,uint256 amount)")
);
// Construct the full type hash for PermitWitnessTransferFrom
const PERMIT_WITNESS_TRANSFER_FROM_TYPEHASH = ethers.keccak256(
ethers.toUtf8Bytes(
`PermitWitnessTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline,${witnessTypeString}`
)
);
// Encode the TokenPermissions struct
const tokenPermissionsHash = ethers.keccak256(
ethers.AbiCoder.defaultAbiCoder().encode(
["bytes32", "address", "uint256"],
[TOKEN_PERMISSIONS_TYPEHASH, permit.permitted.token, permit.permitted.amount]
)
);
// Encode the main struct
const structHash = ethers.keccak256(
ethers.AbiCoder.defaultAbiCoder().encode(
["bytes32", "bytes32", "address", "uint256", "uint256", "bytes32"],
[
PERMIT_WITNESS_TRANSFER_FROM_TYPEHASH,
tokenPermissionsHash,
spender,
permit.nonce,
permit.deadline,
witness
]
)
);
// Create the final digest
const digest = ethers.keccak256(
ethers.concat([
"0x1901",
permit2DomainSeparator,
structHash
])
);
// Sign the digest directly (EIP-712 signature, no Ethereum message prefix)
// Use _signingKey().sign() to sign the raw digest without any prefixes
const sig = wallet.signingKey.sign(digest);
// Reconstruct signature as r + s + v to match Solidity abi.encodePacked(r, s, v)
const rearrangedSignature = ethers.concat([sig.r, sig.s, ethers.toBeHex(sig.v, 1)]);
return ethers.hexlify(rearrangedSignature);
}
```
## EVM Signing Example
```javascript
import { signOrderRFQ, calculateWitness, WITNESS_TYPE_STRING, signPermit2WithWitness } from "./signOrderRFQ.js";
const currentTime = Math.floor(Date.now() / 1000);
const expiry = currentTime + 90;
const MAKER_ADDRESS = "YOUR_ADDRESS";
const privateKey = "YOUR_PRIVATE_KEY";
const VERIFYING_CONTRACT = "0x5C1c902e7E04DE98b49aCd3De68E12BEE2d7908D";
const PERMIT2_DOMAIN_SEPARATOR = "0x8a6e6e19bdfb3db3409910416b47c2f8fc28b49488d6555c7fceaa4479135bc3";
const MAKER_ASSET = "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1";
const TAKER_ASSET = "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9";
const MAKER_AMOUNT = 400000000000000;
const TAKER_AMOUNT = 1000;
const chainId = 42161;
const rfqId = 42;
// Order 1: usePermit2: false
const order1 = {
privateKey: privateKey,
verifyingContract: VERIFYING_CONTRACT,
chainId: chainId,
order: {
rfqId: rfqId,
expiry: expiry,
makerAsset: MAKER_ASSET,
takerAsset: TAKER_ASSET,
makerAddress: MAKER_ADDRESS,
makerAmount: MAKER_AMOUNT,
takerAmount: TAKER_AMOUNT,
usePermit2: false,
permit2Signature: "0x",
permit2Witness: "0x0000000000000000000000000000000000000000000000000000000000000000",
permit2WitnessType: ""
},
};
// Order 2: usePermit2: true, no witness
const order2 = {
privateKey: privateKey,
verifyingContract: VERIFYING_CONTRACT,
chainId: chainId,
order: {
rfqId: rfqId,
expiry: expiry,
makerAsset: MAKER_ASSET,
takerAsset: TAKER_ASSET,
makerAddress: MAKER_ADDRESS,
makerAmount: MAKER_AMOUNT,
takerAmount: TAKER_AMOUNT,
usePermit2: true,
permit2Signature: "0x",
permit2Witness: "0x0000000000000000000000000000000000000000000000000000000000000000",
permit2WitnessType: ""
},
};
// Order 3: usePermit2: true, with witness
const order3 = {
privateKey: privateKey,
verifyingContract: VERIFYING_CONTRACT,
chainId: chainId,
order: {
rfqId: rfqId,
expiry: expiry,
makerAsset: MAKER_ASSET,
takerAsset: TAKER_ASSET,
makerAddress: MAKER_ADDRESS,
makerAmount: MAKER_AMOUNT,
takerAmount: TAKER_AMOUNT,
usePermit2: true,
permit2Signature: await signPermit2WithWitness({
permit: {
permitted: {
token: MAKER_ASSET,
amount: MAKER_AMOUNT
},
nonce: rfqId,
deadline: expiry
},
spender: VERIFYING_CONTRACT,
witness: calculateWitness({ user: MAKER_ADDRESS }),
witnessTypeString: WITNESS_TYPE_STRING,
privateKey: privateKey,
permit2DomainSeparator: PERMIT2_DOMAIN_SEPARATOR
}),
permit2Witness: calculateWitness({ user: MAKER_ADDRESS }),
permit2WitnessType: WITNESS_TYPE_STRING
},
};
console.log("Signature 1:", await signOrderRFQ(order1));
console.log("Signature 2:", await signOrderRFQ(order2));
console.log("Signature 3:", await signOrderRFQ(order3));
console.log("permit2Signature (Order 3):", order3.order.permit2Signature);
```
## EVM testSignOrder
```javascript
import "dotenv/config";
import { ethers } from "ethers";
import { calculateWitness, calculateWitnessConsideration, signOrderRFQ, signPermit2WithWitness, WITNESS_TYPE_STRING } from "./signOrderRFQ.js";
const currentTime = Math.floor(Date.now() / 1000);
const expiry = currentTime + 1000*60 * 60 * 24 * 30;
const privateKey = process.env.PK;
const MAKER_ADDRESS = new ethers.Wallet(privateKey).address;
const VERIFYING_CONTRACT = "0x1Ef032a3c471a99CC31578c8007F256D95E89896";
const PERMIT2_DOMAIN_SEPARATOR = "0x8a6e6e19bdfb3db3409910416b47c2f8fc28b49488d6555c7fceaa4479135bc3";
const MAKER_ASSET = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831";
const TAKER_ASSET = "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9";
const MAKER_AMOUNT = 100;
const TAKER_AMOUNT = 90;
const chainId = 42161;
const rfqId = Math.floor(Math.random() * 10000000000000);
// Order 1: usePermit2: false
const order1 = {
privateKey: privateKey,
verifyingContract: VERIFYING_CONTRACT,
chainId: chainId,
order: {
rfqId: rfqId,
expiry: expiry,
makerAsset: MAKER_ASSET,
takerAsset: TAKER_ASSET,
makerAddress: MAKER_ADDRESS,
makerAmount: MAKER_AMOUNT,
takerAmount: TAKER_AMOUNT,
usePermit2: false,
permit2Signature: "0x",
permit2Witness: "0x0000000000000000000000000000000000000000000000000000000000000000",
permit2WitnessType: ""
},
};
// Order 2: usePermit2: true, no witness
const order2 = {
privateKey: privateKey,
verifyingContract: VERIFYING_CONTRACT,
chainId: chainId,
order: {
rfqId: rfqId,
expiry: expiry,
makerAsset: MAKER_ASSET,
takerAsset: TAKER_ASSET,
makerAddress: MAKER_ADDRESS,
makerAmount: MAKER_AMOUNT,
takerAmount: TAKER_AMOUNT,
usePermit2: true,
permit2Signature: "0x",
permit2Witness: "0x0000000000000000000000000000000000000000000000000000000000000000",
permit2WitnessType: ""
},
};
// Order 3: usePermit2: true, with witness
const order3 = {
privateKey: privateKey,
verifyingContract: VERIFYING_CONTRACT,
chainId: chainId,
order: {
rfqId: rfqId,
expiry: expiry,
makerAsset: MAKER_ASSET,
takerAsset: TAKER_ASSET,
makerAddress: MAKER_ADDRESS,
makerAmount: MAKER_AMOUNT,
takerAmount: TAKER_AMOUNT,
usePermit2: true,
permit2Signature: await signPermit2WithWitness({
permit: {
permitted: {
token: MAKER_ASSET,
amount: MAKER_AMOUNT
},
nonce: rfqId,
deadline: expiry
},
spender: VERIFYING_CONTRACT,
witness: calculateWitness({ user: MAKER_ADDRESS }),
witnessTypeString: WITNESS_TYPE_STRING,
privateKey: privateKey,
permit2DomainSeparator: PERMIT2_DOMAIN_SEPARATOR
}),
permit2Witness: calculateWitness({ user: MAKER_ADDRESS }),
permit2WitnessType: WITNESS_TYPE_STRING
},
};
const TAKER_ADDRESS = "0x1111111111111111111111111111111111111111";
const CONSIDERATION = {
token: MAKER_ASSET,
amount: MAKER_AMOUNT,
counterparty: TAKER_ADDRESS
};
const CONSIDERATION_TYPE_STRING_STUB = "Consideration witness)Consideration(address token,uint256 amount,address counterparty)TokenPermissions(address token,uint256 amount)";
// Order 4: usePermit2: true, with witness
const order4 = {
privateKey: privateKey,
verifyingContract: VERIFYING_CONTRACT,
chainId: chainId,
order: {
rfqId: rfqId,
expiry: expiry,
makerAsset: MAKER_ASSET,
takerAsset: TAKER_ASSET,
makerAddress: MAKER_ADDRESS,
makerAmount: MAKER_AMOUNT,
takerAmount: TAKER_AMOUNT,
usePermit2: true,
permit2Signature: await signPermit2WithWitness({
permit: {
permitted: {
token: MAKER_ASSET,
amount: MAKER_AMOUNT
},
nonce: rfqId,
deadline: expiry
},
spender: VERIFYING_CONTRACT,
witness: calculateWitnessConsideration(CONSIDERATION),
witnessTypeString: CONSIDERATION_TYPE_STRING_STUB,
privateKey: privateKey,
permit2DomainSeparator: PERMIT2_DOMAIN_SEPARATOR
}),
permit2Witness: calculateWitnessConsideration(CONSIDERATION),
permit2WitnessType: CONSIDERATION_TYPE_STRING_STUB
},
};
// console.log("Signature 1:", await signOrderRFQ(order1));
// console.log("Signature 2:", await signOrderRFQ(order2));
// console.log("Signature 3:", await signOrderRFQ(order3));
console.log("CONSIDERATION:", CONSIDERATION);
console.log("order4:", order4);
const order4Signature = await signOrderRFQ(order4);
console.log("Signature 4:", order4Signature);
// send tx to fill order (optional)
// Requirements:
// - export RPC_URL=https://arb1.arbitrum.io/rpc (or your node)
// - export TAKER_PK=0x...
// - export SEND_TX=1
const sendTx = async () => {
const rpcUrl = process.env.RPC_URL || "https://arb1.arbitrum.io/rpc";
const takerPk = process.env.PK;
if (!takerPk) throw new Error("missing env: TAKER_PK");
const provider = new ethers.JsonRpcProvider(rpcUrl, chainId);
const taker = new ethers.Wallet(takerPk, provider);
const pmmAbi = [
"function fillOrderRFQTo((uint256 rfqId,uint256 expiry,address makerAsset,address takerAsset,address makerAddress,uint256 makerAmount,uint256 takerAmount,bool usePermit2,bytes permit2Signature,bytes32 permit2Witness,string permit2WitnessType) order, bytes signature, uint256 flagsAndAmount, address target) returns (uint256,uint256,bytes32)",
];
const pmm = new ethers.Contract(VERIFYING_CONTRACT, pmmAbi, taker);
const flagsAndAmount = BigInt(order4.order.takerAmount);
const target = taker.address;
const tx = await pmm.fillOrderRFQTo(order4.order, order4Signature, flagsAndAmount, target);
console.log("fill tx:", tx.hash);
await tx.wait();
}
sendTx();
// console.log("permit2Signature (Order 3):", order3.order.permit2Signature);
```
- [Smart Contract](https://web3.okx.com/onchainos/dev-docs/trade/dex-smart-contract.md)
# Smart Contract
The contract addresses of OKX DEX router and ABI
## Contract Address
The contract addresses of the DEX router and token approval may be subject to replacement due to contract upgrades. To ensure uninterrupted use of the API, we recommend using the contract addresses returned by the response parameters: `/approve-transaction` API and `/swap` API for approvals and transactions.
### DEX Router
| Chain | DEX router address |
|----------------|----------------------------------------------|
| Ethereum | 0x28b1Dc1a5E3699A428BC51d234DFab7C9CB2a183 |
| Solana | proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u |
| SUI | 0x0705aa6aad0a6195d1da7f1b1b057d58684040459c375db056059b1747d5d1f0 extended: 0xfb62ac68863a8cf5066a04171ea173c49051049a460163304bd4a938f0a7ef3a extended: 0xab71c2c2c37f973e28b2d28847046615bf47acc85ffc3ba2eb3d9a6442b18422 SUI packge id 大小限制,部署 extended 合约,支持 Momentum、Scallop、Haedal、Alphafi 等流动性)|
| Sonic | 0x79f7C6C6dc16Ed3154E85A8ef9c1Ef31CEFaEB19 |
| Tron | TAGVH5t42MuofaAfUauPPRe4Qw3i8Z3QHM |
| Ton | EQAgvOlWk7C0Pz3YgSaX-MA7UDDhE9n6eQgQRwJahOBm4VKr |
| zkSync Era | 0x6f7c20464258c732577c87a9B467619e03e5C158 |
| Optimism | 0xDd5E9B947c99Aa60bab00ca4631Dce63b49983E7 |
| Polygon | 0xF6E1B4b201e220FC3741bd7a75675ffEA25c02AD |
| BNB Chain | 0x62cceF0b4545166f721cAa9fEe13c1d3767E27dc |
| Avalanche C | 0xa94Fcf9fc56a864f8DE51e6315aee5863AD63C91 |
| Fantom | 0x25e7f77F33206d311A0130D4b5B881E5Db1181b1 |
| Arbitrum | 0x7CF6b330b437E9fb432B1400DE17B03357Cf049A |
| Linea | 0x2E1Dee213BA8d7af0934C49a23187BabEACa8764 |
| Conflux eSpace | 0x95418635f012fFd10eAFcDaF4137e90371f06917 |
| Base | 0xC8F6b8Ba0DC0f175B568B99440B0867F69A29265 |
| Mantle | 0xcF76984119C7f6ae56fAfE680d39C08278b7eCF4 |
| Scroll | 0x5e2F47bD7D4B357fCfd0Bb224Eb665773B1B9801 |
| Manta | 0x69C236E021F5775B0D0328ded5EaC708E3B869DF |
| Metis | 0x25e7f77F33206d311A0130D4b5B881E5Db1181b1 |
| Blast | 0xcF76984119C7f6ae56fAfE680d39C08278b7eCF4 |
| Zeta | 0xF5402CCC5fC3181B45D7571512999D3Eea0257B6 |
| Polygon zkEvm | 0x6f7c20464258c732577c87a9B467619e03e5C158 |
| Merlin | 0x6f7c20464258c732577c87a9B467619e03e5C158 |
| X Layer | 0xbec6d0E341102732e4FD62EC50E2F0a9D1bd1D33 |
| UniChain | 0x6733Eb2E75B1625F1Fe5f18aD2cB2BaBDA510d19 |
| Cronos | 0x25e7f77F33206d311A0130D4b5B881E5Db1181b1 |
| Plasma | 0x19D345f95A80cc136d898f41b490E023cFF78658 |
| Monad | 0x7A7AD9aa93cd0A2D0255326E5Fb145CEc14997FF |
DEX Router Addresses for OKX DEX used in signing exactOut transactions
| Chain Name | DEX Router Contract Address |
|----------------|--------------------------------------------------|
| Ethereum | 0xa875Fb2204cE71679BE054d97f7fAFFeb6536D67 |
| Base | 0x77449Ff075C0A385796Da0762BCB46fd5cc884c6 |
| BNB Chain | 0x5cb43Bae4f36E2f9f858232B4Dce0dbE27bb85e3 |
| Arbitrum | 0x9736d9a45115E33411390EbD54e5A5C3A6E25aA6 |
### Token Approval
A list of smart contracts for ERC-20 token approval.
Ton and Solana chains do not require authorization.
| Chain | Approval contract address |
|----------------|--------------------------------------------|
| Ethereum | 0x40aA958dd87FC8305b97f2BA922CDdCa374bcD7f |
| Tron | THRAE2VhGNAcvPKtT96AqyXtSQwhiU1XL8 |
| Sonic | 0xd321ab5589d3e8fa5df985ccfef625022e2dd910 |
| zkSync Era | 0xc67879F4065d3B9fe1C09EE990B891Aa8E3a4c2f |
| Optimism | 0x68D6B739D2020067D1e2F713b999dA97E4d54812 |
| Polygon | 0x3B86917369B83a6892f553609F3c2F439C184e31 |
| BNB Chain | 0x2c34A2Fb1d0b4f55de51E1d0bDEfaDDce6b7cDD6 |
| OKC | 0x70cBb871E8f30Fc8Ce23609E9E0Ea87B6b222F58 |
| Avalanche C | 0x40aA958dd87FC8305b97f2BA922CDdCa374bcD7f |
| Fantom | 0x70cBb871E8f30Fc8Ce23609E9E0Ea87B6b222F58 |
| Arbitrum | 0x70cBb871E8f30Fc8Ce23609E9E0Ea87B6b222F58 |
| Linea | 0x57df6092665eb6058DE53939612413ff4B09114E |
| Conflux eSpace | 0x68D6B739D2020067D1e2F713b999dA97E4d54812 |
| Base | 0x57df6092665eb6058DE53939612413ff4B09114E |
| Mantle | 0x57df6092665eb6058DE53939612413ff4B09114E |
| Scroll | 0x57df6092665eb6058DE53939612413ff4B09114E |
| Manta | 0x57df6092665eb6058DE53939612413ff4B09114E |
| Metis | 0x57df6092665eb6058DE53939612413ff4B09114E |
| Blast | 0x5fD2Dc91FF1dE7FF4AEB1CACeF8E9911bAAECa68 |
| Zeta | 0x03B5ACdA01207824cc7Bc21783Ee5aa2B8d1D2fE |
| Polygon zkEvm | 0x57df6092665eb6058DE53939612413ff4B09114E |
| Merlin | 0x8b773D83bc66Be128c60e07E17C8901f7a64F000 |
| X Layer | 0x8b773D83bc66Be128c60e07E17C8901f7a64F000 |
| UniChain | 0x2e28281Cf3D58f475cebE27bec4B8a23dFC7782c |
| Cronos | 0x70cbb871e8f30fc8ce23609e9e0ea87b6b222f58 |
| Plasma | 0x9FD43F5E4c24543b2eBC807321E58e6D350d6a5A |
| Monad | 0xf534A8a1CAD0543Cd6438f7534CA3486c01998d4 |
## Contract Application Binary Interface (ABI)
Please refer to: https://github.com/okxlabs/DEX-Router-EVM-V1/tree/main/DexRouterabi
https://github.com/okxlabs/Web3-DEX-EVM-PMM
- [Error Codes](https://web3.okx.com/onchainos/dev-docs/trade/dex-error-code.md)
# Error Codes
## Swap API
| Code | HTTP status | Message |
| ----- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 0 | 200 | Succeeded |
| 50011 | 429 | Rate limit reached. Please refer to API documentation and throttle requests accordingly |
| 50014 | 400 | Parameter param0 cannot be empty |
| 50026 | 500 | System error. Try again later |
| 50103 | 401 | Request header "OK-ACCESS-KEY" cannot be empty |
| 50104 | 401 | Request header "OK-ACCESS-PASSPHRASE" cannot be empty |
| 50105 | 401 | Request header "OK-ACCESS-PASSPHRASE" incorrect |
| 50106 | 401 | Request header "OK-ACCESS-SIGN" cannot be empty |
| 50107 | 401 | Request header "OK-ACCESS-TIMESTAMP" cannot be empty |
| 50111 | 401 | Invalid OK-ACCESS-KEY |
| 50112 | 401 | Invalid OK-ACCESS-TIMESTAMP |
| 50113 | 401 | Invalid signature |
| 51000 | 400 | Parameter param0 error |
| 80000 | 200 | Repeated request |
| 80001 | 200 | CallData exceeds the maximum limit. Try again in 5 minutes. |
| 80002 | 200 | Requested token Object count has reached the limit. |
| 80003 | 200 | Requested native token Object count has reached the limit. |
| 80004 | 200 | Timeout when querying SUI Object. |
| 80005 | 200 | Not enough Sui objects under the address for swapping |
| 82000 | 200 | Insufficient liquidity |
| 82001 | 500 | The commission service is not available during the upgrade |
| 82003 | 200 | toTokenReferrerWalletAddress address is not valid |
| 82102 | 200 | Less than the minimum quantity limit,the minimum amount is 0 |
| 82103 | 200 | Exceeds than the maximum quantity limit,the maximum amount is 0 |
| 82104 | 200 | This token is not supported |
| 82105 | 200 | This chain is not supported |
| 82112 | 200 | The value difference from this transaction’s quote route is higher than num, which may cause asset loss,The default value is 90%. It can be adjusted using the string age. |
| 82116 | 200 | callData exceeds the maximum limit. Try again in 5 minutes. |
| 82130 | 200 | The chain does not require authorized transactions and can be exchanged directly. |
| 82004 | 200 | Commission split for swaps via Four.meme is not supported |
| 82005 | 200 | Commission split for swaps via aspecta is not supported |
## RFQ API
Market makers should return appropriate HTTP status codes along with error messages.
| Code | HTTP status | Message |
| ----- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| 0 | 200 | The request was successful, and the endpoint will return a quote. |
| 404 | 404 | The endpoint will not return a quote for this request (e.g. the pair or the size are not supported). |
| 400 | 400 | The request sent to the endpoint is malformed (e.g. missing an expected parameter). |
| 401 | 401 | Authorization failed. For example the X-API-KEY is missing or incorrect. |
| 50x | 50x | The endpoint is offline or unable to respond. If the status persist, the endpoint will be temporarily suspended and will not receive requests. |
| 82000 | 200 | Liquidity too low for this quote |
| 82001 | 200 | The quote does not exceed the minimum size of the maker |
| 82002 | 200 | The maker is unavailable to handle the quote |
| 82003 | 200 | The maker rejects to respond to this user |
- [FAQ](https://web3.okx.com/onchainos/dev-docs/trade/dex-aggregation-faq.md)
# FAQ
## What Is the Native Token Address for Each Chain?
We have defined the native tokens for each chain. Please refer to the table below for details:
| Chain Name | Native Token Address |
|------------|----------------------------------------------------|
| EVM | 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee |
| Solana | 11111111111111111111111111111111 |
| Sui | 0x2::sui::SUI |
| Tron | T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb |
| Ton | EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c |
## What Is “Transfer amount exceeds allowance”?
This error message indicates that the amount you're trying to transfer exceeds the approved limit. Set your approval limit higher than the amount you wish to transfer to resolve this error.
## What Is “min return not reached”?
This means that the expected minimum return was not met during trade execution. This usually happens when there is a significant slippage or high market volatility. If the expected minimum return is not met, the trade will not be executed.
You may increase slippage to raise the chances of the order going through as a higher slippage allows a greater price fluctuation at execution. However, please note that too high a slippage may lead to results that are worse than expected.
## Which Tokens Require an Approval Transaction?
1. EVM + Tron:
Typically, non-native tokens (such as ERC-20/TRC-20 tokens on Ethereum or Tron) require an approval transaction. This ensures that you are allowing the smart contract to transfer these tokens from your account.
2. Other heterogeneous chains:
On some other chains, such as Solana, approval transactions are not required for tokens.
- [Introduction](https://web3.okx.com/onchainos/dev-docs/trade/onchain-gateway-api-introduction.md)
# Introduction
The Transaction API offers on-chain simulation and on-chain transaction broadcasting services, supporting both self-developed RPC nodes and external premium RPC nodes. By leveraging OKX Web3's advanced node management infrastructure and expertise, it enables intelligent transaction broadcasting, significantly reducing failure rates and accelerating confirmation speeds.
Developers can seamlessly integrate the Transaction API with the Swap API to create a comprehensive DEX experience to their users, eliminating the need for additional external resources.
**Key capabilities**
1. **High-availability hybrid node architecture**
- Self-developed multi-chain node clusters for robust performance.
- Intelligent integration of third-party premium node resources to build a redundant and resilient network.
- Dynamic load balancing with real-time node health monitoring and sub-second failover for uninterrupted services.
2. **Intelligent multi-broadcasting engine**
- Breakthrough capability to broadcast transactions across multiple node networks simultaneously.
- Enhanced on-chain success rates through advanced distributed propagation algorithms.
- Priority block packaging acceleration for major chains like ETH, BNB Chain, and Solana and more.
- [API Reference](https://web3.okx.com/onchainos/dev-docs/trade/onchain-gateway-reference.md)
# API Reference
- [Get Supported Chains](https://web3.okx.com/onchainos/dev-docs/trade/onchain-gateway-api-chains.md)
{/* api-page */}
# Get Supported Chains
Retrieve information on chains supported by Onchain gateway API
### Request URL
GET `https://web3.okx.com/api/v6/dex/pre-transaction/supported/chain`
## Request Parameters
None
## Response Parameters
| Parameter | Type | Description |
|-----------|--------|-------------------|
| name | String | Chain name |
| logoUrl | String | Chain logo URL |
| shortName | String | Chain short name |
| chainIndex| String | Chain unique identifier |
## Request Example
``` shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/pre-transaction/supported/chain' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"name": "Ethereum",
"logoUrl": "http://www.eth.org/eth.png",
"shortName": "ETH",
"chainIndex": "1"
}
],
"msg": ""
}
```
- [Get Gas Price](https://web3.okx.com/onchainos/dev-docs/trade/onchain-gateway-api-gas-price.md)
{/* api-page */}
# Get Gas Price
Dynamically obtain estimated gas prices for various chains.
### Request URL
GET `https://web3.okx.com/api/v6/dex/pre-transaction/gas-price`
## Request Parameters
| Parameter | Type | Required | Description |
|------------|--------|----------|-----------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
## Response Parameters
### EVM & Tron
| Parameter | Type | Description |
|------------------|---------|-----------------------------------|
| normal | String | Medium gas price. For EVM, it is in wei. For Tron,it is in SUN |
| min | String | Low gas price. For EVM, it is in wei. For Tron,it is in SUN |
| max | String | High gas price. For EVM, it is in wei. For Tron,it is in SUN |
| supporteip1559 | Boolean | Whether supports 1559 |
| eip1559Protocol | Object | 1559 protocol |
### eip1559 Protocol
| Parameter | Type | Description |
|---------------------|--------|--------------------------------------|
| eip1559Protocol | Object | Structure of 1559 protocol |
| >suggestBaseFee | String | Suggested base fee = base fee * 1.25, in wei |
| >baseFee | String | Base fee, in wei |
| >proposePriorityFee | String | Medium priority fee, in wei |
| >safePriorityFee | String | Low priority fee, in wei |
| >fastPriorityFee | String | High priority fee, in wei |
### Solana
| Parameter | Type | Description |
|------------------|---------|-------------------|
| priorityFee | String | Priority fee per compute unit. Only applicable to Solana |
| >proposePriorityFee | String | Medium priority fee in microlamports.( it is also called Medium compute unit price ) 80th percentile|
| >safePriorityFee | String | Low priority fee in microlamports.( it is also called Low compute unit price ) 60th percentile|
| >fastPriorityFee | String | High priority fee in microlamports.( it is also called High compute unit price ) 95th percentile|
| >extremePriorityFee | String | Extreme High priority fee in microlamports.( it is also called Extreme High compute unit price ) 99th percentile |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/pre-transaction/gas-price?chainIndex=1' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"normal" : "21289500000", // Medium gas price
"min" : "15670000000", // Low gas price
"max" : "29149000000", // High gas price
"supportEip1559" : true, // Whether supports 1559
"eip1599Protocol": {
"suggestBaseFee" : "15170000000", // Suggested base fee
"baseFee" : "15170000000", // Base fee
"proposePriorityFee" : "810000000", // Medium priority fee
"safePriorityFee" : "500000000", // Low priority fee
"fastPriorityFee" : "3360000000" // High priority fee
},
"priorityFee":{}
}
],
"msg": ""
}
```
- [Get Gas Limit](https://web3.okx.com/onchainos/dev-docs/trade/onchain-gateway-api-gas-limit.md)
{/* api-page */}
# Get Gas Limit
Retrieve estimated Gas Limit consumption through pre-execution of transaction information.
### Request URL
POST `https://web3.okx.com/api/v6/dex/pre-transaction/gas-limit`
## Request Parameters
| Parameter | Type | Required | Description |
|------------|--------|----------|-----------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| fromAddress| String | Yes | From address. For `transfer`,`Swap`, `Approve`, it is a wallet address |
| toAddress | String | Yes | To address. For `transfer`, it can be a token address or wallet address. For `Swap`, it should be OKX DEX router address. For `Approve`, it is a token address |
| txAmount | String | No | Transaction amount. Default value: `0`. 1. For **Native token transactions** ( where the `fromToken` is native token. e.g., Ethereum), the txAmount can be set to the native token quantity, or retrieved from [/swap](./dex-swap) api(e.g., `txAmount = swapResponse.tx.value`). 2.For **non-native token transactions**, set `txAmount` to `0`. The valle must use base unit of the native token, e.g., wei for ETH |
| extJson | Object | No | Additional parameters for calldata and other information |
extJson
| Parameter | Type | Required | Description |
|-----------|--------|----------|-------------|
| inputData | String | No | Calldata |
## Response Parameters
| Parameter | Type | Description |
|-----------|--------|-------------------|
| gasLimit | String | Estimated gas limit |
## Request Example
``` shell
curl --location --request POST 'https://web3.okx.com/api/v6/dex/pre-transaction/gas-limit' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z' \
--data-raw '{
"fromAddress": "0x383c8208b4711256753b70729ba0cf0cda55efad",
"toAddress": "0x4ad041bbc6fa102394773c6d8f6d634320773af4",
"txAmount": "31600000000000000",
"chainIndex": "1",
"extJson": {
"inputData":"041bbc6fa102394773c6d8f6d634320773af4"
}
}'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"gasLimit": "652683"
}
],
"msg": ""
}
```
- [Simulate Transactions](https://web3.okx.com/onchainos/dev-docs/trade/onchain-gateway-api-simulate-transaction.md)
{/* api-page */}
# Simulate Transactions
Simulate a blockchain transaction before executing it to see the expected outcomes and potential risks.
Transaction simulate API is available to our whitelisted customers only. If you are interested, please contact us dexapi@okx.com.
### Request URL
GET `https://web3.okx.com/api/v6/dex/pre-transaction/simulate`
## Request Parameters
| Parameter | Type | Required | Description |
|--------------|--------|----------|---------------------------------------------------------------------------------------|
| fromAddress | String | Yes | Source address. For `Swap`, `Approve`, it is a wallet address |
| toAddress | String | Yes | Destination address. For `Swap`, it should be OKX DEX router address. For `Approve`, it is a token address |
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum See [Supported Chains](../home/supported-chain) for more. It supports EVM、SOL、SUI, more chains will be supported soon. |
| txAmount | String | No | Transaction amount. Default value: `0`. 1. For **Native token transactions** ( where the `fromToken` is native token. e.g., Ethereum), the txAmount can be set to the native token quantity, or retrieved from [/swap](./dex-swap) api(e.g., `txAmount = swapResponse.tx.value`). 2.For **non-native token transactions**, set `txAmount` to `0`. The valle must use base unit of the native token, e.g., wei for ETH |
| extJson | Object | Yes | Extended information object containing the following fields: |
| > inputData | String | Yes | Call data for the transaction. The encoding rule require `base58`. |
| priorityFee | String | No | Priority fee. Only applicable to Solana. |
| gasPrice | String | No | Gas price for the transaction. |
## Response Parameters
| Parameter | Type | Description |
|--------------|--------|--------------------------------------------------------------------|
| intention | String | Transaction purpose. Valid values: "Swap", "Token Approval" |
| assetChange | Array | Details of asset changes resulting from the transaction |
| > assetType | String | Asset type. Valid values: "NATIVE", "ERC20", "SPLTOKEN","SUITOKEN"|
| > name | String | Asset name (e.g., "Ethereum") |
| > symbol | String | Asset symbol (e.g., "ETH") |
| > decimals | Number | Asset decimal precision |
| > address | String | Asset contract address |
| > imageUrl | String | URL to the asset's image |
| > rawValue | String | Asset amount. Positive values indicate receiving assets, negative values indicate sending assets. |
| gasUsed | String | Gas consumed by the transaction |
| failReason | String | Human-friendly explanation if the transaction would fail |
| risks | Array | Potential risks identified in the transaction |
| > address | String | Address associated with the risk |
| > addressType| String | Type of address. Valid values: "contract", "eoa" |
## Request Example
```shell
curl --location --request POST 'https://web3.okx.com/api/v6/dex/pre-transaction/simulate' \
--header 'OK-ACCESS-KEY: your-access-key' \
--header 'OK-ACCESS-SIGN: your-access-sign' \
--header 'OK-ACCESS-PASSPHRASE: your-passphrase' \
--header 'OK-ACCESS-TIMESTAMP: 2025-05-19T10:00:00.000Z' \
--header 'Content-Type: application/json' \
--data-raw '{
"fromAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"toAddress": "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
"chainIndex": "1",
"txAmount": "0",
"extJson": {
"inputData": "0x38ed1739000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000042ab52c000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000742d35cc6634c0532925a3b844bc454e4438f44e0000000000000000000000000000000000000000000000000000000064794b4b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
},
"gasPrice": "12000000000"
}'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"intention": "SWAP",
"assetChange": [
{
"assetType": "NATIVE",
"name": "Ether",
"symbol": "ETH",
"decimals": 18,
"address": "",
"imageUrl": "",
"rawValue": "-1000000000000000"
},
{
"assetType": "ERC20",
"name": "USD Coin",
"symbol": "USDC",
"decimals": 6,
"address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"imageUrl": "",
"rawValue": "1000000000000000"
}
],
"gasUsed": "180000",
"failReason": "",
"risks": []
}
],
"msg": "success"
}
```
- [Broadcast Transactions](https://web3.okx.com/onchainos/dev-docs/trade/onchain-gateway-api-broadcast-transaction.md)
{/* api-page */}
# Broadcast Transactions
Broadcast transactions to the specified blockchain.
Your end-user's transaction can only be covered by the MEV protection feature if you actually utilise OKX Build's API services for that particular transaction. MEV protection is currently an experimental feature provided by third-parties and OKX Build does not guarantee the effectiveness and quality of such MEV protection.
### Request URL
POST `https://web3.okx.com/api/v6/dex/pre-transaction/broadcast-transaction`
## Request Parameters
| Parameter | Type | Required | Description |
|------------ |-------- |----------|------------------------------------------------------------------------|
| signedTx | String | Yes | The transaction string after being signed |
| chainIndex | String | Yes | Unique identifier for the chain. e.g., ETH=1. See more [here](../home/supported-chain). |
| address | String | Yes | Address. |
| extraData | String | No | Additional parameters for calldata and other information |
| > enableMevProtection | Boolean | No | Enable MEV protection. Not enabled by default. Valid values: `false`:not enabled, `true`:enabled It supports only `ETH`、`BSC`、`SOL`、`BASE`, more chains will be supported soon. |
| > jitoSignedTx | String | No | The transaction string after being signed that will send to Jito. The encoding rule require `base58`, applicable to `SOL`. For SOL, `signedTx` and `jitoSignedTx` must be passed at the same time |
## Response Parameters
| Parameter | Type | Description |
|-----------|--------|--------------------|
| orderId | String | Unique transaction identifier |
| txHash | String | Transaction Hash. It supports only `ETH`、`BSC`、`SOL`、`BASE`, more chains will be supported soon. |
## Request Example
``` shell
curl --location --request POST 'https://web3.okx.com/api/v6/dex/pre-transaction/broadcast-transaction' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z' \
--data-raw '{
"signedTx":"0x08b47112567534ad041bbc6fa102394773c6d8f6d634320773af4da55efa",
"address": "0x383c8208b4711256753b70729ba0cf0cda55efad",
"chainIndex": "1",
"extraData":"{\"enableMevProtection\":true,\"jitoSignedTx\":\"0x123456\"}"
}'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"orderId": "0x383c8208b4711256753b70729ba0cf0cda55efad",
"txHash": "0xd394f356a16b618ed839c66c935c9cccc5dde0af832ff9b468677eea38759db5"
}
],
"msg": ""
}
```
- [Get Transaction Orders](https://web3.okx.com/onchainos/dev-docs/trade/onchain-gateway-api-orders.md)
{/* api-page */}
# Get Transaction Orders
Get the list of orders sent from transaction broadcasting API. This supports querying transactions sorted in descending order by time.
### Request URL
GET `https://web3.okx.com/api/v6/dex/post-transaction/orders`
## Request Parameters
| Parameter | Type | Required | Description |
|------------ |-------- |----------|----------------------------------------------------|
| address | String | Yes | Address |
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| txStatus | String | No | Transaction status: `1`: Pending `2`: Success `3`: Failed |
| orderId | String | No | Unique identifier for the transaction order |
| cursor | String | No | Cursor |
| limit | String | No | Number of records returned, default is the most recent 20, maximum is 100 |
## Response Parameters
| Parameter | Type | Description |
|------------ |-------- |-----------------------------------------------------|
| chainIndex | String | Unique identifier for the chain |
| address | String | Address |
| orderId | String | Order ID |
| txStatus | String | Transaction status: `1`: Pending `2`: Success `3`: Failed |
| failReason | String | The reason for failed transaction |
| txHash | String | Transaction hash |
## Request Example
``` shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/post-transaction/orders?address=0x238193be9e80e68eace3588b45d8cf4a7eae0fa3&chainIndex=1' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
``` json
{
"code": "0",
"msg": "success",
"data": [
{
"cursor": "1",
"orders":[
{
"chainIndex": "1",
"orderId": "016cf21d020be6c2f071dad9bbd8ec5cb9342fa8",
"address": "0x238193be9e80e68eace3588b45d8cf4a7eae0fa3",
"txHash": "0xb240e65dd9156b4a450be72f6c9fe41be6f72397025bb465b21a96ee9871a589",
"failReason": "",
"txstatus": "2"
},
{
"chainIndex": "1",
"orderId": "592051a92a744627022955be929ecb5c9e777705",
"address": "0x238193be9e80e68eace3588b45d8cf4a7eae0fa3",
"txHash": "0xc401ffcd2a2b4b1db42ce68dfde8e63c0a1e9653484efb2873dbf5d0cbeb227a",
"txstatus": "1",
"failReason": "",
}
]
}
]
}
```
- [Error Codes](https://web3.okx.com/onchainos/dev-docs/trade/onchain-gateway-error-code.md)
# Error Codes
| Code | HTTP status | Message |
|-------|-------------|-----------------------------------------------------------------------------------------|
| 50001 | 200 | Service temporarily unavailable. Try again later |
| 81001 | 200 | Incorrect parameter |
| 81108 | 200 | Wallet type does not match the required type |
| 81104 | 200 | Chain not support |
| 81152 | 200 | Coin not exist |
| 81451 | 200 | node return failed |
- [Use Widget](https://web3.okx.com/onchainos/dev-docs/trade/dex-widget.md)
# Use Widget
Integrate the powerful OKX Widget into your product! With this widget, you can create an effective trading interface within 30 minutes.
## Install
```javaScript
yarn add @okxweb3/dex-widget
// or
npm install @okxweb3/dex-widget
// or
pnpm add @okxweb3/dex-widget
```
## Quickstart
Here is an example which shows how to use @okxweb3/dex-widget in a React project. You can find more examples through this [link](https://github.com/okx/dex-widget/tree/develop/packages/widget-configurator/src/react-cra).
Demo:https://okx.github.io/dex-widget/
You should pass the wallet provider information from your application if you want to connect a wallet. Then add the ON_CONNECT_WALLET event to seamlessly use the widget as part of your application.
- If it’s on Ethereum or other EVM networks, the provider must comply with EIP-1193 to implement the interface.
- If it’s on Solana, the provider must pass the wallet provider information from your application.
```typeScript
import { createOkxSwapWidget, ProviderType } from '@okxweb3/dex-widget';
const widgetEthInstance = createOkxSwapWidget(
document.getElementById('widget'),
{
params: {
providerType: ProviderType.EVM,
},
provider: window.ethereum, // e.g. window.okexchain
}
);
const widgetSolanaInstance = createOkxSwapWidget(
document.getElementById('widget'),
{
params: {
providerType: ProviderType.SOLANA,
},
provider: window.solana, // window.okexchain.solana
}
);
```
You can check out this [link](https://github.com/okx/dex-widget/blob/faf69c76b90268f2352507c9a90fb37bb80fdbc7/example/widget-demo/src/main.tsx#L22)。 for an example of using the Rainbow kit to connect to a wallet.
## Params
The following sheet contains the descriptions of the params.
| Parameter | Type | Default | Description |
| -------------- | --------------- | ------- |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `width` | `number` | 450 | The width of the widget in css values (px). If the width is not set, the display style for the width will be: 450px when the screen width > 767px. 100% when the screen width < 768px. 375px when the screen width < 375px. |
| `theme` | `THEME` | light | The swap widget provides a default light theme and a dark theme as options. You can change the theme of the widget by following the example below. |
| `lang` | `string` | en_us | The widget language is adjustable. Check the Multilanguage section for more details. |
| `tradeType` | `TradeType` | auto | The type of transaction. It can be “swap”, “bridge”, or “auto”.Note: “Auto” includes “swap” and “bridge”. |
| `chainIds` | `Array` | [] | The ID of the blockchain on which the single-chain swap will take place. Check the ChainId config section for all the networks that you can choose from. |
| `tokenPair` | `ITokenPair` | {} | The default token pair you have set for Swap, can be found in the default tokenPair configuration section for more details. |
| `bridgeTokenPair` | `ITokenPair` | {} | The default token pair you have set for Bridge, can be found in the default tokenPair configuration section for more details. |
| `providerType` | `ProviderType` | ' ' | ProviderType represents the type of the provider and corresponds to it one-to-one. For example, if the provider is Solana, then the providerType would be SOLANA. |
| `defaultTab` | `TradeTab ` | 'swap' | Default open mode can be set to single-chain or cross-chain. Supported from version 1.3.16 and above. |
| `walletName` | `string` | ' ' | Name of the connected wallet. This parameter helps the DEX widget continuously improve its products and services to provide a better user experience. Supported from version 1.3.16 and above. |
## Type Description
```typeScript
interface ITokenPair {
fromChain: string | number;
toChain: string | number;
fromToken?: string;
toToken?: string;
}
enum ProviderType {
EVM = 'EVM',
SOLANA = 'SOLANA',
WALLET_CONNECT = 'WALLET_CONNECT',
}
enum TradeType {
SWAP = 'swap',
BRIDGE = 'bridge',
AUTO = 'auto',
}
enum THEME {
LIGHT = 'light',
DARK = 'dark',
}
```
## Multilanguage
| lang | Description |
| -------- | ----------------------- |
| `en_us` | English,Default |
| `zh_cn` | 简体中文 |
| `zh_tw` | 繁體中文 |
| `fr_fr` | Français (Afrique) |
| `id_id` | Bahasa Indonesia |
| `ru_ru` | Русский |
| `tr_tr` | Türkçe |
| `vi_vn` | Tiếng Việt |
| `de_de` | Deutsch |
| `it_it` | Italiano |
| `pl_pl` | Polski |
| `pt_pt` | Português (Portugal) |
| `es_es` | Español (España) |
| `pt_br` | Português (Brasil) |
| `es_419` | Español (Latinoamérica) |
| `cs_cz` | Čeština |
| `ro_ro` | Română |
| `uk_ua` | Українська |
| `ar_eh` | العربية |
| `nl_nl` | Nederlands |
## ChainId Config
| Network | ChainId | Native token contract |
| ----------- | ------- | ------------------------------------------ |
| Ethereum | 1 | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE |
| zkSync Era | 324 | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE |
| Optimism | 10 | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE |
| Polygon | 137 | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE |
| Avalanche C | 43114 | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE |
| Arbitrum | 42161 | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE |
| Linea | 59144 | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE |
| Base | 8453 | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE |
| Mantle | 5000 | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE |
| Scroll | 534352 | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE |
| X Layer | 196 | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE |
| Blast | 81457 | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE |
| BNB Chain | 56 | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE |
| Solana | 501 | 11111111111111111111111111111111 |
## Default tokenPair Config
`tokenPair`: If tokenPair not configured, the default network for single-chain swaps is set to Ethereum, with ETH as `fromToken` and USDC as `toToken`.
`bridgeTokenPair`: If bridgeTokenPair not configured, the default bridge transaction is set to one from Ethereum to BNB Chain, with ETH as `fromToken` and BNB as `toToken`.
```javaScript
import React, { useEffect, useRef } from 'react';
import {
OkxSwapWidgetParams,
ProviderType,
TradeType,
} from '@okxweb3/dex-widget';
const provider = window.ethereum;
export function EvmWidget() {
const widgetRef = useRef();
const params = {
chainIds: ['1', '10'],
lang: 'zh_cn',
providerType: ProviderType.EVM,
theme: 'dark',
tradeType: TradeType.AUTO,
tokenPair: {
fromChain: 1, //ETH
toChain: 1, // ETH
fromToken: '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT
toToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', // ETH
},
bridgeTokenPair: {
fromChain: 1, //ETH
toChain: 56, // BNB
fromToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', // ETH
toToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', // BNB
},
};
const initialConfig = {
params,
provider,
listeners: [
{
event: 'ON_CONNECT_WALLET',
handler: (token, preToken) => {
provider.enable();
},
},
],
};
useEffect(() => {
const widgetHandler = createOkxSwapWidget(widgetRef.current, initialConfig);
return () => {
widgetHandler?.destroy();
};
}, []);
return ;
}
```
| Parameter | Type | Description |
| --------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| fromChain | String | The ID of the source network that the fromToken belongs to (e.g., 1: Ethereum. Check ChainId config for a full list of the supported networks and the corresponding chain IDs). |
| fromToken | String | The contract address of the token to be sold. E.g., ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE. If the fromToken is a blockchain’s native token, check the chain ID to get the contract address. |
| toChain | String | The ID of the destination network that the toToken belongs to (e.g., 1: Ethereum. Check ChainId config for a full list of the supported networks and the corresponding chain IDs). |
| toToken | String | "The contract address of a token to be bought. E.g., USDC: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48. If the toToken is a blockchain’s native token, check the chain ID to get the contract address." |
### updateProvider
The widget supports EVM and Solana. When switching from EVM to Solana, remember to update the corresponding widget’s provider, and vice versa.
If you don’t pass in provider information for the first rendering and want the widget to respond to a wallet connection, you need to call updateProvider.
Updating the provider can also include the `walletName` parameter to identify the plugin wallet connected by the user.
```javaScript
// 3. Update the provider if the user connects a different wallet, EVM => SOLANA
const walletName = 'phantom';
widgetHandler.updateProvider(window.solana, ProviderType.SOLANA, walletName);
// SOLANA => EVM
// widgetHandler.updateProvider(window.ethereum, ProviderType.EVM);
```
### updateListeners
You can update the events that the widget listens to.
```javaScript
// 4. Modify event listeners to handle new types of events
widgetHandler.updateListeners([
{
event: OkxEvents.ON_FROM_CHAIN_CHANGE,
handler: (payload) => {
//
},
},
]);
```
- Listeners mainly listen to the interfaces exposed externally by the widget, enabling customized processing through various events. The updateListeners function is used to modify custom processing after switching chains.
- By adding event listeners, you can capture and handle data passed out from the iframe. This data usually represents events or state changes happening within the iframe and is passed to the external page through events.
- The received data can be processed and manipulated flexibly according to your needs. This allows you to implement different logic or update UI elements based on the type or content of the data passed.
- With the updateListeners method, you can add handlers for different types of events, such as OkxEvents.ON_TOKEN_CHANGE. When the event is triggered, the handler will receive the relevant payload data for further processing.
### destroy
Call this method when removing the widget module.
```javascript
const widgetHandler = createOkxSwapWidget(container, initialConfig);
widgetHandler.destory();
```
#### Note:
- Whenever you refresh or update, make sure to call the destroy method to remove previously connected events in order to prevent duplicate requests.
## Event Listeners
Widget provides event listeners for ON_CONNECT_WALLET and ON_FROM_CHAIN_CHANGE.
- `ON_CONNECT_WALLET`: This event is triggered when the widget is not connected to a wallet and the connect wallet button is clicked.
- `ON_FROM_CHAIN_CHANGE`: This event is triggered when fromChain changes.
- `ON_SUBMIT_TX`: This event is triggered after the transaction is completed and returns the `txHash` and `chainId`. Supported from version 1.3.16 and above.
Here’s how to use them:
```typeScript
import {createOkxSwapWidget, OkxSwapWidgetParams, OkxEventListeners, OkxEvents} from '@okxweb3/dex-widget'
const params: OkxSwapWidgetParams = {
// ...
}
const listeners: OkxEventListeners = [
{
event: OkxEvents.ON_CONNECT_WALLET,
handler: () => {
// open connect wallet method, eg openConnectModal of the rainbow kit.
window.ethereum.enable()
}
},
{
event: OkxEvents.ON_FROM_CHAIN_CHANGE,
handler: (token) => {
//
}
},
{
event: OkxEvents.ON_SUBMIT_TX,
handler: (res) => {
console.log(`Transaction submitted successfully, txHash: ${res.data.txHash}`);
}
},
]
const { updateListeners } = createOkxSwapWidget(container, { params, listeners, provider })
```
- [API Fee](https://web3.okx.com/onchainos/dev-docs/trade/api-fee.md)
# API Fee
Our API provides integration partners with various API tiers and flexible methodology to add fees to each transaction, designed to support various stages of development and commercialization.
As our integration partner, you can access the API with a few different tiers to meet your specific needs. With the DEX API module, you can earn revenue by setting your own partner fee scheme to charge your users per swap.
In detail, integration partners can configure partner fee and fee-receiving address for each token swap. You can charge your users up to 3% per swap for most supported chains; while for Solana, you can charge up to 10% per swap.
All tiers of the API are subject to the [User agreement](https://web3.okx.com/help/okx-web3-build-user-agreement).
## Trial API Tier
Our API offers a Trial tier that includes access to selected API functions. To start using the trial plan, you need to create an account on the [Developer Portal](./developer-portal) and verify your email and phone number.
This plan provides a default rate limit of 1 request per second (RPS) and can be increased to 5 RPS upon review and approval. The trial tier is valid for 60 days upon the API key creation.
For the trial tier, when the DEX API module secures a better price than quoted infrequently, the additional value named positive slippage is kept as our infrastructure fee. The positive slippage is capped at 10% of the trade amount.
## Start-up API Tier
To continue using the API after the 60-day trial period, you can upgrade your developer account to the Start-up tier on the [Developer Portal](./developer-portal).
Once upgraded to the Start-up tier, you immediately have access to additional product features, much higher RPS, standard technical and growth support.
For start-up tier partners charging partner fee in token swaps, a standard revenue-sharing agreement is entered to retain 20% of your revenue as our infrastructure fee. In such cases, the positive slippage (if happened) is returned to your users by default; however, you can apply to have access to the feature to keep a portion of the positive slippage as your revenue.
If you do not configure partner fees in token swaps to make revenue, we will keep the positive slippage (if happened) as our infrastructure fee, capped at 10% of the trade value.
## Enterprise API Tier
For high-volume partners who need a much higher RPS, customized fee structure, dedicated technical and growth support, access to additional product features or customized features, you can upgrade to the Enterprise tier by contacting our BD team and signing a contract with us.
Furthermore, Enterprise partners have access to the feature to keep the positive slippage as your revenue or provide the positive slippage entirely to your users.
## Need Help?
If you need to increase your rate limit, upgrade to the Start-up tier or Enterprise tier, or have product-related queries, please join our [discord](https://discord.com/invite/okxdexapi), or contact our BD team directly at dexapi@okx.com
## API Tiers Summary
| | Trial Tier | Start-up Tier | Enterprise Tier |
|--------------------- |-------------------------------------------------------------------------------------------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| API fee | Positive slippage only | 20% rev-share, or positive slippage if no partner fee is configured | Customized fee structure |
| Features & Benefits | • RPS 1-5 • Access to standard API endpoints • Valid for 60 days | • RPS 2-50 • Access to most advanced API endpoints • Access to positive slippage feature to retain as extra revenue (upon approval) • Receive standard tech and growth support | • Customised RPS • Access to all advanced API endpoints, including positive slippage feature to retain as extra revenue • Request customized feature development (upon approval) • Receive dedicated tech and growth support |
*RPS adjustment is correlated to trading volume
*Positive slippage refers to a situation where a trade is executed at a more favorable price than initially quoted, which is relatively rare. For selected API tiers, the additional value generated by positive slippage will be retained as an API fee, capped at no more than 10% of the total traded amount.
*Start-up customers with monthly trading volume exceeding $10M need to contact our BD to upgrade to Enterprise tier for customized support
- [Smart Contract Safety](https://web3.okx.com/onchainos/dev-docs/trade/smart-contract-safety.md)
# Smart Contract Safety
## Bug Bounty Program
Security is our paramount priority. To learn about the security programs at OKX Web3, please visit our [website](https://web3.okx.com/security).
We also have bug bounty programs to reward ethical hackers and our community to report bugs or security vulnerabilities to our product.
Submit a bug report: https://hackerone.com/okg
## Open Source Smart Contract
We have open-sourced our smart contract codes on github, inviting community oversight.
- Solana Chain Contract Repository
https://github.com/okxlabs/DEX-Router-Solana-V1
- EVM Chain Contract Repository
https://github.com/okxlabs/DEX-Router-EVM-V1
## Join Community Discussions
Join our [Discord community](https://discord.gg/okxdexapi) to share your experience with our DEX router and help other developers troubleshoot their integration issues.
Our [Discord](https://discord.gg/okxdexapi) is the main hub for announcements, community interactions, technical discussion and 24/7 technical support.
- [Market API](https://web3.okx.com/onchainos/dev-docs/market/market-api-introduction.md)
# Market API
Our Market API is a suite of high-performance Restful JSON endpoints and real-time websocket channels to provide comprehensive multi-market and onchain data for cryptocurrency tokens, trades, transactions, accounts and more, pulling information from hundreds of decentralized exchanges (DEX) and centralized exchanges (CEX) across different blockchain ecosystems.
Developers can utilize the Market API and the Trade API to build complete DEX experience with insightful market and portfolio dashboards, allowing users to monitor token performance across different exchange types in real-time, analyze portfolio changes and identify trading opportunities.
- [Build with AI](https://web3.okx.com/onchainos/dev-docs/market/market-ai-tools-introduction.md)
# Build with AI
Onchain OS Market give agents and developers a unified data layer for real-time on-chain intelligence, token prices, trading activity, candlestick charts, token discovery, wallet balances, and transaction history , all from a single integration.
Use it as the research and monitoring layer that runs alongside trade execution, or independently for portfolio dashboards and opportunity detection.
## Why Onchain OS Market for AI
- **Dual-source price aggregation**. Market Price data is aggregated in real time from both on-chain DEX activity and centralized exchange feeds, giving a more complete picture of a token's true market price than any single-venue source. For agents that need a manipulation-resistant reference , stop-loss thresholds, risk parameters, multi-step trade validation, the Index Price API computes a stable composite price from multiple independent third-party sources (CEX, DEX, oracles), specifically designed to resist single-venue distortion.
- **Token intelligence from discovery to depth**. The Token API is a full information stack: search by name, symbol, or contract address; retrieve basic metadata (decimals, contract); pull live trading metrics (24h volume, market cap, circulating supply, holders, liquidity); surface trending tokens by price change, volume, or market cap; and inspect the top 20 holder addresses for concentration analysis. An Agent can go from a user's natural language question ("what's trending on Ethereum today?") to a structured, actionable answer without leaving the tool.
- **Complete portfolio visibility in one call**. The Balance API queries token balances and total USD portfolio value across all supported chains simultaneously , no need to iterate per chain. This is designed for agents that manage multi-chain portfolios or need to verify sufficient balance before executing a swap.
- **Candlestick history for analysis and backtesting**. The Market Price API provides OHLCV data from minute-level to daily candles, with a historical endpoint that extends the lookback window for trend analysis, pattern detection, and strategy backtesting.
- **Agent-optimized interface Skill and MCP Server**. OKX Market provides two integration paths depending on how your Agent is deployed. The Skill teaches your Agent how to call the OKX Web3 Market API through structured instructions and code generation. The MCP Server exposes the same capabilities as directly callable tools via the Model Context Protocol.
## Quickstart
Add the Skill files to the Agent’s `skill` directory. The Agent will automatically load the intent router, chain-specific execution playbooks, signing modes, and error-handling logic.
```shell
npx skills add okx/onchainos-skills
```
For more details, please refer to the [GitHub repository](https://github.com/okx/onchainos-skills).
For Claude Desktop, Cursor, and other MCP-compatible clients, add the following configuration:
**for General Claude code**
```shell
claude mcp add onchainos-mcp https://web3.okx.com/api/v1/onchainos-mcp -t http -H"OK-ACCESS-KEY: d573a84c-8e79-4a35-b0c6-427e9ad2478d"
```
**for Claude Desktop Installation**
1. Go to the `Settings` page and locate the `Connector` menu.
2. Scroll to the bottom, find `Add custom connector`, and enter the URL: https://web3.okx.com/api/v1/onchainos-mcp
You can also try a local installation. For detailed instructions, please consult your Claude client.
**for Claude code MCP settings**
```shell
claude mcp add-json onchainos-mcp '{
"type": "http",
"url": "https://web3.okx.com/api/v1/onchainos-mcp",
"headers": {
"OK-ACCESS-KEY": "d573a84c-8e79-4a35-b0c6-427e9ad2478d"
}
}'
```
One MCP server covers both Market and Trade capabilities. Restart your client after updating the config.
- [Skills](https://web3.okx.com/onchainos/dev-docs/market/market-ai-tools-skills.md)
# Skills
Beyond simply "retrieving documentation," Skills encapsulate domain knowledge and engineering workflows into stable capabilities. They enable the Agent not only to answer "how to write the docs," but also to handle "which endpoint to choose, what the next step is, and how to deal with errors."
## Why Onchain OS Market Skills for AI
- Covers the full Market data lifecycle , price lookup, token discovery, candlestick retrieval, balance queries, and trade history.
- Provides an Intent Router: maps user questions ("what's ETH's price?", "who holds the most USDT?", "show me SOL's 4h chart") to the correct API family and the appropriate first action.
- Plug-and-play for AI agents: Structured capability modules that directly encapsulate the OKX DEX API for AI Agents. Each Skill corresponds to a specific capability (e.g., filtering trending tokens by price change, trading volume, or market cap; retrieving real-time trading metrics; analyzing address concentration) and defines clear input/output schemas, enabling seamless integration into any Agent architecture.
## Quickstart
```shell
npx skills add okx/onchainos-skills
```
For more details, please refer to the [GitHub repository](https://github.com/okx/onchainos-skills).
## Example interactions
Once the Skills is uploaded to agents, an Agent can respond to natural-language queries like:
```shell
What is the current price of OKB?
# Call index-current-price
```
```shell
Show me OKB’s 4-hour candlesticks for the past week.
# Call market-candlesticks
```
```shell
What is the hottest token on the X-layer chain right now?
# Call market-token-ranking
```
```shell
Which address holds the most USDT on X-layer?
# Call market-token-holder
```
```shell
What is the total asset value of wallet 0xd8dA...?
# Call balance-total-value
```
- [MCP Server](https://web3.okx.com/onchainos/dev-docs/market/market-ai-tools-mcp-server.md)
# MCP Server
MCP (Model Context Protocol) connects AI tools with developer resources. After adding the OKX DEX MCP Server, the Agent can query live token prices, retrieve candlestick charts, search for tokens, inspect holder distributions, and check wallet balances all through standardized, directly callable tool interfaces, within a single conversation or editor session, without any additional integration code.
For Claude Desktop, Cursor, and other MCP-compatible clients, add the following configuration:
One MCP server covers both Market and Trade capabilities. Restart your client after updating the config.
## What the MCP Server exposes
- **Price Tools:** `dex-okx-index-current-price`, `dex-okx-index-historical-price`, `dex-okx-market-price`, `dex-okx-market-price-chains` — real-time and historical index/DEX prices across 20+ chains.
- **Candlestick Tools:** `dex-okx-market-candlesticks`, `dex-okx-market-candlesticks-history` — OHLCV data from 1-minute to daily intervals, with support for extended lookback windows.
- **Token Intelligence Tools:** `dex-okx-market-token-search`, `dex-okx-market-token-price-info`, `dex-okx-market-token-ranking`, `dex-okx-market-token-holder` — token discovery, metadata, real-time metrics, and holder concentration analysis.
- **Trade Data Tool:** `dex-okx-market-trades` — latest on-chain trade records for any token.
- **Balance Tools:** `dex-okx-balance-chains`, `dex-okx-balance-total-token-balances`, `dex-okx-balance-specific-token-balance`, `dex-okx-balance-total-value` — multi-chain wallet balances and USD portfolio valuation.
## Quickstart
**for General Claude code**
```shell
claude mcp add onchainos-mcp https://web3.okx.com/api/v1/onchainos-mcp -t http -H"OK-ACCESS-KEY: d573a84c-8e79-4a35-b0c6-427e9ad2478d"
```
**for Claude Desktop Installation**
1. Go to the `Settings` page and locate the `Connector` menu.
2. Scroll to the bottom, find `Add custom connector`, and enter the URL: https://web3.okx.com/api/v1/onchainos-mcp
You can also try a local installation. For detailed instructions, please consult your Claude client.
**for Claude code MCP setting**
```shell
claude mcp add-json onchainos-mcp '{
"type": "http",
"url": "https://web3.okx.com/api/v1/onchainos-mcp",
"headers": {
"OK-ACCESS-KEY": "d573a84c-8e79-4a35-b0c6-427e9ad2478d"
}
}'
```
## Example interactions
Once the MCP Server is active, an Agent can respond to natural-language queries like:
```shell
What is the current price of OKB?
# Call index-current-price
```
```shell
Show me OKB’s 4-hour candlesticks for the past week.
# Call market-candlesticks
```
```shell
What is the hottest token on the X-layer chain right now?
# Call market-token-ranking
```
```shell
Which address holds the most USDT on X-layer?
# Call market-token-holder
```
```shell
What is the total asset value of wallet 0xd8dA...?
# Call balance-total-value
```
- [llms.txt](https://web3.okx.com/onchainos/dev-docs/market/market-ai-tools-llm.md)
# llms.txt
## Onchain OS Market llms.txt Structure
`llms.txt` is a proposed standard designed to help AI language models efficiently understand and navigate documentation or websites. It enables Agents to quickly grasp the structure of documentation and locate relevant pages. Based on minimal context, it can precisely identify the appropriate module within shorter response times, reduce hallucinations, and lower token consumption—serving as a low-cost navigation layer.
A typical `llms.txt` file includes:
- Site title (H1)
- Sections organized by module (H2)
- A link to each page + a one-sentence description (used for routing and retrieval)
In addition to `llms.txt`, we also provide `llms-full.txt`. Unlike a directory-style index, it aggregates the full site documentation in Markdown format (including more granular descriptions and examples) for deeper indexing and search.
Please visit [OnchainOS.llms.txt](https://web3.okx.com/llms.txt) to view the detailed structure of `llms.txt`.
## Onchain OS Market llms-full.txt Structure
Suitable for:
- AI tools that require full-context access (deep indexing / advanced search / offline knowledge base construction)
- Developers who want a comprehensive view of all pages and resources at once
- Building custom AI workflows (e.g., full indexing first, then chunk-based retrieval as needed)
Please visit [OnchainOS.llms-full.txt](https://web3.okx.com/llms-full.txt) to view the detailed structure of `llms-full.txt`.
- [API Reference](https://web3.okx.com/onchainos/dev-docs/market/market-price-reference.md)
# API Reference
- [Get supported chains](https://web3.okx.com/onchainos/dev-docs/market/market-price-chains.md)
{/* api-page */}
# Get supported chains
Retrieve information on chains supported by Market Price API.
### Request URL
GET `https://web3.okx.com/api/v6/dex/market/supported/chain`
## Request Parameters
| Parameter | Type | Required | Description |
|------------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| chainIndex | String | No | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
## Response Parameters
| Parameter | Type | Description |
|-----------------|--------|-----------------------------------------------------------------------------------------------------------------------|
| chainIndex | String | Unique identifier for the chain |
| chainName | String | Chain name (e.g.,`Optimism`) |
| chainLogoUrl | String | Chain icon |
| chainSymbol | String | Chain symbol (e.g., ETH). |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/supported/chain?chainIndex=1' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code":"0",
"data":[
{
"chainIndex":"1",
"chainName":"Ethereum",
"chainSymbol":"ETH"
},
],
"msg":""
}
```
- [Get Price](https://web3.okx.com/onchainos/dev-docs/market/market-price.md)
{/* api-page */}
# Get Price
Retrieve the latest price of a token.
### Request URL
POST `https://web3.okx.com/api/v6/dex/market/price`
## Request Parameters
| Parameter | Type | Required | Description |
|------------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| tokenContractAddress | String | Yes | Token contract address,for EVM please pass all-lowercase addresses (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
## Response Parameters
| Parameter | Type | Description |
|-----------------|--------|-----------------------------------------------------------------------------------------------------------------------|
| chainIndex | String| Unique identifier for the chain |
| tokenContractAddress | String | Token contract address |
| time | String | Timestamp of the price, Unix timestamp format in milliseconds |
| price | String | Latest token price |
## Request Example
```shell
curl --location --request POST 'https://web3.okx.com/api/v6/dex/market/price' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z' \
--data-raw '[
{
"chainIndex": "66",
"tokenContractAddress":"0x382bb369d343125bfb2117af9c149795c6c65c50"
}
]'
```
## Response Example
```json
{
"code":"0",
"data":[
{
"chainIndex": "1",
"tokenContractAddress": "0x382bb369d343125bfb2117af9c149795c6c65c50",
"time": "1716892020000",
"price": "26.458143090226812"
}
],
"msg":""
}
```
- [Get Candlesticks](https://web3.okx.com/onchainos/dev-docs/market/market-candlesticks.md)
{/* api-page */}
# Get Candlesticks
Retrieve the candlestick charts. This endpoint can retrieve the latest 1,440 data entries. Charts are returned in groups based on the requested bar.
### Request URL
GET `https://web3.okx.com/api/v6/dex/market/candles`
## Request Parameters
| Parameter | Type | Required | Description |
| -------------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| tokenContractAddress | String | Yes | Token contract address,for EVM please pass all-lowercase addresses (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
| after | String | No | Pagination of data to return records earlier than the requested ts. |
| before | String | No | Pagination of data to return records newer than the requested ts. The latest data will be returned when using before individually |
| bar | String | No | Bar size, the default is 1m e.g. [1s/1m/3m/5m/15m/30m/1H/2H/4H] Hong Kong time opening price k-line:[6H/12H/1D/1W/1M/3M] UTC time opening price k-line:[/6Hutc/12Hutc/1Dutc/1Wutc/1Mutc/3Mutc] |
| limit | String | No | Number of results per request. The maximum is 299. The default is 100. |
## Response Parameters
| Parameter | Type | Description |
| --------- | ------ | ---------------------------------------------------------------------------------------------------- |
| ts | String | Opening time of the candlestick, Unix timestamp format in milliseconds, e.g. 1597026383085 |
| o | String | Open price |
| h | String | Highest price |
| l | String | Lowest price |
| c | String | Close price |
| vol | String | Trading volume, with a unit of base currency. |
| volUsd | String | Trading volume, with a unit of usd. |
| confirm | String | The state of candlesticks. `0` represents that it is uncompleted, `1` represents that it is completed. |
The first candlestick data may be incomplete, and should not be polled repeatedly.
The data returned will be arranged in an array like this: [ts,o,h,l,c,vol,volUsd,confirm].
Use the closing price of the last candle as the opening price of the following candle.
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/candles?chainIndex=66&tokenContractAddress=0x382bb369d343125bfb2117af9c149795c6c65c50' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
[
"1597026383085",
"3.721",
"3.743",
"3.677",
"3.708",
"22698348.04828491",
"226348.0482",
"0"
],
[
"1597026383085",
"3.731",
"3.799",
"3.494",
"3.72",
"67632347.24399722",
"6767.2439",
"1"
]
],
"msg": ""
}
```
- [Get Candlesticks History](https://web3.okx.com/onchainos/dev-docs/market/market-candlesticks-history.md)
{/* api-page */}
# Get Candlesticks History
Retrieve historical candlestick charts.
Historical candlestick data does not include unfinished candlesticks
### Request URL
GET `https://web3.okx.com/api/v6/dex/market/historical-candles`
## Request Parameters
| Parameter | Type | Required | Description |
|------------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| tokenContractAddress | String | Yes | Token contract address ,for EVM please pass all-lowercase addresses(e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
| after | String | No | Pagination of data to return records earlier than the requested ts. |
| before | String | No | Pagination of data to return records newer than the requested ts. The latest data will be returned when using before individually |
| bar | String | No | Bar size, the default is 1m e.g. [1s/1m/3m/5m/15m/30m/1H/2H/4H] Hong Kong time opening price k-line:[6H/12H/1D/1W/1M/3M] UTC time opening price k-line:[/6Hutc/12Hutc/1Dutc/1Wutc/1Mutc/3Mutc] |
| limit | String | No | Number of results per request. The maximum is 299. The default is 100. |
## Response Parameters
| Parameter | Type | Description |
|-----------------|--------|-----------------------------------------------------------------------------------------------------------------------|
| ts | String | Opening time of the candlestick, Unix timestamp format in milliseconds, e.g. 1597026383085 |
| o | String | Open price |
| h | String | Highest price |
| l | String | Lowest price |
| c | String | Close price |
| vol | String | Trading volume, with a unit of base currency. |
| volUsd | String | Trading volume, with a unit of usd. |
| confirm | String | The state of candlesticks. `0` represents that it is uncompleted, `1` represents that it is completed. |
The first candlestick data may be incomplete, and should not be polled repeatedly.
The data returned will be arranged in an array like this: [ts,o,h,l,c,vol,volUsd,confirm].
Use the closing price of the last candle as the opening price of the following candle.
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/historical-candles?chainIndex=66&tokenContractAddress=0x382bb369d343125bfb2117af9c149795c6c65c50' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code":"0",
"data":[
[
"1597026383085",
"3.721",
"3.743",
"3.677",
"3.708",
"22698348.04828491",
"226348.0482",
"0"
],
[
"1597026383085",
"3.731",
"3.799",
"3.494",
"3.72",
"67632347.24399722",
"6767.2439",
"1"
]
],
"msg":""
}
```
- [Error Codes](https://web3.okx.com/onchainos/dev-docs/market/market-price-error-code.md)
# Error Codes
| Code | HTTP status | Message |
|-------|-------------|-----------------------------------------------------------------------------------------|
| 0 | 200 | Succeeded |
| 50011 | 429 | Rate limit reached. Please refer to API documentation and throttle requests accordingly |
| 50014 | 400 | Parameter \{param0\} cannot be empty |
| 50026 | 500 | System error. Try again later |
| 50103 | 401 | Request header "OK-ACCESS-KEY" cannot be empty |
| 50104 | 401 | Request header "OK-ACCESS-PASSPHRASE" cannot be empty|
| 50105 | 401 | Request header "OK-ACCESS-PASSPHRASE" incorrect |
| 50106 | 401 | Request header "OK-ACCESS-SIGN" cannot be empty |
| 50107 | 401 | Request header "OK-ACCESS-TIMESTAMP" cannot be empty |
| 50111 | 401 | Invalid OK-ACCESS-KEY |
| 50112 | 401 | Invalid OK-ACCESS-TIMESTAMP |
| 50113 | 401 | Invalid signature |
| 51000 | 400 | Parameter \{param0\} error |
- [Websocket](https://web3.okx.com/onchainos/dev-docs/market/websocket.md)
# Websocket
WebSocket is a new HTML5 protocol that achieves full-duplex data transmission between the client and server, allowing data to be transferred effectively in both directions. A connection between the client and server can be established with just one handshake. The server will then be able to push data to the client according to preset rules. Its advantages include:
- The WebSocket request header size for data transmission between client and server is only 2 bytes.
- Either the client or server can initiate data transmission.
- There's no need to repeatedly create and delete TCP connections, saving resources on bandwidth and server.
## Connect
**Connection limit**: 3 requests per second (based on API KEY)
When subscribing to a private channel, use the address of the private service
**Request limit**
The total number of 'subscribe'/'unsubscribe'/'login' requests per connection is limited to 480 times per hour.
If there’s a network problem, the system will automatically disable the connection.
The connection will break automatically if the subscription is not established or data has not been pushed for more than 30 seconds.
To keep the connection stable:
1. Set a timer of N seconds whenever a response message is received, where N is less than 30.
2. If the timer is triggered, which means that no new message is received within N seconds, send the String 'ping'.
3. Expect a 'pong' as a response. If the response message is not received within N seconds, please raise an error or reconnect.
## Notification
WebSocket has introduced a new message type (event = notice).
Client will receive the information in the following scenarios:
- Websocket disconnect for service upgrade
30 seconds prior to the upgrade of the WebSocket service, the notification message will be sent to users indicating that the connection will soon be disconnected. Users are encouraged to establish a new connection to prevent any disruptions caused by disconnection.
Response Example
```json
{
"event": "notice",
"code": "64008",
"msg": "The connection will soon be closed for a service upgrade. Please reconnect.",
"connId": "a4d3ae55"
}
```
- [Login](https://web3.okx.com/onchainos/dev-docs/market/websocket-login.md)
{/* api-page */}
# Login
## Request Parameters
| Parameter | Type | Required | Description |
|------------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| op | String | Yes | Operation, `login` |
| args | Array | Yes | List of subscribed channels |
| > apiKey | String | Yes | API Key |
| > passphrase | String | Yes | API Key password |
| > timestamp | String | Yes | Unix Epoch time, the unit is seconds |
| > sign | String | Yes | Signature string |
## Response Parameters
| Parameter | Type | Description |
|-----------------|--------|-----------------------------------------------------------------------------------------------------------------------|
| event | String | Operation.`login` or `error` |
| code | String | Error code |
| msg | String | Error message |
| connId | String | WebSocket connection ID |
**apiKey**: Unique identification for invoking API. Requires users to apply one manually in the [developer portal](https://web3.okx.com/zh-hans/build/dev-portal).
**passphrase**: API Key password
**timestamp**: the Unix Epoch time, the unit is seconds, e.g. 1704876947
**sign**: signature string, the signature algorithm is as follows:
First concatenate timestamp, method, requestPath, strings,
then use HMAC SHA256 method to encrypt the concatenated string with SecretKey,
and then perform Base64 encoding.
**secretKey**: The security key generated when the user applies for API Key, e.g. : 22582BD0CFF14C41EDBF1AB98506286D
**Example of timestamp**: const timestamp = '' + Date.now() / 1,000
**Among sign example**: sign=CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(timestamp +'GET'+'/users/self/verify', secretKey))
**method**: always 'GET'.
**requestPath**: always '/users/self/verify'
The request will expire 30 seconds after the timestamp. If your server time differs from the API server time,
we recommend using the REST API to query the API server time and then set the timestamp.
## Request Example
```json
{
"op": "login",
"args": [{
"apiKey": "985d5b66-57ce-40fb-b714-afc0b9787083",
"passphrase": "123456",
"timestamp": "1538054050",
"sign": "7L+zFQ+CEgGu5rzCj4+BdV2/uUHGqddA9pI6ztsRRPs="
}]
}
```
## Response Example
Successful Response Example
```json
{
"event": "login",
"code": "0",
"msg": "",
"connId": "a4d3ae55"
}
```
Failure Response Example
```json
{
"event": "error",
"code": "60009",
"msg": "Login failed.",
"connId": "a4d3ae55"
}
```
- [Subscribe](https://web3.okx.com/onchainos/dev-docs/market/websocket-subscribe.md)
{/* api-page */}
# Subscribe
Users can choose to subscribe to one or more channels, with the total length of all channels not exceeding 64 KB.
Price channels and trading channels require authentication before subscription.
K-line (candlestick) channels do not require authentication.
Below is an example of request parameters. Each channel has different parameter requirements, so please subscribe according to the specific requirements of each channel.
## Request Parameters
| Parameter | Type | Required | Description |
|------------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| op | String | Yes | Operation, `subscribe` |
| args | Array | Yes | List of subscribed channels |
| > channel | String | Yes | Channel name |
| > chainIndex | String | Yes | Unique identifier for the chain. (e.g., 1 for Ethereum. See [ChainIndex](../home/supported-chain)) |
| > timestamp | String | Yes | Unix Epoch time, the unit is seconds |
| > tokenContractAddress | String | Yes | Token contract address,for EVM please pass all-lowercase addresses (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
## Response Parameters
| Parameter | Type | Description |
|-----------------|--------|-----------------------------------------------------------------------------------------------------------------------|
| event | String | Operation.`subscribe` or `error` |
| arg | String | Subscribed channel |
| > channel | String | Channel name |
| > chainIndex | String | Unique identifier for the chain. (e.g., 1 for Ethereum. See [ChainIndex](../home/supported-chain)) |
| > tokenContractAddress | String | Token contract address (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
| code | String | Error code |
| msg | String | Error message |
| connId | String | WebSocket connection ID |
Request format description
```json
{"op": "subscribe","args": ["SubscriptionTopic"]}
```
## Request Example
```json
{
"op": "subscribe",
"args": [{
"channel": "price",
"chainIndex": "1",
"tokenContractAddress": "0x382bb369d343125bfb2117af9c149795c6c65c50"
}]
}
```
## Response Example
```json
{
"event": "subscribe",
"arg": {
"channel": "price",
"chainIndex": "1"
"tokenContractAddress":"0x382bb369d343125bfb2117af9c149795c6c65c50"
},
"connId": "accb8e21"
}
```
- [Unsubscribe](https://web3.okx.com/onchainos/dev-docs/market/websocket-unsubscribe.md)
{/* api-page */}
# Unsubscribe
Unsubscribe from one or more channels.
## Request Parameters
| Parameter | Type | Required | Description |
|------------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| op | String | Yes | Operation, `unsubscribe` |
| args | Array | Yes | List of subscribed channels |
| > channel | String | Yes | Channel name |
| > chainIndex | String | Yes | Unique identifier for the chain. (e.g., 1 for Ethereum. See [ChainIndex](../home/supported-chain)) |
| > timestamp | String | Yes | Unix Epoch time, the unit is seconds |
| > tokenContractAddress | String | Yes | Token contract address,for EVM please pass all-lowercase addresses (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
## Response Parameters
| Parameter | Type | Description |
|-----------------|--------|-----------------------------------------------------------------------------------------------------------------------|
| event | String | Operation.`unsubscribe` or `error` |
| arg | String | Subscribed channel |
| > channel | String | Channel name |
| > chainIndex | String | Unique identifier for the chain. (e.g., 1 for Ethereum. See [ChainIndex](../home/supported-chain)) |
| > tokenContractAddress | String | Token contract address (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
| code | String | Error code |
| msg | String | Error message |
| connId | String | WebSocket connection ID |
Request format description
```json
{
"op": "unsubscribe",
"args": ["SubscriptionTopic"]
}
```
## Request Example
```json
{
"op": "unsubscribe",
"args": [{
"channel": "price",
"chainIndex": "1",
"tokenContractAddress": "0x382bb369d343125bfb2117af9c149795c6c65c50"
}]
}
```
## Response Example
```json
{
"event": "unsubscribe",
"arg": {
"channel": "price",
"chainIndex": "1",
"tokenContractAddress":"0x382bb369d343125bfb2117af9c149795c6c65c50"
},
"connId": "d0b44253"
}
```
- [Websocket Channels](https://web3.okx.com/onchainos/dev-docs/market/websocket-channels.md)
# Websocket Channels
- [Price Channel](https://web3.okx.com/onchainos/dev-docs/market/websocket-price-channel.md)
{/* api-page */}
# Price Channel
Retrieve the latest price data of a token. The fastest push frequency updates in real time. A push will only occur if there is a trade and the price is not filtered out by the candlestick (K-line) price filter.
**Request URL**
wss://wsdex.okx.com/ws/v6/dex
## Request Parameters
| Parameter | Type | Required | Description |
|------------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| op | String | Yes | Operation, `subscribe` `unsubscribe` |
| args | Array | Yes | List of subscribed channels |
| channel | String | Yes | Channel name,`price` |
| chainIndex | String | Yes | Unique identifier for the chain. (e.g., 1 for Ethereum. See [ChainIndex](../home/supported-chain)) |
| tokenContractAddress | String | Yes | Token contract address,for EVM please pass all-lowercase addresses (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
## Response Parameters
| Parameter | Type | Description |
|-----------------|--------|-----------------------------------------------------------------------------------------------------------------------|
| event | String | Event, `subscribe` `unsubscribe` `error` |
| arg | Object | Token contract address |
| channel | String | Channel name |
| chainIndex| String | Unique identifier for the chain. (e.g., 1 for Ethereum. See [ChainIndex](../home/supported-chain)) |
| tokenContractAddress| String | Token contract address (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
| code | String | Error code |
| msg | String | Error message |
## Push Data Parameters
| Parameter | Type | Description |
|-----------------|--------|-----------------------------------------------------------------------------------------------------------------------|
| arg | Object | Successfully subscribed channel |
| > channel | String | Channel name |
| > chainIndex | String | Unique identifier for the chain. (e.g., 1 for Ethereum. See [ChainIndex](../home/supported-chain)) |
| > tokenContractAddress| String | Token contract address (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
| data | Array | Subscribed data |
| > time | String | Timestamp of the price, Unix timestamp format in milliseconds |
| > price | String | Latest token price |
## Request Example
```json
{
"op": "subscribe",
"args": [
{
"channel": "price",
"chainIndex": "1",
"tokenContractAddress":"0x382bb369d343125bfb2117af9c149795c6c65c50"
}
]
}
```
## Response Example
Successful response example
```json
{
"event": "subscribe",
"arg": {
"channel": "price",
"chainIndex": "1"
"tokenContractAddress":"0x382bb369d343125bfb2117af9c149795c6c65c50"
},
"connId": "a4d3ae55"
}
```
Failure response example
```json
{
"event": "error",
"code": "60012",
"msg": "Invalid request: {\"op\": \"subscribe\", \"argss\":[{ \"channel\" : \"price\", \"chainIndex\" : \"1\", \"tokenContractAddress\" : \"0x382bb369d343125bfb2117af9c149795c6c65c50\"}]}",
"connId": "a4d3ae55"
}
```
Push data example
```json
{
"arg": {
"channel": "price",
"chainIndex": "1"
"tokenContractAddress":"0x382bb369d343125bfb2117af9c149795c6c65c50"
},
"data": [
{
"time": "1716892020000",
"price": "26.458143090226812",
}
]
}
```
- [Liquidity channel](https://web3.okx.com/onchainos/dev-docs/market/websocket-price-info-channel.md)
{/* api-page */}
# Liquidity channel
Returns token liquidity-related data with a maximum push frequency of once per second.
**Request URL**
wss://wsdex.okx.com/ws/v6/dex
## Request Parameters
| Parameter | Type | Required | Description |
|------------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| op | String | Yes | Operation, `subscribe` `unsubscribe` |
| args | Array | Yes | List of subscribed channels |
| channel | String | Yes | Channel name,`price` |
| chainIndex | String | Yes | Unique identifier for the chain. (e.g., 1 for Ethereum. See [ChainIndex](../home/supported-chain)) |
| tokenContractAddress | String | Yes | Token contract address,for EVM please pass all-lowercase addresses (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
## Response Parameters
| Parameter | Type | Description |
|-----------------|--------|-----------------------------------------------------------------------------------------------------------------------|
| event | String | Event, `subscribe` `unsubscribe` `error` |
| arg | Object | Token contract address |
| channel | String | Channel name |
| chainIndex| String | Unique identifier for the chain. (e.g., 1 for Ethereum. See [ChainIndex](../home/supported-chain)) |
| tokenContractAddress| String | Token contract address (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
| code | String | Error code |
| msg | String | Error message |
## Push Data Parameters
| Parameter | Type | Description |
|------------------------ |-------- |--------------------------------------------------------------------------- |
| arg | Object | Successfully subscribed channel |
| > channel | String | Channel name |
| > chainIndex | String | Unique identifier for the chain. (e.g., 1 for Ethereum. See ChainIndex) |
| > tokenContractAddress | String | Token contract address (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
| data | Array | Subscribed data |
| > time | String | Timestamp of the price, Unix timestamp format in milliseconds |
| > price | String | Latest token price |
| > marketCap | String | Token marketcap |
| > priceChange5M | String | 5 min price change |
| > priceChange1H | String | 1 hour price change |
| > priceChange4H | String | 4 hour price change |
| > priceChange24H | String | 24 hour price change |
| > volume5M | String | 5 min volume |
| > volume1H | String | 1 hour volume |
| > volume4H | String | 4 hour volume |
| > volume24H | String | 24 hour volume |
| > txs5M | String | 代币 5 分钟内交易笔数 |
| >txs1H | String | 代币 1 小时内交易笔数 |
| >txs4H | String | 代币 4 小时内交易笔数 |
| >txs24H | String | 代币 24 小时内交易笔数 |
| >maxPrice | String | 代币 24h 最高价格 |
| >tradeNum | String | 24h 代币交易数量 |
| >minPrice | String | 代币 24h 最低价格 |
| >circSupply | String | 代币流通供应量 |
| >liquidity | String | 代币资金池中的流动性 |
| >holders | String | 代币持仓地址数 |
## Request Example
```json
{
"op": "subscribe",
"args": [
{
"channel": "price-info",
"chainIndex": "1",
"tokenContractAddress":"0x382bb369d343125bfb2117af9c149795c6c65c50"
}
]
}
```
## Response Example
Successful response example
```json
{
"event": "subscribe",
"arg": {
"channel": "price-info",
"chainIndex": "501"
"tokenContractAddress":"eL5fUxj2J4CiQsmW85k5FG9DvuQjjUoBHoQBi2Kpump"
},
"connId": "a4d3ae55"
}
```
Failure response example
```json
{
"event": "error",
"code": "60012",
"msg": "Invalid request: {\"op\": \"subscribe\", \"argss\":[{ \"channel\" : \"price-info\", \"chainIndex\" : \"501\", \"tokenContractAddress\" : \"eL5fUxj2J4CiQsmW85k5FG9DvuQjjUoBHoQBi2Kpump\"}]}",
"connId": "a4d3ae55"
}
```
Push data example
```json
{
"arg": {
"channel": "price-info",
"chainIndex": "501"
"tokenContractAddress":"0x382bb369d343125bfb2117af9c149795c6c65c50"
},
"data": [
{
"chainIndex": "501",
"circSupply": "999973312.2632950000",
"holders": "37241",
"liquidity": "3923952.461979153265333544895656917",
"marketCap": "19960307.19257757296691203",
"maxPrice": "0.1656024888921609",
"minPrice": "0.02292722724150618",
"price": "0.019960839902217294",
"priceChange1H": "9.12",
"priceChange24H": "374.25",
"priceChange4H": "68.26",
"priceChange5M": "6.91",
"time": "1758702741738",
"tokenContractAddress": "eL5fUxj2J4CiQsmW85k5FG9DvuQjjUoBHoQBi2Kpump",
"tradeNum": "2460429287.120492",
"txs1H": "15142",
"txs24H": "276164",
"txs4H": "38998",
"txs5M": "1196",
"volume1H": "12864939.572057",
"volume24H": "169512096.311189",
"volume4H": "29069166.04389",
"volume5M": "893224.505265"
}
]
}
```
- [Candlesticks Channel](https://web3.okx.com/onchainos/dev-docs/market/websocket-candlesticks-channel.md)
{/* api-page */}
# Candlesticks Channel
Retrieve the candlesticks data of a token. The fastest push frequency is 1 push per second.
**Request URL**
wss://wsdex.okx.com/ws/v6/dex
## Request Parameters
| Parameter | Type | Required | Description |
|------------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| op | String | Yes | Operation, `subscribe` `unsubscribe` |
| args | Array | Yes | List of subscribed channels |
| channel | String | Yes | Channel name. `dex-token-candle1s` `dex-token-candle1m` `dex-token-candle3m` `dex-token-candle5m` `dex-token-candle15m` `dex-token-candle30m` `dex-token-candle1H` `dex-token-candle2H` `dex-token-candle4H` `dex-token-candle6H` `dex-token-candle12H` `dex-token-candle1M` `dex-token-candle3M` `dex-token-candle1W` `dex-token-candle1D` `dex-token-candle2D` `dex-token-candle3D` `dex-token-candle5D` `dex-token-candle6Hutc` `dex-token-candle12Hutc` `dex-token-candle1Dutc` `dex-token-candle2Dutc` `dex-token-candle3Dutc` `dex-token-candle5Dutc` `dex-token-candle1Wutc` `dex-token-candle1Mutc` `dex-token-candle3Mutc` |
| chainIndex | String | Yes | Unique identifier for the chain. (e.g., 1 for Ethereum. See [ChainIndex](../home/supported-chain)) |
| tokenContractAddress | String | Yes | Token contract address,for EVM please pass all-lowercase addresses (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
## Response Parameters
| Parameter | Type | Description |
|-----------------|--------|-----------------------------------------------------------------------------------------------------------------------|
| event | String | Event, `subscribe` `unsubscribe` `error` |
| arg | Object | Token contract address |
| channel | String | Channel name |
| chainIndex| String | Unique identifier for the chain. (e.g., 1 for Ethereum. See [ChainIndex](../home/supported-chain)) |
| tokenContractAddress| String | Token contract address (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
| code | String | Error code |
| msg | String | Error message |
## Push Data Parameters
| Parameter | Type | Description |
|-----------------|--------|-----------------------------------------------------------------------------------------------------------------------|
| arg | Object | Successfully subscribed channel |
| > channel | String | Channel name |
| > chainIndex | String | Unique identifier for the chain. (e.g., 1 for Ethereum. See [ChainIndex](../home/supported-chain)) |
| > tokenContractAddress| String | Token contract address (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
| data | Array | Subscribed data |
| > ts | String | Opening time of the candlestick, Unix timestamp format in milliseconds, e.g. 1597026383085 |
| > o | String | Open price |
| > h | String | highest price |
| > l | String | Lowest price |
| > c | String | Close price |
| > vol | String | Trading volume, with a unit of base currency |
| > volUsd | String | Trading volume, with a unit of usd. |
| > confirm | String | The state of candlesticks. `0`: represents that it is uncompleted `1`: represents that it is completed. |
## Request Example
```json
{
"op": "subscribe",
"args": [
{
"channel": "dex-token-candle1s",
"chainIndex": "1",
"tokenContractAddress":"0x382bb369d343125bfb2117af9c149795c6c65c50"
}
]
}
```
## Response Example
Successful response example
```json
{
"event": "subscribe",
"arg": {
"channel": "dex-token-candle1s",
"chainIndex": "1"
"tokenContractAddress":"0x382bb369d343125bfb2117af9c149795c6c65c50"
},
"connId": "a4d3ae55"
}
```
Failure response example
```json
{
"event": "error",
"code": "60012",
"msg": "Invalid request: {\"op\": \"subscribe\", \"argss\":[{ \"channel\" : \"dex-token-candle1s\", \"chainIndex\" : \"1\", \"tokenContractAddress\" : \"0x382bb369d343125bfb2117af9c149795c6c65c50\"}]}",
"connId": "a4d3ae55"
}
```
Push data example
```json
{
"arg": {
"channel": "dex-token-candle1s",
"chainIndex": "1"
"tokenContractAddress":"0x382bb369d343125bfb2117af9c149795c6c65c50"
},
"data": [
[
"1597026383085",
"8533.02",
"8553.74",
"8527.17",
"8548.26",
"529.5858061",
"226348.0482",
"0"
]
]
}
```
- [Token Update Channel](https://web3.okx.com/onchainos/dev-docs/market/websocket-token-channel.md)
{/* api-page */}
# Token Update Channel
Real-time push of incremental market metric updates for Meme tokens. Data is pushed whenever metrics change.
**URL Path**
wss://wsdex.okx.com/ws/v6/dex
## Request Parameters
| Parameter | Type | Required | Description |
|--------------------------|--------|----------|-------------------------------------------------------------------------------------------------|
| op | String | Yes | Operation: `subscribe` `unsubscribe` |
| args | Array | Yes | List of channels to subscribe |
| channel | String | Yes | Channel name: `dex-market-memepump-update-metrics-openapi` |
| chainIndex | String | Yes | Unique identifier for the chain. Pass the chain ID (e.g., 501 for Solana). Single-chain only. |
## Response Parameters
| Parameter | Type | Description |
|-----------|--------|-----------------------------------------------------------|
| event | String | Event type: `subscribe` `unsubscribe` `error` |
| arg | Object | Subscribed channel |
| channel | String | Channel name |
| code | String | Error code (only returned when event=error) |
| msg | String | Error message (only returned when event=error) |
| connId | String | WebSocket connection ID |
## Push Data Parameters
| Parameter | Type | Description |
|------------------------------------|---------|---------------------------------------------------------------------------------------|
| arg | Object | Successfully subscribed channel info |
| > channel | String | Channel name |
| > chainIndex | String | Unique identifier for the chain |
| data | Array | Batched real-time token metric updates (each batch contains multiple token objects) |
| > chainIndex | String | Chain ID (e.g., 501=Solana) |
| > protocolId | String | Protocol source ID (e.g., 1=PUMP_FUN) |
| > quoteTokenAddress | String | Quote token contract address |
| > tokenContractAddress | String | Token contract address |
| > symbol | String | Token symbol |
| > name | String | Token name |
| > logoUrl | String | Token logo URL |
| > createdTimestamp | String | Token creation time (Unix timestamp in milliseconds) |
| > market | Object | Market data (incremental update) |
| >> marketCapUsd | String | Market cap (USD) |
| >> volumeUsd1h | String | 1-hour trading volume (USD) |
| >> txCount1h | String | 1-hour total transaction count |
| >> buyTxCount1h | String | 1-hour buy transaction count |
| >> sellTxCount1h | String | 1-hour sell transaction count |
| > bondingPercent | String | Bonding curve progress (%) |
| > mayhemModeTimeRemaining | String | Remaining time for Pump.fun Mayhem Mode; empty if token is not in this mode |
| > tags | Object | Tag / audit data |
| >> top10HoldingsPercent | String | Top 10 holders percentage (%) |
| >> devHoldingsPercent | String | Dev holdings percentage (%) |
| >> insidersPercent | String | Insiders percentage (%) |
| >> bundlersPercent | String | Bundlers percentage (%) |
| >> snipersPercent | String | Snipers percentage (%) |
| >> freshWalletsPercent | String | Fresh wallets percentage (%) |
| >> suspectedPhishingWalletPercent | String | Suspected phishing wallet percentage (%) |
| >> totalHolders | String | Total number of token holder addresses |
| > social | Object | Social media information |
| >> x | String | X (Twitter) link |
| >> telegram | String | Telegram link |
| >> website | String | Website link |
| >> dexScreenerPaid | Boolean | DEX Screener paid |
| >> communityTakeover | Boolean | Community takeover (CTO) |
| >> liveOnPumpFun | Boolean | Live on Pump.fun |
| > bagsFeeClaimed | Boolean | Whether bags fee has been claimed |
## Request Example
```json
{
"op": "subscribe",
"args": [
{
"channel": "dex-market-memepump-update-metrics-openapi",
"chainIndex": "501"
}
]
}
```
## Response Example
Successful response example
```json
{
"event": "subscribe",
"arg": {
"channel": "dex-market-memepump-update-metrics-openapi",
"chainIndex": "501"
},
"connId": "a4d3ae55"
}
```
Failure response example
```json
{
"event": "error",
"code": "60012",
"msg": "Invalid request: {\"op\": \"subscribe\", \"argss\":[{ \"channel\": \"dex-market-memepump-update-metrics-openapi\", \"chainIndex\": \"501\"}]}",
"connId": "a4d3ae55"
}
```
Push data example
```json
{
"arg": {
"channel": "dex-market-memepump-update-metrics-openapi",
"chainIndex": "501"
},
"data": [
[
{
"bagsFeeClaimed": false,
"bondingPercent": "0.02",
"chainIndex": "501",
"createdTimestamp": "1773129702000",
"creatorAddress": "5DZ1ghesLRDzioYoDoyFejgxRwFBxgs9P85kv6d8Zd7X",
"logoUrl": "https://static.coinall.ltd/cdn/web3/currency/token/default-logo/token_custom_logo_default_P/type=default_350_0",
"market": {
"buyTxCount1h": "1",
"marketCapUsd": "2457.472431547000000000",
"sellTxCount1h": "0",
"txCount1h": "1",
"volumeUsd1h": "4.011085498679725011505911"
},
"name": "$PRISM",
"protocolId": "136137",
"quoteTokenAddress": "So11111111111111111111111111111111111111112",
"social": {
"communityTakeover": false,
"dexScreenerPaid": false,
"liveOnPumpFun": false
},
"symbol": "PSM",
"tags": {
"bundlersPercent": "0",
"devHoldingsPercent": "0.0458",
"freshWalletsPercent": "0",
"insidersPercent": "0",
"snipersPercent": "0.0458",
"suspectedPhishingWalletPercent": "0",
"top10HoldingsPercent": "0.16320",
"totalHolders": "1"
},
"tokenAddress": "SXEdooR2e1RHpYdarMqFPrkhTBARn6NhT88nXjVrD2X"
}
]
]
}
```
- [Trades Channel](https://web3.okx.com/onchainos/dev-docs/market/websocket-trades-channel.md)
{/* api-page */}
# Trades Channel
Retrieve the recent trades data. Data will be pushed whenever there is a trades.
**Request URL**
wss://wsdex.okx.com/ws/v6/dex
## Request Parameters
| Parameter | Type | Required | Description |
| -------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------ |
| op | String | Yes | Operation, `subscribe` `unsubscribe` |
| args | Array | Yes | List of subscribed channels |
| channel | String | Yes | Channel name,`trades` |
| chainIndex | String | Yes | Unique identifier for the chain. (e.g., 1 for Ethereum. See [ChainIndex](../home/supported-chain)) |
| tokenContractAddress | String | Yes | Token contract address,for EVM please pass all-lowercase addresses (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
## Response Parameters
| Parameter | Type | Description |
| -------------------- | ------ | ------------------------------------------------------------------------------------------------ |
| event | String | Event, `subscribe` `unsubscribe` `error` |
| arg | Object | Token contract address |
| channel | String | Channel name |
| chainIndex | String | Unique identifier for the chain. (e.g., 1 for Ethereum. See [ChainIndex](../home/supported-chain)) |
| tokenContractAddress | String | Token contract address (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
| code | String | Error code |
| msg | String | Error message |
## Push Data Parameters
| Parameter | Type | Description |
| ----------------------- | ------ | ------------------------------------------------------------------------------------------------ |
| arg | Object | Successfully subscribed channel |
| > channel | String | Channel name |
| > chainIndex | String | Unique identifier for the chain. (e.g., 1 for Ethereum. See [ChainIndex](../home/supported-chain)) |
| > tokenContractAddress | String | Token contract address (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
| data | Array | Subscribed data |
| > id | String | Unique trade id |
| > txHashUrl | String | On-chain txhash of the transaction |
| > userAddress | String | Authorizer of the transaction |
| > dexName | String | Name of the dex where the trade occured |
| > poolLogoUrl | String | Pool logo url |
| > type | String | Trade Type buy sell |
| > amountExchanged | String | Amount exchanged in this pair |
| >> amount | String | Token exchanged amount in this trade |
| >> tokenSymbol | String | Token symbol |
| >> tokenContractAddress | String | Token contract address |
| > price | String | Latest token price |
| > volume | String | USD value of this trade |
| > time | String | Timestamp of the trade, Unix timestamp format in milliseconds |
| > isFiltered | String | If the trade is filtered for price and k-line calculation. `0`: not filtered `1`: filtered |
## Request Example
```json
{
"op": "subscribe",
"args": [
{
"channel": "trades",
"chainIndex": "501",
"tokenContractAddress": "HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC"
}
]
}
```
## Response Example
Successful response example
```json
{
"event": "subscribe",
"arg": {
"channel": "trades",
"chainIndex": "501",
"tokenContractAddress": "HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC"
},
"connId": "a4d3ae55"
}
```
Failure response example
```json
{
"event": "error",
"code": "60012",
"msg": "Invalid request: {\"op\": \"subscribe\", \"argss\":[{ \"channel\" : \"trades\", \"chainIndex\" : \"501\", \"tokenContractAddress\" : \"HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC\"}]}",
"connId": "a4d3ae55"
}
```
Push data example
```json
{
"arg": {
"channel": "trades",
"chainIndex": "501"
"tokenContractAddress":"HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC"
},
"data":[
{
"id":"1739439633000!@#120!@#14731892839",
"chainIndex": "501",
"tokenContractAddress": "HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC",
"txHashUrl": "https://solscan.io/tx/zgDzoiVG4XuDgQcoEg9vhpRyfyk5thNUQuTeTCeF289Qec5iraeCrUzPLyiE2UCviox2ebbTcsagGvzYF7M5uqs",
"userAddress": "2kCm1RHGJjeCKL4SA3ZJCLyXqUD7nEJ7GMtVaP7c6jQ8",
"dexName": "Orca Whirlpools",
"poolLogoUrl": "https://static.okx.com/cdn/wallet/logo/dex_orcaswap.png",
"type": "sell",
"changedTokenInfo": [
{
"amount":"100.396595878",
"tokenSymbol":"ai16z",
"tokenContractAddress": "HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC"
},
{
"amount":"2.482831",
"tokenSymbol":"SOL",
"tokenContractAddress": "So11111111111111111111111111111111111111112"
}
]
"price": "26.458143090226812",
"volume": "519.788163",
"time": "1739439633000",
"isFiltered": "0"
}
]
}
```
- [Signal Channel](https://web3.okx.com/onchainos/dev-docs/market/websocket-signal-channel.md)
{/* api-page */}
# Signal Channel
Real-time push of on-chain trading signals from Smart Money / KOL / Whale wallets. Subscribe after login; data is pushed whenever a new signal is triggered.
**URL Path**
wss://wsdex.okx.com/ws/v6/dex
## Request Parameters
| Parameter | Type | Required | Description |
|-------------|--------|----------|----------------------------------------------------------------------------------------------------------|
| op | String | Yes | Operation: `subscribe` `unsubscribe` |
| args | Array | Yes | List of channels to subscribe |
| channel | String | Yes | Channel name `dex-market-new-signal-openapi` |
| chainIndex | String | Yes | Unique identifier for the chain. Pass the chain ID (e.g., 1 for Ethereum). Single-chain only. |
## Response Parameters
| Parameter | Type | Description |
|------------------------------|--------|---------------------------------------------------------------------------------------------------------------------|
| event | String | Event type: `subscribe` `unsubscribe` `error` |
| arg | Object | Subscribed channel |
| channel | String | Channel name |
| signal | Object | Signal list |
| > timestamp | String | Timestamp when the signal was triggered |
| > chainIndex | String | Unique identifier for the chain |
| > token | Object | Token information |
| >> tokenAddress | String | Token contract address |
| >> symbol | String | Token symbol |
| >> name | String | Token name |
| >> logo | String | Token logo URL |
| >> marketCapUsd | String | Market cap (USD) |
| >> holders | String | Number of holder addresses |
| >> top10HolderPercent | String | Top 10 holder percentage |
| > price | String | Token price (USD) at signal trigger time |
| > walletType | String | Wallet type code. Enum: `1` = Smart Money, `2` = KOL / Influencer, `3` = Whales. Multiple values separated by commas |
| > triggerWalletCount | String | Number of wallet addresses that triggered the signal |
| > triggerWalletAddress | String | List of wallet addresses, comma-separated |
| > amountUsd | String | Trade amount (USD) |
| > soldRatioPercent | String | Sell-off ratio percentage |
| code | String | Error code (only returned when event=error) |
| msg | String | Error message (only returned when event=error) |
## Push Data Parameters
| Parameter | Type | Description |
|------------------------------|--------|---------------------------------------------------------------------------------------------------------------------|
| arg | Object | Successfully subscribed channel info |
| > channel | String | Channel name |
| > timestamp | String | Timestamp when the signal was triggered |
| > chainIndex | String | Unique identifier for the chain |
| > token | Object | Token information |
| >> tokenAddress | String | Token contract address |
| >> symbol | String | Token symbol |
| >> name | String | Token name |
| >> logo | String | Token logo URL |
| >> marketCapUsd | String | Market cap (USD) |
| >> holders | String | Number of holder addresses |
| >> top10HolderPercentage | String | Top 10 holder percentage |
| > price | String | Token price (USD) at signal trigger time |
| > walletType | String | Wallet type code. Enum: `1` = Smart Money, `2` = KOL / Influencer, `3` = Whales. Multiple values separated by commas |
| > triggerWalletCount | String | Number of wallet addresses that triggered the signal |
| > triggerWalletAddress | String | List of wallet addresses, comma-separated |
| > amountUsd | String | Trade amount (USD) |
| > soldRatioPercentage | String | Sell-off ratio percentage |
## Request Example
```json
{
"op": "subscribe",
"args": [
{
"channel": "dex-market-new-signal-openapi",
"chainIndex": "1"
}
]
}
```
## Response Example
Successful response example
```json
{
"event": "subscribe",
"arg": {
"channel": "dex-market-new-signal-openapi",
"chainIndex": "1"
},
"connId": "a4d3ae55"
}
```
Failure response example
```json
{
"event": "error",
"code": "60012",
"msg": "Invalid request: {\"op\": \"subscribe\", \"argss\":[{ \"channel\" , \"chainIndex\" : \"1\", \"tokenContractAddress\" : \"0x382bb369d343125bfb2117af9c149795c6c65c50\"}]}",
"connId": "a4d3ae55"
}
```
Push data example
```json
{
"arg": {
"channel": "dex-market-new-signal-openapi",
"chainIndex": "1",
"timestamp": "1739439633000",
"token": {
"tokenAddress": "0x382bb369d343125bfb2117af9c149795c6c65c50",
"symbol": "ORBS",
"name": "Orbs",
"logo": "https://static.okx.com/cdn/wallet/logo/ORBS.png",
"marketCapUsd": "89234567.12",
"holders": "23456",
"top10HolderPercentage": "35.6"
},
"price": "0.0421",
"walletType": "1,2",
"triggerWalletCount": "5",
"triggerWalletAddress": "0xabc...111,0xdef...222",
"amountUsd": "128000.00",
"soldRatioPercentage": "0"
}
}
```
- [Memepump Channel](https://web3.okx.com/onchainos/dev-docs/market/websocket-memepump-channel.md)
{/* api-page */}
# Memepump Channel
Real-time push of newly launched Meme token data. Data is pushed whenever a new token is released.
**URL Path**
wss://wsdex.okx.com/ws/v6/dex
## Request Parameters
| Parameter | Type | Required | Description |
|--------------------------|--------|----------|-------------------------------------------------------------------------------------------------|
| op | String | Yes | Operation: `subscribe` `unsubscribe` |
| args | Array | Yes | List of channels to subscribe |
| channel | String | Yes | Channel name: `dex-market-memepump-new-token-openapi` |
| chainIndex | String | Yes | Unique identifier for the chain. Pass the chain ID (e.g., 501 for Solana). Single-chain only. |
## Response Parameters
| Parameter | Type | Description |
|-----------|--------|-----------------------------------------------------------|
| event | String | Event type: `subscribe` `unsubscribe` `error` |
| arg | Object | Subscribed channel |
| channel | String | Channel name |
| code | String | Error code (only returned when event=error) |
| msg | String | Error message (only returned when event=error) |
| connId | String | WebSocket connection ID |
## Push Data Parameters
| Parameter | Type | Description |
|------------------------------------|---------|--------------------------------------------------------------------------|
| arg | Object | Successfully subscribed channel info |
| > channel | String | Channel name |
| > chainIndex | String | Unique identifier for the chain |
| data | Array | List of newly launched tokens |
| > chainIndex | String | Chain ID (e.g., 501=Solana) |
| > protocolId | String | Protocol source ID (e.g., 1=PUMP_FUN) |
| > quoteTokenAddress | String | Quote token contract address |
| > tokenContractAddress | String | Token contract address |
| > symbol | String | Token symbol |
| > name | String | Token name |
| > logoUrl | String | Token logo URL |
| > createdTimestamp | String | Token creation time (Unix timestamp in milliseconds) |
| > market | Object | Market data |
| >> marketCapUsd | String | Market cap (USD) |
| >> volumeUsd1h | String | 1-hour trading volume (USD) |
| >> txCount1h | String | 1-hour total transaction count |
| >> buyTxCount1h | String | 1-hour buy transaction count |
| >> sellTxCount1h | String | 1-hour sell transaction count |
| > bondingPercent | String | Bonding curve progress (%) |
| > tags | Object | Tag / audit data |
| >> top10HoldingsPercent | String | Top 10 holders percentage (%) |
| >> devHoldingsPercent | String | Dev holdings percentage (%) |
| >> insidersPercent | String | Insiders percentage (%) |
| >> bundlersPercent | String | Bundlers percentage (%) |
| >> snipersPercent | String | Snipers percentage (%) |
| >> freshWalletsPercent | String | Fresh wallets percentage (%) |
| >> suspectedPhishingWalletPercent | String | Suspected phishing wallet percentage (%) |
| >> totalHolders | String | Total number of token holder addresses |
| > social | Object | Social media information |
| >> x | String | X (Twitter) link |
| >> telegram | String | Telegram link |
| >> website | String | Website link |
| >> dexScreenerPaid | Boolean | DEX Screener paid |
| >> communityTakeover | Boolean | Community takeover (CTO) |
| >> liveOnPumpFun | Boolean | Live on Pump.fun |
| > bagsFeeClaimed | Boolean | Whether bags fee has been claimed |
## Request Example
```json
{
"op": "subscribe",
"args": [
{
"channel": "dex-market-memepump-new-token-openapi",
"chainIndex": "501"
}
]
}
```
## Response Example
Successful response example
```json
{
"event": "subscribe",
"arg": {
"channel": "dex-market-memepump-new-token-openapi",
"chainIndex": "501"
},
"connId": "a4d3ae55"
}
```
Failure response example
```json
{
"event": "error",
"code": "60012",
"msg": "Invalid request: {\"op\": \"subscribe\", \"argss\":[{ \"channel\": \"dex-market-memepump-new-token-openapi\", \"chainIndex\": \"501\"}]}",
"connId": "a4d3ae55"
}
```
Push data example
```json
{
"arg": {
"channel": "dex-market-memepump-new-token-openapi",
"chainIndex": "501"
},
"data": [
{
"bagsFeeClaimed": false,
"bondingPercent": "0",
"chainIndex": "501",
"createdTimestamp": "1773111278502",
"creatorAddress": "CfCpn9LFW6HDsUcNUbX65HfxNmouhTNCb6RD9FmF8sy9",
"logoUrl": "https://static.oklink.com/cdn/web3/currency/token/default-logo/token_custom_logo_default_S/type=default_350_0",
"market": {},
"name": "Siberian Husky Scarlett",
"protocolId": "136460",
"social": {
"communityTakeover": false,
"dexScreenerPaid": false,
"liveOnPumpFun": false
},
"symbol": "Scarlett",
"tags": {},
"tokenAddress": "5E2yC3KVFhm2kvUXFMWJCKP7pLtkScBP8CnZ9rtjpump"
}
]
}
```
- [Address Tracker Trades Channel](https://web3.okx.com/onchainos/dev-docs/market/websocket-address-activity-channel.md)
{/* api-page */}
# Address Tracker Trades Channel
Real-time push of on-chain transaction activity from tracked wallet addresses. Subscribe after login; data is pushed whenever a new transaction occurs.
**URL Path**
wss://wsdex.okx.com/ws/v6/dex
## Request Parameters
| Parameter | Type | Required | Description |
|-------------|--------|----------|---------------------------------------------------------------|
| op | String | Yes | Operation: `subscribe` `unsubscribe` |
| args | Array | Yes | List of subscription parameters |
| > channel | String | Yes | Channel name: `address-tracker-activity` |
| > walletAddress | String | Yes | The wallet addresses you want to track, One connection can support up to 200 addresses; for 1,000 addresses, simply establish 5 connections. |
## Response Parameters
| Parameter | Type | Description |
|-------------|--------|--------------------------------------------------------------------|
| event | String | Event type: `subscribe` `unsubscribe` `error` |
| arg | Object | Successfully subscribed channel parameters |
| > channel | String | Channel name |
| code | String | Error code (only returned when event=error) |
| msg | String | Error message (only returned when event=error) |
| connId | String | WebSocket connection ID |
## Push Data Parameters
| Parameter | Type | Description |
|------------------------|--------|-------------------------------------------------------------------------------------------------|
| arg | Object | Channel info that triggered the push |
| > channel | String | Channel name |
| data | Array | List of pushed trade activity |
| > txHash | String | Transaction hash |
| > walletAddress | String | Trading wallet address |
| > quoteTokenSymbol | String | Quote token symbol (native chain token) |
| > quoteTokenAmount | String | Quote token trade amount |
| > tokenSymbol | String | Traded token symbol |
| > tokenContractAddress | String | Traded token contract address |
| > chainIndex | String | Chain identifier of the traded token |
| > tokenPrice | String | Trade price of the token (USD) |
| > marketCap | String | Market cap of the token at the trade price (USD) |
| > realizedPnlUsd | String | Realized PnL for the token (USD) |
| > tradeType | String | Trade type: `1` buy `2` sell |
| > tradeTime | String | Trade time (Unix timestamp in milliseconds) |
| > trackerType | Array | Tracker wallet type: `1` Smart Money `2` KOL |
## Request Example
```json
{
"channel": "address-tracker-activity",
"walletAddress": "DHfshpzoC9Q7rz32j5juq2do3Bo8bA1KLmkNiRYaA8tf"
},
{
"channel": "address-tracker-activity",
"walletAddress": "0x1234567890abcdef1234567890abcdef12345678"
},
{
"channel": "address-tracker-activity",
"walletAddress": "0x1234567890abcdef1234567890abcdef12345678"
}
```
## Response Example
Successful response example
```json
{
"event": "subscribe",
"arg": {
"channel": "address-tracker-activity"
},
"connId": "a4d3ae55"
}
```
Failure response example
```json
{
"event": "error",
"code": "60012",
"msg": "Invalid request: {\"op\": \"subscribe\", \"argss\":[{ \"channel\" , \"chainIndex\" : \"1\", \"tokenContractAddress\" : \"0x382bb369d343125bfb2117af9c149795c6c65c50\"}]}",
"connId": "a4d3ae55"
}
```
Push data example
```json
{
"arg": {
"channel": "address-tracker-activity"
},
"data": [
{
"baseTokenChainIndex": "501",
"baseTokenContractAddress": "FmxDdxpFmmuN4DeXoHFzuEyrH8RfRsej6oxg4MaUpump",
"baseTokenSymbol": "XAIC",
"marketCap": "3229.75150297000000000000000000000000000",
"quoteTokenAmount": "1.576294",
"quoteTokenSymbol": "SOLANA",
"realizedPnlUsd": "-0.481221442431056693018746",
"trackerType": [1],
"tradePrice": "0.00000322975150297",
"tradeTime": 1773628806000,
"tradeType": "2",
"walletAddress": "DHfshpzoC9Q7rz32j5juq2do3Bo8bA1KLmkNiRYaA8tf"
}
]
}
```
- [KOL & Smart Money Tracker Trades Channel](https://web3.okx.com/onchainos/dev-docs/market/websocket-kol-smartmoney-activity-channel.md)
{/* api-page */}
# KOL & Smart Money Tracker Trades Channel
Real-time push of on-chain transaction activity from tracked KOL / Smart Money addresses. Subscribe after login; data is pushed whenever a new transaction occurs.
**URL Path**
wss://wsdex.okx.com/ws/v6/dex
## Request Parameters
| Parameter | Type | Required | Description |
|-------------|--------|----------|---------------------------------------------------------------|
| op | String | Yes | Operation: `subscribe` `unsubscribe` |
| args | Array | Yes | List of subscription parameters |
| > channel | String | Yes | Channel name: `kol_smartmoney-tracker-activity` |
## Response Parameters
| Parameter | Type | Description |
|-------------|--------|--------------------------------------------------------------------|
| event | String | Event type: `subscribe` `unsubscribe` `error` |
| arg | Object | Successfully subscribed channel parameters |
| > channel | String | Channel name |
| code | String | Error code (only returned when event=error) |
| msg | String | Error message (only returned when event=error) |
| connId | String | WebSocket connection ID |
## Push Data Parameters
| Parameter | Type | Description |
|------------------------|--------|-------------------------------------------------------------------------------------------------|
| arg | Object | Channel info that triggered the push |
| > channel | String | Channel name |
| data | Array | List of pushed trade activity |
| > txHash | String | Transaction hash |
| > walletAddress | String | Trading wallet address |
| > quoteTokenSymbol | String | Quote token symbol (native chain token) |
| > quoteTokenAmount | String | Quote token trade amount |
| > tokenSymbol | String | Traded token symbol |
| > tokenContractAddress | String | Traded token contract address |
| > chainIndex | String | Chain identifier of the traded token |
| > tokenPrice | String | Trade price of the token (USD) |
| > marketCap | String | Market cap of the token at the trade price (USD) |
| > realizedPnlUsd | String | Realized PnL for the token (USD) |
| > tradeType | String | Trade type: `1` buy `2` sell |
| > tradeTime | String | Trade time (Unix timestamp in milliseconds) |
| > trackerType | Array | Tracker wallet type: `1` Smart Money `2` KOL |
## Request Example
```json
{
"op": "subscribe",
"args": [
{
"channel": "kol_smartmoney-tracker-activity"
}
]
}
```
## Response Example
Successful response example
```json
{
"event": "subscribe",
"arg": {
"channel": "kol_smartmoney-tracker-activity"
},
"connId": "a4d3ae55"
}
```
Failure response example
```json
{
"event": "error",
"code": "60012",
"msg": "Invalid request: {\"op\": \"subscribe\", \"argss\":[{ \"channel\" , \"chainIndex\" : \"1\", \"tokenContractAddress\" : \"0x382bb369d343125bfb2117af9c149795c6c65c50\"}]}",
"connId": "a4d3ae55"
}
```
Push data example
```json
{
"arg": {
"channel": "kol_smartmoney-tracker-activity"
},
"data": [
{
"baseTokenChainIndex": "501",
"baseTokenContractAddress": "FmxDdxpFmmuN4DeXoHFzuEyrH8RfRsej6oxg4MaUpump",
"baseTokenSymbol": "XAIC",
"marketCap": "3229.75150297000000000000000000000000000",
"quoteTokenAmount": "1.576294",
"quoteTokenSymbol": "SOLANA",
"realizedPnlUsd": "-0.481221442431056693018746",
"trackerType": [1],
"tradePrice": "0.00000322975150297",
"tradeTime": 1773628806000,
"tradeType": "2",
"walletAddress": "DHfshpzoC9Q7rz32j5juq2do3Bo8bA1KLmkNiRYaA8tf"
}
]
}
```
- [Error Codes](https://web3.okx.com/onchainos/dev-docs/market/websocket-error-code.md)
# Error Codes
| Code | Message |
|-------|------------------------------------------------------------------------------------------------------------|
| 60004 | Invalid timestamp |
| 60005 | Invalid apiKey |
| 60006 | Timestamp request expired |
| 60007 | Invalid sign |
| 60008 | The current WebSocket endpoint does not support subscribing to {0} channels. Please check the WebSocket URL |
| 60009 | Login failure |
| 60011 | Please log in |
| 60012 | Invalid request |
| 60013 | Invalid args |
| 60014 | Requests too frequent |
| 60018 | Wrong URL or {0} doesn't exist. Please use the correct URL, channel and parameters referring to API document. |
| 60019 | Invalid op: \{op\} |
| 60020 | APIKey subscription amount exceeds the limit {0}. |
| 60021 | This operation does not support multiple accounts login. |
| 60022 | Bulk login partially succeeded |
| 60023 | Bulk login requests too frequent |
| 60024 | Wrong passphrase |
| 60025 | token subscription amount exceeds the limit {0} |
| 60026 | Batch login by APIKey and token simultaneously is not supported. |
| 60027 | Parameter {0} can not be empty. |
| 60028 | The current operation is not supported by this URL. Please use the correct WebSocket URL for the operation. |
| 60029 | Only users who are in the whitelist are allowed to subscribe to this channel. |
| 60030 | The WebSocket endpoint does not allow multiple or repeated logins. |
| 60031 | API key doesn't exist. |
| 63999 | Login failed due to internal error. Please try again later. |
- [API Reference](https://web3.okx.com/onchainos/dev-docs/market/index-price-reference.md)
# API Reference
- [Get Supported Chains](https://web3.okx.com/onchainos/dev-docs/market/index-price-chains.md)
{/* api-page */}
# Get Supported Chains
Retrieve information on chains supported by Index price API
### Request URL
GET `https://web3.okx.com/api/v6/dex/balance/supported/chain`
## Request Parameters
None
## Response Parameters
| Parameter | Type | Description |
|-----------|--------|-------------------|
| name | String | Chain name |
| logoUrl | String | Chain logo URL |
| shortName | String | Chain short name |
| chainIndex| String | Chain unique identifier |
## Request Example
``` shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/balance/supported/chain' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"name": "Ethereum",
"logoUrl": "http://www.eth.org/eth.png",
"shortName": "ETH",
"chainIndex": "1"
}
],
"msg": ""
}
```
- [Get Token Index Price](https://web3.okx.com/onchainos/dev-docs/market/index-price.md)
{/* api-page */}
# Get Token Index Price
The index price refers to a currency price calculated from the prices of multiple third-party data sources.
Batch query for index token prices, maximum 100 token prices can be queried per request.
Request Parameters should be passed in the form of an array.
### Request URL
POST `https://web3.okx.com/api/v6/dex/index/current-price`
## Request Parameters
| Parameter | Type | Required | Description |
|--------------|--------|----------|-------------|
| chainIndex | String | Yes | Unique identifier of the blockchain |
| tokenContractAddress | String | Yes | Token address. `1`: Pass an empty string `""` to query the native token of the corresponding chain. `2`: Pass the specific token contract address to query the corresponding token. |
## Response Parameters
| Parameter | Type | Description |
|--------------|--------|-------------------------------|
| price | String | Token price |
| time | String | Timestamp of the price, Unix timestamp in milliseconds |
| chainIndex | String | Unique identifier of the blockchain |
| tokenContractAddress | String | Token address.|
## Request Example
``` shell
curl --location --request POST 'https://web3.okx.com/api/v6/dex/index/current-price' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z' \
--data-raw '[
{
"chainIndex": "1",
"tokenContractAddress":"0xc18360217d8f7ab5e7c516566761ea12ce7f9d72"
},
]'
```
## Response Example
``` json
{
"code": 0,
"msg": "success",
"data": [
{
"chainIndex": "1",
"tokenContractAddress": "0xc18360217d8f7ab5e7c516566761ea12ce7f9d72"
"time": "1716892020000",
"price": "26.458143090226812",
}
]
}
```
- [Get Historical Index Price](https://web3.okx.com/onchainos/dev-docs/market/historical-index-price.md)
{/* api-page */}
# Get Historical Index Price
Query historical prices for a specific token.
### Request URL
GET `https://web3.okx.com/api/v6/dex/index/historical-price`
## Request Parameters
| Parameter | Type | Required | Description |
|--------------|--------|----------|----------------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier of the blockchain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| tokenContractAddress | String | No | Token address. `1`: Pass an empty string `""` to query the native token of the corresponding chain. `2`: Pass the specific token contract address to query the corresponding token. |
| limit | String | No | Number of entries per query, default is 50, maximum is 200 |
| cursor | String | No | Cursor position, defaults to the first entry |
| begin | String | No | Start time to query historical prices after. Unix timestamp in milliseconds |
| end | String | No | End time to query historical prices before. If neither begin nor end is provided, query historical prices before the current time. Unix timestamp in milliseconds |
| period | String | No | Time interval unit: `1m`: 1 minute `5m`: 5 minutes `30m`: 30 minutes `1h`: 1 hour `1d`: 1 day (default) |
## Response Parameters
| Parameter | Type | Description |
|-----------|--------|----------------------|
| prices | Array | List of historical prices |
| >time | String | Timestamp of the minute (whole minute) |
| >price | String | Cryptocurrency price (precision 18 decimal places) |
## Request Example
``` shell
curl --location --request GET 'https://web3.okx.com/api/v5/wallet/token/historical-price?chainIndex=1&limit=5&begin=1700040600000&period=5m' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"msg": "success",
"data": [
{
"cursor": "31",
"prices": [
{
"time": "1700040600000",
"price": "1994.430000000000000000"
},
{
"time": "1700040300000",
"price": "1994.190000000000000000"
},
{
"time": "1700040000000",
"price": "1992.090000000000000000"
},
{
"time": "1700039700000",
"price": "1992.190000000000000000"
},
{
"time": "1700039400000",
"price": "1990.190000000000000000"
}
]
}
]
}
```
- [Error Codes](https://web3.okx.com/onchainos/dev-docs/market/index-price-error-code.md)
# Error Codes
| Code | HTTP status | Message |
|-------|-------------|-----------------------------------------------------------------------------------------|
| 81001 | 200 | Incorrect parameter |
- [API Reference](https://web3.okx.com/onchainos/dev-docs/market/market-token-reference.md)
# API Reference
- [Token Search](https://web3.okx.com/onchainos/dev-docs/market/market-token-search.md)
{/* api-page */}
# Token Search
Search tokens by token name, symbol or token contract address.
For token name or symbol search, return maximum 100 results by relevance.For token contract address, return the exact match result.
### Request URL
GET `https://web3.okx.com/api/v6/dex/market/token/search`
## Request Parameters
| Parameter | Type | Required | Description |
|------------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| chains | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| search | String | Yes | Search for token keywords, token address or token symbol |
| cursor | String | No | Pagination cursor, pass the cursor value returned from the previous request |
| limit | String | No | Number of records per page, max 100 |
## Response Parameters
| Parameter | Type | Description |
|---------------------- |--------- |--------------------------------------------------------------------------------------------- |
| chainIndex | String | Unique identifier of the chain. (Such as 1: Ethereum, for more see the list of [chainIndex](../home/supported-chain) ) |
| cursor | String | Pagination cursor |
| tokenName | String | Token name |
| tokenSymbol | String | Token identification |
| tokenLogoUrl | String | Token icon url |
| tokenContractAddress | String | Token contract address |
| decimal | String | Token precision |
| explorerUrl | String | Token Browser link |
| change | String | 24H price change ratio |
| holders | String | Number of holders |
| liquidity | String | Token liquidity (24h) |
| marketCap | String | Token market cap |
| price | String | Price |
| tagList | Object | Label |
| >communityRecognized | Boolean | True means it's listed in the Top 10 CEX or is community verifed |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/token/search?chains=1,10&search=weth' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"chainIndex": "501",
"change": "6.59",
"decimal": "8",
"explorerUrl": "https://web3.okx.com/explorer/solana/token/XsDoVfqeBukxuZHWhdvWHBhgEHjGNst4MLodqsJHzoB",
"holders": "22447",
"liquidity": "3492546.00711048141155477039737838",
"marketCap": "51095303.595316825571569398",
"price": "381.228119807066217995",
"tagList": {
"communityRecognized": true
},
"tokenContractAddress": "XsDoVfqeBukxuZHWhdvWHBhgEHjGNst4MLodqsJHzoB",
"tokenLogoUrl": "https://static.oklink.com/cdn/web3/currency/token/large/501-XsDoVfqeBukxuZHWhdvWHBhgEHjGNst4MLodqsJHzoB-900/type=default_90_0?v=1771926275192",
"tokenName": "Tesla xStock",
"tokenSymbol": "TSLAx",
"cursor": "0"
},
{
"chainIndex": "501",
"change": "6.19",
"decimal": "9",
"explorerUrl": "https://web3.okx.com/explorer/solana/token/KeGv7bsfR4MheC1CkmnAVceoApjrkvBhHYjWb67ondo",
"holders": "133",
"liquidity": "",
"marketCap": "7747.288131154003818",
"price": "380.833333",
"tagList": {
"communityRecognized": true
},
"tokenContractAddress": "KeGv7bsfR4MheC1CkmnAVceoApjrkvBhHYjWb67ondo",
"tokenLogoUrl": "https://static.oklink.com/cdn/web3/currency/token/large/501-KeGv7bsfR4MheC1CkmnAVceoApjrkvBhHYjWb67ondo-900/type=default_90_0?v=1774296033373",
"tokenName": "Tesla (Ondo Tokenized)",
"tokenSymbol": "TSLAon",
"cursor": "1"
}
],
"msg": ""
}
```
- [Token Basic Information](https://web3.okx.com/onchainos/dev-docs/market/market-token-basic-info.md)
{/* api-page */}
# Token Basic Information
Retrieve the basic token information of the specified token contract address
### Request URL
POST `https://web3.okx.com/api/v6/dex/market/token/basic-info`
## Request Parameters
| Parameter | Type | Required | Description |
|------------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| tokenContractAddress | String | Yes | Token contract address (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
## Response Parameters
| Parameter | Type | Description |
|---------------------- |--------- |-------------------------------------------------------------------------------------------- |
| chainIndex | String | Unique identifier of the chain. (Such as 1: Ethereum, for more see the list of [chainIndex](../home/supported-chain) ) |
| tokenName | String | Token name |
| tokenSymbol | String | Token identification |
| tokenLogoUrl | String | Token icon url |
| decimal | String | Token precision |
| tagList | Object | Label |
| >communityRecognized | Boolean | True means it's listed in the Top 10 CEX or is community verifed |
## Request Example
```shell
curl --location --request POST 'https://web3.okx.com/api/v6/dex/market/token/basic-info' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z' \
--data-raw '[
{
"chainIndex": "501",
"tokenContractAddress": "5mbK36SZ7J19An8jFochhQS4of8g6BwUjbeCSxBSoWdp"
},
{
"chainIndex": "501",
"tokenContractAddress": "eL5fUxj2J4CiQsmW85k5FG9DvuQjjUoBHoQBi2Kpump"
}
]'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"chainIndex": "501",
"decimal": "6",
"tagList": {
"communityRecognized": true
},
"tokenContractAddress": "5mbK36SZ7J19An8jFochhQS4of8g6BwUjbeCSxBSoWdp",
"tokenLogoUrl": "https://static.oklink.com/cdn/web3/currency/token/small/501-5mbK36SZ7J19An8jFochhQS4of8g6BwUjbeCSxBSoWdp-106?v=1749146943562",
"tokenName": "michi",
"tokenSymbol": "$michi"
},
{
"chainIndex": "501",
"decimal": "6",
"tagList": {
"communityRecognized": true
},
"tokenContractAddress": "eL5fUxj2J4CiQsmW85k5FG9DvuQjjUoBHoQBi2Kpump",
"tokenLogoUrl": "https://static.oklink.com/cdn/web3/currency/token/large/501-eL5fUxj2J4CiQsmW85k5FG9DvuQjjUoBHoQBi2Kpump-108/type=default_90_0?v=1755815305433",
"tokenName": "Unicorn Fart Dust",
"tokenSymbol": "UFD"
}
],
"msg": ""
}
```
- [Token Liquidity Pool Information](https://web3.okx.com/onchainos/dev-docs/market/market-token-top-liquidity.md)
{/* api-page */}
# Token Liquidity Pool Information
Support viewing pool information of the top 5 liquidity
### Request Path
GET `https://web3.okx.com/api/v6/dex/market/token/top-liquidity`
## Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| chainIndex | String | Yes | Unique identifier of the chain. For example: `1`: Ethereum. |
| tokenContractAddress | String | Yes | Token contract address (e.g. 0x382bb369d343125bfb2117af9c149795c6c65c50) |
## Response Parameters
| Field | Type | Description |
|---|---|---|
| pool | String | Funding pool, e.g. Punch/SOL |
| protocolName | String | Agreement name |
| liquidityUsd | String | Liquidity value |
| liquidityAmount | Array | Quantity of liquidity |
| >tokenAmount | String | Token amount in liquidity pool |
| >tokenSymbol | String | Token name in liquidity pool |
| liquidityProviderFeePercent | String | Liquidity provider fee percentage |
| poolAddress | String | Pool address |
| poolCreator | String | Pool creator |
## Request Example
```shell
curl --location 'https://web3pre.okex.org/api/v6/dex/market/token/liquidity?chainIndex=8453&tokenContractAddress=0x1f16e03c1a5908818f47f6ee7bb16690b40d0671' \
--header 'Cookie: locale=en-US'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"liquidityAmount": [
{
"tokenAmount": "4347613.0508917095",
"tokenSymbol": "RECALL"
},
{
"tokenAmount": "142351.302373",
"tokenSymbol": "USDC"
}
],
"liquidityProviderFeePercent": "0.06%",
"liquidityUsd": "344879.7538226645330963352547001735",
"pool": "RECALL/USDC",
"poolAddress": "0x5e3791f68ebceac82788f3becab154c15141a2f4",
"poolCreator": "0xee4bbf067ce361b75e8f31fd2fe726caba757a67",
"protocolName": "Aerodrome"
},
{
"liquidityAmount": [
{
"tokenAmount": "1627239.5672854523",
"tokenSymbol": "RECALL"
},
{
"tokenAmount": "25.183845099309004",
"tokenSymbol": "WETH"
}
],
"liquidityProviderFeePercent": "0.30%",
"liquidityUsd": "126390.2349541208658853815306505521",
"pool": "RECALL/WETH",
"poolAddress": "0x6ee714d6d8df7662bca805f58cc1d5a8886d78eb",
"poolCreator": "0xee4bbf067ce361b75e8f31fd2fe726caba757a67",
"protocolName": "Aerodrome"
}
],
"msg": ""
}
```
- [Token Trading Information](https://web3.okx.com/onchainos/dev-docs/market/market-token-price-info.md)
{/* api-page */}
# Token Trading Information
Return token trading information including price, volume, trading info, supply, holders, liquidity etc at specific timestamp, support max 100 multiple tokens query.
### Request URL
POST `https://web3.okx.com/api/v6/dex/market/price-info`
## Request Parameters
| Parameter | Type | Required | Description |
|------------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| tokenContractAddress | String | Yes | Token contract address (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
## Response Parameters
| Parameter | Type | Description |
|---------------------- |-------- |-------------------------------------------------------------------------------------------- |
| chainIndex | String | Unique identifier of the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain) |
| tokenContractAddress | String | Token contract address (e.g. 0x382bb369d343125bfb2117af9c149795c6c65c50) |
| time | String | Price timestamp, using Unix Millisecond timestamp format |
| price | String | Latest token price |
| marketCap | String | Token market cap |
| priceChange5M | String | Price changes within 5 minutes, in percentage increase or decrease |
| priceChange1H | String | Price changes within one hour, expressed as a percentage increase or decrease |
| priceChange4H | String | Price changes within 4 hours, expressed as a percentage increase or decrease |
| priceChange24H | String | Price changes within 24 hours, expressed as a percentage increase or decrease |
| volume5M | String | Trading volume within 5 minutes |
| volume1H | String | Trading volume within 1 hour |
| volume4H | String | Trading volume within 4 hours |
| volume24H | String | Trading volume within 24 hours |
| txs5M | String | Token transactions within 5 minutes |
| txs1H | String | Token transactions within 1 hour |
| txs4H | String | Token transactions within 4 hours |
| txs24H | String | Number of token transactions within 24 hours |
| maxPrice | String | Token 24h highest price |
| tradeNum | String | 24H token trading quantity |
| minPrice | String | Token 24h lowest price |
| circSupply | String | Token circulation supply |
| liquidity | String | Liquidity in the token pool |
| holders | String | Number of token holding addresses |
## Request Example
```shell
curl --location --request POST 'https://web3.okx.com/api/v6/dex/market/price-info' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z' \
--data-raw '[
{
"chainIndex": "501",
"tokenContractAddress": "5mbK36SZ7J19An8jFochhQS4of8g6BwUjbeCSxBSoWdp"
},
{
"chainIndex": "501",
"tokenContractAddress": "eL5fUxj2J4CiQsmW85k5FG9DvuQjjUoBHoQBi2Kpump"
}
]'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"chainIndex": "501",
"circSupply": "555761678.7348550000",
"holders": "46998",
"liquidity": "2909733.283759244562509712218832656",
"marketCap": "12252728.212049527112081409",
"maxPrice": "0.023000175796511334",
"minPrice": "0.021290716539724027",
"price": "0.022046730965585534",
"priceChange1H": "-1.24",
"priceChange24H": "-1.8",
"priceChange4H": "-2.64",
"priceChange5M": "0.22",
"time": "1756816961600",
"tokenContractAddress": "5mbK36SZ7J19An8jFochhQS4of8g6BwUjbeCSxBSoWdp",
"tradeNum": "9577269.994247",
"txs1H": "27664",
"txs24H": "127879",
"txs4H": "108489",
"txs5M": "2265",
"volume1H": "7376.6139697593500223",
"volume24H": "213047.96235814982022456",
"volume4H": "33943.03136454470008366",
"volume5M": "544.69701090069000181"
},
{
"chainIndex": "501",
"circSupply": "999973538.9201610000",
"holders": "37302",
"liquidity": "2946946.965800864848335614721052390",
"marketCap": "25792746.281014184438384254",
"maxPrice": "0.026539875867707924",
"minPrice": "0.02427728431412691",
"price": "0.025793428802993062",
"priceChange1H": "-1.13",
"priceChange24H": "3.4",
"priceChange4H": "-2.07",
"priceChange5M": "0.52",
"time": "1756816961600",
"tokenContractAddress": "eL5fUxj2J4CiQsmW85k5FG9DvuQjjUoBHoQBi2Kpump",
"tradeNum": "10046562.105494",
"txs1H": "166",
"txs24H": "2888",
"txs4H": "322",
"txs5M": "29",
"volume1H": "10343.0564689259900202",
"volume24H": "253496.67765782958052983",
"volume4H": "21530.5160926891601342",
"volume5M": "3278.85450250897"
}
],
"msg": ""
}
```
- [Token Advanced Information](https://web3.okx.com/onchainos/dev-docs/market/market-token-advanced-info.md)
{/* api-page */}
# Token Advanced Information
Obtain advanced information about the specified token contract address
### Request Path
GET `https://web3.okx.com/api/v6/dex/market/token/advanced-info`
## Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| chainIndex | String | Yes | Unique identifier of the chain. For example: `1`: Ethereum. |
| tokenContractAddress | String | Yes | Token contract address (e.g. 0x382bb369d343125bfb2117af9c149795c6c65c50) |
## Response Parameters
| Field | Type | Description |
|---|---|---|
| totalFee | String | Total fees include priority fees and tip fees |
| lpBurnedPercent | String | Liquidity pool burned percentage |
| isInternal | Boolean | Whether it is an internal token |
| protocolId | String | Protocol ID |
| progress | String | Internal token launch progress |
| tokenTags | Array | Token tags: can be empty, or return one or more of the following |
| >honeypot | String | Honeypot |
| >dexBoost | String | Boost activity |
| >lowLiquidity | String | Low liquidity |
| >communityRecognized | String | Community recognized |
| >devHoldingStatusSell | String | Developers sold |
| >devHoldingStatusSellAll | String | Developers sold all |
| >devHoldingStatusBuy | String | Developers buy more |
| >initialHighLiquidity | String | High initial liquidity |
| >smartMoneyBuy | String | Smart money buy |
| >devAddLiquidity | String | Developers add liquidity |
| >devBurnToken | String | Developers burn tokens |
| >volumeChangeRateHoldersPlunge | String | Trading volume plummeted |
| >holdersChangeRateHoldersSurge | String | The number of holding addresses has increased sharply |
| >dexScreenerTokenCommunityTakeOver | String | Community takeover |
| >dexScreenerPaid | String | Paid on DEX Screener |
| createTime | String | Token creation time |
| creatorAddress | String | Creator address |
| devRugPullTokenCount | String | The number of Rug Pull tokens created by the developer |
| devCreateTokenCount | String | Total number of tokens created by developers |
| devLaunchedTokenCount | String | Developer created tokens in the number of migrated |
| riskControlLevel | String | Risk control level: `0`=undefined, `1`=low, `2`=medium, `3`=medium high, `4`=high, `5`=high (manual configuration) |
| top10HoldPercent | String | Position percent of Top 10 Holders |
| devHoldingPercent | String | Developer position percent |
| bundleHoldingPercent | String | Bundle position percent |
| suspiciousHoldingPercent | String | Suspicious address position percent |
| sniperHoldingPercent | String | Sniper position percent |
| snipersClearAddressCount | String | Number of Sniper Clearance Addresses |
| snipersTotal | String | Total number of snipers |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/token/advanced-info?chainIndex=501&tokenContractAddress=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": {
"bundleHoldingPercent": "",
"chainIndex": "501",
"createTime": "1729231507000",
"creatorAddress": "HyYNVYmnFmi87NsQqWzLJhUTPBKQUfgfhdbBa554nMFF",
"devCreateTokenCount": "480",
"devHoldingPercent": "",
"devLaunchedTokenCount": "",
"devRugPullTokenCount": "26",
"isInternal": false,
"lpBurnedPercent": "74.3609957824270349103361582217",
"progress": "",
"protocolId": "120596",
"riskControlLevel": "1",
"sniperHoldingPercent": "",
"snipersClearAddressCount": "",
"snipersTotal": "",
"suspiciousHoldingPercent": "",
"tokenContractAddress": "9BB6NFEcjBCtnNLFko2FqVQBq8HHM13kCyYcdQbgpump",
"tokenTags": [
"dexBoost",
"communityRecognized",
"devHoldingStatusSellAll",
"smartMoneyBuy"
],
"top10HoldPercent": "13.6832",
"totalFee": "2180.105145301"
},
"msg": ""
}
```
- [Get Tokens Trades Activity](https://web3.okx.com/onchainos/dev-docs/market/market-trades.md)
{/* api-page */}
# Get Tokens Trades Activity
Retrieve the recent transactions of a token.
### Request URL
GET `https://web3.okx.com/api/v6/dex/market/trades`
## Request Parameters
| Parameter | Type | Required | Description |
|------------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| tokenContractAddress | String | Yes | Token contract address,for EVM please pass all-lowercase addresses (e.g., 0x382bb369d343125bfb2117af9c149795c6c65c50) |
| after | String | No | Pagination of data to return records earlier than the requested id. |
| limit | String | No | Number of results per request. The maximum is 500 and default is 100. |
| tagFilter | String | No | Default: do not enter Enter 1: Return the holding address labeled KOL. Enter 2: Return the token address labeled Developer Enter 3: Return the holding address labeled Smart Money Enter 4: Return the holding address labeled as Whale Enter 5: Return the holding address labeled New Wallet Enter 6: Return the holding address labeled Suspicious Enter 7: Return the holding address labeled Sniper Enter 8: Return the holding address labeled as Suspected phishing. Enter 9: Return the holding address labeled Bundle |
| walletAddressFilter | String | No | Query history for multiple addresses. Enter multiple addresses, separated by commas, up to 10 |
## Response Parameters
| Parameter | Type | Description |
|-----------------|--------|-----------------------------------------------------------------------------------------------------------------------|
| id | String | Unique trade id |
| chainIndex | String | Unique identifier for the chain. (e.g., 1 for Ethereum. See [ChainIndex](../home/supported-chain)) |
| tokenContractAddress | String | Token contract address |
| txHashUrl | String | On-chain txhash of the transactions |
| userAddress | String | Authorizer of the transaction |
| dexName | String | Name of the dex where the trade occured |
| poolLogoUrl | String | Pool logo url |
| type | String | trade Type. `buy` `sell` |
| changedTokenInfo | String | exchanged info |
| > amount | String | token exchanged amount in this trade |
| > tokenSymbol | String | Token symbol |
| > tokenContractAddress | String | Token contract address |
| price | String | Latest token price |
| volume | String | USD value of this trade |
| time | String | Timestamp of the price, Unix timestamp format in milliseconds |
| isFiltered | String | If the trade is filtered for price and k-line calculation. `0`: not filtered `1`: filtered" |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/trades?chainIndex=501&tokenContractAddress=HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code":"0",
"data":[
{
"id":"1739439633000!@#120!@#14731892839",
"chainIndex": "501",
"tokenContractAddress": "HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC",
"txHashUrl": "https://solscan.io/tx/zgDzoiVG4XuDgQcoEg9vhpRyfyk5thNUQuTeTCeF289Qec5iraeCrUzPLyiE2UCviox2ebbTcsagGvzYF7M5uqs",
"userAddress": "2kCm1RHGJjeCKL4SA3ZJCLyXqUD7nEJ7GMtVaP7c6jQ8",
"dexName": "Orca Whirlpools",
"poolLogoUrl": "https://static.okx.com/cdn/wallet/logo/dex_orcaswap.png",
"type": "sell",
"changedTokenInfo": [
{
"amount":"100.396595878",
"tokenSymbol":"ai16z",
"tokenContractAddress": "HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC"
},
{
"amount":"2.482831",
"tokenSymbol":"SOL",
"tokenContractAddress": "So11111111111111111111111111111111111111112"
}
]
"price": "26.458143090226812",
"volume": "519.788163",
"time": "1739439633000",
"isFiltered": "0"
}
],
"msg":"",
}
```
- [Hot Tokens](https://web3.okx.com/onchainos/dev-docs/market/market-token-hot-token.md)
{/* api-page */}
# Hot Tokens
Returns a list of tokens ranked by token score and social media mentions based on different time ranges. It can be sorted in descending order according to the specified data, and can also be filtered based on the specified data. Up to 100 results are returned.
### Request Path
GET `https://web3.okx.com/api/v6/dex/market/token/hot-token`
## Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| rankingType | String | Yes | Default use trending, does not support multiple selection 4: Trending, sorting by token score 5: Xmentioned, sorting by number of mentions on Twitter Note: Only 4 and 5, no 1, 2, 3. |
| chainIndex | String | No | Default is all networks, it does not support multiple selection chain. Example: 1, Filter out the tokens of the Ethereum chain |
| rankBy | String | No | Trending lists are sorted backwards by "12" by default; The Xmentioned list is sorted by default in reverse order according to "11". Sort in reverse order according to more parameters, single choice Enter 1: token price \| Enter 2: price changes percent \| Enter 3: transactions \| Enter 4: unique traders \| Enter 5: volume(in USD) \| Enter 6: marketcap \| Enter 7: liquidity value \| Enter 8: token creation time \| Enter 9: OKX in-app search frequency \| Enter 10: number of holder \| Enter 11: mentions on social media \| Enter 12: social media scores \| Enter 14: net inflow \| Enter 15: token score |
| rankingTimeFrame | String | No | Default is 1h, single choice 1: 5 minutes \| 2: 1 hour \| 3: 4 hours \| 4: 24 hours |
| riskFilter | Boolean | No | Hide risk tokens, default is true. Example: true, false |
| protocolId | String | No | Filter tokens by protocol ID. No protocol is selected by default, multiple options can be selected, separated by commas (For example, 120596 can filter out tokens Pump.fun protocols) |
| priceChangePercentMin | String | No | Minimum price change percent |
| priceChangePercentMax | String | No | Maximum price change percent |
| tradeAmountMin | String | No | Minimum volume |
| tradeAmountMax | String | No | Maximum volume |
| volumeMin | String | No | Minimum turnover |
| volumeMax | String | No | Maximum turnover |
| txsMin | String | No | Minimum number of transactions |
| txsMax | String | No | Maximum number of transactions |
| uniqueTraderMin | String | No | Minimum number of unique trader |
| uniqueTraderMax | String | No | Maximum number of unique trader |
| marketCapMin | String | No | Minimum Marketcap |
| marketCapMax | String | No | Maximum Marketcap |
| liquidityMin | String | No | Minimum liquidity |
| liquidityMax | String | No | Maximum liquidity |
| stableTokenFilter | Boolean | No | Whether to filter stablecoins, the default is true. Example: true, false |
| holdersMin | String | No | Minimum number of holders |
| holdersMax | String | No | Maximum number of holders |
| top10HoldPercentMin | String | No | Top 10 minimum holding percent |
| top10HoldPercentMax | String | No | Top 10 maximum holding percent |
| devHoldPercentMin | String | No | Minimum developer holding percent |
| devHoldPercentMax | String | No | Maximum developer holding percent |
| suspiciousHoldPercentMin | String | No | Minimum suspicious holding percent |
| suspiciousHoldPercentMax | String | No | Maximum suspicious holding percent |
| bundleHoldPercentMin | String | No | Minimum binding holding percent |
| bundleHoldPercentMax | String | No | Maximum binding holding percent |
| mentionedCountMin | String | No | Minimum number of mentions |
| mentionedCountMax | String | No | Maximum number of mentions |
| socialScoreMin | String | No | Minimum social score |
| socialScoreMax | String | No | Maximum social score |
| isLpBurnt | Boolean | No | Whether LP is burned, the default is true. Example: true, false |
| isMint | Boolean | No | Whether it can be mint, the default is true. Example: true, false |
| isFreeze | Boolean | No | Whether to freeze, default is true. Example: true, false |
| inflowUsdMin | String | No | Minimum inflow |
| inflowUsdMax | String | No | Maximum inflow |
| fdvMin | String | No | FDV minimum |
| fdvMax | String | No | Maximum FDV |
| cursor | String | No | Pagination cursor, pass the cursor value returned from the previous request |
| limit | String | No | Number of records per page, max 100 |
## Response Parameters
| Field | Type | Description |
|---|---|---|
| chainIndex | String | Unique identifier of the chain (e.g. 1: Ethereum) |
| cursor | String | Pagination cursor |
| tokenSymbol | String | Token symbol |
| tokenLogoUrl | String | Token Icon URL |
| tokenContractAddress | String | Token contract address |
| marketCap | String | Marketcap (token price × circulating supply) |
| volume | String | Token trading volume (in USD) |
| firstTradeTime | String | Token first transaction time |
| change | String | Token price change ratio |
| liquidity | String | Liquidity value (in USD) |
| price | String | Token price |
| holders | String | Number of holder |
| uniqueTraders | String | The number of unique trader |
| txsBuy | String | The number of buy transactions within the specified time |
| txsSell | String | Number of sell transactions within a specified time |
| txs | String | Total number of transactions within a specified time |
| inflowUsd | String | Net inflow |
| riskLevelControl | String | Risk control level: `0`=undefined, `1`=low, `2`=medium, `3`=medium high, `4`=high, `5`=high (manual configuration) |
| devHoldPercent | String | Developer position percent |
| top10HoldPercent | String | Position ratio of top 10 holding addresses |
| insiderHoldPercent | String | Internal trader position percent |
| bundleHoldPercent | String | Bundled trader position percent |
| vibeScore | String | Social media score |
| mentionsCount | String | Number of mentions |
## Request Example
```shell
curl --location 'https://web3pre.okex.org/api/v6/dex/market/token/hot-token?rankingType=4&category=' \
--header 'Cookie: locale=en-US'
```
## Response Example
```json
-- param limit=10&cursor=20
-- This is not deep pagination. It is in-memory pagination, with a maximum of only 100 records. The maximum cursor value is 100.
{
"code": "0",
"data": [
{
"bundleHoldPercent": "0.055471",
"chainIndex": "501",
"change": "124.46",
"devHoldPercent": "",
"firstTradeTime": "1774376729000",
"holders": "3518",
"inflowUsd": "48877.03736178689",
"insiderHoldPercent": "",
"liquidity": "656584.515776979964291925",
"marketCap": "7120200.171337115253774998",
"mentionsCount": "",
"price": "0.007120235922518026",
"riskLevelControl": "1",
"tokenContractAddress": "FtSRgyCEhKTc1PPgEAXvuHN3NyiP6LS9uyB28KCN3CAP",
"tokenLogoUrl": "https://static.oklink.com/cdn/web3/currency/token/large/501-FtSRgyCEhKTc1PPgEAXvuHN3NyiP6LS9uyB28KCN3CAP-109/type=default_90_0?v=1774381887925",
"tokenSymbol": "CAPTCHA",
"top10HoldPercent": "20.6599",
"txs": "13616",
"txsBuy": "6699",
"txsSell": "6917",
"uniqueTraders": "3508",
"vibeScore": "",
"volume": "2733012.43208665053",
"cursor": 21
},
{
"bundleHoldPercent": "0.055471",
"chainIndex": "501",
"change": "124.46",
"devHoldPercent": "",
"firstTradeTime": "1774376729000",
"holders": "3518",
"inflowUsd": "48877.03736178689",
"insiderHoldPercent": "",
"liquidity": "656584.515776979964291925",
"marketCap": "7120200.171337115253774998",
"mentionsCount": "",
"price": "0.007120235922518026",
"riskLevelControl": "1",
"tokenContractAddress": "FtSRgyCEhKTc1PPgEAXvuHN3NyiP6LS9uyB28KCN3CAP",
"tokenLogoUrl": "https://static.oklink.com/cdn/web3/currency/token/large/501-FtSRgyCEhKTc1PPgEAXvuHN3NyiP6LS9uyB28KCN3CAP-109/type=default_90_0?v=1774381887925",
"tokenSymbol": "CAPTCHA",
"top10HoldPercent": "20.6599",
"txs": "13616",
"txsBuy": "6699",
"txsSell": "6917",
"uniqueTraders": "3508",
"vibeScore": "",
"volume": "2733012.43208665053",
"cursor": 22
}
],
"msg": ""
}
```
- [Token Holder Information](https://web3.okx.com/onchainos/dev-docs/market/market-token-holder.md)
{/* api-page */}
# Token Holder Information
Support viewing the addresses and corresponding position information of the top 100 holders
### Request Path
GET `https://web3.okx.com/api/v6/dex/market/token/holder`
## Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| chainIndex | String | Yes | Unique identifier of the chain. For example: `1`: Ethereum. |
| tokenContractAddress | String | Yes | Token contract address (e.g. 0x382bb369d343125bfb2117af9c149795c6c65c50) |
| tagFilter | String | No | Default: No input, return the address data of the first 100 holding coin addresses, arranged in reverse order according to the total income. Enter 1: Return the address labeled KOL Enter 2: Return the token address labeled Developer Enter 3: Return the address labeled Smart Money Enter 4: Return the holding address labeled as Whale Enter 5: Return the address labeled New Wallet Enter 6: Return the holding address labeled Suspicious Enter 7: Return the holding address labeled Sniper Enter 8: Return the address labeled as suspected phishing Enter 9: Return the address labeled as Bundle |
| cursor | String | No | Pagination cursor, pass the cursor value returned from the previous request |
| limit | String | No | Number of records per page, max 100 |
## Response Parameters
| Field | Type | Description |
|---|---|---|
| holdPercent | String | Percentage of holdings |
| cursor | String | Pagination cursor |
| nativeTokenBalance | String | Mainnet currency balance |
| boughtAmount | String | Total Buy Quantity |
| avgBuyPrice | String | Average buying price |
| totalSellAmount | String | Total sold quantity |
| avgSellPrice | String | Average selling price |
| totalPnlUsd | String | Total profit and loss |
| realizedPnlUsd | String | Realized profit and loss |
| unrealizedPnlUsd | String | Unrealized profit and loss |
| fundingSource | String | Sources of funding |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/token/holder?chainIndex=501&tokenContractAddress=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&tagFilter=1' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
-- Not deep pagination; only supports pagination within up to 100 records returned in memory.
{
"code": "0",
"data": [
{
"avgBuyPrice": "0",
"avgSellPrice": "0",
"boughtAmount": "0",
"fundingSource": "9k8jWWqfmTTXrc8gmZqYVxSFJhygURvyVRztivJXFTYP",
"holdAmount": "767501192.534284",
"holdPercent": "76.750184380179418200",
"holderWalletAddress": "2RH6rUTPBJ9rUDPpuV9b8z1YL56k1tYU6Uk5ZoaEFFSK",
"nativeTokenBalance": "6.091849989",
"realizedPnlUsd": "0",
"totalPnlUsd": "0.000000000000000000",
"totalSellAmount": "0",
"unrealizedPnlUsd": "0.000000000000000000",
"cursor": "0"
},
{
"avgBuyPrice": "0",
"avgSellPrice": "0",
"boughtAmount": "0",
"fundingSource": "5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9",
"holdAmount": "36715842.016",
"holdPercent": "3.671587317143695100",
"holderWalletAddress": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
"nativeTokenBalance": "13908916.377375046",
"realizedPnlUsd": "0",
"totalPnlUsd": "0.000000000000000000",
"totalSellAmount": "0",
"unrealizedPnlUsd": "0.000000000000000000",
"cursor": "1"
}
],
"msg": ""
}
```
- [Get Cluster Supported Chain](https://web3.okx.com/onchainos/dev-docs/market/market-token-cluster-supported-chain.md)
{/* api-page */}
# Get Cluster Supported Chain
Get the supported chains
### Request Path
GET `https://web3.okx.com/api/v6/dex/market/token/cluster/supported/chain`
## Request Parameters
None
## Response Parameters
| Field | Type | Description |
|---|---|---|
| chainIndex | String | Unique identifier of the chain |
| chainName | String | Chain name |
| chainLogo | String | Chain logo |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/token/cluster/supported/chain' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"chainIndex": "1",
"chainName": "Ethereum",
"chainLogo": "https://static.okx.com/cdn/wallet/logo/ETH-20220328.png"
},
{
"chainIndex": "501",
"chainName": "Solana",
"chainLogo": "https://static.okx.com/cdn/wallet/logo/SOL.png"
}
],
"msg": ""
}
```
- [Get Token Holding Cluster Overview](https://web3.okx.com/onchainos/dev-docs/market/market-token-cluster-overview.md)
{/* api-page */}
# Get Token Holding Cluster Overview
Get the holding cluster overview data for a specified token.
### Request Path
GET `https://web3.okx.com/api/v6/dex/market/token/cluster/overview`
## Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| chainIndex | String | Yes | Unique chain identifier. Pass in the chain ID (e.g., 501 for Solana). Only supports single-chain queries |
| tokenContractAddress | String | Yes | Token contract address |
## Response Parameters
| Field | Type | Description |
|---|---|---|
| ClusterConcentration | String | Cluster concentration level: Low / Medium / High |
| top100HoldingsPercent | String | Top 100 address holding percentage (%) |
| rugPullPercent | String | Rug pull probability percentage (%) |
| holderNewAddressPercent | String | Percentage of new addresses created within the past 3 days among the top 1000 holders (%) |
| holderSameFundSourcePercent | String | Percentage of addresses among the top 1000 holders that have mutual mainstream token transactions with each other |
| holderSameCreationTimePercent | String | Percentage of addresses created at the same time (%) |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/token/cluster/overview?chainIndex=1&tokenContractAddress=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"ClusterConcentration": "HIGH",
"top100HoldingsPercent": "42.35",
"rugPullPercent": "12.50",
"holderNewAddressPercent": "8.20",
"holderSameFundSourcePercent": "15.60",
"holderSameCreationTimePercent": "6.80"
}
],
"msg": ""
}
```
- [Get Token Cluster List](https://web3.okx.com/onchainos/dev-docs/market/market-token-cluster-list.md)
{/* api-page */}
# Get Token Cluster List
Get the list of holding clusters for a specified token. Only includes address clusters within the top 300 holders.
Limit: Returns only the top 100 clusters by holding amount
### Request Path
GET `https://web3.okx.com/api/v6/dex/market/token/cluster/list`
## Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| chainIndex | string | Yes | Unique chain identifier. Pass in the chain ID (e.g., 501 for Solana). Only supports single-chain queries |
| tokenContractAddress | string | Yes | Token contract address |
## Response Parameters
| Field | Type | Description |
|---|---|---|
| clustList | array | Cluster list |
| > holdingAmount | string | Total token holdings of cluster addresses (excluding black holes and liquidity pool addresses) |
| > holdingValueUsd | string | Total USD value of token holdings of cluster addresses (excluding black holes and liquidity pool addresses) |
| > holdingPercent | string | Percentage of token supply held by cluster addresses |
| > trendType | object | Buy/Sell/Neutral/Transfer dominant. The overall direction of cluster traders' holdings over time — increasing (buying), decreasing (selling), flat (neutral), or primarily accumulated through transfers (transfer dominant) |
| > averageHoldingPeriod | string | Weighted average holding period of cluster holders |
| > pnlUsd | string | Total token PnL of cluster holders |
| > pnlPercent | string | Total token PnL percentage of cluster holders |
| > buyVolume | string | Value of tokens bought by cluster holders |
| > averageBuyPriceUsd | string | Average cost price of cluster holders |
| > sellVolume | string | Value of tokens sold by cluster holders |
| > averageSellPriceUsd | string | Weighted average selling price of cluster holders |
| > lastActiveTimestamp | string | Last active time |
| > clusterAddressList | array | List of addresses in the cluster |
| >> address | string | Address |
| >> holdingAmount | string | Total token holdings of cluster addresses (excluding black holes and liquidity pool addresses) |
| >> holdingValueUsd | string | Total USD value of token holdings of cluster addresses (excluding black holes and liquidity pool addresses) |
| >> holdingPercent | string | Percentage of token supply held by cluster addresses |
| >> averageHoldingPeriod | string | Average holding period of the address |
| >> lastActiveTimestamp | string | Last active time |
| >> isContract | boolean | Whether it is a contract address |
| >> isExchange | boolean | Whether it is an exchange address |
| >> isKol | boolean | Whether it is a KOL address |
| >> addressRank | string | Address holding rank |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/token/cluster/list?chainIndex=1&tokenContractAddress=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"clustList": [
{
"holdingAmount": "320000000.00",
"holdingValueUsd": "320145600.00",
"holdingPercent": "6.52",
"trendType": {
"trendType": "buy"
},
"averageHoldingPeriod": "62",
"pnlUsd": "45230.12",
"pnlPercent": "14.15",
"buyVolume": "980000.00",
"averageBuyPriceUsd": "0.9982",
"sellVolume": "210000.00",
"averageSellPriceUsd": "1.0015",
"lastActiveTimestamp": "1697630501000",
"clusterAddressList": [
{
"address": "0x28c6c06298d514db089934071355e5743bf21d60",
"holdingAmount": "180000000.00",
"holdingValueUsd": "180082800.00",
"holdingPercent": "3.67",
"averageHoldingPeriod": "75",
"lastActiveTimestamp": "1697630501000",
"isContract": false,
"isExchange": true,
"isKol": false,
"addressRank": "1"
}
]
}
]
}
],
"msg": ""
}
```
- [Get Token Top Holders Cluster Overview](https://web3.okx.com/onchainos/dev-docs/market/market-token-cluster-top-holders.md)
{/* api-page */}
# Get Token Top Holders Cluster Overview
Get the overview data for the top 10/50/100 holding addresses of a specified token.
### Request Path
GET `https://web3.okx.com/api/v6/dex/market/token/cluster/top-holders`
## Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| chainIndex | string | Yes | Unique chain identifier. Pass in the chain ID (e.g., 501 for Solana). Only supports single-chain queries |
| tokenContractAddress | string | Yes | Token contract address |
| rangeFilter | string | Yes | Data for the top 10/50/100 holding addresses. Values: 1=10, 2=50, 3=100 |
## Response Parameters
| Field | Type | Description |
|---|---|---|
| holdingAmount | string | Total token holdings of the top 10/50/100 addresses (excluding black holes and liquidity pool addresses) |
| holdingPercent | string | Percentage of token supply held by the top 10/50/100 addresses |
| clusterTrendType | object | Buy/Sell/Neutral/Transfer dominant. The overall direction of traders' holdings over time — increasing (buying), decreasing (selling), flat (neutral), or primarily accumulated through transfers (transfer dominant) |
| averageHoldingPeriod | string | Weighted average holding period of the top 10/50/100 holders |
| averagePnlUsd | string | Weighted average PnL of the top 10/50/100 holders |
| averageBuyPriceUsd | string | Weighted average cost price of the top 10/50/100 holders |
| averageBuyPricePercent | string | Percentage difference between the average cost price of the top 10/50/100 holders and the current token price |
| averageSellPriceUsd | string | Weighted average selling price of the top 10/50/100 holders |
| averageSellPricePercent | string | Percentage difference between the average selling price of the top 10/50/100 holders and the current token price |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/token/cluster/top-holders?chainIndex=1&tokenContractAddress=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&rangeFilter=2' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"holdingAmount": "1250000000.00",
"holdingPercent": "25.40",
"clusterTrendType": {
"trendType": "buy"
},
"averageHoldingPeriod": "45",
"averagePnlUsd": "12340.56",
"averageBuyPriceUsd": "0.9985",
"averageBuyPricePercent": "-0.15",
"averageSellPriceUsd": "1.0023",
"averageSellPricePercent": "0.23"
}
],
"msg": ""
}
```
- [Token Profit Address Information](https://web3.okx.com/onchainos/dev-docs/market/market-token-top-trader.md)
{/* api-page */}
# Token Profit Address Information
Support viewing the top 100 profitable addresses and corresponding address information
### Request Path
GET `https://web3.okx.com/api/v6/dex/market/token/top-trader`
## Request Parameters
Return to the top 100 profitable addresses
| Parameter | Type | Required | Description |
|---|---|---|---|
| chainIndex | String | Yes | Unique identifier of the chain. For example: `1`: Ethereum. |
| tokenContractAddress | String | Yes | Token contract address (e.g. 0x382bb369d343125bfb2117af9c149795c6c65c50) |
| tagFilter | String | No | Default: No input, return the address data of the top 100 profitable addresses, arranged in reverse order according to the realized income. Enter 1: Return the holding address labeled KOL. Enter 2: Return the token address labeled Developer Enter 3: Return the holding address labeled Smart Money Enter 4: Return the holding address labeled as Whale Enter 5: Return the holding address labeled New Wallet Enter 6: Return the holding address labeled Suspicious Enter 7: Return the holding address labeled Sniper Enter 8: Return the holding address labeled as Suspected phishing. Enter 9: Return the holding address labeled Bundle |
| cursor | String | No | Pagination cursor, pass the cursor value returned from the previous request |
| limit | String | No | Number of records per page, max 100 |
## Response Parameters
| Field | Type | Description |
|---|---|---|
| holderWalletAddress | String | Cash holding address |
| cursor | String | Pagination cursor |
| holdAmount | String | Number of coins held |
| holdPercent | String | Percentage of holdings |
| nativeTokenBalance | String | Mainnet currency balance |
| boughtAmount | String | Total Buy Quantity |
| avgBuyPrice | String | Average buying price |
| soldAmount | String | Total sold quantity |
| avgSellPrice | String | Average selling price |
| totalPnlUsd | String | Total profit and loss |
| realizedPnlUsd | String | Realized profit and loss |
| unrealizedPnlUsd | String | Unrealized profit and loss |
| fundingSource | String | Sources of funding |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/token/top-trader?chainIndex=501&tokenContractAddress=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&tagFilter=1' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"avgBuyPrice": "0.000061961311739113",
"avgSellPrice": "0.000122073405687202",
"boughtAmount": "48443482.085545000000000000",
"cursor": "0",
"fundingSource": "AC5RDfQFmDS1deWZos921JfqscXdByf8BKHs5ACWjtW2",
"holdAmount": "0",
"holdPercent": "0.000000000000000000",
"holderWalletAddress": "GHPyCYecXh398J72qi8nx9Sdaq8PiUJmLoL2q2yLwTY9",
"nativeTokenBalance": "0.024254276",
"realizedPnlUsd": "2912.039146298874538842343358",
"soldAmount": "41626711.761828000000000000",
"totalPnlUsd": "2912.039146298874538842343358",
"unrealizedPnlUsd": "0.000000000000000000"
},
{
"avgBuyPrice": "0.000045836227988662",
"avgSellPrice": "0.000117400306217832",
"boughtAmount": "29120882.898397000000000000",
"cursor": "1",
"fundingSource": "FLipG5QHjZe1H12f6rr5LCnrmqjhwuBTBp78GwzxnwkR",
"holdAmount": "0",
"holdPercent": "0.000000000000000000",
"holderWalletAddress": "HYSq1KBAvqWpEv1pCbV31muKM1za5A1WSHGdiVLUoNhb",
"nativeTokenBalance": "37.572873625",
"realizedPnlUsd": "2084.009141843422246184643326",
"soldAmount": "29120882.898397000000000000",
"totalPnlUsd": "2084.009141843422246184643326",
"unrealizedPnlUsd": "0.000000000000000000"
}
],
"msg": ""
}
```
- [Error Codes](https://web3.okx.com/onchainos/dev-docs/market/market-token-error-code.md)
# Error Codes
| Code | HTTP status | Message |
|-------|-------------|-----------------------------------------------------------------------------------------|
| 0 | 200 | Succeeded |
| 50011 | 429 | Rate limit reached. Please refer to API documentation and throttle requests accordingly |
| 50014 | 400 | Parameter \{param0\} cannot be empty |
| 50026 | 500 | System error. Try again later |
| 50103 | 401 | Request header "OK-ACCESS-KEY" cannot be empty |
| 50104 | 401 | Request header "OK-ACCESS-PASSPHRASE" cannot be empty|
| 50105 | 401 | Request header "OK-ACCESS-PASSPHRASE" incorrect |
| 50106 | 401 | Request header "OK-ACCESS-SIGN" cannot be empty |
| 50107 | 401 | Request header "OK-ACCESS-TIMESTAMP" cannot be empty |
| 50111 | 401 | Invalid OK-ACCESS-KEY |
| 50112 | 401 | Invalid OK-ACCESS-TIMESTAMP |
| 50113 | 401 | Invalid signature |
| 51000 | 400 | Parameter \{param0\} error |
- [Signal API Reference](https://web3.okx.com/onchainos/dev-docs/market/market-signal-reference.md)
# Signal API Reference
- [Get Signal Supported Chains](https://web3.okx.com/onchainos/dev-docs/market/market-signal-chains.md)
{/* api-page */}
# Get Signal Supported Chains
Returns a list of all blockchain networks supported by the Signal service.
## Request URL
GET `https://web3.okx.com/api/v6/dex/market/signal/supported/chain`
## Request Parameters
No
## Response Parameters
| Parameter | Type | Description |
|------------|--------|----------------------------|
| chainIndex | String | Unique identifier of the chain. |
| chainName | String | Chain name (e.g., Ethereum). |
| chainLogo | String | Chain icon. |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/signal/supported/chain' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"chainIndex": "1",
"chainLogo": "https://static.coinall.ltd/cdn/wallet/logo/ETH-20220328.png",
"chainName": "Ethereum"
},
{
"chainIndex": "196",
"chainLogo": "https://static.coinall.ltd/cdn/wallet/logo/okb_22400.png",
"chainName": "X Layer"
},
{
"chainIndex": "501",
"chainLogo": "https://static.coinall.ltd/cdn/wallet/logo/SOL-20220525.png",
"chainName": "Solana"
},
{
"chainIndex": "8453",
"chainLogo": "https://static.coinall.ltd/cdn/web3/dex/market/base_v2.png",
"chainName": "Base"
},
{
"chainIndex": "56",
"chainLogo": "https://static.coinall.ltd/cdn/web3/oklinkadmin/picture/new_bsc_chain_color.png",
"chainName": "BNB Chain"
}
],
"msg": ""
}
```
- [Get Latest Signal List](https://web3.okx.com/onchainos/dev-docs/market/market-signal-list.md)
{/* api-page */}
# Get Latest Signal List
Retrieve the latest buy-direction token signal list on a specified chain, sorted in descending order by time.
## Request URL
POST `https://web3.okx.com/api/v6/dex/market/signal/list`
## Request Parameters
| Parameter | Type | Required | Description |
|----------------------|---------|----------|-----------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier of the chain. Pass the chain ID (e.g., 1: Ethereum). Only single-chain queries are supported. |
| walletType | String | No | Wallet type code. Enum: 1 = Smart Money, 2 = KOL / Influencer, 3 = Whales. Multiple values supported, separated by commas. |
| minAmountUsd | String | No | Minimum total transaction amount (USD) for the selected wallet type(s). |
| maxAmountUsd | String | No | Maximum total transaction amount (USD) for the selected wallet type(s). |
| minAddressCount | String | No | Minimum number of addresses of the selected wallet type that triggered the signal. |
| maxAddressCount | String | No | Maximum number of addresses of the selected wallet type that triggered the signal. |
| tokenAddress | String | No | Token contract address. If provided, query the specified token; if not, return a filtered list within constraints. |
| minMarketCapUsd | String | No | Minimum market cap (USD) of the token when the signal was triggered. |
| maxMarketCapUsd | String | No | Maximum market cap (USD) of the token when the signal was triggered. |
| minLiquidityUsd | String | No | Minimum liquidity (USD) of the token when the signal was triggered. |
| maxLiquidityUsd | String | No | Maximum liquidity (USD) of the token when the signal was triggered. | |
| cursor | String | No | Pagination cursor, pass the cursor value returned from the previous request |
| limit | String | No | Number of records per page, max 100 |
## Response Parameters
| Parameter | Type | Description |
|-------------------------|---------|-------------|
| timestamp | String | Timestamp when the signal was triggered. |
| cursor | String | Pagination cursor |
| chainIndex | String | Unique identifier of the chain. |
| token | Object | Token information. |
| >tokenAddress | String | Token address. |
| >symbol | String | Token symbol. |
| >name | String | Token name. |
| >logo | String | Token logo. |
| >marketCapUsd | String | Market cap in USD. |
| >holders | String | Number of holder addresses. |
| >top10HolderPercent | String | Top 10 holder percentage. |
| price | String | Token price in USD when the signal was triggered. |
| walletType | String | Wallet type. Enum: SMART_MONEY / WHALE / INFLUENCER. |
| triggerWalletCount | String | Number of triggering wallet addresses. |
| triggerWalletAddress | String | List of triggering wallet addresses, separated by commas. |
| amountUsd | String | Transaction amount in USD. |
| soldRatioPercent | String | Sold percentage. |
## Request Example
```shell
curl --location --request POST 'https://web3.okx.com/api/v6/dex/market/signal/list' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z' \
--data-raw '[
{
"chainIndex": "501",
"walletType": "1,2,3",
"minAmountUsd": "1000",
"maxAmountUsd": "500000",
"minAddressCount": "2",
"maxAddressCount": "50",
"tokenAddress": "",
"minMarketCapUsd": "168564",
"maxMarketCapUsd": "",
"minLiquidityUsd": "333",
"maxLiquidityUsd": ""
}
]'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"amountUsd": "669.455417761799554132",
"chainIndex": "501",
"price": "0.000122496972899498",
"soldRatioPercent": "64.29",
"timestamp": "1774364940575",
"token": {
"holders": "445",
"logo": "https://static.oklink.com/cdn/web3/currency/token/pre/large/501-FN9ZSeNDdPV6bBF9DeDYxvqYK4JvFKeF7DBrhGGXJZ3Q-109/type=webp_90_0?v=1774292374274",
"marketCapUsd": "64466.670905193533007211",
"name": "Van Cleef Memes",
"symbol": "VanCleef",
"tokenAddress": "FN9ZSeNDdPV6bBF9DeDYxvqYK4JvFKeF7DBrhGGXJZ3Q",
"top10HolderPercent": "17.9547"
},
"triggerWalletAddress": "5jds1qi6nYu2JTZv5YTczPwyaq1RAkNkjbpsma8xSTem,4YzpSZpxDdjNf3unjkCtdWEsz2FL5mok7e5XQaDNqry8,F8sHTSpZpoNB7H2JUZ7tmMb2YWRM1pXiHPuubHVRDSsB",
"triggerWalletCount": "3",
"walletType": "2",
"cursor": "1774364940575!@#123"
},
{
"amountUsd": "1912.742277985131470193",
"chainIndex": "501",
"price": "0.000043905847119596",
"soldRatioPercent": "74.73",
"timestamp": "1774350239648",
"token": {
"holders": "1112",
"logo": "https://static.oklink.com/cdn/web3/currency/token/pre/large/501-AytNmHWe8uXfgBACLnvhyKWrecAcYFywLK2nXbegpump-109/type=webp_90_0?v=1774359780606",
"marketCapUsd": "127139.702497190490981742",
"name": "The Sandwich Heist",
"symbol": "Sandwich",
"tokenAddress": "AytNmHWe8uXfgBACLnvhyKWrecAcYFywLK2nXbegpump",
"top10HolderPercent": "18.4131"
},
"triggerWalletAddress": "As7HjL7dzzvbRbaD3WCun47robib2kmAKRXMvjHkSMB5,719sfKUjiMThumTt2u39VMGn612BZyCcwbM5Pe8SqFYz,3L8RAxLkvwkz4CgHivaVRtq19741FAdGLk5DgjRfc1fW,9FNz4MjPUmnJqTf6yEDbL1D4SsHVh7uA8zRHhR5K138r,FRbUNvGxYNC1eFngpn7AD3f14aKKTJVC6zSMtvj2dyCS,6iM1Ljh8zkY9Vxw6VqHmm9HJ3axD4f9yYpXnSCi22hvY",
"triggerWalletCount": "6",
"walletType": "2",
"cursor": "1774350239648!@#123" // pass this for next pagination
}
]
}
```
- [Get Leaderboard Supported Chain List](https://web3.okx.com/onchainos/dev-docs/market/market-signal-leaderboard-supported-chain.md)
{/* api-page */}
# Get Leaderboard Supported Chain List
Get the list of supported chains
### Request Path
GET `https://web3.okx.com/api/v6/dex/market/leaderboard/supported/chain`
## Request Parameters
None
## Response Parameters
| Field | Type | Description |
|---|---|---|
| chainIndex | String | Unique identifier of the chain |
| chainName | String | Chain name |
| chainLogo | String | Chain logo URL |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/leaderboard/supported/chain' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"chainIndex": "1",
"chainName": "Ethereum",
"chainLogo": "https://static.okx.com/cdn/wallet/logo/ETH-20220328.png"
},
{
"chainIndex": "501",
"chainName": "Solana",
"chainLogo": "https://static.okx.com/cdn/wallet/logo/SOL.png"
}
],
"msg": ""
}
```
- [Get Smart Money Leaderboard List](https://web3.okx.com/onchainos/dev-docs/market/market-signal-leaderboard-list.md)
{/* api-page */}
# Get Smart Money Leaderboard List
Retrieve the Smart Money leaderboard according to specified sorting and filter options; can be used in conjunction with the Signal API.
Limit: Maximum 20 records per request
### Request Path
GET `https://web3.okx.com/api/v6/dex/market/leaderboard/list`
## Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| chainIndex | string | Yes | Unique chain identifier (e.g., 1 for Ethereum). Only supports single-chain queries |
| timeFrame | string | Yes | Address transactions and PnL timeframe index (i.e. 1=1D, 2=3D, 3=7D, 4=1M, 5=3M) |
| sortBy | string | Yes | Sorting according to the selected timeFrame: PnL, ProfitRate, WinRate, TxVolume, TxCount. 1=Pnl, 2=Win Rate, 3=Tx number, 4=Volume, 5=ROI (profit rate) |
| walletType | string | No | Wallet type index. Enum: 1 = KOL, 2 = Developer, 3 = Smart Money, 4 = Whale, 5 = New Wallet, 6 = Insider (Mouse Position), 7 = Sniper, 8 = Suspected Phishing, 9 = Bundled Trader, 10 = Pump Smart Money. Supports single selection. If not provided, rankings for all wallet types will be returned. |
| minRealizedPnlUsd | string | No | Minimum realized profit and loss amount (USD) |
| maxRealizedPnlUsd | string | No | Maximum realized profit and loss amount (USD) |
| minWinRatePercent | string | No | Minimum win rate percentage |
| maxWinRatePercent | string | No | Maximum win rate percentage |
| minTxs | string | No | Minimum number of transactions |
| maxTxs | string | No | Maximum number of transactions |
| minTxVolume | string | No | Minimum transaction volume (USD) |
| maxTxVolume | string | No | Maximum transaction volume (USD) |
## Response Parameters
| Field | Type | Description |
|---|---|---|
| walletAddress | string | Wallet address |
| realizedPnlUsd | string | Accumulated realized PnL USD value for completed transactions within the selected timeframe |
| realizedPnlPercent | string | Accumulated realized PnL percentage for completed transactions within the selected timeframe |
| winRatePercent | string | The percentage of profitable tokens relative to the total number of tokens traded |
| avgBuyValueUsd | string | Average buy value |
| topPnlTokenList | array | Top 3 token list by PnL |
| > tokenContractAddress | string | Token contract address |
| > tokenSymbol | string | Token symbol |
| > tokenPnlUsd | string | Token PnL in USD |
| > tokenPnlPercent | string | Token PnL percentage |
| txVolume | string | Transaction volume in USD within the selected timeframe |
| txs | string | Transaction count within the selected timeframe |
| lastActiveTimestamp | string | Last active time |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/leaderboard/list?chainIndex=501&timeFrame=1&sortBy=1' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"walletAddress": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"realizedPnlUsd": "125430.56",
"realizedPnlPercent": "312.45",
"winRatePercent": "68.5",
"avgBuyValueUsd": "2340.00",
"topPnlTokenList": [
{
"tokenContractAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"tokenSymbol": "USDC",
"tokenPnlUsd": "45230.12",
"tokenPnlPercent": "156.78"
},
{
"tokenContractAddress": "So11111111111111111111111111111111111111112",
"tokenSymbol": "SOL",
"tokenPnlUsd": "38120.00",
"tokenPnlPercent": "98.34"
},
{
"tokenContractAddress": "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
"tokenSymbol": "BONK",
"tokenPnlUsd": "20450.88",
"tokenPnlPercent": "204.60"
}
],
"txVolume": "890234.50",
"txs": "342",
"lastActiveTimestamp": "1697630501000"
}
],
"msg": ""
}
```
- [Trenches API Reference](https://web3.okx.com/onchainos/dev-docs/market/market-scan-chain-api-reference.md)
# Trenches API Reference
- [Get Supported Chains and Protocols](https://web3.okx.com/onchainos/dev-docs/market/market-memepump-get-supported-chains-and-protocols.md)
{/* api-page */}
# Get Supported Chains and Protocols
Retrieve the list of chains and their corresponding protocols supported by Meme Pump.
## Request URL
GET `https://web3.okx.com/api/v6/dex/market/memepump/supported/chainsProtocol`
## Request Parameters
None
## Response Parameters
| Parameter | Type | Description |
|-----------------|--------|--------------------------------------------------------------|
| chainIndex | String | Chain unique identifier (e.g., `501` = Solana, `56` = BSC). |
| chainName | String | Chain name(eg.Ethereum) |
| protocolList | Array | List of supported protocols on this chain. |
| >protocolId | String | Protocol ID (e.g., `1` = PUMP_FUN). |
| >protocolName | String | Protocol name (e.g., `PUMP_FUN`, `SUNPUMP`). |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/memepump/supported/chainsProtocol' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"msg": "",
"data": [
{
"chainIndex": "501",
"chainName": "Solana",
"protocolList": [
{ "protocolId": "1", "protocolName": "PUMP_FUN" },
{ "protocolId": "2", "protocolName": "MOONSHOT" }
]
},
{
"chainIndex": "56",
"chainName": "BSC",
"protocolList": [
{ "protocolId": "3", "protocolName": "SUNPUMP" }
]
}
]
}
```
- [Get Token List](https://web3.okx.com/onchainos/dev-docs/market/market-memepump-get-token-list.md)
{/* api-page */}
# Get Token List
Retrieve the list of tokens matching specified filter criteria,with a maximum limit of 30 entries.
## Request URL
GET `https://web3.okx.com/api/v6/dex/market/memepump/tokenList`
## Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| chainIndex | String | Yes | Unique identifier for the blockchain network. e.g., `501` = Solana, `56` = BSC. Only single-chain queries are supported. |
| stage | String | Yes | Token lifecycle stage filter. Enum: `NEW` = newly created tokens, `MIGRATING` = tokens nearly migrated to DEX, `MIGRATED` = tokens that have completed migration to DEX. |
| walletAddress | String | No | User's wallet address. When provided, response will include user-specific position data (e.g., holdings, P&L). |
| protocolIdList | String | No | Comma-separated protocol IDs to filter by. e.g., `"120596"` for PumpFun, `"120596,139661"` for multiple protocols. Get available protocol IDs from `/memepump/supported/chainsProtocol`. |
| quoteTokenAddressList | String | No | Comma-separated quote token contract addresses to filter by. e.g., `"So11111111111111111111111111111111111111111"` for SOL. Filters tokens by their trading pair's quote currency. |
| minTop10HoldingsPercent | String | No | Minimum percentage of total supply held by top 10 holders. Value range: 0–100. e.g., `"10"` means top 10 holders hold at least 10%. |
| maxTop10HoldingsPercent | String | No | Maximum percentage of total supply held by top 10 holders. Value range: 0–100. |
| minDevHoldingsPercent | String | No | Minimum percentage of total supply held by the developer wallet. Value range: 0–100. |
| maxDevHoldingsPercent | String | No | Maximum percentage of total supply held by the developer wallet. Value range: 0–100. |
| minInsidersPercent | String | No | Minimum percentage of insider wallets among all holders. Value range: 0–100. |
| maxInsidersPercent | String | No | Maximum percentage of insider wallets among all holders. Value range: 0–100. |
| minBundlersPercent | String | No | Minimum percentage of bundler wallets among all holders. Value range: 0–100. Bundlers are wallets that bundle multiple transactions together. |
| maxBundlersPercent | String | No | Maximum percentage of bundler wallets among all holders. Value range: 0–100. |
| minSnipersPercent | String | No | Minimum percentage of sniper wallets among all holders. Value range: 0–100. Snipers are wallets that buy tokens extremely early after launch. |
| maxSnipersPercent | String | No | Maximum percentage of sniper wallets among all holders. Value range: 0–100. |
| minFreshWalletsPercent | String | No | Minimum percentage of fresh (newly created) wallets among all holders. Value range: 0–100. |
| maxFreshWalletsPercent | String | No | Maximum percentage of fresh (newly created) wallets among all holders. Value range: 0–100. |
| minSuspectedPhishingWalletPercent | String | No | Minimum percentage of suspected phishing wallets among all holders. Value range: 0–100. |
| maxSuspectedPhishingWalletPercent | String | No | Maximum percentage of suspected phishing wallets among all holders. Value range: 0–100. |
| minBotTraders | String | No | Minimum number of bot trader wallets. |
| maxBotTraders | String | No | Maximum number of bot trader wallets. |
| minDevMigrated | String | No | Minimum number of tokens previously migrated by the same developer. Useful for evaluating dev history. |
| maxDevMigrated | String | No | Maximum number of tokens previously migrated by the same developer. |
| communityTakeover | Boolean | No | Filter by whether the token has undergone a community takeover (CTO). `true` = only CTO tokens, `false` = only non-CTO tokens. |
| minFeesNative | String | No | Minimum total fees spent on the token, denominated in native chain currency (e.g., SOL). |
| maxFeesNative | String | No | Maximum total fees spent on the token, denominated in native chain currency. |
| minTxCount | String | No | Minimum total transaction count for the token. |
| maxTxCount | String | No | Maximum total transaction count for the token. |
| minBondingPercent | String | No | Minimum bonding curve completion percentage. Value range: 0–100. Applicable when `stage=NEW` or `stage=MIGRATING`. |
| maxBondingPercent | String | No | Maximum bonding curve completion percentage. Value range: 0–100. |
| minMarketCapUsd | String | No | Minimum market capitalization in USD. e.g., `"50000"` means market cap ≥ $50,000. |
| maxMarketCapUsd | String | No | Maximum market capitalization in USD. |
| minVolumeUsd | String | No | Minimum 24-hour trading volume in USD. |
| maxVolumeUsd | String | No | Maximum 24-hour trading volume in USD. |
| minHolders | String | No | Minimum number of unique token holders. e.g., `"100"` means at least 100 holders. |
| maxHolders | String | No | Maximum number of unique token holders. |
| minTokenAge | String | No | Minimum token age in minutes. When `stage=MIGRATED`, counted from migration timestamp; otherwise from creation timestamp. e.g., `"60"` means at least 1 hour old. |
| maxTokenAge | String | No | Maximum token age in minutes. When `stage=MIGRATED`, counted from migration timestamp; otherwise from creation timestamp. |
| minBuyTxCount | String | No | Minimum number of buy transactions in the last 1 hour. |
| maxBuyTxCount | String | No | Maximum number of buy transactions in the last 1 hour. |
| minSellTxCount | String | No | Minimum number of sell transactions in the last 1 hour. |
| maxSellTxCount | String | No | Maximum number of sell transactions in the last 1 hour. |
| minTokenSymbolLength | String | No | Minimum length of the token ticker symbol. e.g., `"3"` means symbol has at least 3 characters. |
| maxTokenSymbolLength | String | No | Maximum length of the token ticker symbol. |
| hasAtLeastOneSocialLink | Boolean | No | Filter by whether the token has at least one social media link. `true` = must have at least one social link. |
| hasX | Boolean | No | Filter by whether the token has an X (Twitter) link. `true` = must have X link. |
| hasTelegram | Boolean | No | Filter by whether the token has a Telegram link. `true` = must have Telegram link. |
| hasWebsite | Boolean | No | Filter by whether the token has an official website link. `true` = must have a website. |
| websiteTypeList | String | No | Comma-separated website type codes. Enum: `0` = official website, `1` = YouTube, `2` = Twitch, `3` = Facebook, `4` = Instagram, `5` = TikTok, `6` = Discord, `7` = GitHub. e.g., `"1,6"` for tokens with YouTube or Discord links. |
| dexScreenerPaid | Boolean | No | Filter by whether the token has paid for DexScreener promotion. `true` = only tokens that have paid for DexScreener. |
| liveOnPumpFun | Boolean | No | Filter by whether the token is currently live streaming on PumpFun. `true` = only tokens with active PumpFun live stream. |
| bagsFeeClaimed | Boolean | No | Filter by whether the developer has claimed bags fees (royalties). `true` = developer has claimed fees. |
| devSellAll | Boolean | No | Filter by whether the developer has sold all their token holdings. `true` = developer has sold everything. |
| devStillHolding | Boolean | No | Filter by whether the developer is still holding tokens. `true` = developer still holds tokens. |
| keywordsInclude | String | No | Filter tokens whose name or symbol contains the specified keyword. Case-insensitive. e.g., `"dog"` matches `"DOGE"`, `"DogWifHat"`. |
| keywordsExclude | String | No | Exclude tokens whose name or symbol contains the specified keyword. Case-insensitive. |
## Response Parameters
| Parameter | Type | Description |
|------------------------------------|---------|-------------------------------------------------------------------------------|
| cursor | String | Pagination cursor for the next page. Empty if no more data. |
| items | Array | Token list. |
| >chainIndex | String | Chain ID (e.g., `501` = Solana). |
| >protocolId | String | Protocol ID (e.g., `1` = PUMP_FUN). |
| >quoteTokenAddress | String | Quote token contract address. |
| >tokenContractAddress | String | Token contract address. |
| >symbol | String | Token symbol. |
| >name | String | Token name. |
| >logoUrl | String | Token logo URL. |
| >createdTimestamp | String | Token creation time (millisecond timestamp). |
| >market | Object | Market data. |
| >>marketCapUsd | String | Market cap (USD). |
| >>volumeUsd1h | String | 1h trading volume (USD). |
| >>txCount1h | String | 1h total transaction count. |
| >>buyTxCount1h | String | 1h buy transaction count. |
| >>sellTxCount1h | String | 1h sell transaction count. |
| >bondingPercent | String | Bonding curve progress (%). |
| >mayhemModeTimeRemaining | String | Pump.fun Mayhem Mode remaining time. Empty if not applicable. |
| >tags | Object | Audit / tag data. |
| >>top10HoldingsPercent | String | Top 10 holders' combined holdings (%). |
| >>devHoldingsPercent | String | Developer holdings (%). |
| >>insidersPercent | String | Insiders holdings (%). |
| >>bundlersPercent | String | Bundlers holdings (%). |
| >>snipersPercent | String | Snipers holdings (%). |
| >>freshWalletsPercent | String | Fresh wallets holdings (%). |
| >>suspectedPhishingWalletPercent | String | Suspected phishing wallets (%). |
| >>totalHolders | String | Total number of holder addresses. |
| >social | Object | Social media info. |
| >>x | String | X (Twitter) link. |
| >>telegram | String | Telegram link. |
| >>website | String | Website link. |
| >>websiteType | String | Website type identifier. |
| >>dexScreenerPaid | Boolean | Whether DEX Screener ads are active. |
| >>communityTakeover | Boolean | Community takeover (CTO) flag. |
| >>liveOnPumpFun | Boolean | Live on Pump.fun flag. |
| >bagsFeeClaimed | Boolean | Whether bags fee has been claimed. |
| >aped | String | Number of co-invested (aped) wallets. |
| >migratedBeginTimestamp | String | Migration start time (ISO 8601). |
| >migratedEndTimestamp | String | Migration end time (ISO 8601). |
| >creatorAddress | String | Token creator wallet address. |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/memepump/tokenList?chainIndex=501&protocolId=1&sort=createdTimestamp&order=desc&limit=30' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"msg": "",
"data": {
"cursor": "eyJsYXN0SWQiOiI3R2Y5Li4ucHVtcCJ9",
"items": [
{
"chainIndex": "501",
"protocolId": "1",
"quoteTokenAddress": "11111111111111111111111111111111",
"tokenContractAddress": "7Gf9...pump",
"symbol": "TETANUS",
"name": "tetanus",
"logoUrl": "https://static.okx.com/cdn/assets/imgs/xxx.png",
"createdTimestamp": "1730000000000",
"market": {
"marketCapUsd": "154880.12",
"volumeUsd1h": "50231.11",
"txCount1h": "225",
"buyTxCount1h": "128",
"sellTxCount1h": "97"
},
"bondingPercent": "63.5",
"mayhemModeTimeRemaining": "",
"tags": {
"top10HoldingsPercent": "0.12",
"devHoldingsPercent": "0.10",
"insidersPercent": "0.23",
"bundlersPercent": "0.48",
"snipersPercent": "0.35",
"freshWalletsPercent": "0.50",
"suspectedPhishingWalletPercent": "0.00",
"totalHolders": "2080"
},
"social": {
"x": "https://x.com/xxxx",
"telegram": "https://t.me/xxxx",
"website": "https://xxxx.com",
"websiteType": "1",
"dexScreenerPaid": false,
"communityTakeover": false,
"liveOnPumpFun": true
},
"bagsFeeClaimed": false,
"aped": "12",
"migratedBeginTimestamp": "",
"migratedEndTimestamp": "",
"creatorAddress": "3kXoZt...q1Re"
}
]
}
}
```
- [Get Token Details](https://web3.okx.com/onchainos/dev-docs/market/market-memepump-get-token-details.md)
{/* api-page */}
# Get Token Details
Retrieve Meme Pump scanner data for a specified token.
## Request URL
GET `https://web3.okx.com/api/v6/dex/market/memepump/tokenDetails`
## Request Parameters
| Parameter | Type | Required | Description |
|-----------------------|--------|----------|------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Chain unique identifier (e.g., `501` = Solana). Only single-chain queries are supported. |
| tokenContractAddress | String | Yes | Token contract address. |
| walletAddress | String | Yes | User's wallet address. When provided, response will include user-specific position and P&L data for this token. |
## Response Parameters
Same fields as individual items in the [Get Token List](#get-token-list) response, returned as a single object.
| Parameter | Type | Description |
|------------------------------------|---------|-------------------------------------------------------------------------------|
| chainIndex | String | Chain ID (e.g., `501` = Solana). |
| protocolId | String | Protocol ID (e.g., `1` = PUMP_FUN). |
| quoteTokenAddress | String | Quote token contract address. |
| tokenContractAddress | String | Token contract address. |
| symbol | String | Token symbol. |
| name | String | Token name. |
| logoUrl | String | Token logo URL. |
| createdTimestamp | String | Token creation time (millisecond timestamp). |
| market | Object | Market data. |
| >marketCapUsd | String | Market cap (USD). |
| >volumeUsd1h | String | 1h trading volume (USD). |
| >txCount1h | String | 1h total transaction count. |
| >buyTxCount1h | String | 1h buy transaction count. |
| >sellTxCount1h | String | 1h sell transaction count. |
| bondingPercent | String | Bonding curve progress (%). |
| mayhemModeTimeRemaining | String | Pump.fun Mayhem Mode remaining time. Empty if not applicable. |
| tags | Object | Audit / tag data. |
| >top10HoldingsPercent | String | Top 10 holders' combined holdings (%). |
| >devHoldingsPercent | String | Developer holdings (%). |
| >insidersPercent | String | Insiders holdings (%). |
| >bundlersPercent | String | Bundlers holdings (%). |
| >snipersPercent | String | Snipers holdings (%). |
| >freshWalletsPercent | String | Fresh wallets holdings (%). |
| >suspectedPhishingWalletPercent | String | Suspected phishing wallets (%). |
| >totalHolders | String | Total number of holder addresses. |
| social | Object | Social media info. |
| >x | String | X (Twitter) link. |
| >telegram | String | Telegram link. |
| >website | String | Website link. |
| >websiteType | String | Website type identifier. |
| >dexScreenerPaid | Boolean | Whether DEX Screener ads are active. |
| >communityTakeover | Boolean | Community takeover (CTO) flag. |
| >liveOnPumpFun | Boolean | Live on Pump.fun flag. |
| bagsFeeClaimed | Boolean | Whether bags fee has been claimed. |
| aped | String | Number of co-invested (aped) wallets. |
| migratedBeginTimestamp | String | Migration start time (ISO 8601). |
| migratedEndTimestamp | String | Migration end time (ISO 8601). |
| creatorAddress | String | Token creator wallet address. |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/memepump/tokenDetails?chainIndex=501&tokenContractAddress=7Gf9...pump' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"msg": "",
"data": {
"chainIndex": "501",
"protocolId": "1",
"quoteTokenAddress": "11111111111111111111111111111111",
"tokenContractAddress": "7Gf9...pump",
"symbol": "TETANUS",
"name": "tetanus",
"logoUrl": "https://static.okx.com/cdn/assets/imgs/xxx.png",
"createdTimestamp": "1730000000000",
"market": {
"marketCapUsd": "154880.12",
"volumeUsd1h": "50231.11",
"txCount1h": "225",
"buyTxCount1h": "128",
"sellTxCount1h": "97"
},
"bondingPercent": "63.5",
"mayhemModeTimeRemaining": "",
"tags": {
"top10HoldingsPercent": "0.12",
"devHoldingsPercent": "0.10",
"insidersPercent": "0.23",
"bundlersPercent": "0.48",
"snipersPercent": "0.35",
"freshWalletsPercent": "0.50",
"suspectedPhishingWalletPercent": "0.00",
"totalHolders": "2080"
},
"social": {
"x": "https://x.com/xxxx",
"telegram": "https://t.me/xxxx",
"website": "https://xxxx.com",
"websiteType": "1",
"dexScreenerPaid": false,
"communityTakeover": false,
"liveOnPumpFun": true
},
"bagsFeeClaimed": false,
"aped": "12",
"migratedBeginTimestamp": "",
"migratedEndTimestamp": "",
"creatorAddress": "3kXoZt...q1Re"
}
}
```
- [Get Token Developer Info](https://web3.okx.com/onchainos/dev-docs/market/market-memepump-get-token-developer-info.md)
{/* api-page */}
# Get Token Developer Info
Retrieve developer-related data for a specified token.
## Request URL
GET `https://web3.okx.com/api/v6/dex/market/memepump/tokenDevInfo`
## Request Parameters
| Parameter | Type | Required | Description |
|-----------------------|--------|----------|------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Chain unique identifier (e.g., `501` = Solana). Only single-chain queries are supported. |
| tokenContractAddress | String | Yes | Token contract address. |
## Response Parameters
| Parameter | Type | Description |
|-----------------------|--------|------------------------------------------------------------------------|
| devLaunchedInfo | Object | Summary of tokens launched by this developer. |
| >totalToken | String | Total number of tokens launched by this developer. |
| >rugPullCount | String | Number of rug pulls. |
| >migratedCount | String | Number of successfully migrated tokens. |
| >goldenGemCount | String | Number of golden gem tokens. |
| devHoldingInfo | Object | Developer's current holding info for this token. |
| >devHoldingPercent | String | Developer's current holdings (%). |
| >devAddress | String | Developer wallet address. |
| >fundingAddress | String | Funding source wallet address. |
| >devBalance | String | Developer's native token balance. |
| >lastFundedTimestamp | String | Last time the developer wallet was funded (ISO 8601). |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/memepump/tokenDevInfo?chainIndex=501&tokenContractAddress=7Gf9...pump' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"msg": "",
"data": {
"devLaunchedInfo": {
"totalToken": "18",
"rugPullCount": "3",
"migratedCount": "11",
"goldenGemCount": "2"
},
"devHoldingInfo": {
"devHoldingPercent": "2.35",
"devAddress": "3kXoZt...q1Re",
"fundingAddress": "Fv8N...tuQ",
"devBalance": "0.064",
"lastFundedTimestamp": "2025-06-26T06:37:39Z"
}
}
}
```
- [Get Similar Tokens](https://web3.okx.com/onchainos/dev-docs/market/market-memepump-get-similar-tokens.md)
{/* api-page */}
# Get Similar Tokens
Retrieve tokens similar to the specified token.
## Request URL
GET `https://web3.okx.com/api/v6/dex/market/memepump/similarToken`
## Request Parameters
| Parameter | Type | Required | Description |
|-----------------------|--------|----------|------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Chain unique identifier (e.g., `501` = Solana). Only single-chain queries are supported. |
| tokenContractAddress | String | Yes | Token contract address. |
## Response Parameters
| Parameter | Type | Description |
|-----------------------|--------|--------------------------------------------------|
| similarToken | Array | List of similar tokens. |
| >tokenContractAddress | String | Token contract address. |
| >tokenSymbol | String | Token symbol. |
| >tokenLogo | String | Token logo URL. |
| >marketCapUsd | String | Market cap (USD). |
| >lastTxTimestamp | String | Last transaction time (ISO 8601). |
| >createdTimestamp | String | Token creation time (ISO 8601). |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/memepump/similarToken?chainIndex=501&tokenContractAddress=7Gf9...pump' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"msg": "",
"data": {
"similarToken": [
{
"tokenContractAddress": "8Hd92...xYp1",
"tokenSymbol": "TETAX",
"tokenLogo": "https://static.okx.com/cdn/assets/imgs/tetax.png",
"marketCapUsd": "245800.32",
"lastTxTimestamp": "2025-03-08T12:45:21Z",
"createdTimestamp": "2025-03-01T08:00:00Z"
},
{
"tokenContractAddress": "9Ks81...LpQ9",
"tokenSymbol": "TETAN",
"tokenLogo": "https://static.okx.com/cdn/assets/imgs/tetan.png",
"marketCapUsd": "158920.00",
"lastTxTimestamp": "2025-03-08T12:40:10Z",
"createdTimestamp": "2025-02-28T15:12:00Z"
}
]
}
}
```
- [Get Token Bundle Details](https://web3.okx.com/onchainos/dev-docs/market/market-memepump-get-token-bundle-details.md)
{/* api-page */}
# Get Token Bundle Details
Retrieve bundler-related data for a specified token.
## Request URL
GET `https://web3.okx.com/api/v6/dex/market/memepump/tokenBundleInfo`
## Request Parameters
| Parameter | Type | Required | Description |
|-----------------------|--------|----------|------------------------------------------------------------------------------------------|
| chainIndex | String | Yes | Chain unique identifier (e.g., `501` = Solana). Only single-chain queries are supported. |
| tokenContractAddress | String | Yes | Token contract address. |
## Response Parameters
| Parameter | Type | Description |
|----------------------|--------|-----------------------------------------------------|
| bundlerAthPercent | String | Bundlers' all-time-high combined holdings (%). |
| totalBundlers | String | Total number of bundler addresses. |
| bundledValueNative | String | Total bundled amount in native token. |
| bundledTokenAmount | String | Total bundled token amount. |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/memepump/tokenBundleInfo?chainIndex=501&tokenContractAddress=7Gf9...pump' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"msg": "",
"data": {
"bundlerAthPercent": "0.92",
"totalBundlers": "9",
"bundledValueNative": "375",
"bundledTokenAmount": "4880000"
}
}
```
- [Get Token Aped Wallet Details](https://web3.okx.com/onchainos/dev-docs/market/market-memepump-get-token-aped-wallet-details.md)
{/* api-page */}
# Get Token Aped Wallet Details
Retrieve co-invested ("aped") wallet data for a specified token,with a maximum limit of 50 entries.
## Request URL
GET `https://web3.okx.com/api/v6/dex/market/memepump/apedWallet`
## Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| chainIndex | String | Yes | Unique identifier for the blockchain network. e.g., `501` = Solana, `56` = BSC. Only single-chain queries are supported. |
| tokenContractAddress | String | Yes | The contract address of the token to query aped wallet list for. |
| walletAddress | String | No | User's wallet address. When provided, the response will highlight whether the user's wallet is among the aped wallets. |
## Response Parameters
| Parameter | Type | Description |
|------------------|--------|----------------------------------------------------------------------|
| apedWalletList | Array | List of co-invested wallets. |
| >walletAddress | String | Wallet address. |
| >walletType | String | Wallet type. Enum: `SMART_MONEY`, `INFLUENCER`, `NORMAL`. |
| >holdingUsd | String | Current holdings value (USD). |
| >holdingPercent | String | Current holdings as a percentage of total supply (%). |
| >totalPnl | String | Total profit and loss (USD). |
| >pnlPercent | String | PnL as a percentage (%). |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/memepump/apedWallet?chainIndex=501&tokenContractAddress=7Gf9...pump' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"msg": "",
"data": {
"apedWalletList": [
{
"walletAddress": "9xK3ab...Tg91",
"walletType": "SMART_MONEY",
"holdingUsd": "12450.32",
"holdingPercent": "1.28",
"totalPnl": "5320.11",
"pnlPercent": "74.32"
},
{
"walletAddress": "3Lm92Q...Hs77",
"walletType": "INFLUENCER",
"holdingUsd": "8420.50",
"holdingPercent": "0.86",
"totalPnl": "2100.00",
"pnlPercent": "33.18"
}
]
}
}
```
- [Error Codes](https://web3.okx.com/onchainos/dev-docs/market/market-scan-chain-error-code.md)
# Error Codes
| Code | HTTP status | Message |
|-------|-------------|-----------------------------------------------------------------------------------------|
| 0 | 200 | Succeeded |
| 50011 | 429 | Rate limit reached. Please refer to API documentation and throttle requests accordingly |
| 50014 | 400 | Parameter \{param0\} cannot be empty |
| 50026 | 500 | System error. Try again later |
| 50103 | 401 | Request header "OK-ACCESS-KEY" cannot be empty |
| 50104 | 401 | Request header "OK-ACCESS-PASSPHRASE" cannot be empty|
| 50105 | 401 | Request header "OK-ACCESS-PASSPHRASE" incorrect |
| 50106 | 401 | Request header "OK-ACCESS-SIGN" cannot be empty |
| 50107 | 401 | Request header "OK-ACCESS-TIMESTAMP" cannot be empty |
| 50111 | 401 | Invalid OK-ACCESS-KEY |
| 50112 | 401 | Invalid OK-ACCESS-TIMESTAMP |
| 50113 | 401 | Invalid signature |
| 51000 | 400 | Parameter \{param0\} error |
- [API Reference](https://web3.okx.com/onchainos/dev-docs/market/market-portfolio-reference.md)
# API Reference
- [Get Supported Chains](https://web3.okx.com/onchainos/dev-docs/market/market-portfolio-supported-chain.md)
{/* api-page */}
# Get Supported Chains
Get the list of currently supported chains.
### Request Path
GET `https://web3.okx.com/api/v6/dex/market/portfolio/supported/chain`
## Request Parameters
None
## Response Parameters
| Parameter | Type | Description |
|---|---|---|
| chainIndex | String | Unique identifier of the chain |
| chainName | String | Chain name |
| chainLogo | String | Chain logo URL |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/portfolio/supported/chain' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"chainIndex": "1",
"chainName": "Ethereum",
"chainLogo": "https://static.okx.com/cdn/wallet/logo/ETH-20220328.png"
},
{
"chainIndex": "501",
"chainName": "Solana",
"chainLogo": "https://static.okx.com/cdn/wallet/logo/SOL.png"
}
],
"msg": ""
}
```
- [Get Address Portfolio Overview](https://web3.okx.com/onchainos/dev-docs/market/market-portfolio-overview.md)
{/* api-page */}
# Get Address Portfolio Overview
Get overview data related to address PnL, including total realized and unrealized PnL, winning rate, Top3 PnL tokens, and buy and sell transaction statistics
### Request Path
GET `https://web3.okx.com/api/v6/dex/market/portfolio/overview`
## Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| chainIndex | String | Yes | Unique identifier of the chain, pass the chain ID (e.g., 501 for Solana), only single-chain query is supported |
| walletAddress | String | Yes | Wallet address to query |
| timeFrame | String | Yes | Statistical range number for address transactions and PnL (1=1D, 2=3D, 3=7D, 4=1M, 5=3M) |
## Response Parameters
| Parameter | Type | Description |
|---|---|---|
| realizedPnlUsd | String | Realized PnL (USD) |
| top3PnlTokenSumUsd | String | Total PnL of Top 3 tokens (USD) |
| top3PnlTokenPercent | String | Top 3 tokens PnL percentage |
| topPnlTokenList | Array | Top 3 PnL token list |
| >tokenContractAddress | String | Token contract address |
| >tokenSymbol | String | Token symbol |
| >tokenPnLUsd | String | Token PnL (USD) |
| >tokenPnLPercent | String | Token PnL percentage |
| winRate | String | Win rate |
| tokenCountByPnlPercent | Object | Token count statistics categorized by PnL percentage |
| >over500Percent | String | Number of tokens with PnL over 500% |
| >zeroTo500Percent | String | Number of tokens with PnL between 0% and 500% |
| >zeroToMinus50Percent | String | Number of tokens with PnL between -50% and 0% |
| >overMinus50Percent | String | Number of tokens with PnL below -50% |
| buyTxCount | String | Number of buy transactions |
| buyTxVolume | String | Buy transaction volume |
| sellTxCount | String | Number of sell transactions |
| sellTxVolume | String | Sell transaction volume |
| avgBuyValueUsd | String | Average buy value (USD) |
| preferredMarketCap | String | Preferred market cap range. Primary preference: `1: less than $100K, 2: $100K-$1M, 3: $1M-$10M, 4: $10M-$100M, 5: greater than $100M` |
| buysByMarketCap | Array | Buy statistics categorized by market cap |
| >marketCapRange | String | Market cap range. Enum includes: `1: less than $100K, 2: $100K-$1M, 3: $1M-$10M, 4: $10M-$100M, 5: greater than $100M` |
| >buyCount | String | Buy count |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/portfolio/overview?chainIndex=1&walletAddress=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&timeFrame=3' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": {
"avgBuyValueUsd": "0",
"buyTxCount": "0",
"buyTxVolume": "0",
"buysByMarketCap": [
{
"buyCount": "0",
"marketCapRange": "1"
},
{
"buyCount": "0",
"marketCapRange": "2"
},
{
"buyCount": "0",
"marketCapRange": "3"
},
{
"buyCount": "0",
"marketCapRange": "4"
},
{
"buyCount": "0",
"marketCapRange": "5"
}
],
"preferredMarketCap": "1",
"realizedPnlUsd": "0",
"sellTxCount": "4",
"sellTxVolume": "0",
"tokenCountByPnlPercent": {
"over500Percent": "0",
"overMinus50Percent": "0",
"zeroTo500Percent": "0",
"zeroToMinus50Percent": "0"
},
"top3PnlTokenPercent": "0",
"top3PnlTokenSumUsd": "0",
"topPnlTokenList": [],
"winRate": "0.00"
},
"msg": ""
}
```
- [Get Address Recent PnL List](https://web3.okx.com/onchainos/dev-docs/market/market-portfolio-recent-pnl.md)
{/* api-page */}
# Get Address Recent PnL List
Get a list of recent PnL for an address in reverse chronological order
Limit: 1000 records, up to 100 per request
### Request Path
GET `https://web3.okx.com/api/v6/dex/market/portfolio/recent-pnl`
## Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| chainIndex | String | Yes | Unique identifier of the chain, pass the chain ID (e.g., 501 for Solana), only single-chain query is supported |
| walletAddress | String | Yes | Wallet address to query |
| cursor | String | No | Pagination cursor, pass the cursor value returned from the previous request |
| limit | String | No | Number of records per page, max 100 |
## Response Parameters
| Parameter | Type | Description |
|---|---|---|
| cursor | String | Pagination cursor |
| pnlList | Array | PnL list |
| >chainIndex | String | Unique identifier of the chain |
| >tokenContractAddress | String | Token contract address |
| >tokenSymbol | String | Token symbol |
| >lastActiveTimestamp | String | Last active timestamp (milliseconds) |
| >unrealizedPnlUsd | String | Unrealized PnL (USD), returns SELL_ALL if the address has sold all holdings |
| >unrealizedPnlPercent | String | Unrealized PnL percentage |
| >realizedPnlUsd | String | Realized PnL (USD) |
| >realizedPnlPercent | String | Realized PnL percentage |
| >totalPnlUsd | String | Total PnL (USD) |
| >totalPnlPercent | String | Total PnL percentage |
| >tokenBalanceUsd | String | Token balance value (USD) |
| >tokenBalanceAmount | String | Token balance amount |
| >tokenPositionPercent | String | Token position percentage |
| >tokenPositionDuration | Object | Token position duration info |
| >>holdingTimestamp | String | Holding start timestamp (milliseconds) |
| >>sellOffTimestamp | String | Sell-off timestamp (milliseconds), empty if still holding |
| >buyTxCount | String | Number of buy transactions |
| >buyTxVolume | String | Buy transaction volume |
| >buyAvgPrice | String | Average buy price |
| >sellTxCount | String | Number of sell transactions |
| >sellTxVolume | String | Sell transaction volume |
| >sellAvgPrice | String | Average sell price |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/portfolio/recent-pnl?chainIndex=1&walletAddress=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&limit=10' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": {
"pnlList": [
{
"chainIndex": "1",
"tokenContractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"tokenSymbol": "USDT",
"lastActiveTimestamp": "1710000000000",
"unrealizedPnlUsd": "100.00",
"unrealizedPnlPercent": "10.00",
"realizedPnlUsd": "200.00",
"realizedPnlPercent": "20.00",
"totalPnlUsd": "300.00",
"totalPnlPercent": "30.00",
"tokenBalanceUsd": "1000.00",
"tokenBalanceAmount": "1000",
"tokenPositionPercent": "10.00",
"tokenPositionDuration": {
"holdingTimestamp": "1700000000000",
"sellOffTimestamp": ""
},
"buyTxCount": "5",
"buyTxVolume": "900.00",
"buyAvgPrice": "0.99",
"sellTxCount": "2",
"sellTxVolume": "400.00",
"sellAvgPrice": "1.01"
}
]
},
"msg": ""
}
```
- [Get Address Latest PnL for Specific Token](https://web3.okx.com/onchainos/dev-docs/market/market-portfolio-latest-pnl.md)
{/* api-page */}
# Get Address Latest PnL for Specific Token
Get the latest income of the specified token of the address
### Request Path
GET `https://web3.okx.com/api/v6/dex/market/portfolio/token/latest-pnl`
## Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| chainIndex | String | Yes | Unique identifier of the chain, pass the chain ID (e.g., 501 for Solana) |
| walletAddress | String | Yes | Wallet address to query |
| tokenContractAddress | String | Yes | Token contract address |
## Response Parameters
| Parameter | Type | Description |
|---|---|---|
| totalPnlUsd | String | Total PnL (USD) |
| totalPnlPercent | String | Total PnL percent |
| unrealizedPnlUsd | String | Unrealized PnL (USD) |
| unrealizedPnlPercent | String | Unrealized PnL percent |
| realizedPnlUsd | String | Realized PnL (USD) |
| realizedPnlPercent | String | Realized PnL percent |
| isPnlSupported | Boolean | Whether PnL calculation is supported |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/portfolio/token/latest-pnl?chainIndex=1&walletAddress=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&tokenContractAddress=0xdac17f958d2ee523a2206206994597c13d831ec7' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": 0,
"data": {
"totalPnlUsd": "1371.68",
"totalPnlPercent": "20.22",
"unrealizedPnlUsd": "685.4",
"unrealizedPnlPercent": "10.11",
"realizedPnlUsd": "-685.4",
"realizedPnlPercent": "-10.11",
"isPnlSupported": true
}
}
```
- [Get Address DEX Transaction List](https://web3.okx.com/onchainos/dev-docs/market/market-portfolio-dex-history.md)
{/* api-page */}
# Get Address DEX Transaction List
Get the historical DEX transaction list of the address in reverse chronological order
Limit: 1000 records, up to 100 per request
### Request Path
GET `https://web3.okx.com/api/v6/dex/market/portfolio/dex-history`
## Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| chainIndex | String | Yes | Unique identifier of the chain, pass the chain ID (e.g., 501 for Solana) |
| walletAddress | String | Yes | Wallet address to query |
| begin | String | Yes | Start timestamp (milliseconds) |
| end | String | Yes | End timestamp (milliseconds) |
| tokenContractAddress | String | No | Token contract address; if not provided, returns transactions for all tokens |
| type | String | No | Transaction type: 1=BUY, 2=SELL, 3=Transfer In, 4=Transfer Out, supports comma-separated multiple types |
| cursor | String | No | Pagination cursor, pass the cursor value returned from the previous request |
| limit | String | No | Number of records per page, max 100 |
## Response Parameters
| Parameter | Type | Description |
|---|---|---|
| transactionList | Array | Transaction list |
| >type | String | Transaction type (1=BUY, 2=SELL, 3=Transfer In, 4=Transfer Out) |
| >chainIndex | String | Unique identifier of the chain |
| >tokenContractAddress | String | Token contract address |
| >tokenSymbol | String | Token symbol |
| >valueUsd | String | Transaction value (USD) |
| >amount | String | Token amount |
| >price | String | Transaction price |
| >marketCap | String | Market cap |
| >pnlUsd | String | PnL (USD) |
| >time | String | Transaction timestamp (milliseconds) |
| cursor | String | Pagination cursor for retrieving the next page of data |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/portfolio/dex-history?chainIndex=1&walletAddress=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&begin=1700000000000&end=1710000000000&limit=10' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": {
"transactionList": [
{
"type": "1",
"chainIndex": "1",
"tokenContractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"tokenSymbol": "USDT",
"valueUsd": "1000.00",
"amount": "1000",
"price": "1.00",
"marketCap": "100000000000",
"pnlUsd": "50.00",
"time": "1709900000000"
}
],
"cursor": "0"
},
"msg": ""
}
```
- [Get Latest DEX Trades for Multiple Addresses](https://web3.okx.com/onchainos/dev-docs/market/market-portfolio-address-tracker-trades.md)
{/* api-page */}
# Get Latest DEX Trades for Multiple Addresses
Retrieve on-chain transaction dynamics of tracked addresses. Supports filtering by dynamic type (custom grouping, platform KOL addresses, smart money addresses), trade type, volume, market cap, liquidity, chain, and more.
### Request Path
GET `https://web3.okx.com/api/v6/dex/market/address-tracker/trades`
## Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| trackerType | String | Yes | Track dynamic type. 1: smart_money, platform smart money addresses; 2: kol, platform Top 100 KOL addresses; 3: multi_address, query multiple addresses |
| walletAddress | String | No | Required when trackerType=multi_address. Can be a single address or multiple addresses separated by commas, up to 20 |
| tradeType | String | No | Trade type: 0: all (default); 1: buy; 2: sell |
| chainIndex | String | No | Chain identifier, defaults to all. Options: 501 (Solana), 1 (Ethereum), 56 (BNB Chain), 8453 (Base), 196 (X Layer) |
| minVolume | String | No | Minimum trade volume (USD) |
| maxVolume | String | No | Maximum trade volume (USD) |
| minHolders | String | No | Minimum number of holding addresses |
| minMarketCap | String | No | Minimum market cap (USD) |
| maxMarketCap | String | No | Maximum market cap (USD) |
| minLiquidity | String | No | Minimum liquidity (USD) |
| maxLiquidity | String | No | Maximum liquidity (USD) |
## Response Parameters
| Parameter | Type | Description |
|---|---|---|
| trades | Array | List of transaction dynamics for tracked addresses |
| >txHash | String | Transaction hash |
| >walletAddress | String | Wallet address of the transaction |
| >quoteTokenSymbol | String | Pricing token symbol; only mainnet tokens are returned here |
| >quoteTokenAmount | String | Quantity of pricing tokens traded, indicating the amount priced in the mainnet token |
| >tokenSymbol | String | Trading token symbol |
| >tokenContractAddress | String | Trading token contract address |
| >chainIndex | String | Chain identifier where the trading token is located |
| >tokenPrice | String | Trading price of the token (USD) |
| >marketCap | String | Market cap corresponding to the token's transaction price (USD) |
| >realizedPnlUsd | String | Realized profit and loss of the trading token (USD) |
| >tradeType | String | Trade type for the token: 1: buy; 2: sell |
| >tradeTime | String | Transaction time (millisecond timestamp) |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/market/address-tracker/trades?trackerType=1' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": {
"trades": [
{
"chainIndex": "56",
"marketCap": "6781.4431281050000000000000000000",
"quoteTokenAmount": "0.105214264768396182",
"quoteTokenSymbol": "BSC",
"realizedPnlUsd": "-28.212213718646256043110124512",
"tokenContractAddress": "0xcad59e019f06003303e56d4cc38fab0667114444",
"tokenPrice": "0.000006781443128105",
"tokenSymbol": "DoraemonToken",
"tradeTime": "1773736186000",
"tradeType": "2",
"txHash": "0x1daf1ab1c393ea5bf901f2d8efdd4a67ce1e1e0e7dfd748820e195f2398a8afd",
"walletAddress": "0xeb1222a0c07d4e62b0235d8b4b3e7e617d259be2"
}
]
},
"msg": ""
}
```
- [Error Codes](https://web3.okx.com/onchainos/dev-docs/market/market-portfolio-error-code.md)
# Error Codes
| Code | HTTP status | Message |
|-------|-------------|-----------------------------------------------------------------------------------------|
| 0 | 200 | Succeeded |
| 50011 | 429 | Rate limit reached. Please refer to API documentation and throttle requests accordingly |
| 50014 | 400 | Parameter \{param0\} cannot be empty |
| 50026 | 500 | System error. Try again later |
| 50103 | 401 | Request header "OK-ACCESS-KEY" cannot be empty |
| 50104 | 401 | Request header "OK-ACCESS-PASSPHRASE" cannot be empty|
| 50105 | 401 | Request header "OK-ACCESS-PASSPHRASE" incorrect |
| 50106 | 401 | Request header "OK-ACCESS-SIGN" cannot be empty |
| 50107 | 401 | Request header "OK-ACCESS-TIMESTAMP" cannot be empty |
| 50111 | 401 | Invalid OK-ACCESS-KEY |
| 50112 | 401 | Invalid OK-ACCESS-TIMESTAMP |
| 50113 | 401 | Invalid signature |
| 51000 | 400 | Parameter \{param0\} error |
- [API Reference](https://web3.okx.com/onchainos/dev-docs/market/balance-reference.md)
# API Reference
- [Get Supported Chains](https://web3.okx.com/onchainos/dev-docs/market/balance-chains.md)
{/* api-page */}
# Get Supported Chains
Retrieve information on chains supported by the DEX Balance endpoint
### Request URL
GET `https://web3.okx.com/api/v6/dex/balance/supported/chain`
## Request Parameters
None
## Response Parameters
| Parameter | Type | Description |
|-----------|--------|-------------------|
| name | String | Chain name |
| logoUrl | String | Chain logo URL |
| shortName | String | Chain short name |
| chainIndex| String | Chain unique identifier |
## Request Example
``` shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/balance/supported/chain' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"name": "Ethereum",
"logoUrl": "http://www.eth.org/eth.png",
"shortName": "ETH",
"chainIndex": "1"
}
],
"msg": ""
}
```
- [Get Total Value](https://web3.okx.com/onchainos/dev-docs/market/balance-total-value.md)
{/* api-page */}
# Get Total Value
Retrieve the total balance of all tokens and DeFi assets under an account.
### Request URL
GET `https://web3.okx.com/api/v6/dex/balance/total-value-by-address`
## Request Parameters
| Parameter | Type | Required | Description |
| ------------------| ------- | -------- | ------------------------------------------------------------------ |
| address | String | Yes | Get the total valuation for the address |
| chains | String | Yes | Filter chains for which to query total assets, separated by ",". Supports up to 50 chains.. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| assetType | String | No | Query balance type. Default is to query all asset balances. `0`: Query total balance for all assets, including tokens and DeFi assets. `1`: Query only token balance. `2`: Query only DeFi balance. |
| excludeRiskToken | Boolean | No | Option to filter out risky airdrop & honeypot tokens. Default is to filter. `true`: filter out, `false`: do not filter out It supports only `ETH`、`BSC`、`SOL`、`BASE` for honeypot tokens, more chains will be supported soon. |
## Response Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| totalValue | String | Total asset balance based on the query type, returned in USD |
## Request Example
``` shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/balance/total-value-by-address?address=0x0b32aa5c1e71715206fe29b7badb21ad95f272c0&chains=1&assetType=0' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
``` json
{
"code": "0",
"msg": "success",
"data": [
{
"totalValue": "1172.895057177065864522056725546579939398"
}
]
}
```
- [Get Total Token Balances](https://web3.okx.com/onchainos/dev-docs/market/balance-total-token-balances.md)
{/* api-page */}
# Get Total Token Balances
Retrieve the list of token balances for an address across multiple chains or specified chains.
### Request URL
GET `https://web3.okx.com/api/v6/dex/balance/all-token-balances-by-address`
## Request Parameters
| Parameter | Type | Required | Description |
|----------------|--------|----------|-----------------------------------------|
| address | String | Yes | Address |
| chains | Array | Yes | When filtering the chains for querying asset details, multiple chains should be separated by commas (`,`). A maximum of 50 chains is supported. e.g., `1`: Ethereum. See more [here](../home/supported-chain).|
| excludeRiskToken | String | No | Option to filter out risky airdrop & honeypot tokens. Default is to filter. `0`: Filter out `1`: Do not filter out It supports only `ETH`、`BSC`、`SOL`、`BASE` for honeypot tokens, more chains will be supported soon. |
## Response Parameters
| Parameter | Type | Description |
|---------------|--------|------------------------------------------|
| tokenAssets | Array | List of token balances |
| >chainIndex | String | Unique identifier for the chain |
| >tokenContractAddress | String | Contract address |
| >address | String | Address |
| >symbol | String | Token symbol |
| >balance | String | Token balance |
| >rawBalance | String | Raw balance of token address. For unsupported chains, this field is empty. More chains will be supported soon. |
| >tokenPrice | String | Token unit value, priced in USD |
| >isRiskToken | Boolean| `true`: flagged as a risky airdrop & honeypot token `false`: not flagged as a risky airdrop & honeypot token |
## Request Example
``` shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/balance/all-token-balances-by-address?address=0xEd0C6079229E2d407672a117c22b62064f4a4312&chains=1' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
``` json
{
"code": "0",
"msg": "success",
"data": [
{
"tokenAssets": [
{
"chainIndex": "1",
"tokenContractAddress": "0x386ae941d4262b0ee96354499df2ab8442734ec0",
"symbol": "PT-sUSDE-27FEB2025",
"balance": "47042180.520700015",
"tokenPrice": "0.968391562089677097",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0",
"symbol": "wstETH",
"balance": "7565.892480395067",
"tokenPrice": "4321.611627695311",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
"symbol": "WBTC",
"balance": "329.10055205",
"tokenPrice": "98847.8",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x23878914efe38d27c4d67ab83ed1b93a74d4086a",
"symbol": "aEthUSDT",
"balance": "30057379.938443",
"tokenPrice": "0.99978",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x657e8c867d8b37dcc18fa4caead9c45eb088c642",
"symbol": "eBTC",
"balance": "271.94970471",
"tokenPrice": "99094.345321371",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x4d5f47fa6a74757f35c14fd3a6ef8e3c9bc514e8",
"symbol": "aEthWETH",
"balance": "6080.001975381972",
"tokenPrice": "3634.32",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xe00bd3df25fb187d6abbb620b3dfd19839947b81",
"symbol": "PT-sUSDE-27MAR2025",
"balance": "19016580.895408865",
"tokenPrice": "0.952031186961110727",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xa17581a9e3356d9a858b789d68b4d866e593ae94",
"symbol": "cWETHv3",
"balance": "3000.000734740809",
"tokenPrice": "3663.74",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x9d39a5de30e57443bff2a8307a4256c8797a3497",
"symbol": "sUSDe",
"balance": "4863500.628333919",
"tokenPrice": "1.144688569528375454",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xec5a52c685cc3ad79a6a347abace330d69e0b1ed",
"symbol": "PT-LBTC-27MAR2025",
"balance": "46.02912324",
"tokenPrice": "97165.169717785655331396",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x8236a87084f8b84306f72007f36f2618a5634494",
"symbol": "LBTC",
"balance": "38.09998",
"tokenPrice": "99187.19184268864",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xbeef047a543e45807105e51a8bbefcc5950fcfba",
"symbol": "steakUSDT",
"balance": "482651.8612595832",
"tokenPrice": "1.063",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x4c9edd5852cd905f086c759e8383e09bff1e68b3",
"symbol": "USDe",
"balance": "69564",
"tokenPrice": "0.99977",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x8be3460a480c80728a8c4d7a5d5303c85ba7b3b9",
"symbol": "sENA",
"balance": "42294.989425",
"tokenPrice": "1.19",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "",
"symbol": "ETH",
"balance": "8.135546539084933",
"tokenPrice": "3638.63",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xbf5495efe5db9ce00f80364c8b423567e58d2110",
"symbol": "ezETH",
"balance": "5.270854886240325",
"tokenPrice": "3763.152404188635320082",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x6b175474e89094c44da98b954eedeac495271d0f",
"symbol": "DAI",
"balance": "1196.2693184870445",
"tokenPrice": "1.0002",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xc00e94cb662c3520282e6f5717214004a7f26888",
"symbol": "COMP",
"balance": "0.007643",
"tokenPrice": "84.43345772756197",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x9abfc0f085c82ec1be31d30843965fcc63053ffe",
"symbol": "Q*",
"balance": "900",
"tokenPrice": "0.000419255747329174",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xa1290d69c65a6fe4df752f95823fae25cb99e5a7",
"symbol": "rsETH",
"balance": "0.00007090104120006",
"tokenPrice": "3765.640772858747921444",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x56015bbe3c01fe05bc30a8a9a9fd9a88917e7db3",
"symbol": "CAT",
"balance": "0.42",
"tokenPrice": "0.06242994543936436",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xec53bf9167f50cdeb3ae105f56099aaab9061f83",
"symbol": "EIGEN",
"balance": "0.002496149915967488",
"tokenPrice": "4.018538365202288",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x58d97b57bb95320f9a05dc918aef65434969c2b2",
"symbol": "MORPHO",
"balance": "0.001409373661132556",
"tokenPrice": "3.3568669630371337",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xaf5191b0de278c7286d6c7cc6ab6bb8a73ba2cd6",
"symbol": "STG",
"balance": "0.000009547670354338",
"tokenPrice": "0.49707759500034454",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xba3335588d9403515223f109edc4eb7269a9ab5d",
"symbol": "GEAR",
"balance": "0.000009005734110189",
"tokenPrice": "0.012329598382413718",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x35fa164735182de50811e8e2e824cfb9b6118ac2",
"symbol": "eETH",
"balance": "0.000000000000000001",
"tokenPrice": "3637.93",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xae7ab96520de3a18e5e111b5eaab095312d7fe84",
"symbol": "stETH",
"balance": "0.000000000000000001",
"tokenPrice": "3637.93",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xa3931d71877c0e7a3148cb7eb4463524fec27fbd",
"symbol": "sUSDS",
"balance": "67435907.43236613",
"tokenPrice": "0",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xa8705a14c79fa1cded70875510211fec822b3c30",
"symbol": "BEEX",
"balance": "5000000",
"tokenPrice": "0",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0xabc0abace9fb9625fcefbedc423e8f94225bd251",
"symbol": "TANUKI",
"balance": "3548102.746002181",
"tokenPrice": "0",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
},
{
"chainIndex": "1",
"tokenContractAddress": "0x356b8d89c1e1239cbbb9de4815c39a1474d5ba7d",
"symbol": "syrupUSDT",
"balance": "1750000",
"tokenPrice": "0",
"isRiskToken": false,
"rawBalance": "",
"address": "0xed0c6079229e2d407672a117c22b62064f4a4312"
}
]
}
]
}
```
- [Get Specific Token Balance](https://web3.okx.com/onchainos/dev-docs/market/balance-specific-token-balance.md)
{/* api-page */}
# Get Specific Token Balance
Query the balance of a specific token under an address.
### Request URL
POST `https://web3.okx.com/api/v6/dex/balance/token-balances-by-address`
## Request Parameters
| Parameter | Type | Required | Description |
|----------------|--------|----------|-----------------------------------------|
| address | String | Yes | Address |
| tokenContractAddresses | Array | Yes | List of tokens addresses to query. Maximum of 20 items. |
| >chainIndex | String | Yes | Unique identifier for the chain. e.g., `1`: Ethereum. See more [here](../home/supported-chain). |
| >tokenContractAddress | String | Yes | Token address. `1`: Pass an empty string `""` to query the native token of the corresponding chain. `2`: Pass the specific token contract address to query the corresponding token. |
| excludeRiskToken | String | No | Option to filter out risky airdrop & honeypot tokens. Default is to filter `0`: Filter out `1`: Do not filter out It supports only `ETH`、`BSC`、`SOL`、`BASE` for honeypot tokens, more chains will be supported soon. |
## Response Parameters
| Parameter | Type | Description |
|--------------|--------|-----------------------------------------|
| tokenAssets | Array | List for token balances |
| >chainIndex | String | Unique identifier for the chain |
| >tokenContractAddress | String | Token address.If the return is an empty string `""`, it means the query is for the native token of the corresponding blockchain. |
| >address | String | Address |
| >symbol | String | Token symbol |
| >balance | String | Token balance. |
| >rawBalance | String | Raw balance of token address. For unsupported chains, this field is empty. More chains will be supported soon. |
| >tokenPrice | String | Token price in USD |
| >isRiskToken | Boolean| `true`: flagged as a risky airdrop & honeypot token `false`: not flagged as a risky airdrop & honeypot token |
## Request Example
```shell
curl --location --request POST 'https://web3.okx.com/api/v6/dex/balance/token-balances' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z' \
--data-raw '{
"address": "0x50c476a139aab23fdaf9bca12614cdd54a4244e3",
"tokenContractAddresses": [
{
"chainIndex": "1",
"tokenContractAddress": ""
}
]
}'
```
## Response Example
``` json
{
"code": "0",
"msg": "success",
"data": [
{
"tokenAssets": [
{
"chainIndex": "1",
"tokenContractAddress": "",
"symbol": "eth",
"balance": "0",
"tokenPrice": "3640.43",
"isRiskToken": false,
"rawBalance": "",
"address": ""
}
]
}
]
}
```
- [Error Codes](https://web3.okx.com/onchainos/dev-docs/market/balance-error-code.md)
# Error Codes
| Code | HTTP status | Message |
|-------|-------------|-----------------------------------------------------------------|
| 50014 | 400 | param \{param0\} is invalid |
| 50001 | 200 | Service temporarily unavailable. Try again later |
| 81001 | 200 | Incorrect parameter: : \{param0\} |
| 50011 | 429 | Too Many Requests |
| 81104 | 200 | Chain not support |
| 81001 | 200 | Required request body is missing |
- [API Reference](https://web3.okx.com/onchainos/dev-docs/market/tx-history-reference.md)
# API Reference
- [Get Supported Chains](https://web3.okx.com/onchainos/dev-docs/market/tx-history-chains.md)
{/* api-page */}
# Get Supported Chains
Retrieve information on chains supported by Transaction history API
### Request URL
GET `https://web3.okx.com/api/v6/dex/balance/supported/chain`
## Request Parameters
None
## Response Parameters
| Parameter | Type | Description |
|-----------|--------|-------------------|
| name | String | Chain name |
| logoUrl | String | Chain logo URL |
| shortName | String | Chain short name |
| chainIndex| String | Chain unique identifier |
## Request Example
``` shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/balance/supported/chain' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
```json
{
"code": "0",
"data": [
{
"name": "Ethereum",
"logoUrl": "http://www.eth.org/eth.png",
"shortName": "ETH",
"chainIndex": "1"
}
],
"msg": ""
}
```
- [Get History by Address](https://web3.okx.com/onchainos/dev-docs/market/tx-history-transactions-by-address.md)
{/* api-page */}
# Get History by Address
Query the transaction history under the address dimension for 6 months, sorted in descending chronological order.
### Request URL
GET `https://web3.okx.com/api/v6/dex/post-transaction/transactions-by-address`
## Request Parameters
| Parameter | Type | Required | Description |
|-------------- |-------- |---------- |--------------------------------------------------------------------------------------------------------------- |
| address | String | Yes | Address to query the transaction history for |
| chains | String | No | Filter the chains whose transaction history needs to be queried. Multiple chains are separated by ",". A maximum of 50 chains are supported. |
| tokenContractAddress | String | No | Token contract address; if empty, query addresses with main chain currency balance;if not pass, query all |
| begin | String | No | Start time, queries transactions after this time. Unix timestamp, in milliseconds |
| end | String | No | End time, queries transactions before this time. If both begin and end are not provided, queries transactions before the current time. Unix timestamp, in milliseconds |
| cursor | String | No | Cursor |
| limit | String | No | Number of records to return, defaults to the most recent 20 records. Up to a maximum of 20 records for query on single chain. Up to a maximum of 100 records for query on multiple chain. | |
## Response Parameters
| Parameter | Type | Description |
|----------------- |--------------------------------- |----------------------------------------------------- |
| transactions | Array | List of transactions |
| >chainIndex | String | Chain ID |
| >txHash | String | Transaction hash |
| >itype | String | Transaction tier type `0`: Outer main chain coin transfer `1`: Contract inner main chain coin transfer `2`: Token transfer |
| >methodId | String | Contract Function Call |
| >nonce | String | The nth transaction initiated by the sender address |
| >txTime | String | Transaction time in Unix timestamp format, in milliseconds, e.g., 1597026383085 |
| >from | Array | Transaction input |
| >>address | String | Sending/input address, comma-separated for multi-signature transactions |
| >>amount | String | Input amount |
| >to | Array | Transaction output |
| >>address | String | Receiving/output address, comma-separated for multiple addresses |
| >>amount | String | Output amount
| >tokenContractAddress | String | Token contract address |
| >amount | String | Transaction amount |
| >symbol | String | Currency symbol corresponding to the transaction amount |
| >txFee | String | Transaction fee |
| >txStatus | String | Transaction status: `success` for successful transactions, `fail` for failed transactions, `pending` for pending transactions |
| >hitBlacklist | Boolean | `false`: Not in blacklist, `true`: In blacklist |
| cursor | String | Cursor |
## Request Example
``` shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/post-transaction/transactions-by-address?addresses=0x50c476a139aab23fdaf9bca12614cdd54a4244e4&chains=1' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
``` json
{
"code": "0",
"msg": "success",
"data": [
{
"cursor": "1706197403",
"transactionList": [
{
"chainIndex": "1",
"txHash": "0x963767695543cfb7804039c470b110b87adf9ab69ebc002b571523b714b828ca",
"methodId": "",
"nonce": "",
"txTime": "1724213411000",
"from": [
{
"address":
"0xae7ab96520de3a18e5e111b5eaab095312d7fe84"
"amount": ""
}
],
"to": [
{
"address":
"0x50c476a139aab23fdaf9bca12614cdd54a4244e4"
"amount": ""
}
],
"tokenContractAddress": "0xe13c851c331874028cd8f681052ad3367000fb13",
"amount": "1",
"symbol": "claim rewards on stethdao.net",
"txFee": "",
"txStatus": "success",
"hitBlacklist": true,
"itype": "2"
}
]
}
]
}
```
- [Get Specific Transaction](https://web3.okx.com/onchainos/dev-docs/market/tx-history-specific-transaction-detail-by-txhash.md)
{/* api-page */}
# Get Specific Transaction
Retrieve details of a transaction based on `txHash` for 6 months. It decomposes a transaction and its internal transactions into sub-transactions based on asset type: `0`: Outer layer mainnet coin transfer `1`: Inner layer mainnet coin transfer in a contract `2`: Token transfer
It decomposes a transaction into sub-transactions based on asset type. For EVM transactions, different sub-transaction types include: `0`: Outer layer mainnet coin transfer `1`: Inner layer mainnet coin transfer in a contract `2`: Token transfer
### Request URL
GET `https://web3.okx.com/api/v6/dex/post-transaction/transaction-detail-by-txhash`
## Request Parameters
| Parameter | Type | Required | Description |
|--------------------|----------------|------------------|--------------------------------------------------------------------------|
| chainIndex | String | Yes | Unique identifier for the chain |
| txHash | String | Yes | Transaction hash |
| itype | String | No | Layer type for transactions `0`: Outer layer mainnet coin transfer `1`: Inner layer mainnet coin transfer `2`: Token transfer |
## Response Parameters
| Parameter | Type | Description |
|------------------------------------|----------------|----------------------------------------------------------------|
| chainIndex | String | Unique identifier for the chain |
| height | String | Block height where the transaction occurred |
| txTime | String | Transaction time; Unix timestamp in milliseconds |
| txhash | String | Transaction hash |
| txStatus | String | Transaction status: `1`: pending `2`: success `3`: fail |
| gasLimit | String | Gas limit |
| gasUsed | String | Gas used |
| gasPrice | String | Gas price |
| txFee | String | Transaction fee. |
| nonce | String | Nonce |
| amount | String | Transaction amount |
| symbol | String | Currency symbol for the transaction amount |
| methodId | String | Contract method ID |
| fromDetails | Array | Details of transaction inputs |
| >address | String | Sender/input address |
| >vinIndex | String | Index of the input in the current transaction |
| >preVoutIndex | String | Index of the output in the previous transaction |
| >txhash | String | Transaction hash, used with `preVoutIndex` to uniquely identify the UTXO |
| >isContract | Boolean | Whether the sender address is a contract (true: yes; false: no) |
| >amount | String | Transaction amount |
| toDetails | Array | Details of transaction outputs |
| >address | String | Receiver/output address |
| >voutIndex | String | Output index |
| >isContract | Boolean | Whether the receiver address is a contract (true: yes; false: no) |
| >amount | String | Transaction amount |
| internalTransactionDetails | Array | Internal transaction details |
| >from | String | Sender address for the internal transaction |
| >to | String | Receiver address for the internal transaction |
| >isFromContract | Boolean | Whether the sender address is a contract (true: yes; false: no) |
| >isToContract | Boolean | Whether the receiver address is a contract (true: yes; false: no) |
| >amount | String | Transaction amount |
| >txStatus | String | Transaction status |
| tokenTransferDetails | Array | Token transfer details |
| >from | String | Sender address for token transfer |
| >to | String | Receiver address for token transfer |
| >isFromContract | Boolean | Whether the sender address is a contract (true: yes; false: no) |
| >isToContract | Boolean | Whether the receiver address is a contract (true: yes; false: no) |
| >tokenContractAddress | String | Token contract address |
| >symbol | String | Token symbol |
| >amount | String | Token amount |
| l1OriginHash | String | Hash of the L1 transaction executed |
## Request Example
```shell
curl --location --request GET 'https://web3.okx.com/api/v6/dex/post-transaction/transaction-detail-by-txhash?txHash=0x9ab8ccccc9f778ea91ce4c0f15517672c4bd06d166e830da41ba552e744d29a5&chainIndex=42161' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'
```
## Response Example
``` json
{
"code": "0",
"msg": "success",
"data": [
{
"chainIndex": "42161",
"height": "245222398",
"txTime": "1724253417000",
"txhash": "0x9ab8ccccc9f778ea91ce4c0f15517672c4bd06d166e830da41ba552e744d29a5",
"gasLimit": "2000000",
"gasUsed": "2000000",
"gasPrice": "10000000",
"txFee":"",
"nonce": "0",
"symbol": "ETH",
"amount": "0",
"txStatus": "success",
"methodId": "0xc9f95d32",
"l1OriginHash": "0xa6a87ba2f18cc32bbae8f3b2253a29a9617ed1eb0940d80443f6e3bf9873dbad",
"fromDetails": [
{
"address": "0xd297fa914353c44b2e33ebe05f21846f1048cfeb",
"vinIndex": "",
"preVoutIndex": "",
"txHash": "",
"isContract": false,
"amount": ""
}
],
"toDetails": [
{
"address": "0x000000000000000000000000000000000000006e",
"voutIndex": "",
"isContract": false,
"amount": ""
}
],
"internalTransactionDetails": [
{
"from": "0x0000000000000000000000000000000000000000",
"to": "0xd297fa914353c44b2e33ebe05f21846f1048cfeb",
"isFromContract": false,
"isToContract": false,
"amount": "0.02",
"txStatus": "success"
},
{
"from": "0xd297fa914353c44b2e33ebe05f21846f1048cfeb",
"to": "0x428ab2ba90eba0a4be7af34c9ac451ab061ac010",
"isFromContract": false,
"isToContract": false,
"amount": "0.00998",
"txStatus": "success"
},
{
"from": "0xd297fa914353c44b2e33ebe05f21846f1048cfeb",
"to": "0x428ab2ba90eba0a4be7af34c9ac451ab061ac010",
"isFromContract": false,
"isToContract": false,
"amount": "0.009977946366846017",
"txStatus": "success"
}
],
"tokenTransferDetails": []
}
]
}
```
- [Error Codes](https://web3.okx.com/onchainos/dev-docs/market/tx-history-error-code.md)
# Error Codes
| Code | HTTP status | Message |
|-------|-------------|-----------------------------------------------------------------------------------------|
| 81001 | 200 | Incorrect parameter |
- [What is Connecting Wallet](https://web3.okx.com/onchainos/dev-docs/sdks/okx-wallet-integration-introduction.md)
# What is Connecting Wallet
In a DApp, adding a "Connect OKX Wallet" button allows interaction with the OKX Wallet.
OKX Wallet comes in various forms and currently supports:
App Wallet
Browser Extension Wallet
DApps can choose the appropriate connection method:
**Please note that you can choose between UI SDK and ProviderAPI when accessing. It is recommended to choose UI SDK for one-time access and multi-end compatibility.**
- [Supported Networks](https://web3.okx.com/onchainos/dev-docs/sdks/okx-wallet-integration-supported-networks.md)
# Supported Networks
As a leading global Web3 wallet, OKX Wallet supports over 100 networks, including EVM series, UTXO series, Solana, Ton, and other popular networks. It also supports the latest ecosystems such as Ordinals.
The current mainstream networks in the industry can all connect and invoke the OKX Wallet. You can refer to the table below to understand the support status of DApp connections for each network.
| | Connect Browser Extension Wallet | Connect Mobile App Wallet |
|----------|-----------------------------------------------|--------------------------------------------|
| EVM | [Supported](./chains/evm/introduce) | [Supported](./app-connect-evm) |
| Bitcoin | [Supported](./chains/bitcoin/introduce) | [Supported](./app-connect-bitcoin) |
| Solana | [Supported](./chains/solana/introduce) | [Supported](./app-connect-solana) |
| Ton | [Supported](./chains/ton/introduce) | [Supported](./app-connect-ton) |
| SUI | [Supported](./chains/sui/introduce) | [Supported](./app-connect-sui) |
| Aptos | [Supported](./chains/aptos/introduce) | [Supported](./app-connect-aptos) |
| Cosmos | [Supported](./chains/cosmos/introduce) | [Supported](./app-connect-cosmos) |
| Tron | [Supported](./chains/tron/introduce) | [Supported](./app-connect-tron) |
| Starknet | [Supported](./chains/starknet/introduce) | [Supported](./app-connect-starknet) |
| NEAR | [Supported](./chains/near/introduce) | Coming Soon |
| Stacks | [Supported](./chains/stacks/introduce) | Coming Soon |
| Cardano | [Supported](./chains/cardano/introduce) | Coming Soon |
| Nostr | [Supported](./chains/nostr/introduce) | Coming Soon |
| WAX | [Supported](./chains/wax/introduce) | Coming Soon |
- [Connection Prerequisites](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-preparation.md)
# Connection Prerequisites
The OKX Connect protocol allows connection to the OKX mobile App Wallet, suitable for DApp operations in mobile browsers.
If connecting via SDK, add a "Connect OKX Wallet" button within the DApp. Clicking this button will launch the mobile App Wallet and enable interaction with the OKX Wallet, such as retrieving addresses, initiating wallet signatures, and other functions.
In addition to SDK, we also provide a UI interface.
If you haven't downloaded the OKX App Wallet yet, please visit the download page:
[Download OKX App](https://web3.okx.com/download)
If you have already downloaded the App Wallet, find the network where your DApp is deployed in the left menu and follow the corresponding method to start the connection. Connection methods vary by network.
- [EVM-Compatible Chains](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-evm.md)
# EVM-Compatible Chains
EVM-compatible chains refer to blockchain networks that use Ethereum Virtual Machine (EVM) technology.
These chains share the same smart contract execution environment as Ethereum, allowing developers to easily deploy Ethereum-based applications on these networks.
This compatibility enables developers to use existing Ethereum tools and libraries, such as Solidity, Web3.js, and Truffle, to build and deploy decentralized applications (DApps).
Common EVM-compatible chains include Polygon, Avalanche, and Fantom. The emergence of these networks has enriched the blockchain ecosystem, providing users with more options while promoting the development of cross-chain interoperability.
If connecting via SDK, add a "Connect OKX Wallet" button within the DApp. Clicking this button will launch the mobile App Wallet and enable interaction with the OKX Wallet, such as retrieving addresses, initiating wallet signatures, and other functions.
In addition to SDK, we also provide a UI interface.
- [SDK](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-evm-sdk.md)
# SDK
## Installation and Initialization
Make sure to update the OKX App to version 6.88.0 or later to start integration:
To integrate OKX Connect into your DApp, use npm:
```
npm install @okxconnect/universal-provider
```
Before connecting to the wallet, you need to create an object for subsequent wallet connection, transaction sending, and other operations.
`OKXUniversalProvider.init({DAppMetaData: {name, icon}})`
**Request Parameters**
- DAppMetaData - object
- name - string: Application name, not used as a unique identifier
- icon - string: URL of the application icon. Must be in PNG, ICO, or similar formats; SVG icons are not supported. Ideally, provide a URL for a 180x180px PNG icon.
**Return Value**
- OKXUniversalProvider
**Example**
```typescript
import {OKXUniversalProvider} from "@okxconnect/universal-provider";
const okxUniversalProvider = await OKXUniversalProvider.init({
DAppMetaData: {
name: "application name",
icon: "application icon url"
},
})
```
## Connect to Wallet
Connect the wallet to obtain the wallet address, which serves as an identifier and is necessary for signing transactions
`okxUniversalProvider.connect(connectParams: ConnectParams);`
**Request Parameters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace; Information required for connection. The key for EVM systems is "eip155". If any chain requested is not supported by the wallet, the wallet will reject the connection.
- chains: string[]; Chain ID information
- defaultChain?: string; default chain
- rpcMap?: [chainId: string]: string; rpc information, configure rpc url to request rpc information on the chain, only support EVM system, the chain configured for RPC must be included in the chains
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information for requesting connection, the key of EVM system is 'eip155', if the corresponding chain information is not supported by the wallet, it can still be connected; if you need to connect to a custom network, you can add the request of the custom network to this parameter, if the wallet does not support it, you can add the request of the custom network to this parameter. If you need to connect to a custom network, you can add the request for the custom network to this parameter, if the wallet already has the custom network, the information of the custom chain will be returned in the request result session; if the wallet doesn't support it, and there is no information of the custom chain in the request result session, you can add the custom chain by calling the request method again, with the method set to wallet_addEthereumChain. Add the custom chain.
- chains: string[]; chain id information
- defaultChain?: string; default chain
- rpcMap?: [chainId: string]: string; rpc information, configure rpc url to request rpc information on the chain, only support EVM system, the chain configured for RPC must be included in the chains
- sessionConfig: object
- redirect: string; Redirection parameter after a successful connection. If in a Telegram Mini App, set this to the Telegram deeplink: "tg://resolve".
**Return Value**
- Promise ``
- topic: string; The session identifier
- namespaces: `Record`; namespace information for a successful connection
- chains: string[]; Chain information for the connection
- accounts: string[]; accounts information for the connection
- methods: string[]; methods supported by the wallet under the current namespace
- defaultChain?: string; default chain for the current session
- sessionConfig?: SessionConfig
- DAppInfo: object DApp information
- name:string
- icon:string
- redirect?: string, the redirect parameter after successful connection
**Example**
```typescript
var session = await okxUniversalProvider.connect({
namespaces: {
eip155: {
// Please pass in as many chain ids as you need.
chains: ["eip155:1","eip155:137"],
defaultChain: "1",
rpcMap: {
"137":"https://polygon.drpc.org"
}
}
},
optionalNamespaces: {
eip155: {
chains: ["eip155:43114"]
}
},
sessionConfig: {
redirect: "tg://resolve"
}
})
```
## Determine if the wallet is connected
Get whether the wallet is currently connected
**Return Value**
- boolean
**Example**
```typescript
okxUniversalProvider.connected();
```
## Sending Signature and Transactions
This method allows sending messages to the wallet, supporting signatures, transactions
```
okxUniversalProvider.request(requestArguments, chain);
```
**Request Parameters**
- requestArguments - object
- method: string; the name of the requested method
- params?: unknown[] | Record`` | object | undefined; Parameters corresponding to the requested method
- redirect -string 'none' | `${string}://${string}`; App wallet, the return policy of the deep link when the user signs or rejects the request, if it is a Mini App in Telegram, it can be configured with tg://resolve, if it's not configured here, it'll take the redirect passed by connect method, default is 'none'
- chain: string, the chain in which the requested method will be executed, it is recommended to pass this parameter, if not, it will be set to the current defaultChain
**Return Values**
The results returned vary depending on the method executed. Refer to the examples below for specific parameters
- personal_sign
- Promise - string: Signature result
- eth_signTypedData_v4
- Promise - string: Signature result
- eth_sendTransaction
- Promise - string: Transaction hash
- eth_accounts
- Promise - string[]: Returns addresses for the default chainId
- eth_requestAccounts
- Promise - string[]: Returns addresses for the default chainId
- eth_chainId
- Promise - number: Returns the default chain ID
- wallet_switchEthereumChain
- Promise - null
- wallet_addEthereumChain
- Promise - null
- wallet_watchAsset
- Promise - boolean: Successfully added
**Examples**
```typescript
let chain ='eip155:1'
var data = {}
// Execute personalSign on the chain.
// The first parameter in the params array is mandatory for Challenge;
// The second parameter, hex encoded address, is optional.
data = {
"params": [
"0x506c65617365207369676e2074686973206d65737361676520746f20636f6e6669726d20796f7572206964656e746974792e",
"0x4B0897b0513FdBeEc7C469D9aF4fA6C0752aBea7"
]
}
var personalSignResult = await okxUniversalProvider.request(data, chain)
//personalSignResult: "0xe8d34297c33a61"
// Execute eth_signTypedData_v4 on chain
// params array, first parameter is Address is optional;
// The second parameter is TypedData, which must be passed.
data = {
"method": "eth_signTypedData_v4",
"params": [
"0x00000",
{
"domain": {
"name": "Ether Mail",
"version": "1",
"chainId": 1,
"verifyingContract": "0xcccccccccccccccccccccccccccccccccccccccc"
},
"message": {
"from": {"name": "Cow", "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"},
"to": {"name": "Bob", "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"},
"contents": "Hello, Bob!"
},
"primaryType": "Mail",
"types": {
"EIP712Domain": [{"name": "name", "type": "string"}, {
"name": "version",
"type": "string"
}, {"name": "chainId", "type": "uint256"}, {"name": "verifyingContract", "type": "address"}],
"Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}],
"Mail": [{"name": "from", "type": "Person"}, {"name": "to", "type": "Person"}, {
"name": "contents",
"type": "string"
}]
}
}
]
}
var signTypeV4Result = await okxUniversalProvider.request(data, chain)
//signTypeV4Result: "0xa8bb3c6b33a119d..."
// Execute sendTransaction on the chain,
data = {
"method": "eth_sendTransaction",
"params": [
{
to: "0x4B...",
from: "0xDe...",
gas: "0x76c0",
value: "0x8ac7230489e80000",
data: "0x",
gasPrice: "0x4a817c800"
}
]
}
var sendTransactionResult = await okxUniversalProvider.request(data, chain)
// "0x1ccf2c4a3d689067fc2ac..."
// Get address information for the default chain;
data = {"method": "eth_requestAccounts"}
var ethRequestAccountsResult = await okxUniversalProvider.request(data, chain)
// ["0xf2f3e73b..."]
// Get the default chain information;
data = {"method": "eth_chainId"}
var chainIdResult = await okxUniversalProvider.request(data, chain)
//chainIdResult 1
// Switching chains;
data = {
"method": "wallet_switchEthereumChain",
"params": [
{
chainId: "0x1"
}
]
}
var switchResult = await okxUniversalProvider.request(data, chain)
// switchResult null
// Add chain
data = {
"method": "wallet_addEthereumChain",
"params": [{
"blockExplorerUrls": ["https://explorer.fuse.io"],
"chainId": "0x7a",
"chainName": "Fuse",
"nativeCurrency": {"name": "Fuse", "symbol": "FUSE", "decimals": 18},
"rpcUrls": ["https://rpc.fuse.io"]
}]
}
var addEthereumChainResult = await okxUniversalProvider.request(data, chain)
//addEthereumChainResult null
// add coins to the chain watchAsset
data = {
"method": "wallet_watchAsset",
"params": [{
"type": "ERC20",
"options": {
"address": "0xeB51D9A39AD5EEF215dC0Bf39a8821ff804A0F01",
"symbol": "LGNS",
"image": "https://polygonscan.com/token/images/originlgns_32.png",
"decimals": 9
}
}]
}
var watchAssetResult = await okxUniversalProvider.request(data, chain)
// watchAssetResult
// true/false
```
## Using RPC
When EVM request method can not meet the demand, you can configure RPC to achieve more functions, in the connection wallet connect(), RPC configuration in the rpcMap.
**Example**
```typescript
// Query the details of the transaction hash
let rpcData = {
method: "eth_getTransactionByHash",
params: ["0xd62fa4ea3cf7ee3bf6f5302b764490730186ed6a567c283517e8cb3c36142e1a"],
};
let result = await universalUi.request(rpcData,"eip155:137")
```
## Set Default Network
In the case of multiple networks, if the developer does not specify the network where the current operation is performed, the interaction will be performed through the default network.
**Example**
```typescript
okxUniversalProvider.setDefaultChain("eip155:1")
```
## Disconnect wallet
Disconnect from a connected wallet and delete the current session. If you want to switch wallets, disconnect from the current wallet first.
```typescript
okxUniversalProvider.disconnect();
```
## Event
```typescript
// Generate universalLink
okxUniversalProvider.on('display_uri', (uri) => {
console.log(uri);
});
// Session information changes will trigger this event;
okxUniversalProvider.on('session_update', (session) => {
console.log(JSON.stringify(session)); // Session information changes (e.g., adding a custom chain).
});
// Disconnecting triggers this event;
okxUniversalProvider.on('session_delete', ({topic}) => {
console.log(topic);
});
```
## Error codes
Exceptions that may be thrown during connection, transaction, and disconnection.
**Exception**
| Error Code | Description |
|----------------------------------------------|-------------------------|
| OKX_CONNECT_ERROR_CODES.UNKNOWN_ERROR | Unknown Error |
| OKX_CONNECT_ERROR_CODES.ALREADY_CONNECTED_ERROR | Wallet Already Connected |
| OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR | Wallet Not Connected |
| OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR | User Rejected |
| OKX_CONNECT_ERROR_CODES.METHOD_NOT_SUPPORTED | Method Not Supported |
| OKX_CONNECT_ERROR_CODES.CHAIN_NOT_SUPPORTED | Chain Not Supported |
| OKX_CONNECT_ERROR_CODES.WALLET_NOT_SUPPORTED | Wallet Not Supported |
| OKX_CONNECT_ERROR_CODES.CONNECTION_ERROR | Connection Error |
```typescript
export enum OKX_CONNECT_ERROR_CODES {
UNKNOWN_ERROR = 0,
ALREADY_CONNECTED_ERROR = 11,
NOT_CONNECTED_ERROR = 12,
USER_REJECTS_ERROR = 300,
METHOD_NOT_SUPPORTED = 400,
CHAIN_NOT_SUPPORTED = 500,
WALLET_NOT_SUPPORTED = 600,
CONNECTION_ERROR = 700
}
```
- [UI](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-evm-ui.md)
# UI
Make sure to update the OKX App to version 6.90.1 or later to start accessing:
To integrate OKX Connect into your DApp, you can use npm:
## Install via npm
```
npm install @okxconnect/ui
```
## Initialization
Before connecting to the wallet, you need to create an object for subsequent operations such as connecting to the wallet and sending transactions.
```
OKXUniversalConnectUI.init(DAppMetaData, actionsConfiguration, uiPreferences, language, restoreConnection)
```
**Request Parameterseters**
- dappMetaData - object
- name - string: the name of the application, will not be used as a unique representation.
- icon - string: URL of the application icon, must be in PNG, ICO, etc. SVG icons are not supported. SVG icons are not supported. It is better to pass a url pointing to a 180x180px PNG icon.
- actionsConfiguration - object
- modals - ('before' |'success' |'error')[] |'all' The modes of displaying alerts during transaction, defaults to'before'.
- returnStrategy -string'none' | `${string}://${string}`; For app wallet, specify the return strategy for the deep link when the user signs/rejects the request, or configure tg://resolve if it's in tg;
- uiPreferences -object
- theme - Theme can be: THEME.DARK, THEME.LIGHT,'SYSTEM'.
- language -'en_US' |'ru_RU' |'zh_CN' |'ar_AE' |'cs_CZ' |'de_DE' |'es_ES' |'es_LAT' |'fr_FR' |'id_ID' |'it_IT' |'nl_NL' |'pl_PL' |'pt_BR' |'pt_PT' |'ro_RO' |'tr_TR' |'uk_UA' |'vi_VN'.
, defaults to en_US
- restoreConnection?: boolean - Whether to automatically restore the previous connection;
**Return Value**.
- OKXUniversalConnectUI
**Examples**
```typescript
import { OKXUniversalConnectUI } from "@okxconnect/ui";
const universalUi = await OKXUniversalConnectUI.init({
DAppMetaData: {
icon: "https://static.okx.com/cdn/assets/imgs/247/58E63FEA47A2B7D7.png",
name: "OKX Connect Demo"
},
actionsConfiguration: {
returnStrategy:'tg://resolve',
modals:'all',
tmaReturnUrl:'back'
},
language: "en_US",
uiPreferences: {
theme: THEME.LIGHT
},
});
```
## Connecting to a wallet
Connecting to a wallet goes to get the wallet address as an identifier and the necessary parameters used to sign the transaction, the
```
await universalUi.openModal(connectParams: ConnectParams);
```
**Request Parameters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace; The necessary information for requesting a connection. The key for the EVM system is "eip155". If any chain in the request is not supported by a wallet, the connection will be rejected.
- chains: string[]; Chain ID information, defined as decimal numbers in EIP155, for example, Ethereum is eip155:1.
- defaultChain?: string; The default chain.
- rpcMap?: [chainId: string]: string; rpc information, configure rpc url to request rpc information on the chain, only support EVM system, the chain configured for RPC must be included in the chains;
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information for requesting connection, the key of EVM system is "eip155", if the corresponding chain information is not supported by the wallet, it can still be connected; if you need to connect to a custom network, you can add the request of the custom network to this parameter, if the wallet does not support it, you can add the request of the custom network to this parameter. If you need to connect to a custom network, you can add the request for the custom network to this parameter, if the wallet already has the custom network, the information of the custom chain will be returned in the request result session; if the wallet doesn't support it, and there is no information of the custom chain in the request result session, you can add the custom chain by calling the request method again, with the method set to wallet_addEthereumChain. Add the custom chain.
- chains: string[]; chain id information
- rpcMap?: [chainId: string]: string; rpc information, configure rpc url to request rpc information on the chain, only support EVM system, the chain configured with RPC must be included in the chains;
**Return value**
- Promise ``
- topic: string; the session identifier
- namespaces: `Record`; namespace information for a successful connection
- chains: string[]; Chain information for the connection
- accounts: string[]; accounts information for the connection
- methods: string[]; methods supported by the wallet under the current namespace
- defaultChain?: string; The default chain for the current session
- sessionConfig?: SessionConfig
- DAppInfo: object DApp information
- name:string
- Info: object DApp info; name:string
**Example**
```typescript
var session = await universalUi.openModal({
namespaces: {
eip155: {
// Please pass in as many chain ids as you need.
chains: ["eip155:1","eip155:137"],
defaultChain: "1",
rpcMap: {
"137":"https://polygon.drpc.org"
}
}
},
optionalNamespaces: {
eip155: {
chains: ["eip155:43114"]
}
}
})
```
## Connect to wallet and sign
Connect to the wallet to get the wallet address and sign the data; the result will be called back in the event 'connect_signResponse';
```
await universalUi.openModalAndSign(connectParams: ConnectParams,signRequest: RequestParams[]);
```
**Request Parameterseters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; Necessary information for requesting a connection, the EVM system key is 'eip155'.
If any of the requested chains are not supported by the wallet, the wallet will reject the connection;
- chains: string[]; chain id information, decimal number defined in EIP155, e.g. eip155:1
- defaultChain?: string; default chain
- rpcMap?: [chainId: string]: string; rpc information, configure rpc url to request rpc information on the chain, only support EVM system, the chain configured for RPC must be included in the chains;
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information for requesting connection, the key of EVM system is 'eip155', if the corresponding chain information is not supported by the wallet, it can still be connected; if you need to connect to a custom network, you can add the request of the custom network to this parameter, if the wallet does not support it, you can add the request of the custom network to this parameter. If you need to connect to a custom network, you can add the request for the custom network to this parameter, if the wallet already has the custom network, the information of the custom chain will be returned in the request result session; if the wallet doesn't support it, and there is no information of the custom chain in the request result session, you can add the custom chain by calling the request method again, with the method set to wallet_addEthereumChain. Add the custom chain.
- chains: string[]; chain id information
- rpcMap?: [chainId: string]: string; rpc information, configure rpc url to request rpc information on the chain, only support EVM system, configure RPC chain must be included in chains;
- signRequest - RequestParams[]; the method to request the connection and sign, only up to one method is supported at a time
- method: string; the name of the requested method, EVM systems support methods such as 'personal_sign'
- chainId: string; the ID of the chain in which the method is executed, this chainId must be included in the namespaces above
- params: unknown[] | Record`` | object | undefined; Parameters for the requested method
**Return Value**
- Promise ``
- topic: string; The session identifier
- namespaces: `Record`; namespace information for a successful connection
- chains: string[]; Chain information for the connection
- accounts: string[]; accounts information for the connection
- methods: string[]; Methods supported by the wallet in the current namespace
- defaultChain?: string; The default chain for the current session
- sessionConfig?: SessionConfig
- dappInfo: object DApp information
- name: string
- icon:string
**Example**
```typescript
// Add the signature result listener first
universalUi.on("connect_signResponse", (signResponse) => {
console.log(signResponse);
});
var session = await universalUi.openModalAndSign({
namespaces: {
eip155: {
// Please pass in as many chain ids as you need, more than one for more than one chain.
chains: ["eip155:1","eip155:137"],
defaultChain: "1",
rpcMap: {
"137":"https://polygon.drpc.org"
}
}
},
optionalNamespaces: {
eip155: {
chains: ["eip155:43114"]
}
},
sessionConfig: {
redirect: "tg://resolve"
}
},[{
method: "personal_sign",
chainId: "eip155:1",
params: [
"0x4d7920656d61696c206973206a6f686e40646f652e636f6d202d2031373237353937343537313336",
],
}])
```
## Determine if the wallet is connected
Get whether the wallet is currently connected.
**Return Value**
- boolean
**Example**
```typescript
universalUi.connected();
```
## Prepare the transaction
Methods for sending messages to the wallet, supporting signatures, transactions.
```
universalUi.request(requestArguments, chain, actionConfigurationRequest);
```
**requestArguments**
- requestArguments - object
- method: string; the name of the requested method.
- params?: unknown[] | Record`` | object | undefined; The parameters corresponding to the requested method
- returnStrategy -string'none' | `${string}://${string}`; The return strategy for the deep link in the App wallet when the user signs or rejects the request, if it is a Mini App in Telegram, it can be configured with tg://resolve, and if it's not configured here, the will take the returnStrategy passed by the init method, default is'none'
- chain: string; the chain in which the requested method will be executed, it is recommended to pass this parameter, if not it will be set to the current defaultChain
- actionConfigurationRequest - object
- modals : ('before' |'success' |'error')[] |'all' The modals of the alert display during the transaction, if request does not have this parameter set, take the parameter added during init, if init does not have this parameter set as well, then take the default value:'before'
** return value **
[return parameter details same as EVM-compatible chain for sending signatures and transactions](https://web3.okx.com/web3/build/docs/sdks/app-connect-evm-sdk#sending-signature-and-transactions)
**Examples**
[Example same EVM-compatible chain for sending signatures and transactions](https://web3.okx.com/web3/build/docs/sdks/app-connect-evm-sdk#sending-signature-and-transactions)
```typescript
let chain ='eip155:1'
var data = {}
data = {
"method": "personal_sign",
"params": [
"0x506c65617365207369676e2074686973206d65737361676520746f20636f6e6669726d20796f7572206964656e746974792e",
"0x4B0897b0513FdBeEc7C469D9aF4fA6C0752aBea7"
]
}
var personalSignResult = await universalUi.request(data, chain,'all')
//personalSignResult: 0xe8d34297c33a61"
```
## Using RPC
When EVM request method can not meet the demand, you can configure RPC to achieve more functions, in the connection wallet openModal or openModalAndSign, RPC configuration in the rpcMap.
**Example
```typescript
//Query the details of the transaction hash
let rpcData = {
method: "eth_getTransactionByHash",
params: ["0xd62fa4ea3cf7ee3bf6f5302b764490730186ed6a567c283517e8cb3c36142e1a"],
};
let result = await universalUi.request(rpcData,"eip155:137")
```
## Close connection popup
**Example**
```typescript
universalUi.closeModal();
```
## Monitoring the state changes of connection popup
**Example**
```typescript
const unsubscribe = universalUi.onModalStateChange((state)=>{
})
```
Remove the monitor when it's not needed
``` typescript const unsubscribe = universalUi.onModalStateChange(state) => { }
unsubscribe()
```
## Get information about the currently connected session
Get information about whether there is a currently connected wallet, and the connected wallets;
**Example**
``` typescript
universalUi.session.
```
## Set ui configuration items
Support to change the theme, text language setting, also can add these configurations during initialisation;
**Example**
```typescript
universalUi.uiOptions = {
language:'zh_CN',
uiPreferences: {
theme: THEME.DARK
}
};
```
## Setting the default network
In the case of multiple networks, if the developer does not specify the network where the current operation is taking place, the interaction will take place through the default network.
'setDefaultChain(chain)'
**Example**
```typescript
universalUi.setDefaultChain('eip155:1')
```
## Disconnect the wallet
**Example**
```typescript
universalUi.disconnect();
```
## Event
```typescript
// Generate universalLink
universalUi.on("display_uri", (uri) => {
console.log(uri);
});
// Session information changes (e.g. adding a custom chain) will trigger this event;
universalUi.on("session_update", (session) => {
console.log(JSON.stringify(session));
});
// Disconnecting triggers this event;
universalUi.on("session_delete", ({topic}) => {
console.log(topic);
});
// This event is triggered when a connection is made and the signature is signed.
universalUi.on("connect_signResponse", (signResponse) => {
console.log(signResponse);
});
// This event is triggered when connected to OKX Extension wallet and switch wallet
universalUi.on("accountChanged", (session) => {
if (session){
console.log(`accountChanged `, JSON.stringify(session));
}
});
```
## Error codes
Exceptions that may be thrown during connection, transaction, and disconnection.
**Exception**
| Error Code | Description |
|----------------------------------------------|-------------------------|
| OKX_CONNECT_ERROR_CODES.UNKNOWN_ERROR | Unknown Error |
| OKX_CONNECT_ERROR_CODES.ALREADY_CONNECTED_ERROR | Wallet Already Connected |
| OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR | Wallet Not Connected |
| OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR | User Rejected |
| OKX_CONNECT_ERROR_CODES.METHOD_NOT_SUPPORTED | Method Not Supported |
| OKX_CONNECT_ERROR_CODES.CHAIN_NOT_SUPPORTED | Chain Not Supported |
| OKX_CONNECT_ERROR_CODES.WALLET_NOT_SUPPORTED | Wallet Not Supported |
| OKX_CONNECT_ERROR_CODES.CONNECTION_ERROR | Connection Error |
```typescript
export enum OKX_CONNECT_ERROR_CODES {
UNKNOWN_ERROR = 0,
ALREADY_CONNECTED_ERROR = 11,
NOT_CONNECTED_ERROR = 12,
USER_REJECTS_ERROR = 300,
METHOD_NOT_SUPPORTED = 400,
CHAIN_NOT_SUPPORTED = 500,
WALLET_NOT_SUPPORTED = 600,
CONNECTION_ERROR = 700
}
```
- [Bitcoin-Compatible Chains](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-bitcoin.md)
# Bitcoin-Compatible Chains
The Bitcoin network is a peer-to-peer electronic cash system that uses blockchain technology to record all transactions without relying on a central authority or intermediary. It is maintained by thousands of nodes around the world, working together to uphold a public distributed ledger.
Key features of Bitcoin and similar blockchains (such as Fractal Bitcoin) include a fixed supply, transparent transaction records, anonymity (or pseudonymity), and a tamper-resistant design.
These chains typically employ Proof of Work (PoW) or other consensus mechanisms (like Proof of Stake) to ensure the security and consistency of the network.
As the first successful cryptocurrency, Bitcoin pioneered a new category of digital assets and laid the foundation for subsequent blockchain projects and decentralized systems.
Emerging chains like Fractal Bitcoin build on Bitcoin's foundation, aiming to improve transaction efficiency, scalability, and community governance, advancing the development of the digital currency ecosystem.
If connecting via SDK, add a "Connect OKX Wallet" button within the DApp. Clicking this button will launch the mobile App Wallet and enable interaction with the OKX Wallet, such as retrieving addresses, initiating wallet signatures, and other functions.
In addition to SDK, we also provide a UI interface.
- [SDK](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-bitcoin-sdk.md)
# SDK
## Installation and Initialization
Make sure to update to version 6.92.0 or later to get started with access: to integrate OKX Connect into your DApp, you can use npm:
```
npm install @okxconnect/universal-provider
```
Before connecting to a wallet, you need to create an object that will be used to connect to the wallet, send transactions, and so on.
`OKXUniversalProvider.init({dappMetaData: {name, icon}})`
**Request Parameterseters**
- dappMetaData - object
- name - string: the name of the application, will not be used as a unique representation.
- icon - string: URL of the application icon, must be in PNG, ICO, etc. SVG icons are not supported. SVG icons are not supported. It is better to pass a url pointing to a 180x180px PNG icon.
**Returns a value**
- OKXUniversalProvider
**Examples**
```typescript
import { OKXUniversalProvider } from "@okxconnect/universal-provider";
const okxUniversalProvider = await OKXUniversalProvider.init({
dappMetaData: {
name: "application name",
icon: "application icon url"
},
})
```
## Connecting to a wallet
Connect to the wallet to get the wallet address as an identifier and the necessary parameters for signing transactions.
`okxUniversalProvider.connect(connectParams: ConnectParams);`
**Request Parameters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; Optional information for the requested connection, the key is 'eip155' for EVM and 'btc' for BTC, if any of the requested chain is not supported by the wallet, the wallet will reject the connection;
- chains: string[]; Chain id information
- defaultChain?: string; default chain
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information for connection request, the key is 'eip155' for EVM system and 'btc' for BTC system, if the corresponding chain information is not supported by the wallet, you can still connect;
- chains: string[]; Chain id information, chain information of the wallet
- defaultChain?: string; default chain
- sessionConfig: object
- redirect: string Jump parameter after successful connection, if it is Mini App in Telegram, here can be set to Telegram's deeplink: 'tg://resolve'
**Return value**
- Promise ``
- topic: string; the session identifier;
- namespaces: `Record`; namespace information for a successful connection;
- chains: string[]; Chain information for the connection;
- accounts: string[]; accounts information for the connection;
- methods: string[]; methods supported by the wallet under the current namespace;
- defaultChain?: string; The default chain for the current session.
- sessionConfig?: SessionConfig
- dappInfo: object DApp information;
- name:string
- icon:string
- redirect?: string, the redirect parameter after successful connection;
**Example**
```typescript
var session = await okxUniversalProvider.connect({
namespaces: {
btc: {
chains: [
"btc:mainnet",
// "fractal:mainnet"
],
}
},
sessionConfig: {
redirect: "tg://resolve"
}
})
```
## Determine if the wallet is connected
Gets whether the wallet is currently connected.
**Return Value**
- boolean
**Example**
```typescript
okxUniversalProvider.connected();
```
## Send signature and transaction
First create an OKXBtcProvider object and pass OKXUniversalProvider into the constructor.
```typescript
import { OKXBtcProvider } from "@okxconnect/universal-provider";
let okxBtcProvider = new OKXBtcProvider(okxUniversalProvider)
```
**getAccount**
`okxBtcProvider.getAccount(chainId);`
***Request Parameterseters***
- chainId: the requested chain, e.g. btc:mainnet, fractal:mainnet
***Return value***
- Object
- address: string wallet address
- publicKey: string public key
***Example***
```typescript
let result = okxBtcProvider.getAccount("btc:mainnet")
// Return structure
{
"address": "038936b367d47b3796b430a31694320918afdc458d81dea9bb7dd35c0aad8bc694",
"publicKey": "03cbaedc26f03fd3ba02fc936f338e980c9e2172c5e23128877ed46827e935296f"
}
```
**Signature**
`okxBtcProvider.signMessage(chain, message, type?);`
***Request Parameterseters***
- chain - string, the chain of the requested execution method
- signStr - string the message to be signed
- type - (optional) 'ecdsa' | 'bip322-simple', default is 'ecdsa'.
***Return Value***
- Promise - string: Signature result
***Example***
```ts
let chain = "btc:mainnet"
let signStr = "data need to sign ..."
let result = okxBtcProvider.signMessage(chain, signStr)
// Return structure: "H83jZpulbMDDGUiTA4M8QNChmWwaKxwPCm8U5EBvftKlSMMzuvtVxBHlygtof5NBbdSVPiAtCvOUwZmz2vViHHU="
```
## Send
`okxBtcProvider.send(chainId, input);`
***Request Parameterseters***
- chainId - string, the chain for which the signature is requested to be executed, mandatory parameter, e.g. btc:mainnet
- input - Object
- from - string, the BTC address of the currently connected wallet
- to - string, the address of the wallet receiving BTC
- value - string, the amount of BTC to send
- satBytes - string, (optional) customised rate
- memo - string, (optional) specify outputs OP_RETURN content example
- memoPos - number, (optional) Specify the outputs OP_RETURN output position, if you pass memo then you must pass in memoPos to specify the position, otherwise memo will not take effect.
***Return Value***
- Promise - Object
- txHash The transaction hash.
***Example***
```ts
let chain = "btc:mainnet"
let input = {
from: '',
to: '1NKnZ3uAuQLnm....Y44u1efwCgTiAxBn',
value: '0.000015'
}
let result = okxBtcProvider.send(chain, input)
/**
Return structure:
{"txhash":"ff18d01ef6abed3b7fd23247a1fc457ca...f49b6bb4529a19a5fb637f18ce2e"}
*/
```
**Send Bitcoin**
`okxBtcProvider.sendBitcoin(chainId, toAddress, satoshis, options);`
***Request Parameterseters***
- chainId - string, the chain for which the signature is requested to be executed, mandatory parameter, e.g. btc:mainnet
- toAddress - string, string, accepted address
- satoshis - number, the number of satoshis to be sent
- options - Object (optional)
- feeRate - number (optional) customised fee rate
***Return Value***
- Promise - string The transaction hash
***Example***
```ts
let chain = "btc:mainnet"
let toAddress = '1NKnZ3uAuQLnmE...4u1efwCgTiAxBn' // pattern测试钱包的legacy地址
let satoshis = 17000
let options = {
feeRate: 16
}
let result = okxBtcProvider.sendBitcoin(chain, toAddress, satoshis, options)
/**
Return structure:
"ff18d01ef6abed3b7fd23247a1fc457ca...f49b6bb4529a19a5fb637f18ce2e"
*/
```
## signPsbt
`okxBtcProvider.signPsbt(chainId, psbtHex, options);`
***Request Parameterseters***
- chain - string, the chain for which the signature is requested, must be passed, e.g. btc:mainnet
- psbtHex - string, the hexadecimal string of the psbt to be signed.
- options.
- autoFinalized - boolean: whether the psbt is finalised after signing, default is true
- toSignInputs - array:
- index - number: the input to sign
- address - string: the address of the corresponding private key to be used for signing
- publicKey - string: the public key of the corresponding private key to be used for signature
- sighashTypes - number[]: (optional) sighashTypes
- disableTweakSigner - boolean: (optional) When signing and unlocking Taproot addresses, tweakSigner is used to generate signatures by default, enable this option to allow signing with the original private key.
***Return Values***
- Promise - string hex string of the signed psbt.
***Example***
```ts
let chain = "btc:mainnet"
let psbtHex = ""
let options = { autoFinalized: false }
let result = okxBtcProvider.signPsbt(chain, psbtHex, options)
/**
Return structure:
"cHNidP8BAP0GAQIAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAA/////yjWH1Uvx225V01diYYZ2i5jVAORF4nLWUWCg5bBaLQwAAAAAAD/////AwEAAAAAAAAAIlEgwSVNrUCq6hIeU+DOwJmGNi9s1CInltGUjJR5GzUoHLUBAAAAAAAAACJRIMElTa1AquoSHlPgzsCZhjYvbNQiJ5bRlIyUeRs1KBy1AIb9jA0AAAAiUSDwUTBk/h5bXDG+3/Q7lD8vEhHRSrKJFockGxONIUiI4wAAAAAAAQErAQAAAAAAAAAiUSDBJU2tQKrqEh5T4M7AmYY2L2zUIieW0ZSMlHkbNSgctQETQD9magM5RHYbdRd4KZ70FfVEAW5hw3rLjrocWIyn2Gi2P2c6Gri0E/S/wREhgjM8u5zQ3GrpcSaC8KhCRxBq5/oBFyANVBOudKlTUiKevmZzGqdVcp6Y8XbMOTfPV03fEyLOFgABASsBAAAAAAAAACJRIMElTa1AquoSHlPgzsCZhjYvbNQiJ5bRlIyUeRs1KBy1ARNA83DNEJj5u/mgUoOhCWL07enXpb6RX/WfEBh97tyrXLlA/e0CowU1fpgrKn+PQ+9Z/5/EXGwcr1UkYaqBJ0ZpKQEXIA1UE650qVNSIp6+ZnMap1Vynpjxdsw5N89XTd8TIs4WAAEBK+gDAAAAAAAAIlEg8FEwZP4eW1wxvt/0O5Q/LxIR0UqyiRaHJBsTjSFIiOMBAwSDAAAAARNBZcHpcb6YDNWF+eIcFckjF1c8C83uRmEhS/8jJQOBFkIQol8hBCTYXOFAaeu6/4o2MsS20iITiM/rAOAOBZkXC4MBFyANVBOudKlTUiKevmZzGqdVcp6Y8XbMOTfPV03fEyLOFgABBSBhbicyOEDuDCrkNNmYJn+BFwmIupR3943NAPwkeifbQAABBSBhbicyOEDuDCrkNNmYJn+BFwmIupR3943NAPwkeifbQAABBSCJNrNn1Hs3lrQwoxaUMgkYr9xFjYHeqbt901wKrYvGlAA="
*/
```
## Sign multiple Psbts
```
okxBtcProvider.signPsbts(chainId, psbtHexs, options);
```
***Request Parameters***
- chainId - string, the chain for which the signature execution is requested, mandatory parameter, e.g. btc:mainnet
- psbtHexs - string[], hexadecimal string of the psbt to be signed.
- options - object[], a hexadecimal string of the psbt to be signed.
- autoFinalized - boolean: whether the psbt is finalised after signing, default is true
- toSignInputs - array:
- index - number: the input to sign
- address - string: the address of the corresponding private key to be used for signing
- publicKey - string: the public key of the corresponding private key to be used for signature
- sighashTypes - number[]: (optional) sighashTypes
- disableTweakSigner - boolean: (optional) When signing and unlocking Taproot addresses, tweakSigner is used to generate signatures by default, enable this option to allow signing with the original private key.
***Return Values***
- Promise - string[] Hexadecimal string of the signed psbt.
***Example***
```ts
let chain = "btc:mainnet"
let psbtHexs = [""]
let options = [{ autoFinalized: false }]
let result = okxBtcProvider.signPsbts(chain, psbtHexs, options)
/**
Return structure:
["cHNidP8BAP0GAQIAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAA/////yjWH1Uvx225V01diYYZ2i5jVAORF4nLWUWCg5bBaLQwAAAAAAD/////AwEAAAAAAAAAIlEgwSVNrUCq6hIeU+DOwJmGNi9s1CInltGUjJR5GzUoHLUBAAAAAAAAACJRIMElTa1AquoSHlPgzsCZhjYvbNQiJ5bRlIyUeRs1KBy1AIb9jA0AAAAiUSDwUTBk/h5bXDG+3/Q7lD8vEhHRSrKJFockGxONIUiI4wAAAAAAAQErAQAAAAAAAAAiUSDBJU2tQKrqEh5T4M7AmYY2L2zUIieW0ZSMlHkbNSgctQETQD9magM5RHYbdRd4KZ70FfVEAW5hw3rLjrocWIyn2Gi2P2c6Gri0E/S/wREhgjM8u5zQ3GrpcSaC8KhCRxBq5/oBFyANVBOudKlTUiKevmZzGqdVcp6Y8XbMOTfPV03fEyLOFgABASsBAAAAAAAAACJRIMElTa1AquoSHlPgzsCZhjYvbNQiJ5bRlIyUeRs1KBy1ARNA83DNEJj5u/mgUoOhCWL07enXpb6RX/WfEBh97tyrXLlA/e0CowU1fpgrKn+PQ+9Z/5/EXGwcr1UkYaqBJ0ZpKQEXIA1UE650qVNSIp6+ZnMap1Vynpjxdsw5N89XTd8TIs4WAAEBK+gDAAAAAAAAIlEg8FEwZP4eW1wxvt/0O5Q/LxIR0UqyiRaHJBsTjSFIiOMBAwSDAAAAARNBZcHpcb6YDNWF+eIcFckjF1c8C83uRmEhS/8jJQOBFkIQol8hBCTYXOFAaeu6/4o2MsS20iITiM/rAOAOBZkXC4MBFyANVBOudKlTUiKevmZzGqdVcp6Y8XbMOTfPV03fEyLOFgABBSBhbicyOEDuDCrkNNmYJn+BFwmIupR3943NAPwkeifbQAABBSBhbicyOEDuDCrkNNmYJn+BFwmIupR3943NAPwkeifbQAABBSCJNrNn1Hs3lrQwoxaUMgkYr9xFjYHeqbt901wKrYvGlAA="]
*/
```
## Sign and Push psbt
> required App: >= 6.93.0
`okxBtcProvider.signAndPushPsbt(chainId, psbtHex, options);`
***Request Parameterseters***
- chain - string, the chain for which the signature is requested, must be passed, e.g. btc:mainnet
- psbtHex - string, the hexadecimal string of the psbt to be signed.
- options: - object
- autoFinalized - boolean: whether the psbt is finalised after signing, default is true
- toSignInputs - array:
- index - number: the input to sign
- address - string: the address of the corresponding private key to be used for signing
- publicKey - string: the public key of the corresponding private key to be used for signing
- sighashTypes - number[]: (optional) sighashTypes
- disableTweakSigner - boolean: (optional) When signing and unlocking Taproot addresses, tweakSigner is used to generate signatures by default, enable this option to allow signing with the original private key.
***Returns a value***
- Promise - object
- txhash Transaction hash
- signature Hexadecimal string of the signed psbt.
***Example***
```ts
let chain = "btc:mainnet"
let psbtHex = ""
let options = { autoFinalized: false }
let result = okxBtcProvider.signAndPushPsbt(chain, psbtHex, options)
/**
Return structure:
{
txhash: "",
signature: ""
}
*/
```
## Disconnect wallet
Disconnect the connected wallet and delete the current session. If you want to switch the connected wallet, please disconnect the current wallet first.
```typescript
okxUniversalProvider.disconnect();
```
## Event
```typescript
// Generate universalLink
okxUniversalProvider.on('display_uri', (uri) => {
console.log(uri);
});
// Session information changes will trigger this event;
okxUniversalProvider.on('session_update', (session) => {
console.log(JSON.stringify(session)); // Session information changes (e.g., adding a custom chain).
});
// Disconnecting triggers this event;
okxUniversalProvider.on('session_delete', ({topic}) => {
console.log(topic);
});
```
## Error codes
Exceptions that may be thrown during connection, transaction, and disconnection.
**Exception**
| Error Code | Description |
|----------------------------------------------|-------------------------|
| OKX_CONNECT_ERROR_CODES.UNKNOWN_ERROR | Unknown Error |
| OKX_CONNECT_ERROR_CODES.ALREADY_CONNECTED_ERROR | Wallet Already Connected |
| OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR | Wallet Not Connected |
| OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR | User Rejected |
| OKX_CONNECT_ERROR_CODES.METHOD_NOT_SUPPORTED | Method Not Supported |
| OKX_CONNECT_ERROR_CODES.CHAIN_NOT_SUPPORTED | Chain Not Supported |
| OKX_CONNECT_ERROR_CODES.WALLET_NOT_SUPPORTED | Wallet Not Supported |
| OKX_CONNECT_ERROR_CODES.CONNECTION_ERROR | Connection Error |
```typescript
export enum OKX_CONNECT_ERROR_CODES {
UNKNOWN_ERROR = 0,
ALREADY_CONNECTED_ERROR = 11,
NOT_CONNECTED_ERROR = 12,
USER_REJECTS_ERROR = 300,
METHOD_NOT_SUPPORTED = 400,
CHAIN_NOT_SUPPORTED = 500,
WALLET_NOT_SUPPORTED = 600,
CONNECTION_ERROR = 700
}
```
- [UI](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-bitcoin-ui.md)
# UI
## Installation and Initialization
Make sure to update the OKX App to version 6.92.0 or later to begin access:
To integrate OKX Connect into your DApp, you can use npm:
```
npm install @okxconnect/ui
npm install @okxconnect/universal-provider
```
Before connecting to the wallet, you need to create an object that can provide a UI interface for subsequent operations such as connecting to the wallet and sending transactions.
```typescript
OKXUniversalConnectUI.init(dappMetaData, actionsConfiguration, uiPreferences, language)
```
**Request Parameterseters**
- dappMetaData - object
- name - string: The name of the application, will not be used as a unique representation.
- icon - string: URL of the application icon, must be in PNG, ICO, etc. SVG icons are not supported. SVG icons are not supported. It is best to pass a url pointing to a 180x180px PNG icon.
- actionsConfiguration - object
- modals - ('before' | 'success' | 'error')[] | 'all' The modes of displaying alerts during transaction, defaults to 'before'.
- returnStrategy -string 'none' | `${string}://${string}`; for app wallet, specify the return strategy for the deep link when the user signs/rejects the request, if it is in telegram, you can configure tg://resolve
- uiPreferences -object
- theme - Theme can be: THEME.DARK, THEME.LIGHT, 'SYSTEM'.
- language - 'en_US' | 'ru_RU' | 'zh_CN' | 'ar_AE' | 'cs_CZ' | 'de_DE' | 'es_ES' | 'es_LAT' | 'fr_FR' | 'id_ID' | 'it_IT' | 'nl_NL' | 'pl_PL' | 'pt_BR' | 'pt_PT' | 'ro_RO' | 'tr_TR' | 'uk_UA' | 'vi_VN'.
, defaults to en_US
**Return value**
- OKXUniversalConnectUI
**Example**
```typescript
import { OKXUniversalConnectUI } from "@okxconnect/ui";
const okxUniversalConnectUI = await OKXUniversalConnectUI.init({
dappMetaData: {
icon: "https://static.okx.com/cdn/assets/imgs/247/58E63FEA47A2B7D7.png",
name: "OKX Connect UI Demo"
},
actionsConfiguration: {
returnStrategy: 'tg://resolve',
modals:"all"
},
language: "en_US",
uiPreferences: {
theme: THEME.LIGHT
},
});
```
## Connecting to OKX wallet
Connecting to a wallet goes to get the wallet address as an identifier and the necessary parameters used to sign the transaction;
```typescript
okxUniversalConnectUI.connect(connectParams: ConnectParams);
```
**Request Parameterseters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; Optional information for requesting a connection, the key is 'eip155' for EVM, or 'btc' for BTC, if any of the requested chain is not supported by the wallet, the wallet will reject the connection;
- chains: string[]; Chain id information
- defaultChain?: string; default chain
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information for connection request, the key is 'eip155' for EVM system and 'btc' for BTC system, if the corresponding chain information is not supported by the wallet, you can still connect;
- chains: string[]; Chain id information, chain information of the wallet
- defaultChain?: string; default chain
- sessionConfig: object
- redirect: string Jump parameter after successful connection, if it is Mini App in Telegram, here can be set to Telegram's deeplink: 'tg://resolve'
**Return value**
- Promise ``
- topic: string; the session identifier
- namespaces: `Record`; namespace information for a successful connection
- chains: string[]; Chain information for the connection
- accounts: string[]; accounts information for the connection
- methods: string[]; methods supported by the wallet under the current namespace
- defaultChain?: string; The default chain for the current session
- sessionConfig?: SessionConfig
- dappInfo: object DApp information
- name:string
- icon:string
- redirect?: string, the redirect parameter after successful connection
**Example**
```typescript
var session = await okxUniversalConnectUI.connect({
namespaces: {
btc: {
chains: [
"btc:mainnet",
// "fractal:mainnet"
],
}
},
sessionConfig: {
redirect: "tg://resolve"
}
})
```
## Connect to wallet and sign
Connect to the wallet to get the wallet address and sign the data; the result will be called back in the event 'connect_signResponse'
```
await okxUniversalConnectUI.openModalAndSign(connectParams: ConnectParams, signRequest: RequestParams[]);
```
**Request Parameters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; Optional information for requesting a connection, the key for the BTC system is 'btc', if any of the requested chains is not supported by the wallet, the wallet will reject the connection;
- chains: string[]; chain id information
- defaultChain?: string; default chain
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information of the requested connection, the key of BTC is 'btc', if the corresponding chain information is not supported by the wallet, it can still be connected;
- chains: string[]; chain id information
- defaultChain?: string; default chain
- sessionConfig: object
- redirect: string The jump parameter after successful connection, if it is Mini App in Telegram, here can be set to Telegram's deeplink: 'tg://resolve'
- signRequest - RequestParams[]; the method to request the connection and sign the request, at most one method can be supported at the same time;
- method: string; the name of the requested method, the BTC system supports methods such as 'btc_signMessage';
- chainId: string; the ID of the chain in which the method is executed, the chainId must be included in the namespaces above;
- params: unknown[] | Record`` | object | undefined; Parameters corresponding to the requested method;
**Return Value**
- Promise ``
- topic: string; the session identifier;
- namespaces: `Record`; namespace information for a successful connection;
- chains: string[]; Chain information for the connection;
- accounts: string[]; accounts information for the connection;
- methods: string[]; Methods supported by the wallet in the current namespace;
- defaultChain?: string; The default chain for the current session.
- sessionConfig?: SessionConfig
- dappInfo: object DApp information;
- name: string
- icon:string
**Example**
```typescript
// Add the signature result listener first
okxUniversalConnectUI.on("connect_signResponse", (signResponse) => {
console.log(signResponse);
});
var session = await okxUniversalConnectUI.openModalAndSign({
namespaces: {
btc: {
chains: [
"btc:mainnet",
// "fractal:mainnet"
],
}
},
sessionConfig: {
redirect: "tg://resolve"
}
},
[
{
method: "btc_signMessage",
chainId: "btc:mainnet",
params: {
message: "Welcome to BTC"
}
}
])
```
## Determine if the wallet is connected
Gets whether the wallet is currently connected.
**Return Value**
- boolean
**Example**
```typescript
universalUi.connected();
```
## Prepare to trade
First create an OKXBtcProvider object, the constructor passes in okxUniversalConnectUI
```typescript
import { OKXBtcProvider } from '@okxconnect/universal-provider';
let okxBtcProvider = new OKXBtcProvider(okxUniversalConnectUI)
```
## Get wallet account information
```typescript
okxBtcProvider.getAccount(chainId);
```
***Request Parameterseters***
- chainId: the requested chain, e.g. btc:mainnet, fractal:mainnet
***Return Value***
- Object
- address: string wallet address
- publicKey: string public key
***Example***
```typescript
let result = okxBtcProvider.getAccount('btc:mainnet')
// return structure
{
"address": "038936b367d47b3796b430a31694320918afdc458d81dea9bb7dd35c0aad8bc694",
"publicKey": "03cbaedc26f03fd3ba02fc936f338e980c9e2172c5e23128877ed46827e935296f"
}
```
## signMessage
```typescript
okxBtcProvider.signMessage(chain, message, type?);
```
***Request Parameterseters***
- chain - string, the chain of the requested execution method
- signStr - string the message to be signed
- type - (optional) 'ecdsa' | 'bip322-simple', default is 'ecdsa'.
***Return Value***
- Promise - string: Signature result
***Example***
```ts
let chain = "btc:mainnet"
let signStr = "data need to sign ..."
let result = okxBtcProvider.signMessage(chain, signStr)
//Return structure: "H83jZpulbMDDGUiTA4M8QNChmWwaKxwPCm8U5EBvftKlSMMzuvtVxBHlygtof5NBbdSVPiAtCvOUwZmz2vViHHU="
```
## Send Bitcoin
```
okxBtcProvider.sendBitcoin(chainId, toAddress, satoshis, options);
```
***Request Parameterseters***
- chainId - string, the chain for which the signature is requested to be executed, mandatory parameter, e.g. btc:mainnet
- toAddress - string, string, accepted address
- satoshis - number, the number of satoshis to be sent
- options - Object (optional)
- feeRate - number (optional) customised fee rate
***Return Value***
- Promise - string The transaction hash
***Example***
```ts
let chain = "btc:mainnet"
let toAddress = '1NKnZ3uAuQLnmE...4u1efwCgTiAxBn'
let satoshis = 17000
let options = {
feeRate: 16
}
let result = okxBtcProvider.sendBitcoin(chain, toAddress, satoshis, options)
/**
Return structure:
"ff18d01ef6abed3b7fd23247a1fc457ca...f49b6bb4529a19a5fb637f18ce2e"
*/
```
## signPsbt
```
okxBtcProvider.signPsbt(chainId, psbtHex, options);
```
***Request Parameterseters***
- chain - string, the chain for which the signature is requested, must be passed, e.g. btc:mainnet
- psbtHex - string, the hexadecimal string of the psbt to be signed.
- options.
- autoFinalized - boolean: whether the psbt is finalised after signing, default is true
- toSignInputs - array:
- index - number: the input to sign
- address - string: the address of the corresponding private key to be used for signing
- publicKey - string: the public key of the corresponding private key to be used for signature
- sighashTypes - number[]: (optional) sighashTypes
- disableTweakSigner - boolean: (optional) When signing and unlocking Taproot addresses, tweakSigner is used to generate signatures by default, enable this option to allow signing with the original private key.
***Return Values***
- Promise - string hex string of the signed psbt.
***Example***
```ts
let chain = "btc:mainnet"
let psbtHex = ""
let options = { autoFinalized: false }
let result = okxBtcProvider.signPsbt(chain, psbtHex, options)
/**
Return structure:
"cHNidP8BAP0GAQIAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAA/////yjWH1Uvx225V01diYYZ2i5jVAORF4nLWUWCg5bBaLQwAAAAAAD/////AwEAAAAAAAAAIlEgwSVNrUCq6hIeU+DOwJmGNi9s1CInltGUjJR5GzUoHLUBAAAAAAAAACJRIMElTa1AquoSHlPgzsCZhjYvbNQiJ5bRlIyUeRs1KBy1AIb9jA0AAAAiUSDwUTBk/h5bXDG+3/Q7lD8vEhHRSrKJFockGxONIUiI4wAAAAAAAQErAQAAAAAAAAAiUSDBJU2tQKrqEh5T4M7AmYY2L2zUIieW0ZSMlHkbNSgctQETQD9magM5RHYbdRd4KZ70FfVEAW5hw3rLjrocWIyn2Gi2P2c6Gri0E/S/wREhgjM8u5zQ3GrpcSaC8KhCRxBq5/oBFyANVBOudKlTUiKevmZzGqdVcp6Y8XbMOTfPV03fEyLOFgABASsBAAAAAAAAACJRIMElTa1AquoSHlPgzsCZhjYvbNQiJ5bRlIyUeRs1KBy1ARNA83DNEJj5u/mgUoOhCWL07enXpb6RX/WfEBh97tyrXLlA/e0CowU1fpgrKn+PQ+9Z/5/EXGwcr1UkYaqBJ0ZpKQEXIA1UE650qVNSIp6+ZnMap1Vynpjxdsw5N89XTd8TIs4WAAEBK+gDAAAAAAAAIlEg8FEwZP4eW1wxvt/0O5Q/LxIR0UqyiRaHJBsTjSFIiOMBAwSDAAAAARNBZcHpcb6YDNWF+eIcFckjF1c8C83uRmEhS/8jJQOBFkIQol8hBCTYXOFAaeu6/4o2MsS20iITiM/rAOAOBZkXC4MBFyANVBOudKlTUiKevmZzGqdVcp6Y8XbMOTfPV03fEyLOFgABBSBhbicyOEDuDCrkNNmYJn+BFwmIupR3943NAPwkeifbQAABBSBhbicyOEDuDCrkNNmYJn+BFwmIupR3943NAPwkeifbQAABBSCJNrNn1Hs3lrQwoxaUMgkYr9xFjYHeqbt901wKrYvGlAA="
*/
```
## Sign multiple Psbts
```
okxBtcProvider.signPsbts(chainId, psbtHexs, options);
```
***Request Parameters***
- chainId - string, the chain for which the signature execution is requested, mandatory parameter, e.g. btc:mainnet
- psbtHexs - string[], hexadecimal string of the psbt to be signed.
- options - object[], a hexadecimal string of the psbt to be signed.
- autoFinalized - boolean: whether the psbt is finalised after signing, default is true
- toSignInputs - array:
- index - number: the input to sign
- address - string: the address of the corresponding private key to be used for signing
- publicKey - string: the public key of the corresponding private key to be used for signing
- sighashTypes - number[]: (optional) sighashTypes
- disableTweakSigner - boolean: (optional) When signing and unlocking Taproot addresses, tweakSigner is used to generate signatures by default, enable this option to allow signing with the original private key.
***Return Value***
- Promise - string[] hex string of signed psbt
***Example***
```ts
let chain = "btc:mainnet"
let psbtHexs = [""]
let options = [{ autoFinalized: false }]
let result = okxBtcProvider.signPsbts(chain, psbtHexs, options)
/**
Return structure:
["cHNidP8BAP0GAQIAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAA/////yjWH1Uvx225V01diYYZ2i5jVAORF4nLWUWCg5bBaLQwAAAAAAD/////AwEAAAAAAAAAIlEgwSVNrUCq6hIeU+DOwJmGNi9s1CInltGUjJR5GzUoHLUBAAAAAAAAACJRIMElTa1AquoSHlPgzsCZhjYvbNQiJ5bRlIyUeRs1KBy1AIb9jA0AAAAiUSDwUTBk/h5bXDG+3/Q7lD8vEhHRSrKJFockGxONIUiI4wAAAAAAAQErAQAAAAAAAAAiUSDBJU2tQKrqEh5T4M7AmYY2L2zUIieW0ZSMlHkbNSgctQETQD9magM5RHYbdRd4KZ70FfVEAW5hw3rLjrocWIyn2Gi2P2c6Gri0E/S/wREhgjM8u5zQ3GrpcSaC8KhCRxBq5/oBFyANVBOudKlTUiKevmZzGqdVcp6Y8XbMOTfPV03fEyLOFgABASsBAAAAAAAAACJRIMElTa1AquoSHlPgzsCZhjYvbNQiJ5bRlIyUeRs1KBy1ARNA83DNEJj5u/mgUoOhCWL07enXpb6RX/WfEBh97tyrXLlA/e0CowU1fpgrKn+PQ+9Z/5/EXGwcr1UkYaqBJ0ZpKQEXIA1UE650qVNSIp6+ZnMap1Vynpjxdsw5N89XTd8TIs4WAAEBK+gDAAAAAAAAIlEg8FEwZP4eW1wxvt/0O5Q/LxIR0UqyiRaHJBsTjSFIiOMBAwSDAAAAARNBZcHpcb6YDNWF+eIcFckjF1c8C83uRmEhS/8jJQOBFkIQol8hBCTYXOFAaeu6/4o2MsS20iITiM/rAOAOBZkXC4MBFyANVBOudKlTUiKevmZzGqdVcp6Y8XbMOTfPV03fEyLOFgABBSBhbicyOEDuDCrkNNmYJn+BFwmIupR3943NAPwkeifbQAABBSBhbicyOEDuDCrkNNmYJn+BFwmIupR3943NAPwkeifbQAABBSCJNrNn1Hs3lrQwoxaUMgkYr9xFjYHeqbt901wKrYvGlAA="]
*/
```
## Sign and Push psbt
> required App: >= 6.93.0
```typescript
okxBtcProvider.signAndPushPsbt(chainId, psbtHex, options);
```
***Request Parameterseters***
- chain - string, the chain for which the signature is requested, must be passed, e.g. btc:mainnet
- psbtHex - string, hexadecimal string of the psbt to be signed.
- options: - object
- autoFinalized - boolean: whether the psbt is finalised after signing, default is true
- toSignInputs - array:
- index - number: the input to sign
- address - string: the address of the corresponding private key to be used for signing
- publicKey - string: the public key of the corresponding private key to be used for signing
- sighashTypes - number[]: (optional) sighashTypes
- disableTweakSigner - boolean: (optional) When signing and unlocking Taproot addresses, tweakSigner is used to generate signatures by default, enable this option to allow signing with the original private key.
***Return Value***
- Promise - object
- txhash Transaction hash
- signature hex string of signed psbt
***Example***
```ts
let chain = "btc:mainnet"
let psbtHex = ""
let options = { autoFinalized: false }
let result = okxBtcProvider.signAndPushPsbt(chain, psbtHex, options)
/**
Return structure:
{
txhash: "",
signature: ""
}
*/
```
## Disconnect wallet
Disconnect the connected wallet and delete the current session. If you want to switch the connected wallet, please disconnect the current wallet first.
```typescript
okxUniversalConnectUI.disconnect();
```
## Event
```typescript
// Generate universalLink
okxUniversalConnectUI.on("display_uri", (uri) => {
console.log(uri);
});
// Session information changes will trigger this event;
okxUniversalConnectUI.on("session_update", (session) => {
console.log(JSON.stringify(session));
});
// Disconnecting triggers this event;
okxUniversalConnectUI.on("session_delete", ({topic}) => {
console.log(topic);
});
// This event is triggered when a connection is made and the signature is signed.
okxUniversalConnectUI.on("connect_signResponse", (signResponse) => {
console.log(signResponse);
});
```
## Error codes
Exceptions that may be thrown during connection, transaction, and disconnection.
**Exception**
| Error Code | Description |
|----------------------------------------------|-------------------------|
| OKX_CONNECT_ERROR_CODES.UNKNOWN_ERROR | Unknown Error |
| OKX_CONNECT_ERROR_CODES.ALREADY_CONNECTED_ERROR | Wallet Already Connected |
| OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR | Wallet Not Connected |
| OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR | User Rejected |
| OKX_CONNECT_ERROR_CODES.METHOD_NOT_SUPPORTED | Method Not Supported |
| OKX_CONNECT_ERROR_CODES.CHAIN_NOT_SUPPORTED | Chain Not Supported |
| OKX_CONNECT_ERROR_CODES.WALLET_NOT_SUPPORTED | Wallet Not Supported |
| OKX_CONNECT_ERROR_CODES.CONNECTION_ERROR | Connection Error |
```typescript
export enum OKX_CONNECT_ERROR_CODES {
UNKNOWN_ERROR = 0,
ALREADY_CONNECTED_ERROR = 11,
NOT_CONNECTED_ERROR = 12,
USER_REJECTS_ERROR = 300,
METHOD_NOT_SUPPORTED = 400,
CHAIN_NOT_SUPPORTED = 500,
WALLET_NOT_SUPPORTED = 600,
CONNECTION_ERROR = 700
}
```
- [Solana-Compatible Chains](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-solana.md)
# Solana-Compatible Chains
Solana is a high-performance blockchain platform committed to providing fast, secure, and scalable solutions for decentralized applications and cryptocurrencies. The platform utilizes an innovative consensus algorithm called Proof of History (PoH) that can handle tens of thousands of transactions per second (TPS) while maintaining decentralization and security. Overall, Solana aims to achieve mass adoption of blockchain through its unique technological advantages, catering to various complex decentralized applications and global financial systems.
Common Solana-compatible networks include SOON, Sonic, etc.
If connecting via SDK, add a "Connect OKX Wallet" button within the DApp. Clicking this button will launch the mobile App Wallet and enable interaction with the OKX Wallet, such as retrieving addresses, initiating wallet signatures, and other functions.
In addition to SDK, we also provide a UI interface.
- [SDK](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-solana-sdk.md)
# SDK
## Installation and Initialization
Make sure to update the OKX App to version 6.90.1 or later to start integrating OKX Connect into your DApp can be done using npm:
```
npm install @okxconnect/solana-provider
```
Before connecting the wallet, you need to create an object for subsequent wallet connections, transaction submissions, and other operations.
```
OKXUniversalProvider.init({dappMetaData: {name, icon}})
```
**Request Parameters**
- dappMetaData - object
- name - string: the name of the application, will not be used as a unique representation.
- icon - string: URL of the application icon, must be in PNG, ICO, etc. SVG icons are not supported. SVG icons are not supported. It is better to pass a url pointing to a 180x180px PNG icon.
**Returns a value**
- OKXUniversalProvider
**Example**
```typescript
import { OKXUniversalProvider } from "@okxconnect/universal-provider";
const okxUniversalProvider = await OKXUniversalProvider.init({
dappMetaData: {
name: "application name",
icon: "application icon url"
},
})
```
## Connect to Wallet
Connect the wallet to obtain the wallet address, which serves as an identifier and is necessary for signing transactions.
```
okxUniversalProvider.connect(connectParams: ConnectParams);
```
**Request Parameters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; information necessary to request a connection, the key for Solana is "solana" .
If any of the requested chains are not supported by the wallet, the wallet will reject the connection;
- chains: string[]; Chain id information
- defaultChain?: string; default chain
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information for requesting a connection, the key for Solana is "solana".
If the corresponding chain information is not supported by the wallet, the connection can still be made
- chains: string[]; Chain id information
- defaultChain?: string; default chain
- sessionConfig: object
- redirect: string Jump parameter after successful connection, if it is Mini App in Telegram, here can be set deeplink: "tg://resolve" in Telegram
- Promise ``
- topic: string; The session identifier;
- namespaces: `Record`; namespace information for a successful connection
- chains: string[]; Chain information for the connection
- accounts: string[]; accounts information for the connection
- methods: string[]; methods supported by the wallet under the current namespace
- defaultChain?: string; default chain for the current session
- sessionConfig?: SessionConfig
- dappInfo: object DApp information
- name:string
- icon:string
- redirect?: string, the redirect parameter after successful connection
**Example**
```typescript
var session = await okxUniversalProvider.connect({
namespaces: {
solana: {
chains: ["solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", // solana mainnet
// "sonic:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",// sonic mainnet
// "solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z",// solana testnet
// "sonic:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z",// sonic testnet ;
],
}
},
sessionConfig: {
redirect: "tg://resolve"
}
})
```
## Determine if the wallet is connected
Gets whether the wallet is currently connected
**Return Value**
- boolean
**Example**
```typescript
okxUniversalProvider.connected();
```
## Sending Signature and Transactions
This method allows sending messages to the wallet, supporting signatures, transactions
First create an OKXSolanaProvider object, passing OKXUniversalProvider into the constructor
```typescript
import { OKXSolanaProvider } from "@okxconnect/solana-provider/OKXSolanaProvider";
let okxSolanaProvider = new OKXSolanaProvider(okxUniversalProvider)
```
## Signature
`okxSolanaProvider.signMessage(message, chain);`
**Request Parameters**
- message - string, the message to be signed
- chain: string, the chain for which the signature is requested, recommended to pass this parameter; mandatory when connecting multiple chains; if a chain is not passed, it will be treated as solana:mainnet or sonic:mainnet
**Return Value**
- Promise - object
- publicKey:string wallet address
- signature:Uint8Array Signature result
## Signature for single transaction
```
okxSolanaProvider.signTransaction(transaction, chain);
```
**Request Parameterseters**
- transaction - Transaction | VersionedTransaction transaction data object
- chain: string, the chain for which the signature execution is requested, it is recommended to pass this parameter; it is mandatory to pass this parameter when connecting multiple chains; connecting a chain without passing it will be handled according to solana:mainnet or sonic:mainnet
**Return value**
- Promise - Transaction | VersionedTransaction signed transaction object
***Signs multiple transactions***
`okxSolanaProvider.signAllTransactions(transactions, chain);`
**Request Parameterseters**
- transactions - [Transaction | VersionedTransaction] Array of transaction data objects
- chain: string, the chain for which the signature execution is requested, it is recommended to pass this parameter; it is mandatory to pass this parameter when connecting multiple chains; connecting a chain without passing it will be handled according to solana:mainnet or sonic:mainnet
**Return value**
- Promise - [Transaction | VersionedTransaction] An array of signed transaction objects
## Signs a transaction and broadcasts on chain
```
okxSolanaProvider.signAllTransactions(transactions, chain);
```
**Request Parameters
- transactions - Transaction | VersionedTransaction transaction data object
- chain: string, the chain for which the signature execution is requested, it is recommended to pass this parameter; it is mandatory to pass this parameter when connecting multiple chains; connecting a chain without passing it will be handled according to solana:mainnet or sonic:mainnet;
**Return Value
- Promise - string transactionhash
## get wallet address and pubKey
```
okxSolanaProvider.getAccount(chain);
```
**Request Parameters**
- chain: string, get the chain id of the wallet address, if not passed then the first connected svm address will be taken by default
**Return Value**
- Object
- address: string wallet address
- publicKey: PublicKey
**Example**
```typescript
// Signing a transfer transaction on solana mainnet
let provider = new OKXSolanaProvider(okxUniversalProvider)
const transaction = new Transaction({
feePayer: new PublicKey(provider.getAccount().address),
recentBlockhash: "xNWbUfdEPktMsZQHY6Zk5RJqamWFcTKasekjr7c3wFX",
}).add(SystemProgram.transfer(
{
fromPubkey: new PublicKey(provider.getAccount().address),
toPubkey: new PublicKey(provider.getAccount().address),
lamports: 1000,
}
))
let result = await provider.signTransaction(transaction, "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp")
```
## Disconnect wallet
Disconnect from a connected wallet and delete the current session. If you want to switch wallets, disconnect from the current wallet first
```typescript
okxUniversalProvider.disconnect();
```
## Event
```typescript
// Generate universalLink
okxUniversalProvider.on('display_uri', (uri) => {
console.log(uri);
});
// Session information changes will trigger this event;
okxUniversalProvider.on('session_update', (session) => {
console.log(JSON.stringify(session)); // Session information changes (e.g., adding a custom chain).
});
// Disconnecting triggers this event;
okxUniversalProvider.on('session_delete', ({topic}) => {
console.log(topic);
});
```
## Error codes
Exceptions that may be thrown during connection, transaction, and disconnection
**Exception**
| Error Code | Description |
|----------------------------------------------|-------------------------|
| OKX_CONNECT_ERROR_CODES.UNKNOWN_ERROR | Unknown Error |
| OKX_CONNECT_ERROR_CODES.ALREADY_CONNECTED_ERROR | Wallet Already Connected |
| OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR | Wallet Not Connected |
| OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR | User Rejected |
| OKX_CONNECT_ERROR_CODES.METHOD_NOT_SUPPORTED | Method Not Supported |
| OKX_CONNECT_ERROR_CODES.CHAIN_NOT_SUPPORTED | Chain Not Supported |
| OKX_CONNECT_ERROR_CODES.WALLET_NOT_SUPPORTED | Wallet Not Supported |
| OKX_CONNECT_ERROR_CODES.CONNECTION_ERROR | Connection Error |
```typescript
export enum OKX_CONNECT_ERROR_CODES {
UNKNOWN_ERROR = 0,
ALREADY_CONNECTED_ERROR = 11,
NOT_CONNECTED_ERROR = 12,
USER_REJECTS_ERROR = 300,
METHOD_NOT_SUPPORTED = 400,
CHAIN_NOT_SUPPORTED = 500,
WALLET_NOT_SUPPORTED = 600,
CONNECTION_ERROR = 700
}
```
- [UI](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-solana-ui.md)
# UI
## Installation and Initialisation
Make sure to update the OKX App to version 6.90.1 or later to start accessing:
To integrate OKX Connect into your DApp, you can use npm:
```
npm install @okxconnect/ui
npm install @okxconnect/solana-provider
```
Before connecting to the wallet, you need to create an object that can provide a UI interface for subsequent operations such as connecting to the wallet and sending transactions.
```
OKXUniversalConnectUI.init(dappMetaData, actionsConfiguration, uiPreferences, language)
```
**Request Parameterseters**
- dappMetaData - object
- name - string: name of the application, will not be used as a unique representation
- icon - string: URL of the application icon, must be in PNG, ICO, etc. SVG icons are not supported. SVG icons are not supported. It is best to pass a url pointing to a 180x180px PNG icon.
- actionsConfiguration - object
- modals - ('before' | 'success' | 'error')[] | 'all' The modes of displaying alerts during transaction, defaults to 'before'.
- returnStrategy -string 'none' | `${string}://${string}`; for app wallet, specify the return strategy of the deep link when the user signs/rejects the request, in case of telegram, you can configure tg://resolve
- uiPreferences -object
- theme - Theme can be: THEME.DARK, THEME.LIGHT, 'SYSTEM'.
- language - 'en_US' | 'ru_RU' | 'zh_CN' | 'ar_AE' | 'cs_CZ' | 'de_DE' | 'es_ES' | 'es_LAT' | 'fr_FR' | 'id_ID' | 'it_IT' | 'nl_NL' | 'pl_PL' | 'pt_BR' | 'pt_PT' | 'ro_RO' | 'tr_TR' | 'uk_UA' | 'vi_VN'.
, defaults to en_US
**Return value**
- OKXUniversalConnectUI
**Examples**
```typescript
import { OKXUniversalConnectUI } from '@okxconnect/ui';
const universalUi = await OKXUniversalConnectUI.init({
dappMetaData: {
icon: 'https://static.okx.com/cdn/assets/imgs/247/58E63FEA47A2B7D7.png',
name: 'OKX Connect Demo'
},
actionsConfiguration: {
returnStrategy: 'tg://resolve',
modals: 'all',
tmaReturnUrl:'back'
},
language: 'en_US',
uiPreferences: {
theme: THEME.LIGHT
},
});
// Switching the plugin to connect to the wallet fires this event;
universalUi.on('accountChanged', (session) => {
if (session){
console.log(`accountChanged `, JSON.stringify(session));
}
});
```
## Connect to the wallet
Connects to the wallet to get the wallet address as an identifier and the necessary parameters used to sign the transaction.
```
universalUi.openModal(connectParams: ConnectParams);
```
**Request Parameterseters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; Necessary information for the requested connection, the Solana line has a key of 'solana'
If any of the requested chains are not supported by the wallet, the wallet will reject the connection
- chains: string[]; chain id information
- defaultChain?: string; default chain
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information about the requested connection, Solana key is 'solana'.
If the corresponding chain information is not supported by the wallet, the connection can still be made
- chains: string[]; chain id information
- defaultChain?: string; default chain
**Return Value**
- Promise ``
- topic: string; The session identifier;
- namespaces: `Record`; namespace information for a successful connection
- chains: string[]; Chain information for the connection
- accounts: string[]; accounts information for the connection
- methods: string[]; Methods supported by the wallet in the current namespace
- defaultChain?: string; The default chain for the current session
- sessionConfig?: SessionConfig
- dappInfo: object DApp information
- name: string
- icon:string
**Example**
```typescript
var session = await universalUi.openModal({
namespaces: {
solana: {
chains: ['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', // solana mainnet
// "sonic:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",// sonic mainnet
// 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z', // solana testnet
// 'sonic:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z',// sonic testnet ;
],
}
}
})
```
## Connect to wallet and sign
Connect to the wallet to get the wallet address and sign the data; the result will be called back in the event 'connect_signResponse'
```
await universalUi.openModalAndSign(connectParams: ConnectParams,signRequest: RequestParams[]);
```
**Request Parameters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; information necessary to request a connection, the Solana key is 'solana'.
If any of the requested chains are not supported by the wallet, the wallet will reject the connection
- chains: string[]; chain id information
- defaultChain?: string; default chain
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information about the requested connection, Solana key is 'solana'.
If the corresponding chain information is not supported by the wallet, the connection can still be made
- chains: string[]; chain id information
- defaultChain?: string; default chain
- signRequest - RequestParams[]; the method for requesting a connection and signing the request, at most one method can be supported at a time
- method: string; The name of the requested method, Solana supports methods such as: 'solana_signMessage'
- chainId: string; the ID of the chain in which the method is executed, the chainId must be included in the namespaces above
- params: unknown[] | Record`` | object | undefined; Parameters corresponding to the requested method
**ReturnValue**
- Promise ``
- topic: string; The session identifier
- namespaces: `Record`; namespace information for a successful connection
- chains: string[]; Chain information for the connection
- accounts: string[]; accounts information for the connection
- methods: string[]; Methods supported by the wallet in the current namespace
- defaultChain?: string; The default chain for the current session
- sessionConfig?: SessionConfig
- dappInfo: object DApp information
- name: string
- icon: string
**Examples**
```typescript
// Start by adding a signature result listener
universalUi.on('connect_signResponse', (signResponse) => {
console.log(signResponse);
});
var session = await universalUi.openModalAndSign({
namespaces: {
solana: {
chains: ['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', // solana mainnet
// "sonic:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",// sonic mainnet
// 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z', // solana testnet
// 'sonic:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z',// sonic testnet ;
],
}
},
sessionConfig: {
redirect: 'tg://resolve'
}
},[
{
chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
method: 'solana_signMessage',
params: {
message: 'Hello Solana',
}
}
])
```
## Determine if the wallet is connected
Gets whether the wallet is currently connected
** return value **
- boolean
**example**
``typescript
universalUi.connected();
``
## Prepare a transaction
Methods to send a message to a wallet, support signature, transaction
First create an OKXSolanaProvider object, the constructor passes in OKXUniversalProviderUI, and when the OKXSolanaProvider related methods are called, the actionsConfiguration.mode configuration will be handled according to the values passed at init time
```typescript
import { OKXSolanaProvider } from '@okxconnect/solana-provider';
let okxSolanaProvider = new OKXSolanaProvider(universalUi)
```
## Signature ##
```
okxSolanaProvider.signMessage(message, chain);
```
**Request Parameters**
- message - string, the message to be signed
- chain: string, the chain to be executed by the request signature, it is recommended to pass this parameter; it is mandatory to pass this parameter when connecting multiple chains; connecting a chain without passing it will be handled according to solana:mainnet or sonic:mainnet
**Return value**
- Promise - object
- publicKey:string wallet address
- signature:Uint8Array The signature result
***Sign a single transaction***
```
okxSolanaProvider.signTransaction(transaction, chain);
```
**Request Parameterseters**
- transaction - Transaction | VersionedTransaction transaction data object
- chain: string, the chain for which the signature execution is requested, it is recommended to pass this parameter; it is mandatory to pass this parameter when connecting multiple chains; connecting a chain without passing it will be handled according to solana:mainnet or sonic:mainnet
**Return Value**
- Promise - Transaction | VersionedTransaction signed transaction object
## Sign multiple transactions
```
okxSolanaProvider.signAllTransactions(transactions, chain);
```
**Request Parameterseters**
- transactions - [Transaction | VersionedTransaction] array of transaction data objects
- chain: string, the chain for which the signature execution is requested, it is recommended to pass this parameter; it is mandatory to pass this parameter when connecting multiple chains; connecting a chain without passing it will be handled according to solana:mainnet or sonic:mainnet
**Return value**
- Promise - [Transaction | VersionedTransaction] An array of signed transaction objects
## Sign a transaction and broadcast it on the chain
```
okxSolanaProvider.signAndSendTransaction(transaction, chain);
```
**Request Parameterseters**
- transactions - Transaction | VersionedTransaction transaction data object
- chain: string, the chain for which the signature execution is requested, it is recommended to pass this parameter; it is mandatory to pass this parameter when connecting multiple chains; connecting a chain without passing it will be handled according to solana:mainnet or sonic:mainnet
**Return Value**
- Promise - string transactionhash
## Get wallet account information
```
okxSolanaProvider.getAccount(chain);
```
**Request Parameters**
- chain: string, get the chain id of the wallet address, if not passed then the first connected svm address will be taken by default
**Return Value**
- Object
- address: string The address of the wallet
- publicKey: PublicKey
**Example**
```typescript
// Sign a transfer transaction on solana mainnet.
let provider = new OKXSolanaProvider(universalUi)
const transaction = new Transaction({
feePayer: new PublicKey(provider.getAccount().address),
recentBlockhash: 'xNWbUfdEPktMsZQHY6Zk5RJqamWFcTKasekjr7c3wFX',
}).add(SystemProgram.transfer(
{
fromPubkey: new PublicKey(provider.getAccount().address),
toPubkey: new PublicKey(provider.getAccount().address),
lamports: 1000,
}
))
let result = await provider.signTransaction(transaction, 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp')
```
## Disconnect wallet
Disconnect the connected wallet and delete the current session, if you want to switch the connected wallet, please disconnect the current wallet first
``` Typescript
universalUi.disconnect();
```
## Event
[Same as EVM compatibility chain](https://web3.okx.com/zh-hans/web3/build/docs/sdks/app-connect-evm-ui#event%E4%BA%8B%E4%BB%B6)
## Error code
[Same as EVM compatibility chain](https://web3.okx.com/zh-hans/web3/build/docs/sdks/app-connect-evm-sdk#%E9%94%99%E8%AF%AF%E7%A0%81)
- [TON](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-ton.md)
# TON
TON chain, fully known as The Open Network, aims to provide fast and efficient transaction processing capabilities, while supporting smart contracts and decentralized applications. TON uses an innovative design called "multi-chain architecture," enabling high scalability and allowing multiple blockchains to run in parallel, thus improving the throughput and performance of the entire network.
If connecting via SDK, add a "Connect OKX Wallet" button within the DApp. Clicking this button will launch the mobile App Wallet and enable interaction with the OKX Wallet, such as retrieving addresses, initiating wallet signatures, and other functions.
In addition to SDK, we also provide a UI interface.
If you've used Ton Connect before, this document is a seamless way to reduce your development costs.
If you have used OKX Connect before, you can reduce your development costs by connecting to it with this documentation
- [SDK](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-ton-sdk.md)
# SDK
If you have used Ton Connect before, you can continue to use this document to connect, which can reduce development costs.
If you have used OKX Connect before, you can jump to using ProviderSDK to connect, which can reduce development costs and support multiple network requests at the same time.
# SDK Installation
SDK can be installed via cdn or npm
**Installation via cdn**
You can add the following code to the HTML file, or replace "latest" with a specific version number, such as "1.3.7".
``
Once introduced, OKXTonConnectSDK will be available as a global object that can be directly referenced.
``
**Using npm**
```
npm install @okxconnect/tonsdk
```
## Initialization
Before connecting to the wallet, create an instance of the SDK:
`new OKXTonConnect({metaData: {name, icon}})`
**Request Parameters**
- metaData - object
- name - string: Application name (not unique).
- icon - string: URL for the application icon (PNG, ICO formats; best as 180x180px PNG).
**Return Value**
- okxTonConnect - OKXTonConnect
**Example**
```typescript
import { OKXTonConnect } from "@okxconnect/tonsdk";
const okxTonConnect = new OKXTonConnect({
metaData: {
name: "application name",
icon: "application icon url"
}
});
```
## Connect to Wallet
Connect to the wallet to get the wallet address as an identifier and the necessary parameters used to sign the transaction.
`connect(request): Promise;`
**Request Parameters**
- request - object (optional)
- tonProof - string (optional): signature information;
- redirect - string (optional) : After processing the wallet event, the app will return the required deeplink, e.g. in Telegram environment, this field needs to pass the Telegram deeplink, when the wallet signing is done, OKX App will open the Telegram program through this deeplink, it is not recommended to set it in non-Telegram environment;
- openUniversalLink - boolean (可选) : When connecting to the wallet, whether to call up the OKX App client via Universal link; if set to true, when the user initiates the connection to the wallet, the OKX App client will be pulled up and a confirmation page will pop up, and if the OKX App client is not installed on the cell phone, it will be redirected to the download page;
**Return Value**
- Promise - string: PC web side can generate QR code according to this field, OKX App client can scan the generated QR code in web3 and connect to DApp;
**Recommendations**
- Set openUniversalLink to true in your mobile browser or mobile Telegram environment;
- Set openUniversalLink to false in PC browser environment, and generate QR code according to the returned universalLink, you can use OKX App client to scan the code to connect to it, and cancel the QR code pop-up window after successful connection;
**Example**
```typescript
import { OKXConnectError } from "@okxconnect/tonsdk";
try {
okxTonConnect.connect({
tonProof: "signmessage",
redirect: "tg://resolve",
openUniversalLink: true
})
} catch (error) {
if (error instanceof OKXConnectError) {
if (error.code === OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR) {
alert('User reject');
} else if (error.code === OKX_CONNECT_ERROR_CODES.ALREADY_CONNECTED_ERROR) {
alert('Already connected');
} else {
alert('Unknown error happened');
}
} else {
alert('Unknown error happened');
}
}
```
## Restore Connection
If the user has previously connected their wallet, use this method to restore the connection state:
`restoreConnection(): Promise`
**Request Parameterseters**
None
**Return Value**
None
**Example**
```typescript
okxTonConnect.restoreConnection()
```
## Disconnect
Disconnects the connected wallet and deletes the current session. If you want to switch connected wallets, disconnect the current wallet first.
**Example**
```typescript
import { OKX_CONNECT_ERROR_CODES } from "@okxconnect/tonsdk";
try {
await okxTonConnect.disconnect()
} catch (error) {
if (error instanceof OKXConnectError) {
switch (error.code) {
case OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR:
alert('Not connected');
break;
default:
alert('Unknown error happened');
break;
}
} else {
alert('Unknown error happened');
}
}
```
## Connected
Get whether there is currently a connected wallet
**Example**
```typescript
var connect: boolean = okxTonConnect.connected()
```
## Send transaction
Method for sending a message to a wallet:
`sendTransaction(transaction, options): Promise`
**Request Parameterseters**
- transaction - object
- validUntil - number :unix timestamp. The transaction will be invalid after this point
- from - string (optional): address of the sender to which the DApp is sending the transaction, defaults to the currently connected wallet address;
- messages - object[] : (array of messages): 1-4 output messages from the wallet contract to other accounts. All messages are sent out in order, but the
The wallet cannot guarantee that the messages will be delivered and executed in the same order.
- address - string : the destination of the message
- amount - string : The amount to be sent.
- stateInit - string (optional) : The original cell BoC encoded in Base64.
- payload - string (optional) : Base64 encoded raw cell BoC.
- options - object
- onRequestSent - () => void : This method is called when a signature request is sent;
**Return value**
- Promise - `{boc: string}`: signed result
**Example**
```typescript
import { OKXConnectError } from "@okxconnect/tonsdk";
let transactionRequest = {
"validUntil": Date.now() / 1000 + 360,
"from": "0:348bcf827469c5fc38541c77fdd91d4e347eac200f6f2d9fd62dc08885f0415f",
"messages": [
{
"address": "0:412410771DA82CBA306A55FA9E0D43C9D245E38133CB58F1457DFB8D5CD8892F",
"amount": "20000000",
"stateInit": "base64bocblahblahblah==" //deploy contract
}, {
"address": "0:E69F10CC84877ABF539F83F879291E5CA169451BA7BCE91A37A5CED3AB8080D3",
"amount": "60000000",
"payload": "base64bocblahblahblah==" //transfer nft to new deployed account 0:412410771DA82CBA306A55FA9E0D43C9D245E38133CB58F1457DFB8D5CD8892F
}
]
}
let requestOptions = {
onRequestSent: () => {
//requestMsgSend
}
}
try {
const result = await okxTonConnect.sendTransaction(transactionRequest, requestOptions);
} catch (error) {
if (error instanceof OKXConnectError) {
switch (error.code) {
case OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR:
alert('You rejected the transaction.');
break;
case OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR:
alert('Not connected');
break;
default:
alert('Unknown error happened');
break;
}
} else {
alert('Unknown error happened');
}
}
```
## Monitor wallet state changes
The wallet statuses are: successful connection, successful restoration of connection, disconnection, etc. You can use this method to get the status.
`onStatusChange(
callback: (walletInfo) => void,
errorsHandler?: (err) => void
): () => void;`
**Request Parameters**
- callback - (walletInfo) => void : This callback is called when the wallet state changes;
- walletinfo - object
- device - object
- appName - string : the name of the wallet
- platform - string : the platform of the wallet, (android,ios)
- appVersion - string : the version number of the wallet
- maxProtocolVersion - number : the version of the wallet, (android,ios)
- features - string[] : supported methods, current version is sendTransaction
- account - Account
- address - string : TON address raw (`0:`)
- chain - "-239"
- walletStateInit - string : Base64 (not url safe) encoded stateinit cell for the wallet contract
- publicKey - string : HEX string without 0x
- connectItems - object
- name - string : "ton_proof"
- proof - object
- timestamp - number : timestamp
- domain - object
- lengthBytes - number : AppDomain Length
- value - string : app domain name (as url part, without encoding)
- payload - string: Base64-encoded signature
- signature - string: payload from the request
- errorsHandler - (err: OKXConnectError) => void : This errorsHandler is called when an exception occurs due to a change in the wallet state;
- err - TonConnectError
- code - number
- message - string
**Return Value**
- () => void : Execute this method to save resources when there is no longer a need to listen for updates.
**Example**
```typescript
import { Wallet } from "@okxconnect/tonsdk";
const unsubscribe = okxTonConnect.onStatusChange((walletInfo: Wallet | null) => {
console.log('Connection status:', walletInfo);
}, (err: OKXConnectError) => {
console.log('Connection status:', err);
}
)
```
Call unsubscribe to save resources when you no longer need to listen for updates.
```typescript
unsubscribe()
```
## Listening to Event
When the following events occur, corresponding event notifications will be sent, and the Dapp can add listeners as needed to handle the corresponding logic;
**event**
| event name | trigger timing |
|----------------------------------------|----------------------|
| OKX_TON_CONNECTION_STARTED | When the user starts to connect to the wallet |
| OKX_TON_CONNECTION_COMPLETED | When the user successfully connects to the wallet |
| OKX_TON_CONNECTION_ERROR | When the user canceled the connection or there was an error during the connection process |
| OKX_TON_CONNECTION_RESTORING_STARTED | When the dApp starts to resume the connection |
| OKX_TON_CONNECTION_RESTORING_COMPLETED | When the dApp successfully restores the connection | |
| OKX_TON_CONNECTION_RESTORING_ERROR | When the dApp fails to restore the connection |
| OKX_TON_DISCONNECTION | When the user starts to disconnect from the wallet |
| OKX_TON_TRANSACTION_SENT_FOR_SIGNATURE | When the user sends a transaction for signature |
| OKX_TON_TRANSACTION_SIGNED | When the user successfully signs a transaction |
| OKX_TON_TRANSACTION_SIGNING_FAILED | When the user canceled the transaction signing or there was an error during the signing process |
**Example**
```typescript
import { OKX_TON_CONNECTION_AND_TRANSACTION_EVENT } from "@okxconnect/tonsdk";
window.addEventListener(OKX_TON_CONNECTION_AND_TRANSACTION_EVENT.OKX_TON_CONNECTION_STARTED, (event) => {
if (event instanceof CustomEvent) {
console.log('Transaction init', event.detail);
}
});
```
## Get account information
Get the currently connected account
**example**
```typescript
import { Account } from "@okxconnect/tonsdk";
var connect: Account = okxTonConnect.account()
```
## Get wallet information
Get the currently connected wallet
**Example**
```typescript
import { Wallet } from "@okxconnect/tonsdk";
var connect: Wallet = okxTonConnect.wallet()
```
## Event
```typescript
// Generate universalLink
okxUniversalProvider.on('display_uri', (uri) => {
console.log(uri);
});
// Session information changes will trigger this event;
okxUniversalProvider.on('session_update', (session) => {
console.log(JSON.stringify(session)); // Session information changes (e.g., adding a custom chain).
});
// Disconnecting triggers this event;
okxUniversalProvider.on('session_delete', ({topic}) => {
console.log(topic);
});
```
## Error codes
Exceptions that may be thrown during connection, transaction, and disconnection.
**Exception**
| Error Code | Description |
|----------------------------------------------|-------------------------|
| OKX_CONNECT_ERROR_CODES.UNKNOWN_ERROR | Unknown Error |
| OKX_CONNECT_ERROR_CODES.ALREADY_CONNECTED_ERROR | Wallet Already Connected |
| OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR | Wallet Not Connected |
| OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR | User Rejected |
| OKX_CONNECT_ERROR_CODES.METHOD_NOT_SUPPORTED | Method Not Supported |
| OKX_CONNECT_ERROR_CODES.CHAIN_NOT_SUPPORTED | Chain Not Supported |
| OKX_CONNECT_ERROR_CODES.WALLET_NOT_SUPPORTED | Wallet Not Supported |
| OKX_CONNECT_ERROR_CODES.CONNECTION_ERROR | Connection Error |
```typescript
export enum OKX_CONNECT_ERROR_CODES {
UNKNOWN_ERROR = 0,
ALREADY_CONNECTED_ERROR = 11,
NOT_CONNECTED_ERROR = 12,
USER_REJECTS_ERROR = 300,
METHOD_NOT_SUPPORTED = 400,
CHAIN_NOT_SUPPORTED = 500,
WALLET_NOT_SUPPORTED = 600,
CONNECTION_ERROR = 700
}
```
- [UI](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-ton-ui.md)
# UI
If you have used Ton Connect before, you can continue to use this document to connect, which can reduce development costs.
If you have used OKX Connect before, you can jump to using ProviderUI to connect, which can reduce development costs and support multiple network requests at the same time.
## Install via npm
```
npm install @okxconnect/ui
```
## Initialization
Before connecting to the wallet, you need to create an object for subsequent operations such as connecting to the wallet and sending transactions.
`new OKXTonConnectUI(dappMetaData, buttonRootId, actionsConfiguration, uiPreferences, language, restoreConnection)`
**Request Parameterseters**
- metaData - object
- name - string: The name of the application, will not be used as a unique representation.
- icon - string: URL of the application icon, must be in PNG, ICO, etc. SVG icons are not supported. It is better to pass a url pointing to a 180x180px PNG icon.
- buttonRootId - string: the HTML element ID of the button used to attach the wallet connection. if not passed, the button will not appear;.
- actionsConfiguration - object
- modals - ('before' |'success' |'error')[] |'all' The modals for displaying the alert screen during a transaction.
- returnStrategy -string'none' | `${string}://${string}`; Specify the return strategy for deep links when the user signs/rejects the request, if in telegram, configure tg://resolve
- uiPreferences -object
- theme - Theme can be: THEME.DARK, THEME.LIGHT,'SYSTEM'.
- language -'en_US’ |'ru_RU’ |'zh_CN’ |'ar_AE’ |'cs_CZ’ |'de_DE’ |'es_ES’ |'es_LAT’ |'fr_FR’ |'id_ID’ |'it_IT’ |'nl_NL’ |'pl_PL’ |'pt_BR’ |'pt_PT’ |'ro_RO’ |'tr_TR’ |'uk_UA’ |'vi_VN’.
, defaults to en_US
- restoreConnection?: boolean - Whether to automatically restore the previous connection;
**Returns the value**
- OKXTonConnectUI
**Example**
```typescript
import { OKXTonConnectUI } from "@okxconnect/ui";
const okxTonConnectUI = new OKXTonConnectUI({
dappMetaData: {
name: "application name",
icon: "application icon url"
},
buttonRootId:'button-root',
actionsConfiguration:{
returnStrategy:'none',
tmaReturnUrl:'back'
},
uiPreferences: {
theme: THEME.LIGHT
},
language:'en_US',
restoreConnection: true
});
```
## Monitor wallet state changes
The wallet status are: connect successfully, resume connect successfully, disconnect, etc. You can use this method to get the status.
[Method details same as OKXTonConnect.onStatusChange](https://web3.okx.com/web3/build/docs/sdks/app-connect-ton-sdk#monitor-wallet-state-changes)
**Example**
```typescript
import { Wallet } from "@okxconnect/tonsdk";
const unsubscribe = okxTonConnectUI.onStatusChange((walletInfo: Wallet | null) => {
console.log('Connection status:', walletInfo);
}, (err: OKXConnectError) => {
console.log('Connection status:', err);
}
)
```
Call unsubscribe to save resources when you no longer need to listen for updates.
```typescript
unsubscribe()
```
## Connect to Wallet
Connecting to a wallet goes to get the wallet address, which serves as the identifier and the necessary parameters used to sign the transaction.
The “Connect Button” (added to buttonRootId) automatically handles the click and invokes the connection.
If no buttonRootId is added, this method needs to be called.
`await okxTonConnectUI.openModal();`
**Example**
```typescript
okxTonConnectUI.openModal();
```
## Set the tonProof
Add the connection signature parameter, the
If you need to set tonProof, set state:'loading', before the tonProof parameter is ready.
When ready, set state to'ready' and add value;.
You can also remove the loading state by setting setConnectRequestParameters(null);
**Example**
```typescript
okxtonConnectUI.setConnectRequestParameters({ state:'loading' });
const tonProofPayload: string | null = await fetchTonProofPayloadFromBackend();
if (!tonProofPayload) {
okxtonConnectUI.setConnectRequestParameters(null);
} else {
okxtonConnectUI.setConnectRequestParameters({
state: "ready",
value: { tonProof: tonProofPayload }
});
}
```
## Close connection popup
**Example**
```typescript
okxTonConnectUI.closeModal();
```
## Get the currently connected Wallet and WalletInfo
Get information about whether there is a currently connected wallet, and the connected wallet;
**Example**
```typescript
const currentWallet = okxTonConnectUI.wallet;
const currentAccount = okxTonConnectUI.account;
const isConnected = okxTonConnectUI.connected;
```
## Disconnect
**Example**
```typescript
okxTonConnectUI.disconnect();
```
## Send transaction
Method for sending a message to a wallet:
`sendTransaction(transaction, actionConfigurationRequest): Promise`
**Request Parameterseters**
- transaction - object, [parameters same as OKXTonConnect.sendTransaction's transaction](/web3/build/docs/sdks/app-connect-ton-sdk#%E5%8F%91%E9%80%81% E4%BA%A4%E6%98%93)
- actionConfigurationRequest - object
- modals : ('before' |'success' |'error')[] |'all' Mode of displaying the alert screen during the transaction, defaults to'before'
- returnStrategy -string'none’ | `${string}://${string}`; The return strategy for the deep link in the App wallet when the user signs or rejects the request, if it is a Mini App in Telegram, it can be configured with tg://resolve, and if it's not configured here, the will take the returnStrategy passed by the init method, default is'none’
**Return Value**
- Promise - `{boc: string}`: Signature Result
```typescript
import { OKXConnectError, OKX_CONNECT_ERROR_CODES } from "@okxconnect/core";
let transactionRequest = {
"validUntil": Date.now() / 1000 + 360,
"from": "0:348bcf827469c5fc38541c77fdd91d4e347eac200f6f2d9fd62dc08885f0415f",
"messages": [
{
"address": "0:412410771DA82CBA306A55FA9E0D43C9D245E38133CB58F1457DFB8D5CD8892F",
"amount": "20000000",
"stateInit": "base64bocblahblahblah==" //deploy contract
},
{
"address": "0:E69F10CC84877ABF539F83F879291E5CA169451BA7BCE91A37A5CED3AB8080D3",
"amount": "60000000",
"payload": "base64bocblahblahblah==" //transfer nft to new deployed account 0:412410771DA82CBA306A55FA9E0D43C9D245E38133CB58F1457DFB8D5CD8892F
}
]
}
okxTonConnectUI.sendTransaction(transactionRequest, {
modals:'all',
tmaReturnUrl:'back'
}).then((result) => {
let boc = result.boc
}).catch((error) => {
if (error instanceof OKXConnectError && error.code == OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR) {
//userReject;
} else {
//other error;
}
})
```
## Set ui configuration items
Support to modify theme, text language setting, also can add these configurations during initialisation;
**Example**
```typescript
okxTonConnectUI.uiOptions = {
language:'zh_CN',
uiPreferences: {
theme: THEME.DARK
}
};
```
## Listening to Events
When the following events occur, a notification of the corresponding event will be sent, and the Dapp can add listeners as needed to handle the corresponding logic;
**event**
| Event Name | Trigger Timing |
|---------------------------------------|----------------------|
| OKX_UI_CONNECTION_STARTED | When the user starts to connect to the wallet |
| OKX_UI_CONNECTION_COMPLETED | When the user successfully connects to the wallet |
| OKX_UI_CONNECTION_ERROR | When the user canceled the connection or there was an error during the connection process |
| OKX_UI_CONNECTION_RESTORING_STARTED | When the dApp starts restoring the connectio |
| OKX_UI_CONNECTION_RESTORING_COMPLETED | When the dApp successfully restores the connection |
| OKX_UI_CONNECTION_RESTORING_ERROR | When the dApp failed to restore the connection |
| OKX_UI_DISCONNECTION | When the user starts to disconnect from the wallet |
| OKX_UI_TRANSACTION_SENT_FOR_SIGNATURE | When the user sends a transaction for signature |
| OKX_UI_TRANSACTION_SIGNED | When the user successfully signs a transaction |
| OKX_UI_TRANSACTION_SIGNING_FAILED | When the user canceled the transaction signing or there was an error during the signing process |
**Example**
```typescript
import { OKX_UI_CONNECTION_AND_TRANSACTION_EVENT } from "@okxconnect/ui";
window.addEventListener(OKX_UI_CONNECTION_AND_TRANSACTION_EVENT.OKX_UI_CONNECTION_STARTED, (event) => {
if (event instanceof CustomEvent) {
console.log('Transaction init', event.detail);
}
});
```
## Event
```typescript
// Generate universalLink
universalUi.on("display_uri", (uri) => {
console.log(uri);
});
// Session information changes will trigger this event;
universalUi.on("session_update", (session) => {
console.log(JSON.stringify(session));
});
// Disconnecting triggers this event;
universalUi.on("session_delete", ({topic}) => {
console.log(topic);
});
// This event is triggered when a connection is made and the signature is signed.
universalUi.on("connect_signResponse", (signResponse) => {
console.log(signResponse);
});
```
## Error codes
Exceptions that may be thrown during connection, transaction, and disconnection.
**Exception**
| Error Code | Description |
|----------------------------------------------|-------------------------|
| OKX_CONNECT_ERROR_CODES.UNKNOWN_ERROR | Unknown Error |
| OKX_CONNECT_ERROR_CODES.ALREADY_CONNECTED_ERROR | Wallet Already Connected |
| OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR | Wallet Not Connected |
| OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR | User Rejected |
| OKX_CONNECT_ERROR_CODES.METHOD_NOT_SUPPORTED | Method Not Supported |
| OKX_CONNECT_ERROR_CODES.CHAIN_NOT_SUPPORTED | Chain Not Supported |
| OKX_CONNECT_ERROR_CODES.WALLET_NOT_SUPPORTED | Wallet Not Supported |
| OKX_CONNECT_ERROR_CODES.CONNECTION_ERROR | Connection Error |
```typescript
export enum OKX_CONNECT_ERROR_CODES {
UNKNOWN_ERROR = 0,
ALREADY_CONNECTED_ERROR = 11,
NOT_CONNECTED_ERROR = 12,
USER_REJECTS_ERROR = 300,
METHOD_NOT_SUPPORTED = 400,
CHAIN_NOT_SUPPORTED = 500,
WALLET_NOT_SUPPORTED = 600,
CONNECTION_ERROR = 700
}
```
- [SUI](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-sui.md)
# SUI
Sui (or Sui Network) is the first Layer 1 blockchain designed from the ground up to enable creators and developers to build experiences that cater for the next billion users in Web3. Sui is horizontally scalable to support a wide range of DApp development with fast speeds and low costs. The platform brings users a general-purpose blockchain with high throughput, instant settlement speeds, rich on-chain assets, and user-friendly Web3 experiences. Sui is a step-function advancement in blockchain, designed from the bottom up to meet the needs of everyone involved in crypto.
If connecting via SDK, add a "Connect OKX Wallet" button within the DApp. Clicking this button will launch the mobile App Wallet and enable interaction with the OKX Wallet, such as retrieving addresses, initiating wallet signatures, and other functions.
In addition to SDK, we also provide a UI interface.
- [SDK](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-sui-sdk.md)
# SDK
## Installation and Initialization
Make sure to update the OKX App to version 6.90.1 or later to start integrating OKX Connect into your DApp can be done using npm:
```
npm install @okxconnect/sui-provider
```
Before connecting the wallet, you need to create an object for subsequent wallet connections, transaction submissions, and other operations.
`OKXUniversalProvider.init({dappMetaData: {name, icon}})`
**Request Parameterseters**
- dappMetaData - object
- name - string: the name of the app, will not be used as a unique representation
- icon - string: URL of the application icon, must be in PNG, ICO, etc. SVG icons are not supported. SVG icons are not supported. It is better to pass a url pointing to a 180x180px PNG icon.
**Returns a value**.
- OKXUniversalProvider
**Example**
```typescript
import { OKXUniversalProvider } from "@okxconnect/universal-provider"
const okxUniversalProvider = OKXUniversalProvider.init({dappMetaData: {
name: "application name",
icon: "application icon url"
}})
```
## Connecting to Wallet
Connecting to a wallet goes to get the wallet address as an identifier and the necessary parameters used to sign the transaction.
`okxUniversalProvider.connect(connectParams: ConnectParams);`
**Request Parameterseters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; Necessary information for the requested connection, the key for the Sui line is 'sui'
If any of the requested chains are not supported by the wallet, the wallet will reject the connection;
- chains: string[]; chain id information, the chain's key is 'sui', the wallet will reject the connection if any of the requested chains is not supported.
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information for requesting connection, the key for EVM is 'eip155', the key for Sui is 'sui'.
If the corresponding chain information is not supported by the wallet, the connection can still be made;
- chains: string[]; chain id information, if the corresponding chain is not supported by the wallet.
- sessionConfig: object
- redirect: string Jump parameter after successful connection, if it is a Mini App in Telegram, here can be set to Telegram's deeplink: 'tg://resolve'.
**Return value**
- Promise ``
- topic: string; The session identifier;
- namespaces: `Record`; namespace information for a successful connection;
- chains: string[]; Chain information for the connection;
- accounts: string[]; accounts information for the connection;
- methods: string[]; methods supported by the wallet under the current namespace;
- defaultChain?: string; default chain for the current session
- sessionConfig?: SessionConfig
- dappInfo: object DApp information;
- name:string
- icon:string
- redirect?: string, the redirect parameter after successful connection;
**Example**
```typescript
var session = await okxUniversalProvider.connect({
namespaces: {
sui: {
chains: ["sui:mainnet"]
}
},
sessionConfig: {
redirect: "tg://resolve"
}
})
```
## Determine if the wallet is connected
Gets whether the wallet is currently connected.
**Return Value**
- boolean
**Example**
```typescript
okxUniversalProvider.connected();
```
## Disconnect wallet
Disconnect from a connected wallet and delete the current session. If you want to switch wallets, disconnect from the current wallet first.
**Example**
```typescript
okxUniversalProvider.disconnect();
```
## Prepare the transaction
Methods to send messages to the wallet, support signature, transaction.
First create an OKXSuiProvider object, pass OKXUniversalProvider into the constructor.
```typescript
import { OKXSuiProvider } from '@okxconnect/sui-provider'
let suiProvider = new OKXSuiProvider(okxUniversalProvider)
```
## Get the account
``suiProvider.getAccount();``
***Return Value***
- Object
- address: string wallet address
- publicKey: string public key (requires App 6.92.0 or later support)
***Example***
```typescript
let result = suiProvider.getAccount()
// Return structure
{
'address": “0x7995ca23961fe06d8cea7da58ca751567ce820d7cba77b4a373249034eecca4a”,
'publicKey": “tUvCYrG22rHKR0c306MxgnhXOSf16Ot6H3GMO7btwDI=”,
}
```
## SignMessage
```typescript
suiProvider.signMessage(input: SuiSignMessageInput);
```
***Request Parameters***
- SuiSignMessageInput - object
- message: Uint8Array
***Return Value***
- Promise - object
- messageBytes: string
- signature: string
## SignPersonalMessage
```typescript
suiProvider.signPersonalMessage(input: SuiSignMessageInput);
```
***Request Parameters***
- SuiSignMessageInput - object
- message: Uint8Array
***Return Value***
- Promise - object
- bytes: string
- signature: string
**Example**
```typescript
const data = [76, 111, 103, 105, 110, 32, 119, 105, 116, 104, 32, 66, 108, 117, 101, 109, 111, 118, 101];
const uint8Array = new Uint8Array(data);
let input = {
message: uint8Array
}
let signResult1 = await suiProvider.signMessage(input)
let signResult2 = await suiProvider.signPersonalMessage(input)
```
## Sign Transaction
```typescript
suiProvider.signTransaction(input);
```
***Request Parameters***
```typescript
// txBytes and txSerialize are the serialization of the transactionBlock.
// and transactionBlock can be passed in one way or the other, but not both.
interface SuiSignTransactionBlockInput {
transactionBlock: TransactionBlock;
chain: IdentifierString;
txBytes: string?;
txSerialize: string?
}
```
***Return Value***
- Promise - object
- signature: string,
- transactionBlockBytes: string
## Signs a transaction and broadcasts onchain
```typescript
suiProvider.signAndExecuteTransaction(input);
```
***Request Parameters***
```typescript
// txBytes and txSerialize are the serialization of the transactionBlock.
// and transactionBlock can be passed in one way or the other, but not both.
interface SuiSignTransactionBlockInput {
transactionBlock: TransactionBlock;
chain: IdentifierString;
txBytes: string?;
txSerialize: string?;
}
```
***Return Value***
- Promise - object
- confirmedLocalExecution: bool,
- digest: string,
- txBytes: string
**Example**
```typescript
// Define the amount to be transferred and the destination address
const amount = 109; // Amount to be transferred
const recipientAddress = '0x'; // destination address
/// Construct a transfer transaction
const tx = new Transaction();
const [coin] = tx.splitCoins(tx.gas, [amount]);
tx.transferObjects([coin], recipientAddress)
const input = {
transactionBlock: tx,
chain: 'sui:mainnet',
options: {
showEffects: true,
}
}
let signResult1 = await suiProvider.signTransaction(input)
let signResult2 = await suiProvider.signAndExecuteTransaction(input)
```
## Event
```typescript
// Generate universalLink
okxUniversalProvider.on('display_uri', (uri) => {
console.log(uri);
});
// Session information changes will trigger this event;
okxUniversalProvider.on('session_update', (session) => {
console.log(JSON.stringify(session)); // Session information changes (e.g., adding a custom chain).
});
// Disconnecting triggers this event;
okxUniversalProvider.on('session_delete', ({topic}) => {
console.log(topic);
});
```
## Error codes
Exceptions that may be thrown during connection, transaction, and disconnection.
**Exception**
| Error Code | Description |
|----------------------------------------------|-------------------------|
| OKX_CONNECT_ERROR_CODES.UNKNOWN_ERROR | Unknown Error |
| OKX_CONNECT_ERROR_CODES.ALREADY_CONNECTED_ERROR | Wallet Already Connected |
| OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR | Wallet Not Connected |
| OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR | User Rejected |
| OKX_CONNECT_ERROR_CODES.METHOD_NOT_SUPPORTED | Method Not Supported |
| OKX_CONNECT_ERROR_CODES.CHAIN_NOT_SUPPORTED | Chain Not Supported |
| OKX_CONNECT_ERROR_CODES.WALLET_NOT_SUPPORTED | Wallet Not Supported |
| OKX_CONNECT_ERROR_CODES.CONNECTION_ERROR | Connection Error |
```typescript
export enum OKX_CONNECT_ERROR_CODES {
UNKNOWN_ERROR = 0,
ALREADY_CONNECTED_ERROR = 11,
NOT_CONNECTED_ERROR = 12,
USER_REJECTS_ERROR = 300,
METHOD_NOT_SUPPORTED = 400,
CHAIN_NOT_SUPPORTED = 500,
WALLET_NOT_SUPPORTED = 600,
CONNECTION_ERROR = 700
}
```
- [UI](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-sui-ui.md)
# UI
## Installation and Initialisation:
Make sure to update the OKX App to version 6.90.1 or later to start accessing:
To integrate OKX Connect into your DApp, you can use npm:
```
npm install @okxconnect/ui
npm install @okxconnect/sui-provider
```
Before connecting to a wallet, you need to create an object that can provide a UI interface for subsequent operations such as connecting to the wallet and sending transactions.
```OKXUniversalConnectUI.init(dappMetaData, actionsConfiguration, uiPreferences, language)```
**Request Parameterseters**
- dappMetaData - object
- name - string: the name of the application, will not be used as a unique representation
- icon - string: URL of the application icon, must be in PNG, ICO, etc. SVG icons are not supported. SVG icons are not supported. It is best to pass a url pointing to a 180x180px PNG icon.
- actionsConfiguration - object
- modals - ('before' | 'success' | 'error')[] | 'all' The modes of displaying alerts during transaction, defaults to 'before'.
- returnStrategy -string 'none' | `${string}://${string}`; for app wallet, specify the return strategy of the deep link when the user signs/rejects the request, in case of telegram, you can configure tg://resolve
- uiPreferences -object
- theme - Theme can be: THEME.DARK, THEME.LIGHT, 'SYSTEM'.
- language - 'en_US' | 'ru_RU' | 'zh_CN' | 'ar_AE' | 'cs_CZ' | 'de_DE' | 'es_ES' | 'es_LAT' | 'fr_FR' | 'id_ID' | 'it_IT' | 'nl_NL' | 'pl_PL' | 'pt_BR' | 'pt_PT' | 'ro_RO' | 'tr_TR' | 'uk_UA' | 'vi_VN'.
, defaults to en_US
**Return value**
- OKXUniversalConnectUI
**Examples**
```typescript
import { OKXUniversalConnectUI } from '@okxconnect/ui';
const okxUniversalConnectUI = await OKXUniversalConnectUI.init({
dappMetaData: {
icon: 'https://static.okx.com/cdn/assets/imgs/247/58E63FEA47A2B7D7.png',
name: 'OKX Connect Demo'
},
actionsConfiguration: {
returnStrategy: 'tg://resolve', modals: 'all', {
modals: 'all',
tmaReturnUrl:'back'
},
language: 'en_US',
uiPreferences: {
theme: THEME.LIGHT
}, }
})
// Switching the plugin to connect to the wallet fires this event;
okxUniversalConnectUI.on('accountChanged', (session) => {
if (session){
console.log(`accountChanged `, JSON.stringify(session));
}
});
```
## Connect to the wallet
Connects to the wallet to get the wallet address as an identifier and the necessary parameters for signing the transaction.
``okxUniversalConnectUI.openModal(connectParams: ConnectParams);``
**Request Parameterseters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; Necessary information for the requested connection, the key for the Sui line is 'sui'.
If any of the requested chains are not supported by the wallet, the wallet will reject the connection;
- chains: string[]; information about the chain ids, the name of the chain, the name of the wallet, and the name of the wallet.
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information of the requested connection, the key of EVM system is 'eip155', the key of Sui system is 'sui'.
If the corresponding chain information is not supported by the wallet, the connection can still be made;
- chains: string[]; chain id information, if the corresponding chain is not supported by the wallet.
- sessionConfig: object
- redirect: string Jump parameter after successful connection, if it is a Mini App in Telegram, here you can set it to Telegram's deeplink: 'tg://resolve'
**Return Value**
- Promise ``
- topic: string; The session identifier;
- namespaces: `Record`; namespace information for a successful connection;
- chains: string[]; Chain information for the connection;
- accounts: string[]; accounts information for the connection;
- methods: string[]; Methods supported by the wallet in the current namespace;
- defaultChain?: string; The default chain for the current session.
- sessionConfig?: SessionConfig
- dappInfo: object DApp information;
- name: string
- icon:string
**Example**
```typescript
var session = await okxUniversalConnectUI.openModal({
namespaces: {
sui: {
chains: ['sui:mainnet']
}
}
})
```
## Connect to wallet and sign
Connect to the wallet to get the wallet address and sign the data; the result will be called back in the event 'connect_signResponse';
```
await universalUi.openModalAndSign(connectParams: ConnectParams,signRequest: RequestParams[]);
```
**Request Parameters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; information necessary to request a connection, the key for Sui is 'sui'
If any of the requested chains are not supported by the wallet, the wallet will reject the connection;
- chains: string[]; information about the chain ids, the name of the chain, the name of the wallet, and the name of the wallet.
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information of the requested connection, the key of Sui is 'sui'
If the corresponding chain information is not supported by the wallet, the connection can still be made;
- chains: string[]; Chain id information, if the corresponding chain is not supported by the wallet, it can still be connected.
- sessionConfig: object
- redirect: string Jump parameter after successful connection, if it is a Mini App in Telegram, here you can set it to Telegram's deeplink: 'tg://resolve'.
- signRequest - RequestParams[]; the method to request the connection and sign the request, at most one method can be supported at the same time;
- method: string; the name of the requested method, Sui supports methods such as 'sui_signMessage' and 'sui_signPersonalMessage';
- chainId: string; the ID of the chain where the method is executed, the chainId must be included in the namespaces above;
- params: unknown[] | Record`` | object | undefined; Parameters corresponding to the requested method;
**ReturnValue**
- Promise``; the parameters corresponding to the requested method.
- topic: string; The session identifier;
- namespaces: `Record`; namespace information for a successful connection;
- chains: string[]; Chain information for the connection;
- accounts: string[]; accounts information for the connection;
- methods: string[]; Methods supported by the wallet in the current namespace;
- defaultChain?: string; The default chain for the current session.
- sessionConfig?: SessionConfig
- dappInfo: object DApp information;
- name: string
- icon:string
**Example**
```typescript
// Add the signature result listener first
okxUniversalConnectUI.on('connect_signResponse', (signResponse) => {
console.log(signResponse);
});
let suiData = [
76, 111, 103, 105, 110, 32, 119, 105, 116, 104, 32, 66, 108, 117, 101.
109, 111, 118, 101,
];
let uint8Array = new Uint8Array(suiData);
var session = await okxUniversalConnectUI.openModalAndSign({
namespaces: {
sui: {
chains: ['sui:mainnet']
}
},
sessionConfig: {
redirect: 'tg://resolve'
}
},
[
{
chainId: 'sui:mainnet',
method: 'sui_signMessage',
params: {
message: uint8Array,
}
}
]
)
```
## Determine if the wallet is connected
Gets whether the wallet is currently connected or not;
** Return Value**
- boolean
**example**
```typescript
okxUniversalConnectUI.connected();
```
## Disconnect
Disconnect the connected wallet and delete the current session, if you want to switch the connected wallet, please disconnect the current wallet first.
**Example**
```typescript
okxUniversalConnectUI.disconnect();
```
## Prepare a transaction
Methods to send messages to the wallet, support signature, transaction; when the wallet confirmation method is needed, it will pop up a prompt page;
First create an OKXSuiProvider object, pass okxUniversalConnectUI into the constructor.
```typescript
import { OKXSuiProvider } from '@okxconnect/sui-provider'
let suiProvider = new OKXSuiProvider(okxUniversalConnectUI)
```
## Get account information
`suiProvider.getAccount();`
***Returns the value***.
- Object
- address: string wallet address
- publicKey: string public key (requires App 6.92.0 or later)
***Example***
```typescript
let result = suiProvider.getAccount()
// Return structure
{
"address": “0x7995ca23961fe06d8cea7da58ca751567ce820d7cba77b4a373249034eecca4a”,
"publicKey": “tUvCYrG22rHKR0c306MxgnhXOSf16Ot6H3GMO7btwDI=”,
}
```
## Signature Message
```typescript
suiProvider.signMessage(input: SuiSignMessageInput);
```
***Request Parameterseters***
- SuiSignMessageInput - object
- message: Uint8Array
***Return value***
- Promise - object
- messageBytes: string
- signature: string
## Signature PersonalMessage
```typescript
suiProvider.signPersonalMessage(input: SuiSignMessageInput);
```
***Request Parameterseters***
- SuiSignMessageInput - object
- message: Uint8Array
***Return value***
- Promise - object
- bytes: string
- signature: string
**Example**
```typescript
const data = [76, 111, 103, 105, 110, 32, 119, 105, 116, 104, 32, 66, 108, 117, 101, 109, 111, 118, 101];
const uint8Array = new Uint8Array(data);
let input = {
message: uint8Array
}
let signResult1 = await suiProvider.signMessage(input)
let signResult2 = await suiProvider.signPersonalMessage(input)
```
## Sign the deal ##
```typescript
suiProvider.signTransaction(input);
```
*** request parameters ***
```typescript
// txBytes and txSerialize for serialisation of transactionBlock
// and transactionBlock can be passed in one way without passing in both.
interface SuiSignTransactionBlockInput {
transactionBlock: TransactionBlock; chain: IdentifierString; }
chain: IdentifierString;
txBytes: string?
txSerialize: string?
}
```
***Return Value***
- Promise - object
- signature: string,
- transactionBlockBytes: string
## Sign the transaction and broadcast it up the chain
```typescript
suiProvider.signAndExecuteTransaction(input);
```
***Request Parameters
```typescript
// txBytes with txSerialize for transactionBlock serialisation
// and transactionBlock can be passed in one way without passing in both.
interface SuiSignTransactionBlockInput {
transactionBlock: TransactionBlock;
chain: IdentifierString;
txBytes: string?;
txSerialize: string?;
}
```
***Return Value***
- Promise - object
- confirmedLocalExecution: bool,
- digest: string,
- txBytes: string
**Example**
```typescript
// Define the amount to be transferred and the destination address.
const amount = 109; // Amount to be transferred
const recipientAddress = '0x'; // destination address
/// Construct a transaction to transfer
const tx = new Transaction();
const [coin] = tx.splitCoins(tx.gas, [amount]);
tx.transferObjects([coin], recipientAddress)
const input = {
transactionBlock: tx,
chain: 'sui:mainnet',
options: {
showEffects: true,
}
}
let signResult1 = await suiProvider.signTransaction(input)
let signResult2 = await suiProvider.signAndExecuteTransaction(input)
```
## Event event
[Same details as EVM compatibility chain](https://web3.okx.com/zh-hans/web3/build/docs/sdks/app-connect-evm-ui#event%E4%BA%8B%E4%BB%B6)
## Error code
[Same details as EVM compatibility chain](https://web3.okx.com/zh-hans/web3/build/docs/sdks/app-connect-evm-sdk#%E9%94%99%E8%AF%AF%E7%A0%81)
- [Aptos/Movement](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-aptos.md)
# Aptos/Movement
Aptos is a layer 1 public blockchain project that aims to develop a safe, scalable, and upgradeable Web3 infrastructure. Developed by former engineers from the Facebook crypto project Diem (formerly Libra), Aptos contracts are written using Move. Common Aptos-compatible networks include Movement.
If connecting via SDK, add a "Connect OKX Wallet" button within the DApp. Clicking this button will launch the mobile App Wallet and enable interaction with the OKX Wallet, such as retrieving addresses, initiating wallet signatures, and other functions.
In addition to SDK, we also provide a UI interface.
- [SDK](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-aptos-sdk.md)
# SDK
## Installation and Initialization
Make sure to update to version 6.92.0 or later to get started with access: integrating OKX Connect into your DApp can be done using npm:
```
npm install @okxconnect/aptos-provider
```
Before connecting to a wallet, you need to create an object that will be used to connect to the wallet, send transactions and so on.
```
OKXUniversalProvider.init({dappMetaData: {name, icon}})
```
**Request Parameterseters**
- dappMetaData - object
- name - string: the name of the app, will not be used as a unique representation
- icon - string: URL of the application icon, must be in PNG, ICO, etc. SVG icons are not supported. SVG icons are not supported. It is better to pass a url pointing to a 180x180px PNG icon.
**Return Value**
- OKXUniversalProvider
**Examples**
```typescript
import { OKXUniversalProvider } from "@okxconnect/universal-provider";
const okxUniversalProvider = await OKXUniversalProvider.init({
dappMetaData: {
name: "application name",
icon: "application icon url"
},
})
```
## Connecting to a wallet
Connect to the wallet to get the wallet address as an identifier and the necessary parameters for signing the transaction;
```
okxUniversalProvider.connect(connectParams: ConnectParams);
```
**Request Parameterseters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; information necessary to request a connection, the key is 'eip155' for EVM and 'aptos' for Aptos.
If any of the requested chains are not supported by the wallet, the wallet will reject the connection
- chains: string[]; chain id information
- defaultChain?: string; default chain
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information for requesting a connection, the key is 'eip155' for EVM and 'aptos' for Aptos.
If the corresponding chain information is not supported by the wallet, the connection can still be made;
- chains: string[]; Chain id information, if the corresponding chain information is not supported by the wallet, it can still be connected.
- defaultChain?: string; default chain
- sessionConfig: object
- redirect: string Jump parameter after successful connection, if it is Mini App in Telegram, here can be set to Telegram's deeplink: 'tg://resolve'
**Return value**
- Promise ``
- topic: string; the session identifier;
- namespaces: `Record`; namespace information for a successful connection;
- chains: string[]; Chain information for the connection
- accounts: string[]; accounts information for the connection
- methods: string[]; methods supported by the wallet under the current namespace
- defaultChain?: string; The default chain for the current session
- sessionConfig?: SessionConfig
- dappInfo: object DApp information
- name:string
- icon:string
- redirect?: string, the redirect parameter after successful connection
**Example**
```typescript
var session = await okxUniversalProvider.connect({
namespaces: {
aptos: {
chains: ["aptos:mainnet", // aptos mainnet
// "movement:mainnet",// movement mainnet
// "movement:testnet",// movement testnet
],
}
},
sessionConfig: {
redirect: "tg://resolve"
}
})
```
## Determine if the wallet is connected
Get whether the wallet is currently connected
**Return Value**
- boolean
**Example**
```typescript
okxUniversalProvider.connected();
```
## Prepare the transaction
First create an OKXAptosProvider object, with the constructor passing in OKXUniversalProvider
```typescript
import { OKXAptosProvider } from '@okxconnect/aptos-provider';
let okxAptosProvider = new OKXAptosProvider(okxUniversalProvider)
```
***Get the wallet address and publicKey***
```
okxAptosProvider.getAccount(chain);
```
**Request Parameters**
- chain: string, get the chain id of the wallet address, if not passed then the first connected aptos system address will be taken by default.
**Return Value**
- Object
- address: string The address of the wallet.
- publicKey: string Public Key
## Signature
```
okxAptosProvider.signMessage(message, chain)
```
**Request Parameterseters**
- message - object
- address?: boolean; // Should we include the address of the account in the message?
- application?: boolean; // Should we include the domain of the DApp
- chainId?: boolean; // Should we include the current chain id the wallet is connected to?
- message: string; // The message to be signed and displayed to the user
- nonce: string; // A nonce the DApp should generate
- chain: string, the chain to be signed, recommended; mandatory when connecting multiple chains;
**Return value**
- Promise - object
- address: string;
- application: string;
- chainId: number;
- fullMessage: string; // The message that was generated to sign
- message: string; // The message passed in by the user
- nonce: string;
- prefix: string; // Should always be APTOS
- signature: string; // The signed full message
## sign single transaction
```
okxAptosProvider.signTransaction(transaction, chain)
```
**Request Parameters**
- transaction - object | SimpleTransaction transaction data object
- chain: string, the chain for which the signature execution is requested, this parameter is recommended; mandatory when connecting multiple chains;
**Return Value**
- Promise - Buffer signed result.
## Sign transaction and broadcast on chain
```
okxAptosProvider.signAndSubmitTransaction(transaction, chain)
```
**Request Parameters**
- transaction - object | SimpleTransaction transaction data object
- chain: string, the chain for which the signature execution is requested, this parameter is recommended; mandatory when connecting multiple chains;
**Return Value**
- Promise - string transaction hash.
**Example**
```typescript
// Signature message
let data = {
address:true,
application:true,
chainId:true,
message:"Hello OKX",
nonce:"1234"
}
let provider = new OKXAptosProvider(okxUniversalConnectUI)
let message = await provider.signMessage(data, "aptos:mainnet")
//return value {'address': '0x2acddad65c27c6e5b568b398f0d1d01ebb8b55466461bbd51c1e42763a92fdfe', 'application': 'http://192.168.101.13',"' chainId": “aptos:mainnet”, “fullMessage”: 'APTOS\naddress: 0x2acddad65c27c6e5b568b398f0d1d01ebb8b55466461bbd51c1e42763a92fdfe\ napplication: http://192.168.101.13\nchainId: aptos:mainnet\nmessage: 123 Signature Test! \nnonce: 1234', “message”: '123 Signature test!' , 'nonce': '1234', 'prefix': 'APTOS', 'signature':' 0xef4e587f537b80a2f4e424079984b80e130c92d939a92225764be00ed36486521e8857b8a222de4023c5f4d2e9fd2f62c26ca8a43694660583c8a5d4328da303 ', 'verified':true}
// Sign the transaction and upload it
const config = new AptosConfig({ network: Network.MAINNET });
const aptos = new Aptos(config);
// Support for transactions created via @aptos-labs/ts-sdk
const transaction = await aptos.transaction.build.simple({
sender: "0x07897a0496703c27954fa3cc8310f134dd1f7621edf5e88b5bf436e4af70cfc6",
data: {
function: "0x80273859084bc47f92a6c2d3e9257ebb2349668a1b0fb3db1d759a04c7628855::router::swap_exact_coin_for_coin_x1",
typeArguments: ["0x1::aptos_coin::AptosCoin", "0x111ae3e5bc816a5e63c2da97d0aa3886519e0cd5e4b046659fa35796bd11542a::stapt_token::StakedApt", "0x0163df34fccbf003ce219d3f1d9e70d140b60622cb9dd47599c25fb2f797ba6e::curves::Uncorrelated", "0x80273859084bc47f92a6c2d3e9257ebb2349668a1b0fb3db1d759a04c7628855::router::BinStepV0V05"],
functionArguments: ["10000", ["9104"], ["5"], ["true"]],
},
});
let result1 = await provider.signAndSubmitTransaction(transaction, "aptos:mainnet")
//Support transactions in the following data formats at the same time
let transactionData = {
"arguments": ["100000",["0","0","10533"],["10","5","5"],["false","false","true"]],
"function": "0x80273859084bc47f92a6c2d3e9257ebb2349668a1b0fb3db1d759a04c7628855::router::swap_exact_coin_for_coin_x3",
"type": "entry_function_payload",
"type_arguments": ["0x1::aptos_coin::AptosCoin","0x73eb84966be67e4697fc5ae75173ca6c35089e802650f75422ab49a8729704ec::coin::DooDoo","0x53a30a6e5936c0a4c5140daed34de39d17ca7fcae08f947c02e979cef98a3719::coin::LSD","0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC","0x80273859084bc47f92a6c2d3e9257ebb2349668a1b0fb3db1d759a04c7628855::router::CurveV1","0x0163df34fccbf003ce219d3f1d9e70d140b60622cb9dd47599c25fb2f797ba6e::curves::Uncorrelated","0x0163df34fccbf003ce219d3f1d9e70d140b60622cb9dd47599c25fb2f797ba6e::curves::Uncorrelated","0x54cb0bb2c18564b86e34539b9f89cfe1186e39d89fce54e1cd007b8e61673a85::bin_steps::X80","0x80273859084bc47f92a6c2d3e9257ebb2349668a1b0fb3db1d759a04c7628855::router::BinStepV0V05","0x80273859084bc47f92a6c2d3e9257ebb2349668a1b0fb3db1d759a04c7628855::router::BinStepV0V05"]
}
let result2 = await provider.signAndSubmitTransaction(transactionData, "movement:testnet")
```
## Disconnect wallet
Disconnect the connected wallet and delete the current session. If you want to switch the connected wallet, please disconnect the current wallet first.
```typescript
okxUniversalProvider.disconnect();
```
## Event
```typescript
// Generate universalLink
okxUniversalProvider.on('display_uri', (uri) => {
console.log(uri);
});
// Session information changes will trigger this event;
okxUniversalProvider.on('session_update', (session) => {
console.log(JSON.stringify(session)); // Session information changes (e.g., adding a custom chain).
});
// Disconnecting triggers this event;
okxUniversalProvider.on('session_delete', ({topic}) => {
console.log(topic);
});
```
## Error codes
Exceptions that may be thrown during connection, transaction, and disconnection.
**Exception**
| Error Code | Description |
|----------------------------------------------|-------------------------|
| OKX_CONNECT_ERROR_CODES.UNKNOWN_ERROR | Unknown Error |
| OKX_CONNECT_ERROR_CODES.ALREADY_CONNECTED_ERROR | Wallet Already Connected |
| OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR | Wallet Not Connected |
| OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR | User Rejected |
| OKX_CONNECT_ERROR_CODES.METHOD_NOT_SUPPORTED | Method Not Supported |
| OKX_CONNECT_ERROR_CODES.CHAIN_NOT_SUPPORTED | Chain Not Supported |
| OKX_CONNECT_ERROR_CODES.WALLET_NOT_SUPPORTED | Wallet Not Supported |
| OKX_CONNECT_ERROR_CODES.CONNECTION_ERROR | Connection Error |
```typescript
export enum OKX_CONNECT_ERROR_CODES {
UNKNOWN_ERROR = 0,
ALREADY_CONNECTED_ERROR = 11,
NOT_CONNECTED_ERROR = 12,
USER_REJECTS_ERROR = 300,
METHOD_NOT_SUPPORTED = 400,
CHAIN_NOT_SUPPORTED = 500,
WALLET_NOT_SUPPORTED = 600,
CONNECTION_ERROR = 700
}
```
- [UI](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-aptos-ui.md)
# UI
## Installation and Initialization
Make sure to update the OKX App to version 6.92.0 or later to begin access:
To integrate OKX Connect into your DApp, you can use npm:
```
npm install @okxconnect/ui
npm install @okxconnect/aptos-provider
```
Before connecting to the wallet, you need to create an object that can provide a UI interface for subsequent operations such as connecting to the wallet and sending transactions.
```
OKXUniversalConnectUI.init(dappMetaData, actionsConfiguration, uiPreferences, language)
```
**Request Parameterseters**
- dappMetaData - object
- name - string: The name of the application, will not be used as a unique representation.
- icon - string: URL of the application icon, must be in PNG, ICO, etc. SVG icons are not supported. SVG icons are not supported. It is best to pass a url pointing to a 180x180px PNG icon.
- actionsConfiguration - object
- modals - ('before' | 'success' | 'error')[] | 'all' The modes of displaying alerts during transaction, defaults to 'before'.
- returnStrategy -string 'none' | `${string}://${string}`; for app wallet, specify the return strategy for the deep link when the user signs/rejects the request, if it is in telegram, you can configure tg://resolve
- uiPreferences -object
- theme - Theme can be: THEME.DARK, THEME.LIGHT, 'SYSTEM'.
- language - 'en_US' | 'ru_RU' | 'zh_CN' | 'ar_AE' | 'cs_CZ' | 'de_DE' | 'es_ES' | 'es_LAT' | 'fr_FR' | 'id_ID' | 'it_IT' | 'nl_NL' | 'pl_PL' | 'pt_BR' | 'pt_PT' | 'ro_RO' | 'tr_TR' | 'uk_UA' | 'vi_VN'.
, defaults to en_US
**Return value**
- OKXUniversalConnectUI
**Examples**
```typescript
import { OKXUniversalConnectUI } from "@okxconnect/ui";
const okxUniversalConnectUI = await OKXUniversalConnectUI.init({
dappMetaData: {
icon: "https://static.okx.com/cdn/assets/imgs/247/58E63FEA47A2B7D7.png",
name: "OKX Connect Demo"
},
actionsConfiguration: {
returnStrategy: 'tg://resolve',
modals:"all"
},
language: "en_US",
uiPreferences: {
theme: THEME.LIGHT
},
});
```
## Connecting to a wallet
Connecting to a wallet goes to get the wallet address as an identifier and the necessary parameters used to sign the transaction.
```
okxUniversalConnectUI.openModal(connectParams: ConnectParams);
```
**Request Parameterseters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; Necessary information for the requested connection, the key is 'eip155' for EVM systems and 'aptos' for Aptos systems.
If any of the requested chains are not supported by the wallet, the wallet will reject the connection
- chains: string[]; chain id information
- defaultChain?: string; default chain
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information for requesting a connection, the key for EVM is 'eip155' and for Aptos is 'aptos'.
If the corresponding chain information is not supported by the wallet, the connection can still be made
- chains: string[]; Chain id information
- defaultChain?: string; default chain
- sessionConfig: object
- redirect: string Jump parameter after successful connection, if it is Mini App in Telegram, here can be set to Telegram's deeplink: 'tg://resolve'
**Return value**
- Promise ``
- topic: string; the session identifier;
- namespaces: `Record`; namespace information for a successful connection;
- chains: string[]; Chain information for the connection
- accounts: string[]; accounts information for the connection
- methods: string[]; methods supported by the wallet under the current namespace
- defaultChain?: string; The default chain for the current session.
- sessionConfig?: SessionConfig
- dappInfo: object DApp information
- name:string
- icon:string
- redirect?: string, the redirect parameter after successful connection
**Example**
```typescript
var session = await okxUniversalConnectUI.openModal({
namespaces: {
aptos: {
chains: ["aptos:mainnet", // aptos mainnet
// "movement:mainnet",// movement mainnet
// "movement:testnet",// movement testnet
],
}
},
sessionConfig: {
redirect: "tg://resolve"
}
})
```
## Connect to wallet and sign
Connect to the wallet to get the wallet address and sign the data; the result will be called back in the event 'connect_signResponse'
```
await okxUniversalConnectUI.openModalAndSign(connectParams: ConnectParams, signRequest: RequestParams[]);
```
**Request Parameters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; information necessary to request a connection, the key for Aptos is 'aptos'.
If any of the requested chains are not supported by the wallet, the wallet will reject the connection
- chains: string[]; chain id information
- defaultChain?: string; default chain
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information about the requested connection, the key for Aptos is 'aptos'.
If the corresponding chain information is not supported by the wallet, the connection can still be made
- chains: string[]; chain id information
- defaultChain?: string; default chain
- sessionConfig: object
- redirect: string Jump parameter after successful connection, if it is a Mini App in Telegram, here can be set to Telegram's deeplink: 'tg://resolve'
- signRequest - RequestParams[]; the method to request the connection and sign the request, at most one method can be supported at the same time;
- method: string; the name of the requested method, Aptos supports the method: 'aptos_signMessage'
- chainId: string; the ID of the chain in which the method is executed, this chainId must be included in the namespaces above
- params: unknown[] | Record`` | object | undefined; Parameters corresponding to the requested method
**Return Value**
- Promise ``
- topic: string; the session identifier
- namespaces: `Record`; namespace information for a successful connection
- chains: string[]; Chain information for the connection
- accounts: string[]; accounts information for the connection
- methods: string[]; Methods supported by the wallet in the current namespace
- defaultChain?: string; The default chain for the current session
- sessionConfig?: SessionConfig
- dappInfo: object DApp information;
- name: string
- icon:string
**Example**
```typescript
// Add the signature result monitor first
okxUniversalConnectUI.on("connect_signResponse", (signResponse) => {
console.log(signResponse);
});
var session = await okxUniversalConnectUI.openModalAndSign({
namespaces: {
aptos: {
chains: ["aptos:mainnet", // aptos mainnet
// "movement:testnet",// movement testnet
],
}
},
sessionConfig: {
redirect: "tg://resolve"
}
},
[
{
chainId: "aptos:mainnet",
method: "aptos_signMessage",
params: {
address: true,
application: true,
chainId: true,
message: "Hello Aptos",
nonce: "1234"
}
}
])
```
## Determine if the wallet is connected
Check if wallet is connected
**Return Value**
- boolean
**Example**
```typescript
okxUniversalConnectUI.connected();
```
## Prepare the transaction
First create an OKXAptosProvider object, with the constructor passing in OKXUniversalProvider
```typescript
import { OKXAptosProvider } from '@okxconnect/aptos-provider' ;
let okxAptosProvider = new OKXAptosProvider(okxUniversalConnectUI)
```
***Get the wallet address and publicKey***
```
okxAptosProvider.getAccount(chain);
```
**Request Parameters**
- chain: string, the id of the chain to get the wallet address, if you don't pass it, it will take the first connected aptos system address.
**Return Value**
- Object
- address: string The address of the wallet.
- publicKey: string Public key
## Signature
```
okxAptosProvider.signMessage(message, chain);
```
**Request Parameterseters**
- message - object
- address?: boolean; // Should we include the address of the account in the message
- application?: boolean; // Should we include the domain of the DApp
- chainId?: boolean; // Should we include the current chain id the wallet is connected to
- message: string; // The message to be signed and displayed to the user
- nonce: string; // A nonce the DApp should generate
- chain: string, the chain to be executed by the request signature, it is recommended to pass this parameter; it is mandatory to pass this parameter when connecting multiple chains;
**Return value**
- Promise - object
- address: string;
- application: string;
- chainId: number;
- fullMessage: string; // The message that was generated to sign
- message: string; // The message passed in by the user
- nonce: string;
- prefix: string; // Should always be APTOS
- signature: string; // The signed full message
## sign single transaction
```
okxAptosProvider.signTransaction(transaction, chain);
```
**Request Parameters**
- transaction - object | SimpleTransaction transaction data object
- chain: string, the chain for which the signature execution is requested, this parameter is recommended; mandatory when connecting multiple chains;
**Return Value**
- Promise - Buffer signed result.
## Sign transaction and broadcast on chain
`okxAptosProvider.signAndSubmitTransaction(transaction, chain);`
**Request Parameters**
- transaction - object | SimpleTransaction transaction data object
- chain: string, the chain for which the signature execution is requested, this parameter is recommended; mandatory when connecting multiple chains;
**Return Value**
- Promise - string transaction hash.
**Example**
```typescript
// Signature message
let data = {
address:true,
application:true,
chainId:true,
message:"Hello OKX",
nonce:"1234"
}
let provider = new OKXAptosProvider(okxUniversalConnectUI)
let message = await provider.signMessage(data, "aptos:mainnet")
//return value {'address': '0x2acddad65c27c6e5b568b398f0d1d01ebb8b55466461bbd51c1e42763a92fdfe', 'application': 'http://192.168.101.13',"' chainId": “aptos:mainnet”, “fullMessage”: 'APTOS\naddress: 0x2acddad65c27c6e5b568b398f0d1d01ebb8b55466461bbd51c1e42763a92fdfe\ napplication: http://192.168.101.13\nchainId: aptos:mainnet\nmessage: 123 Signature Test! \nnonce: 1234', “message”: '123 Signature test!' , 'nonce': '1234', 'prefix': 'APTOS', 'signature':' 0xef4e587f537b80a2f4e424079984b80e130c92d939a92225764be00ed36486521e8857b8a222de4023c5f4d2e9fd2f62c26ca8a43694660583c8a5d4328da303 ', 'verified':true}
// Sign the transaction and upload it
const config = new AptosConfig({ network: Network.MAINNET });
const aptos = new Aptos(config);
// Support for transactions created via @aptos-labs/ts-sdk
const transaction = await aptos.transaction.build.simple({
sender: '0x07897a0496703c27954fa3cc8310f134dd1f7621edf5e88b5bf436e4af70cfc6',
data: {
function: '0x80273859084bc47f92a6c2d3e9257ebb2349668a1b0fb3db1d759a04c7628855::router::swap_exact_coin_for_coin_x1',
typeArguments: ['0x1::aptos_coin::AptosCoin', '0x111ae3e5bc816a5e63c2da97d0aa3886519e0cd5e4b046659fa35796bd11542a::stapt_token:. StakedApt', '0x0163df34fccbf003ce219d3f1d9e70d140b60622cb9dd47599c25fb2f797ba6e::curves::Uncorrelated', ' 0x80273859084bc47f92a6c2d3e9257ebb2349668a1b0fb3db1d759a04c7628855::router::BinStepV0V05'],
functionArguments: ['10000', ['9104'], ['5'], ['true']],
}
});
let result1 = await provider.signAndSubmitTransaction(transaction, 'aptos:mainnet');
// Transactions that also support the following data formats
let transactionData = {
"arguments": ["100000",["0","0","10533"],["10","5","5"],["false","false","true"]],
"function": "0x80273859084bc47f92a6c2d3e9257ebb2349668a1b0fb3db1d759a04c7628855::router::swap_exact_coin_for_coin_x3",
"type": "entry_function_payload",
"type_arguments": ["0x1::aptos_coin::AptosCoin","0x73eb84966be67e4697fc5ae75173ca6c35089e802650f75422ab49a8729704ec::coin::DooDoo","0x53a30a6e5936c0a4c5140daed34de39d17ca7fcae08f947c02e979cef98a3719::coin::LSD","0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC","0x80273859084bc47f92a6c2d3e9257ebb2349668a1b0fb3db1d759a04c7628855::router::CurveV1","0x0163df34fccbf003ce219d3f1d9e70d140b60622cb9dd47599c25fb2f797ba6e::curves::Uncorrelated","0x0163df34fccbf003ce219d3f1d9e70d140b60622cb9dd47599c25fb2f797ba6e::curves::Uncorrelated","0x54cb0bb2c18564b86e34539b9f89cfe1186e39d89fce54e1cd007b8e61673a85::bin_steps::X80","0x80273859084bc47f92a6c2d3e9257ebb2349668a1b0fb3db1d759a04c7628855::router::BinStepV0V05","0x80273859084bc47f92a6c2d3e9257ebb2349668a1b0fb3db1d759a04c7628855::router::BinStepV0V05"]
}
let result2 = await provider.signAndSubmitTransaction(transactionData, "movement:testnet")
```
## Disconnect wallet
Disconnect the connected wallet and delete the current session. If you want to switch the connected wallet, please disconnect the current wallet first.
```typescript
okxUniversalConnectUI.disconnect();
```
## Event
```typescript
// Generate universalLink
okxUniversalConnectUI.on("display_uri", (uri) => {
console.log(uri);
});
// Session information changes will trigger this event;
okxUniversalConnectUI.on("session_update", (session) => {
console.log(JSON.stringify(session));
});
// Disconnecting triggers this event;
okxUniversalConnectUI.on("session_delete", ({topic}) => {
console.log(topic);
});
// This event is triggered when a connection is made and the signature is signed.
okxUniversalConnectUI.on("connect_signResponse", (signResponse) => {
console.log(signResponse);
});
```
## Error codes
Exceptions that may be thrown during connection, transaction, and disconnection.
**Exception**
| Error Code | Description |
|----------------------------------------------|-------------------------|
| OKX_CONNECT_ERROR_CODES.UNKNOWN_ERROR | Unknown Error |
| OKX_CONNECT_ERROR_CODES.ALREADY_CONNECTED_ERROR | Wallet Already Connected |
| OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR | Wallet Not Connected |
| OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR | User Rejected |
| OKX_CONNECT_ERROR_CODES.METHOD_NOT_SUPPORTED | Method Not Supported |
| OKX_CONNECT_ERROR_CODES.CHAIN_NOT_SUPPORTED | Chain Not Supported |
| OKX_CONNECT_ERROR_CODES.WALLET_NOT_SUPPORTED | Wallet Not Supported |
| OKX_CONNECT_ERROR_CODES.CONNECTION_ERROR | Connection Error |
```typescript
export enum OKX_CONNECT_ERROR_CODES {
UNKNOWN_ERROR = 0,
ALREADY_CONNECTED_ERROR = 11,
NOT_CONNECTED_ERROR = 12,
USER_REJECTS_ERROR = 300,
METHOD_NOT_SUPPORTED = 400,
CHAIN_NOT_SUPPORTED = 500,
WALLET_NOT_SUPPORTED = 600,
CONNECTION_ERROR = 700
}
```
- [Cosmos/Sei](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-cosmos.md)
# Cosmos/Sei
The Cosmos network consists of many independent, parallel blockchains, called zones, each powered by classical Byzantine fault-tolerant (BFT) consensus protocols like Tendermint (already used by platforms like ErisDB). Some zones act as hubs with respect to other zones, allowing many zones to interoperate through a shared hub. The architecture is a more general application of the Bitcoin sidechains concept, using classic BFT and Proof-of-Stake algorithms, instead of Proof-of-Work. Cosmos can interoperate with multiple other applications and cryptocurrencies, something other blockchains can't do well. By creating a new zone, you can plug any blockchain system into the Cosmos hub and pass tokens back and forth between those zones, without the need for an intermediary.
If connecting via SDK, add a "Connect OKX Wallet" button within the DApp. Clicking this button will launch the mobile App Wallet and enable interaction with the OKX Wallet, such as retrieving addresses, initiating wallet signatures, and other functions.
In addition to SDK, we also provide a UI interface.
- [SDK](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-cosmos-sdk.md)
# SDK
## Installation and Initialization
Make sure to update to version 6.94.0 or later to start integrating OKX Connect into your DApp can be done using npm:
```
npm install @okxconnect/universal-provider
```
Before connecting to a wallet, you need to create an object for subsequent operations such as connecting to the wallet and sending transactions.
```
OKXUniversalProvider.init({dappMetaData: {name, icon}})
```
**Request Parameters**
- dappMetaData - object
- name - string: The name of the application, will not be used as a unique representation.
- icon - string: URL of the application icon, must be in PNG, ICO, etc. SVG icons are not supported. SVG icons are not supported. It is better to pass a url pointing to a 180x180px PNG icon.
**Returns Value**
- OKXUniversalProvider
**Examples**
```typescript
import { OKXUniversalProvider } from "@okxconnect/universal-provider";
const okxUniversalProvider = await OKXUniversalProvider.init({
dappMetaData: {
name: "application name",
icon: "application icon url"
},
})
```
## Connecting to a wallet
Connect to the wallet to get the wallet address as an identifier and the necessary parameters for signing transactions.
```
okxUniversalProvider.connect(connectParams: ConnectParams);
```
**Request Parameters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; Optional information about the requested connection, the key of the COSMOS system is 'cosmos', the wallet will reject the connection if any of the requested chains are not supported by the wallet;
- chains: string[]; chain id information
- defaultChain?: string; defaultChain
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information of the requested connection, the key of COSMOS is 'cosmos', if the corresponding chain is not supported by the wallet, it can still be connected;
- chains: string[]; chain id information
- defaultChain?: string; default chain
- sessionConfig: object
- redirect: string The jump parameter after successful connection, if it is Mini App in Telegram, here can be set to Telegram's deeplink: 'tg://resolve'
**Returns Value**
- Promise ``
- topic: string; Session identifier;
- namespaces: `Record`; namespace information for a successful connection;
- chains: string[]; Chain information for the connection;
- accounts: string[]; accounts information for the connection;
- methods: string[]; Methods supported by the wallet in the current namespace;
- defaultChain?: string; The default chain for the current session.
- sessionConfig?: SessionConfig
- dappInfo: object DApp information;
- name: string
- icon:string
- redirect?: string, the redirect parameter after successful connection;
**Example**
```typescript
var session = await okxUniversalProvider.connect({
namespaces: {
cosmos: {
chains: [
"cosmos:cosmoshub-4",
// "cosmos:osmosis-1"
],
}
},
sessionConfig: {
redirect: "tg://resolve"
}
})
```
## Determine if the wallet is connected
Gets whether the wallet is currently connected.
**Return Value**
- boolean
**Example**
```typescript
okxUniversalProvider.connected();
```
## Prepare the transaction
First create an OKXCosmosProvider object and pass OKXUniversalProvider into the constructor.
```typescript
import { OKXCosmosProvider } from "@okxconnect/universal-provider";
let okxCosmosProvider = new OKXCosmosProvider(okxUniversalProvider)
```
## Get account information
```
okxCosmosProvider.getAccount(chainId);
```
***Request Parameterseters***
- chainId: the requested chain, e.g. cosmos:cosmoshub-4, cosmos:osmosis-1
***Return Value***
- Object
- algo: 'secp256k1',
- address: string wallet-address, bech32Address: string wallet-address, bech32Address
- bech32Address: string walletAddress, pubKey: Uint8Address, pubKey: Uint8Address
- pubKey: Uint8Array publicKey, pubKey: Uint8Array publicKey, pubKey: Uint8Array publicKey
***Example***
```typescript
let result = okxCosmosProvider.getAccount("cosmos:cosmoshub-4")
//Return structure
{
"algo": "secp256k1",
"address": "cosmos1u6lts9ng4etxj0zdaxsada6zgl8dudpg3ygvjw",
"bech32Address": "cosmos1u6lts9ng4etxj0zdaxsada6zgl8dudpg3ygvjw",
"pubKey": {
"0": 2,
"1": 68,
"2": 110,
...
"32": 144
}
}
```
## Sign the message
```
okxCosmosProvider.signArbitrary(chain, signerAddress, message);
```
***Request Parameters***
- chain - string, chain of requested execution methods
- signerAddress - string The address of the signature wallet.
- message - string The message to be signed.
***Return Value***
- Promise - object
- pub_key : object
- type:string Public key type
- value: string Public key
- signature: string Signature result
***Example***
```ts
let chain = "cosmos:cosmoshub-4"
let signStr = "data need to sign ..."
let result = okxCosmosProvider.signArbitrary(chain, signStr)
//Return structure: {"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AkRuGelKwOg+qJbScSUHV36zn73S1q6fD8C5dZ8furqQ"},"signature":"YSyndEFlHYTWpSXsn28oolZpKim/BnmCVD0hZfvPQHQV3Bc0B0EU77CKE6LpV+PUJn19d1skAQy/bXyzppnuxw=="}
```
## SignAmino
```
okxCosmosProvider.signAmino(chainId: string, signerAddress: string, signDoc: StdSignDoc, signOptions?: object);
```
***Request Parameters***
- chainId - string, the chain for which the signature execution is requested, mandatory parameter
- signerAddress - string, the address of the wallet.
- signDoc - object, the transaction information to be signed in a fixed format, similar to cosmjs OfflineSigner signAmino method, the parameters are objects, signDoc is a fixed format.
***signDoc is a fixed format.
- Promise - Object
- signed - object,transaction information
- signature -object, the result of the signature.
***Example***
```ts
let signDoc = {
"chain_id": "osmosis-1",
"account_number": "630104",
"sequence": "480",
"fee": {"gas": "683300", "amount": [{"denom": "uosmo", "amount": "2818"}]},
"msgs": [{
"type": "osmosis/poolmanager/swap-exact-amount-in",
"value": {
"sender": "osmo1u6lts9ng4etxj0zdaxsada6zgl8dudpgelmuyu",
"routes": [{
"pool_id": "1096",
"token_out_denom": "ibc/987C17B11ABC2B20019178ACE62929FE9840202CE79498E29FE8E5CB02B7C0A4"
}, {
"pool_id": "611",
"token_out_denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2"
}],
"token_in": {"denom": "uosmo", "amount": "100"},
"token_out_min_amount": "8"
}
}],
"memo": "FE",
"timeout_height": "23603788",
"signOptions": {
"useOneClickTrading": false,
"preferNoSetFee": true,
"fee": {"gas": "683300", "amount": [{"denom": "uosmo", "amount": "2818"}]}
}
}
let res = await provider.signAmino("cosmos:osmosis-1", provider.getAccount("cosmos:osmosis-1").address, signDoc)
/**
Return structure:
{
"signed": {
"chain_id": "osmosis-1",
"account_number": "630104",
"sequence": "480",
"fee": {
"amount": [
{
"amount": "12500",
"denom": "uosmo"
}
],
"gas": "500000"
},
"msgs": [
{
"type": "osmosis/poolmanager/swap-exact-amount-in",
"value": {
"sender": "osmo1u6lts9ng4etxj0zdaxsada6zgl8dudpgelmuyu",
"routes": [
{
"pool_id": "1096",
"token_out_denom": "ibc/987C17B11ABC2B20019178ACE62929FE9840202CE79498E29FE8E5CB02B7C0A4"
},
{
"pool_id": "611",
"token_out_denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2"
}
],
"token_in": {
"denom": "uosmo",
"amount": "100"
},
"token_out_min_amount": "8"
}
}
],
"memo": "FE",
"timeout_height": "23603788",
"signOptions": {
"useOneClickTrading": false,
"preferNoSetFee": true,
"fee": {
"gas": "683300",
"amount": [
{
"denom": "uosmo",
"amount": "2818"
}
]
}
}
},
"signature": {
"pub_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "AkRuGelKwOg+qJbScSUHV36zn73S1q6fD8C5dZ8furqQ"
},
"signature": "2Brt/w+1U3C+tIbsI//pv9zTYca9WlBd1eKm/Gde5MFaRagmxtsn6h2beP7+4R4MDav7r1G+0Nxd5arB0qVfUw=="
}
}
*/
```
## SignDirect
```
okxCosmosProvider.signDirect(chainId, signerAddress, signDoc, signOptions?);
```
***Request Parameters***
- chainId - string, the chain where the signature execution is requested, mandatory parameter.
- signerAddress - string, wallet address
- signDoc - object transaction data
- bodyBytes ,Uint8Array
- authInfoBytes, Uint8Array
- chainId, string
- accountNumber, string
***Return Value***
- Promise - Object
- signed - object,transaction information
- signature -object, the result of the signature.
***Example***
```ts
let signDoc = {
"bodyBytes": Uint8Array,
"authInfoBytes": Uint8Array,
"chainId": "osmosis-1",
"accountNumber": "630104",
}
let res = await provider.signDirect("cosmos:osmosis-1", provider.getAccount("cosmos:osmosis-1").address, signDoc)
/**
The return structure is the same as
{
"signed": {
"bodyBytes": {
"type": "Buffer",
"data": [
10, 193, 1, 10, 41, ...]
},
"authInfoBytes": {
"0": 10,
"1": 81,
...
},
"chainId": "osmosis-1",
"accountNumber": "630104"
},
"signature": {
"pub_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "AkRuGelKwOg+qJbScSUHV36zn73S1q6fD8C5dZ8furqQ"
},
"signature": "YpX2kGmbZYVxUqK8y9OCweJNgZkS4WaS79nBDfOJaTgowPfY0gSbXSQeRLlif2SIkBqcwTNSItBqb5M7a6K30g=="
}
}
*/
```
## Disconnect wallet
Disconnect the connected wallet and delete the current session. If you want to switch the connected wallet, please disconnect the current wallet first.
```typescript
okxUniversalProvider.disconnect();
```
## Event
```typescript
// Generate universalLink
okxUniversalProvider.on('display_uri', (uri) => {
console.log(uri);
});
// Session information changes will trigger this event;
okxUniversalProvider.on('session_update', (session) => {
console.log(JSON.stringify(session)); // Session information changes (e.g., adding a custom chain).
});
// Disconnecting triggers this event;
okxUniversalProvider.on('session_delete', ({topic}) => {
console.log(topic);
});
```
## Error codes
Exceptions that may be thrown during connection, transaction, and disconnection.
**Exception**
| Error Code | Description |
|----------------------------------------------|-------------------------|
| OKX_CONNECT_ERROR_CODES.UNKNOWN_ERROR | Unknown Error |
| OKX_CONNECT_ERROR_CODES.ALREADY_CONNECTED_ERROR | Wallet Already Connected |
| OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR | Wallet Not Connected |
| OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR | User Rejected |
| OKX_CONNECT_ERROR_CODES.METHOD_NOT_SUPPORTED | Method Not Supported |
| OKX_CONNECT_ERROR_CODES.CHAIN_NOT_SUPPORTED | Chain Not Supported |
| OKX_CONNECT_ERROR_CODES.WALLET_NOT_SUPPORTED | Wallet Not Supported |
| OKX_CONNECT_ERROR_CODES.CONNECTION_ERROR | Connection Error |
```typescript
export enum OKX_CONNECT_ERROR_CODES {
UNKNOWN_ERROR = 0,
ALREADY_CONNECTED_ERROR = 11,
NOT_CONNECTED_ERROR = 12,
USER_REJECTS_ERROR = 300,
METHOD_NOT_SUPPORTED = 400,
CHAIN_NOT_SUPPORTED = 500,
WALLET_NOT_SUPPORTED = 600,
CONNECTION_ERROR = 700
}
```
- [UI](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-cosmos-ui.md)
# UI
## Installation and Initialization
Make sure to update the OKX App to version 6.94.0 or later to start integrating OKX Connect into your DApp can be done using npm:
```
npm install @okxconnect/ui
npm install @okxconnect/universal-provider
```
Before connecting to a wallet, you need to create an object that can provide a UI interface for subsequent operations such as connecting to the wallet and sending transactions.
```
OKXUniversalConnectUI.init(dappMetaData, actionsConfiguration, uiPreferences, language)
```
**Request Parameterseters**
- dappMetaData - object
- name - string: The name of the application, will not be used as a unique representation.
- icon - string: URL of the application icon, must be in PNG, ICO, etc. SVG icons are not supported. SVG icons are not supported. It is best to pass a url pointing to a 180x180px PNG icon.
- actionsConfiguration - object
- modals - ('before' | 'success' | 'error')[] | 'all' The modes of displaying alerts during transaction, defaults to 'before'.
- returnStrategy -string 'none' | `${string}://${string}`; for app wallet, specify the return strategy for the deep link when the user signs/rejects the request, if it is in telegram, you can configure tg://resolve
- uiPreferences -object
- theme - Theme can be: THEME.DARK, THEME.LIGHT, 'SYSTEM'.
- language - 'en_US' | 'ru_RU' | 'zh_CN' | 'ar_AE' | 'cs_CZ' | 'de_DE' | 'es_ES' | 'es_LAT' | 'fr_FR' | 'id_ID' | 'it_IT' | 'nl_NL' | 'pl_PL' | 'pt_BR' | 'pt_PT' | 'ro_RO' | 'tr_TR' | 'uk_UA' | 'vi_VN'.
, defaults to en_US
**Return value**
- OKXUniversalConnectUI
**Example**
```typescript
import { OKXUniversalConnectUI } from "@okxconnect/ui";
const okxUniversalConnectUI = await OKXUniversalConnectUI.init({
dappMetaData: {
icon: "https://static.okx.com/cdn/assets/imgs/247/58E63FEA47A2B7D7.png",
name: "OKX Connect Demo"
},
actionsConfiguration: {
returnStrategy: 'tg://resolve',
modals:"all",
tmaReturnUrl:'back'
},
language: "en_US",
uiPreferences: {
theme: THEME.LIGHT
},
});
```
## Connect to a wallet
Connect to the wallet to get the wallet address as an identifier and the necessary parameters for signing transactions.
```
okxUniversalConnectUI.connect(connectParams: ConnectParams);
```
**Request Parameters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; Optional information about the requested connection, the key is 'eip155' for EVM, 'cosmos' for COSMOS, if any of the requested chain is not supported by the wallet, the wallet will reject the connection;
- chains: string[]; chain id information
- defaultChain?: string; defaultChain
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information of the requested connection, the key of EVM is 'eip155', the key of COSMOS is 'cosmos', if the corresponding chain information is not supported by the wallet, it can still be connected;
- chains: string[]; chain id information
- defaultChain?: string; default chain
- sessionConfig: object
- redirect: string Jump parameter after successful connection, if it is a Mini App in Telegram, you can set it to deeplink of Telegram: 'tg://resolve'.
**Return Value**
- Promise ``
- topic: string; The session identifier;
- namespaces: `Record`; namespace information for a successful connection;
- chains: string[]; Chain information for the connection;
- accounts: string[]; accounts information for the connection;
- methods: string[]; Methods supported by the wallet in the current namespace;
- defaultChain?: string; The default chain for the current session.
- sessionConfig?: SessionConfig
- dappInfo: object DApp information;
- name: string
- icon:string
- redirect?: string, the redirect parameter after successful connection;
**Example**
```typescript
var session = await okxUniversalConnectUI.connect({
namespaces: {
cosmos: {
chains: [
"cosmos:cosmoshub-4",
// "cosmos:osmosis-1"
],
}
},
sessionConfig: {
redirect: "tg://resolve"
}
})
```
## Connect to wallet and sign
Connect to the wallet to get the wallet address and sign the data; the result will be called back in the event 'connect_signResponse';
```
await okxUniversalConnectUI.openModalAndSign(connectParams: ConnectParams, signRequest: RequestParams[]);
```
**Request Parameters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; Optional information about the requested connection, the key of COSMOS system is 'cosmos', if any of the requested chain is not supported by the wallet, the wallet will reject the connection
- chains: string[]; chain id information
- defaultChain?: string; defaultChain
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information of the requested connection, the key of COSMOS is 'cosmos', if the corresponding chain information is not supported by the wallet, it can still be connected
- chains: string[]; chain id information
- defaultChain?: string; default chain
- sessionConfig: object
- redirect: string The jump parameter after successful connection, if it is Mini App in Telegram, here can be set to Telegram's deeplink: 'tg://resolve'
- signRequest - RequestParams[]; the method to request the connection and sign the request, at most one method can be supported at the same time
- method: string; the name of the requested method, COSMOS supports the following methods: 'cosmos_signArbitrary'
- chainId: string; the ID of the chain where the method is executed, the chainId must be included in the namespaces above
- params: unknown[] | Record`` | object | undefined; Parameters corresponding to the requested method
**Return Value**
- Promise ``
- topic: string; the session identifier;
- namespaces: `Record`; namespace information for a successful connection;
- chains: string[]; Chain information for the connection;
- accounts: string[]; accounts information for the connection;
- methods: string[]; Methods supported by the wallet in the current namespace;
- defaultChain?: string; The default chain for the current session.
- sessionConfig?: SessionConfig
- dappInfo: object DApp information;
- name: string
- icon:string
**Example**
```typescript
// Add the signature result minitor first
okxUniversalConnectUI.on("connect_signResponse", (signResponse) => {
console.log(signResponse);
});
var session = await okxUniversalConnectUI.openModalAndSign({
namespaces: {
cosmos: {
chains: [
"cosmos:cosmoshub-4",
// "cosmos:osmosis-1"
],
}
},
sessionConfig: {
redirect: "tg://resolve"
}
},
[
{
chainId: "cosmos:cosmoshub-4",
method: "cosmos_signArbitrary",
params: {
message: "Hello Cosmos"
}
}
])
```
## Determine if the wallet is connected
Gets whether the wallet is currently connected.
**Return Value**
- boolean
**Example**
```typescript
okxUniversalConnectUI.connected();
```
## Prepare the transaction
First create an OKXCosmosProvider object, with the constructor passing in OKXUniversalConnectUI
``` Type script
import { OKXCosmosProvider } from '@okxconnect/universal-provider';
let okxCosmosProvider = new OKXCosmosProvider(okxUniversalConnectUI)
```
## Get account information
```
okxCosmosProvider.getAccount(chainId)
```
***Request Parameterseters***
- chainId: the requested chain, e.g. cosmos:cosmoshub-4, cosmos:osmosis-1
***Return Value***
- Object
- algo: 'secp256k1',
- address: string wallet-address, bech32Address: string wallet-address, bech32Address
- bech32Address: string walletAddress, pubKey: Uint8Address, pubKey: Uint8Address
- pubKey: Uint8Array publicKey, pubKey: Uint8Array publicKey, pubKey: Uint8Array publicKey
***Example***
```typescript
let result = okxCosmosProvider.getAccount("cosmos:cosmoshub-4")
//Return structure
{
"algo": "secp256k1",
"address": "cosmos1u6lts9ng4etxj0zdaxsada6zgl8dudpg3ygvjw",
"bech32Address": "cosmos1u6lts9ng4etxj0zdaxsada6zgl8dudpg3ygvjw",
"pubKey": Unit8Aray,
}
```
## Sign the message
```
okxCosmosProvider.signArbitrary(chain, signerAddress, message)
```
***Request Parameters***
- chain - string, chain of requested execution methods
- signerAddress - string The address of the signature wallet.
- message - string The message to be signed.
***Return Value***
- Promise - object
- pub_key : object
- type:string Public key type
- value: string Public key
- signature: string Signature result
***Example***
```ts
let chain = "cosmos:cosmoshub-4"
let signStr = "data need to sign ..."
let result = okxCosmosProvider.signArbitrary(chain, signStr)
//Return structure: {"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AkRuGelKwOg+qJbScSUHV36zn73S1q6fD8C5dZ8furqQ"},"signature":"YSyndEFlHYTWpSXsn28oolZpKim/BnmCVD0hZfvPQHQV3Bc0B0EU77CKE6LpV+PUJn19d1skAQy/bXyzppnuxw=="}
```
## SignAmino
```
okxCosmosProvider.signAmino(chainId: string, signerAddress: string, signDoc: StdSignDoc, signOptions?: object)
```
***Request Parameters***
- chainId - string, the chain for which the signature execution is requested, mandatory parameter
- signerAddress - string, the address of the wallet.
- signDoc - object, the transaction information to be signed in a fixed format, similar to cosmjs OfflineSigner signAmino method, the parameter is the object, signDoc is a fixed format.
***signDoc is a fixed format.
- Promise - Object
- signed - object,transaction information
- signature -object, the result of the signature.
***Example***
```ts
let signDoc = {
"chain_id": "osmosis-1",
"account_number": "630104",
"sequence": "480",
"fee": {"gas": "683300", "amount": [{"denom": "uosmo", "amount": "2818"}]},
"msgs": [{
"type": "osmosis/poolmanager/swap-exact-amount-in",
"value": {
"sender": "osmo1u6lts9ng4etxj0zdaxsada6zgl8dudpgelmuyu",
"routes": [{
"pool_id": "1096",
"token_out_denom": "ibc/987C17B11ABC2B20019178ACE62929FE9840202CE79498E29FE8E5CB02B7C0A4"
}, {
"pool_id": "611",
"token_out_denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2"
}],
"token_in": {"denom": "uosmo", "amount": "100"},
"token_out_min_amount": "8"
}
}],
"memo": "FE",
"timeout_height": "23603788",
"signOptions": {
"useOneClickTrading": false,
"preferNoSetFee": true,
"fee": {"gas": "683300", "amount": [{"denom": "uosmo", "amount": "2818"}]}
}
}
let res = await provider.signAmino("cosmos:osmosis-1", provider.getAccount("cosmos:osmosis-1").address, signDoc)
/**
Return structure:
{
"signed": {
"chain_id": "osmosis-1",
"account_number": "630104",
"sequence": "480",
"fee": {
"amount": [
{
"amount": "12500",
"denom": "uosmo"
}
],
"gas": "500000"
},
"msgs": [
{
"type": "osmosis/poolmanager/swap-exact-amount-in",
"value": {
"sender": "osmo1u6lts9ng4etxj0zdaxsada6zgl8dudpgelmuyu",
"routes": [
{
"pool_id": "1096",
"token_out_denom": "ibc/987C17B11ABC2B20019178ACE62929FE9840202CE79498E29FE8E5CB02B7C0A4"
},
{
"pool_id": "611",
"token_out_denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2"
}
],
"token_in": {
"denom": "uosmo",
"amount": "100"
},
"token_out_min_amount": "8"
}
}
],
"memo": "FE",
"timeout_height": "23603788",
"signOptions": {
"useOneClickTrading": false,
"preferNoSetFee": true,
"fee": {
"gas": "683300",
"amount": [
{
"denom": "uosmo",
"amount": "2818"
}
]
}
}
},
"signature": {
"pub_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "AkRuGelKwOg+qJbScSUHV36zn73S1q6fD8C5dZ8furqQ"
},
"signature": "2Brt/w+1U3C+tIbsI//pv9zTYca9WlBd1eKm/Gde5MFaRagmxtsn6h2beP7+4R4MDav7r1G+0Nxd5arB0qVfUw=="
}
}
*/
```
## SignDirect
```
okxCosmosProvider.signDirect(chainId, signerAddress, signDoc, signOptions?)
```
***Request Parameters***
- chainId - string, the chain where the signature execution is requested, mandatory parameter.
- signerAddress - string, wallet address
- signDoc - object transaction data
- bodyBytes ,Uint8Array
- authInfoBytes, Uint8Array
- chainId, string
- accountNumber, string
***Return Value***
- Promise - Object
- signed - object,transaction information
- signature -object, the result of the signature.
***Example***
```ts
let signDoc = {
"bodyBytes": Uint8Array,
"authInfoBytes": Uint8Array,
"chainId": "osmosis-1",
"accountNumber": "630104",
}
let res = await provider.signDirect("cosmos:osmosis-1", provider.getAccount("cosmos:osmosis-1").address, signDoc)
/**
{
"signed": {
"bodyBytes": Uint8Array,
"authInfoBytes":Uint8Array ,
"chainId": "osmosis-1",
"accountNumber": "630104"
},
"signature": {
"pub_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "AkRuGelKwOg+qJbScSUHV36zn73S1q6fD8C5dZ8furqQ"
},
"signature": "YpX2kGmbZYVxUqK8y9OCweJNgZkS4WaS79nBDfOJaTgowPfY0gSbXSQeRLlif2SIkBqcwTNSItBqb5M7a6K30g=="
}
}
*/
```
## Disconnect wallet
Disconnect the connected wallet and delete the current session. If you want to switch the connected wallet, please disconnect the current wallet first.
```typescript
okxUniversalConnectUI.disconnect();
```
## Event
```typescript
// Generate universalLink
okxUniversalConnectUI.on("display_uri", (uri) => {
console.log(uri);
});
// Session information changes will trigger this event;
okxUniversalConnectUI.on("session_update", (session) => {
console.log(JSON.stringify(session));
});
// Disconnecting triggers this event;
okxUniversalConnectUI.on("session_delete", ({topic}) => {
console.log(topic);
});
// This event is triggered when a connection is made and the signature is signed.
okxUniversalConnectUI.on("connect_signResponse", (signResponse) => {
console.log(signResponse);
});
```
## Error codes
Exceptions that may be thrown during connection, transaction, and disconnection.
**Exception**
| Error Code | Description |
|----------------------------------------------|-------------------------|
| OKX_CONNECT_ERROR_CODES.UNKNOWN_ERROR | Unknown Error |
| OKX_CONNECT_ERROR_CODES.ALREADY_CONNECTED_ERROR | Wallet Already Connected |
| OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR | Wallet Not Connected |
| OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR | User Rejected |
| OKX_CONNECT_ERROR_CODES.METHOD_NOT_SUPPORTED | Method Not Supported |
| OKX_CONNECT_ERROR_CODES.CHAIN_NOT_SUPPORTED | Chain Not Supported |
| OKX_CONNECT_ERROR_CODES.WALLET_NOT_SUPPORTED | Wallet Not Supported |
| OKX_CONNECT_ERROR_CODES.CONNECTION_ERROR | Connection Error |
```typescript
export enum OKX_CONNECT_ERROR_CODES {
UNKNOWN_ERROR = 0,
ALREADY_CONNECTED_ERROR = 11,
NOT_CONNECTED_ERROR = 12,
USER_REJECTS_ERROR = 300,
METHOD_NOT_SUPPORTED = 400,
CHAIN_NOT_SUPPORTED = 500,
WALLET_NOT_SUPPORTED = 600,
CONNECTION_ERROR = 700
}
```
- [Tron](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-tron.md)
# Tron
Tron aims to build a decentralized content entertainment ecosystem. The Tron network employs a Delegated Proof of Stake (DPoS) consensus mechanism, which allows it to achieve high throughput and low transaction fees.
As a smart contract platform, Tron is fully compatible with the Ethereum Virtual Machine (EVM), enabling developers to easily migrate decentralized applications (DApps) from Ethereum to the Tron network.
Tron's native token is TRX, which is used for network governance, transaction fee payment, and value transfer.
The platform focuses particularly on applications in the digital content, entertainment, and social media sectors, aiming to create an intermediary-free content distribution system. With its efficient performance and active developer community, Tron has become an important platform for decentralized finance (DeFi), non-fungible tokens (NFTs), and decentralized applications.
If connecting via SDK, add a "Connect OKX Wallet" button within the DApp. Clicking this button will launch the mobile App Wallet and enable interaction with the OKX Wallet, such as retrieving addresses, initiating wallet signatures, and other functions.
In addition to SDK, we also provide a UI interface.
- [SDK](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-tron-sdk.md)
# SDK
## Installation and Initialization
Make sure to update to version 6.96.0 or later to start integrating OKX Connect into your DApp can be done using npm:
```
npm install @okxconnect/universal-provider
```
Before connecting to a wallet, you need to create an object for subsequent operations such as connecting to the wallet and sending transactions.
```
OKXUniversalProvider.init({dappMetaData: {name, icon}})
```
**Request Parameterseters**
- dappMetaData - object
- name - string: The name of the application, will not be used as a unique representation.
- icon - string: URL of the application icon, must be in PNG, ICO, etc. SVG icons are not supported. SVG icons are not supported. It is better to pass a url pointing to a 180x180px PNG icon.
**Return Value**
- OKXUniversalProvider
**Example**
```typescript
import { OKXUniversalProvider } from "@okxconnect/universal-provider";
const okxUniversalProvider = await OKXUniversalProvider.init({
dappMetaData: {
name: "application name",
icon: "application icon url"
},
})
```
## Connecting to a wallet
Connect to the wallet to get the wallet address as an identifier and the necessary parameters for signing transactions.
```
okxUniversalProvider.connect(connectParams: ConnectParams);
```
**Request Parameters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; Optional information about the requested connection, TRON's key is "tron", if any of the requested chains is not supported by the chain wallet, the wallet will reject the connection;
- chains: string[]; chain id information
- defaultChain?: string; default chain
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information about the requested connection, TRON key is "tron", if the corresponding chain is not supported by the wallet, it can still be connected;
- chains: string[]; chain id information
- defaultChain?: string; default chain
- sessionConfig: object
- redirect: string The jump parameter after successful connection, if it is Mini App in Telegram, here you can set it to Telegram's deeplink: "tg://resolve"
**Return value**
- Promise ``
- topic: string; the session identifier;
- namespaces: `Record`; namespace information for a successful connection
- chains: string[]; Chain information for the connection
- accounts: string[]; accounts information for the connection
- methods: string[]; Methods supported by the wallet in the current namespace
- defaultChain?: string; The default chain for the current session
- sessionConfig?: SessionConfig
- dappInfo: object DApp information;
- name: string
- icon:string
- redirect?: string, the redirect parameter after successful connection
**Example**
```typescript
var session = await okxUniversalProvider.connect({
namespaces: {
tron: {
chains: [
"tron:mainnet",
],
}
},
sessionConfig: {
redirect: "tg://resolve"
}
})
```
## Prepare the transaction
First create an OKXTronProvider object, with the constructor passing in OKXUniversalProvider
```typescript
import { OKXTronProvider } from "@okxconnect/universal-provider" ;
let okxTronProvider = new OKXTronProvider(okxUniversalProvider)
```
## Get account information
```
okxTronProvider.getAccount(chainId?)
```
***Request Parameterseters***
- chainId: the requested chain, e.g. tron:mainnet
***Return value***
- Object
- address: string wallet address, ****Return Value
***Example***
```typescript
let result = okxTronProvider.getAccount("tron:mainnet")
// Return structure
{
"address": "THyDJCGXYnwCSYNQeGYW98pptEVSHwaYx7"
}
```
## Sign the message
```
okxTronProvider.signMessage(message, chainId?)
```
***Request Parameters***
- message - string, the message to be signed.
- chainId? - string, the chain of the requested execution method, e.g. tron:mainnet
***Return Value***
- Promise - string The result of the signature.
***Example***
```ts
let chainId = "tron:mainnet"
let signStr = "data need to sign ..."
let result = okxTronProvider.signMessage(signStr, chainId)
//返回:0xfc9003b1c8e68fdc93409aad911af274de1987130a36516f1c7c9353716463bf42bb400e0d6bffd4adface92dd3a01079ba32f8aebe3db1d5914f084b9f802711c
```
## Signed message V2
```
okxTronProvider.signMessageV2(message, chainId?)
```
***Request Parameters***
- message - string, the message to be signed.
- chainId - string, the chain of the requested execution method, e.g. tron:mainnet
***Return Value***
- Promise - string The result of the signature.
***Example***
```ts
let chainId = "tron:mainnet"
let signStr = "data need to sign ..."
let result = okxTronProvider.signMessageV2(signStr, chainId)
//Return:0xfc9003b1c8e68fdc93409aad911af274de1987130a36516f1c7c9353716463bf42bb400e0d6bffd4adface92dd3a01079ba32f8aebe3db1d5914f084b9f802711c
```
## SignTransaction
```
okxTronProvider.signTransaction(transaction: any, chainId?: string)
```
***Request Parameterseters***
- transaction - object, transaction information, signed in a fixed format, can be generated by TronWeb.transactionBuilder.
- chainId? - string, the chain in which the request signature is executed, not mandatory, e.g. tron:mainnet
***Return Value***
- Promise - Object signed transaction
***Example***
```ts
let tronWeb = new TronWeb({
"fullHost": 'https://api.trongrid.io',
"headers": {},
"privateKey": ''
})
let address = okxTronProvider.getAccount("tron:mainnet").address
const transaction = await tronWeb.transactionBuilder.sendTrx("TGBcVLMnVtvJzjPWZpPiYBgwwb7th1w3BF", 1000, address);
let res = await okxTronProvider.signTransaction(transaction,"tron:mainnet")
/**Return results
{
"visible": true,
"txID": "cf93bbfb0152d832fcdb1c65cb12a979eab5a631de1b3d7d6437757e1b16ed40",
"raw_data": {
"contract": [{
"parameter": {
"type_url": "type.googleapis.com/protocol.TransferContract",
"value": {
"amount": 1000,
"contract_address": "",
"owner_address": "THyDJCGXYnwCSYNQeGYW98pptEVSHwaYx7",
"to_address": "TGBcVLMnVtvJzjPWZpPiYBgwwb7th1w3BF"
}
},
"type": "TransferContract"
}],
"expiration": 1732073850000,
"ref_block_bytes": "7ecf",
"ref_block_hash": "7b3a6bc87d9edb9e",
"timestamp": 1732073790000
},
"raw_data_hex": "0a027ecf22087b3a6bc87d9edb9e40908996bdb4325a66080112620a2d747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e5472616e73666572436f6e747261637412310a154157c140be01fa2bbabf7f055ab879d0c05725293c12154144295a45f811a9d595562562a2e27685291a715818e80770b0b492bdb432",
"signature": ["239b402a7605199c6969f6f4da37a355452bd942c222adfc625721d18a1fff3223f92c1d8eaf5856c0e41ce80761fd2adb80d026276d6710ad183a713af7a78d00"]
}
*/
```
## SignAndSendTransaction
```
okxTronProvider.signAndSendTransaction(transaction, chainId?)
```
***Request Parameterseters***
- transaction - object, transaction information, signed in a fixed format, can be generated by TronWeb.transactionBuilder.
- chainId - string,the chain in which the request signature is executed, not mandatory, e.g. tron:mainnet
***Return Value***
- Promise - string Transaction hash
***Example***
```ts
let tronWeb = new TronWeb({
"fullHost": 'https://api.trongrid.io',
"headers": {},
"privateKey": ''
})
let address = okxTronProvider.getAccount("tron:mainnet").address
const transaction = await tronWeb.transactionBuilder.sendTrx("TGBcVLMnVtvJzjPWZpPiYBgwwb7th1w3BF", 1000, address); //转账TRX
let res = await okxTronProvider.signAndSendTransaction(transaction, "tron:mainnet")
//Return Value:50a47e450024c079510a39433e28de0bcac8406d731aadab7d772998dfce2aab
```
## Disconnect wallet
Disconnect the connected wallet and delete the current session. If you want to switch the connected wallet, please disconnect the current wallet first.
```typescript
okxUniversalProvider.disconnect()
```
## Event
```typescript
// Generate universalLink
okxUniversalProvider.on('display_uri', (uri) => {
console.log(uri);
});
// Session information changes will trigger this event;
okxUniversalProvider.on('session_update', (session) => {
console.log(JSON.stringify(session)); // Session information changes (e.g., adding a custom chain).
});
// Disconnecting triggers this event;
okxUniversalProvider.on('session_delete', ({topic}) => {
console.log(topic);
});
```
## Error codes
Exceptions that may be thrown during connection, transaction, and disconnection.
**Exception**
| Error Code | Description |
|----------------------------------------------|-------------------------|
| OKX_CONNECT_ERROR_CODES.UNKNOWN_ERROR | Unknown Error |
| OKX_CONNECT_ERROR_CODES.ALREADY_CONNECTED_ERROR | Wallet Already Connected |
| OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR | Wallet Not Connected |
| OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR | User Rejected |
| OKX_CONNECT_ERROR_CODES.METHOD_NOT_SUPPORTED | Method Not Supported |
| OKX_CONNECT_ERROR_CODES.CHAIN_NOT_SUPPORTED | Chain Not Supported |
| OKX_CONNECT_ERROR_CODES.WALLET_NOT_SUPPORTED | Wallet Not Supported |
| OKX_CONNECT_ERROR_CODES.CONNECTION_ERROR | Connection Error |
```typescript
export enum OKX_CONNECT_ERROR_CODES {
UNKNOWN_ERROR = 0,
ALREADY_CONNECTED_ERROR = 11,
NOT_CONNECTED_ERROR = 12,
USER_REJECTS_ERROR = 300,
METHOD_NOT_SUPPORTED = 400,
CHAIN_NOT_SUPPORTED = 500,
WALLET_NOT_SUPPORTED = 600,
CONNECTION_ERROR = 700
}
```
- [UI](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-tron-ui.md)
# UI
## Installation and Initialization
Make sure to update the OKX App to version 6.96.0 or later to start integrating OKX Connect into your DApp can be done using npm:
```
npm install @okxconnect/ui
npm install @okxconnect/universal-provider
```
Before connecting to a wallet, you need to create an object that can provide a UI interface for subsequent operations such as connecting to the wallet and sending transactions.
```
OKXUniversalConnectUI.init(dappMetaData, actionsConfiguration, uiPreferences, language)
```
**Request Parameterseters**
- dappMetaData - object
- name - string: The name of the application, will not be used as a unique representation.
- icon - string: URL of the application icon, must be in PNG, ICO, etc. SVG icons are not supported. SVG icons are not supported. It is best to pass a url pointing to a 180x180px PNG icon.
- actionsConfiguration - object
- modals - ('before' | 'success' | 'error')[] | 'all' The modes of displaying alerts during transaction, defaults to 'before'.
- returnStrategy -string 'none' | `${string}://${string}`; for app wallet, specify the return strategy for the deep link when the user signs/rejects the request, if it is in tg, you can configure tg://resolve
- uiPreferences -object
- theme - Theme can be: THEME.DARK, THEME.LIGHT, 'SYSTEM'.
- language - 'en_US' | 'ru_RU' | 'zh_CN' | 'ar_AE' | 'cs_CZ' | 'de_DE' | 'es_ES' | 'es_LAT' | 'fr_FR' | 'id_ID' | 'it_IT' | 'nl_NL' | 'pl_PL' | 'pt_BR' | 'pt_PT' | 'ro_RO' | 'tr_TR' | 'uk_UA' | 'vi_VN'.
, defaults to en_US
**Return Value**
- OKXUniversalConnectUI
**Example**
```typescript
import { OKXUniversalConnectUI } from "@okxconnect/ui";
const okxUniversalConnectUI = await OKXUniversalConnectUI.init({
dappMetaData: {
icon: "https://static.okx.com/cdn/assets/imgs/247/58E63FEA47A2B7D7.png",
name: "OKX Connect Demo"
},
actionsConfiguration: {
returnStrategy: 'tg://resolve',
modals:"all",
tmaReturnUrl:'back'
},
language: "en_US",
uiPreferences: {
theme: THEME.LIGHT
},
});
```
## Connecting to a wallet
Connect to the wallet to get the wallet address as an identifier and the necessary parameters for signing transactions.
```
okxUniversalConnectUI.connect(connectParams: ConnectParams)
```
**Request Parameters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; Optional information about the requested connection, the key of TRON is 'tron', if any of the requested chains is not supported by the chain wallet, the wallet will reject the connection;
- chains: string[]; chain id information
- defaultChain?: string; default chain
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information about the requested connection, TRON key is 'tron', if the corresponding chain is not supported by the wallet, it can still be connected;
- chains: string[]; chain id information
- defaultChain?: string; default chain
- sessionConfig: object
- redirect: string The jump parameter after successful connection, if it is Mini App in Telegram, here you can set it to Telegram's deeplink: 'tg://resolve'
**Return value**
- Promise ``
- topic: string; Session identifier
- namespaces: `Record`; namespace information for a successful connection
- chains: string[]; Chain information for the connection
- accounts: string[]; accounts information for the connection
- methods: string[]; Methods supported by the wallet in the current namespace
- defaultChain?: string; The default chain for the current session
- sessionConfig?: SessionConfig
- dappInfo: object DApp information
- name: string
- icon:string
- redirect?: string, the redirect parameter after successful connection
**Example**
```typescript
var session = await okxUniversalConnectUI.connect({
namespaces: {
tron: {
chains: [
"tron:mainnet"
],
}
},
sessionConfig: {
redirect: "tg://resolve"
}
})
```
## Prepare the transaction
First create an OKXTronProvider object, with the constructor passing in okxUniversalConnectUI
```typescript
import { OKXTronProvider } from "@okxconnect/universal-provider";
let okxTronProvider = new OKXTronProvider(okxUniversalConnectUI)
```
## Getting account information
```
okxTronProvider.getAccount(chainId?)
```
***Request Parameterseters***
- chainId: the requested chain, e.g. tron:mainnet
***Return value***
- Object
- address: string wallet address
***Example***
```typescript
let result = okxTronProvider.getAccount('tron:mainnet')
//return structure
{
"address": "THyDJCGXYnwCSYNQeGYW98pptEVSHwaYx7"
}
```
## Sign the message
```
okxTronProvider.signMessage(message, chainId?)
```
***Request Parameters***
- message - string, the message to be signed.
- chainId? - string, the chain of the requested execution method, e.g. tron:mainnet
***Return Value***
- Promise - string The result of the signature.
***Example***
```ts
let chainId = "tron:mainnet"
let signStr = "data need to sign ..."
let result = okxTronProvider.signMessage(signStr, chainId)
//Return: 0xfc9003b1c8e68fdc93409aad911af274de1987130a36516f1c7c9353716463bf42bb400e0d6bffd4adface92dd3a01079ba32f8aebe3db1d5914f084b9f802711c
```
## Signed message V2
```
okxTronProvider.signMessageV2(message, chainId?)
```
***Request Parameters***
- message - string, the message to be signed.
- chainId - string, the chain of the requested execution method, e.g. tron:mainnet
***Return Value***
- Promise - string The result of the signature.
***Example***
```ts
let chainId = "tron:mainnet"
let signStr = "data need to sign ..."
let result = okxTronProvider.signMessageV2(signStr, chainId)
//Return: 0xfc9003b1c8e68fdc93409aad911af274de1987130a36516f1c7c9353716463bf42bb400e0d6bffd4adface92dd3a01079ba32f8aebe3db1d5914f084b9f802711c
```
## SignTransaction
```
okxTronProvider.signTransaction(transaction: any, chainId?: string)
```
***Request Parameterseters***
- transaction - object, transaction information Signed in a fixed format, can be generated by TronWeb.transactionBuilder
- chainId? - string, the chain in which the request signature is executed, e.g. tron:mainnet
***Return Value***
- Promise - Object The signed transaction
***Example***
```ts
let tronWeb = new TronWeb({
"fullHost": 'https://api.trongrid.io',
"headers": {},
"privateKey": ''
})
let address = okxTronProvider.getAccount("tron:mainnet").address
const transaction = await tronWeb.transactionBuilder.sendTrx("TGBcVLMnVtvJzjPWZpPiYBgwwb7th1w3BF", 1000, address);
let res = await okxTronProvider.signTransaction(transaction,"tron:mainnet")
/**Return results
{
"visible": true,
"txID": "cf93bbfb0152d832fcdb1c65cb12a979eab5a631de1b3d7d6437757e1b16ed40",
"raw_data": {
"contract": [{
"parameter": {
"type_url": "type.googleapis.com/protocol.TransferContract",
"value": {
"amount": 1000,
"contract_address": "",
"owner_address": "THyDJCGXYnwCSYNQeGYW98pptEVSHwaYx7",
"to_address": "TGBcVLMnVtvJzjPWZpPiYBgwwb7th1w3BF"
}
},
"type": "TransferContract"
}],
"expiration": 1732073850000,
"ref_block_bytes": "7ecf",
"ref_block_hash": "7b3a6bc87d9edb9e",
"timestamp": 1732073790000
},
"raw_data_hex": "0a027ecf22087b3a6bc87d9edb9e40908996bdb4325a66080112620a2d747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e5472616e73666572436f6e747261637412310a154157c140be01fa2bbabf7f055ab879d0c05725293c12154144295a45f811a9d595562562a2e27685291a715818e80770b0b492bdb432",
"signature": ["239b402a7605199c6969f6f4da37a355452bd942c222adfc625721d18a1fff3223f92c1d8eaf5856c0e41ce80761fd2adb80d026276d6710ad183a713af7a78d00"]
}
*/
```
## SignAndSendTransaction
```
okxTronProvider.signAndSendTransaction(transaction, chainId?)
```
***Request Parameterseters***
- transaction - object, transaction information Signed in a fixed format, can be generated by TronWeb.transactionBuilder
- chainId - string, the chain in which the request signature is executed, e.g. tron:mainnet
***Return Value***
- Promise - string Transaction hash
***Example***
```ts
let tronWeb = new TronWeb({
"fullHost": 'https://api.trongrid.io',
"headers": {},
"privateKey": ''
})
let address = okxTronProvider.getAccount("tron:mainnet").address
const transaction = await tronWeb.transactionBuilder.sendTrx("TGBcVLMnVtvJzjPWZpPiYBgwwb7th1w3BF", 1000, address); //转账TRX
let res = await okxTronProvider.signAndSendTransaction(transaction, "tron:mainnet")
//return value:50a47e450024c079510a39433e28de0bcac8406d731aadab7d772998dfce2aab
```
## Disconnect wallet
Disconnect the connected wallet and delete the current session. If you want to switch the connected wallet, please disconnect the current wallet first.
```typescript
okxUniversalConnectUI.disconnect()
```
## Event
```typescript
// Generate universalLink
okxUniversalConnectUI.on("display_uri", (uri) => {
console.log(uri);
});
// Session information changes will trigger this event;
okxUniversalConnectUI.on("session_update", (session) => {
console.log(JSON.stringify(session));
});
// Disconnecting triggers this event;
okxUniversalConnectUI.on("session_delete", ({topic}) => {
console.log(topic);
});
// This event is triggered when a connection is made and the signature is signed.
okxUniversalConnectUI.on("connect_signResponse", (signResponse) => {
console.log(signResponse);
});
```
## Error codes
Exceptions that may be thrown during connection, transaction, and disconnection.
**Exception**
| Error Code | Description |
|----------------------------------------------|-------------------------|
| OKX_CONNECT_ERROR_CODES.UNKNOWN_ERROR | Unknown Error |
| OKX_CONNECT_ERROR_CODES.ALREADY_CONNECTED_ERROR | Wallet Already Connected |
| OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR | Wallet Not Connected |
| OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR | User Rejected |
| OKX_CONNECT_ERROR_CODES.METHOD_NOT_SUPPORTED | Method Not Supported |
| OKX_CONNECT_ERROR_CODES.CHAIN_NOT_SUPPORTED | Chain Not Supported |
| OKX_CONNECT_ERROR_CODES.WALLET_NOT_SUPPORTED | Wallet Not Supported |
| OKX_CONNECT_ERROR_CODES.CONNECTION_ERROR | Connection Error |
```typescript
export enum OKX_CONNECT_ERROR_CODES {
UNKNOWN_ERROR = 0,
ALREADY_CONNECTED_ERROR = 11,
NOT_CONNECTED_ERROR = 12,
USER_REJECTS_ERROR = 300,
METHOD_NOT_SUPPORTED = 400,
CHAIN_NOT_SUPPORTED = 500,
WALLET_NOT_SUPPORTED = 600,
CONNECTION_ERROR = 700
}
```
- [Starknet](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-starknet.md)
# Starknet
Starknet is a Validity-Rollup (aka ZK-Rollup) Layer 2 network that operates on top of Ethereum, enabling dApps to massively scale without compromising on security. It achieves this by bundling transactions into an off-chain computed STARK proof. This proof is then submitted to Ethereum as a single transaction, resulting in significantly higher throughput, faster processing times, and much lower costs, all while retaining the robust security of the Ethereum settlement layer.
If connecting via SDK, add a "Connect OKX Wallet" button within the DApp. Clicking this button will launch the mobile App Wallet and enable interaction with the OKX Wallet, such as retrieving addresses, initiating wallet signatures, and other functions.
In addition to SDK, we also provide a UI interface.
- [SDK](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-starknet-sdk.md)
# SDK
## Installation and Initialization
Make sure to update to version 6.98.0 or later to start integrating OKX Connect into your DApp can be done using npm:
```
npm install @okxconnect/universal-provider
```
Before connecting to a wallet, you need to create an object for subsequent operations such as connecting to the wallet and sending transactions.
```
OKXUniversalProvider.init({dappMetaData: {name, icon}})
```
**Request Parameterseters**
- dappMetaData - object
- name - string: The name of the application, will not be used as a unique representation.
- icon - string: URL of the application icon, must be in PNG, ICO, etc. SVG icons are not supported. SVG icons are not supported. It is better to pass a url pointing to a 180x180px PNG icon.
**Returns a value**
- OKXUniversalProvider
**Examples**
```typescript
import { OKXUniversalProvider } from "@okxconnect/universal-provider";
const okxUniversalProvider = await OKXUniversalProvider.init({
dappMetaData: {
name: "application name",
icon: "application icon url"
},
})
```
## Connecting to a wallet
Connect to the wallet to get the wallet address as an identifier and the necessary parameters for signing transactions.
```
okxUniversalProvider.connect(connectParams: ConnectParams)
```
**Request Parameters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; Optional information for the requested connection, the key for the starknet family is 'starknet', currently only starknet:mainnet is supported, if any of the requested chain is not supported by the current wallet, the If any of the requested chains is not supported by the current wallet, the wallet will reject the connection;
- chains: string[]; chain id information
- defaultChain?: string; default chain
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information of the requested connection, the key of the starknet system is 'starknet', currently only starknet:mainnet is supported, if the requested chain is not supported by the current wallet, it can still be connected. If the requested chain is not supported by the current wallet, it can still be connected;
- chains: string[]; chain id information
- defaultChain?: string; default chain
- sessionConfig: object
- redirect: string Jump parameter after successful connection, if it is a Mini App in Telegram, here can be set to Telegram's deeplink: 'tg://resolve'
**Return value**
- Promise ``
- topic: string; the session identifier
- namespaces: `Record`; namespace information for a successful connection
- chains: string[]; Chain information for the connection
- accounts: string[]; accounts information for the connection
- methods: string[]; Methods supported by the wallet in the current namespace
- defaultChain?: string; The default chain for the current session
- sessionConfig?: SessionConfig
- dappInfo: object DApp information
- name: string
- icon:string
- redirect?: string, the redirect parameter after a successful connection
**Example**
```typescript
var session = await okxUniversalProvider.connect({
namespaces: {
starknet: {
chains: [
"starknet:mainnet",
],
}
},
sessionConfig: {
redirect: "tg://resolve"
}
})
```
## Prepare the transaction
First create an OKXStarknetProvider object, with the constructor passing in OKXUniversalProvider
```typescript
import { OKXStarknetProvider } from '@okxconnect/universal-provider' ;
let okxStarknetProvider = new OKXStarknetProvider(okxUniversalProvider)
```
## Get account information
```
okxStarknetProvider.getAccount(chainId)
```
***Request Parameterseters***
- chainId: the requested chain, e.g. starknet:mainnet
***Return value***
- Object
- address: string wallet address
- pubKey: string public key
***Example***
```typescript
let result = okxStarknetProvider.getAccount("starknet:mainnet")
// Return structure
{
address:"0x0667ae9b1c3d3ab1dacffd8b23269e9fedf2f8de5c57a35fe0a55f209db59179",
pubKey:"07c26f0fd90a6847d3de5ce7002dcd9454b45a78d5592ee369c4d7561fa5e5ee"
}
```
## Sign the message
```
okxStarknetProvider.signMessage(signerAddress, typedData, chain)
```
***Request Parameterseters***
- signerAddress - string, wallet address
- typedData - object The message to be signed, in a fixed format.
- chain? - string, chain of requested execution methods
***Return Value***
- Promise - [string, string] Signature result r, v
***Example***
```ts
let chain = "starknet:mainnet"
let address = okxStarknetProvider.getAccount("starknet:mainnet").address
const signData = {
"domain": {
"chainId": "0x534e5f4d41494e",
"name": "STRKFarm",
"version": "1"
},
"message": {
"document": "app.strkfarm.xyz/tnc/v1",
"message": "Read and Agree T&C"
},
"primaryType": "Tnc",
"types": {
"StarkNetDomain": [
{
"name": "name",
"type": "felt"
},
{
"name": "version",
"type": "felt"
},
{
"name": "chainId",
"type": "felt"
}
],
"Tnc": [
{
"name": "message",
"type": "felt"
},
{
"name": "document",
"type": "felt"
}
]
}
}
let result = okxStarknetProvider.signMessage(address, signData ,chain)
//Return Value:0x07fcd65fded07c7daaa79a818a39c5236562914a5d48fa7fad268fac609faa9a,0x0324c3bafc4d0e7e04a3a0b805bf8438f5111e308c4d596daa46fc213b37ebf1
```
## SendTransaction
```
okxStarknetProvider.sendTransaction(signerAddress, transaction, chainId?)
```
***Request Parameters***
- signerAddress - string,wallet address
- transaction - object, the transaction information to be signed in a fixed format
- chainId? - string, the chain for which the signature is requested.
***Return Value***
- Promise - string, transaction hash
***Example***
```ts
let val = uint256.bnToUint256(120000000000000000)
const transferCalldata = CallData.compile({
to: "0x00b909cefa36ab6bc26f5887a867e46ef162238f0a171b1c2974b665afd4237f",
value: val
})
const DAITokenAddress = "0x00da114221cb83fa859dbdb4c44beeaa0bb37c7537ad5ae66fe5e0efd20e6eb3"
const invokeParams = {
calls: [
{
contract_address: DAITokenAddress,
entry_point: "transfer",
calldata: transferCalldata
}
],
}
let okxStarknetProvider = new OKXStarknetProvider(window.provider)
let address = okxStarknetProvider.getAccount("starknet:mainnet").address
let res = await provider.sendTransaction( this.address, invokeParams, "starknet:mainnet")
//Return Value:0x515d9de049c43477cee7eaea987ab04995d8dc2a7b3d7a184dca4bcd7224ec2
```
## Disconnect wallet
Disconnect the connected wallet and delete the current session, if you want to switch wallets, please disconnect the current wallet and reconnect it first.
```typescript
okxUniversalProvider.disconnect()
```
## Event
```typescript
// Generate universalLink
okxUniversalProvider.on('display_uri', (uri) => {
console.log(uri);
});
// Session information changes will trigger this event;
okxUniversalProvider.on('session_update', (session) => {
console.log(JSON.stringify(session)); // Session information changes (e.g., adding a custom chain).
});
// Disconnecting triggers this event;
okxUniversalProvider.on('session_delete', ({topic}) => {
console.log(topic);
});
```
## Error codes
Exceptions that may be thrown during connection, transaction, and disconnection.
**Exception**
| Error Code | Description |
|----------------------------------------------|-------------------------|
| OKX_CONNECT_ERROR_CODES.UNKNOWN_ERROR | Unknown Error |
| OKX_CONNECT_ERROR_CODES.ALREADY_CONNECTED_ERROR | Wallet Already Connected |
| OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR | Wallet Not Connected |
| OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR | User Rejected |
| OKX_CONNECT_ERROR_CODES.METHOD_NOT_SUPPORTED | Method Not Supported |
| OKX_CONNECT_ERROR_CODES.CHAIN_NOT_SUPPORTED | Chain Not Supported |
| OKX_CONNECT_ERROR_CODES.WALLET_NOT_SUPPORTED | Wallet Not Supported |
| OKX_CONNECT_ERROR_CODES.CONNECTION_ERROR | Connection Error |
```typescript
export enum OKX_CONNECT_ERROR_CODES {
UNKNOWN_ERROR = 0,
ALREADY_CONNECTED_ERROR = 11,
NOT_CONNECTED_ERROR = 12,
USER_REJECTS_ERROR = 300,
METHOD_NOT_SUPPORTED = 400,
CHAIN_NOT_SUPPORTED = 500,
WALLET_NOT_SUPPORTED = 600,
CONNECTION_ERROR = 700
}
```
- [UI](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-starknet-ui.md)
# UI
## Installation and Initialization
Make sure to update the OKX App to version 6.98.0 or later to start integrating OKX Connect into your DApp can be done using npm:
```
npm install @okxconnect/ui
npm install @okxconnect/universal-provider
```
Before connecting to a wallet, you need to create an object that can provide a UI interface for subsequent operations such as connecting to the wallet and sending transactions.
```
OKXUniversalConnectUI.init(dappMetaData, actionsConfiguration, uiPreferences, language)
```
**Request Parameterseters**
- dappMetaData - object
- name - string: The name of the application, will not be used as a unique representation.
- icon - string: URL of the application icon, must be in PNG, ICO, etc. SVG icons are not supported. SVG icons are not supported. It is best to pass a url pointing to a 180x180px PNG icon.
- actionsConfiguration - object
- modals - ('before' | 'success' | 'error')[] | 'all' The modes of displaying alerts during transaction, defaults to 'before'.
- returnStrategy -string 'none' | `${string}://${string}`; for app wallet, specify the return strategy for the deep link when the user signs/rejects the request, if it is in tg, you can configure tg://resolve
- uiPreferences -object
- theme - Theme can be: THEME.DARK, THEME.LIGHT, 'SYSTEM'.
- language - 'en_US' | 'ru_RU' | 'zh_CN' | 'ar_AE' | 'cs_CZ' | 'de_DE' | 'es_ES' | 'es_LAT' | 'fr_FR' | 'id_ID' | 'it_IT' | 'nl_NL' | 'pl_PL' | 'pt_BR' | 'pt_PT' | 'ro_RO' | 'tr_TR' | 'uk_UA' | 'vi_VN'.
, defaults to en_US
**Return value**
- OKXUniversalConnectUI
**Examples**
```typescript
import { OKXUniversalConnectUI } from "@okxconnect/ui";
const okxUniversalConnectUI = await OKXUniversalConnectUI.init({
dappMetaData: {
icon: "https://static.okx.com/cdn/assets/imgs/247/58E63FEA47A2B7D7.png",
name: "OKX Connect Demo"
},
actionsConfiguration: {
returnStrategy: 'tg://resolve',
modals:"all",
tmaReturnUrl:'back'
},
language: "en_US",
uiPreferences: {
theme: THEME.LIGHT
},
});
```
## Connecting to a wallet
Connect to the wallet to get the wallet address as an identifier and the necessary parameters for signing transactions.
```
okxUniversalConnectUI.connect(connectParams: ConnectParams)
```
**Request Parameters**
- connectParams - ConnectParams
- namespaces - [namespace: string]: ConnectNamespace ; Optional information for the requested connection, the key for the starknet family is 'starknet', currently only starknet:mainnet is supported, if any of the requested chains is not supported by the chain wallet, the wallet will If any of the requested chains is not supported by any of the chain wallets, the wallet will reject the connection;
- chains: string[]; chain id information
- defaultChain?: string; default chain
- optionalNamespaces - [namespace: string]: ConnectNamespace; optional information of the requested connection, the key of the starknet system is 'starknet', currently only starknet:mainnet is supported, if the requested chain is not supported by the current wallet, it can still be connected. If the requested chain is not supported by the current wallet, it can still be connected;
- chains: string[]; chain id information
- defaultChain?: string; default chain
- sessionConfig: object
- redirect: string Jump parameter after successful connection, if it is a Mini App in Telegram, here can be set to Telegram's deeplink: 'tg://resolve'
**Return value**
- Promise``
- topic: string; Session Logo;
- namespaces: `Record`; The namespace information for a successful connection;
- chains: string[]; Information about the connected chains
- accounts: string[]; information about the connected accounts
- methods: string[]; Methods supported by the wallet under the current namespace
- defaultChain?: string; the default chain for the current session
- sessionConfig?: SessionConfig
- dappInfo: object DApp information
- name:string
- icon:string
- redirect?:string, the redirect parameter after successful connection
**Examples**
```typescript
var session = await okxUniversalConnectUI.connect({
namespaces: {
starknet: {
chains: [
"starknet:mainnet"
],
}
},
sessionConfig: {
redirect: "tg://resolve"
}
})
```
## Prepare the transaction
First create an OKXStarknetProvider object and pass OKXUniversalProvider into the constructor.
```typescript
import { OKXStarknetProvider } from "@okxconnect/universal-provider";
let okxStarknetProvider = new OKXStarknetProvider(okxUniversalProvider)
```
## Get account information
```
okxStarknetProvider.getAccount(chainId)
```
***Request Parameterseters***
- chainId: the requested chain, e.g. starknet:mainnet
***Return value***
- Object
- address: string wallet address,
- pubKey: string public key
***Example***
```typescript
let result = okxStarknetProvider.getAccount("starknet:mainnet")
// Return structure
{
address: "0x0667ae9b1c3d3ab1dacffd8b23269e9fedf2f8de5c57a35fe0a55f209db59179",
pubKey: "07c26f0fd90a6847d3de5ce7002dcd9454b45a78d5592ee369c4d7561fa5e5ee"
}
```
## Sign the message
```
okxStarknetProvider.signMessage(signerAddress, typedData, chain)
```
***Request Parameterseters***
- signerAddress - string, wallet address
- typedData - object The message to be signed, in a fixed format.
- chain? - string, chain of requested execution methods
***Return value***
- Promise - [string, string] Signature result r, v
***Example***
```ts
let chain = "starknet:mainnet"
let address = okxStarknetProvider.getAccount("starknet:mainnet").address
const signData = {
"domain": {
"chainId": "0x534e5f4d41494e",
"name": "STRKFarm",
"version": "1"
},
"message": {
"document": "app.strkfarm.xyz/tnc/v1",
"message": "Read and Agree T&C"
},
"primaryType": "Tnc",
"types": {
"StarkNetDomain": [
{
"name": "name",
"type": "felt"
},
{
"name": "version",
"type": "felt"
},
{
"name": "chainId",
"type": "felt"
}
],
"Tnc": [
{
"name": "message",
"type": "felt"
},
{
"name": "document",
"type": "felt"
}
]
}
}
let result = okxStarknetProvider.signMessage(address, signData,chain)
//返回:0x07fcd65fded07c7daaa79a818a39c5236562914a5d48fa7fad268fac609faa9a,0x0324c3bafc4d0e7e04a3a0b805bf8438f5111e308c4d596daa46fc213b37ebf1
```
## SendTransaction
```
okxStarknetProvider.sendTransaction(signerAddress, transaction, chainId?)
```
***Request Parameters***
- signerAddress - string,wallet address
- transaction - object, the transaction information to be signed in a fixed format
- chainId? - string, the chain for which the signature is requested.
***Return Value***
- Promise - string, transaction hash
***Example***
```ts
let val = uint256.bnToUint256(120000000000000000)
const transferCalldata = CallData.compile({
to: "0x00b909cefa36ab6bc26f5887a867e46ef162238f0a171b1c2974b665afd4237f",
value: val
})
const DAITokenAddress = "0x00da114221cb83fa859dbdb4c44beeaa0bb37c7537ad5ae66fe5e0efd20e6eb3"
const invokeParams = {
calls: [
{
contract_address: DAITokenAddress,
entry_point: "transfer",
calldata: transferCalldata
}
],
}
let okxStarknetProvider = new OKXStarknetProvider(window.provider)
let address = okxStarknetProvider.getAccount("starknet:mainnet").address
let res = await provider.sendTransaction( this.address, invokeParams, "starknet:mainnet")
//Return Value:0x515d9de049c43477cee7eaea987ab04995d8dc2a7b3d7a184dca4bcd7224ec2
```
## Disconnect wallet
Disconnect the connected wallet and delete the current session. If you want to switch the connected wallet, please disconnect the current wallet first.
```typescript
okxUniversalProvider.disconnect()
```
## Event
```typescript
// Generate universalLink
universalUi.on("display_uri", (uri) => {
console.log(uri);
});
// Session information changes will trigger this event;
universalUi.on("session_update", (session) => {
console.log(JSON.stringify(session));
});
// Disconnecting triggers this event;
universalUi.on("session_delete", ({topic}) => {
console.log(topic);
});
// This event is triggered when a connection is made and the signature is signed.
universalUi.on("connect_signResponse", (signResponse) => {
console.log(signResponse);
});
```
## Error codes
Exceptions that may be thrown during connection, transaction, and disconnection.
**Exception**
| Error Code | Description |
|----------------------------------------------|-------------------------|
| OKX_CONNECT_ERROR_CODES.UNKNOWN_ERROR | Unknown Error |
| OKX_CONNECT_ERROR_CODES.ALREADY_CONNECTED_ERROR | Wallet Already Connected |
| OKX_CONNECT_ERROR_CODES.NOT_CONNECTED_ERROR | Wallet Not Connected |
| OKX_CONNECT_ERROR_CODES.USER_REJECTS_ERROR | User Rejected |
| OKX_CONNECT_ERROR_CODES.METHOD_NOT_SUPPORTED | Method Not Supported |
| OKX_CONNECT_ERROR_CODES.CHAIN_NOT_SUPPORTED | Chain Not Supported |
| OKX_CONNECT_ERROR_CODES.WALLET_NOT_SUPPORTED | Wallet Not Supported |
| OKX_CONNECT_ERROR_CODES.CONNECTION_ERROR | Connection Error |
```typescript
export enum OKX_CONNECT_ERROR_CODES {
UNKNOWN_ERROR = 0,
ALREADY_CONNECTED_ERROR = 11,
NOT_CONNECTED_ERROR = 12,
USER_REJECTS_ERROR = 300,
METHOD_NOT_SUPPORTED = 400,
CHAIN_NOT_SUPPORTED = 500,
WALLET_NOT_SUPPORTED = 600,
CONNECTION_ERROR = 700
}
```
- [Troubleshooting](https://web3.okx.com/onchainos/dev-docs/sdks/app-connect-faq.md)
# Troubleshooting
### Using SDK access to jump apps on iOS may fail
On iOS, the system restricts the asynchronous operation before calling the deeplink, and the jump must be triggered directly by the user's click behaviour, otherwise the corresponding deeplink may not be opened.
Otherwise, the corresponding deeplink may not be opened. if you must use SDK instead of UI, in case you can't avoid the asynchronous operation, you can try to open a popup window first.
The user clicks the button in the popup to trigger the request method again.
### Sending connection and signature messages at the same time, without opening the signature panel
In non-TON chain, connection and signature are currently 2 messages, if the signature message occurs after opening the app, the web page may fail to send. Best practice is to connect to the wallet first.
Connect successfully and then click the button to generate the signature request. Each time you need to wake up the app, it should be triggered by a separate user action.
- [Preparation](https://web3.okx.com/onchainos/dev-docs/sdks/web-detect-okx-wallet.md)
# Preparation
If you haven't downloaded the OKX Plugin Wallet yet, please go to the download page:
- [Chrome Plugin](https://chromewebstore.google.com/detail/%E6%AC%A7%E6%98%93-web3-%E9%92%B1%E5%8C%85/mcohilncbfahbmgdjkbpemcciiolgcge)
- [Brave Plugin](https://chromewebstore.google.com/detail/%E6%AC%A7%E6%98%93-web3-%E9%92%B1%E5%8C%85/mcohilncbfahbmgdjkbpemcciiolgcge)
- [Edge Plugin](https://microsoftedge.microsoft.com/addons/detail/%E6%AC%A7%E6%98%93-web3-%E9%92%B1%E5%8C%85/pbpjkcldjiffchgbbndmhojiacbgflha)
- [Safari Plugin](https://apps.apple.com/app/okx-wallet/id6463797825)
If you have already downloaded the plugin wallet, check if the plugin wallet is running normally in your browser by copying the following code into the browser's developer console:
```javascript
if (typeof window.okxwallet !== 'undefined') {
console.log('OKX is installed!');
}
```
- [EVM Compatible Chains](https://web3.okx.com/onchainos/dev-docs/sdks/chains/evm/introduce.md)
# EVM Compatible Chains
EVM compatible chains refer to blockchain networks that use Ethereum Virtual Machine (EVM) technology.
These chains share the same smart contract execution environment as Ethereum, allowing developers to easily deploy Ethereum-based applications to these networks. This compatibility enables developers to use existing Ethereum tools and libraries, such as Solidity, Web3.js, and Truffle, to build and deploy decentralized applications (DApps).
Common EVM compatible chains include Polygon, Avalanche, and Fantom. The emergence of these networks enriches the blockchain ecosystem, providing users with more options while also promoting cross-chain interoperability.
- [Obtain wallet address](https://web3.okx.com/onchainos/dev-docs/sdks/chains/evm/web-access-user-accounts.md)
# Obtain wallet address
Wallet addresses are used in various scenarios, including as identifiers and for signing transactions.
For example, in Ethereum, an Ethereum address is the unique public identifier of an account. Each account has a corresponding address, which is used for interactions and identification on the network. The account contains all state information and functions associated with that address.
If a DApp wants to request a user's signature or have the user approve a transaction, it must use the `eth_requestAccounts` RPC method to access the user's account.
## Creating a Connection
It is recommended to provide a button here that allows users to connect the OKX Web3 wallet to the DApp. Clicking this button will call the `eth_requestAccounts` method to access the user's account address.
In the example project code below, the JavaScript code accesses the user's account address when the user clicks the connect button, and the HTML code displays the button and the current account address:
```html
```
```javascript
const connectEthereumButton = document.querySelector('.connectEthereumButton');
connectEthereumButton.addEventListener('click', () => {
//Will Start the OKX extension
okxwallet.request({ method: 'eth_requestAccounts' });
});
```
## Detect Account Address Changes
You can also listen to the emitted events to get updates:
```typescript
okxwallet.on('accountsChanged', handler: (accounts: Array) => void);
```
Whenever the return value of the eth_accounts RPC method changes, OKX will emit a corresponding event notification. eth_accounts will return an array that is either empty or contains a single account address. If an account address is present, it is the most recently used account address accessible to the caller.
Since callers are identified by their URL origin, sites with the same origin will hold the same permissions. The accountsChanged event is emitted whenever the publicly available account address changes.
## Obtaining Account Addresses for More Chains
Check out [injected providers](./provider) for account monitoring on other blockchains.
- [Obtain chainId](https://web3.okx.com/onchainos/dev-docs/sdks/chains/evm/web-detect-user-network.md)
# Obtain chainId
For Ethereum chain development, it's important to keep track of a user's network chain ID, as all RPC requests are submitted to the currently connected network.
Use the `eth_chainId` RPC method to detect the chain ID of a user's current network. Listen to the `chainChanged` provider event to detect when the user changes networks.
As an example, the following code is used to detect a user's network and when the user changes networks:
```javascript
const chainId = await window.ethereum.request({ method: 'eth_chainId' });
window.ethereum.on('chainChanged', handleChainChanged);
function handleChainChanged(chainId) {
// We recommend reloading the page, unless you must do otherwise.
window.location.reload();
}
```
### Chain IDs
These are the IDs of the Ethereum chains that OKX Wallet supports by default.
Consult [chainid.network](https://chainid.network) for more.
| Hex | Decimal | Network |
| ---- | ------- | ------------------------------- |
| 0x1 | 1 | Ethereum Main Network (Mainnet) |
| 0x2711 | 10001 | ETHW |
| 0x38 | 56 | Binance Smart Chain Mainnet |
| 0x89 | 137 | Matic Mainnet |
| 0xa86a | 43114 | Avax Mainnet |
| 0xfa | 250 | Fantom Mainnet |
| 0xa4b1 | 42161 | Arbitrum Mainnet |
| 0xa | 10 | Optimism Mainnet |
| 0x19 | 25 | Cronos Mainnet |
| 0x2019 | 8217 | Klaytn Mainnet |
| 0x141 | 321 | KCC Mainnet |
| 0x440 | 1088 | Metis Mainnet |
| 0x120 | 288 | Boba Mainnet |
| 0x64 | 100 | Gnosis Mainnet |
| 0x505 | 1285 | Moonriver Mainnet |
| 0x504 | 1284 | Moonbeam Mainnet |
| 0x406 | 1030 | Conflux eSpace |
- [Display tokens](https://web3.okx.com/onchainos/dev-docs/sdks/chains/evm/web-display-tokens.md)
# Display tokens
When users open OKX Wallet, they're shown a variety of assets, including tokens. By default, OKX Wallet detects mainstream tokens and displays them. However, for most tokens, users will need to add the tokens themselves.
While this is possible using our UI with the `Add Token` button, it can be cumbersome, and more prone to error since it involves the process of users interacting with contract addresses.
It can greatly improve security and user experience for adding tokens to OKX Wallet by taking advantage of the `wallet_watchAsset` API as defined in [EIP-747](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-747.md).
### Example
If you'd like to integrate token suggestions into your own web app, you can follow this code snippet:
```javascript
const tokenAddress = '0xd00981105e61274c8a5cd5a88fe7e037d935b513';
const tokenSymbol = 'TUT';
const tokenDecimals = 18;
const tokenImage = 'http://placekitten.com/200/300';
try {
// wasAdded is a boolean. Like any RPC method, an error may be thrown.
const wasAdded = await okxwallet.request({
method: 'wallet_watchAsset',
params: {
type: 'ERC20', // Initially only supports ERC20, but eventually more!
options: {
address: tokenAddress, // The address that the token is at.
symbol: tokenSymbol, // A ticker symbol or shorthand, up to 5 chars.
decimals: tokenDecimals, // The number of decimals in the token
image: tokenImage, // A string url of the token logo
},
},
});
if (wasAdded) {
console.log('Thanks for your interest!');
} else {
console.log('Your loss!');
}
} catch (error) {
console.log(error);
}
```
Here are a couple live web applications that let you enter token details, and then share them with a simple web link:
- [Watch Token](https://vittominacori.github.io/watch-token/create/)
- [Send transactions](https://web3.okx.com/onchainos/dev-docs/sdks/chains/evm/web-send-transaction.md)
# Send transactions
`eth_sendTransaction`
## Description
Transactions are formal actions on a blockchain. They're always initiated in OKX Wallet by calling `eth_sendTransaction` method. They include simply sending Ether, sending tokens, creating new smart contract, or changing the state of on the blockchain in any way. They're always initiated by a signature from an external account, or a simple key pair.
In the OKX Web3 Wallet, you can use the `okxwallet.request` method to initiate a transaction.
## Parameters
This section mainly introduces the transaction parameters covered in this document. Most of the transaction parameters mentioned here will be handled by the OKX Web3 Wallet. Transactions are categorized into legacy transactions and EIP-1559 transactions, which will be discussed in order.
### Legacy Transactions
```javascript
const transactionParameters = {
gasPrice: '0x09184e72a000', // customizable by user during OKX confirmation.
gas: '0x2710', // customizable by user during OKX confirmation.
to: '0x0000000000000000000000000000000000000000', // Required except during contract publications.
from: okxwallet.selectedAddress, // must match user's active address.
value: '0x00', // Only required to send ether to the recipient from the initiating external account.
data:
'0x7f7465737432000000000000000000000000000000000000000000000000000000600057', // Optional, but used for defining smart contract creation and interaction.
chainId: '0x3', // Used to prevent transaction reuse across blockchains. Auto-filled by OKX.
};
```
**Gas price [optional]**
Optional parameter - best used on private blockchains
In Ethereum, every transaction specifies a price for the gas it'll consume. To maximize their profit, block producers will pick pending transactions with higher gas prices first when creating the next block. This means that a high gas price will usually cause your transaction to be processed faster at the cost of higher transaction fees. Note that this may not be true for Layer 2 networks which may have a fixed gas price or no gas price at all.
In other words, while you can ignore this parameter on OKX Wallet's default networks, you may want to include it in situations where your application knows more about the target network than we do. On our default networks, OKX Wallet allows you to choose between "slow," "medium," and "fast" options for your gas price.
**Gas Limit [optional]**
Optional parameter. Rarely useful to DApp developers.
Gas limit is a highly optional parameter, and we automatically calculate a reasonable price for it. You'll probably know if, for some reason, your smart contract benefits from a custom gas limit.
**To [optional]**
A hex-encoded Ethereum address. Required for transactions with a recipient (all transactions except for contract creation).
Contract creation occurs when there's no `to` value but there's a `data` value.
**Value [optional]**
Hex-encoded value of the network's native currency to be sent. On the main Ethereum network, that currency is Ether, denominated in Wei which is 1e-18 Ether.
Note that these numbers frequently used in Ethereum are far more precise than native JavaScript numbers, and can cause unpredictable behaviors if they're not anticipated. For this reason, we highly recommend using BN.js when manipulating values intended for blockchain.
Hex-encoded value of the network's native currency to send. On the Main Ethereum network, this is [ether](https://www.ethereum.org/eth), which is denominated in _wei_, which is `1e-18` ether.
Please note that these numbers often used in Ethereum are far higher precision than native JavaScript numbers, and can cause unpredictable behavior if not anticipated. For this reason, we highly recommend using [BN.js](https://github.com/indutny/bn.js/) when manipulating values intended for the blockchain.
**Data [optional]**
Required for smart contract creation.
This field is also used for specifying contract methods and their parameters. You can learn more about how that data is encoded on [the solidity ABI spec](https://solidity.readthedocs.io/en/develop/abi-spec.html).
**Chain ID [currently ignored]**
Chain ID is currently derived from the user's selected network at `okxwallet.networkVersion`.
**Return value**
DATA, 32 bytes - transaction hash. If the transaction is not available yet, it is a zero hash.
When you create a contract, after the transaction is mined, use [eth_getTransactionReceipt](https://ethereum.org/zh/developers/docs/apis/json-rpc/#eth_gettransactionreceipt) to get the contract address.
### EIP-1559 Transactions
```javascript
const transactionParameters = {
maxPriorityFeePerGas: "0x0", // Maximum fee, in wei, the sender is willing to pay per gas above the base fee.
maxFeePerGas: "0x6f4d3132b", // Maximum total fee (base fee + priority fee), in wei, the sender is willing to pay per gas.
gas: '0x2710', // customizable by user during OKX confirmation.
to: '0x0000000000000000000000000000000000000000', // Required except during contract publications.
from: okxwallet.selectedAddress, // must match user's active address.
value: '0x00', // Only required to send ether to the recipient from the initiating external account.
data:
'0x7f7465737432000000000000000000000000000000000000000000000000000000600057', // Optional, but used for defining smart contract creation and interaction.
chainId: '0x3', // Used to prevent transaction reuse across blockchains. Auto-filled by OKX.
};
```
For [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) transactions, the key difference from legacy transactions is the use of `maxPriorityFeePerGas` and `maxFeePerGas` instead of `gasPrice` .
**maxPriorityFeePerGas [Optional]**
The additional tip the user is willing to pay to the miner/validator for prioritizing the transaction.
**maxFeePerGas [Optional]**
The maximum total amount the user is willing to pay per unit of gas, including both the base fee and the priority fee.
## Example
Open in [codeopen](https://codepen.io/okxwallet/pen/VwGeGQb).
```html
```
```javascript
const ethereumButton = document.querySelector('.connectEthereumButton');
const signTransactionButton = document.querySelector('.signTransactionButton');
let accounts = [];
signTransactionButton.addEventListener('click', () => {
okxwallet
.request({
method: 'eth_sendTransaction',
params: [
{
from: accounts[0],
to: '0x2f318C334780961FB129D2a6c30D0763d9a5C970',
value: '0x29a2241af62c0000',
gasPrice: '0x09184e72a000',
gas: '0x2710',
},
],
})
.then((txHash) => console.log(txHash))
.catch((error) => console.error);
});
ethereumButton.addEventListener('click', () => {
getAccount();
});
async function getAccount() {
try{
accounts = await okxwallet.request({ method: 'eth_requestAccounts' });
}catch(error){
console.log(error);
}
}
```
- [Interact with smart contracts](https://web3.okx.com/onchainos/dev-docs/sdks/chains/evm/web-interact-with-smart-contracts.md)
# Interact with smart contracts
To interact with a smart contract, your DApp needs the contract's:
- [Network](#Contract-network)
- [Address](#Contract-address)
- [ABI](#Contract-ABI)
- [Bytecode](#Contract-bytecode)
- [Source code](#Contract-source-code)
## Contract network
If you're not connected to the right network, you can't send transactions to your contract. Many DApp developers deploy their contracts to a testnet first, in order to avoid potentially disastrous fees if something goes wrong during development and testing on Mainnet.
Regardless of which network you deploy your final DApp on, your users must be able to access it. Take Ethereum as an example. You can use the [``wallet_addEthereumChain``](https://ethereum-magicians.org/t/eip-3085-wallet-addethereumchain/5469) and [``wallet_switchEthereumChain``](./web-add-network) RPC methods to prompt the user to add a chain that you suggest, and switch to it using a confirmation dialogue.
## Contract address
Every account has an address, whether an external key-pair account or a smart contract. For any smart contract library to communicate with your contracts, a smart contract must know the exact address.
## Contract ABI
Take Ethereum as an example, the [ABI specification](https://solidity.readthedocs.io/en/develop/abi-spec.html) is a way to encode the interface of a smart contract that's comprehensible to your user interface. The ABI is an array of method-describing objects, and when you feed this and the address into a contract-abstraction library, the ABI tells those libraries about what methods to provide, and how to compose transactions to call those methods.
Example libraries include:
- [Ethers](https://www.npmjs.com/package/ethers)
- [web3.js](https://www.npmjs.com/package/web3)
- [Embark](https://github.com/embarklabs)
- [ethjs](https://www.npmjs.com/package/ethjs)
- [Truffle](https://trufflesuite.com/).
## Contract bytecode
If your DApp publishes a new pre-compiled smart contract, it might need to include some bytecode. You don't know the contract address in advance; you must publish the contract, watch for the transaction to be processed, and then extract the final contract's address from the completed transaction.
If you publish a contract from bytecode, you still need an [ABI](#Contract-ABI) to interact with it. The bytecode doesn't describe how to interact with the final contract.
## Contract source code
If your DApp allows users to edit smart contract source code and compile it, similar to Remix, you can import a whole compiler. You derive your bytecode and ABI from that source code, and eventually derive the contract's address from the completed transaction, where that bytecode is published.
- [Switch network](https://web3.okx.com/onchainos/dev-docs/sdks/chains/evm/web-add-network.md)
# Switch network
### Chain switch
`wallet_switchEthereumChain`
This method is detailed in [EIP-3326](https://ethereum-magicians.org/t/eip-3326-wallet-switchethereumchain).
**Description**
This request asks the user if they are switching to a chain with a specified `chainId` and returns a value of confirmation.
As with any method that returns a confirmation, `wallet_switchEthereumChain` should **only** be called in response to a direct user action, such as a button click.
OKX will automatically reject the request under the following circumstances:
- If the chain ID is incorrectly formatted;
- If the chain with the specified chain ID has not been added to OKX.
We recommend that you use [` wallet_addEthereumChain `](https://ethereum-magicians.org/t/eip-3085-wallet-addethereumchain/5469) together with it.
```javascript
try {
await okxwallet.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: '0xf00' }],
});
} catch (switchError) {
// This error code indicates that the chain has not been added to OKX.
if (switchError.code === 4902) {
try {
await okxwallet.request({
method: 'wallet_addEthereumChain',
params: [
{
chainId: '0xf00',
chainName: '...',
rpcUrls: ['https://...'] /* ... */,
},
],
});
} catch (addError) {
// handle "add" error
}
}
// handle other "switch" errors
}
```
**Parameters**
- `Array`
0. `SwitchEthereumChainParameter` - The metadata of the chain that OKX will switch to.
```typescript
interface SwitchEthereumChainParameter {
chainId: string; // A 0x-prefixed hexadecimal string
}
```
**Chain IDs**
These are the IDs of the Ethereum chains that OKX Wallet supports by default. Consult [chainid.network](https://chainid.network) for more.
| Hex | Decimal | Network |
| ---- | ------- | ------------------------------- |
| 0x1 | 1 | Ethereum Main Network (Mainnet) |
| 0x2711 | 10001 | ETHW |
| 0x42 | 66 | OKT Chain Mainnet |
| 0x38 | 56 | Binance Smart Chain Mainnet |
| 0x89 | 137 | Matic Mainnet |
| 0xa86a | 43114 | Avax Mainnet |
| 0xfa | 250 | Fantom Mainnet |
| 0xa4b1 | 42161 | Arbitrum Mainnet |
| 0xa | 10 | Optimism Mainnet |
| 0x19 | 25 | Cronos Mainnet |
| 0x2019 | 8217 | Klaytn Mainnet |
| 0x141 | 321 | KCC Mainnet |
| 0x440 | 1088 | Metis Mainnet |
| 0x120 | 288 | Boba Mainnet |
| 0x64 | 100 | Gnosis Mainnet |
| 0x505 | 1285 | Moonriver Mainnet |
| 0x504 | 1284 | Moonbeam Mainnet |
| 0x406 | 1030 | Conflux eSpace |
**Return value**
`null` - The method returns `null` if the request was successful; otherwise, it will return an error.
If the error code (`error.code`) is `4902`, then the requested chain has not been added by OKX, and you have to request to add it via `wallet_addEthereumChain`.
**Example**
Open in [codeopen](https://codepen.io/okxwallet/pen/yLxeRON).
```html
```
```javascript
const connectEthereumButton = document.querySelector('.connectEthereumButton');
const switchChainButton = document.querySelector('.switchChainButton');
let accounts = [];
//Sending Ethereum to an address
switchChainButton.addEventListener('click', () => {
try {
const chainId = okxwallet.chainId === "0x42" ? "0x38" : "0x42";
await okxwallet.request({
method: "wallet_switchEthereumChain",
params: [{ chainId: chainId }]
});
} catch (switchError) {
// This error code indicates that the chain has not been added to OKX Wallet.
if (error.code === 4902) {
try {
await okxwallet.request({
method: "wallet_addEthereumChain",
params: [{ chainId: "0xf00", rpcUrl: "https://..." /* ... */ }]
});
} catch (addError) {
// handle "add" error
}
}
// handle other "switch" errors
}
});
connectEthereumButton.addEventListener('click', () => {
getAccount();
});
async function getAccount() {
try{
accounts = await okxwallet.request({ method: 'eth_requestAccounts' });
}catch(error){
console.log(error);
}
}
```
- [Provider API](https://web3.okx.com/onchainos/dev-docs/sdks/chains/evm/provider.md)
# Provider API
## What is injected provider API?
The OKX injected provider API is a JavaScript API that OKX injects into websites visited by our users. Your DApp can use this API to request users' accounts, read data from blockchains users are connected to, and help users sign messages and transactions.
## Connecting to your wallet
`eth_requestAccounts`
This method is detailed in [EIP-1102](https://eips.ethereum.org/EIPS/eip-1102).
Under the hood, it calls [`wallet_requestPermissions`](#wallet-requestpermissions) to gain the `eth_accounts` permission.
Since `eth_accounts` is currently the only permission, this method is all you need for now.
**Description**
This request asks the target user to provide an Ethereum address to be identified by. The return value would be a Promise which could be parsed as an array of a single Ethereum address string. If the user denies the request, the Promise will be rejected, returning `4001` error.
The request will cause an OKX popup to appear. You should only request the user's account in response to a direct user action, such as a button click. You should always disable the button that dispatches this request while the previous request is still pending.
If you can't retrieve the user's account(s), you should encourage the user to initiate an account request.
**Return value**
`string[]` - An array of a single, hexadecimal Ethereum address string.
**Example**
Open in [codeopen](https://codepen.io/okxwallet/pen/WNgrgLP).
```html
```
```javascript
const connectEthereumButton = document.querySelector('.connectEthereumButton');
connectEthereumButton.addEventListener('click', () => {
//Will Start the OKX extension
okxwallet.request({ method: 'eth_requestAccounts' });
});
```
## Adding token
**Note**: This function is only supported on the OKX browser extension.
## `wallet_watchAsset`
This method is specified in [EIP-747](https://eips.ethereum.org/EIPS/eip-747).
**Description**
This requests the user to track a token in OKX Wallet. It'll return a `boolean` indicating if the token was successfully added.
Most Ethereum wallets support a certain set of tokens, which usually comes from a centrally curated registry of tokens. `wallet_watchAsset` enables Web3 application developers to ask their users to track tokens in their wallets at runtime.
Once added, the token is indistinguishable from those added via legacy methods, such as a centralized registry.
**Parameters**
- `WatchAssetParams` - The metadata of the asset to watch.
```typescript
interface WatchAssetParams {
type: 'ERC20'; // In the future, other standards will be supported
options: {
address: string; // The address of the token contract
'symbol': string; // A ticker symbol or shorthand, up to 11 characters
decimals: number; // The number of token decimals
image: string; // A string url of the token logo
};
}
```
**Return value**
`boolean` - `true` if the token was added, otherwise, `false`.
**Example**
Open in [codeopen](https://codepen.io/okxwallet/pen/NWLxegB).
```html
```
```javascript
const ethereumButton = document.querySelector('.connectEthereumButton');
const addTokenButton = document.querySelector('.addTokenButton');
addTokenButton.addEventListener('click', async () => {
await okxwallet.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0x1' }] });
okxwallet
.request({
method: 'wallet_watchAsset',
params: {
type: 'ERC20',
options: {
address: '0xdac17f958d2ee523a2206206994597c13d831ec7',
symbol: 'USDT',
decimals: 6,
image: 'https://foo.io/token-image.svg',
},
},
})
.then((success) => {
if (success) {
console.log('USDT successfully added to wallet!');
} else {
throw new Error('Something went wrong.');
}
})
.catch(console.error);
});
ethereumButton.addEventListener('click', () => {
getAccount();
});
function getAccount() {
okxwallet.request({ method: 'eth_requestAccounts' }).catch((error)=>{
console.log(error);
});
}
```
## EIP-5792 Support
The wallet supports batch sending multiple calls and querying transaction results.
Defined by the [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792) specification.
### wallet_sendCalls
Request the wallet to batch send multiple calls.
**Parameters**
```ts
type Capability = {
[key: string]: unknown;
optional?: boolean;
}
type SendCallsParams = {
version: string;
id?: string;
from?: `0x${string}`;
chainId: `0x${string}`;
atomicRequired: boolean;
calls: {
to?: `0x${string}`;
data?: `0x${string}`;
value?: `0x${string}`;
capabilities?: Record;
}[];
capabilities?: Record;
};
```
* `version`: Fixed value, "2.0.0"
* `id`: Optional, unique identifier for the request. If not provided, the wallet will automatically generate an id
* `from`: Optional, the wallet address initiating the request. If not provided, it will be the currently connected wallet
* `chainId`: The chain id for initiating the transaction
* `atomicRequired`: Whether it must be an atomic transaction. Currently, all transactions sent through OKX are atomic transactions
* `calls`: List of batch calls
* `to`: The contract address to call
* `data`: Transaction data
* `value`: Amount of main currency to transfer
* `capabilities`: Not currently supported
* `capabilities`: Not currently supported
**Return Value**
```ts
type SendCallsResult = {
id: string;
capabilities?: Record;
};
```
**Usage Example**
```js
window.okxwallet.request({
"method": "wallet_sendCalls",
"params": [
{
version: "2.0.0",
from: "0x819d3f4c17d50004c165d06f22418c4f28010eda",
chainId: "0x1",
atomicRequired: true,
calls: [
{
to: "0x54f1C1965B355e1AB9ec3465616136be35bb5Ff7",
value: "0x0"
},
{
to: "0x2D48e6f5Ae053e4E918d2be53570961D880905F2",
value: "0x0"
}
],
}
],
})
```
### wallet_getCallsStatus
Get the status of transactions previously sent via `wallet_sendCalls`.
**Parameters**
```ts
// Parameters
type GetCallsParams = [string];
```
Query transaction by `id`.
**Return Value**
```ts
type GetCallsResult = {
version: string;
id: `0x${string}`;
chainId: `0x${string}`;
status: number;
atomic: boolean;
receipts?: {
logs: {
address: `0x${string}`;
data: `0x${string}`;
topics: `0x${string}`[];
}[];
status: `0x${string}`;
blockHash: `0x${string}`;
blockNumber: `0x${string}`;
gasUsed: `0x${string}`;
transactionHash: `0x${string}`;
}[];
capabilities?: Record;
};
```
* `version`: Version of the sent transaction
* `id`: Unique identifier
* `chainId`: Chain id where the transaction was initiated
* `status`: Transaction status,
* `100`: Confirming
* `200`: Confirmed
* `400`: Off-chain failure
* `500`: Rejected
* `receipts`: Detailed transaction information
* `capabilities`: Not currently supported
**Usage Example**
```js
window.okxwallet.request({
"method": "wallet_getCallsStatus",
"params": [
"0x123456"
],
})
```
### wallet_getCapabilities
Returns the wallet's support information for the corresponding atomic functionality.
**Parameters**
```ts
type GetCapabilitiesParams = [`0x${string}`, [`0x${string}`]];
```
* The first parameter is the address to query
* The second parameter is the list of chains to query
**Return Value**
```ts
type GetCapabilitiesResult = Record<`0x${string}`, >;
```
If a chain does not support sending batch transactions, it will not appear in the query results.
**Usage Example**
```js
window.okxwallet.request({
"method": "wallet_getCapabilities",
"params": [
"0x00b909cefa36ab6bc26f5887a867e46ef162238f0a171b1c2974b665afd4237f",
[
"0x1",
"0xaa36a7"
]
],
});
// Response example
{
"0x1": {
"atomic": {
"status": "supported"
}
},
"0xaa36a7": {
"atomic": {
"status": "ready"
}
}
}
```
## Events
OKX's providers have implemented the [Node.js `EventEmitter`](https://nodejs.org/api/events.html) API. This section details the events emitted via that API. There are innumerable `EventEmitter` guides on the internet, but for this documentation, you can listen to events such as:
```javascript
okxwallet.on('accountsChanged', (accounts) => {
// Handle the new accounts, or lack thereof.
// "accounts" will always be an array, but it can be empty.
});
okxwallet.on('chainChanged', (chainId) => {
// Handle the new chain.
// Correctly handling chain changes can be complicated.
// We recommend reloading the page unless you have a very good reason not to.
window.location.reload();
});
```
Also, don't forget to remove listeners once you are done listening to them (for example, when unmounting a component in React):
```javascript
function handleAccountsChanged(accounts) {
// ...
}
okxwallet.on('accountsChanged', handleAccountsChanged);
// Later
okxwallet.removeListener('accountsChanged', handleAccountsChanged);
```
The first argument of the `okxwallet.removeListener` is the event name and the second argument is the reference to the same function, which has passed to `okxwallet.on` for the event name mentioned in the first argument.
**connect**
```typescript
interface ConnectInfo {
chainId: string;
}
okxwallet.on('connect', handler: (connectInfo: ConnectInfo) => void);
```
OKX's providers will emit this event when they can submit RPC requests to the chain for the first time. We recommend using a `connect` event handler and `okxwallet.isConnected()` to confirm if OKX Wallet is connected.
**disconnect**
```typescript
okxwallet.on('disconnect', handler: (error: ProviderRpcError) => void);
```
OKX's providers will emit this event if they can't submit RPC requests to the chain. Usually, this only occurs in the case of network connection issues or certain other unforeseeable error states.
Once `disconnect` has been emitted, the provider won't accept any new requests until the connection to the chain has been re-established, which requires reloading the page. You can also use `okxwallet.isConnected()` to confirm if OKX Wallet is disconnected.
**accountsChanged**
```typescript
okxwallet.on('accountsChanged', handler: (accounts: Array