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:
- Delegation: The user delegates trading authority to a session wallet (1CT Wallet) on-chain via the
OneClickAgentcontract. - Signing: The client signs trade instructions using the session wallet's private key (EIP-712).
- Relaying: The client submits the signed instruction and trade data to this API.
- 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:
{
"chainId": <Current_Chain_ID>,
"verifyingContract": "<OneClickAgent_Contract_Address>"
}- chainId: The integer ID of the network.
- Monad Mainnet:
143 - Monad Testnet:
10143
- Monad Mainnet:
- verifyingContract: The address of the
OneClickAgentcontract 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):
{
"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):
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):
{
"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):
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 thesend-open-position-v2orsend-close-position-v2endpoint.
Response:
{
"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
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
// ... (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.