Skip to content

One-Click Trading API Documentation

This documentation describes how to interact with the One-Click Trading (1CT) service. This service allows for low-latency trade execution by relaying pre-signed EIP-712 messages to the blockchain.

Base URL: https://oneclick-01-keeper.leverup.xyz

Overview

The One-Click Trading flow consists of:

  1. Delegation: The user delegates trading authority to a session wallet (1CT Wallet) on-chain via the OneClickAgent contract.
  2. Signing: The client signs trade instructions using the session wallet's private key (EIP-712).
  3. Relaying: The client submits the signed instruction and trade data to this API.
  4. Execution: The server validates the signature and submits the transaction to the blockchain.

Prerequisites

  • Wallet Connection: The user must have their main wallet connected.
  • Allowlist: Currently, the user must be on the allowlist to access this feature.
  • ERC20 Only: One-Click Trading only supports ERC20 tokens as collateral.
    • If you wish to use native MON, you must first wrap it into WMON.
  • Token Approval: The user must approve the LeverUp contract to spend their ERC20 collateral tokens (e.g., USDC, WMON, LVMON) before attempting to open a position.

Authentication & Security

All trade requests must be signed using EIP-712. The signer address must be authorized as a "Trader Agent" for the user on the OneClickAgent contract.

EIP-712 Domain Separator

All signatures use the following domain separator:

json
{
  "chainId": <Current_Chain_ID>,
  "verifyingContract": "<OneClickAgent_Contract_Address>"
}
  • chainId: The integer ID of the network.
    • Monad Mainnet: 143
    • Monad Testnet: 10143
  • verifyingContract: The address of the OneClickAgent contract on that chain.

Contract Addresses

For contract addresses (OneClickAgent, LeverUp, Tokens), please refer to the Contract Addresses page.

Oracle Update Data

When opening a one-click position through the V2 endpoint, you do not provide Pyth Hermes update data in the request body. Submit the signed trade instruction only; the one-click trading service fetches the required oracle update data through the LeverUp backend before relaying the transaction.

For direct contract calls, see Fetching Oracle Data.


Endpoints

1. Open Position

Submits a request to open a new market position.

Endpoint: POST /v1/trading/send-open-position-v2

Query Parameters:

  • blockchain (required): The target blockchain (e.g., MONAD).

Request Body (application/json):

json
{
  "openData": {
    "pairBase": "0x...",      // Pair base address (string)
    "isLong": true,           // Direction (boolean)
    "tokenIn": "0x...",       // token in address (string)
    "lvToken": "0x...",       // collateral token address(LVUSD/LVMON) (string)
    "amountIn": "1000...",    // Amount in(tokenIn) (wei, as string)
    "qty": "5000...",         // Position size (wei, as string)
    "price": "2000...",       // Execution price (wei, as string)
    "stopLoss": "0",          // Stop loss price (wei, as string)
    "takeProfit": "0",        // Take profit price (wei, as string)
    "broker": "0"             // Broker ID (string)
  },
  "trader": "0x...",          // The actual user's address (string)
  "salt": "0x...",            // Unique 32-byte salt (string)
  "deadline": 1715000000,     // Unix timestamp expiry (integer)
  "signature": "0x..."        // EIP-712 Signature (string)
}

EIP-712 Type Definition (OneClickOpenPosition):

javascript
const types = {
  OneClickOpenPosition: [
    { type: 'address', name: 'pairBase' },
    { type: 'bool', name: 'isLong' },
    { type: 'address', name: 'tokenIn' },
    { type: 'address', name: 'lvToken' },
    { type: 'uint96', name: 'amountIn' },
    { type: 'uint128', name: 'qty' },
    { type: 'uint128', name: 'price' },
    { type: 'uint128', name: 'stopLoss' },
    { type: 'uint128', name: 'takeProfit' },
    { type: 'uint24', name: 'broker' },
    { type: 'address', name: 'trader' },
    { type: 'bytes32', name: 'salt' },
    { type: 'uint128', name: 'deadline' }
  ]
};

2. Close Position

Submits a request to close an existing position.

Endpoint: POST /v1/trading/send-close-position-v2

Query Parameters:

  • blockchain (required): The target blockchain (e.g., MONAD).

Request Body (application/json):

json
{
  "positionHash": "0x...",    // Unique identifier of the position (string)
  "deadline": 1715000000,     // Unix timestamp expiry (integer)
  "signature": "0x..."        // EIP-712 Signature (string)
}

