Browser Wallet SDK
The SDK combines creating and canceling limit order functions.
How to Install the sdk in your project
npm i @openocean.finance/limitorder-sdk
How to use the sdk in your project
import { openoceanLimitOrderSdk } from '@openocean.finance/limitorder-sdk';
You can then use all the functions explored by the SDK (API and swapSdk).
Supported Provider Types
Type
Example
Description
Web3 provider
new Web3(window.ethereum)
Traditional MetaMask-style Web3.js
Ethers provider
new ethers.providers.Web3Provider(...)
(v5) / new ethers.BrowserProvider(...)
(v6)
Modern Ethers.js integration
Initialize Wallet Provider
Using Web3.js (web3-provider
)
web3-provider
)import Web3 from 'web3';
await window.ethereum.request({ method: 'eth_requestAccounts' });
const provider = new Web3(window.ethereum);
const address = await provider.eth.getAccounts();
Pass the provider to the SDK like this:
const sdkParams = {
provider, // Web3 instance
chainKey: 'base', // Supported: base, arbitrum, etc.
account: address[0], // Wallet address
chainId: 8453, // Chain ID
};
Using Ethers.js (ethers-provider
)
ethers-provider
)Ethers v5
import { ethers } from 'ethers';
const provider = new ethers.providers.Web3Provider(window.ethereum); // v5
const signer = provider.getSigner();
const address = await signer.getAddress();
Ethers v6 (Recommended)
import { ethers } from 'ethers';
const provider = new ethers.BrowserProvider(window.ethereum); // v6
const signer = await provider.getSigner();
const address = await signer.getAddress();
Usage in SDK:
const sdkParams = {
provider, // Ethers provider (v5 or v6)
chainKey: 'base',
account: address,
chainId: 8453,
};
Create a Limit Order
const order = await openoceanLimitOrderSdk.createLimitOrder(
sdkParams,
{
makerTokenAddress: '0xabc...',
takerTokenAddress: '0xdef...',
makerTokenDecimals: 6,
takerTokenDecimals: 6,
makerAmount: '1000000', // 1.0 USDC
takerAmount: '2000000', // 2.0 USDT
gasPrice: parseInt(gasPrice * 1.2),
expire: '1H', // Expiration time (e.g., "1H")
}
);
Cancel a Limit Order
await openoceanLimitOrderSdk.cancelLimitOrder(
sdkParams,
{
orderData: order.data,
gasPrice: parseInt(gasPrice * 1.2),
}
);
Load Chart (Optional)
await openoceanLimitOrderSdk.loadChart({
chain: "bsc", // chain code,
fromTokenSymbol: "BNB", // from token symbol
toTokenSymbol: "BUSD", // to token symbol
container: document.getElementById('chart'), // chart's container
timeLimit: "1d", // 1d、1w、1m、1y、all
theme: "dark", // dark、light
type: "line", // line、bar
setPoint: ({ label, des }) => { // setPoint callback
console.log('setPoint', label, des);
}
})
Expiration Options
[
{ value: "10M", label: "10 Mins" },
{ value: "1H", label: "1 Hour" },
{ value: "1D", label: "1 Day" },
{ value: "3D", label: "3 Days" },
{ value: "7D", label: "7 Days" },
{ value: "30D", label: "1 Month" },
{ value: "3Month", label: "3 Months" },
{ value: "6Month", label: "6 Months" },
{ value: "1Y", label: "1 Year" }
]
Full Demo (Vue.js + @openocean.finance/limitorder-sdk)
This is a minimal working demo using Vue.js, Web3 or Ethers v6, and OpenOcean's limit order SDK:
import React, { useState, useEffect } from 'react';
import { ethers } from 'ethers';
import { openoceanLimitOrderSdk } from '@openocean.finance/limitorder-sdk';
import axios from 'axios';
import {
supportedChains,
switchToSupportedChain,
getCurrentChainId,
isChainSupported
} from '../utils/chainUtils';
import './Pages.css';
/**
* LimitOrder Component - Handles limit order creation and management
* Allows users to create and manage limit orders for token trading
*/
const LimitOrder = () => {
const baseUrl = 'https://open-api.openocean.finance';
// Wallet connection state
const [isWalletConnected, setIsWalletConnected] = useState(false);
const [walletAccount, setWalletAccount] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [provider, setProvider] = useState(null);
const [currentChainId, setCurrentChainId] = useState(null);
// Limit order state - maker and taker amounts
const [makerAmount, setMakerAmount] = useState(0.01);
const [takerAmount, setTakerAmount] = useState(0.02);
const [expireTime, setExpireTime] = useState('1H');
// Orders list state
const [orders, setOrders] = useState([]);
// Chain configuration
const chain = {
chainId: 8453,
chainName: 'base',
}
// Token configuration for trading pair
const inToken = {
"address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"decimals": 6,
"symbol": "USDC",
};
const outToken = {
"address": "0xfde4c96c8593536e31f229ea8f37b2ada2699bb2",
"decimals": 6,
"symbol": "USDT"
};
// Available expiration time options for limit orders
const limitOrderExpireOptions = [
{ value: "10M", label: "10 Mins" },
{ value: "1H", label: "1 Hour" },
{ value: "1D", label: "1 Day" },
{ value: "3D", label: "3 Days" },
{ value: "7D", label: "7 Days" },
{ value: "30D", label: "1 Month" },
{ value: "3Month", label: "3 Month" },
{ value: "6Month", label: "6 Month" },
{ value: "1Y", label: "1 Year" }
];
// Initialize component on mount
useEffect(() => {
checkWalletConnection();
}, []);
/**
* Check if wallet is already connected on component mount
*/
const checkWalletConnection = async () => {
if (typeof window.ethereum !== 'undefined') {
try {
const accounts = await window.ethereum.request({ method: 'eth_accounts' });
if (accounts.length > 0) {
await connectWallet();
}
} catch (error) {
console.error('Error checking wallet connection:', error);
}
}
};
/**
* Connect to MetaMask wallet and handle chain switching
*/
const connectWallet = async () => {
setIsLoading(true);
try {
if (typeof window.ethereum === 'undefined') {
alert('Please install MetaMask!');
return;
}
await window.ethereum.request({
method: 'eth_requestAccounts'
});
// Get current chain ID
const chainId = await getCurrentChainId();
setCurrentChainId(chainId);
// Check if current chain is supported
if (chainId != chain.chainId) {
const shouldSwitch = window.confirm(
`Current chain (${chainId}) is not supported. Would you like to switch to Base?`
);
if (shouldSwitch) {
const switched = await switchToSupportedChain(chain.chainId);
if (!switched) {
alert('Failed to switch to supported chain. Please switch manually.');
setIsLoading(false);
return;
}
// Update chain ID after switch
const newChainId = await getCurrentChainId();
setCurrentChainId(newChainId);
} else {
setIsLoading(false);
return;
}
}
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const address = await signer.getAddress();
setProvider(provider);
setWalletAccount(address);
setIsWalletConnected(true);
getLimitOrder(address);
console.log('Wallet connected:', address, 'on chain:', chainId);
// Listen for account changes
window.ethereum.on('accountsChanged', (newAccounts) => {
if (newAccounts.length > 0) {
connectWallet();
} else {
disconnectWallet();
}
});
// Listen for chain changes
window.ethereum.on('chainChanged', (chainId) => {
const newChainId = parseInt(chainId, 16);
setCurrentChainId(newChainId);
if (!isChainSupported(newChainId)) {
alert(`Chain ${newChainId} is not supported. Please switch to a supported chain.`);
setIsWalletConnected(false);
}
});
} catch (error) {
console.error('Error connecting wallet:', error);
alert('Failed to connect wallet: ' + error.message);
} finally {
setIsLoading(false);
}
};
/**
* Disconnect wallet and reset state
*/
const disconnectWallet = () => {
setWalletAccount('');
setIsWalletConnected(false);
setCurrentChainId(null);
setProvider(null);
console.log('Wallet disconnected');
};
/**
* Create a new limit order
* Handles order creation using OpenOcean SDK and API
*/
const createLimitOrder = async () => {
if (!isWalletConnected) {
alert('Please connect your wallet first!');
return;
}
if (!makerAmount || !takerAmount) {
alert('Please enter price and amount!');
return;
}
try {
setIsLoading(true);
if (!provider) {
alert('Please connect wallet first')
return
}
const gasPrice = await getGasPrice();
const providerParams = {
provider: provider,
chainKey: chain.chainName,
account: walletAccount,
chainId: chain.chainId
}
// Prepare order parameters
const params = {
makerTokenAddress: inToken.address,
makerTokenDecimals: inToken.decimals,
takerTokenAddress: outToken.address,
takerTokenDecimals: outToken.decimals,
makerAmount: makerAmount * (10 ** inToken.decimals) + '',
takerAmount: takerAmount * (10 ** outToken.decimals) + '',
gasPrice: gasPrice,
expire: expireTime,
}
// Create limit order using SDK
let order = await openoceanLimitOrderSdk.createLimitOrder(
providerParams,
params
);
// Submit order to OpenOcean API
const result = await axios.post(
`${baseUrl}/v1/${chain.chainId}/limit-order`,
order,
{
headers: { 'Content-Type': 'application/json' },
}
);
await getLimitOrder();
console.log('Limit order created:', result.data);
alert('Limit order created successfully!');
setMakerAmount('');
setTakerAmount('');
} catch (error) {
console.error('Error creating limit order:', error);
alert('Failed to create limit order: ' + error.message);
} finally {
setIsLoading(false);
}
};
/**
* Cancel an existing limit order
* Handles order cancellation through API and blockchain
*/
const cancelOrder = async (order) => {
try {
const { orderHash } = order;
// Cancel order through API
const { data } = await axios.post(
`${baseUrl}/v1/${chain.chainId}/limit-order/cancelLimitOrder`,
{ orderHash }
);
const { status } = (data && data.data) || {};
// If order is still active, cancel on blockchain
if (status && !(status === 3 || status === 4)) {
const gasPrice = await getGasPrice();
await openoceanLimitOrderSdk.cancelLimitOrder(
{
provider: provider,
chainKey: chain.chainName,
account: walletAccount,
chainId: chain.chainId
},
{
orderData: order.data,
gasPrice,
}
);
}
await getLimitOrder();
alert('Order cancelled successfully!');
} catch (error) {
console.error('Error canceling order:', error);
alert('Failed to cancel order: ' + error.message);
}
};
/**
* Fetch user's limit orders from OpenOcean API
*/
const getLimitOrder = async (account) => {
let url = `${baseUrl}/v1/${chain.chainId}/limit-order/address/${walletAccount || account}?page=1&limit=100&statuses=[1,2,5]&sortBy=createDateTime&exclude=0`
const res = await axios.get(url);
setOrders(res.data.data)
}
/**
* Get current gas price with 20% buffer
*/
const getGasPrice = async () => {
const feeData = await provider.getFeeData();
const gasPrice = feeData.gasPrice?.toString();
return parseInt(Number(gasPrice) * 1.2);
}
/**
* Format wallet address for display
*/
const formatAddress = (address) => {
return address ? `${address.slice(0, 6)}...${address.slice(-4)}` : "";
};
/**
* Format date string for display
*/
const formatDate = (dateString) => {
const date = new Date(dateString);
return date.toLocaleString();
};
/**
* Get human-readable status text for order status
*/
const getStatusText = (status) => {
const statusMap = {
1: 'Pending',
2: 'Active',
3: 'Filled',
4: 'Cancelled',
5: 'Expired'
};
return statusMap[status] || 'Unknown';
};
/**
* Get color for order status display
*/
const getStatusColor = (status) => {
const colorMap = {
1: '#ffc107', // Pending - Yellow
2: '#28a745', // Active - Green
3: '#007bff', // Filled - Blue
4: '#dc3545', // Cancelled - Red
5: '#6c757d' // Expired - Gray
};
return colorMap[status] || '#6c757d';
};
return (
<div className="page">
<h1>Limit Order</h1>
<div className="page-content">
<p>Create and manage limit orders for {inToken.symbol}/{outToken.symbol} trading pair on Base network.</p>
{/* Wallet Connection Section */}
<div className="wallet-section">
{!isWalletConnected ? (
<div className="wallet-notice">
<p>⚠️ Please connect your MetaMask wallet to use Limit Order functionality</p>
<button
className="connect-button"
onClick={connectWallet}
disabled={isLoading}
>
{isLoading ? 'Connecting...' : 'Connect MetaMask'}
</button>
</div>
) : (
<div className="wallet-status">
<p>✅ Connected: {formatAddress(walletAccount)}</p>
<p>🌐 Chain: {supportedChains[currentChainId]?.name || `Chain ${currentChainId}`}</p>
<button
className="disconnect-button"
onClick={disconnectWallet}
>
Disconnect
</button>
</div>
)}
</div>
{/* Limit Order Form */}
<div className="limit-order-form">
<div>
<span>
Trading Pair <i style={{ fontSize: '0.8rem', color: 'red' }}>{chain.chainName}</i>
</span>
<span>{inToken.symbol}/{outToken.symbol}</span>
</div>
{/* Maker and Taker Amount Inputs */}
<div style={{ display: 'flex', flexDirection: 'row', gap: '10px' }}>
<div className="form-group" style={{ width: '50%' }}>
<label htmlFor="makerAmount">Maker Amount ({inToken.symbol})</label>
<input
id="makerAmount"
type="number"
placeholder="Enter maker amount"
value={makerAmount}
onChange={(e) => setMakerAmount(e.target.value)}
disabled={!isWalletConnected}
min="0"
step="0.000001"
/>
</div>
<div className="form-group" style={{ width: '50%' }}>
<label htmlFor="takerAmount">Taker Amount ({outToken.symbol})</label>
<input
id="takerAmount"
type="number"
placeholder="Enter taker amount"
value={takerAmount}
onChange={(e) => setTakerAmount(e.target.value)}
disabled={!isWalletConnected}
min="0"
step="0.000001"
/>
</div>
</div>
{/* Expiration Time Selection */}
<div className="form-group" style={{ marginTop: '10px' }}>
<label htmlFor="expireTime">Time in Force</label>
<select
id="expireTime"
value={expireTime}
onChange={(e) => setExpireTime(e.target.value)}
disabled={!isWalletConnected}
>
{limitOrderExpireOptions.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</div>
{/* Create Order Button */}
<button
className="limit-order-button"
onClick={createLimitOrder}
disabled={!isWalletConnected || !takerAmount || !makerAmount || isLoading}
>
{isLoading ? 'Creating...' :
!isWalletConnected ? 'Connect Wallet to Create Order' :
!takerAmount || !makerAmount ? 'Enter Price and Amount' : 'Create Limit Order'}
</button>
</div>
{/* Orders List Section */}
<div className="orders-section">
<h3>Your Orders</h3>
{orders.length === 0 ? (
<div style={{ textAlign: 'center', padding: '40px', color: '#6c757d' }}>
<p>No orders found</p>
<p style={{ fontSize: '0.9rem', marginTop: '10px' }}>Create your first limit order above</p>
</div>
) : (
orders.map((order) => (
<div key={order.id} className="order-item">
<div className="order-info">
<div className="order-pair">
<strong>Date</strong><br />
{formatDate(order.createDateTime)}
</div>
<div className="order-type">
<strong>Maker</strong><br />
{(order.makerAmount / 10 ** inToken.decimals).toFixed(6)} {inToken.symbol}
</div>
<div className="order-price">
<strong>Taker</strong><br />
{(order.takerAmount / 10 ** outToken.decimals).toFixed(6)} {outToken.symbol}
</div>
<div className="order-amount">
<strong>Price</strong><br />
{((order.takerAmount / 10 ** outToken.decimals) / (order.makerAmount / 10 ** inToken.decimals)).toFixed(6)}
</div>
<div className="order-status" style={{ color: getStatusColor(order.statuses) }}>
<strong>Status</strong><br />
{getStatusText(order.statuses)}
</div>
</div>
<button
onClick={() => cancelOrder(order)}
className="cancel-button"
disabled={order.statuses === 3 || order.statuses === 4}
>
{order.statuses === 3 || order.statuses === 4 ? 'Completed' : 'Cancel'}
</button>
</div>
))
)}
</div>
</div>
</div>
);
};
export default LimitOrder;
Last updated