Trading Events
This guide explains how to listen for trading events on the LeverUp protocol, such as opening and closing positions, to track user activity or build notifications.
Event Overview
The LeverUp Diamond contract emits various events during the lifecycle of a trade.
Market Orders
- MarketPendingTrade: Emitted when a user requests to open a market position.
- OpenMarketTrade: Emitted when a market position is successfully opened.
- PendingTradeRefund: Emitted when a pending market order is canceled or fails (e.g., due to slippage).
Limit Orders
- OpenLimitOrder: Emitted when a user places a limit order.
- ExecuteLimitOrderSuccessful: Emitted when a limit order is triggered and executed.
- CancelLimitOrder: Emitted when a user cancels a limit order.
- LimitOrderRefund: Emitted when a limit order execution fails and is refunded.
Closing Positions
- CloseTradeSuccessful: Emitted when a user manually closes a position.
- ExecuteCloseSuccessful: Emitted when a position is closed by a trigger (Take Profit, Stop Loss, or Liquidation).
Event Signatures
To listen for these events, you can use the following ABI definitions.
// Market Order Events
event MarketPendingTrade(address indexed user, bytes32 indexed tradeHash, OpenDataInput trade);
event OpenMarketTrade(address indexed user, bytes32 indexed tradeHash, OpenTrade ot);
event PendingTradeRefund(address indexed user, bytes32 indexed tradeHash, uint8 refundReason);
// Limit Order Events
event OpenLimitOrder(address indexed user, bytes32 indexed orderHash, OpenDataInput data);
event ExecuteLimitOrderSuccessful(address indexed user, bytes32 indexed orderHash);
event CancelLimitOrder(address indexed user, bytes32 indexed orderHash);
// Closing Events
event CloseTradeSuccessful(address indexed user, bytes32 indexed tradeHash, CloseInfo closeInfo);
event ExecuteCloseSuccessful(address indexed user, bytes32 indexed tradeHash, uint8 executionType, CloseInfo closeInfo);Data Structures
The events above reference the following structs and enums.
OpenDataInput
Used in MarketPendingTrade and OpenLimitOrder.
struct OpenDataInput {
address pairBase; // Address of the trading pair
bool isLong; // True for Long, False for Short
address tokenIn; // Collateral token address (e.g., USDC)
address lvToken; // LP Token address (e.g., LVUSD)
uint96 amountIn; // Amount of collateral (tokenIn decimals)
uint128 qty; // Position size in units (1e10 precision)
uint128 price; // Execution price (1e18 precision)
uint128 stopLoss; // Stop Loss price (1e18 precision)
uint128 takeProfit; // Take Profit price (1e18 precision)
uint24 broker; // Broker ID
}OpenTrade
Used in OpenMarketTrade. Represents the state of an active position.
struct OpenTrade {
address user;
uint32 userOpenTradeIndex;
uint40 holdingFeeRate; // Holding fee rate (1e12 precision)
uint128 entryPrice; // Entry price (1e18 precision)
uint128 qty; // Position size (1e10 precision)
address pairBase; // Pair address
address tokenPay; // Token used for payment
address lvToken; // LP Token address
uint96 lvMargin; // Margin in LP Token (lvToken decimals)
uint128 stopLoss; // Stop Loss price
uint128 takeProfit; // Take Profit price
uint24 broker; // Broker ID
bool isLong; // Direction
uint32 timestamp; // Open timestamp
uint96 lvOpenFee; // Open fee in LP Token
uint96 lvExecutionFee; // Execution fee in LP Token
int256 longAccFundingFeePerShare; // Funding fee tracker
uint256 openBlock; // Block number when opened
}CloseInfo
Used in CloseTradeSuccessful and ExecuteCloseSuccessful. Contains details about the closed trade.
struct CloseInfo {
uint128 closePrice; // Closing price (1e18 precision)
int96 fundingFee; // Funding fee paid/received (tokenIn decimals)
uint96 closeFee; // Closing fee (tokenIn decimals)
int96 pnl; // Realized PnL (tokenIn decimals)
uint96 holdingFee; // Holding fee (tokenIn decimals)
}Refund Enum
Used in PendingTradeRefund and LimitOrderRefund to indicate why an order failed.
enum Refund {
NO, // 0
SWITCH, // 1
PAIR_STATUS, // 2
AMOUNT_IN, // 3
USER_PRICE, // 4
MIN_NOTIONAL_USD, // 5
MAX_NOTIONAL_USD, // 6
MAX_LEVERAGE, // 7
TP, // 8
SL, // 9
PAIR_OI, // 10
OPEN_LOST, // 11
SYSTEM, // 12
FEED_DELAY, // 13
PRICE_PROTECTION, // 14
RESERVE_TOKEN_NOT_ACTIVE, // 15
LV_TOKEN_NOT_ACTIVE, // 16
MIN_LEVERAGE // 17
}Example: Listening for Position Updates
Here is an example using viem to listen for OpenMarketTrade and CloseTradeSuccessful events.
import { createPublicClient, http, parseAbi, formatUnits } from 'viem'
// Define Monad Chain
const monad = {
id: 10143, // Monad Testnet ID, update for Mainnet
name: 'Monad',
network: 'monad',
nativeCurrency: { name: 'Monad', symbol: 'MON', decimals: 18 },
rpcUrls: {
default: { http: ['https://rpc.monad.xyz'] },
public: { http: ['https://rpc.monad.xyz'] },
},
}
// Diamond Contract Address
const contractAddress = '0xea1b8E4aB7f14F7dCA68c5B214303B13078FC5ec'
// ABI for the events we want to listen to
const abi = parseAbi([
'event OpenMarketTrade(address indexed user, bytes32 indexed tradeHash, (address user, uint32 userOpenTradeIndex, uint40 holdingFeeRate, uint128 entryPrice, uint128 qty, address pairBase, address tokenPay, address lvToken, uint96 lvMargin, uint128 stopLoss, uint128 takeProfit, uint24 broker, bool isLong, uint32 timestamp, uint96 lvOpenFee, uint96 lvExecutionFee, int256 longAccFundingFeePerShare, uint256 openBlock) ot)',
'event CloseTradeSuccessful(address indexed user, bytes32 indexed tradeHash, (uint128 closePrice, int96 fundingFee, uint96 closeFee, int96 pnl, uint96 holdingFee) closeInfo)'
])
async function watchTradingEvents() {
const client = createPublicClient({
chain: monad,
transport: http()
})
console.log("Listening for trading events...")
// Watch for OpenMarketTrade
client.watchContractEvent({
address: contractAddress,
abi: abi,
eventName: 'OpenMarketTrade',
onLogs: logs => {
logs.forEach(log => {
const { user, tradeHash, ot } = log.args
console.log(`[OPEN] User: ${user}, TradeHash: ${tradeHash}`)
console.log(` Pair: ${ot.pairBase}, Long: ${ot.isLong}, Entry: $${formatUnits(ot.entryPrice, 18)}`)
})
}
})
// Watch for CloseTradeSuccessful
client.watchContractEvent({
address: contractAddress,
abi: abi,
eventName: 'CloseTradeSuccessful',
onLogs: logs => {
logs.forEach(log => {
const { user, tradeHash, closeInfo } = log.args
console.log(`[CLOSE] User: ${user}, TradeHash: ${tradeHash}`)
console.log(` Close Price: $${formatUnits(closeInfo.closePrice, 18)}, PnL: ${formatUnits(closeInfo.pnl, 18)}`) // Adjust decimals for PnL based on token
})
}
})
}
watchTradingEvents();Notes
- Decimals: Prices are typically 1e18 (18 decimals). PnL decimals depend on the collateral token (e.g., USDC is 6 decimals, others might be 18).
- Structs: The event arguments often contain structs (like
OpenTradeandCloseInfo). Inviem'sparseAbi, you need to define these structs inline as tuples.