EIP-712 Type Definition (OneClickClosePosition):

javascript
const types = {
  OneClickClosePosition: [
    { type: 'bytes32', name: 'positionHash' },
    { type: 'uint128', name: 'deadline' }
  ]
};

3. Check Execution Status

Polls the status of a submitted trade using the input hash returned from the open/close endpoints.

Endpoint: GET /v1/trading/{input_hash}/status

Path Parameters:

  • input_hash: The hash returned by the send-open-position-v2 or send-close-position-v2 endpoint.

Response:

json
{
  "executed": true,         // Whether the trade has been processed
  "success": true,          // Whether the trade was successful
  "txnHash": "0x...",       // The transaction hash on-chain
  "reason": null            // Error reason if failed (or null)
}

Signing Examples

Generating a Signature (TypeScript / Viem)

Below are complete examples for generating EIP-712 signatures for both Opening and Closing positions using viem.

1. Sign OneClick Open Position

typescript
import { createWalletClient, http, keccak256, stringToHex, toHex } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { monad } from 'viem/chains';

// 1. Setup Client & Account (The Session Key)
// Ideally, this private key is generated locally in the browser and stored securely.
const account = privateKeyToAccount('0xYOUR_SESSION_PRIVATE_KEY'); 

const client = createWalletClient({
  account,
  chain: monad, // Use monadTestnet for Testnet (Chain ID 10143)
  transport: http()
});

// 2. Define Domain
const domain = {
  chainId: 143, // Monad Mainnet ID (Use 10143 for Testnet)
  verifyingContract: '0x1567FB09f13653f63A047B4F11Fb11fD1D7567d0' // Mainnet OneClickAgent
} as const;

// 3. Define Types
const types = {
  OneClickOpenPosition: [
    { name: 'pairBase', type: 'address' },
    { name: 'isLong', type: 'bool' },
    { name: 'tokenIn', type: 'address' },
    { name: 'lvToken', type: 'address' },
    { name: 'amountIn', type: 'uint96' },
    { name: 'qty', type: 'uint128' },
    { name: 'price', type: 'uint128' },
    { name: 'stopLoss', type: 'uint128' },
    { name: 'takeProfit', type: 'uint128' },
    { name: 'broker', type: 'uint24' },
    { name: 'trader', type: 'address' },
    { name: 'salt', type: 'bytes32' },
    { name: 'deadline', type: 'uint128' }
  ]
} as const;

// 4. Prepare Message
const salt = keccak256(stringToHex(`leverup-1ct-open:${Date.now()}`));
const deadline = BigInt(Math.floor(Date.now() / 1000) + 300); // 5 minutes expiry

const message = {
  pairBase: '0x...', // Trading Pair Base Address
  isLong: true,
  tokenIn: '0x...', // Collateral Token Address
  lvToken: '0x...', // Corresponding LV Token
  amountIn: 1000000000n, // 1000 USDC (6 decimals)
  qty: 50000000000n, // Position Size in Base Asset (10 decimals)
  price: 60000000000000000000000n, // Execution Price (18 decimals)
  stopLoss: 0n,
  takeProfit: 0n,
  broker: 0,
  trader: '0xUserRealAddress...', // The user who authorized the session key
  salt: salt,
  deadline: deadline
};

// 5. Sign Data
const signature = await client.signTypedData({
  domain,
  primaryType: 'OneClickOpenPosition',
  types,
  message
});

console.log('Open Position Signature:', signature);

2. Sign OneClick Close Position

typescript
// ... (Client setup same as above) ...

// 3. Define Types for Close
const closeTypes = {
  OneClickClosePosition: [
    { name: 'positionHash', type: 'bytes32' },
    { name: 'deadline', type: 'uint128' }
  ]
} as const;

// 4. Prepare Message
const closeMessage = {
  positionHash: '0x...', // The hash of the position you want to close
  deadline: BigInt(Math.floor(Date.now() / 1000) + 300) // 5 minutes expiry
};

// 5. Sign Data
const closeSignature = await client.signTypedData({
  domain,
  primaryType: 'OneClickClosePosition',
  types: closeTypes,
  message: closeMessage
});

console.log('Close Position Signature:', closeSignature);

Note on Domain Separator

Ensure you use the correct chainId and verifyingContract address for the network you are targeting (Mainnet vs Testnet). The current one-click flow signs with chainId and verifyingContract in the domain separator.