# Browser Wallet SDK

{% embed url="<https://github.com/openocean-finance/OpenOcean-limit-order>" %}

## How to Install the sdk in your project <a href="#how-to-use-the-sdk-in-your-project" id="how-to-use-the-sdk-in-your-project"></a>

```javascript
npm i @openocean.finance/limitorder-sdk
```

{% hint style="info" %}
DCA and Limit Order are integrated in the same SDK, so their names are the same
{% endhint %}

## How to use the sdk in your project <a href="#how-to-use-the-sdk-in-your-project" id="how-to-use-the-sdk-in-your-project"></a>

```javascript
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`)

```javascript
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:

```javascript
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 v5

```javascript
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)

```javascript
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:

```javascript
const sdkParams = {
  provider,                  // Ethers provider (v5 or v6)
  chainKey: 'base',
  account: address,
  chainId: 8453,
};

```

### Create a Limit Order

```javascript
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

```javascript
await openoceanLimitOrderSdk.cancelLimitOrder(
  sdkParams,
  {
    orderData: order.data,
    gasPrice: parseInt(gasPrice * 1.2),
  }
);

```

### Load Chart (Optional)

```javascript
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

```javascript
[
  { 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:

```javascript
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; 
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://apis.openocean.finance/developer/apis/limit-order-api/browser-wallet-sdk.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
