# Table of Contents - [Intro to Sai | Sai.fun](#intro-to-sai-sai-fun) - [Learn | About Sai | Sai.fun](#learn-about-sai-sai-fun) - [Fees | Sai.fun](#fees-sai-fun) - [FAQ | Sai.fun](#faq-sai-fun) - [Sai Positions (SLP) | Sai.fun](#sai-positions-slp-sai-fun) - [Executing Trades | Sai.fun](#executing-trades-sai-fun) - [Wallet Setup | Sai.fun](#wallet-setup-sai-fun) - [Trading on Sai | Sai.fun](#trading-on-sai-sai-fun) - [Guides | Using Sai | Sai.fun](#guides-using-sai-sai-fun) - [Leverage and Liquidations | Sai.fun](#leverage-and-liquidations-sai-fun) - [Price Impact | Sai.fun](#price-impact-sai-fun) - [Onboarding | Sai.fun](#onboarding-sai-fun) - [Choosing a Market | Sai.fun](#choosing-a-market-sai-fun) - [Blog | Sai.fun](#blog-sai-fun) - [Sai Referral Program | Sai.fun](#sai-referral-program-sai-fun) - [Community | Sai.fun](#community-sai-fun) - [Use Cases | Sai.fun](#use-cases-sai-fun) - [Sai Core | Sai.fun](#sai-core-sai-fun) - [Contract Addresses Reference | Sai.fun](#contract-addresses-reference-sai-fun) - [Sai Keeper | Sai.fun](#sai-keeper-sai-fun) - [Legal | Sai.fun](#legal-sai-fun) - [Perp: State Variables | Sai.fun](#perp-state-variables-sai-fun) - [Perp: Price Impact Module | Sai.fun](#perp-price-impact-module-sai-fun) - [Acquiring Collateral | Sai.fun](#acquiring-collateral-sai-fun) - [Sai Disclosures | Sai.fun](#sai-disclosures-sai-fun) - [WASM Integration Guide | Sai.fun](#wasm-integration-guide-sai-fun) - [Getting Started | Sai.fun](#getting-started-sai-fun) - [Perp Contract | Sai.fun](#perp-contract-sai-fun) - [Fees API | Sai.fun](#fees-api-sai-fun) - [Oracle API | Sai.fun](#oracle-api-sai-fun) - [EVM Integration Guide | Sai.fun](#evm-integration-guide-sai-fun) - [Intro to Sai | Sai.fun](#intro-to-sai-sai-fun) - [Perp: Borrowing Module | Sai.fun](#perp-borrowing-module-sai-fun) - [Perp API | Sai.fun](#perp-api-sai-fun) - [WASM & EVM Integration | Sai.fun](#wasm-evm-integration-sai-fun) - [Examples: Subscriptions | Sai.fun](#examples-subscriptions-sai-fun) - [Client Setup | Sai.fun](#client-setup-sai-fun) - [Filters & Pagination | Sai.fun](#filters-pagination-sai-fun) - [Core Concepts | Sai.fun](#core-concepts-sai-fun) - [Examples: Queries | Sai.fun](#examples-queries-sai-fun) - [Terms of Use | Sai.fun](#terms-of-use-sai-fun) - [Liquidity Provider (LP) API | Sai.fun](#liquidity-provider-lp-api-sai-fun) - [Telegram Bot Integration | Sai.fun](#telegram-bot-integration-sai-fun) --- # Intro to Sai | Sai.fun ![Page cover](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-9b2d1ed166fec46fbd584efde1a20c879894bb6d%252Fsai-docs-banner.png%3Falt%3Dmedia&width=1248&dpr=3&quality=100&sign=b8e59fe7&sv=2) Welcome to **Sai.fun**—a perpetual futures trading platform built on Nibiru. With Sai, you can open leveraged positions (long or short) with low fees and low impact trades, set limit orders, stop-losses and take-profits, or earn yield in a fully onchain trading experience. circle-info Sai is in active development with an expected launch in early 2026. The team has finalized a smart contract audit on the core protocol logic, and the web application is under active development. In the meantime, please [follow Sai on Xarrow-up-right](https://x.com/saidotfun) to help support us. * * * [hashtag](https://docs.sai.fun/#learn-about-sai) Learn About Sai --------------------------------------------------------------------- Master the fundamentals of trading on Sai and understand how the platform works. [](https://docs.sai.fun/learn) **About Sai** Discover how Sai works, its architecture, and what makes it unique in the DeFi trading landscape [](https://docs.sai.fun/learn/trading) **Trading on Sai** Learn the essentials of perpetual futures trading including execution, leverage, fees, and price impact [](https://docs.sai.fun/learn/slp) **Sai Positions (SLP)** Understand Sai Liquidity Provider positions and how to earn yield by providing liquidity to the platform ### [hashtag](https://docs.sai.fun/#trading-fundamentals) Trading Fundamentals [](https://docs.sai.fun/learn/trading/executing-trades) **Executing Trades** Master market orders, limit orders, stop-losses, and take-profit strategies [](https://docs.sai.fun/learn/trading/leverage-and-liquidations) **Leverage and Liquidations** Understand how leverage works and how to manage liquidation risks effectively [](https://docs.sai.fun/learn/trading/fees) **Fees** Learn about trading fees, funding rates, and how to optimize your trading costs [](https://docs.sai.fun/learn/trading/price-impact) **Price Impact** Understand how your trade size affects execution price and how to minimize slippage [](https://docs.sai.fun/learn/trading/faq) **Trading FAQ** Find answers to common questions about trading on Sai * * * [hashtag](https://docs.sai.fun/#user-guides) User Guides ------------------------------------------------------------- Step-by-step walkthroughs to help you get started and maximize your Sai experience. [](https://docs.sai.fun/guides) **Getting Started** Complete collection of guides for onboarding, wallet setup, acquiring collateral, and more [](https://docs.sai.fun/guides/onboarding) **Onboarding** Your first steps to accessing and using the Sai platform [](https://docs.sai.fun/guides/wallet-setup) **Wallet Setup** Learn how to connect your wallet and prepare for trading [](https://docs.sai.fun/guides/referral) **Sai Referral Program** Earn rewards by referring traders and save on trading fees [](https://docs.sai.fun/guides/acquiring-collateral) **Acquiring Collateral** How to obtain the necessary collateral to start trading [](https://docs.sai.fun/guides/choosing-market) **Choosing a Market** Select the right trading market for your strategy * * * [hashtag](https://docs.sai.fun/#for-developers) For Developers ------------------------------------------------------------------- Build on Sai with comprehensive technical documentation, API references, and integration guides. [](https://docs.sai.fun/for-devs/dev) **Getting Started** Begin integrating with Sai - includes EVM and WASM integration guides [](https://docs.sai.fun/for-devs/sai-core) **Sai Core** Deep dive into the core contracts including perp contract, borrowing module, and state variables [](https://docs.sai.fun/for-devs/sai-keeper) **Sai Keeper** Complete GraphQL API documentation for querying positions, liquidity, oracles, fees, and more [](https://docs.sai.fun/for-devs/use-cases) **Use Cases** Practical integration examples including Telegram bot development ### [hashtag](https://docs.sai.fun/#developer-resources) Developer Resources [](https://docs.sai.fun/for-devs/dev/evm-guide) **EVM Integration Guide** Connect to Sai from EVM-compatible chains and applications [](https://docs.sai.fun/for-devs/dev/wasm-guide) **WASM Integration Guide** Integrate with Sai using CosmWasm smart contracts [](https://docs.sai.fun/for-devs/sai-core/contracts) **Contract Addresses** Reference for all deployed contract addresses on mainnet and testnet [](https://docs.sai.fun/for-devs/sai-keeper/api-perp) **Perp API** Query and subscribe to perpetual futures data including positions and markets * * * [hashtag](https://docs.sai.fun/#why-sai) Why Sai? ------------------------------------------------------ * **Oracle settlement**: Simple and easy price behavior so there's no internal surprises. We rely on a combination of Nibiru's Oracle, which aggregates prices from leading exchanges like Binance, Bybit, and OKX through Nibiru's decentralized validator network, and custom feeds for certain markets. * **Leverage**: Amplify your exposure with up to 100x leverage (exact max set by each market and pairs). * **Simple On-Chain Experience**: Manage everything in one intuitive UI, leveraging one click trading, automated order triggering, etc. * **Custom Orders**: Market, limit, stop, and advanced triggers for Take Profits and Stop Losses. Use the sidebar navigation to jump to any topic or type a keyword (e.g. "leverage," "limit order") in the search bar. * * * [hashtag](https://docs.sai.fun/#resources) Resources --------------------------------------------------------- [](https://docs.sai.fun/resources/community) **Community** Join the Sai community on social channels and stay connected [](https://docs.sai.fun/resources/blogs) **Blog** Read the latest updates, announcements, and insights from the Sai team [](https://docs.sai.fun/resources/legal) **Legal** Terms of use, disclosures, and legal documentation * * * [hashtag](https://docs.sai.fun/#stay-connected) Stay Connected ------------------------------------------------------------------- **Follow Sai for the latest updates:** X / Twitter [@SaiDotFunarrow-up-right](https://x.com/SaiDotFun) Telegram News Channel [t.me/saidotfunarrow-up-right](https://t.me/saidotfun) Blog [Sai Blogarrow-up-right](https://docs.sai.fun/blogs) [NextLearn | About Saichevron-right](https://docs.sai.fun/learn) Last updated 6 days ago * [Learn About Sai](https://docs.sai.fun/#learn-about-sai) * [Trading Fundamentals](https://docs.sai.fun/#trading-fundamentals) * [User Guides](https://docs.sai.fun/#user-guides) * [For Developers](https://docs.sai.fun/#for-developers) * [Developer Resources](https://docs.sai.fun/#developer-resources) * [Why Sai?](https://docs.sai.fun/#why-sai) * [Resources](https://docs.sai.fun/#resources) * [Stay Connected](https://docs.sai.fun/#stay-connected) --- # Learn | About Sai | Sai.fun ![Page cover](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252FIwuyYuE2nDVJ49AcVm77%252Fsai-banner-learn-1500x500.png%3Falt%3Dmedia%26token%3Dc8ba6b67-7123-49a7-b82b-fe0df6e5823c&width=1248&dpr=3&quality=100&sign=6e4c5a0d&sv=2) [hashtag](https://docs.sai.fun/learn#what-is-sai) What is Sai? ------------------------------------------------------------------- Sai is a perpetuals exchange powered by Nibiru. It lets anyone trade perpetual futures on a decentralized exchange (DEX) that feels as fast and smooth as a centralized exchange. The goal is simple: give traders a clean, reliable place to get exposure while keeping everything transparent and onchain. [hashtag](https://docs.sai.fun/learn#technical-overview) Technical Overview -------------------------------------------------------------------------------- Sai pairs two building blocks to keep markets fair and responsive: oracles (independent price feeds that track real-world markets) and an automated market maker (AMM) (liquidity you can always trade against instead of waiting for a counterparty). Running on Nibiru provides the performance backbone needed for low-latency, CEX-like trading while retaining self-custody. Liquidity is community directed. Sai Liquidity Positions (SLPs) seed the pools that power every market. Because SLPs choose where to allocate their liquidity, they also have decisive influence over which markets launch and grow on Sai. If SLPs back a market, it can be traded on Sai. Passive capital can earn through the protocol’s highly capital-efficient design, giving users the flexibility to allocate when they want or stay idle, without friction. Sai will list more than just major crypto assets, with a focus on bringing equities, commodities, and real-world assets (RWAs) onchain. The aim is to expand market access while keeping the trading experience simple, fast, and transparent. [hashtag](https://docs.sai.fun/learn#wallet-setup-and-collateral) Wallet Setup & Collateral ------------------------------------------------------------------------------------------------ You can trade on Sai using an EVM-wallet such as MetaMask, Coinbase Wallet, WalletConnect, and Rabby Wallet. EVM-Compatible Wallets MetaMask [https://metamask.io/arrow-up-right](https://metamask.io/) Coinbase Wallet [https://www.coinbase.com/walletarrow-up-right](https://www.coinbase.com/wallet) WalletConnect [https://walletguide.walletconnect.network/arrow-up-right](https://walletguide.walletconnect.network/) Rabby [https://rabby.io/arrow-up-right](https://rabby.io/) **Collateral:** Sai accepts USDC from any EVM-chain as well as stNIBI (Nibiru) * Bridged USDC Address (Stargate): 0x0829F361A05D993d5CEb035cA6DF3446b060970b * Transactions on Sai require NIBI for gas (**WNIBI**: 0x0CaCF669f8446BeCA826913a3c6B96aCD4b02a97). You can obtain WNIBI using the following options: * [Stargatearrow-up-right](https://stargate.finance/?srcChain=ethereum&srcToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE&dstChain=nibiru&dstToken=0xcdA5b77E2E2268D9E09c874c1b9A4c3F07b37555) (Powered by LayerZero) * [Hyperlanearrow-up-right](https://nexus.hyperlane.xyz/?token=0xa582e9e96F5D58A1202ad216E89926283a5dD056&destination=nibiru) (Nexus Bridge) **If you’re transferring from a Centralized Exchange (CEX):** Purchase ETH, USDC, or NIBI on a CEX, then send the funds to a self-custodied wallet (e.g., Rabby, MetaMask) where you control the private keys. You can then use Stargate or Hyperlane to bridge the funds to Nibiru EVM. _Sai will soon offer a gasless experience, soon users will not need gas to sign or run transactions on Sai._ [fireTrading on Saichevron-right](https://docs.sai.fun/learn/trading) [fireSai Positions (SLP)chevron-right](https://docs.sai.fun/learn/slp) [hashtag](https://docs.sai.fun/learn#user-guides) User Guides ------------------------------------------------------------------ [fireGuides | Using Saichevron-right](https://docs.sai.fun/guides) [hashtag](https://docs.sai.fun/learn#why-build-on-sai) Why Build on Sai? ----------------------------------------------------------------------------- Sai gives builders easy to use APIs and real time streams for prices, positions, and funding, so you can ship quickly. Multiple SDKs in common languages let your team work in JavaScript or TypeScript, Python, and more. Because Sai is EVM compatible, you can reuse familiar Solidity tooling, connect standard wallets, and compose with other EVM apps without custom logic. Nibiru provides a high performance base layer. Transactions confirm quickly, liquidations execute promptly, and oracle reads are responsive. Oracles are available out of the box to settle PnL, manage risk, and inform any app that needs reliable market data. Liquidity is community directed through SLPs, or Sai Liquidity Positions. Sai features strong bridge partners such as LayerZero, Stargate, and Hyperlane, making it simple to bring assets in, reach users across chains, and onboard liquidity. [codeGetting Startedchevron-right](https://docs.sai.fun/for-devs/dev) * * * [hashtag](https://docs.sai.fun/learn#sai-updates) Sai Updates ------------------------------------------------------------------ **Stay up to date on Sai’s socials:** * X: [https://x.com/saidotfunarrow-up-right](https://x.com/saidotfun) * Telegram: [https://t.me/saidotfunarrow-up-right](https://t.me/saidotfun) * [Sai Blogarrow-up-right](https://docs.sai.fun/blogs) [PreviousIntro to Saichevron-left](https://docs.sai.fun/) [NextTrading on Saichevron-right](https://docs.sai.fun/learn/trading) Last updated 2 months ago * [What is Sai?](https://docs.sai.fun/learn#what-is-sai) * [Technical Overview](https://docs.sai.fun/learn#technical-overview) * [Wallet Setup & Collateral](https://docs.sai.fun/learn#wallet-setup-and-collateral) * [User Guides](https://docs.sai.fun/learn#user-guides) * [Why Build on Sai?](https://docs.sai.fun/learn#why-build-on-sai) * [Sai Updates](https://docs.sai.fun/learn#sai-updates) --- # Fees | Sai.fun The **Fees Module** is a core component of the perpetual futures trading system. It calculates fees for opening and closing trades, manages fee tiers, implements a referral system, and distributes fees to various parties. [hashtag](https://docs.sai.fun/learn/trading/fees#key-concepts) Key Concepts --------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/learn/trading/fees#fee-structure) Fee Structure * **Base Fees:** Every trade has base percentages for opening, closing, and conditional (trigger) orders. * **Fee Multipliers:** The base fees are adjusted by two multipliers: 1. **Tier-based:** High-volume traders earn points, which unlock a lower fee multiplier. 2. **Referral:** Referred traders receive a discount multiplier. * **Effective Multiplier:** The lowest of the applicable multipliers is used to calculate the final fee. * **Liquidation Fees:** These are a fixed percentage of the collateral and are not subject to the multipliers. [hashtag](https://docs.sai.fun/learn/trading/fees#trading-fee-schedule) Trading Fee Schedule ------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/learn/trading/fees#standard-trading-fees) Standard Trading Fees Standard trading fees are calculated based on your total **Position Size** (Leverage × Collateral), not just your wallet balance. Fee Type Rate Description Open Fee 0.10% (10 bps) Charged on the total position size when you enter a trade. Close Fee 0.10% (10 bps) Charged on the total position size when you exit a trade. Trigger Fee 0.01% (1 bp) Charged only on automated orders (Limit, Stop Loss, Take Profit) to pay for the execution cost. ### [hashtag](https://docs.sai.fun/learn/trading/fees#fee-distribution) Fee Distribution * **The Vault (LPs):** Receives 25% of all Closing Fees to support platform stability and reward Liquidity Providers for their risk. * **The Protocol (Gov):** Receives the remaining 75% of Closing Fees and 100% of Open Fees. A portion of these fees is allocated to a pool for governance stakers. * **Referrers:** Earn a percentage of the fees generated by their referees from the governance staker portion. * **Keepers (Bots):** Receive 20% of the Trigger Fee for executing automated orders (Limit, Stop Loss, Take Profit); the remaining 80% goes to the Protocol. [hashtag](https://docs.sai.fun/learn/trading/fees#fee-lifecycle) Fee Lifecycle ----------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/learn/trading/fees#id-1.-opening-a-trade) 1\. Opening a Trade When a user opens a trade, the system performs the following steps to calculate and process fees: 1. **Determine Multiplier:** It fetches the trader's fee tier and checks for a referral discount to find the **effective fee multiplier**. 2. **Calculate Fees:** The system calculates the adjusted open and trigger fees by applying the effective multiplier to the base fees. 3. **Deduct and Distribute:** The total fee is deducted from the trader's collateral and distributed. A portion goes to the trigger service provider (if applicable), a cut is allocated to the referrer, and the net amount is added to a pool for governance stakers. ### [hashtag](https://docs.sai.fun/learn/trading/fees#id-2.-closing-a-trade) 2\. Closing a Trade When a user closes a trade, fees are handled differently depending on whether it's a normal closure or a liquidation. * **Normal Closure:** The system determines the effective multiplier, calculates adjusted close fees, and distributes them. * **Liquidation:** The fee is a fixed percentage of the trader's collateral, bypassing any fee tier or referral discounts. This fee is then split between the vault and governance stakers. [hashtag](https://docs.sai.fun/learn/trading/fees#liquidation-mechanics) Liquidation Mechanics --------------------------------------------------------------------------------------------------- Liquidation occurs when your collateral drops too low to support your position size. The protocol must force-close the trade to prevent bad debt. ### [hashtag](https://docs.sai.fun/learn/trading/fees#key-difference-collateral-vs.-position) Key Difference: Collateral vs. Position Unlike standard trading fees (which are based on position size), Liquidation Fees are calculated based on your **Remaining Collateral**. ### [hashtag](https://docs.sai.fun/learn/trading/fees#the-liquidation-penalty) The Liquidation Penalty If you are liquidated, the protocol seizes your remaining collateral. From this collateral, a **10% penalty** is deducted and distributed as follows: 1. **Closing Component (5% of Collateral):** * Covers the cost of the vault closing the position. * Split: 20% goes to the Vault; 80% goes to the Protocol. 2. **Trigger Component (5% of Collateral):** * Rewards the entity that flagged and executed the liquidation. * Split: 20% goes to the Liquidator; 80% goes to the Protocol. ### [hashtag](https://docs.sai.fun/learn/trading/fees#priority-of-payments-the-waterfall) Priority of Payments (The "Waterfall") If a user is liquidated and their collateral is nearly empty (insufficient to cover the full fees), the smart contract pays out in a specific priority order to protect the system: 1. **First Priority → The Vault:** The Vault gets its share first to ensure Liquidity Providers are protected. 2. **Second Priority → The Liquidator:** The bot gets its reward next. 3. **Third Priority → The Protocol:** The Government receives whatever is left. ### [hashtag](https://docs.sai.fun/learn/trading/fees#liquidation-example) Liquidation Example You are liquidated while you have $100 of collateral remaining. * **Total Penalty Calculation:** $10.00 (10% of $100). * **Distribution:** * $1.00 goes to the Vault (20% of the Closing portion). * $1.00 goes to the Liquidator (20% of the Trigger portion). * $8.00 goes to the Protocol. * **Outcome:** The fees are paid, and the remaining collateral ($90) is seized by the protocol (added to government fees/insurance). Your collateral is zeroed. [hashtag](https://docs.sai.fun/learn/trading/fees#insufficient-collateral) Insufficient Collateral ------------------------------------------------------------------------------------------------------- If a trader's collateral is not sufficient to cover fees and losses, the fees are still accounted for in the internal ledgers (e.g., for governance and referrers) but are not physically transferred from the trader's collateral. The losses are absorbed by the Protocol Vault. [PreviousLeverage and Liquidationschevron-left](https://docs.sai.fun/learn/trading/leverage-and-liquidations) [NextPrice Impactchevron-right](https://docs.sai.fun/learn/trading/price-impact) Last updated 1 month ago * [Key Concepts](https://docs.sai.fun/learn/trading/fees#key-concepts) * [Fee Structure](https://docs.sai.fun/learn/trading/fees#fee-structure) * [Trading Fee Schedule](https://docs.sai.fun/learn/trading/fees#trading-fee-schedule) * [Standard Trading Fees](https://docs.sai.fun/learn/trading/fees#standard-trading-fees) * [Fee Distribution](https://docs.sai.fun/learn/trading/fees#fee-distribution) * [Fee Lifecycle](https://docs.sai.fun/learn/trading/fees#fee-lifecycle) * [1\. Opening a Trade](https://docs.sai.fun/learn/trading/fees#id-1.-opening-a-trade) * [2\. Closing a Trade](https://docs.sai.fun/learn/trading/fees#id-2.-closing-a-trade) * [Liquidation Mechanics](https://docs.sai.fun/learn/trading/fees#liquidation-mechanics) * [Key Difference: Collateral vs. Position](https://docs.sai.fun/learn/trading/fees#key-difference-collateral-vs.-position) * [The Liquidation Penalty](https://docs.sai.fun/learn/trading/fees#the-liquidation-penalty) * [Priority of Payments (The "Waterfall")](https://docs.sai.fun/learn/trading/fees#priority-of-payments-the-waterfall) * [Liquidation Example](https://docs.sai.fun/learn/trading/fees#liquidation-example) * [Insufficient Collateral](https://docs.sai.fun/learn/trading/fees#insufficient-collateral) --- # FAQ | Sai.fun **Q1: How do I partially close a position?** A: Use **Decrease Position Size**. You specify how much collateral or leverage to reduce. The rest remains open. **Q2: Why didn’t my limit order trigger at the exact price I set?** A: Slippage and real-time Oracle updates can cause execution at a slightly worse or better price. Also, an external keeper must execute the trigger. **Q3: Who triggers my Stop Loss or Take Profit?** A: Anyone can call “TriggerTrade.” Typically, bots or keepers monitor the price and do it for small “trigger fees.” **Q4: How do Borrowing Fees get charged?** A: Borrowing fees accrue while your trade is open and are subtracted when you close the position or reduce its size. **Q5: I see a ‘Close Only’ or ‘Paused’ mode. What does that mean?** A: The protocol can temporarily restrict new positions (Close Only) or fully pause trading in emergencies. You can still close existing trades in Close Only mode. [PreviousPrice Impactchevron-left](https://docs.sai.fun/learn/trading/price-impact) [NextSai Positions (SLP)chevron-right](https://docs.sai.fun/learn/slp) Last updated 1 year ago --- # Sai Positions (SLP) | Sai.fun [hashtag](https://docs.sai.fun/learn/slp#slp-vault-mechanics) SLP Vault Mechanics -------------------------------------------------------------------------------------- Sai employs a single-collaeral model, where each SLP vault supports liquidity for a specific **deposit asset**. By pooling liquidity in this way, Sai helps ensure efficient capital utilization and deep market liquidity, benefitting both traders and liquidity providers. For instance, markets like BTC:USD, ETH:USD, and SOL:USD that all have USDC as the deposit asset share the same SLP vault, a vault for USDC, while markets that have a deposit asset like stNIBI would have a separate SLP vault. **SLP shares** represent your portion of the liquidity in a vault. These are regular, fungible tokens on Nibiru that can be redeemed at any time for your proportional share of the vault's assets and accrued fees. [hashtag](https://docs.sai.fun/learn/slp#risks) Risks ---------------------------------------------------------- **Price Delta Risk**: SLPs are the counterparty to trades on each market. When traders profit, their winnings are received from Sai Liquidity Providers (SLPs). And when traders incur losses, the losses are sent to SLPs. **Smart Contract Risk**: Collateral coming from bridged tokens has an inherent risk depending on the security of the bridge. Similarly, liquid staking tokens come from external smart contract applications. While this has not been any reason for concern thus far, we include it here for completeness. [PreviousFAQchevron-left](https://docs.sai.fun/learn/trading/faq) [NextGuides | Using Saichevron-right](https://docs.sai.fun/guides) Last updated 2 months ago * [SLP Vault Mechanics](https://docs.sai.fun/learn/slp#slp-vault-mechanics) * [Risks](https://docs.sai.fun/learn/slp#risks) --- # Executing Trades | Sai.fun [hashtag](https://docs.sai.fun/learn/trading/executing-trades#order-types) Order types ------------------------------------------------------------------------------------------- Order Type Description **Market Order** Executes at the current market price (from the oracle + potential slippage). It’s the fastest way to enter a position. **Limit Order** Executes only if the market crosses a specific “better” price. Example: “Go long at 50k” (the price is 51k now, so if it dips to 50k, it triggers a buy). **Stop Order** Executes if the market crosses a specific “worse” price. Example: "Go long at 70k” (the price is 60k now; you want to catch momentum going upward). Trigger Order (Stop Loss / Take Profit) Separate from entry orders—these are conditions to **close** a position automatically if price hits a threshold. A network “keeper” must send the final transaction to trigger your order. > **Tip**: Market, Limit, and Stop are used for **opening** a position. A Stop Loss or Take Profit is used for **closing** a position once conditions are met. [hashtag](https://docs.sai.fun/learn/trading/executing-trades#open-a-position) Open a Position --------------------------------------------------------------------------------------------------- 1. **Choose Market & Collateral** * E.g., BTC/USD with “USDC” as collateral. 2. **Select a side** * E.g., long/short 3. **Select Leverage** * Type your desired multiple (1x–100x, subject to max). 4. **Enter Order Type** * Market or set a limit/stop price. 5. **Confirm** * The contract checks your collateral, calculates fees, and _if valid_, opens your position. **Step-by-Step Example (Market Order, Long)** 1. **Collateral**: 500 USDC (You deposit 500 tokens into the vault). 2. **Leverage**: 5x => Position Notional = 2,500 UUSD. 3. **Click “Open Long”** * The protocol takes your 500 collateral, checks everything, and opens a 2,500 notional BTC/USD position at the current price (minus any slippage). 4. **Fees**: Suppose 0.1% open fee => 2.5 USDC is deducted from your margin. [hashtag](https://docs.sai.fun/learn/trading/executing-trades#close-a-position) Close a Position ----------------------------------------------------------------------------------------------------- You can fully or partially close. * **Market Close**: Sells (or buys back) instantly at the current price. * **Partial Close**: Specify how much leverage/collateral to remove. * **Trigger Close** (Stop Loss / Take Profit): If price crosses your chosen threshold, a keeper can finalize the close. **Closing Fees** apply, so plan for that. If your trade was profitable, leftover profits are credited from the vault to your wallet. If the trade was a loss, your collateral will be reduced accordingly. [hashtag](https://docs.sai.fun/learn/trading/executing-trades#stop-loss-and-take-profit) Stop Loss & Take Profit --------------------------------------------------------------------------------------------------------------------- After opening a trade, you may set or update: * **SL**: The price to close if the market moves against you. * **TP**: The price to close if the market moves in your favor. Once triggered, an external call to “TriggerTrade” finalizes it. If no one triggers it, the position remains open. [PreviousTrading on Saichevron-left](https://docs.sai.fun/learn/trading) [NextLeverage and Liquidationschevron-right](https://docs.sai.fun/learn/trading/leverage-and-liquidations) Last updated 3 months ago * [Order types](https://docs.sai.fun/learn/trading/executing-trades#order-types) * [Open a Position](https://docs.sai.fun/learn/trading/executing-trades#open-a-position) * [Close a Position](https://docs.sai.fun/learn/trading/executing-trades#close-a-position) * [Stop Loss & Take Profit](https://docs.sai.fun/learn/trading/executing-trades#stop-loss-and-take-profit) --- # Wallet Setup | Sai.fun [hashtag](https://docs.sai.fun/guides/wallet-setup#evm-compatible-wallets) EVM-Compatible Wallets ------------------------------------------------------------------------------------------------------ For trading on Sai EVM, use: Wallet Link MetaMask [https://metamask.io/arrow-up-right](https://metamask.io/) Coinbase Wallet [https://www.coinbase.com/walletarrow-up-right](https://www.coinbase.com/wallet) WalletConnect [https://walletguide.walletconnect.network/arrow-up-right](https://walletguide.walletconnect.network/) Rabby [https://rabby.io/arrow-up-right](https://rabby.io/) [hashtag](https://docs.sai.fun/guides/wallet-setup#wasmvm-compatible-wallets) WasmVM-Compatible Wallets ------------------------------------------------------------------------------------------------------------ For trading on Nibiru's native chain, use: Wallet Link Keplr [https://www.keplr.app/arrow-up-right](https://www.keplr.app/) Leap [https://www.leapwallet.io/arrow-up-right](https://www.leapwallet.io/) Cosmostation [https://www.cosmostation.io/arrow-up-right](https://www.cosmostation.io/) [hashtag](https://docs.sai.fun/guides/wallet-setup#network-configuration) Network Configuration ---------------------------------------------------------------------------------------------------- Ensure your wallet is configured for [Nibiruarrow-up-right](https://nibiru.fi/docs/wallets/) . [PreviousOnboardingchevron-left](https://docs.sai.fun/guides/onboarding) [NextAcquiring Collateralchevron-right](https://docs.sai.fun/guides/acquiring-collateral) Last updated 3 months ago * [EVM-Compatible Wallets](https://docs.sai.fun/guides/wallet-setup#evm-compatible-wallets) * [WasmVM-Compatible Wallets](https://docs.sai.fun/guides/wallet-setup#wasmvm-compatible-wallets) * [Network Configuration](https://docs.sai.fun/guides/wallet-setup#network-configuration) --- # Trading on Sai | Sai.fun ![Page cover](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252FprnqN699DQnCtI51ocd6%252Fsai-banner-learn-trading-1500x500.png%3Falt%3Dmedia%26token%3Ddec307c9-9303-4eed-bca5-fb84c3eb845c&width=1248&dpr=3&quality=100&sign=7a8ace8f&sv=2) [up-right-from-squareExecuting Tradeschevron-right](https://docs.sai.fun/learn/trading/executing-trades) [arrow-up-9-1Leverage and Liquidationschevron-right](https://docs.sai.fun/learn/trading/leverage-and-liquidations) [barcode-scanPrice Impactchevron-right](https://docs.sai.fun/learn/trading/price-impact) [comment-dollarFeeschevron-right](https://docs.sai.fun/learn/trading/fees) [message-questionFAQchevron-right](https://docs.sai.fun/learn/trading/faq) [PreviousLearn | About Saichevron-left](https://docs.sai.fun/learn) [NextExecuting Tradeschevron-right](https://docs.sai.fun/learn/trading/executing-trades) Last updated 1 year ago --- # Guides | Using Sai | Sai.fun ![Page cover](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252FYyFLT3iIOqFGDN2JP9u9%252Fsai-banner-guides-1500x500.png%3Falt%3Dmedia%26token%3Da7262a89-4e22-4bf9-aa82-27b06588f2ae&width=1248&dpr=3&quality=100&sign=3ad5d264&sv=2) Sai is a very young protocol, and the web application is currently under active development. We are finalizing a smart contract audit of the core protocol code with plans to launch in early 2025. In the meantime, please [follow Sai on Xarrow-up-right](https://x.com/saidotfun) to help support us. [hashtag](https://docs.sai.fun/guides#available-guides) Available Guides ----------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/guides#getting-started) Getting Started [](https://docs.sai.fun/guides/onboarding) **Onboarding** Get started with Sai - your complete guide to setting up and accessing the platform for the first time [](https://docs.sai.fun/guides/wallet-setup) **Wallet Setup** Learn how to set up and connect your wallet to start trading on Sai [](https://docs.sai.fun/guides/acquiring-collateral) **Acquiring Collateral** Discover how to obtain the necessary collateral to begin trading on the platform ### [hashtag](https://docs.sai.fun/guides#trading-essentials) Trading Essentials [](https://docs.sai.fun/guides/choosing-market) **Choosing a Market** Understand how to select the right trading market based on your strategy and risk tolerance [](https://docs.sai.fun/guides/referral) **Sai Referral** Earn rewards and save on fees with Sai's referral program - invite traders and receive commissions [PreviousSai Positions (SLP)chevron-left](https://docs.sai.fun/learn/slp) [NextOnboardingchevron-right](https://docs.sai.fun/guides/onboarding) Last updated 6 days ago * [Available Guides](https://docs.sai.fun/guides#available-guides) * [Getting Started](https://docs.sai.fun/guides#getting-started) * [Trading Essentials](https://docs.sai.fun/guides#trading-essentials) --- # Leverage and Liquidations | Sai.fun > **Tip:** If you’re new to margin trading, start with lower leverage. Watch your margin ratio closely and consider using stop-loss orders to manage downside risk. * * * [hashtag](https://docs.sai.fun/learn/trading/leverage-and-liquidations#leverage-overview) Leverage Overview ---------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/learn/trading/leverage-and-liquidations#what-is-leverage) What is Leverage? Leverage is the ratio of your _position size_ to the _collateral_ you deposit. * For example, depositing $100 to open a $500 position is `$500 / $100 = 5x leverage`. **Key Points** * **Higher Leverage = Higher Risk**: Even small price changes can cause large profits or losses. * **Borrowing Fees**: With larger positions, borrowing fees accumulate faster. * **Liquidation Threshold**: Higher leverage reduces the price cushion before forced liquidation. **How to Set Leverage** 1. **Open a Trade**: Select your desired leverage (e.g., 3x, 5x, 100x) in the Sai UI. 2. **Review Position Size**: Confirm the total notional value of your trade. 3. **Check Liquidation Price**: The UI estimates your liquidation price; higher leverage brings the liquidation price closer to the current market price. 4. **Update Leverage Later**: By adding or removing collateral, you can increase or decrease your effective leverage. > **Note**: Every increase in leverage comes with greater exposure to market swings and potentially higher borrowing fees. ### [hashtag](https://docs.sai.fun/learn/trading/leverage-and-liquidations#leverage-examples) Leverage Examples * **Example A (High Leverage)** * Collateral: $200 * Leverage: 5x → Position Size = $1,000 * A 10% price rise yields roughly a $100 profit (50% gain on collateral). A 10% price drop results in a $100 loss (50% loss on collateral). * **Example B (Lower Leverage, More Cushion)** * Collateral: $200 * Leverage: 2x → Position Size = $400 * A 10% price drop is a $40 loss (20% of your collateral). Lower leverage means more room before liquidation. ### [hashtag](https://docs.sai.fun/learn/trading/leverage-and-liquidations#adjusting-leverage-mid-position) Adjusting Leverage Mid-Position * **Add Collateral**: Lowers your effective leverage, providing a bigger buffer against liquidation. * **Remove Collateral**: Increases leverage, exposing you to higher risk if the market continues to move against you. * * * [hashtag](https://docs.sai.fun/learn/trading/leverage-and-liquidations#liquidation-mechanics) Liquidation Mechanics ------------------------------------------------------------------------------------------------------------------------ ### [hashtag](https://docs.sai.fun/learn/trading/leverage-and-liquidations#what-is-liquidation) What is Liquidation? A **liquidation** automatically closes your position when your collateral can’t cover potential losses. This protects both your remaining capital (if any) and the protocol from a negative balance. ### [hashtag](https://docs.sai.fun/learn/trading/leverage-and-liquidations#liquidation-threshold) Liquidation Threshold SAI uses a _maintenance margin_ requirement, typically a small portion of your initial margin (e.g., 10%). * If your total equity (`PnL + Collateral`) falls below this maintenance margin, your position becomes eligible for liquidation. * The SAI interface normally displays a **liquidation price** to show where you’d be at risk of forced closure. **Example**: * $100 collateral at 5x leverage = $500 position. If maintenance margin is $10, your position can only lose $90 before liquidation is triggered. A price drop beyond that threshold triggers forced closure. ### [hashtag](https://docs.sai.fun/learn/trading/leverage-and-liquidations#the-liquidation-process) The Liquidation Process 1. **Trigger**: If your margin ratio dips below the safe threshold, a keeper (liquidator) can call `Trigger Trade` to forcibly close your position at the current market price. 2. **Close the Position**: The system sells (or covers) your position. 3. **Distribute Remaining Collateral**: After covering losses and fees, any leftover collateral is returned to you. If losses exceed your collateral, you lose most or all of your margin. ### [hashtag](https://docs.sai.fun/learn/trading/leverage-and-liquidations#avoiding-liquidations) Avoiding Liquidations 1. **Use Lower Leverage**: Lower leverage gives you a wider price cushion. 2. **Add Collateral**: If the market moves against you, extra funds reduce your liquidation risk. 3. **Set a Stop-Loss**: Stop-loss orders let you exit earlier, often preventing liquidation and limiting losses. 4. **Monitor Borrowing Fees**: Accumulated fees can reduce your available margin and move you closer to liquidation. * * * [hashtag](https://docs.sai.fun/learn/trading/leverage-and-liquidations#pro-tips) Pro Tips ---------------------------------------------------------------------------------------------- 1. **Mind Volatility**: High volatility can cause rapid price swings—be extra cautious with high leverage. 2. **Watch Borrowing Fees**: Fees accumulate over time, gradually eating into your collateral. 3. **Act Preemptively**: If you see your margin ratio dropping, adding collateral or closing part of the position can prevent a forced liquidation. 4. **Track the “Est. Liquidation Price”**: If the market price moves close to this value, you’re at high risk. * * * ### [hashtag](https://docs.sai.fun/learn/trading/leverage-and-liquidations#id-5.-faq) 5\. FAQ **Q: What is the maximum leverage I can use in SAI?** A: Each market (e.g., BTC-PERP, ETH-PERP) has its own max leverage, displayed in the UI. Values like 10x, 20x, or 50x depend on that market’s risk parameters. **Q: How do I know if I’m near liquidation?** A: Check your margin ratio and the displayed liquidation price in the position details. If the market price is near or past the liquidation price, a forced close can happen any moment. * * * ### [hashtag](https://docs.sai.fun/learn/trading/leverage-and-liquidations#id-6.-next-steps) 6\. Next Steps 1. **Try a Test Market**: Practice opening a small leveraged trade to observe how price changes affect your margin and PnL. 2. **Read About Order Types**: Learn about limit orders, stop-losses, or advanced order types if SAI supports them, to manage risk. 3. **Join the Community**: Have more questions? Need live support? Join our Discord to connect with other traders and the SAI team. > **Trade responsibly, and best of luck with Sai!** [PreviousExecuting Tradeschevron-left](https://docs.sai.fun/learn/trading/executing-trades) [NextFeeschevron-right](https://docs.sai.fun/learn/trading/fees) Last updated 1 year ago * [Leverage Overview](https://docs.sai.fun/learn/trading/leverage-and-liquidations#leverage-overview) * [What is Leverage?](https://docs.sai.fun/learn/trading/leverage-and-liquidations#what-is-leverage) * [Leverage Examples](https://docs.sai.fun/learn/trading/leverage-and-liquidations#leverage-examples) * [Adjusting Leverage Mid-Position](https://docs.sai.fun/learn/trading/leverage-and-liquidations#adjusting-leverage-mid-position) * [Liquidation Mechanics](https://docs.sai.fun/learn/trading/leverage-and-liquidations#liquidation-mechanics) * [What is Liquidation?](https://docs.sai.fun/learn/trading/leverage-and-liquidations#what-is-liquidation) * [Liquidation Threshold](https://docs.sai.fun/learn/trading/leverage-and-liquidations#liquidation-threshold) * [The Liquidation Process](https://docs.sai.fun/learn/trading/leverage-and-liquidations#the-liquidation-process) * [Avoiding Liquidations](https://docs.sai.fun/learn/trading/leverage-and-liquidations#avoiding-liquidations) * [Pro Tips](https://docs.sai.fun/learn/trading/leverage-and-liquidations#pro-tips) * [5\. FAQ](https://docs.sai.fun/learn/trading/leverage-and-liquidations#id-5.-faq) * [6\. Next Steps](https://docs.sai.fun/learn/trading/leverage-and-liquidations#id-6.-next-steps) --- # Price Impact | Sai.fun ### [hashtag](https://docs.sai.fun/learn/trading/price-impact#overview) Overview The Price Impact Module is a key component of Sai, responsible for calculating and applying price impact to trades based on their size and market depth. It ensures that larger trades have a more significant effect on the execution price, reflecting real-world market dynamics and promoting fairness in the trading system. It is applied on all position open. ### [hashtag](https://docs.sai.fun/learn/trading/price-impact#key-features) Key Features 1. **Dynamic Price Impact Calculation:** Computes price impact based on trade size and market depth. 2. **Open Interest Tracking:** Maintains a record of open interest across multiple time windows. 3. **Market Depth Management:** Allows configuration of market depth parameters for each trading pair. 4. **Time-Weighted Open Interest:** Uses a time-windowed approach to calculate cumulative open interest. 5. **Separate Handling for Long and Short Positions:** Calculates price impact differently for long and short trades. ### [hashtag](https://docs.sai.fun/learn/trading/price-impact#price-impact-calculation) Price Impact Calculation The price impact is calculated using the following general formula: Copy Price Impact = (Start OI + Trade Size / 2) / One Percent Depth Where: * **Start OI:** The starting open interest for the relevant direction (long/short) * **Trade Size:** The size of the trade being executed * **One Percent Depth:** The amount of trading volume required to move the price by 1% #### [hashtag](https://docs.sai.fun/learn/trading/price-impact#example) Example Let's consider a long trade with the following parameters: * Open Price: $1000 * Trade Size: $100,000 * Current Open Interest: $500,000 * One Percent Depth: $1,000,000 1. Calculate price impact percentage: 2. Calculate price impact: 3. Apply price impact: [PreviousFeeschevron-left](https://docs.sai.fun/learn/trading/fees) [NextFAQchevron-right](https://docs.sai.fun/learn/trading/faq) Last updated 1 year ago * [Overview](https://docs.sai.fun/learn/trading/price-impact#overview) * [Key Features](https://docs.sai.fun/learn/trading/price-impact#key-features) * [Price Impact Calculation](https://docs.sai.fun/learn/trading/price-impact#price-impact-calculation) Copy Price Impact % = (500,000 + 100,000 / 2) / 1,000,000 / 100 = 0.55% Copy Price Impact = $1000 * 0.55% = $5.50 Copy Price After Impact = $1000 + $5.50 = $1005.50 --- # Onboarding | Sai.fun Welcome to Sai! This guide will walk you through the essential steps to start trading. [hashtag](https://docs.sai.fun/guides/onboarding#prerequisites) Prerequisites ---------------------------------------------------------------------------------- Before you begin, make sure you have: * A supported wallet (see [Wallet Setup](https://docs.sai.fun/guides/wallet-setup) ) * Collateral tokens (see [Acquiring Collateral](https://docs.sai.fun/guides/acquiring-collateral) ) * Gas tokens for transactions [hashtag](https://docs.sai.fun/guides/onboarding#quick-start) Quick Start ------------------------------------------------------------------------------ 1. **Connect Your Wallet** → [Wallet Setup](https://docs.sai.fun/guides/wallet-setup) * Set up your EVM or WasmVM compatible wallet * Configure your network for Nibiru 2. **Get Collateral** → [Acquiring Collateral](https://docs.sai.fun/guides/acquiring-collateral) * Learn how to bridge or purchase collateral tokens * Understand valid collateral types 3. **Choose a Market** → [Choosing a Market](https://docs.sai.fun/guides/choosing-market) * Explore available trading pairs * Understand oracle pricing 4. **Understand Leverage** → [Leverage & Risk Managementarrow-up-right](https://github.com/NibiruChain/sai-docs/blob/main/trading/leverage-and-liquidations.md) * Learn how collateral and leverage work * Understand liquidation mechanics [hashtag](https://docs.sai.fun/guides/onboarding#need-help) Need Help? --------------------------------------------------------------------------- * Having wallet issues? See [Wallet Setup](https://docs.sai.fun/guides/wallet-setup) * Can't bridge assets? Check [Acquiring Collateral](https://docs.sai.fun/guides/acquiring-collateral) * Questions about trading? Visit [FAQ](https://docs.sai.fun/learn/trading/faq) [PreviousGuides | Using Saichevron-left](https://docs.sai.fun/guides) [NextWallet Setupchevron-right](https://docs.sai.fun/guides/wallet-setup) Last updated 3 months ago * [Prerequisites](https://docs.sai.fun/guides/onboarding#prerequisites) * [Quick Start](https://docs.sai.fun/guides/onboarding#quick-start) * [Need Help?](https://docs.sai.fun/guides/onboarding#need-help) --- # Choosing a Market | Sai.fun [hashtag](https://docs.sai.fun/guides/choosing-market#available-markets) Available Markets ----------------------------------------------------------------------------------------------- Sai supports various perpetual markets: * BTC-USD (Bitcoin) * ETH-USD (Ethereum) * And more... [hashtag](https://docs.sai.fun/guides/choosing-market#how-to-select-a-market) How to Select a Market --------------------------------------------------------------------------------------------------------- 1. Open the **sai.fun DApp** 2. Go to the **Trade** section 3. Click the **Market dropdown** 4. Select your desired market (e.g., _BTC-USD_) 5. Review the **Oracle Price** for the current quote [hashtag](https://docs.sai.fun/guides/choosing-market#understanding-markets) Understanding Markets ------------------------------------------------------------------------------------------------------- Each market shows: * **Current Price**: Live price from the oracle * **24h Volume**: Trading activity * **Open Interest**: Total leveraged positions [PreviousAcquiring Collateralchevron-left](https://docs.sai.fun/guides/acquiring-collateral) [NextSai Referral Programchevron-right](https://docs.sai.fun/guides/referral) Last updated 3 months ago * [Available Markets](https://docs.sai.fun/guides/choosing-market#available-markets) * [How to Select a Market](https://docs.sai.fun/guides/choosing-market#how-to-select-a-market) * [Understanding Markets](https://docs.sai.fun/guides/choosing-market#understanding-markets) --- # Blog | Sai.fun ![Page cover](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fs6tMymGljPk7rDj9Ueg1%252Fsai-twitter-banner-1500x500.jpg%3Falt%3Dmedia%26token%3D48da11c3-5690-41a4-b66e-2bde9973f91d&width=1248&dpr=3&quality=100&sign=a054301c&sv=2) Stay updated with the latest news, feature releases, and insights from the Sai perpetuals platform. Our blog covers trading tips, platform updates, ecosystem news, and community highlights. * * * [hashtag](https://docs.sai.fun/resources/blogs#subscribe-to-updates) Subscribe to Updates ---------------------------------------------------------------------------------------------- Don't miss new posts. Follow Sai for real-time announcements: * **X / Twitter:** [@SaiDotFunarrow-up-right](https://x.com/SaiDotFun) * **Telegram:** [Join our communityarrow-up-right](https://t.me/saidotfun) [PreviousCommunitychevron-left](https://docs.sai.fun/resources/community) [NextIntro to Saichevron-right](https://docs.sai.fun/resources/blogs/intro-to-sai) Last updated 22 hours ago --- # Sai Referral Program | Sai.fun ![Page cover](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252FYyFLT3iIOqFGDN2JP9u9%252Fsai-banner-guides-1500x500.png%3Falt%3Dmedia%26token%3Da7262a89-4e22-4bf9-aa82-27b06588f2ae&width=1248&dpr=3&quality=100&sign=3ad5d264&sv=2) * [How Referrals Work on Sai](https://docs.sai.fun/guides/referral#how-referrals-work-on-sai) * [Overview](https://docs.sai.fun/guides/referral#overview) * [Earn by Inviting Traders](https://docs.sai.fun/guides/referral#earn-by-inviting-traders) * [How It Works](https://docs.sai.fun/guides/referral#how-it-works) * [1) Create a Referral Code](https://docs.sai.fun/guides/referral#id-1-create-a-referral-code) * [2) Redeem a Referral Code](https://docs.sai.fun/guides/referral#id-2-redeem-a-referral-code) * [Commission Structure](https://docs.sai.fun/guides/referral#commission-structure) * [Referral Commission Tiers](https://docs.sai.fun/guides/referral#referral-commission-tiers) * [Trading Fee Discounts](https://docs.sai.fun/guides/referral#trading-fee-discounts) * [Get Started](https://docs.sai.fun/guides/referral#get-started) * [Need Help?](https://docs.sai.fun/guides/referral#need-help) [hashtag](https://docs.sai.fun/guides/referral#overview) Overview ---------------------------------------------------------------------- Sai's referral program offers a win-win opportunity for both referrers and traders: 1. **Redeem a referral code** to receive trading fee discounts on every trade 2. **Share a referral code** to earn a percentage of trading fees generated by traders you refer circle-info **Key Benefit:** Referrers earn commissions while referred traders save on fees—everyone wins! [hashtag](https://docs.sai.fun/guides/referral#earn-by-inviting-traders) Earn by Inviting Traders ------------------------------------------------------------------------------------------------------ When traders use your referral code to trade on Sai, you earn a share of the trading fees they generate. At the same time, traders who redeem your code receive discounted trading fees, creating a mutually beneficial relationship. ### [hashtag](https://docs.sai.fun/guides/referral#how-it-works) How It Works #### [hashtag](https://docs.sai.fun/guides/referral#id-1-create-a-referral-code) 1) Create a Referral Code * **Create and share:** Generate a unique referral code or link and share it with your community through social media, Discord, Telegram, or any platform * **Earn commissions:** When someone trades using your code, you automatically earn a percentage of the trading fees they pay * **No impact on your fees:** Your own trading fees remain unaffected—you only earn commissions from referred traders' activity on Sai circle-check **Pro Tip:** The more active traders you refer, the more you earn. Focus on quality referrals who will actively trade on the platform. #### [hashtag](https://docs.sai.fun/guides/referral#id-2-redeem-a-referral-code) 2) Redeem a Referral Code * **Instant savings:** Traders who redeem a code receive a percentage discount on trading fees for every trade, up to a certain amount (e.g., $10,000,000 in total trading volume) * **Automatic application:** The discount applies automatically to all your trades—no need to manually apply it each time * **No volume requirements:** Unlike some platforms, the discount is not tied to your personal trading volume [hashtag](https://docs.sai.fun/guides/referral#commission-structure) Commission Structure ---------------------------------------------------------------------------------------------- Sai offers a competitive commission structure designed to incentivize platform growth and reward active community builders. When a user redeems your code, you receive a baseline **15% commission** on the revenue generated by their trading fees. ### [hashtag](https://docs.sai.fun/guides/referral#referral-commission-tiers) Referral Commission Tiers Referrers can earn an increasing share of referred traders' fees based on their tier level: Tier Commission Rate Eligibility Tier 1 5% Entry level Tier 2 10% Active referrers Tier 3 15% High-volume referrers VIP Up to 50% Exclusive partners circle-exclamation **VIP Partnership:** To qualify for VIP tier commissions (up to 50%), please contact the Sai team to discuss partnership opportunities. [hashtag](https://docs.sai.fun/guides/referral#trading-fee-discounts) Trading Fee Discounts ------------------------------------------------------------------------------------------------ Trading fee discounts **do not stack**. Traders automatically receive whichever discount is higher: * **Volume-based discounts:** Earned from 30-day trading activity on the platform * **Referral code discount:** 15% off trading fees when redeeming a valid referral code This ensures you always get the best possible rate on your trades. [hashtag](https://docs.sai.fun/guides/referral#get-started) Get Started ---------------------------------------------------------------------------- Follow these simple steps to start earning or saving today: ### [hashtag](https://docs.sai.fun/guides/referral#id-1.-create-a-referral-code-and-share-it-with-your-audience) 1\. Create a referral code and share it with your audience ![Create a referral code on Sai](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-843078955b0b4acdcc8f8ac920938ad5dafa6c9a%252F000_create_code.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=72453ca4&sv=2)![Create a referral code on Sai](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-843078955b0b4acdcc8f8ac920938ad5dafa6c9a%252F000_create_code.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=72453ca4&sv=2) Navigate to the Referrals page to create your unique code ### [hashtag](https://docs.sai.fun/guides/referral#id-2.-redeem-a-referral-code-to-start-saving-on-trading-fees) 2\. Redeem a referral code to start saving on trading fees ![Redeem a referral code on Sai](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-304dd9bd9a1fb7670c74b9356e9c315dfd668b91%252F001_redeem_code.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=1a2dca4&sv=2)![Redeem a referral code on Sai](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-304dd9bd9a1fb7670c74b9356e9c315dfd668b91%252F001_redeem_code.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=1a2dca4&sv=2) Enter a referral code to unlock instant trading fee discounts ### [hashtag](https://docs.sai.fun/guides/referral#id-3.-trade-track-your-activity-on-the-leaderboard-and-climb-tiers-over-time) 3\. Trade, track your activity on the leaderboard, and climb tiers over time Monitor your referral performance and earnings directly from your dashboard. As your referred traders become more active, you'll automatically progress through commission tiers and earn higher rewards. * * * [hashtag](https://docs.sai.fun/guides/referral#need-help) Need Help? ------------------------------------------------------------------------- If you have questions about the referral program or need assistance getting started, reach out to our community. **Resources:** * 🐦 [X / Twitterarrow-up-right](https://x.com/SaiDotFun) - @SaiDotFun * 📢 [Telegram News Channelarrow-up-right](https://t.me/saidotfun) - t.me/saidotfun * * * _Last updated: February 2026_ [PreviousChoosing a Marketchevron-left](https://docs.sai.fun/guides/choosing-market) [NextGetting Startedchevron-right](https://docs.sai.fun/for-devs/dev) Last updated 6 days ago * [Overview](https://docs.sai.fun/guides/referral#overview) * [Earn by Inviting Traders](https://docs.sai.fun/guides/referral#earn-by-inviting-traders) * [How It Works](https://docs.sai.fun/guides/referral#how-it-works) * [Commission Structure](https://docs.sai.fun/guides/referral#commission-structure) * [Referral Commission Tiers](https://docs.sai.fun/guides/referral#referral-commission-tiers) * [Trading Fee Discounts](https://docs.sai.fun/guides/referral#trading-fee-discounts) * [Get Started](https://docs.sai.fun/guides/referral#get-started) * [1\. Create a referral code and share it with your audience](https://docs.sai.fun/guides/referral#id-1.-create-a-referral-code-and-share-it-with-your-audience) * [2\. Redeem a referral code to start saving on trading fees](https://docs.sai.fun/guides/referral#id-2.-redeem-a-referral-code-to-start-saving-on-trading-fees) * [3\. Trade, track your activity on the leaderboard, and climb tiers over time](https://docs.sai.fun/guides/referral#id-3.-trade-track-your-activity-on-the-leaderboard-and-climb-tiers-over-time) * [Need Help?](https://docs.sai.fun/guides/referral#need-help) --- # Community | Sai.fun ![Page cover](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-9b2d1ed166fec46fbd584efde1a20c879894bb6d%252Fsai-docs-banner.png%3Falt%3Dmedia&width=1248&dpr=3&quality=100&sign=b8e59fe7&sv=2) X / Twitter [@SaiDotFunarrow-up-right](https://x.com/SaiDotFun) Telegram News Channel [t.me/saidotfunarrow-up-right](https://t.me/saidotfun) [PreviousTelegram Bot Integrationchevron-left](https://docs.sai.fun/for-devs/use-cases/tg-guide) [NextBlogchevron-right](https://docs.sai.fun/resources/blogs) Last updated 1 month ago --- # Use Cases | Sai.fun Explore real-world examples of how to build with Sai. These guides showcase practical implementations using the Sai developer tools, smart contracts, and APIs. [hashtag](https://docs.sai.fun/for-devs/use-cases#featured-use-cases) Featured Use Cases --------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/use-cases#telegram-bot) Telegram Bot Build a trading bot that brings Sai's power directly to your users through Telegram. [Telegram Bot Integrationchevron-right](https://docs.sai.fun/for-devs/use-cases/tg-guide) * * * [hashtag](https://docs.sai.fun/for-devs/use-cases#get-started) Get Started ------------------------------------------------------------------------------- New to Sai? Check out our main developer documentation to learn about the core tools and APIs: [codeGetting Startedchevron-right](https://docs.sai.fun/for-devs/dev) [hashtag](https://docs.sai.fun/for-devs/use-cases#have-a-use-case-to-share) Have a Use Case to Share? ---------------------------------------------------------------------------------------------------------- Building something interesting with Sai? We'd love to hear about it! * **Share your project** on [X/Twitterarrow-up-right](https://x.com/saidotfun) * **Join our community** to showcase your work [PreviousFilters & Paginationchevron-left](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination) [NextTelegram Bot Integrationchevron-right](https://docs.sai.fun/for-devs/use-cases/tg-guide) Last updated 1 month ago * [Featured Use Cases](https://docs.sai.fun/for-devs/use-cases#featured-use-cases) * [Telegram Bot](https://docs.sai.fun/for-devs/use-cases#telegram-bot) * [Get Started](https://docs.sai.fun/for-devs/use-cases#get-started) * [Have a Use Case to Share?](https://docs.sai.fun/for-devs/use-cases#have-a-use-case-to-share) --- # Sai Core | Sai.fun ![Page cover](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fs6tMymGljPk7rDj9Ueg1%252Fsai-twitter-banner-1500x500.jpg%3Falt%3Dmedia%26token%3D48da11c3-5690-41a4-b66e-2bde9973f91d&width=1248&dpr=3&quality=100&sign=a054301c&sv=2) ### [hashtag](https://docs.sai.fun/for-devs/sai-core#how-sai-the-sai-smart-contracts-work) How Sai the Sai Smart Contracts Work * `**perp**` **(Perpetuals trading core)**: * Maintains markets, positions, margin, and liquidations. * Calculates execution price with price impact, applies trading/borrowing/funding fees, and updates PnL. * Enforces risk constraints (maintenance margin, max leverage, funding payments). * Entry points (conceptually): open/close position, modify collateral, place/execute orders, liquidate under-margined positions. * Reads prices from `oracle`, collects/streams fees, and settles PnL against `vault`. * **EVM interface (**`**PerpVaultEvmInterface.sol**`**)**: * Lets MetaMask/EVM users interact with the Wasm contracts. * Handles token conversion and cross-VM calls to `perp` and `vault`. * Mirrors the essential user flows (deposit, withdraw, open/close) for EVM wallets. * `**vault**` **(Liquidity provider pool)**: [Implements SLP Vaultsarrow-up-right](https://github.com/NibiruChain/sai-docs/blob/main/dev/learn/slp.md) . * Aggregates LP liquidity to underwrite trader PnL. * Issues and accounts for shares against total assets; supports deposits and epoch-based withdrawals. * Absorbs trader PnL: when traders win, the vault pays out; when traders lose, the vault accrues profits. * Coordinates with `vault-token-minter` for share token mint/burn and with `perp` for settlement transfers. * `**oracle**` **(Price feed and permissions)**: * Stores and serves the canonical mark price per market. * Controls who can update prices and under what conditions. * Provides prices to `perp` for margin checks, execution, funding, and liquidation thresholds. * `**vault-token-minter**` **(Token factory bridge for shares)**: * Controls minting/burning of vault share tokens via Nibiru’s token factory. * Enforces whitelist/ownership checks to prevent unauthorized supply changes. * Called by `vault` to mint on deposit and burn on withdrawal/redemption. ### [hashtag](https://docs.sai.fun/for-devs/sai-core#interaction-model) Interaction model 1. **Liquidity lifecycle** 1. LPs deposit assets into `vault` → `vault` mints shares via `vault-token-minter`. 2. LPs request withdrawals → `vault` queues and later exits via epochs, burning shares on completion. 2. **Trading lifecycle** 1. Trader requests an action (EVM or Wasm): open/close/modify position in `perp`. 2. `perp` fetches price from `oracle`, computes price impact, fees, and checks margin/leverage constraints. 3. `perp` updates position state, accrues funding/borrowing fees, and computes PnL deltas. 3. **Settlement path** 1. Trader losses → profits accrue to `vault`. 2. Trader gains → `perp` sources payout from `vault`. 4. **Funding and price alignment** 1. `perp` periodically computes funding based on price deltas between mark and index; longs/shorts pay/receive accordingly. 2. Funding transfers are accounted in positions and net settle via `vault` over time. 5. **Liquidations** 1. If a position’s margin fraction drops below maintenance, `perp` allows a liquidator to close the position. 2. `perp` calculates close price (with impact), applies penalties/fees, and settles PnL with `vault`. 3. Any residual collateral after penalties is returned to the trader; bad debt is socialized to `vault` within configured limits. #### [hashtag](https://docs.sai.fun/for-devs/sai-core#access-and-safety) **Access and safety** 1. Ownership/roles restrict sensitive ops: oracle price updates, mint permissions, parameter changes. 2. `perp` enforces invariants (max leverage, min maintenance, fee bounds). 3. `vault` enforces epoch rules and share/accounting correctness. 4. Cross-VM calls are designed to avoid re-entrancy and maintain atomic accounting on state-changing operations. ### [hashtag](https://docs.sai.fun/for-devs/sai-core#data-and-accounting-at-a-glance) Data and accounting at a glance * `**perp**` **state**: markets, position records (size, entry price, collateral, accumulated funding/borrowing), pending orders, parameters. * `**vault**` **state**: total assets, total shares, per-account share balances, withdrawal epochs/queues. * `**oracle**` **state**: price per market with update metadata. * `**vault-token-minter**` **state**: mint/burn permissions and token metadata. ### [hashtag](https://docs.sai.fun/for-devs/sai-core#typical-end-to-end-flows) Typical end-to-end flows * Open a long: * User calls `perp.open_position` → `perp` gets price from `oracle`, applies price impact and fees, locks collateral, records position. * Close a long in profit: * `perp` computes PnL at current `oracle` price (with impact), deducts fees, and transfers profit from `vault` to user. * LP exit: * User requests withdraw → `vault` queues for epoch → on execution, `vault` burns shares via `vault-token-minter` and releases assets. * The contracts’ business roles are: `perp` (trade logic and risk), `vault` (liquidity and PnL sink/source), `oracle` (truthful prices), `vault-token-minter` (controlled share supply), with an EVM facade enabling wallet-native access. ### [hashtag](https://docs.sai.fun/for-devs/sai-core#sai-and-the-evm) Sai and the EVM Sai is written as a Multi VM app with contracts written in both Rust and Solidity. This lets the application benefit from the security benefits of working with Wasm while remaining fully capable of tapping into the flexibility and familiarity that comes with the Ethereum Virtual Machine (EVM). [PreviousWASM Integration Guidechevron-left](https://docs.sai.fun/for-devs/dev/wasm-guide) [NextPerp Contractchevron-right](https://docs.sai.fun/for-devs/sai-core/perp-contract) Last updated 1 month ago * [How Sai the Sai Smart Contracts Work](https://docs.sai.fun/for-devs/sai-core#how-sai-the-sai-smart-contracts-work) * [Interaction model](https://docs.sai.fun/for-devs/sai-core#interaction-model) * [Data and accounting at a glance](https://docs.sai.fun/for-devs/sai-core#data-and-accounting-at-a-glance) * [Typical end-to-end flows](https://docs.sai.fun/for-devs/sai-core#typical-end-to-end-flows) * [Sai and the EVM](https://docs.sai.fun/for-devs/sai-core#sai-and-the-evm) --- # Contract Addresses Reference | Sai.fun Quick reference for all Sai Protocol contract addresses on Testnet and Mainnet. [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#table-of-contents) Table of Contents ---------------------------------------------------------------------------------------------------- * [Testnet Contracts](https://docs.sai.fun/for-devs/sai-core/contracts#testnet-contracts) * [Mainnet Contracts](https://docs.sai.fun/for-devs/sai-core/contracts#mainnet-contracts) * [Payment Tokens](https://docs.sai.fun/for-devs/sai-core/contracts#payment-tokens) * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#testnet-contracts) Testnet Contracts ---------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#core-wasm-contracts) Core Wasm Contracts Contract Address **Perp Manager** `nibi1qtkcns647w959cj9x2yytateu6dgscfnfkraywwa443pr2erak0s5ux7e5` **Oracle** `nibi1mqlrsvfhm5vzsz0wxr6mh8pzxzpz6dd4g7nuyycjf6gy5zc53fvq3lq2fz` ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#lp-vaults-testnet) LP Vaults (Testnet) Vault Type Vault Address Vault Minter Address **Vault 1** `nibi1jkuszyvgufu5kghmzp47gg02j4jyvtv22hs4vtmefg3wuzqayefqukhv7t` `nibi174snnesyj442lxhn53xqcffh7us3f6q3se2sswe5cmmj78majj0sskdk3h` **Vault 2** `nibi1rsp225zym8x4pdcjdazl8jhg9jvxlptutdkynuvkamas8jjv8j5s22s86m` `nibi1qa4cj932sm2rsuvz0vn8sz9qzarrctj693nnwhkqnjnvekan460shr3v0a` ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#evm-interface-contract) EVM Interface Contract Network Address **Testnet** `0x282F097930E24e4fba97EE8687d15Ed1f298ad19` * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#mainnet-contracts) Mainnet Contracts ---------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#core-wasm-contracts-1) Core Wasm Contracts Contract Address **Perp Manager** `nibi1ntmw2dfvd0qnw5fnwdu9pev2hsnqfdj9ny9n0nzh2a5u8v0scflq930mph` **Oracle** `nibi1xfwyfwtdame6645lgcs4xvf4u0hpsuvxrcelfwtztu0pv7n4l6hqw5a8gj` ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#lp-vaults-mainnet-crypto-group) LP Vaults (Mainnet - Crypto Group) Collateral Vault Address Vault Minter Address **USDC** `nibi193m2a00pmdsvkcvugrfewqzhtq6k0srkjzvxp2sk357vlpspx5vqxu8d7p` `nibi15hcra03vdaaveslz6lekm02kuwkce23uj0u5u9ryaggjh4h5dlysfdmfvj` **stNIBI** `nibi1mrplvu3scplnrgns96kg0j8pk3l2p9c7eaz0qdedx0kt3vmcujyqrjkfej` `nibi1vp45yer9anh4p99hgkuy4rx6mqt8cqwglfe5zzve22av67yl0k9qjreyv4` ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#evm-interface-contract-1) EVM Interface Contract Network Address **Mainnet** `0x9F48A925Dda8528b3A5c2A6717Df0F03c8b167c0` * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#payment-tokens) Payment Tokens ---------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#mainnet-payment-tokens) Mainnet Payment Tokens Token Symbol EVM Address Bank Denom Decimals Logo **USDC** USDC `0x0829F361A05D993d5CEb035cA6DF3446b060970b` `erc20/0x0829F361A05D993d5CEb035cA6DF3446b060970b` 6 [🔗arrow-up-right](https://raw.githubusercontent.com/NibiruChain/nibiru/main/token-registry/img/002_usdc.png) **Liquid Staked NIBI** stNIBI `0xcA0a9Fb5FBF692fa12fD13c0A900EC56Bb3f0a7b` `tf/nibi1udqqx30cw8nwjxtl4l28ym9hhrp933zlq8dqxfjzcdhvl8y24zcqpzmh8m/ampNIBI` 6 [🔗arrow-up-right](https://raw.githubusercontent.com/NibiruChain/nibiru/main/token-registry/img/001_stnibi-evm.png) ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#testnet-payment-tokens) Testnet Payment Tokens Token Symbol EVM Address Bank Denom Decimals Logo **USDC** USDC `0xAb68f1D1d91854383fd4Df9016E3040D03e8191a` `tf/nibi1pc2mmwcqhvzn9vsm0umpu40yzl6gfy6nucwn7g/usdc` 6 [🔗arrow-up-right](https://raw.githubusercontent.com/NibiruChain/nibiru/main/token-registry/img/002_usdc.png) **Liquid Staked NIBI** stNIBI `0xCae3d404AFB50016154a4B18091351065154E9bD` `tf/nibi1pc2mmwcqhvzn9vsm0umpu40yzl6gfy6nucwn7g/stnibi` 6 [🔗arrow-up-right](https://raw.githubusercontent.com/NibiruChain/nibiru/main/token-registry/img/001_stnibi-evm.png) * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#usage-notes) Usage Notes ---------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#wasm-contracts) Wasm Contracts * Use these addresses when interacting directly with Cosmos SDK/Wasm contracts * Required for CosmJS or nibijs client libraries * Format: `nibi1...` (Bech32 encoded) ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#evm-interface-contracts) EVM Interface Contracts * Use these addresses when interacting via EVM-compatible wallets (MetaMask, etc.) * Provides EVM-style interface to underlying Wasm contracts * Format: `0x...` (Ethereum hex address) ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#payment-tokens-1) Payment Tokens * **EVM Address**: Use for ERC20 interactions via Web3/Ethers.js * **Bank Denom**: Use for Cosmos SDK transactions * **Contract Address**: Same as Bank Denom, used in Wasm contract calls * All tokens use **6 decimals** ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#vault-structure) Vault Structure * **Vault**: Main vault contract that holds assets * **Vault Minter**: Contract that mints/burns vault shares * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#quick-copy) Quick Copy -------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#testnet) Testnet ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contracts#mainnet) Mainnet [PreviousPerp: State Variableschevron-left](https://docs.sai.fun/for-devs/sai-core/perp-state) [NextWASM & EVM Integrationchevron-right](https://docs.sai.fun/for-devs/sai-core/contract-integration) Last updated 3 months ago * [Table of Contents](https://docs.sai.fun/for-devs/sai-core/contracts#table-of-contents) * [Testnet Contracts](https://docs.sai.fun/for-devs/sai-core/contracts#testnet-contracts) * [Core Wasm Contracts](https://docs.sai.fun/for-devs/sai-core/contracts#core-wasm-contracts) * [LP Vaults (Testnet)](https://docs.sai.fun/for-devs/sai-core/contracts#lp-vaults-testnet) * [EVM Interface Contract](https://docs.sai.fun/for-devs/sai-core/contracts#evm-interface-contract) * [Mainnet Contracts](https://docs.sai.fun/for-devs/sai-core/contracts#mainnet-contracts) * [Core Wasm Contracts](https://docs.sai.fun/for-devs/sai-core/contracts#core-wasm-contracts-1) * [LP Vaults (Mainnet - Crypto Group)](https://docs.sai.fun/for-devs/sai-core/contracts#lp-vaults-mainnet-crypto-group) * [EVM Interface Contract](https://docs.sai.fun/for-devs/sai-core/contracts#evm-interface-contract-1) * [Payment Tokens](https://docs.sai.fun/for-devs/sai-core/contracts#payment-tokens) * [Mainnet Payment Tokens](https://docs.sai.fun/for-devs/sai-core/contracts#mainnet-payment-tokens) * [Testnet Payment Tokens](https://docs.sai.fun/for-devs/sai-core/contracts#testnet-payment-tokens) * [Usage Notes](https://docs.sai.fun/for-devs/sai-core/contracts#usage-notes) * [Wasm Contracts](https://docs.sai.fun/for-devs/sai-core/contracts#wasm-contracts) * [EVM Interface Contracts](https://docs.sai.fun/for-devs/sai-core/contracts#evm-interface-contracts) * [Payment Tokens](https://docs.sai.fun/for-devs/sai-core/contracts#payment-tokens-1) * [Vault Structure](https://docs.sai.fun/for-devs/sai-core/contracts#vault-structure) * [Quick Copy](https://docs.sai.fun/for-devs/sai-core/contracts#quick-copy) * [Testnet](https://docs.sai.fun/for-devs/sai-core/contracts#testnet) * [Mainnet](https://docs.sai.fun/for-devs/sai-core/contracts#mainnet) Copy # Core Contracts PERP_MANAGER="nibi1qtkcns647w959cj9x2yytateu6dgscfnfkraywwa443pr2erak0s5ux7e5" ORACLE="nibi1mqlrsvfhm5vzsz0wxr6mh8pzxzpz6dd4g7nuyycjf6gy5zc53fvq3lq2fz" EVM_INTERFACE="0x282F097930E24e4fba97EE8687d15Ed1f298ad19" # Payment Tokens USDC_EVM="0xAb68f1D1d91854383fd4Df9016E3040D03e8191a" STNIBI_EVM="0xCae3d404AFB50016154a4B18091351065154E9bD" Copy # Core Contracts PERP_MANAGER="nibi1ntmw2dfvd0qnw5fnwdu9pev2hsnqfdj9ny9n0nzh2a5u8v0scflq930mph" ORACLE="nibi1xfwyfwtdame6645lgcs4xvf4u0hpsuvxrcelfwtztu0pv7n4l6hqw5a8gj" EVM_INTERFACE="0x9F48A925Dda8528b3A5c2A6717Df0F03c8b167c0" # Payment Tokens USDC_EVM="0x0829F361A05D993d5CEb035cA6DF3446b060970b" STNIBI_EVM="0xcA0a9Fb5FBF692fa12fD13c0A900EC56Bb3f0a7b" --- # Sai Keeper | Sai.fun ![Page cover](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fs6tMymGljPk7rDj9Ueg1%252Fsai-twitter-banner-1500x500.jpg%3Falt%3Dmedia%26token%3D48da11c3-5690-41a4-b66e-2bde9973f91d&width=1248&dpr=3&quality=100&sign=a054301c&sv=2) Sai Keeper is a GraphQL API that provides real-time and historical data for the Sai protocol on Nibiru Chain. It enables developers to query perpetual trades, liquidity pool data, oracle prices, and fee analytics. [hashtag](https://docs.sai.fun/for-devs/sai-keeper#what-is-sai-keeper) What is Sai Keeper? ----------------------------------------------------------------------------------------------- Sai Keeper is the data indexing and query layer for Sai Protocol. It processes on-chain events and makes them accessible through a fast, flexible GraphQL API. **Key capabilities:** * Query perpetual trading positions and history * Access liquidity pool data and APY metrics * Retrieve oracle price feeds * Analyze fee structures and protocol revenue * Subscribe to real-time updates via WebSockets [hashtag](https://docs.sai.fun/for-devs/sai-keeper#environments) Environments ---------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper#testnet) Testnet * **GraphQL Endpoint**: `https://sai-keeper.testnet-2.nibiru.fi/graphql` * **Interactive Playground**: `https://sai-keeper.testnet-2.nibiru.fi/` * **Purpose**: Development and testing with test data * Use for development and testing ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper#mainnet) Mainnet * **GraphQL Endpoint**: `https://sai-keeper.nibiru.fi/graphql` * **Interactive Playground**: `https://sai-keeper.nibiru.fi/` * **Purpose**: Production applications with real data * Use for production applications ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper#interactive-user-interface) Interactive User Interface Both Testnet and Mainnet endpoints provide an **interactive GraphQL Playground** where you can: * **Test all queries** with live data * **Test subscriptions** with real-time updates * **Explore the schema** with built-in documentation * **View query history** and save queries * **Debug responses** with formatted JSON Simply visit the endpoint URLs in your browser to access the playground! [hashtag](https://docs.sai.fun/for-devs/sai-keeper#quick-start) Quick Start -------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper#id-1.-explore-the-api) 1\. Explore the API Visit the GraphQL Playground to explore the schema interactively: * **Testnet**: [https://sai-keeper.testnet-2.nibiru.fi/arrow-up-right](https://sai-keeper.testnet-2.nibiru.fi/) * **Mainnet**: [https://sai-keeper.nibiru.fi/arrow-up-right](https://sai-keeper.nibiru.fi/) ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper#id-2.-make-your-first-query) 2\. Make Your First Query Try this simple query in the playground: ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper#id-3.-set-up-your-client) 3\. Set Up Your Client Install a GraphQL client library: **JavaScript/TypeScript:** **Python:** **Rust:** [hashtag](https://docs.sai.fun/for-devs/sai-keeper#core-data-domains) Core Data Domains -------------------------------------------------------------------------------------------- Sai Keeper organizes data into four main domains: ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper#id-1.-perp-perpetuals) 1\. Perp (Perpetuals) Query and subscribe to perpetual trading data: * Open and closed positions * Trade history and events * Market borrowing rates ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper#id-2.-lp-liquidity-pools) 2\. LP (Liquidity Pools) Access liquidity provider information: * Vault metrics (TVL, APY, share price) * User deposits and shares * Withdrawal requests * Revenue tracking ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper#id-3.-oracle) 3\. Oracle Get token price data: * Real-time token prices in USD * Token metadata * Price update timestamps ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper#id-4.-fee) 4\. Fee Analyze fee data: * Transaction-level fees * Daily fee statistics * Protocol and trader fee summaries * Fee type breakdowns (opening/closing) [hashtag](https://docs.sai.fun/for-devs/sai-keeper#graphql-basics) GraphQL Basics -------------------------------------------------------------------------------------- If you're new to GraphQL, here are the essentials: ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper#queries) Queries Request specific data: ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper#subscriptions) Subscriptions Get real-time updates: ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper#variables) Variables Parameterize your queries: Variables: [hashtag](https://docs.sai.fun/for-devs/sai-keeper#architecture-overview) Architecture Overview ---------------------------------------------------------------------------------------------------- Sai Keeper indexes blockchain data and provides it through a fast, queryable GraphQL interface. This means you don't need to query the blockchain directly or maintain your own indexer. [hashtag](https://docs.sai.fun/for-devs/sai-keeper#what-you-can-build) What You Can Build ---------------------------------------------------------------------------------------------- * **Trading Platforms**: Full-featured perpetual trading interfaces * **Portfolio Trackers**: Monitor positions, PnL, and performance * **Analytics Dashboards**: Visualize protocol metrics and user activity * **Automated Bots**: Build trading algorithms with real-time data * **LP Management Tools**: Optimize positions strategies [hashtag](https://docs.sai.fun/for-devs/sai-keeper#next-steps) Next Steps ------------------------------------------------------------------------------ * **New to GraphQL?** → Read [Core Concepts](https://docs.sai.fun/for-devs/sai-keeper/core-concepts) * **Ready to code?** → Check out [Client Setup](https://docs.sai.fun/for-devs/sai-keeper/client-setup) * **Want examples?** → See [Query Examples](https://docs.sai.fun/for-devs/sai-keeper/examples-queries) * **Need API details?** → Browse the API references: * [Perp API](https://docs.sai.fun/for-devs/sai-keeper/api-perp) * [LP API](https://docs.sai.fun/for-devs/sai-keeper/api-lp) * [Oracle API](https://docs.sai.fun/for-devs/sai-keeper/api-oracle) * [Fee API](https://docs.sai.fun/for-devs/sai-keeper/api-fees) [PreviousWASM & EVM Integrationchevron-left](https://docs.sai.fun/for-devs/sai-core/contract-integration) [NextCore Conceptschevron-right](https://docs.sai.fun/for-devs/sai-keeper/core-concepts) Last updated 1 month ago * [What is Sai Keeper?](https://docs.sai.fun/for-devs/sai-keeper#what-is-sai-keeper) * [Environments](https://docs.sai.fun/for-devs/sai-keeper#environments) * [Testnet](https://docs.sai.fun/for-devs/sai-keeper#testnet) * [Mainnet](https://docs.sai.fun/for-devs/sai-keeper#mainnet) * [Interactive User Interface](https://docs.sai.fun/for-devs/sai-keeper#interactive-user-interface) * [Quick Start](https://docs.sai.fun/for-devs/sai-keeper#quick-start) * [1\. Explore the API](https://docs.sai.fun/for-devs/sai-keeper#id-1.-explore-the-api) * [2\. Make Your First Query](https://docs.sai.fun/for-devs/sai-keeper#id-2.-make-your-first-query) * [3\. Set Up Your Client](https://docs.sai.fun/for-devs/sai-keeper#id-3.-set-up-your-client) * [Core Data Domains](https://docs.sai.fun/for-devs/sai-keeper#core-data-domains) * [1\. Perp (Perpetuals)](https://docs.sai.fun/for-devs/sai-keeper#id-1.-perp-perpetuals) * [2\. LP (Liquidity Pools)](https://docs.sai.fun/for-devs/sai-keeper#id-2.-lp-liquidity-pools) * [3\. Oracle](https://docs.sai.fun/for-devs/sai-keeper#id-3.-oracle) * [4\. Fee](https://docs.sai.fun/for-devs/sai-keeper#id-4.-fee) * [GraphQL Basics](https://docs.sai.fun/for-devs/sai-keeper#graphql-basics) * [Queries](https://docs.sai.fun/for-devs/sai-keeper#queries) * [Subscriptions](https://docs.sai.fun/for-devs/sai-keeper#subscriptions) * [Variables](https://docs.sai.fun/for-devs/sai-keeper#variables) * [Architecture Overview](https://docs.sai.fun/for-devs/sai-keeper#architecture-overview) * [What You Can Build](https://docs.sai.fun/for-devs/sai-keeper#what-you-can-build) * [Next Steps](https://docs.sai.fun/for-devs/sai-keeper#next-steps) Copy query GetTokenPrices { oracle { tokenPricesUsd(limit: 5) { token { symbol name } priceUsd lastUpdatedBlock { block block_ts } } } } Copy npm install @apollo/client graphql # or npm install urql graphql Copy pip install gql[all] Copy [dependencies] graphql_client = "0.13" Copy query { perp { trades(where: { trader: "nibi1abc..." }, limit: 10) { id isOpen leverage } } } Copy subscription { perpTrades(where: { trader: "nibi1abc..." }) { id isOpen leverage } } Copy query GetTrades($trader: String!, $limit: Int) { perp { trades(where: { trader: $trader }, limit: $limit) { id } } } Copy { "trader": "nibi1abc...", "limit": 10 } Copy ┌─────────────────┐ │ Your App │ │ (Frontend/ │ │ Backend) │ └────────┬────────┘ │ │ GraphQL │ Query/Subscribe │ ┌────────▼────────┐ │ Sai Keeper │ │ GraphQL API │ └────────┬────────┘ │ │ Indexes │ ┌────────▼────────┐ │ Nibiru Chain │ │ (Blockchain) │ └─────────────────┘ --- # Legal | Sai.fun ![Page cover](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fs6tMymGljPk7rDj9Ueg1%252Fsai-twitter-banner-1500x500.jpg%3Falt%3Dmedia%26token%3D48da11c3-5690-41a4-b66e-2bde9973f91d&width=1248&dpr=3&quality=100&sign=a054301c&sv=2) This section contains important legal documents, terms of service, and regulatory information for Sai and Sai.fun. Please review these documents carefully before using our services. [hashtag](https://docs.sai.fun/resources/legal#documents) Documents ------------------------------------------------------------------------ [Terms of Usechevron-right](https://docs.sai.fun/resources/legal/terms-of-use) [Sai Disclosureschevron-right](https://docs.sai.fun/resources/legal/disclosures) [PreviousIntro to Saichevron-left](https://docs.sai.fun/resources/blogs/intro-to-sai) [NextTerms of Usechevron-right](https://docs.sai.fun/resources/legal/terms-of-use) Last updated 13 days ago --- # Perp: State Variables | Sai.fun This document describes the state variables in the perpetual futures trading smart contract. Variables are organized by functional module for clarity and ease of reference. [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-state#borrowing) Borrowing ------------------------------------------------------------------------------------- Variable Type Description `PAIRS` `Map<(TokenIndex, MarketIndex), BorrowingData>` Stores borrowing data for trading pairs `PAIR_GROUPS` `Map<(TokenIndex, MarketIndex), Vec>` Tracks historical group associations for borrowing pairs `PAIR_OIS` `Map<(TokenIndex, MarketIndex), OpenInterest>` Stores open interest data for pairs `GROUPS` `Map<(TokenIndex, GroupIndex), BorrowingData>` Stores borrowing data for groups `GROUP_OIS` `Map<(TokenIndex, GroupIndex), OpenInterest>` Stores open interest data for groups `INITIAL_ACC_FEES` `Map<(TokenIndex, Addr, UserTradeIndex), BorrowingInitialAccFees>` Tracks initial accumulated fees for trades The borrowing module manages leveraged trading fees and risk exposure: * `**PAIRS**` **and** `**GROUPS**`: Store borrowing-specific parameters including fee rates and accumulated fees for calculating trading costs. Note: This borrowing `GROUPS` contains `BorrowingData` and is distinct from the trading pairs `GROUPS` which contains general group information. * `**PAIR_GROUPS**`: Maintains historical group associations for each pair. When pairs change groups, this ensures accurate fee calculations across different fee structures during a trade's lifetime. * `**PAIR_OIS**` **and** `**GROUP_OIS**`: Track open interest for pairs and groups to enforce exposure limits and calculate dynamic borrowing rates based on market imbalances. * `**INITIAL_ACC_FEES**`: Records accumulated fees when a trade opens, providing a reference point for calculating total borrowing fees at closure. [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-state#fees) Fees --------------------------------------------------------------------------- Variable Type Description `FEE_TIERS` `Item<[FeeTier; 8]>` Stores the fee tier structure `PENDING_GOV_FEES` `Map` Tracks pending governance fees `VAULT_CLOSING_FEE_P` `Item` Stores the vault closing fee percentage `TRADER_DAILY_INFOS` `Map>` Stores daily trading information for traders `PAIR_VOLUME_MULTIPLIERS` `Map` Stores volume multipliers for pairs `TRADER_INFOS` `Map` Stores general information about traders ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-state#referrer-system) Referrer System Variable Type Description `REFERRER_CODES` `Map` Maps referrer codes to referrer addresses `REFERRER_CODES_REVERSE` `Map` Maps referrer addresses to their codes `USER_REFERRERS` `Map<&Addr, Addr>` Maps users to their referrers `REFERRER_FEES` `Map<(TokenIndex, &Addr), Uint128>` Tracks fees earned by referrers `REFERRER_FEE_PERCENTAGE` `Item<[Decimal; 4]>` Fee percentages for different referrer tiers `REFERRER_FEE_TIER` `Map` Maps referrers to their fee tier levels `REFERREE_BASE_FEE_MULTIPLIER` `Item` Base fee multiplier for referees The fees module implements dynamic tiered fee structures and referral programs: **Core Fee Management:** * `**FEE_TIERS**`: Defines fee levels based on trading activity to attract high-volume traders. * `**PENDING_GOV_FEES**`: Accumulates governance fees for transparent distribution. * `**VAULT_CLOSING_FEE_P**`: Sets the percentage of closing fees allocated to vault insurance/liquidity. * `**TRADER_DAILY_INFOS**` **and** `**TRADER_INFOS**`: Track daily and cumulative trading data to calculate fee tiers over rolling periods. * `**PAIR_VOLUME_MULTIPLIERS**`: Weight trading volume differently across markets to incentivize specific pairs or balance liquidity. **Referral System:** The referral system rewards users for platform growth through a comprehensive affiliate program: * `**REFERRER_CODES**` **and** `**REFERRER_CODES_REVERSE**`: Enable bidirectional mapping between referrer codes and addresses. * `**USER_REFERRERS**`: Maps users to their referring addresses. * `**REFERRER_FEES**`: Accumulates earnings for each referrer across token types. * `**REFERRER_FEE_PERCENTAGE**` **and** `**REFERRER_FEE_TIER**`: Support multi-tier referrer levels with different fee percentages. * `**REFERREE_BASE_FEE_MULTIPLIER**`: Provides base discounts for referred users. [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-state#trading-pairs-and-groups) Trading Pairs and Groups ------------------------------------------------------------------------------------------------------------------- Variable Type Description `PERP_MARKETS` `Map` Stores information about trading pairs `GROUPS` `Map` Stores information about trading groups `FEES` `Map` Stores fee information for trading pairs `PAIR_CUSTOM_MAX_LEVERAGE` `Map` Stores custom maximum leverage for pairs `ORACLE_ADDRESS` `Item` Stores the address of the oracle contract `VAULT_ADDRESSES` `Map<(GroupIndex, TokenIndex), Addr>` Stores vault addresses for each group/token combination This module defines the core trading infrastructure: * `**PERP_MARKETS**`: Stores trading pair information including base/quote assets, oracle indices, and associated group/fee indices. * `**GROUPS**`: Defines leverage limits and group-wide parameters for categorizing pairs with similar risk profiles. Note: This differs from the borrowing `GROUPS` which contains `BorrowingData`. * `**FEES**`: Contains fee structures (open/close/trigger fees) for each pair, enabling market-specific fee adjustments. * `**PAIR_CUSTOM_MAX_LEVERAGE**`: Provides pair-specific leverage limits that override group settings for volatile or illiquid markets. * `**ORACLE_ADDRESS**`: Points to the oracle contract for price feeds used in trade execution and liquidations. * `**VAULT_ADDRESSES**`: Maps vault contract addresses to group/token combinations for multi-vault asset management. [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-state#price-impact-and-open-interest) Price Impact and Open Interest ------------------------------------------------------------------------------------------------------------------------------- Variable Type Description `OI_WINDOWS_SETTINGS` `Item` Stores settings for open interest windows `WINDOWS` `Map<(WindowDuration, MarketIndex, WindowIndex), PairOi>` Stores open interest data for specific windows `PAIR_DEPTHS` `Map` Stores market depth information for pairs `TRADE_LAST_WINDOW_OI` `Map<(Addr, TradeInfoIndex), Uint128>` Tracks the last window's open interest for trades This module manages market dynamics and prevents excessive price impact: * `**OI_WINDOWS_SETTINGS**`: Defines parameters for tracking open interest over time windows to calculate dynamic market impact. * `**WINDOWS**`: Stores historical open interest data for calculating cumulative trade impact over specific time periods. This time-windowed approach prevents market manipulation. * `**PAIR_DEPTHS**`: Contains market liquidity information used to calculate price impact for large trades, protecting against sudden price movements. * `**TRADE_LAST_WINDOW_OI**`: Tracks each trade's contribution to open interest in its last active window for accurate updates when trades close or modify. [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-state#trading) Trading --------------------------------------------------------------------------------- Variable Type Description `COLLATERALS` `Map` Stores information about collateral types `USER_TRADE_INDEX` `Map` Tracks the next trade index for each user `TRADES` `Map<(User, UserTradeIndex), Trade>` Stores individual trade information `TRADE_INFOS` `Map<(User, TradeInfoIndex), TradeInfo>` Stores additional information about trades `TRADER_STORED` `Map` Tracks whether a trader's information is stored `USER_LIVE_TRADE_COUNTS` `Map` Tracks trade counts for users `MINIMUM_POSITION_SIZE_USD` `Item` Stores the minimum position size in USD `TRADING_ACTIVATED` `Item` Stores the current trading activation status The trading module forms the protocol's core functionality: **Trade Management:** * `**COLLATERALS**`: Defines accepted collateral types with name, active status, and denomination for multi-asset support. * `**USER_TRADE_INDEX**`: Tracks the next available trade index per user for unique identification and prevents index collisions. * `**TRADES**` **and** `**TRADE_INFOS**`: Store comprehensive trade data including position sizes, leverage, prices, and timestamps for fee calculations. **Optimization and Controls:** * `**TRADER_STORED**` **and** `**USER_LIVE_TRADE_COUNTS**`: Optimization mechanisms that reduce gas costs and enable efficient trade ID assignment. * `**MINIMUM_POSITION_SIZE_USD**`: Sets minimum position thresholds to ensure economic viability and prevent dust trades. * `**TRADING_ACTIVATED**`: Critical safety feature supporting multiple activation levels (fully active, close-only, paused) for emergency control. [PreviousPerp: Price Impact Modulechevron-left](https://docs.sai.fun/for-devs/sai-core/perp-price-impact) [NextContract Addresses Referencechevron-right](https://docs.sai.fun/for-devs/sai-core/contracts) Last updated 3 months ago * [Borrowing](https://docs.sai.fun/for-devs/sai-core/perp-state#borrowing) * [Fees](https://docs.sai.fun/for-devs/sai-core/perp-state#fees) * [Referrer System](https://docs.sai.fun/for-devs/sai-core/perp-state#referrer-system) * [Trading Pairs and Groups](https://docs.sai.fun/for-devs/sai-core/perp-state#trading-pairs-and-groups) * [Price Impact and Open Interest](https://docs.sai.fun/for-devs/sai-core/perp-state#price-impact-and-open-interest) * [Trading](https://docs.sai.fun/for-devs/sai-core/perp-state#trading) --- # Perp: Price Impact Module | Sai.fun [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#key-features) Key Features -------------------------------------------------------------------------------------------------- * **Dynamic Calculation:** Computes price impact based on trade size and market depth. * **Open Interest Tracking:** Records open interest across multiple time windows. * **Market Depth Management:** Allows configuration of market depth parameters per trading pair. * **Time-Weighted Open Interest:** Uses a time-windowed approach to calculate cumulative open interest. * **Long/Short Handling:** Calculates price impact differently for long and short trades. [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#components) Components ---------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#state) State * `OI_WINDOWS_SETTINGS`: Settings for open interest windows. * `WINDOWS`: Stores open interest data for specific time windows. * `PAIR_DEPTHS`: Market depth information for trading pairs. * `TRADE_LAST_WINDOW_OI`: Tracks the last window's open interest for trades. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#functions) Functions * `get_trade_price_impact`: Calculates price impact for a trade. * `add_price_impact_open_interest`: Updates open interest when a trade is opened. * `remove_price_impact_open_interest`: Updates open interest when a trade is closed. * `get_price_impact_oi`: Retrieves current price impact open interest. [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#price-impact-calculation) Price Impact Calculation -------------------------------------------------------------------------------------------------------------------------- The price impact is calculated using this formula: Price Impact %\=(Start OI+Trade Size2)One Percent DepthPrice\\ Impact\\ \\% = \\frac{(Start\\ OI + \\frac{Trade\\ Size}{2})}{One\\ Percent\\ Depth}Price Impact %\=One Percent Depth(Start OI+2Trade Size​)​ * **Start OI**: Starting open interest for the relevant direction (long/short). * **Trade Size**: Size of the trade. * **One Percent Depth**: Trading volume needed to move the price by 1%. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#calculation-process) Calculation Process 1. **Fetch Market Depth**: The module retrieves the market depth from `PAIR_DEPTHS`. 2. **Calculate Current Open Interest**: It sums open interest across multiple time windows, giving more weight to recent windows. 3. **Compute Price Impact Percentage**: 4. **Apply Price Impact**: The calculated impact is applied to the trade's execution price: ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#example) Example Let's look at a long trade with these values: * **Open Price**: $1000 * **Trade Size**: $100,000 * **Current Open Interest**: $500,000 * **One Percent Depth**: $1,000,000 1. **Calculate price impact percentage**: 2. **Calculate price impact**: 3. **Apply price impact**: [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#open-interest-management) Open Interest Management -------------------------------------------------------------------------------------------------------------------------- The module uses a time-windowed approach for open interest. 1. **Window Configuration**: `OI_WINDOWS_SETTINGS` defines the duration and count of windows. 2. **Adding OI**: `add_price_impact_open_interest` updates the current window's open interest when a trade is opened. 3. **Removing OI**: `remove_price_impact_open_interest` adjusts open interest when a trade is closed. 4. **Time-Weighted Calculation**: Open interest from multiple recent windows is used for a more stable metric. [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#usage) Usage ------------------------------------------------------------------------------------ 1. Initialize `PAIR_DEPTHS` with market depth values. 2. Configure `OI_WINDOWS_SETTINGS`. 3. Call `get_trade_price_impact` to calculate price impact for a trade. 4. After execution, call `add_price_impact_open_interest` or `remove_price_impact_open_interest` to update open interest. [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#configuration) Configuration ---------------------------------------------------------------------------------------------------- Fine-tune the module by adjusting these parameters: * `one_percent_depth_above_usd` and `one_percent_depth_below_usd` in `PAIR_DEPTHS`. * `windows_count` and `windows_duration` in `OI_WINDOWS_SETTINGS`. [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#future-improvements) Future Improvements ---------------------------------------------------------------------------------------------------------------- * Implement dynamic adjustment of market depth. * Add support for asymmetric price impact. * Introduce more advanced time-weighting algorithms. [PreviousPerp: Borrowing Modulechevron-left](https://docs.sai.fun/for-devs/sai-core/perp-borrowing) [NextPerp: State Variableschevron-right](https://docs.sai.fun/for-devs/sai-core/perp-state) Last updated 4 months ago * [Key Features](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#key-features) * [Components](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#components) * [State](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#state) * [Functions](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#functions) * [Price Impact Calculation](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#price-impact-calculation) * [Calculation Process](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#calculation-process) * [Example](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#example) * [Open Interest Management](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#open-interest-management) * [Usage](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#usage) * [Configuration](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#configuration) * [Future Improvements](https://docs.sai.fun/for-devs/sai-core/perp-price-impact#future-improvements) Copy let price_impact_p = Decimal::from_ratio( start_open_interest_usd + trade_open_interest_usd.checked_div(2_u64.into())?, one_percent_depth_usd ).checked_div(Decimal::from_atomics(100_u64, 0)?)?; Copy let price_after_impact = if long { open_price + price_impact } else { open_price - price_impact }; Copy Price Impact % = (500,000 + 100,000 / 2) / 1,000,000 / 100 = 0.55% Copy Price Impact = $1000 * 0.55% = $5.50 Copy Price After Impact = $1000 + $5.50 = $1005.50 --- # Acquiring Collateral | Sai.fun Sai accepts two types of collateral: * **USDC.e** (bridged USDC) * **stNIBI** (staked Nibiru tokens) You'll also need **NIBI** for gas fees to perform transactions on Sai. * * * [hashtag](https://docs.sai.fun/guides/acquiring-collateral#getting-nibi-gas-token) Getting NIBI (Gas Token) ---------------------------------------------------------------------------------------------------------------- NIBI is required for all transactions on Sai. You can acquire it through: ### [hashtag](https://docs.sai.fun/guides/acquiring-collateral#centralized-exchanges-cex) Centralized Exchanges (CEX) Exchange Link ByBit [Buy NIBIarrow-up-right](https://www.bybit.com/en/) Kucoin [Buy NIBIarrow-up-right](https://www.kucoin.com/) Gate.io [Buy NIBIarrow-up-right](https://www.gate.io/) MEXC [Buy NIBIarrow-up-right](https://www.mexc.com/) ### [hashtag](https://docs.sai.fun/guides/acquiring-collateral#decentralized-exchanges-dex) Decentralized Exchanges (DEX) DEX Network Link Oku EVM [Trade on Okuarrow-up-right](https://oku.trade/swap?inputChain=nibiru&inToken=0x0829F361A05D993d5CEb035cA6DF3446b060970b&outToken=0x0CaCF669f8446BeCA826913a3c6B96aCD4b02a97) Osmosis WasmVM [Trade on Osmosisarrow-up-right](https://app.osmosis.zone/) Astrovault WasmVM [Trade on Astrovaultarrow-up-right](https://astrovault.io/) ### [hashtag](https://docs.sai.fun/guides/acquiring-collateral#bridging) Bridging Bridge Link Nibiru [Bridgearrow-up-right](https://app.nibiru.fi/bridge) Stargate [Bridgearrow-up-right](https://stargate.finance/?srcChain=ethereum&srcToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&dstChain=nibiru&dstToken=0x0829F361A05D993d5CEb035cA6DF3446b060970b) * * * [hashtag](https://docs.sai.fun/guides/acquiring-collateral#getting-stnibi-staked-collateral) Getting stNIBI (Staked Collateral) ------------------------------------------------------------------------------------------------------------------------------------ stNIBI is Nibiru's liquid staking token and serves as collateral on Sai. ### [hashtag](https://docs.sai.fun/guides/acquiring-collateral#step-1-acquire-nibi) Step 1: Acquire NIBI Follow the steps above to get NIBI tokens. ### [hashtag](https://docs.sai.fun/guides/acquiring-collateral#step-2-liquid-stake-nibi) Step 2: Liquid Stake NIBI 1. Go to [app.nibiru.fi/stakearrow-up-right](https://app.nibiru.fi/stake) 2. Connect your wallet (works with both EVM and WasmVM wallets) 3. Stake your NIBI to receive stNIBI ### [hashtag](https://docs.sai.fun/guides/acquiring-collateral#step-3-cross-chain-asset-conversion-optional) Step 3: Cross-Chain Asset Conversion (Optional) If you need to move stNIBI between EVM and WasmVM networks: 1. Go to [app.nibiru.fi/portfolioarrow-up-right](https://app.nibiru.fi/portfolio) 2. Select the stNIBI asset from your table 3. Use the **FunToken** conversion to: * Convert stNIBI ERC20 (EVM) → stNIBI Bank Coin (WasmVM) * Convert stNIBI Bank Coin (WasmVM) → stNIBI ERC20 (EVM) ### [hashtag](https://docs.sai.fun/guides/acquiring-collateral#alternative-trade-stnibi-on-dex) Alternative: Trade stNIBI on DEX Acquire stNIBI directly from decentralized exchanges: DEX Network Link Oku EVM [Trade stNIBIarrow-up-right](https://oku.trade/swap?inputChain=nibiru&inToken=0x0829F361A05D993d5CEb035cA6DF3446b060970b&outToken=0xcA0a9Fb5FBF692fa12fD13c0A900EC56Bb3f0a7b) Stargate EVM [Trade stNIBIarrow-up-right](https://stargate.finance/) Astrovault WasmVM [Trade stNIBIarrow-up-right](https://astrovault.io/) Skip Swap WasmVM [Trade stNIBIarrow-up-right](https://app.skip.money/) * * * [hashtag](https://docs.sai.fun/guides/acquiring-collateral#getting-usdc.e-bridged-usdc) Getting USDC.e (Bridged USDC) -------------------------------------------------------------------------------------------------------------------------- USDC.e is ERC20-standard USDC bridged to Nibiru. ### [hashtag](https://docs.sai.fun/guides/acquiring-collateral#bridge-from-other-chains) Bridge from Other Chains Use these bridging protocols to get USDC.e: Source Bridge Link Base USDC Stargate (LayerZero) [Bridge Base USDCarrow-up-right](hhttps://stargate.finance/?srcChain=base&srcToken=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913&dstChain=nibiru&dstToken=0x0829F361A05D993d5CEb035cA6DF3446b060970b) Arbitrum USDC Stargate (LayerZero) [Bridge ARB USDCarrow-up-right](https://stargate.finance/?srcChain=arbitrum&srcToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831&dstChain=nibiru&dstToken=0x0829F361A05D993d5CEb035cA6DF3446b060970b) Avalache USDC Stargate (LayerZero) [Bridge AVAX USDCarrow-up-right](https://stargate.finance/?srcChain=arbitrum&srcToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831&dstChain=nibiru&dstToken=0x0829F361A05D993d5CEb035cA6DF3446b060970b) ### [hashtag](https://docs.sai.fun/guides/acquiring-collateral#cross-chain-asset-conversion-optional) Cross-Chain Asset Conversion (Optional) If you need to move USDC.e between EVM and WasmVM networks: 1. Go to [app.nibiru.fi/portfolioarrow-up-right](https://app.nibiru.fi/portfolio) 2. Select the USDC.e asset from your table 3. Use the **FunToken** conversion to: * Convert USDC.e ERC20 (EVM) → USDC bank coin (WasmVM) * Convert USDC bank coin (WasmVM) → USDC.e ERC20 (EVM) * * * [hashtag](https://docs.sai.fun/guides/acquiring-collateral#quick-reference) Quick Reference ------------------------------------------------------------------------------------------------ Collateral Acquisition Staking Required Cross-Chain Support NIBI CEX, DEX, Bridge No Yes (via FunToken) stNIBI Stake NIBI or DEX Yes Yes (via FunToken) USDC.e Bridge or CEX No Yes (via FunToken) * * * [hashtag](https://docs.sai.fun/guides/acquiring-collateral#need-help) Need Help? ------------------------------------------------------------------------------------- * Issues with bridging? Check [Wallet Setup](https://docs.sai.fun/guides/wallet-setup) * Questions about staking? See [Nibiru Docsarrow-up-right](https://nibiru.fi/docs) * Having trouble? Visit [FAQ](https://docs.sai.fun/learn/trading/faq) [PreviousWallet Setupchevron-left](https://docs.sai.fun/guides/wallet-setup) [NextChoosing a Marketchevron-right](https://docs.sai.fun/guides/choosing-market) Last updated 16 days ago * [Getting NIBI (Gas Token)](https://docs.sai.fun/guides/acquiring-collateral#getting-nibi-gas-token) * [Centralized Exchanges (CEX)](https://docs.sai.fun/guides/acquiring-collateral#centralized-exchanges-cex) * [Decentralized Exchanges (DEX)](https://docs.sai.fun/guides/acquiring-collateral#decentralized-exchanges-dex) * [Bridging](https://docs.sai.fun/guides/acquiring-collateral#bridging) * [Getting stNIBI (Staked Collateral)](https://docs.sai.fun/guides/acquiring-collateral#getting-stnibi-staked-collateral) * [Step 1: Acquire NIBI](https://docs.sai.fun/guides/acquiring-collateral#step-1-acquire-nibi) * [Step 2: Liquid Stake NIBI](https://docs.sai.fun/guides/acquiring-collateral#step-2-liquid-stake-nibi) * [Step 3: Cross-Chain Asset Conversion (Optional)](https://docs.sai.fun/guides/acquiring-collateral#step-3-cross-chain-asset-conversion-optional) * [Alternative: Trade stNIBI on DEX](https://docs.sai.fun/guides/acquiring-collateral#alternative-trade-stnibi-on-dex) * [Getting USDC.e (Bridged USDC)](https://docs.sai.fun/guides/acquiring-collateral#getting-usdc.e-bridged-usdc) * [Bridge from Other Chains](https://docs.sai.fun/guides/acquiring-collateral#bridge-from-other-chains) * [Cross-Chain Asset Conversion (Optional)](https://docs.sai.fun/guides/acquiring-collateral#cross-chain-asset-conversion-optional) * [Quick Reference](https://docs.sai.fun/guides/acquiring-collateral#quick-reference) * [Need Help?](https://docs.sai.fun/guides/acquiring-collateral#need-help) --- # Sai Disclosures | Sai.fun [hashtag](https://docs.sai.fun/resources/legal/disclosures#sai-is-independent-from-nibiru) Sai is Independent from Nibiru ------------------------------------------------------------------------------------------------------------------------------ Sai is run on the Nibiru chain, but is otherwise owned and managed by entities independent from Nibiru. Sai and Nibiru each have their own terms of use. Any user of either must read and agree to the respective terms of use prior to using. [hashtag](https://docs.sai.fun/resources/legal/disclosures#no-offer-or-solicitation) No Offer or Solicitation ------------------------------------------------------------------------------------------------------------------ Any content, promotion, statements regarding, or other material about Sai ("Content") does not constitute an offer to sell or a solicitation of an offer to buy any securities, tokens, or any other form of investment in any jurisdiction. The distribution or dissemination of this document may be restricted by law in certain jurisdictions, and it is the responsibility of any person in possession of this Update to comply with any such laws and regulations. [hashtag](https://docs.sai.fun/resources/legal/disclosures#nibi-in-any-form-provides-no-rights-to-holders) NIBI In Any Form Provides No Rights to Holders -------------------------------------------------------------------------------------------------------------------------------------------------------------- Purchase of NIBI tokens in any form or any other digital assets mentioned in any Content (each and collectively the "Tokens") does not represent or confer any ownership right or stake, share, security, or equivalent rights, or any right to receive future revenue shares, dividends, intellectual property rights or any other form of participation in or relating to Nibiru, any of its affiliates, Nibiru and its related products, and/or services or any part thereof. The reader acknowledges and accepts that at no time and under no circumstances shall they be entitled, as a holder of any Tokens, to vote, receive dividends or be deemed the holder of equity or capital stock of any entity for any purpose, nor will anything contained herein be construed to confer on the reader such rights. [hashtag](https://docs.sai.fun/resources/legal/disclosures#forward-looking-statements) Forward-Looking Statements ---------------------------------------------------------------------------------------------------------------------- Certain statements contained in Content may be forward-looking, including but not limited to plans, goals, and expectations regarding the future business, operations, and performance of the project. These statements are based on current beliefs, assumptions, and projections and are subject to risks, uncertainties, and changes beyond the control of the project. Actual results may differ materially from those expressed or implied in any forward-looking statements. [hashtag](https://docs.sai.fun/resources/legal/disclosures#regulatory-status) Regulatory Status ---------------------------------------------------------------------------------------------------- The regulatory status of tokens and blockchain technology, digital assets, and cryptocurrencies generally is uncertain and evolving. Content and the project described within may be impacted by legal, regulatory, and compliance requirements. It is the responsibility of potential participants to determine whether they can legally acquire, hold, or participate in Nibiru's activities, including Sai, under the laws of their jurisdiction. [hashtag](https://docs.sai.fun/resources/legal/disclosures#no-liability) No Liability ------------------------------------------------------------------------------------------ Nibiru, its affiliates, contractors, and their respective officers, employees, and agents shall not be held liable for any loss, damage, or liability arising out of or in connection with the use of Content, the project, or any associated products or services. Participation in the project and any related activities is done at your own risk. [hashtag](https://docs.sai.fun/resources/legal/disclosures#no-guarantees) No Guarantees -------------------------------------------------------------------------------------------- There is no guarantee or assurance that the project, its platform, or any tokens described in Content will achieve any of its goals, intended outcomes, or objectives. The value and functionality of any Tokens are not guaranteed, and they may be subject to significant volatility, market forces, and other risks. [hashtag](https://docs.sai.fun/resources/legal/disclosures#risk-factors-trading-on-sai-is-risky) Risk Factors - Trading on Sai is Risky -------------------------------------------------------------------------------------------------------------------------------------------- Participation in Sai and the use of tokens involve significant risks, including but not limited to financial, regulatory, technological, and market risks. It is strongly recommended that participants fully understand these risks before engaging with the project or acquiring any tokens. Trading in perpetuals is particularly risky, and one should only engage in such trading, or any other activities on Sai, if significantly experienced. Any funds or assets of value connected or used with the Sai platform could be lost, and a user should only connect or use assets or funds that they can afford to lose. [hashtag](https://docs.sai.fun/resources/legal/disclosures#independent-advice) Independent Advice ------------------------------------------------------------------------------------------------------ All should seek independent professional advice regarding their individual circumstances before engaging in any activity related to Nibiru or Sai. [hashtag](https://docs.sai.fun/resources/legal/disclosures#amendments-and-updates) Amendments and Updates -------------------------------------------------------------------------------------------------------------- Nibiru reserves the right to amend, modify, or update any Content at any time without prior notice. It is the responsibility of readers to stay informed of any changes. [PreviousTerms of Usechevron-left](https://docs.sai.fun/resources/legal/terms-of-use) Last updated 13 days ago * [Sai is Independent from Nibiru](https://docs.sai.fun/resources/legal/disclosures#sai-is-independent-from-nibiru) * [No Offer or Solicitation](https://docs.sai.fun/resources/legal/disclosures#no-offer-or-solicitation) * [NIBI In Any Form Provides No Rights to Holders](https://docs.sai.fun/resources/legal/disclosures#nibi-in-any-form-provides-no-rights-to-holders) * [Forward-Looking Statements](https://docs.sai.fun/resources/legal/disclosures#forward-looking-statements) * [Regulatory Status](https://docs.sai.fun/resources/legal/disclosures#regulatory-status) * [No Liability](https://docs.sai.fun/resources/legal/disclosures#no-liability) * [No Guarantees](https://docs.sai.fun/resources/legal/disclosures#no-guarantees) * [Risk Factors - Trading on Sai is Risky](https://docs.sai.fun/resources/legal/disclosures#risk-factors-trading-on-sai-is-risky) * [Independent Advice](https://docs.sai.fun/resources/legal/disclosures#independent-advice) * [Amendments and Updates](https://docs.sai.fun/resources/legal/disclosures#amendments-and-updates) --- # WASM Integration Guide | Sai.fun ![Page cover](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252FYyFLT3iIOqFGDN2JP9u9%252Fsai-banner-guides-1500x500.png%3Falt%3Dmedia%26token%3Da7262a89-4e22-4bf9-aa82-27b06588f2ae&width=1248&dpr=3&quality=100&sign=3ad5d264&sv=2) End-to-end instructions for interacting with **Sai** from the Cosmos (WASM) side using `NibiruTxClient`. * * * [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#id-0.-getting-started) 0\. Getting Started ------------------------------------------------------------------------------------------------------ ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#prerequisites) Prerequisites * Node.js ≥ 18 * pnpm, npm, or yarn ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#new-project) New Project Copy mkdir sai-wasm && cd sai-wasm pnpm init -y pnpm add @nibiruchain/nibijs @cosmjs/cosmwasm-stargate @cosmjs/stargate cosmjs-types bignumber.js pnpm add -D typescript ts-node @types/node dotenv npx tsc --init --target ES2022 --module NodeNext --moduleResolution NodeNext ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#environment-setup) Environment Setup Create a `.env` file with your configuration: Copy # Testnet-1 NIBI_RPC=https://rpc.testnet-1.nibiru.fi/ # Or Mainnet # NIBI_RPC=https://rpc.nibiru.fi/ MNEMONIC="word1 word2 ... word12" # Your wallet mnemonic (keep this secret!) ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#bootstrap-client) Bootstrap Client * * * [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#id-1.-collateral-payment-tokens) 1\. Collateral / Payment Tokens ---------------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#decimal-handling) Decimal Handling Default decimals for BANK amounts are **6**. Always scale display amounts to BANK units by multiplying by 10^6. ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#testnet-1) Testnet-1 Token Bank Denom **USDC** `tf/nibi1pc2mmwcqhvzn9vsm0umpu40yzl6gfy6nucwn7g/usdc` **stNIBI** `tf/nibi1pc2mmwcqhvzn9vsm0umpu40yzl6gfy6nucwn7g/stnibi` ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#mainnet) Mainnet Token Bank Denom **USDC** `erc20/0x0829F361A05D993d5CEb035cA6DF3446b060970b` **stNIBI** `tf/nibi1udqqx30cw8nwjxtl4l28ym9hhrp933zlq8dqxfjzcdhvl8y24zcqpzmh8m/ampNIBI` ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#converting-display-to-bank-units) Converting Display to BANK Units * * * [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#id-2.-perps-open-trade) 2\. Perps – Open Trade ---------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#message-structure) Message Structure ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#rules) Rules * **Market trades** require `open_price` set to the current market price * **Limit/Stop trades** require `open_price` set to the trigger price ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#example) Example * * * [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#id-3.-perps-close-trade) 3\. Perps – Close Trade ------------------------------------------------------------------------------------------------------------ Close an open position: * * * [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#id-4.-referral-create-and-redeem-codes) 4\. Referral – Create & Redeem Codes ---------------------------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#create-a-referral-code) Create a Referral Code ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#redeem-a-referral-code) Redeem a Referral Code * * * [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#id-5.-vault-deposit) 5\. Vault – Deposit ---------------------------------------------------------------------------------------------------- Deposit collateral into a vault: * * * [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#id-6.-vault-make-withdraw-request) 6\. Vault – Make Withdraw Request -------------------------------------------------------------------------------------------------------------------------------- Request to withdraw funds from a vault: * * * [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#id-7.-vault-redeem) 7\. Vault – Redeem -------------------------------------------------------------------------------------------------- Redeem shares from a vault: * * * [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#id-8.-vault-cancel-withdraw-request) 8\. Vault – Cancel Withdraw Request ------------------------------------------------------------------------------------------------------------------------------------ Cancel a pending withdraw request: * * * [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#query-examples) Query Examples ------------------------------------------------------------------------------------------ ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#query-vault-share-denom) Query Vault Share Denom ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#query-user-trades) Query User Trades ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#query-vault-info) Query Vault Info * * * [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#error-handling-and-troubleshooting) Error Handling & Troubleshooting -------------------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#common-issues) Common Issues Issue Solution **"No account found"** Ensure your mnemonic is valid and `NibiruTxClient` is properly initialized **"Insufficient funds"** Verify you have enough tokens (in BANK units) in your wallet **"Invalid denom"** Double-check the `bankDenom` matches your network (Testnet-1 vs Mainnet) **"Contract not found"** Ensure the contract address is correct for your network **Decimal conversion errors** Always multiply display amounts by 10^6 before passing to WASM **Transaction timeout** Increase the timeout or check RPC connectivity ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#best-practices) Best Practices * Always ensure the `NibiruTxClient` is connected before executing transactions * Validate that amounts are non-negative integers in BANK units * Use `"auto"` for gas estimation unless you have specific gas requirements * Store your mnemonic securely and never commit it to version control * Always query contract state before making assumptions about parameters * Test on Testnet-1 before moving to Mainnet ### [hashtag](https://docs.sai.fun/for-devs/dev/wasm-guide#debugging-tips) Debugging Tips * Check transaction details: `client.getTx(transactionHash)` * Inspect error messages: Transaction results include detailed error reasons * Use logs: Add `console.log()` statements to trace execution flow * Monitor gas: The transaction response includes gas used and fees paid [PreviousEVM Integration Guidechevron-left](https://docs.sai.fun/for-devs/dev/evm-guide) [NextSai Corechevron-right](https://docs.sai.fun/for-devs/sai-core) Last updated 3 months ago * [0\. Getting Started](https://docs.sai.fun/for-devs/dev/wasm-guide#id-0.-getting-started) * [Prerequisites](https://docs.sai.fun/for-devs/dev/wasm-guide#prerequisites) * [New Project](https://docs.sai.fun/for-devs/dev/wasm-guide#new-project) * [Environment Setup](https://docs.sai.fun/for-devs/dev/wasm-guide#environment-setup) * [Bootstrap Client](https://docs.sai.fun/for-devs/dev/wasm-guide#bootstrap-client) * [1\. Collateral / Payment Tokens](https://docs.sai.fun/for-devs/dev/wasm-guide#id-1.-collateral-payment-tokens) * [Decimal Handling](https://docs.sai.fun/for-devs/dev/wasm-guide#decimal-handling) * [Testnet-1](https://docs.sai.fun/for-devs/dev/wasm-guide#testnet-1) * [Mainnet](https://docs.sai.fun/for-devs/dev/wasm-guide#mainnet) * [Converting Display to BANK Units](https://docs.sai.fun/for-devs/dev/wasm-guide#converting-display-to-bank-units) * [2\. Perps – Open Trade](https://docs.sai.fun/for-devs/dev/wasm-guide#id-2.-perps-open-trade) * [Message Structure](https://docs.sai.fun/for-devs/dev/wasm-guide#message-structure) * [Rules](https://docs.sai.fun/for-devs/dev/wasm-guide#rules) * [Example](https://docs.sai.fun/for-devs/dev/wasm-guide#example) * [3\. Perps – Close Trade](https://docs.sai.fun/for-devs/dev/wasm-guide#id-3.-perps-close-trade) * [4\. Referral – Create & Redeem Codes](https://docs.sai.fun/for-devs/dev/wasm-guide#id-4.-referral-create-and-redeem-codes) * [Create a Referral Code](https://docs.sai.fun/for-devs/dev/wasm-guide#create-a-referral-code) * [Redeem a Referral Code](https://docs.sai.fun/for-devs/dev/wasm-guide#redeem-a-referral-code) * [5\. Vault – Deposit](https://docs.sai.fun/for-devs/dev/wasm-guide#id-5.-vault-deposit) * [6\. Vault – Make Withdraw Request](https://docs.sai.fun/for-devs/dev/wasm-guide#id-6.-vault-make-withdraw-request) * [7\. Vault – Redeem](https://docs.sai.fun/for-devs/dev/wasm-guide#id-7.-vault-redeem) * [8\. Vault – Cancel Withdraw Request](https://docs.sai.fun/for-devs/dev/wasm-guide#id-8.-vault-cancel-withdraw-request) * [Query Examples](https://docs.sai.fun/for-devs/dev/wasm-guide#query-examples) * [Query Vault Share Denom](https://docs.sai.fun/for-devs/dev/wasm-guide#query-vault-share-denom) * [Query User Trades](https://docs.sai.fun/for-devs/dev/wasm-guide#query-user-trades) * [Query Vault Info](https://docs.sai.fun/for-devs/dev/wasm-guide#query-vault-info) * [Error Handling & Troubleshooting](https://docs.sai.fun/for-devs/dev/wasm-guide#error-handling-and-troubleshooting) * [Common Issues](https://docs.sai.fun/for-devs/dev/wasm-guide#common-issues) * [Best Practices](https://docs.sai.fun/for-devs/dev/wasm-guide#best-practices) * [Debugging Tips](https://docs.sai.fun/for-devs/dev/wasm-guide#debugging-tips) Copy // src/wasmClient.ts import "dotenv/config" import { NibiruTxClient } from "@nibiruchain/nibijs" export async function makeNibiruTxClient() { const rpc = process.env.NIBI_RPC! const mnemonic = process.env.MNEMONIC! const client = await NibiruTxClient.connectWithSignerFromMnemonic(rpc, mnemonic) const account = (await client.signer.getAccounts())[0] return { client, bech32: account.address } } Copy import BigNumber from "bignumber.js" const displayAmount = new BigNumber("100") // 100 USDC const bankAmount = displayAmount.times(1e6).integerValue().toString() // Result: "100000000" (100 * 10^6) Copy { "open_trade": { "market_index": "MarketIndex(N)", "leverage": "string (e.g., '5')", "long": true, "collateral_index": "TokenIndex(N)", "trade_type": "trade|limit|stop", "open_price": "", "slippage_p": "1", "tp": "optional take-profit", "sl": "optional stop-loss", "is_evm_origin": false } } Copy import BigNumber from "bignumber.js" import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin" import { makeNibiruTxClient } from "./wasmClient" async function openTrade() { const { client, bech32 } = await makeNibiruTxClient() // Contract addresses (from chain config) const saiPerp = "" // bech32 address const bankDenom = "tf/nibi1pc2mmwcqhvzn9vsm0umpu40yzl6gfy6nucwn7g/usdc" // Testnet-1 USDC // Convert 100 USDC to BANK units const display = new BigNumber("100") const amount = display.times(1e6).integerValue().toString() // "100000000" const msg = { open_trade: { market_index: "MarketIndex(0)", leverage: "5", long: true, collateral_index: "TokenIndex(0)", trade_type: "trade", open_price: "70000", slippage_p: "1", is_evm_origin: false, }, } const res = await client.wasmClient.execute( bech32, saiPerp, msg, "auto", undefined, [Coin.fromPartial({ denom: bankDenom, amount })] ) console.log("Transaction hash:", res.transactionHash) console.log("Gas used:", res.gasUsed) } Copy import { makeNibiruTxClient } from "./wasmClient" async function closeTrade() { const { client, bech32 } = await makeNibiruTxClient() const saiPerp = "" const res = await client.wasmClient.execute( bech32, saiPerp, { close_trade: { trade_index: "UserTradeIndex(0)", }, }, "auto" ) console.log("Trade closed in tx:", res.transactionHash) } Copy async function createReferralCode() { const { client, bech32 } = await makeNibiruTxClient() const saiPerp = "" const res = await client.wasmClient.execute( bech32, saiPerp, { create_referrer_code: { code: "MYCODE", }, }, "auto" ) console.log("Referral code created in tx:", res.transactionHash) } Copy async function redeemReferralCode() { const { client, bech32 } = await makeNibiruTxClient() const saiPerp = "" const res = await client.wasmClient.execute( bech32, saiPerp, { redeem_referrer_code: { code: "PARTNER", }, }, "auto" ) console.log("Referral code redeemed in tx:", res.transactionHash) } Copy import BigNumber from "bignumber.js" import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin" import { makeNibiruTxClient } from "./wasmClient" async function depositToVault() { const { client, bech32 } = await makeNibiruTxClient() const vaultAddr = "" const bankDenom = "tf/nibi1pc2mmwcqhvzn9vsm0umpu40yzl6gfy6nucwn7g/usdc" // Testnet-1 USDC // Convert 250 USDC to BANK units const amount = new BigNumber("250") .times(1e6) .integerValue() .toString() const res = await client.wasmClient.execute( bech32, vaultAddr, { deposit: {} }, "auto", undefined, [Coin.fromPartial({ denom: bankDenom, amount })] ) console.log("Deposited to vault in tx:", res.transactionHash) } Copy import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin" import { makeNibiruTxClient } from "./wasmClient" async function makeWithdrawRequest() { const { client, bech32 } = await makeNibiruTxClient() const vaultAddr = "" // First, query the vault to get the share denom const shareDenom = await client.wasmClient.queryContractSmart(vaultAddr, { get_vault_share_denom: {}, }) console.log("Share denom:", shareDenom) // Request to withdraw 1.0 share (1,000,000 in BANK units) const res = await client.wasmClient.execute( bech32, vaultAddr, { make_withdraw_request: {} }, "auto", undefined, [Coin.fromPartial({ denom: shareDenom, amount: "1000000" })] ) console.log("Withdraw request created in tx:", res.transactionHash) } Copy import { makeNibiruTxClient } from "./wasmClient" async function redeemVaultShares() { const { client, bech32 } = await makeNibiruTxClient() const vaultAddr = "" // Redeem 0.5 shares (500,000 in BANK units) const res = await client.wasmClient.execute( bech32, vaultAddr, { redeem: { shares: "500000", }, }, "auto" ) console.log("Shares redeemed in tx:", res.transactionHash) } Copy import { makeNibiruTxClient } from "./wasmClient" async function cancelWithdrawRequest() { const { client, bech32 } = await makeNibiruTxClient() const vaultAddr = "" const res = await client.wasmClient.execute( bech32, vaultAddr, { cancel_withdraw_request: { unlock_epoch: 123, // Replace with actual unlock epoch }, }, "auto" ) console.log("Withdraw request cancelled in tx:", res.transactionHash) } Copy async function getVaultShareDenom() { const { client } = await makeNibiruTxClient() const vaultAddr = "" const shareDenom = await client.wasmClient.queryContractSmart(vaultAddr, { get_vault_share_denom: {}, }) console.log("Share denom:", shareDenom) return shareDenom } Copy async function getUserTrades() { const { client, bech32 } = await makeNibiruTxClient() const saiPerp = "" const trades = await client.wasmClient.queryContractSmart(saiPerp, { get_user_trades: { user: bech32, }, }) console.log("User trades:", trades) return trades } Copy async function getVaultInfo() { const { client } = await makeNibiruTxClient() const vaultAddr = "" const info = await client.wasmClient.queryContractSmart(vaultAddr, { get_info: {}, }) console.log("Vault info:", info) return info } --- # Getting Started | Sai.fun ![Page cover](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fs6tMymGljPk7rDj9Ueg1%252Fsai-twitter-banner-1500x500.jpg%3Falt%3Dmedia%26token%3D48da11c3-5690-41a4-b66e-2bde9973f91d&width=1248&dpr=3&quality=100&sign=a054301c&sv=2) Build powerful applications on top of Sai's perpetual futures protocol. Whether you're creating trading bots, analytics dashboards, or custom integrations, this documentation will guide you through the process. [hashtag](https://docs.sai.fun/for-devs/dev#quick-start) Quick Start ------------------------------------------------------------------------- Choose your integration path based on your development environment: [](https://docs.sai.fun/for-devs/dev/evm-guide) **EVM Integration Guide** Start here if you're building on EVM chains. Covers environment setup, contract connections, and basic operations like opening positions and managing collateral [](https://docs.sai.fun/for-devs/dev/wasm-guide) **WASM Integration Guide** For WASM-based chains and clients. Learn integration patterns with language-specific examples and deployment guidance * * * [hashtag](https://docs.sai.fun/for-devs/dev#sai-core) Sai Core ------------------------------------------------------------------- Comprehensive documentation of the Sai smart contracts, modules, and architecture. ### [hashtag](https://docs.sai.fun/for-devs/dev#core-modules) Core Modules [](https://docs.sai.fun/for-devs/sai-core/perp-contract) **Perp Contract** Main perpetual futures contract handling position management, collateral, and settlement [](https://docs.sai.fun/for-devs/sai-core/perp-borrowing) **Borrowing Module** Borrowed funds management, interest rates, and liquidation mechanics [](https://docs.sai.fun/for-devs/sai-core/perp-price-impact) **Price Impact Module** How large trades affect pricing, slippage calculation, and execution costs ### [hashtag](https://docs.sai.fun/for-devs/dev#reference-documentation) Reference Documentation [](https://docs.sai.fun/for-devs/sai-core/perp-state) **State Variables** Complete reference of all smart contract state variables and their purposes [](https://docs.sai.fun/for-devs/sai-core/contracts) **Contract Addresses Reference** Mainnet and testnet deployment addresses for all Sai contracts [](https://docs.sai.fun/for-devs/sai-core/contract-integration) **WASM & EVM Integration** Platform-specific details for direct smart contract integration * * * [hashtag](https://docs.sai.fun/for-devs/dev#sai-keeper) Sai Keeper ----------------------------------------------------------------------- Query and subscribe to real-time data from the Sai protocol. Perfect for dashboards, bots, and analytics tools. ### [hashtag](https://docs.sai.fun/for-devs/dev#getting-started) Getting Started [](https://docs.sai.fun/for-devs/sai-keeper/core-concepts) **Core Concepts** Understand indexing, subscriptions, and how Keeper structures protocol data [](https://docs.sai.fun/for-devs/sai-keeper/client-setup) **Client Setup** Get your GraphQL client configured and authenticated in minutes ### [hashtag](https://docs.sai.fun/for-devs/dev#api-reference) API Reference [](https://docs.sai.fun/for-devs/sai-keeper/api-perp) **Perp API** Query positions, orders, trades, and perpetual-specific data [](https://docs.sai.fun/for-devs/sai-keeper/api-lp) **Liquidity Provider (LP) API** Access liquidity pool data, provider positions, and performance metrics [](https://docs.sai.fun/for-devs/sai-keeper/api-oracle) **Oracle API** Get price feeds and oracle data used throughout the protocol [](https://docs.sai.fun/for-devs/sai-keeper/api-fees) **Fees API** Query fee structures, accruals, and historical fee data ### [hashtag](https://docs.sai.fun/for-devs/dev#examples-and-advanced-usage) Examples & Advanced Usage [](https://docs.sai.fun/for-devs/sai-keeper/examples-queries) **Examples: Queries** Copy-paste ready GraphQL queries for common use cases [](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions) **Examples: Subscriptions** See how to subscribe to real-time updates and build event-driven applications [](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination) **Filters & Pagination** Master filtering, sorting, and pagination for efficient large-scale data retrieval * * * [hashtag](https://docs.sai.fun/for-devs/dev#use-cases) Use Cases --------------------------------------------------------------------- Practical integration examples and real-world applications built on Sai. [](https://docs.sai.fun/for-devs/use-cases/tg-guide) **Telegram Bot Integration** Build a Telegram trading bot with position monitoring, alerts, and order execution * * * [hashtag](https://docs.sai.fun/for-devs/dev#developer-resources) Developer Resources ----------------------------------------------------------------------------------------- **Need help or want to connect with other builders?** * 🐦 [X / Twitterarrow-up-right](https://x.com/SaiDotFun) - @SaiDotFun * 📢 [Telegram News Channelarrow-up-right](https://t.me/saidotfun) - t.me/saidotfun **Additional Resources:** * [Contract Addresses](https://docs.sai.fun/for-devs/sai-core/contracts) - Quick reference for all deployed contracts * [GraphQL Playgroundarrow-up-right](https://sai-keeper.nibiru.fi/) - Test queries interactively * * * circle-check **Ready to build?** Start with the [EVM Integration Guide](https://docs.sai.fun/for-devs/dev/evm-guide) or [WASM Integration Guide](https://docs.sai.fun/for-devs/dev/wasm-guide) based on your development stack. [PreviousSai Referral Programchevron-left](https://docs.sai.fun/guides/referral) [NextEVM Integration Guidechevron-right](https://docs.sai.fun/for-devs/dev/evm-guide) Last updated 6 days ago * [Quick Start](https://docs.sai.fun/for-devs/dev#quick-start) * [Sai Core](https://docs.sai.fun/for-devs/dev#sai-core) * [Core Modules](https://docs.sai.fun/for-devs/dev#core-modules) * [Reference Documentation](https://docs.sai.fun/for-devs/dev#reference-documentation) * [Sai Keeper](https://docs.sai.fun/for-devs/dev#sai-keeper) * [Getting Started](https://docs.sai.fun/for-devs/dev#getting-started) * [API Reference](https://docs.sai.fun/for-devs/dev#api-reference) * [Examples & Advanced Usage](https://docs.sai.fun/for-devs/dev#examples-and-advanced-usage) * [Use Cases](https://docs.sai.fun/for-devs/dev#use-cases) * [Developer Resources](https://docs.sai.fun/for-devs/dev#developer-resources) --- # Perp Contract | Sai.fun * **Events** are not covered in this guide. * **Borrowing logic** is described thoroughly here in the [Perp: Borrowing Module](https://docs.sai.fun/for-devs/sai-core/perp-borrowing) specification. * **Fees logic** is described thoroughly in [./fees/README.mdarrow-up-right](https://github.com/NibiruChain/sai-docs/blob/main/sai-core/smart-contracts/src/fees/README.md) . * **Price impact** logic is described thoroughly in [Perp: Price Impact Module](https://docs.sai.fun/for-devs/sai-core/perp-price-impact) . * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#table-of-contents) Table of Contents -------------------------------------------------------------------------------------------------------- * [Developer Documentation: Perpetual Futures Contract](https://docs.sai.fun/for-devs/sai-core/perp-contract#developer-documentation-perpetual-futures-contract) * [Table of Contents](https://docs.sai.fun/for-devs/sai-core/perp-contract#table-of-contents) * [Overview](https://docs.sai.fun/for-devs/sai-core/perp-contract#overview) * [Repository Layout](https://docs.sai.fun/for-devs/sai-core/perp-contract#repository-layout) * [Key Concepts](https://docs.sai.fun/for-devs/sai-core/perp-contract#key-concepts) * [Trade Lifecycle](https://docs.sai.fun/for-devs/sai-core/perp-contract#trade-lifecycle) * [Contract Entry Points](https://docs.sai.fun/for-devs/sai-core/perp-contract#contract-entry-points) * [Index Types](https://docs.sai.fun/for-devs/sai-core/perp-contract#index-types) * [Inter-Contract Interactions](https://docs.sai.fun/for-devs/sai-core/perp-contract#inter-contract-interactions) * [Messages (`ExecuteMsg` and `AdminExecuteMsg`)](https://docs.sai.fun/for-devs/sai-core/perp-contract#messages-executemsg-and-adminexecutemsg) * [Trade Flow in Detail](https://docs.sai.fun/for-devs/sai-core/perp-contract#trade-flow-in-detail) * [Open a Trade](https://docs.sai.fun/for-devs/sai-core/perp-contract#open-a-trade) * [Close a Trade](https://docs.sai.fun/for-devs/sai-core/perp-contract#close-a-trade) * [Modify a Trade (Stop/Limit/SL/TP/Leverage)](https://docs.sai.fun/for-devs/sai-core/perp-contract#modify-a-trade-stoplimitsltpleverage) * [Additional Modules](https://docs.sai.fun/for-devs/sai-core/perp-contract#additional-modules) * [Borrowing Reference](https://docs.sai.fun/for-devs/sai-core/perp-contract#borrowing-reference) * [Fees Reference](https://docs.sai.fun/for-devs/sai-core/perp-contract#fees-reference) * [Price Impact Reference](https://docs.sai.fun/for-devs/sai-core/perp-contract#price-impact-reference) * [Further Notes](https://docs.sai.fun/for-devs/sai-core/perp-contract#further-notes) * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#overview) Overview -------------------------------------------------------------------------------------- This contract implements **perpetual futures** trading features in CosmWasm. It allows: * **Opening** and **closing** of leveraged positions (long/short). * **Limit** and **stop** orders for trade entry. * Dynamic **stop-loss (SL)** and **take-profit (TP)** updates. * **Partial** position modifications (increase or decrease collateral/leverage). * Automatic **borrowing fee** accrual (see [Borrowing Reference](https://docs.sai.fun/for-devs/sai-core/perp-contract#borrowing-reference) ). * Dynamic **fee** calculations (see [Fees Reference](https://docs.sai.fun/for-devs/sai-core/perp-contract#fees-reference) ). * **Price impact** adjustments based on open interest (see [Price Impact Reference](https://docs.sai.fun/for-devs/sai-core/perp-contract#price-impact-reference) ). * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#repository-layout) Repository Layout -------------------------------------------------------------------------------------------------------- Key files and folders relevant to this contract: * `**./contract.rs**` * High-level instantiation, migration, and execution entry points. * Handles top-level dispatch for `ExecuteMsg::Admin { .. }`. * `**./msgs.rs**` * Defines public `ExecuteMsg` messages for external users. * Defines admin messages (`AdminExecuteMsg`) for privileged ops. * `**./trade.rs**`, `**./update_position_size.rs**`, `**./exec_update_leverage.rs**` * Main **trade** management logic: open/close trades, partial close, update SL/TP, etc. * `**./borrowing/**` * [**Reference** herearrow-up-right](https://github.com/NibiruChain/sai-docs/blob/main/sai-core/smart-contracts/src/borrowing/README.md) . * Borrowing data structures, open-interest tracking, and fee accrual. * `**./fees/**` * [**Reference** herearrow-up-right](https://github.com/NibiruChain/sai-docs/blob/main/sai-core/smart-contracts/src/fees/README.md) . * Fee calculations, tier logic, distribution of fees to vaults. * `**./price_impact/**` * [**Reference** herearrow-up-right](https://github.com/NibiruChain/sai-docs/blob/main/sai-core/smart-contracts/src/price_impact/README.md) . * Logic to handle large trades moving the market price (slippage). * `**./trading/state.rs**`, `**./trading/utils.rs**` * Core state definitions for trades (like `Trade`, `TradeInfo`, `TradingActivated`). * Helper methods for trade validation, order type checks, slippage logic, and exposure-limiting. You will see references to modules such as: * `borrowing`, `fees`, `price_impact`, `pairs`, `trading` - each in its own folder. * `keys.rs` - containing typed indexes (e.g., `MarketIndex`, `TokenIndex`, etc.). * `entry.rs` - collects logic for certain "entry-level" operations like opening/closing trades. * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#key-concepts) Key Concepts ---------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#trade-lifecycle) Trade Lifecycle 1. **Open** * Create a `Trade` struct with details on leverage, collateral, direction (long/short). * Fees are charged on open. 2. **Position Live** * Accrues **borrowing fees** over time (borrowed from `borrowing` module). * Market can move: position PnL is conceptual, realized only on close. 3. **Close** * Reverse the open interest effect, finalize fees, distribute collateral to user or vault if loss. 4. **Stop/Limit/TP/SL** * The contract allows storing limit orders or setting stop-loss/take-profit boundaries. * Typically triggered by an off-chain or keeper system calling `TriggerTrade`. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#contract-entry-points) Contract Entry Points * `**instantiate**` Initializes ownership, oracle references, default settings for `OI_WINDOWS_SETTINGS`. * `**execute**` Dispatches user calls to `ExecuteMsg`. Distinguishes between normal user ops vs. admin messages. * `**migrate**` For contract upgrade logic. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#index-types) Index Types `./keys.rs` defines typed indexes used throughout storage: * `**MarketIndex(u16)**`: Unique ID for a trading market/pair. * `**GroupIndex(u16)**`: Group of markets for borrowing fee grouping. * `**TokenIndex(u16)**`: Represents a collateral token. * `**UserTradeIndex(u64)**`: Uniquely identifies a trade for a given user. * `**TradeInfoIndex(u64)**`: Additional info index for the same trade (like creation block, slippage settings, etc.). These typed indexes help ensure type safety and consistent 2-byte or 8-byte key usage in storage. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#inter-contract-interactions) Inter-Contract Interactions Key interactions are: * **Oracle** (`oracle_address`) * Queried for `GetExchangeRate` to determine the price of base vs. quote assets. * Also used for collateral price in USD. * **Vault** (`VAULT_ADDRESSES`) * Receives or sends collateral during partial close, liquidation, or exit. * Manages user margin deposits (collateral). No direct "execute calls" are done to other modules like borrowing/fees; these modules are integrated inside this contract's logic. However, certain distributions are done via `BankMsg::Send` or calling the vault's `ExecuteMsg::ReceiveAssets`. * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#messages-executemsg-and-adminexecutemsg) Messages (`ExecuteMsg` and `AdminExecuteMsg`) ---------------------------------------------------------------------------------------------------------------------------------------------------------- User commands are in [`./msgs.rs`arrow-up-right](https://github.com/NibiruChain/sai-docs/blob/main/sai-core/smart-contracts/src/msgs.rs) . Notable fields (you can see the file for all arguments): * `**OpenTrade { market_index: MarketIndex, leverage: Decimal, long: bool, ... }**` Opens a brand new position with specific parameters. * `**CloseTrade { trade_index: UserTradeIndex }**` Closes a currently open trade at market price. * `**UpdateOpenLimitOrder { trade_index, price, tp, sl, slippage_p }**` Edits the price or SL/TP for a limit order that hasn't become a live position yet. * `**TriggerTrade { trader: String, trade_index: UserTradeIndex, order_type: PendingOrderType }**` Activates a limit/stop/TP/SL/liq order if conditions are met. * `**UpdateLeverage { trade_index, new_leverage }**` Adjusts leverage on an existing position (increasing or decreasing collateral). * `**IncreasePositionSize**` / `**DecreasePositionSize**` Allows partial modifications in position size or adding/removing collateral. **Admin** operations (`AdminExecuteMsg`) revolve around: * Setting markets (`SetMarkets`) * Updating Oracle or Vault addresses * Configuring fees or borrowing groups * Withdrawing protocol funds * Pausing/trading activation toggles These admin messages **reference** logic from: * [Borrowing Admin Opsarrow-up-right](https://github.com/NibiruChain/sai-docs/blob/main/sai-core/smart-contracts/src/borrowing/README.md#key-components) * [Fees Admin Opsarrow-up-right](https://github.com/NibiruChain/sai-docs/blob/main/sai-core/smart-contracts/src/fees/README.md#key-components) * [Price Impact Admin Opsarrow-up-right](https://github.com/NibiruChain/sai-docs/blob/main/sai-core/smart-contracts/src/price_impact/README.md#open-interest-management) * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#trade-flow-in-detail) Trade Flow in Detail -------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#open-a-trade) Open a Trade 1. **User** calls `**ExecuteMsg::OpenTrade**` with parameters like: 2. **Funds** must be included in the message for the specified `collateral_index`. 3. The contract: * **Fetches** the user's deposit from `info.funds`. * **Validates** leverage, min collateral, exposure limits, slippage, etc. * **Charges** open fees (see [Fees module](https://docs.sai.fun/for-devs/sai-core/perp-contract#fees-reference) ). * **Applies** borrowing logic to keep track of open interest (see [Borrowing module](https://docs.sai.fun/for-devs/sai-core/perp-contract#borrowing-reference) ). * **Stores** the new trade in `TRADES[(User, UserTradeIndex)]`. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#close-a-trade) Close a Trade 1. **User** calls `**CloseTrade { trade_index }**` to close an open position. 2. The contract: * Updates the position's final fees (borrowing + close fees). * Returns leftover collateral or collects more from user if losing more than stored. * Removes open interest from Borrowing storage. * Deletes or marks the trade as closed in `TRADES`. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#modify-a-trade-stop-limit-sl-tp-leverage) Modify a Trade (Stop/Limit/SL/TP/Leverage) Several helper messages exist to alter the trade during its lifetime: * `**TriggerTrade { order_type: PendingOrderType }**` Usually invoked by keepers. This makes a limit/stop order active or triggers SL/TP closure. * `**UpdateOpenLimitOrder**` Edits a not-yet-triggered limit or stop order's parameters (price, SL, TP, etc.). * `**UpdateTp**` **/** `**UpdateSl**` Adjust the take-profit or stop-loss boundaries for an already open "live" trade. * `**UpdateLeverage { new_leverage }**` Increases or decreases the position's leverage. If you reduce leverage, you must send additional collateral. If you increase leverage, the system returns collateral to you. > Under the hood, these modifications recalculate fees, re-check liquidation price, update Borrowing open interest if necessary, and more. * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#additional-modules) Additional Modules ---------------------------------------------------------------------------------------------------------- Below are references to the specialized submodules for advanced logic: ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#borrowing-reference) Borrowing Reference See `**./borrowing/README.md**` for how: * Borrowing fees accumulate over time. * The system enforces maximum open interest (OI) constraints on a **Group** or **Pair** basis. * Each trade references a **BorrowingPairGroup** for historical group changes. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#fees-reference) Fees Reference See `**./fees/README.md**` for how: * Fees are computed on open, close, partial close, trigger orders, and liquidation. * Fees are distributed among governance, trigger reward, and vault. * Fee tiers reduce fees for high-volume traders. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#price-impact-reference) Price Impact Reference See `**./price_impact/README.md**` for how: * `PAIR_DEPTHS` sets the "1% market depth" for each pair (above/below). * Large trades face an execution price penalty or improvement. * Time-windowed open interest approach is used to smooth out short-term spikes. * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-contract#further-notes) Further Notes ------------------------------------------------------------------------------------------------ * **Contract Ownership**: Updated via nibiru-ownable's `UpdateOwnership` admin message. * **Validation**: Full validations across multiple modules ensure no negative exposure or insufficient collateral. * **Extensibility**: Additional messages or fee logic can be integrated by referencing the same approach for typed storage, slippage checks, and Borrowing/Fees hooks. That covers the **core** perpetual futures logic in this repository. For further details on borrowing, fees, or price impact calculations, please **reference** their dedicated READMEs in `./borrowing`, `./fees`, and `./price_impact`. [PreviousSai Corechevron-left](https://docs.sai.fun/for-devs/sai-core) [NextPerp: Borrowing Modulechevron-right](https://docs.sai.fun/for-devs/sai-core/perp-borrowing) Last updated 4 months ago * [Table of Contents](https://docs.sai.fun/for-devs/sai-core/perp-contract#table-of-contents) * [Overview](https://docs.sai.fun/for-devs/sai-core/perp-contract#overview) * [Repository Layout](https://docs.sai.fun/for-devs/sai-core/perp-contract#repository-layout) * [Key Concepts](https://docs.sai.fun/for-devs/sai-core/perp-contract#key-concepts) * [Trade Lifecycle](https://docs.sai.fun/for-devs/sai-core/perp-contract#trade-lifecycle) * [Contract Entry Points](https://docs.sai.fun/for-devs/sai-core/perp-contract#contract-entry-points) * [Index Types](https://docs.sai.fun/for-devs/sai-core/perp-contract#index-types) * [Inter-Contract Interactions](https://docs.sai.fun/for-devs/sai-core/perp-contract#inter-contract-interactions) * [Messages (ExecuteMsg and AdminExecuteMsg)](https://docs.sai.fun/for-devs/sai-core/perp-contract#messages-executemsg-and-adminexecutemsg) * [Trade Flow in Detail](https://docs.sai.fun/for-devs/sai-core/perp-contract#trade-flow-in-detail) * [Open a Trade](https://docs.sai.fun/for-devs/sai-core/perp-contract#open-a-trade) * [Close a Trade](https://docs.sai.fun/for-devs/sai-core/perp-contract#close-a-trade) * [Modify a Trade (Stop/Limit/SL/TP/Leverage)](https://docs.sai.fun/for-devs/sai-core/perp-contract#modify-a-trade-stop-limit-sl-tp-leverage) * [Additional Modules](https://docs.sai.fun/for-devs/sai-core/perp-contract#additional-modules) * [Borrowing Reference](https://docs.sai.fun/for-devs/sai-core/perp-contract#borrowing-reference) * [Fees Reference](https://docs.sai.fun/for-devs/sai-core/perp-contract#fees-reference) * [Price Impact Reference](https://docs.sai.fun/for-devs/sai-core/perp-contract#price-impact-reference) * [Further Notes](https://docs.sai.fun/for-devs/sai-core/perp-contract#further-notes) Copy OpenTrade { market_index: MarketIndex(0), leverage: Decimal::from_str("5.0")?, long: true, collateral_index: TokenIndex(1), trade_type: TradeType::Trade, open_price: Decimal::from_str("1000")?, tp: Some(...), sl: Some(...), slippage_p: Decimal::from_str("0.03")?, } --- # Fees API | Sai.fun Query fee data including transaction-level fees, daily statistics, and protocol/trader summaries. [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#table-of-contents) Table of Contents ----------------------------------------------------------------------------------------------------- * [Queries](https://docs.sai.fun/for-devs/sai-keeper/api-fees#queries) * [feeTransaction](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feetransaction) * [feeTransactions](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feetransactions) * [feeDailyStats](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feedailystats) * [feeAnalytics](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feeanalytics) * [protocolFeeSummary](https://docs.sai.fun/for-devs/sai-keeper/api-fees#protocolfeesummary) * [traderFeeSummary](https://docs.sai.fun/for-devs/sai-keeper/api-fees#traderfeesummary) * [Types Reference](https://docs.sai.fun/for-devs/sai-keeper/api-fees#types-reference) [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#queries) Queries --------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feetransaction) feeTransaction Get a specific fee transaction by ID. **Signature:** Copy feeTransaction(id: ID!): FeeTransaction **Parameters:** * `id`: Transaction ID (string) **Returns:** `FeeTransaction` object or null **Example:** * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feetransactions) feeTransactions List fee transactions with filtering and pagination. **Signature:** **Parameters:** * `filter`: Filter object (optional) * `traderAddress`: Filter by trader * `collateralDenom`: Filter by collateral token * `feeType`: Filter by `OPENING` or `CLOSING` * `fromDate`: Start date (RFC3339) * `toDate`: End date (RFC3339) * `minAmount`: Minimum fee amount * `maxAmount`: Maximum fee amount * `limit`: Max results (default: 100) * `offset`: Skip results (default: 0) **Returns:** Array of `FeeTransaction` objects **Example - Get User Fees:** **Example - Filter by Date Range:** **Example - Large Fees Only:** **Use Cases:** * User fee history * Protocol fee monitoring * Large transaction tracking * Fee analysis by period * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feedailystats) feeDailyStats Get aggregated daily fee statistics. **Signature:** **Parameters:** * `filter`: Filter object (optional) * `traderAddress`: Filter by specific trader * `collateralDenom`: Filter by collateral token * `fromDate`: Start date * `toDate`: End date * `protocolWide`: If true, get protocol-wide stats * `limit`: Max results (default: 30) * `offset`: Skip results (default: 0) **Returns:** Array of `FeeDailyStats` objects **Example - Protocol Daily Stats:** **Example - Trader Daily Stats:** **Use Cases:** * Daily fee charts * Historical fee analysis * Revenue tracking * Trader fee breakdown * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feeanalytics) feeAnalytics Get enhanced fee analytics with additional metrics. **Signature:** **Parameters:** Same as `feeDailyStats` **Returns:** Array of `FeeAnalytics` objects **Example:** **Key Differences from feeDailyStats:** * Includes `avgFeeMultiplier` * Includes `totalFeesAll` (sum of opening + closing) * More granular trigger fee breakdown **Use Cases:** * Advanced analytics dashboards * Fee multiplier tracking * Comprehensive fee reports * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#protocolfeesummary) protocolFeeSummary Get protocol-wide fee summary for a time period. **Signature:** **Parameters:** * `collateralDenom`: Optional collateral filter * `fromDate`: Start date (optional) * `toDate`: End date (optional) **Returns:** `ProtocolFeeSummary` object **Example - All-Time Protocol Summary:** **Example - Monthly Summary:** **Example - Per-Collateral Summary:** **Use Cases:** * Protocol revenue reports * KPI tracking * Performance metrics * Time-period comparisons * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#traderfeesummary) traderFeeSummary Get fee summary for a specific trader. **Signature:** **Parameters:** * `traderAddress`: Trader's address (required) * `collateralDenom`: Optional collateral filter * `fromDate`: Start date (optional) * `toDate`: End date (optional) **Returns:** `TraderFeeSummary` object **Example - All-Time Trader Summary:** **Example - Monthly Trader Summary:** **Use Cases:** * User dashboards * Trading cost analysis * Fee optimization insights * Referral tracking * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#types-reference) Types Reference ------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feetransaction-1) FeeTransaction Complete fee transaction details. **Key Fields:** * `totalFeeCharged`: Total fee paid by trader * `govFee`: Fee to protocol governance * `vaultFee`: Fee to LP vault * `referrerAllocation`: Fee to referrer (if any) * `triggerFee`: Fee paid to order trigger executor * `badDebt`: Bad debt if position liquidated with insufficient collateral * `feeMultiplier`: Dynamic fee multiplier applied * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feedailystats-1) FeeDailyStats Daily aggregated fee statistics. * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feeanalytics-1) FeeAnalytics Enhanced analytics with additional metrics. * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#protocolfeesummary-1) ProtocolFeeSummary Protocol-wide fee summary. * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#traderfeesummary-1) TraderFeeSummary Per-trader fee summary. * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#enums) Enums **FeeType:** * `OPENING`: Fees charged when opening positions * `CLOSING`: Fees charged when closing positions * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#fee-calculations) Fee Calculations --------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#total-protocol-revenue) Total Protocol Revenue ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#average-fee-per-trade) Average Fee Per Trade ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#fee-breakdown-percentages) Fee Breakdown Percentages ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#convert-fee-amounts) Convert Fee Amounts * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#best-practices) Best Practices ----------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#date-range-queries) Date Range Queries ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#pagination-for-large-datasets) Pagination for Large Datasets ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-fees#caching-daily-stats) Caching Daily Stats [PreviousOracle APIchevron-left](https://docs.sai.fun/for-devs/sai-keeper/api-oracle) [NextExamples: Querieschevron-right](https://docs.sai.fun/for-devs/sai-keeper/examples-queries) Last updated 3 months ago * [Table of Contents](https://docs.sai.fun/for-devs/sai-keeper/api-fees#table-of-contents) * [Queries](https://docs.sai.fun/for-devs/sai-keeper/api-fees#queries) * [feeTransaction](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feetransaction) * [feeTransactions](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feetransactions) * [feeDailyStats](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feedailystats) * [feeAnalytics](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feeanalytics) * [protocolFeeSummary](https://docs.sai.fun/for-devs/sai-keeper/api-fees#protocolfeesummary) * [traderFeeSummary](https://docs.sai.fun/for-devs/sai-keeper/api-fees#traderfeesummary) * [Types Reference](https://docs.sai.fun/for-devs/sai-keeper/api-fees#types-reference) * [FeeTransaction](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feetransaction-1) * [FeeDailyStats](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feedailystats-1) * [FeeAnalytics](https://docs.sai.fun/for-devs/sai-keeper/api-fees#feeanalytics-1) * [ProtocolFeeSummary](https://docs.sai.fun/for-devs/sai-keeper/api-fees#protocolfeesummary-1) * [TraderFeeSummary](https://docs.sai.fun/for-devs/sai-keeper/api-fees#traderfeesummary-1) * [Enums](https://docs.sai.fun/for-devs/sai-keeper/api-fees#enums) * [Fee Calculations](https://docs.sai.fun/for-devs/sai-keeper/api-fees#fee-calculations) * [Total Protocol Revenue](https://docs.sai.fun/for-devs/sai-keeper/api-fees#total-protocol-revenue) * [Average Fee Per Trade](https://docs.sai.fun/for-devs/sai-keeper/api-fees#average-fee-per-trade) * [Fee Breakdown Percentages](https://docs.sai.fun/for-devs/sai-keeper/api-fees#fee-breakdown-percentages) * [Convert Fee Amounts](https://docs.sai.fun/for-devs/sai-keeper/api-fees#convert-fee-amounts) * [Best Practices](https://docs.sai.fun/for-devs/sai-keeper/api-fees#best-practices) * [Date Range Queries](https://docs.sai.fun/for-devs/sai-keeper/api-fees#date-range-queries) * [Pagination for Large Datasets](https://docs.sai.fun/for-devs/sai-keeper/api-fees#pagination-for-large-datasets) * [Caching Daily Stats](https://docs.sai.fun/for-devs/sai-keeper/api-fees#caching-daily-stats) Copy query GetFeeTransaction { fee { feeTransaction(id: "abc123") { id traderAddress feeType totalFeeCharged govFee vaultFee referrerAllocation triggerFee collateralDenom blockHeight blockTime } } } Copy feeTransactions( filter: FeeTransactionFilter limit: Int = 100 offset: Int = 0 ): [FeeTransaction!]! Copy query GetUserFees($trader: String!) { fee { feeTransactions( filter: { traderAddress: $trader } limit: 50 ) { id feeType totalFeeCharged govFee vaultFee referrerAllocation triggerFee blockTime tradeId orderType } } } Copy query GetFeesInRange { fee { feeTransactions( filter: { fromDate: "2024-01-01T00:00:00Z" toDate: "2024-01-31T23:59:59Z" feeType: OPENING } limit: 100 ) { id traderAddress totalFeeCharged blockTime } } } Copy query GetLargeFees { fee { feeTransactions( filter: { minAmount: 1000000 # Filter fees > 1,000,000 } limit: 20 ) { id traderAddress totalFeeCharged feeType } } } Copy feeDailyStats( filter: FeeDailyStatsFilter limit: Int = 30 offset: Int = 0 ): [FeeDailyStats!]! Copy query GetProtocolDailyStats { fee { feeDailyStats( filter: { protocolWide: true } limit: 30 ) { date collateralDenom openingFeeCount openingFeeTotal openingGovFee openingReferrerFee openingVaultFee closingFeeCount closingFeeTotal closingGovFee closingReferrerFee closingVaultFee closingTriggerFee totalBadDebt updatedAt } } } Copy query GetTraderDailyStats($trader: String!) { fee { feeDailyStats( filter: { traderAddress: $trader fromDate: "2024-01-01T00:00:00Z" } limit: 90 ) { date openingFeeTotal closingFeeTotal totalBadDebt } } } Copy feeAnalytics( filter: FeeDailyStatsFilter limit: Int = 30 offset: Int = 0 ): [FeeAnalytics!]! Copy query GetFeeAnalytics { fee { feeAnalytics( filter: { protocolWide: true } limit: 30 ) { date collateralDenom openingCount openingTotal openingGov openingReferrer openingVault openingTrigger closingCount closingTotal closingGov closingReferrer closingVault closingTrigger totalFeesAll avgFeeMultiplier totalBadDebt traderAddress } } } Copy protocolFeeSummary( collateralDenom: String fromDate: Time toDate: Time ): ProtocolFeeSummary! Copy query GetProtocolSummary { fee { protocolFeeSummary { period { fromDate toDate } totalFees totalOpeningFees totalClosingFees totalGovFees totalVaultFees totalReferrerFees totalTriggerFees totalBadDebt openingCount closingCount uniqueTraders avgFeeMultiplier } } } Copy query GetMonthlySummary { fee { protocolFeeSummary( fromDate: "2024-01-01T00:00:00Z" toDate: "2024-01-31T23:59:59Z" ) { totalFees totalOpeningFees totalClosingFees uniqueTraders openingCount closingCount } } } Copy query GetCollateralSummary { fee { protocolFeeSummary( collateralDenom: "uusdc" fromDate: "2024-01-01T00:00:00Z" ) { totalFees totalVaultFees totalGovFees uniqueTraders } } } Copy traderFeeSummary( traderAddress: String! collateralDenom: String fromDate: Time toDate: Time ): TraderFeeSummary! Copy query GetTraderSummary($trader: String!) { fee { traderFeeSummary(traderAddress: $trader) { traderAddress period { fromDate toDate } totalFees totalOpeningFees totalClosingFees totalReferrerAllocation totalBadDebt openingCount closingCount avgFeeMultiplier } } } Copy query GetTraderMonthlySummary($trader: String!) { fee { traderFeeSummary( traderAddress: $trader fromDate: "2024-01-01T00:00:00Z" toDate: "2024-01-31T23:59:59Z" ) { totalFees openingCount closingCount avgFeeMultiplier } } } Copy type FeeTransaction { id: ID! traderAddress: String! tradeId: Int! tradeIndex: Int! feeType: FeeType! collateralDenom: String! totalFeeCharged: Int! govFee: Int! netGovFee: Int! vaultFee: Int! referrerAllocation: Int! triggerFee: Int! openFeeComponent: Int triggerFeeComponent: Int triggererReward: Int collateralLeft: Int badDebt: Int! feeMultiplier: Float! orderType: String catalystAddress: String blockHeight: Int! blockTime: Time! createdAt: Time! } Copy type FeeDailyStats { date: Time! collateralDenom: String! traderAddress: String openingFeeCount: Int! openingFeeTotal: Int! openingGovFee: Int! openingReferrerFee: Int! openingTriggerFee: Int! openingVaultFee: Int! closingFeeCount: Int! closingFeeTotal: Int! closingGovFee: Int! closingReferrerFee: Int! closingTriggerFee: Int! closingVaultFee: Int! totalBadDebt: Int! updatedAt: Time! } Copy type FeeAnalytics { date: Time! collateralDenom: String! traderAddress: String openingCount: Int! openingTotal: Int! openingGov: Int! openingReferrer: Int! openingTrigger: Int! openingVault: Int! closingCount: Int! closingTotal: Int! closingGov: Int! closingReferrer: Int! closingTrigger: Int! closingVault: Int! totalFeesAll: Int! avgFeeMultiplier: Float! totalBadDebt: Int! } Copy type ProtocolFeeSummary { period: TimePeriod! totalFees: Int! totalOpeningFees: Int! totalClosingFees: Int! totalGovFees: Int! totalVaultFees: Int! totalReferrerFees: Int! totalTriggerFees: Int! totalBadDebt: Int! openingCount: Int! closingCount: Int! uniqueTraders: Int! avgFeeMultiplier: Float! } Copy type TraderFeeSummary { traderAddress: String! period: TimePeriod! totalFees: Int! totalOpeningFees: Int! totalClosingFees: Int! totalReferrerAllocation: Int! totalBadDebt: Int! openingCount: Int! closingCount: Int! avgFeeMultiplier: Float! } Copy const totalRevenue = protocolFeeSummary.totalGovFees + protocolFeeSummary.totalVaultFees Copy const avgOpeningFee = protocolFeeSummary.totalOpeningFees / protocolFeeSummary.openingCount const avgClosingFee = protocolFeeSummary.totalClosingFees / protocolFeeSummary.closingCount Copy const calculateBreakdown = (summary) => { const total = summary.totalFees return { govPct: (summary.totalGovFees / total) * 100, vaultPct: (summary.totalVaultFees / total) * 100, referrerPct: (summary.totalReferrerFees / total) * 100, triggerPct: (summary.totalTriggerFees / total) * 100 } } Copy // Fees are in smallest unit, convert to decimal const convertFee = (feeAmount, decimals) => { return feeAmount / (10 ** decimals) } // Example: USDC fees (6 decimals) const feeUSDC = convertFee(1000000, 6) // = 1.0 USDC Copy // Get current month stats const now = new Date() const firstDay = new Date(now.getFullYear(), now.getMonth(), 1) const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0) const query = ` query GetMonthlyStats { fee { protocolFeeSummary( fromDate: "${firstDay.toISOString()}" toDate: "${lastDay.toISOString()}" ) { totalFees uniqueTraders } } } ` Copy // Fetch all fee transactions in batches const fetchAllFees = async (trader, limit = 100) => { let allFees = [] let offset = 0 let hasMore = true while (hasMore) { const { data } = await client.query({ query: FEE_TRANSACTIONS_QUERY, variables: { trader, limit, offset } }) const fees = data.fee.feeTransactions allFees = allFees.concat(fees) hasMore = fees.length === limit offset += limit } return allFees } Copy // Cache daily stats (they don't change) const cacheDailyStats = new Map() const getDailyStats = async (date) => { const dateKey = date.toISOString().split('T')[0] if (cacheDailyStats.has(dateKey)) { return cacheDailyStats.get(dateKey) } const stats = await fetchDailyStats(date) cacheDailyStats.set(dateKey, stats) return stats } --- # Oracle API | Sai.fun Query and subscribe to token price feeds and metadata. [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#table-of-contents) Table of Contents ------------------------------------------------------------------------------------------------------- * [Queries](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#queries) * [token](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#token) * [tokens](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#tokens) * [tokenPricesUsd](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#tokenpricesusd) * [Subscriptions](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#subscriptions) * [Types Reference](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#types-reference) [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#queries) Queries ----------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#token) token Get details for a specific token by ID. **Signature:** Copy token(id: Int!): Token! **Parameters:** * `id`: Token ID (integer) **Returns:** `Token` object **Example:** **Use Cases:** * Get token metadata * Display token information * Resolve token ID to symbol * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#tokens) tokens List all available tokens with filtering and ordering. **Signature:** **Parameters:** * `where`: Filter object (optional) * `name`: Filter by token name (string match) * `permissionGroup`: Filter by permission group * `limit`: Max results * `order_by`: Sort by `id`, `name`, or `permission_group` * `order_desc`: Sort descending **Returns:** Array of `Token` objects **Example - List All Tokens:** **Example - Search by Name:** **Example - Filter by Permission Group:** **Use Cases:** * Build token selector dropdown * Search tokens by name * List tradeable assets * Filter by permissions * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#tokenpricesusd-query) tokenPricesUsd query Get current USD prices for tokens. **Signature:** **Parameters:** * `where`: Filter object (optional) * `tokenId`: Filter by specific token ID * `limit`: Max results * `order_by`: Sort by `oracle_token_id` * `order_desc`: Sort descending **Returns:** Array of `TokenPricesUsd` objects **Example - Get All Prices:** **Example - Get Specific Token Price:** **Example - Get Multiple Prices:** **Key Fields:** * `priceUsd`: Current price in USD (float) * `lastUpdatedBlock`: Block info for last price update **Use Cases:** * Display token prices * Calculate position values * Monitor price changes * Verify price freshness * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#subscriptions) Subscriptions ----------------------------------------------------------------------------------------------- Subscribe to real-time price updates. ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#tokenpricesusd) tokenPricesUsd Subscribe to price updates for specific or all tokens. **Signature:** **Example - Subscribe to All Prices:** **Example - Subscribe to Specific Token:** **Use Cases:** * Real-time price tickers * Live trading dashboards * Price alert systems * Chart updates * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#userbalances) userBalances Subscribe to user balance updates across all tokens. **Signature:** **Example:** **Use Cases:** * Track wallet balances * Monitor account changes * Real-time portfolio updates * Display available funds * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#types-reference) Types Reference --------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#token-1) Token **Field Details:** * `id`: Unique token identifier * `symbol`: Token ticker (e.g., "BTC", "ETH") * `name`: Full token name (e.g., "Bitcoin", "Ethereum") * `logoUrl`: URL to token logo image * `tradingViewSymbol`: Symbol for TradingView charts * `permissionGroup`: Access/trading permission level * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#tokenpricesusd-1) TokenPricesUsd **Field Details:** * `token`: Associated token metadata * `priceUsd`: Current USD price * `lastUpdatedBlock`: Last price update info * `block`: Block height * `block_ts`: Timestamp (RFC3339 format) * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#balance) Balance * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#tokeninfo) TokenInfo Detailed token information for balances. **TokenType Enum:** * `bank`: Native Cosmos SDK token * `erc20`: ERC20 token on EVM * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#working-with-prices) Working with Prices ----------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#price-freshness) Price Freshness Always check when price was last updated: ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#price-formatting) Price Formatting ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#calculate-position-value) Calculate Position Value ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#balance-conversion) Balance Conversion * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#best-practices) Best Practices ------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#caching-prices) Caching Prices ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#handling-multiple-tokens) Handling Multiple Tokens ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#subscription-management) Subscription Management [PreviousLiquidity Provider (LP) APIchevron-left](https://docs.sai.fun/for-devs/sai-keeper/api-lp) [NextFees APIchevron-right](https://docs.sai.fun/for-devs/sai-keeper/api-fees) Last updated 15 hours ago * [Table of Contents](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#table-of-contents) * [Queries](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#queries) * [token](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#token) * [tokens](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#tokens) * [tokenPricesUsd query](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#tokenpricesusd-query) * [Subscriptions](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#subscriptions) * [tokenPricesUsd](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#tokenpricesusd) * [userBalances](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#userbalances) * [Types Reference](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#types-reference) * [Token](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#token-1) * [TokenPricesUsd](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#tokenpricesusd-1) * [Balance](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#balance) * [TokenInfo](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#tokeninfo) * [Working with Prices](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#working-with-prices) * [Price Freshness](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#price-freshness) * [Price Formatting](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#price-formatting) * [Calculate Position Value](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#calculate-position-value) * [Balance Conversion](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#balance-conversion) * [Best Practices](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#best-practices) * [Caching Prices](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#caching-prices) * [Handling Multiple Tokens](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#handling-multiple-tokens) * [Subscription Management](https://docs.sai.fun/for-devs/sai-keeper/api-oracle#subscription-management) Copy query GetToken { oracle { token(id: 1) { id symbol name logoUrl tradingViewSymbol permissionGroup } } } Copy tokens( where: TokensFilter limit: Int order_by: TokensOrder order_desc: Boolean ): [Token!]! Copy query ListAllTokens { oracle { tokens(order_by: name) { id symbol name logoUrl tradingViewSymbol } } } Copy query SearchToken($searchTerm: String!) { oracle { tokens(where: { name: $searchTerm }) { id symbol name } } } Copy query GetPermittedTokens { oracle { tokens(where: { permissionGroup: 1 }) { id symbol name } } } Copy tokenPricesUsd( where: TokenPriceUsdFilter limit: Int order_by: TokenPriceUsdOrder order_desc: Boolean ): [TokenPricesUsd!]! Copy query GetAllPrices { oracle { tokenPricesUsd { token { id symbol name } priceUsd lastUpdatedBlock { block block_ts } } } } Copy query GetBTCPrice { oracle { tokenPricesUsd(where: { tokenId: 1 }) { token { symbol } priceUsd lastUpdatedBlock { block block_ts } } } } Copy query GetPrices { btc: oracle { tokenPricesUsd(where: { tokenId: 1 }) { priceUsd lastUpdatedBlock { block_ts } } } eth: oracle { tokenPricesUsd(where: { tokenId: 2 }) { priceUsd lastUpdatedBlock { block_ts } } } } Copy subscription { tokenPricesUsd(where: SubTokenPriceUsdFilter): [TokenPricesUsd!]! } Copy subscription WatchAllPrices { tokenPricesUsd { token { id symbol } priceUsd lastUpdatedBlock { block block_ts } } } Copy subscription WatchBTCPrice { tokenPricesUsd(where: { tokenId: 1 }) { token { symbol } priceUsd lastUpdatedBlock { block_ts } } } Copy subscription { userBalances(where: SubUserBalancesFilter!): [Balance!]! } Copy subscription WatchUserBalances($user: String!) { userBalances(where: { user: $user }) { amount token_info { symbol decimals bank_denom erc20_contract_address logo name type } } } Copy type Token { id: Int! symbol: String! name: String! logoUrl: String tradingViewSymbol: String permissionGroup: Int! } Copy type TokenPricesUsd { token: Token priceUsd: Float! lastUpdatedBlock: Block! } Copy type Balance { amount: String! token_info: TokenInfo! } Copy type TokenInfo { bank_denom: String! symbol: String! name: String! decimals: Int! logo: String type: TokenType! erc20_contract_address: String! } Copy const now = Date.now() const lastUpdate = new Date(lastUpdatedBlock.block_ts).getTime() const ageSeconds = (now - lastUpdate) / 1000 if (ageSeconds > 60) { console.warn('Price may be stale') } Copy // Format price with appropriate decimals const formatPrice = (priceUsd) => { if (priceUsd >= 1000) { return priceUsd.toFixed(2) } else if (priceUsd >= 1) { return priceUsd.toFixed(4) } else { return priceUsd.toFixed(6) } } Copy // Calculate position value in USD const positionValueUsd = (collateralAmount, priceUsd, decimals) => { const actualAmount = collateralAmount / (10 ** decimals) return actualAmount * priceUsd } Copy // Convert balance amount string to number const getBalanceValue = (balance) => { const amount = parseFloat(balance.amount) return amount / (10 ** balance.token_info.decimals) } Copy // Cache prices for short periods to reduce queries const priceCache = new Map() const CACHE_TTL = 5000 // 5 seconds const getCachedPrice = async (tokenId) => { const cached = priceCache.get(tokenId) if (cached && Date.now() - cached.timestamp < CACHE_TTL) { return cached.price } const price = await fetchPrice(tokenId) priceCache.set(tokenId, { price, timestamp: Date.now() }) return price } Copy // Batch query multiple prices efficiently const query = ` query GetMultiplePrices($tokenIds: [Int!]!) { oracle { prices: tokenPricesUsd(limit: 100) { token { id symbol } priceUsd } } } ` // Filter client-side for needed tokens const relevantPrices = data.oracle.prices.filter(p => tokenIds.includes(p.token.id) ) Copy // Manage price subscription lifecycle let priceSubscription const subscribeToPrices = (tokenIds) => { // Unsubscribe from previous if (priceSubscription) { priceSubscription.unsubscribe() } // Subscribe to new set priceSubscription = client.subscribe({ query: PRICE_SUBSCRIPTION, variables: { tokenIds } }).subscribe({ next: (data) => updatePrices(data), error: (err) => console.error(err) }) } --- # EVM Integration Guide | Sai.fun ![Page cover](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252FYyFLT3iIOqFGDN2JP9u9%252Fsai-banner-guides-1500x500.png%3Falt%3Dmedia%26token%3Da7262a89-4e22-4bf9-aa82-27b06588f2ae&width=1248&dpr=3&quality=100&sign=3ad5d264&sv=2) How to drive Sai using an **EVM signer** through the EVM Interface contract. You pass a JSON‑encoded WASM message (`wasmMsgBytes`) along with token amounts/addresses. * * * [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#id-0.-getting-started) 0\. Getting Started ----------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#prerequisites) Prerequisites * Node.js ≥ 18 * pnpm, npm, or yarn ### [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#new-project) New Project Copy mkdir sai-evm && cd sai-evm pnpm init -y pnpm add ethers bignumber.js pnpm add -D typescript ts-node @types/node dotenv npx tsc --init --target ES2022 --module NodeNext --moduleResolution NodeNext > If you also want to call the Cosmos side in the same app, add: `@nibiruchain/nibijs @cosmjs/cosmwasm-stargate @cosmjs/stargate cosmjs-types`. ### [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#environment-setup) Environment Setup Create a `.env` file with your configuration: ### [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#bootstrap-provider--signer) Bootstrap Provider + Signer ### [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#interface-abi) Interface ABI Import the ABI that exposes these methods: `openTrade`, `executeSimpleFunctions`, `deposit`, `makeWithdrawRequest`, `redeem`, and `executeVaultSimpleFunctions`. ### [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#gas-estimation-helpers) Gas Estimation Helpers * * * [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#id-1.-collateral-payment-tokens) 1\. Collateral / Payment Tokens --------------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#decimal-handling) Decimal Handling * **BANK units** use **6 decimals** (Cosmos side) * **ERC‑20 units** use the token's decimals (typically 6) ### [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#testnet-1) Testnet-1 Token ERC-20 Address **USDC** `0xAb68f1D1d91854383fd4Df9016E3040D03e8191a` **stNIBI** `0xCae3d404AFB50016154a4B18091351065154E9bD` ### [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#mainnet) Mainnet Token ERC-20 Address **USDC** `0x0829F361A05D993d5CEb035cA6DF3446b060970b` **stNIBI** `0xcA0a9Fb5FBF692fa12fD13c0A900EC56Bb3f0a7b` ### [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#converting-between-decimals) Converting Between Decimals When combining BANK and ERC-20 amounts, ensure both use BANK units (6 decimals) before summing: * * * [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#id-2.-perps-open-trade) 2\. Perps – Open Trade --------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#method-signature) Method Signature ### [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#example) Example * * * [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#id-3.-perps-close-trade) 3\. Perps – Close Trade ----------------------------------------------------------------------------------------------------------- Close an open position: * * * [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#id-4.-referral-create-and-redeem-codes) 4\. Referral – Create & Redeem Codes --------------------------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#create-a-referral-code) Create a Referral Code ### [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#redeem-a-referral-code) Redeem a Referral Code * * * [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#id-5.-vault-deposit) 5\. Vault – Deposit --------------------------------------------------------------------------------------------------- Deposit collateral into a vault: * * * [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#id-6.-vault-make-withdraw-request) 6\. Vault – Make Withdraw Request ------------------------------------------------------------------------------------------------------------------------------- Request to withdraw funds from a vault: * * * [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#id-7.-vault-redeem) 7\. Vault – Redeem ------------------------------------------------------------------------------------------------- Redeem shares from a vault: * * * [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#id-8.-vault-cancel-withdraw-request) 8\. Vault – Cancel Withdraw Request ----------------------------------------------------------------------------------------------------------------------------------- Cancel a pending withdraw request: * * * [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#error-handling-and-troubleshooting) Error Handling & Troubleshooting ------------------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#common-issues) Common Issues Issue Solution **"No EVM signer"** Ensure `makeSigner()` is called and `PRIVATE_KEY` is set in `.env` **Gas estimation fails** The function automatically falls back to `5,000,000` gas limit and `1 gwei` price **Transaction reverts on-chain** Use `debug_traceTransaction` on your archive RPC to inspect the deepest call error **Decimal conversion errors** Always convert ERC-20 and BANK amounts to the same decimal base before summing **Invalid INTERFACE\_ADDR** Verify the address matches your network (Testnet-1 vs Mainnet) ### [hashtag](https://docs.sai.fun/for-devs/dev/evm-guide#best-practices) Best Practices * Always use an **EVM signer** (provider alone cannot send transactions) * Apply gas buffering to avoid "out of gas" errors * Validate that `is_evm_origin: true` is set in all trade messages * Convert token decimals correctly before combining BANK and ERC-20 amounts * Store `PRIVATE_KEY` securely and never commit it to version control [PreviousGetting Startedchevron-left](https://docs.sai.fun/for-devs/dev) [NextWASM Integration Guidechevron-right](https://docs.sai.fun/for-devs/dev/wasm-guide) Last updated 3 months ago * [0\. Getting Started](https://docs.sai.fun/for-devs/dev/evm-guide#id-0.-getting-started) * [Prerequisites](https://docs.sai.fun/for-devs/dev/evm-guide#prerequisites) * [New Project](https://docs.sai.fun/for-devs/dev/evm-guide#new-project) * [Environment Setup](https://docs.sai.fun/for-devs/dev/evm-guide#environment-setup) * [Bootstrap Provider + Signer](https://docs.sai.fun/for-devs/dev/evm-guide#bootstrap-provider--signer) * [Interface ABI](https://docs.sai.fun/for-devs/dev/evm-guide#interface-abi) * [Gas Estimation Helpers](https://docs.sai.fun/for-devs/dev/evm-guide#gas-estimation-helpers) * [1\. Collateral / Payment Tokens](https://docs.sai.fun/for-devs/dev/evm-guide#id-1.-collateral-payment-tokens) * [Decimal Handling](https://docs.sai.fun/for-devs/dev/evm-guide#decimal-handling) * [Testnet-1](https://docs.sai.fun/for-devs/dev/evm-guide#testnet-1) * [Mainnet](https://docs.sai.fun/for-devs/dev/evm-guide#mainnet) * [Converting Between Decimals](https://docs.sai.fun/for-devs/dev/evm-guide#converting-between-decimals) * [2\. Perps – Open Trade](https://docs.sai.fun/for-devs/dev/evm-guide#id-2.-perps-open-trade) * [Method Signature](https://docs.sai.fun/for-devs/dev/evm-guide#method-signature) * [Example](https://docs.sai.fun/for-devs/dev/evm-guide#example) * [3\. Perps – Close Trade](https://docs.sai.fun/for-devs/dev/evm-guide#id-3.-perps-close-trade) * [4\. Referral – Create & Redeem Codes](https://docs.sai.fun/for-devs/dev/evm-guide#id-4.-referral-create-and-redeem-codes) * [Create a Referral Code](https://docs.sai.fun/for-devs/dev/evm-guide#create-a-referral-code) * [Redeem a Referral Code](https://docs.sai.fun/for-devs/dev/evm-guide#redeem-a-referral-code) * [5\. Vault – Deposit](https://docs.sai.fun/for-devs/dev/evm-guide#id-5.-vault-deposit) * [6\. Vault – Make Withdraw Request](https://docs.sai.fun/for-devs/dev/evm-guide#id-6.-vault-make-withdraw-request) * [7\. Vault – Redeem](https://docs.sai.fun/for-devs/dev/evm-guide#id-7.-vault-redeem) * [8\. Vault – Cancel Withdraw Request](https://docs.sai.fun/for-devs/dev/evm-guide#id-8.-vault-cancel-withdraw-request) * [Error Handling & Troubleshooting](https://docs.sai.fun/for-devs/dev/evm-guide#error-handling-and-troubleshooting) * [Common Issues](https://docs.sai.fun/for-devs/dev/evm-guide#common-issues) * [Best Practices](https://docs.sai.fun/for-devs/dev/evm-guide#best-practices) Copy # Testnet-1 EVM_RPC=https://evm-rpc.testnet-1.nibiru.fi/ # Or Mainnet # EVM_RPC=https://evm-rpc.nibiru.fi/ PRIVATE_KEY=0x... # Your private key (keep this secret!) INTERFACE_ADDR=0x... # Contract address from saiEvmInterfaceFromChainType(chainType) Copy // src/evm.ts import "dotenv/config" import { ethers } from "ethers" export function makeSigner() { const rpc = process.env.EVM_RPC! const pk = process.env.PRIVATE_KEY! const provider = new ethers.JsonRpcProvider(rpc) const signer = new ethers.Wallet(pk, provider) return { provider, signer } } Copy import PerpVaultEvmInterfaceAbi from "./PerpVaultEvmInterface.json" // Place your ABI file here Copy const GAS_BUFFER_NUMERATOR = 11n const GAS_BUFFER_DENOMINATOR = 10n const FALLBACK_GAS_LIMIT = 5_000_000n const FALLBACK_GAS_PRICE = ethers.parseUnits("1", "gwei") const withGasBuffer = (g: bigint) => g === 0n ? 1n : (g * GAS_BUFFER_NUMERATOR) / GAS_BUFFER_DENOMINATOR async function estimateGasWithFallback( fn: () => Promise, info?: unknown ) { try { const est = await fn() return { gasLimit: withGasBuffer(est) } } catch (error) { console.warn("Gas estimation failed, using fallback", info) return { gasLimit: FALLBACK_GAS_LIMIT, gasPrice: FALLBACK_GAS_PRICE } } } const toTxOverrides = ({ gasLimit, gasPrice, }: { gasLimit: bigint gasPrice?: bigint }) => (gasPrice === undefined ? { gasLimit } : { gasLimit, gasPrice }) Copy const toBankUnits = ( amt: bigint, sourceDecimals: number, bankDecimals: number = 6 ): bigint => { if (sourceDecimals === bankDecimals) return amt const diff = Math.abs(sourceDecimals - bankDecimals) const factor = 10n ** BigInt(diff) return sourceDecimals > bankDecimals ? amt / factor : amt * factor } Copy contract.openTrade( wasmMsgBytes, // JSON-encoded WASM message with is_evm_origin: true collateralIndex, // Token index (e.g., 0 for first collateral) totalAmountBankUnits, // Total amount in BANK units useErc20Amount, // ERC-20 amount in token decimals overrides // Gas overrides ) Copy import { ethers } from "ethers" import BigNumber from "bignumber.js" import PerpVaultEvmInterfaceAbi from "./PerpVaultEvmInterface.json" import { makeSigner } from "./evm" async function openTrade() { const { signer } = makeSigner() const iface = new ethers.Contract( process.env.INTERFACE_ADDR!, PerpVaultEvmInterfaceAbi.abi, signer ) const msg = { open_trade: { market_index: "MarketIndex(0)", leverage: "5", long: true, collateral_index: "TokenIndex(0)", trade_type: "trade", open_price: "70000", tp: undefined, sl: undefined, slippage_p: "1", is_evm_origin: true, // IMPORTANT: Set to true for EVM origin }, } const bankDecimals = 6 const erc20Decimals = 6 const displayAmount = new BigNumber("100") const bankAmount = ethers.parseUnits(displayAmount.toString(), bankDecimals) const erc20Amount = ethers.parseUnits(displayAmount.toString(), erc20Decimals) const toBankUnits = ( amt: bigint, ercDec: number, bankDec: number ): bigint => { if (ercDec === bankDec) return amt const diff = Math.abs(ercDec - bankDec) const factor = 10n ** BigInt(diff) return ercDec > bankDec ? amt / factor : amt * factor } const totalBank = bankAmount + toBankUnits(erc20Amount, erc20Decimals, bankDecimals) const wasmMsgBytes = ethers.toUtf8Bytes(JSON.stringify(msg)) const gas = await estimateGasWithFallback(() => iface.openTrade.estimateGas(wasmMsgBytes, 0, totalBank, erc20Amount) ) const tx = await iface.openTrade( wasmMsgBytes, 0, totalBank, erc20Amount, toTxOverrides(gas) ) console.log("Transaction submitted:", tx.hash) const receipt = await tx.wait() console.log("Mined in block:", receipt?.blockNumber) } Copy async function closeTrade() { const { signer } = makeSigner() const iface = new ethers.Contract( process.env.INTERFACE_ADDR!, PerpVaultEvmInterfaceAbi.abi, signer ) const msg = { close_trade: { trade_index: "UserTradeIndex(0)" } } const wasmMsgBytes = ethers.toUtf8Bytes(JSON.stringify(msg)) const gas = await estimateGasWithFallback(() => iface.executeSimpleFunctions.estimateGas(wasmMsgBytes) ) const tx = await iface.executeSimpleFunctions(wasmMsgBytes, toTxOverrides(gas)) const receipt = await tx.wait() console.log("Trade closed in block:", receipt?.blockNumber) } Copy async function createReferralCode() { const { signer } = makeSigner() const iface = new ethers.Contract( process.env.INTERFACE_ADDR!, PerpVaultEvmInterfaceAbi.abi, signer ) const msg = { create_referrer_code: { code: "MYCODE" } } const bytes = ethers.toUtf8Bytes(JSON.stringify(msg)) const gas = await estimateGasWithFallback(() => iface.executeSimpleFunctions.estimateGas(bytes) ) const tx = await iface.executeSimpleFunctions(bytes, toTxOverrides(gas)) await tx.wait() console.log("Referral code created") } Copy async function redeemReferralCode() { const { signer } = makeSigner() const iface = new ethers.Contract( process.env.INTERFACE_ADDR!, PerpVaultEvmInterfaceAbi.abi, signer ) const msg = { redeem_referrer_code: { code: "PARTNER" } } const bytes = ethers.toUtf8Bytes(JSON.stringify(msg)) const gas = await estimateGasWithFallback(() => iface.executeSimpleFunctions.estimateGas(bytes) ) const tx = await iface.executeSimpleFunctions(bytes, toTxOverrides(gas)) await tx.wait() console.log("Referral code redeemed") } Copy async function depositToVault() { const { signer } = makeSigner() const iface = new ethers.Contract( process.env.INTERFACE_ADDR!, PerpVaultEvmInterfaceAbi.abi, signer ) const msg = { deposit: {} } const bytes = ethers.toUtf8Bytes(JSON.stringify(msg)) const bankDecimals = 6 const erc20Decimals = 6 const bankAmount = ethers.parseUnits("250", bankDecimals) const erc20Amount = ethers.parseUnits("250", erc20Decimals) const toBankUnits = ( amt: bigint, ercDec: number, bankDec: number ): bigint => { if (ercDec === bankDec) return amt const diff = Math.abs(ercDec - bankDec) const factor = 10n ** BigInt(diff) return ercDec > bankDec ? amt / factor : amt * factor } const totalBank = bankAmount + toBankUnits(erc20Amount, erc20Decimals, bankDecimals) const vaultAddr = "" const collateralErc20 = "" const gas = await estimateGasWithFallback(() => iface.deposit.estimateGas( bytes, totalBank, erc20Amount, vaultAddr, collateralErc20, true ) ) const tx = await iface.deposit( bytes, totalBank, erc20Amount, vaultAddr, collateralErc20, true, toTxOverrides(gas) ) await tx.wait() console.log("Deposited to vault") } Copy async function makeWithdrawRequest() { const { signer } = makeSigner() const iface = new ethers.Contract( process.env.INTERFACE_ADDR!, PerpVaultEvmInterfaceAbi.abi, signer ) const msg = { make_withdraw_request: {} } const bytes = ethers.toUtf8Bytes(JSON.stringify(msg)) const sharesBank = 1_000_000n // 1.0 shares in BANK units const sharesFromErc20 = 0n const totalShares = sharesBank + sharesFromErc20 const vaultAddr = "" const gas = await estimateGasWithFallback(() => iface.makeWithdrawRequest.estimateGas(vaultAddr, bytes, totalShares, sharesFromErc20) ) const tx = await iface.makeWithdrawRequest( vaultAddr, bytes, totalShares, sharesFromErc20, toTxOverrides(gas) ) await tx.wait() console.log("Withdraw request created") } Copy async function redeemVaultShares() { const { signer } = makeSigner() const iface = new ethers.Contract( process.env.INTERFACE_ADDR!, PerpVaultEvmInterfaceAbi.abi, signer ) const msg = { redeem: { shares: "500000" } } // 0.5 shares const bytes = ethers.toUtf8Bytes(JSON.stringify(msg)) const vaultAddr = "" const sendToEvm = true const gas = await estimateGasWithFallback(() => iface.redeem.estimateGas(bytes, vaultAddr, "500000", sendToEvm) ) const tx = await iface.redeem( bytes, vaultAddr, "500000", sendToEvm, toTxOverrides(gas) ) await tx.wait() console.log("Shares redeemed") } Copy async function cancelWithdrawRequest() { const { signer } = makeSigner() const iface = new ethers.Contract( process.env.INTERFACE_ADDR!, PerpVaultEvmInterfaceAbi.abi, signer ) const msg = { cancel_withdraw_request: { unlock_epoch: 123 } } const bytes = ethers.toUtf8Bytes(JSON.stringify(msg)) const vaultAddr = "" const gas = await estimateGasWithFallback(() => iface.executeVaultSimpleFunctions.estimateGas(bytes, vaultAddr) ) const tx = await iface.executeVaultSimpleFunctions( bytes, vaultAddr, toTxOverrides(gas) ) await tx.wait() console.log("Withdraw request cancelled") } --- # Intro to Sai | Sai.fun ![Page cover](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-7789701fcdeea50f019c4517735836a64e2fa2a8%252F0000_sai_header.png%3Falt%3Dmedia&width=1248&dpr=3&quality=100&sign=e75e7ba&sv=2) Over the last few years, crypto trading has moved from spot markets to derivatives. Perpetual futures, or perps, now drive most activity, and a new wave of perpetual DEX apps has proven that crypto perpetual futures are the product many traders care about most. Crypto trading has evolved, but the user experience hasn't kept up. Traders are still forced to choose: the performance of a CEX, or the security of a DEX. Sai is a decentralized perpetuals platform built to deliver CEX-level performance with an intuitive UX and reliable settlement. The system provides easy access to yield, reinforces collateral and features protection within the trading experience. Traders get a familiar, streamlined workflow, while liquidity providers gain a transparent path to earning from real activity. ![Sai](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-5d8d2e24020dc49ba31c11b1d5bc974954c1f530%252F001_why-sai-slide.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=538b75&sv=2)![Sai](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-5d8d2e24020dc49ba31c11b1d5bc974954c1f530%252F001_why-sai-slide.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=538b75&sv=2) Trading made simple. Perps without limits. Sai combines: * Oracle settled pricing that tracks the global market * Flexible collateral so capital can be reused across positions * Single asset vaults that let anyone earn fees from real trading ![Sai](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-fd73b9cb20e2a473c86710abd15d1891367def4a%252F002_sai-trading.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=a7e8b29b&sv=2)![Sai](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-fd73b9cb20e2a473c86710abd15d1891367def4a%252F002_sai-trading.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=a7e8b29b&sv=2) Sai’s Trading Interface [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#what-traders-get-with-sai) What Traders Get with Sai --------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#fair-pricing) Fair Pricing Trades settle at oracle based prices that reflect global markets. Your fills and liquidations follow a reliable price feed. This reduces scam wicks, increases confidence when sizing larger positions, and makes Sai feel like a low slippage DEX even when liquidity appears light. ### [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#funding-that-is-easy-to-understand) Funding that is easy to understand Many perps platforms use funding payments in fixed windows. This can turn funding into a timing game. Sai uses a continuous borrow fee that updates every block based on open interest. You pay a clear borrow cost instead of needing to remember when the next funding window hits: * Others have payments in fixed windows * Sai has continuous block by block payments * Clear borrow costs instead of timing the windows ### [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#collateral-that-works-cross-chain) Collateral that works cross-chain Sai will soon support multiple collateral types, such as USDC and yield-bearing assets like stNIBI. You can deposit USDC from chains like Ethereum or Base, and the protocol treats it the same once it arrives. ![- Deposit USDC from major chains such as Ethereum or Base\ - Use yield-bearing assets as collateral so capital can keep earning while you trade\ - Open and manage all positions from a single margin balance instead of splitting funds across pairs or platforms\ - Deposit funds into Sai using a checking, debit, or credit card through integrated on-ramp partners, so you can start trading without using a separate exchange](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-2ffe745514bd77f0d8c914b868a1c79ee8a3bd85%252F006_sai-in-practice.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=9283e35a&sv=2)![- Deposit USDC from major chains such as Ethereum or Base\ - Use yield-bearing assets as collateral so capital can keep earning while you trade\ - Open and manage all positions from a single margin balance instead of splitting funds across pairs or platforms\ - Deposit funds into Sai using a checking, debit, or credit card through integrated on-ramp partners, so you can start trading without using a separate exchange](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-2ffe745514bd77f0d8c914b868a1c79ee8a3bd85%252F006_sai-in-practice.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=9283e35a&sv=2) ### [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#how-sai-compares-to-other-platforms) How Sai compares to other platforms Under the hood, Sai is built around a simple question: what would the best perpetual platform look like if it was designed from scratch? #### [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#oracle-native-perps-model) Oracle-native Perps Model Aspect Details **Why it works** Oracle settled pricing, continuous borrow fee, single asset vaults, flexible collateral **Impact on Traders** Fairer fills, more, predictable costs, clear roles for traders and LPs #### [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#sai-vs-legacy-models) Sai vs Legacy Models Platform Type Common Issues Impact on Traders Centralized Perp Exchanges Custody risk, opaque liquidation logic, possible withdrawal limits Funds are not fully under user control AMM Style Perp DEXs Funding games, skewed vaults, limited markets Unclear costs, LPs take uneven risk Hybrid Off-Chain Engines Extra trust in sequencers or keepers, downtime risk Users rely on a middle layer instead of the chain itself Sai Oracle settled pricing, continuous borrow fee, single asset vaults, flexible collateral Fairer fills, more predictable costs, clear roles for traders and LPs Sai tries to answer a simple question: what would a best perpetual DEX look like if it stayed fully onchain and focused on a small set of strong ideas. [See how trading works on Saiarrow-up-right](https://docs.sai.fun/) . [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#sais-key-design-choices) Sai’s Key Design Choices ------------------------------------------------------------------------------------------------------------------ Under the hood, Sai is meant to be simple. Sai Features What it Means Oracle Settled Execution Custody risk, opaque liquidation logic, possible withdrawal limits Continuous Borrow Fee Funding adjusts per block with OI skew Passive LP via SLP Vaults Sai V2 allows collateral deposits of USDC or stNIBI. Users can earn a share of fees without managing pairs Real Fee Perks Volume-tier discounts and perks for ecosystem users Aligned Economics Revenue flows back to buybacks and ecosystem growth Multi VM Performance Built on Nibiru's EVM + Wasm stack for smooth integrations Fast Market Onboarding New markets can list quickly once an oracle feed and liquidity are ready [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#how-does-sai-handle-liquidations) How Does Sai Handle Liquidations? ------------------------------------------------------------------------------------------------------------------------------------ Liquidations are where most perp platforms break during liquidity crunches. [The Oct 10 flash crasharrow-up-right](https://www.coindesk.com/opinion/2025/12/02/why-the-market-crashed-on-october-10-and-why-it-s-struggling-to-bounce) showed how a sharp move on one venue can trigger a chain of forced closes, even when the broader market never truly traded there. Sai liquidates using onchain rules and a global oracle price, not a thin local order book. Validator feeders pull prices from multiple venues using a commit then reveal flow. The chain only accepts updates when participation clears a threshold, then publishes a weighted median price. If data is stale or participation is low, Sai uses the last good price and caps large moves so a single bad update does not snowball into unfair liquidations. Liquidation is meant to be a last resort. Sai uses a deep buffer, with liquidations only triggering once losses and fees have consumed roughly 90 percent of posted collateral. Your liquidation price is deterministic and includes fees, so it is predictable. If you set a stop loss that would exit earlier than liquidation, Sai prioritizes the stop loss instead of forcing a liquidation. Any remaining collateral after closing costs is returned to the trader. ### [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#built-for-stress) Built for stress * Oracle guardrails (multi source median, participation checks, last good price fallback, capped large moves) * Exposure limits so single markets do not get dangerously crowded * Stop loss checks so orders cannot be set in a way that only triggers after liquidation * Onchain enforcement with permissionless triggering, so execution stays fast and transparent when markets move quickly [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#sai-as-an-all-market-platform) Sai as an All-Market Platform ----------------------------------------------------------------------------------------------------------------------------- With oracle based pricing and pooled SLP liquidity, Sai can list any asset that has a reliable price feed and strong market demand. ![How new markets get created\ - **Fee routing:** A portion of trading fees from liquid pairs like BTC and ETH can be directed to seed vaults for less liquid markets\ - **Revenue recycling:** As platform revenue grows, funds can be allocated into SLPs, deepening liquidity\ - **Auto-seeding:** When demand crosses a threshold (open interest, votes, or community signals), Sai can seed initial liquidity and spin up that market](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-137f2d730bea0a8ee81cba1419afc06e8f436d76%252F009_sai-new-markets.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=6328ec09&sv=2)![How new markets get created\ - **Fee routing:** A portion of trading fees from liquid pairs like BTC and ETH can be directed to seed vaults for less liquid markets\ - **Revenue recycling:** As platform revenue grows, funds can be allocated into SLPs, deepening liquidity\ - **Auto-seeding:** When demand crosses a threshold (open interest, votes, or community signals), Sai can seed initial liquidity and spin up that market](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-137f2d730bea0a8ee81cba1419afc06e8f436d76%252F009_sai-new-markets.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=6328ec09&sv=2) ### [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#what-this-unlocks-for-traders) What this unlocks for traders * Tokenized stocks and IPOs that trade around the clock * Commodities such as gold or oil through onchain futures * Synthetic indexes, such as sector or theme based baskets For many users, this is what a modern decentralized derivatives exchange should look like: one adaptable venue always ready for the next phase of decentralized finance. ### [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#what-this-unlocks-for-lps) What this unlocks for LPs * Core vaults gain more fee sources as new markets appear * Different vaults can target different levels of risk and return * In future versions, SLP holders will be able to vote on listings and earn extra rewards for backing new markets early Sai contributors and the community will surface high-potential assets to list. Users can propose and vote on new markets, keeping listings aligned with real demand. When interest is strong, the protocol can seed initial liquidity so trading can start immediately without relying on external market makers. [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#earning-with-sai-liquidity-provision-slp) Earning with Sai Liquidity Provision (SLP) ----------------------------------------------------------------------------------------------------------------------------------------------------- SLPs earn trading fees by providing liquidity through single asset vaults. Vaults can be segmented by volatility profile, giving LPs more control over exposure. At launch, deposits in USDC and stNIBI are supported. ![Sai](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-81ad4e30b9ecfb1e1b912bacb3f561c7df216212%252F012_sai-lp-vaults.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=ad44c76b&sv=2)![Sai](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-81ad4e30b9ecfb1e1b912bacb3f561c7df216212%252F012_sai-lp-vaults.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=ad44c76b&sv=2) Risk note: SLPs are the counterparty to traders. When traders profit, vaults can take losses. When traders lose, LPs earn fees. Each vault will support a curated basket of markets, ranging from bluechips to volatile assets to RWAs. **For example:** * Vault 1: Solana, Bitcoin, Ethereum, and other bluechips * Vault 2: WIF, Pepe, TRUMP, Aster, Hype * Vault 3: S&P 500, Gold Index, Japanese Yen [Provide liquidity for Sai - app.sai.fun.arrow-up-right](https://app.sai.fun/buy-SLP/) [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#sai-roadmap-2026) Sai Roadmap (2026) ----------------------------------------------------------------------------------------------------- Sai will continue shipping in three main verticals: more markets, better tools, and deeper yield. ![Sai](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-6cceea4d4d67c264dac1a25a9672910429f85617%252F014_sai-roadmap.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=99afa101&sv=2)![Sai](https://docs.sai.fun/~gitbook/image?url=https%3A%2F%2F2515721841-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FgB6mtn0kuhxAsB9XeaXx%252Fuploads%252Fgit-blob-6cceea4d4d67c264dac1a25a9672910429f85617%252F014_sai-roadmap.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=99afa101&sv=2) ### [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#near-term-focus) Near term focus * **Real world asset markets:** Add new pairs, through crypto, stocks and commodities to grow liquidity on current markets * **Refined trading interface:** Continue improvements for a simple interface for new users and fast execution for active traders * **DeFi integrations:** Connect Sai to other DeFi apps for swaps, routing, and using SLPs as collateral through the earn tab ### [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#new-products-and-yield) New products and yield * **Sai Savings**: A way for traders to earn yield on their idle stables parked on Sai, 5% on all idle funds while maintaining control and flexibility * **Automated strategies:** Launch vaults that run user managed strategies on Sai so users can get exposure with one click * **Accounts:** A true CEX UX with gasless trades, multi-chain and fiat deposits ### [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#ecosystem-and-access) Ecosystem and access * **Cross chain and fiat funding:** Support more networks for USDC deposits and improve on ramps so users can fund with a card or bank account * **Mobile app:** Release a mobile app that covers core trading and portfolio management * **Comprehensive tooling:** Robust tooling for developers to build apps on the Sai platform * Data platform: Backtesting, historical data, custom order types, and automated strategies Sai’s vision is simple: a platform that matches CEX scale while staying transparent and onchain, and expands into whatever markets traders demand. [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#stay-updated-with-sai) Stay Updated with Sai ------------------------------------------------------------------------------------------------------------- Don't miss what's coming next. Follow us for real-time updates on: * **Beta Launch**: Early access opportunities and testing phases * **Feature Releases**: New trading pairs, vault strategies, and platform upgrades * **Developer News**: API changes, smart contract updates, and integration guides * **Community Events**: Trading competitions, AMAs, and governance discussions ### [hashtag](https://docs.sai.fun/resources/blogs/intro-to-sai#follow-sai) Follow Sai X / Twitter Telegram Stay updated on announcements, market insights, and community highlights. [Follow @SaiDotFunarrow-up-right](https://x.com/SaiDotFun) Quick updates and community coordination. [Join Telegramarrow-up-right](https://t.me/saidotfun) _Published: December 16, 2025_ [PreviousBlogchevron-left](https://docs.sai.fun/resources/blogs) [NextLegalchevron-right](https://docs.sai.fun/resources/legal) Last updated 1 month ago * [What Traders Get with Sai](https://docs.sai.fun/resources/blogs/intro-to-sai#what-traders-get-with-sai) * [Fair Pricing](https://docs.sai.fun/resources/blogs/intro-to-sai#fair-pricing) * [Funding that is easy to understand](https://docs.sai.fun/resources/blogs/intro-to-sai#funding-that-is-easy-to-understand) * [Collateral that works cross-chain](https://docs.sai.fun/resources/blogs/intro-to-sai#collateral-that-works-cross-chain) * [How Sai compares to other platforms](https://docs.sai.fun/resources/blogs/intro-to-sai#how-sai-compares-to-other-platforms) * [Sai’s Key Design Choices](https://docs.sai.fun/resources/blogs/intro-to-sai#sais-key-design-choices) * [How Does Sai Handle Liquidations?](https://docs.sai.fun/resources/blogs/intro-to-sai#how-does-sai-handle-liquidations) * [Built for stress](https://docs.sai.fun/resources/blogs/intro-to-sai#built-for-stress) * [Sai as an All-Market Platform](https://docs.sai.fun/resources/blogs/intro-to-sai#sai-as-an-all-market-platform) * [What this unlocks for traders](https://docs.sai.fun/resources/blogs/intro-to-sai#what-this-unlocks-for-traders) * [What this unlocks for LPs](https://docs.sai.fun/resources/blogs/intro-to-sai#what-this-unlocks-for-lps) * [Earning with Sai Liquidity Provision (SLP)](https://docs.sai.fun/resources/blogs/intro-to-sai#earning-with-sai-liquidity-provision-slp) * [Sai Roadmap (2026)](https://docs.sai.fun/resources/blogs/intro-to-sai#sai-roadmap-2026) * [Near term focus](https://docs.sai.fun/resources/blogs/intro-to-sai#near-term-focus) * [New products and yield](https://docs.sai.fun/resources/blogs/intro-to-sai#new-products-and-yield) * [Ecosystem and access](https://docs.sai.fun/resources/blogs/intro-to-sai#ecosystem-and-access) * [Stay Updated with Sai](https://docs.sai.fun/resources/blogs/intro-to-sai#stay-updated-with-sai) * [Follow Sai](https://docs.sai.fun/resources/blogs/intro-to-sai#follow-sai) --- # Perp: Borrowing Module | Sai.fun The **Borrowing Module** is a crucial part of the perpetual futures trading system. Its primary responsibilities include: * **Calculating and accumulating borrowing fees** for leveraged positions. * **Tracking open interest (OI)** for trading pairs and borrowing groups. * **Enforcing exposure limits** to ensure trading activity remains within defined thresholds. This document explains how the borrowing process works—from how **borrowing groups** are structured to the logic that updates them. It also covers the lifecycle of borrowing fees, from when a trade is opened until it is closed or liquidated. * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#key-components) Key Components --------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#data-structures) Data Structures 1. **BorrowingData** * **Purpose**: Stores fee rates and the accumulated fees for both pairs and groups. * **Fields**: * `fee_per_block`: Base fee rate per block. * `acc_fee_long`: Accumulated fee for long positions. * `acc_fee_short`: Accumulated fee for short positions. * `acc_last_updated_block`: Block number of the most recent fee update. * `fee_exponent`: Adjusts sensitivity for fee calculation based on open interest imbalances. 2. **BorrowingPairGroup** * **Purpose**: Captures the historical group associations for borrowing pairs. * **Fields**: * `group_index`: Identifier for the borrowing group. * `block`: Block number at which the group association was set. * `initial_acc_fee_long`: Initial accumulated fee for long positions. * `initial_acc_fee_short`: Initial accumulated fee for short positions. * `prev_group_acc_fee_long`: Previous group's accumulated fee for long positions. * `prev_group_acc_fee_short`: Previous group's accumulated fee for short positions. * `pair_acc_fee_long`: Accumulated fee for the pair's long positions at the time of group change. * `pair_acc_fee_short`: Accumulated fee for the pair's short positions at the time of group change. 3. **OpenInterest** * **Purpose**: Tracks open interest (total position sizes) for both pairs and groups. * **Fields**: * `long`: Total open interest for long positions. * `short`: Total open interest for short positions. * `max`: Maximum allowable open interest. 4. **BorrowingInitialAccFees** * **Purpose**: Records initial accumulated fees for a trade at the moment it opens. * **Fields**: * `acc_pair_fee`: Pair's accumulated fee when the trade opened. * `acc_group_fee`: Group's accumulated fee when the trade opened. * `block`: Block number when the trade was opened. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#storage-maps) Storage Maps * `**PAIRS**`: Stores `BorrowingData` for each trading pair. * `**PAIR_GROUPS**`: Stores a list of `BorrowingPairGroup` records for each pair (capturing historical group changes). * `**PAIR_OIS**`: Stores `OpenInterest` for each pair. * `**GROUPS**`: Stores `BorrowingData` for each borrowing group. * `**GROUP_OIS**`: Stores `OpenInterest` for each borrowing group. * `**INITIAL_ACC_FEES**`: Stores `BorrowingInitialAccFees` for each trade. * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#borrowing-groups) Borrowing Groups ------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#what-is-a-borrowing-group) What is a Borrowing Group? A **Borrowing Group** is a set of trading pairs that share common borrowing parameters. It simplifies fee management and exposure control by allowing: * **Collective fee adjustments**: Update borrowing fees for multiple pairs simultaneously. * **Shared exposure limits**: Manage cumulative open interest across all pairs in the group. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#role-of-borrowing-groups) Role of Borrowing Groups * **Fee Adjustments**: Updates borrowing fees in response to market conditions—affecting all pairs in the group at once. * **Exposure Control**: Aggregates open interest to ensure the group's overall risk remains within acceptable limits. * **Historical Fee Tracking**: Logs when pairs change groups so that fee calculations remain accurate for open trades. * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#how-borrowing-groups-are-updated) How Borrowing Groups Are Updated --------------------------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#setting-borrowing-group-parameters) Setting Borrowing Group Parameters Use `set_borrowing_group_params` to create or update a borrowing group's parameters: * **Key Parameters**: * `fee_per_block`: Base borrowing fee rate per block. * `fee_exponent`: Exponent to scale fees based on open interest imbalances. * `max_oi`: Maximum allowable open interest for the group. * `current_block`: Current block number (for fee updates). * **Process**: 1. **Validation**: Ensures `fee_exponent` is within allowed bounds (1% to 300%). 2. **Update Accumulated Fees**: Calls `set_group_pending_acc_fees` to synchronize the group's fees up to `current_block`. 3. **Store New Data**: Updates the group's parameters in `GROUPS`. 4. **Adjust Open Interest Limit**: Sets `max_oi` in `GROUP_OIS`. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#changing-a-pairs-borrowing-group) Changing a Pair's Borrowing Group When calling `set_borrowing_pair_params` to place a pair in a new borrowing group: * **Key Parameters**: * `group_index`: The new group index to assign to the pair. * **Process**: 1. **Fee Synchronization**: Updates fees for both the old and new groups via `set_group_pending_acc_fees`. 2. **Open Interest Update**: Removes the pair's open interest from the old group and adds it to the new group using `update_group_oi`. 3. **Record Group Change**: Appends a new `BorrowingPairGroup` entry to `PAIR_GROUPS`, capturing relevant fee data and the block number. 4. **Update Pair Data**: Adjusts any pair-specific borrowing parameters in `PAIRS`. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#updating-open-interest) Updating Open Interest Open interest is recalculated whenever trades open or close: * **Key Functions**: * `update_pair_oi`: Updates pair-level open interest in `PAIR_OIS`. * `update_group_oi`: Updates group-level open interest in `GROUP_OIS`. * **Process**: * **Opening a Trade**: Increases the `long` or `short` OI depending on the trade's direction. * **Closing a Trade**: Decreases the `long` or `short` OI accordingly. * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#logic-behind-updates) Logic Behind Updates --------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#accumulating-borrowing-fees) Accumulating Borrowing Fees Borrowing fees grow over time, affected by OI imbalances: * **Fee Formula**: * `fee_per_block`: Base fee rate per block. * `current_block - acc_last_updated_block`: Blocks elapsed since the last fee update. * `net_oi`: Absolute difference between `long` and `short` OI. * `max_oi`: Maximum allowed open interest (for the pair or group). * `fee_exponent`: Exponent that increases fees exponentially with larger OI imbalances. * **Side-Specific Fee Accumulation**: * Fees accrue only on the side (long or short) that has the larger open interest, incentivizing a more balanced market. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#handling-trades) Handling Trades #### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#opening-a-trade) Opening a Trade When a new trade is opened: * **Function**: `handle_trade_borrowing` * **Fee Synchronization**: Calls `set_pair_pending_acc_fees` and `set_group_pending_acc_fees` to update fees. * **Open Interest**: Updates pair and group OI via `update_pair_oi` and `update_group_oi`. * **Initial Fee Recording**: Invokes `reset_trade_borrowing_fees` to log the accumulated fees at the time the trade opens. #### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#closing-a-trade) Closing a Trade When an existing trade closes: * **Function**: `handle_trade_borrowing` * **Fee Synchronization**: Updates fees up to the current block. * **Open Interest**: Reduces the pair and group OI (long or short) accordingly. * **Borrowing Fee Calculation**: * **Function**: `get_trade_borrowing_fees` * Computes the difference between the fees at closure vs. the `INITIAL_ACC_FEES` from trade open. * Accounts for any group changes that happened during the trade. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#adjusting-for-group-changes) Adjusting for Group Changes If a pair moves to a new group while a trade is open: * **Challenge**: The position's borrowing fees must reflect fees from both the old group and the new group. * **Solution**: * Use `get_borrowing_pair_group_acc_fees_deltas` to determine the fee delta across group transitions. * Sum the larger of (pair delta, group delta) for each period to get total fees. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#calculating-liquidation-price-with-fees) Calculating Liquidation Price with Fees To calculate liquidation prices, including borrowing fees: * **Function**: `get_trade_liquidation_price_with_fees` * **Includes**: * Closing fees (e.g., close fee, trigger order fee) * Borrowing fees from `get_trade_borrowing_fees` * **Purpose**: Computes the final liquidation price after deducting all fees. * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#process-flow-summary) Process Flow Summary --------------------------------------------------------------------------------------------------------------- A high-level look at how trades interact with the Borrowing Module: 1. **Trade Opening** * **User Action**: Opens a new trade. * **System**: 1. Uses `handle_trade_borrowing` to sync fees and update OI. 2. Stores initial accrued fees in `INITIAL_ACC_FEES`. 2. **During Trade** * **System**: * Continues to accumulate fees over time (not updated in storage unless an event triggers an update, like another trade). 3. **Trade Closing** * **User Action**: Closes an existing trade. * **System**: 1. Calls `handle_trade_borrowing` to update fees and OI. 2. Uses `get_trade_borrowing_fees` to compute the total borrowing fees. 3. Finalizes the trade, deducting any closing fees and borrowing fees. 4. **Parameter Updates** * **Admin Action**: Updates group or pair parameters. * **System**: 1. Syncs fees up to the current block. 2. Updates open interest if a pair's group changes. * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#detailed-function-explanations) Detailed Function Explanations ----------------------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#handle_trade_borrowing) `handle_trade_borrowing` **Purpose**: Coordinates the borrowing logic whenever a trade opens or closes. **Workflow**: 1. **Pending Fees Update**: Invokes `set_pair_pending_acc_fees` and `set_group_pending_acc_fees`. 2. **Open Interest Update**: Adjusts OI via `update_pair_oi` and `update_group_oi`. 3. **Reset Initial Fees** (for new trades): Stores current fees in `INITIAL_ACC_FEES`. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#get_trade_borrowing_fees) `get_trade_borrowing_fees` **Purpose**: Calculates total borrowing fees for a trade upon closure. **Workflow**: 1. **Load Initial Fees**: Retrieves `INITIAL_ACC_FEES` for the trade. 2. **Compute Current Fees**: Uses `get_direction_borrowing_pair_pending_acc_fees` and `get_direction_borrowing_group_pending_acc_fees`. 3. **Group Transition Handling**: Applies `get_borrowing_pair_group_acc_fees_deltas` for fee periods affected by group changes. 4. **Final Fee Calculation**: `(Current Acc Fees - Initial Acc Fees) * (Trade Collateral * Leverage)`. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#get_borrowing_pending_acc_fees) `get_borrowing_pending_acc_fees` **Purpose**: Calculates the additional fees that accumulated between the last update and the current block. **Workflow**: 1. **Identify OI Imbalance**: Determines whether long or short positions exceed the other. 2. **Calculate Fee Delta**: Uses the fee formula to calculate newly accrued fees. 3. **Add Fee Delta**: Applies the calculated delta to either `acc_fee_long` or `acc_fee_short`. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#reset_trade_borrowing_fees) `reset_trade_borrowing_fees` **Purpose**: Logs the borrow-related fees at the moment a trade is opened. **Workflow**: * Saves the current `BorrowingData` for both pair and group into `INITIAL_ACC_FEES`, including the current block number. * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#example-scenario) Example Scenario ------------------------------------------------------------------------------------------------------- Consider a pair that transitions from **Group A** to **Group B** while a trade is open: 1. **Block 100** * **Trade Opened**: * Trader opens a long position on Pair X. * Pair X is in **Group A**. * Accumulated Fees: 2% for the pair, 1% for the group. * Initial fees are recorded in `INITIAL_ACC_FEES`. 2. **Block 150** * **Group Change**: * Pair X is moved from **Group A** to **Group B**. * System updates all relevant fees for Group A up to Block 150, then assigns Pair X to Group B. * The group transition is recorded in `PAIR_GROUPS`. 3. **Block 200** * **Trade Closed**: * System updates fees for Pair X and Group B up to Block 200. * It calculates borrowing fees via `get_trade_borrowing_fees`: * Considers the 2%/1% from **Group A** until Block 150. * Includes the new rates from **Group B** from Block 150 to 200. * Final settlement occurs, including close fees and borrowing fees. * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#visualization-of-logic-flow) Visualization of Logic Flow ----------------------------------------------------------------------------------------------------------------------------- * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#key-takeaways) Key Takeaways ------------------------------------------------------------------------------------------------- The Borrowing Module is vital to mastering the perpetual futures platform. It ensures accurate fee accrual, consistent open interest tracking, and robust exposure management. * **Borrowing Groups**: Centralize exposure limits and streamline fee adjustments for multiple pairs. * **Accumulated Fees**: Updated based on time, OI imbalances, and fee parameters. * **Open Interest**: Tracked at both the pair and group level, essential for risk control. * **Trade Lifecycle**: * **Opening** a trade sets the initial fees. * **Closing** a trade calculates total borrowing fees and updates open interest. By carefully managing how trades interact with pairs and borrowing groups, the module ensures fairness, clarity, and security for leveraged traders and the platform as a whole. [PreviousPerp Contractchevron-left](https://docs.sai.fun/for-devs/sai-core/perp-contract) [NextPerp: Price Impact Modulechevron-right](https://docs.sai.fun/for-devs/sai-core/perp-price-impact) Last updated 4 months ago * [Key Components](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#key-components) * [Data Structures](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#data-structures) * [Storage Maps](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#storage-maps) * [Borrowing Groups](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#borrowing-groups) * [What is a Borrowing Group?](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#what-is-a-borrowing-group) * [Role of Borrowing Groups](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#role-of-borrowing-groups) * [How Borrowing Groups Are Updated](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#how-borrowing-groups-are-updated) * [Setting Borrowing Group Parameters](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#setting-borrowing-group-parameters) * [Changing a Pair's Borrowing Group](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#changing-a-pairs-borrowing-group) * [Updating Open Interest](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#updating-open-interest) * [Logic Behind Updates](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#logic-behind-updates) * [Accumulating Borrowing Fees](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#accumulating-borrowing-fees) * [Handling Trades](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#handling-trades) * [Adjusting for Group Changes](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#adjusting-for-group-changes) * [Calculating Liquidation Price with Fees](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#calculating-liquidation-price-with-fees) * [Process Flow Summary](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#process-flow-summary) * [Detailed Function Explanations](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#detailed-function-explanations) * [handle\_trade\_borrowing](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#handle_trade_borrowing) * [get\_trade\_borrowing\_fees](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#get_trade_borrowing_fees) * [get\_borrowing\_pending\_acc\_fees](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#get_borrowing_pending_acc_fees) * [reset\_trade\_borrowing\_fees](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#reset_trade_borrowing_fees) * [Example Scenario](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#example-scenario) * [Visualization of Logic Flow](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#visualization-of-logic-flow) * [Key Takeaways](https://docs.sai.fun/for-devs/sai-core/perp-borrowing#key-takeaways) Copy delta = fee_per_block * (current_block - acc_last_updated_block) * (net_oi / max_oi) ^ fee_exponent Copy graph TD A[Open Trade] --> B[handle_trade_borrowing] B --> C[Set Pending Accumulated Fees for Pair and Group] B --> D[Update Open Interest for Pair and Group] B --> E[reset_trade_borrowing_fees] E --> G[Store Initial Accumulated Fees] subgraph Pair and Group Fees C --> F[get_borrowing_pending_acc_fees] end subgraph Open Interest Updates D --> H[update_pair_oi and update_group_oi] end --- # Perp API | Sai.fun Query and subscribe to perpetual trading data including positions, trade history, market borrowing rates, and referrals. [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-perp#table-of-contents) Table of Contents ----------------------------------------------------------------------------------------------------- * [Perp API Reference](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perp-api-reference) * [Table of Contents](https://docs.sai.fun/for-devs/sai-keeper/api-perp#table-of-contents) * [Queries](https://docs.sai.fun/for-devs/sai-keeper/api-perp#queries) * [trade](https://docs.sai.fun/for-devs/sai-keeper/api-perp#trade) * [trades](https://docs.sai.fun/for-devs/sai-keeper/api-perp#trades) * [tradeHistory](https://docs.sai.fun/for-devs/sai-keeper/api-perp#tradehistory) * [borrowing](https://docs.sai.fun/for-devs/sai-keeper/api-perp#borrowing) * [borrowings](https://docs.sai.fun/for-devs/sai-keeper/api-perp#borrowings) * [Subscriptions](https://docs.sai.fun/for-devs/sai-keeper/api-perp#subscriptions) * [perpTrades](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perptrades) * [perpTradeHistory](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perptradehistory) * [perpBorrowing](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perpborrowing) * [perpBorrowings](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perpborrowings) * [Types Reference](https://docs.sai.fun/for-devs/sai-keeper/api-perp#types-reference) * [PerpTrade](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perptrade) * [PerpTradeState](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perptradestate) * [PerpBorrowing](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perpborrowing-1) * [Enums](https://docs.sai.fun/for-devs/sai-keeper/api-perp#enums) [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-perp#queries) Queries --------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-perp#trade) trade Get a specific trade by ID and trader address. **Signature:** **Parameters:** * `id`: Trade ID (integer) * `trader`: Trader's wallet address (required) **Returns:** `PerpTrade` object or null if not found **Example:** **Use Cases:** * Display single trade details * Track specific position status * Calculate current PnL * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-perp#trades) trades List trades with filtering, pagination, and ordering. **Signature:** **Parameters:** * `where`: Filter object (required) * `trader`: Trader address (required) * `isOpen`: Filter by open/closed status * `perpMarketId`: Filter by market ID * `perpCollateralId`: Filter by collateral token ID * `limit`: Max results (default: 100) * `offset`: Skip results for pagination * `order_by`: Sort field (`sequence` only) * `order_desc`: Sort descending (default: false) **Returns:** Array of `PerpTrade` objects **Example - Get Open Positions:** **Example - Filter by Market:** **Use Cases:** * List user's open positions * Display trading history * Filter by specific markets * Build position dashboard * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-perp#tradehistory) tradeHistory Get trade events and position changes. **Signature:** **Parameters:** * `where`: Filter object (required) * `trader`: Trader address (required) * `limit`: Max results (default: 100) * `offset`: Skip results * `order_by`: Sort by `sequence` or `trade_id` * `order_desc`: Sort descending **Returns:** Array of `PerpTradeHistoryItem` objects **Trade Change Types:** * `position_opened`: New position created * `position_closed_user`: User closed position * `position_closed_sl`: Stop loss triggered * `position_closed_tp`: Take profit triggered * `position_liquidated`: Position liquidated * `sl_updated`: Stop loss updated * `tp_updated`: Take profit updated * `limit_order_created`: Limit order placed * `stop_order_created`: Stop order placed * `order_triggered`: Order executed * `order_closed_user`: Order cancelled **Example:** **Use Cases:** * Build activity feed * Track position modifications * Calculate realized PnL history * Audit trade actions * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-perp#borrowing) borrowing Get detailed borrowing information for a specific market and collateral. **Signature:** **Parameters:** * `collateralId`: Collateral token ID * `marketId`: Market ID **Returns:** `PerpBorrowing` object or null **Example:** **Key Fields Explained:** * `oiLong` / `oiShort`: Open interest for longs/shorts * `oiMax`: Maximum allowed open interest * `feesPerHourLong` / `feesPerHourShort`: Borrowing rate (per hour) * `openFeePct` / `closeFeePct`: Opening/closing fee percentages * `maxLeverage` / `minLeverage`: Leverage limits * `minPositionSizeUSD`: Minimum position size in USD **Use Cases:** * Display market information * Calculate funding rates * Show available leverage * Check market capacity (OI limits) * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-perp#borrowings) borrowings List all available markets with basic information. **Signature:** **Parameters:** * `where`: Filter object (optional) * `marketId`: Filter by market ID * `collateralId`: Filter by collateral ID * `limit`: Max results * `offset`: Skip results * `order_by`: Sort by `base_token_name` * `order_desc`: Sort descending **Returns:** Array of `PerpBorrowingShortInfo` objects **Example:** **Use Cases:** * List all tradeable markets * Build market selector * Filter visible markets * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-perp#subscriptions) Subscriptions --------------------------------------------------------------------------------------------- Subscribe to real-time updates for perpetual trading data. ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perptrades) perpTrades Subscribe to trade updates for a specific trader. **Signature:** **Example:** * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perptradehistory) perpTradeHistory Subscribe to trade events for a specific trader. **Signature:** **Example:** * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perpborrowing) perpBorrowing Subscribe to borrowing rate updates for a specific market. **Signature:** **Example:** * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perpborrowings) perpBorrowings Subscribe to updates for all markets. **Signature:** **Example:** * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-perp#types-reference) Types Reference ------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perptrade) PerpTrade ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perptradestate) PerpTradeState Real-time position state with PnL and fees. ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perpborrowing-1) PerpBorrowing Complete market information. ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-perp#enums) Enums **PerpTradeType:** * `trade`: Market trade * `limit`: Limit order * `stop`: Stop order **PerpTradeChangeType:** * `position_opened` * `position_closed_user` * `position_closed_sl` * `position_closed_tp` * `position_liquidated` * `sl_updated` * `tp_updated` * `limit_order_created` * `stop_order_created` * `order_triggered` * `order_closed_user` * * * [PreviousCore Conceptschevron-left](https://docs.sai.fun/for-devs/sai-keeper/core-concepts) [NextLiquidity Provider (LP) APIchevron-right](https://docs.sai.fun/for-devs/sai-keeper/api-lp) Last updated 3 months ago * [Table of Contents](https://docs.sai.fun/for-devs/sai-keeper/api-perp#table-of-contents) * [Queries](https://docs.sai.fun/for-devs/sai-keeper/api-perp#queries) * [trade](https://docs.sai.fun/for-devs/sai-keeper/api-perp#trade) * [trades](https://docs.sai.fun/for-devs/sai-keeper/api-perp#trades) * [tradeHistory](https://docs.sai.fun/for-devs/sai-keeper/api-perp#tradehistory) * [borrowing](https://docs.sai.fun/for-devs/sai-keeper/api-perp#borrowing) * [borrowings](https://docs.sai.fun/for-devs/sai-keeper/api-perp#borrowings) * [Subscriptions](https://docs.sai.fun/for-devs/sai-keeper/api-perp#subscriptions) * [perpTrades](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perptrades) * [perpTradeHistory](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perptradehistory) * [perpBorrowing](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perpborrowing) * [perpBorrowings](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perpborrowings) * [Types Reference](https://docs.sai.fun/for-devs/sai-keeper/api-perp#types-reference) * [PerpTrade](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perptrade) * [PerpTradeState](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perptradestate) * [PerpBorrowing](https://docs.sai.fun/for-devs/sai-keeper/api-perp#perpborrowing-1) * [Enums](https://docs.sai.fun/for-devs/sai-keeper/api-perp#enums) Copy trade(id: Int!, trader: String!): PerpTrade Copy query GetTrade { perp { trade(id: 42, trader: "nibi1abc...") { id isOpen isLong leverage collateralAmount openPrice closePrice perpBorrowing { baseToken { symbol } collateralToken { symbol } marketId } openBlock { block block_ts } closeBlock { block block_ts } state { pnlCollateral pnlPct liquidationPrice positionValue } } } } Copy trades( where: PerpTradesFilter! limit: Int offset: Int order_by: PerpTradesOrder order_desc: Boolean ): [PerpTrade!]! Copy query GetOpenPositions($trader: String!) { perp { trades( where: { trader: $trader isOpen: true } limit: 50 order_desc: true ) { id isLong leverage collateralAmount openPrice perpBorrowing { baseToken { symbol } marketId } state { pnlCollateral pnlPct liquidationPrice } } } } Copy query GetBTCTrades($trader: String!) { perp { trades( where: { trader: $trader perpMarketId: 1 # BTC market } limit: 20 ) { id isOpen openPrice closePrice } } } Copy tradeHistory( where: PerpTradeHistoryFilter! limit: Int offset: Int order_by: PerpTradeHistoryOrder order_desc: Boolean ): [PerpTradeHistoryItem!]! Copy query GetTradeHistory($trader: String!) { perp { tradeHistory( where: { trader: $trader } limit: 50 order_by: sequence order_desc: true ) { id tradeChangeType block { block block_ts } trade { id isLong leverage } realizedPnlCollateral realizedPnlPct } } } Copy borrowing(collateralId: Int!, marketId: Int!): PerpBorrowing Copy query GetMarketInfo { perp { borrowing(collateralId: 1, marketId: 1) { marketId baseToken { symbol name } quoteToken { symbol } collateralToken { symbol } price oiLong oiShort oiMax feesPerHourLong feesPerHourShort openFeePct closeFeePct triggerOrderFeePct maxLeverage minLeverage minPositionSizeUSD visible } } } Copy borrowings( where: PerpBorrowingsFilter limit: Int offset: Int order_by: PerpBorrowingsOrder order_desc: Boolean ): [PerpBorrowingShortInfo!]! Copy query ListMarkets { perp { borrowings( order_by: base_token_name limit: 20 ) { marketId baseToken { symbol name logoUrl } quoteToken { symbol } collateralToken { symbol } visible } } } Copy subscription { perpTrades(where: SubPerpTradesFilter!): [PerpTrade!]! } Copy subscription WatchTrades($trader: String!) { perpTrades(where: { trader: $trader }) { id isOpen leverage state { pnlCollateral pnlPct } } } Copy subscription { perpTradeHistory(where: SubPerpTradeHistoryFilter!): [PerpTradeHistoryItem!]! } Copy subscription WatchTradeEvents($trader: String!) { perpTradeHistory(where: { trader: $trader }) { id tradeChangeType realizedPnlCollateral block { block_ts } } } Copy subscription { perpBorrowing(collateralId: Int!, marketId: Int!): PerpBorrowing } Copy subscription WatchMarket($collateralId: Int!, $marketId: Int!) { perpBorrowing(collateralId: $collateralId, marketId: $marketId) { price oiLong oiShort feesPerHourLong feesPerHourShort } } Copy subscription { perpBorrowings: [PerpBorrowingShortInfo!]! } Copy subscription WatchAllMarkets { perpBorrowings { marketId baseToken { symbol } visible } } Copy type PerpTrade { id: Int! trader: String! isOpen: Boolean! isLong: Boolean! tradeType: PerpTradeType! leverage: Float! collateralAmount: Int! openCollateralAmount: Int! openPrice: Float! closePrice: Float sl: Float # Stop loss price tp: Float # Take profit price perpBorrowing: PerpBorrowingShortInfo! openBlock: Block closeBlock: Block state: PerpTradeState } Copy type PerpTradeState { pnlCollateral: Float! pnlPct: Float! pnlCollateralAfterFees: Float! positionValue: Float! liquidationPrice: Float! borrowingFeeCollateral: Float! borrowingFeePct: Float! closingFeeCollateral: Float! closingFeePct: Float! remainingCollateralAfterFees: Float! } Copy type PerpBorrowing { marketId: Int! baseToken: Token! quoteToken: Token! collateralToken: Token! price: Float! oiLong: Int! oiShort: Int! oiMax: Int! feesPerHourLong: Float! feesPerHourShort: Float! openFeePct: Float! closeFeePct: Float! triggerOrderFeePct: Float! maxLeverage: Float! minLeverage: Float! minPositionSizeUSD: Int! visible: Boolean! } --- # WASM & EVM Integration | Sai.fun [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#sai-contracts-wasm-and-evm-integration) Sai Contracts – WASM & EVM Integration --------------------------------------------------------------------------------------------------------------------------------------------------------- A practical, copy‑paste friendly reference for integrating with Sai’s perpetuals and SLP vaults from **Cosmos (WASM)** and **EVM** wallets. It documents all calls shown in the code you shared, with params, expected preconditions, and example snippets. > **Environments:** Mainnet and Testnet-2 are both supported. Token routes and contract addresses differ by network; see the **Collateral / Payment Tokens** section. * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#quick-glossary) Quick Glossary * **BANK**: Cosmos-side coins (e.g., `tf/...` denoms). * **ERC-20**: EVM-side tokens (e.g., `0x...`). * **Interface (EVM)**: A single EVM contract that accepts a JSON-encoded WASM message and forwards it cross‑stack to Sai’s modules. * **Perp**: The perpetuals contract/router on the Cosmos side (referenced via `saiContracts.perp`). * **Vault**: SLP vault contracts (Cosmos bech32 addresses) that accept deposits/withdrawals/redeems. * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#collateral-payment-tokens) Collateral / Payment Tokens These are the routes supported in the shared code (`usd`, `stnibi`). Values are used for **funds** on WASM calls and for **ERC‑20 params** on EVM interface calls. #### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#mainnet) Mainnet Route Symbol Display Name bankDenom evmAddr Default ERC‑20 Decimals `usd` USDC USDC `erc20/0x0829F361A05D993d5CEb035cA6DF3446b060970b` `0x0829F361A05D993d5CEb035cA6DF3446b060970b` 6 `stnibi` stNIBI Liquid Staked Nibiru (Wrapped) `tf/nibi1udqqx30cw8nwjxtl4l28ym9hhrp933zlq8dqxfjzcdhvl8y24zcqpzmh8m/ampNIBI` `0xcA0a9Fb5FBF692fa12fD13c0A900EC56Bb3f0a7b` 6 #### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#testnet-2) Testnet‑2 Route Symbol Display Name bankDenom evmAddr Default ERC‑20 Decimals `usd` USDC USDC `tf/nibi1pc2mmwcqhvzn9vsm0umpu40yzl6gfy6nucwn7g/usdc` `0xAb68f1D1d91854383fd4Df9016E3040D03e8191a` 6 `stnibi` stNIBI Liquid Staked Nibiru (Wrapped) `tf/nibi1pc2mmwcqhvzn9vsm0umpu40yzl6gfy6nucwn7g/stnibi` `0xCae3d404AFB50016154a4B18091351065154E9bD` 6 > **Tip:** When calling WASM, you spend BANK units (scaled by token decimals, 6 by default). On EVM, you pass both a **BANK portion** and an **ERC‑20 portion**; these are combined for total amounts. * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#shared-concepts-and-types) Shared Concepts & Types * **Signers** * _WASM signer_ `{ fromBech32Addr, runner: NibiruTxClient }` * _EVM signer_ `{ fromHexAddr, runner?: ContractRunner, signer?: Signer }` (must have `signer` to send tx) * **TradeType Derivation** * `Market → "trade"` * `Limit/Stop` is derived by comparing `limitPriceUsd` to `open_price`: * Long: `limit > open_price` → `stop`, else `limit` * Short: `limit < open_price` → `stop`, else `limit` * **Gas Strategy (EVM)** * Estimate with `contract.method.estimateGas(...)` and pad by ×1.1 * Fallbacks: `gasLimit = 5_000_000`, `gasPrice = 1 gwei` if estimation fails * **ExecuteResult (normalized)** * EVM receipts are converted to a CosmJS‑like `ExecuteResult` with `height`, `transactionHash`, `gasUsed/Wanted`, and `events` (topics/data echoed per log). * **Explorer Links** * UI examples compute `blockUrl` and `txUrl` from `height` and `hash`. * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#evm-integration-via-interface-contract) EVM Integration (via Interface Contract) ----------------------------------------------------------------------------------------------------------------------------------------------------------- All EVM flows call a single **Interface** contract (obtained via `saiEvmInterfaceFromChainType(chainType)`). Each call sends a JSON-encoded WASM message (`wasmMsgBytes`) together with token amounts/addresses. Always ensure an EVM **signer** is available. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-1.-open-perp-trade) 1\. Open Perp Trade **Method:** `openTrade(wasmMsgBytes, collateralIndex, totalAmountBankUnits, useErc20Amount)` **wasmMsg (JSON):** **Amounting:** * Compute BANK portion in **6‑dec units**. * Compute ERC‑20 portion in **token decimals** (default 6; use `collateral.evmDefaultDecimals` if present). * `totalAmount` = `bankAmount + evmAmount` (on‑chain Interface handles bridging/combining). **Preconditions:** * `orderType === "Market"` requires `open_price`. * `collateral` must be present; `amtTrade` must be > 0. * If using smart allocation, split requested amount across BANK/ERC‑20 per wallet balances and preference. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-2-close-perp-trade) 2 Close Perp Trade **Method:** `executeSimpleFunctions(wasmMsgBytes)` **wasmMsg:** ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-3.-referral-create-code) 3\. Referral: Create Code **Method:** `executeSimpleFunctions(wasmMsgBytes)` **wasmMsg:** ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-4.-referral-redeem-code) 4\. Referral: Redeem Code **Method:** `executeSimpleFunctions(wasmMsgBytes)` **wasmMsg:** ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-5.-vault-deposit) 5\. Vault: Deposit **Method:** **Notes:** * Convert ERC‑20 portion to BANK units before summing into `depositAmountTotal` if needed (decimals may differ). ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-6.-vault-make-withdraw-request) 6\. Vault: Make Withdraw Request **Method:** ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-7.-vault-redeem) 7\. Vault: Redeem **Method:** ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-8.-vault-cancel-withdraw-request) 8\. Vault: Cancel Withdraw Request **Method:** `executeVaultSimpleFunctions(wasmMsgBytes, vaultAddress)` **wasmMsg:** * * * [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#wasm-cosmos-integration) WASM (Cosmos) Integration ----------------------------------------------------------------------------------------------------------------------------- WASM calls use the `NibiruTxClient` (`signer.runner.wasmClient`). Funds are passed as Cosmos `Coin { denom, amount }` where `amount` is **BANK units** (1e6‑scaled by default). ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-1.-open-perp-trade-1) 1\. Open Perp Trade **Client:** `wasmClient.execute(sender, saiContracts.perp, msg, fee, memo, funds)` **Msg:** **Funds:** **Preconditions:** * For `Market`, `open_price` is required. * For `Limit/Stop`, `limitPriceUsd` is required. **Optional ERC‑20 → BANK bridging** * If the current BANK balance is insufficient, prepend a `MsgConvertEvmToCoin` to convert ERC‑20 to BANK for the deficit. ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-2.-close-perp-trade) 2\. Close Perp Trade **Client:** `execute(sender, saiContracts.perp, { close_trade: { trade_index: "UserTradeIndex(N)" } }, fee)` ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-3.-referral-create-code-1) 3\. Referral: Create Code **Client:** `execute(sender, saiContracts.perp, { create_referrer_code: { code } }, fee)` ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-4.-referral-redeem-code-1) 4\. Referral: Redeem Code **Client:** `execute(sender, saiContracts.perp, { redeem_referrer_code: { code } }, fee)` ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-5.-vault-deposit-1) 5\. Vault: Deposit **Client:** `execute(sender, vaultSelection.address, { deposit: {} }, fee, memo, [funds])` **Funds:** ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-6.-vault-make-withdraw-request-1) 6\. Vault: Make Withdraw Request **Share Denom Discovery:** Query once before executing: **Client:** * Execute with funds in **share denom**: **Msg:** `{ "make_withdraw_request": {} }` ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-7.-vault-redeem-1) 7\. Vault: Redeem **Client:** `execute(sender, vaultAddress, { redeem: { shares: "..." } }, fee)` (no funds) ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-8.-vault-cancel-withdraw-request-1) 8\. Vault: Cancel Withdraw Request **Client:** `execute(sender, vaultAddress, { cancel_withdraw_request: { unlock_epoch } }, fee)` * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#amounts-and-decimals-gotchas) Amounts & Decimals (Gotchas) * **Default decimals = 6** for BANK. Always scale UI amounts: `display × 10^decimals`. * When mixing BANK and ERC‑20 on **EVM deposit/withdraw**, convert across decimals before summing. * Validate that amounts are **finite, non‑negative, non‑zero** after scaling. * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#error-handling-and-ux-patterns) Error Handling & UX Patterns * **Signer presence**: EVM calls require `signer` (not just a runner). WASM requires `runner`. * **Toast/Notifications**: Use pending/success/failure to surface status. On success, show `height` and `hash` with explorer links. * **Debugging (EVM)**: If a tx reverts, use archive RPC’s `debug_traceTransaction` to extract the deepest `calls[-1].error`. * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#minimal-pseudocode-examples) Minimal Pseudocode Examples > Below are slimmed examples you can adapt; they assume your app has already selected `chainType`, `saiContracts`, `interfaceAddress`, `collateral`, etc. #### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#open-trade-evm) Open Trade (EVM) #### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#open-trade-wasm) Open Trade (WASM) #### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#vault-deposit-evm) Vault Deposit (EVM) #### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#make-withdraw-request-wasm) Make Withdraw Request (WASM) * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#preconditions-checklist-per-call) Preconditions Checklist (per call) * **Open Trade**: amount > 0; Market→`open_price` present; Limit/Stop→`limitPriceUsd` present; `collateral.index` parseable; signer ready. * **Close Trade**: `userTradeIndex` set; signer ready. * **Create/Redeem Referral**: `code` non‑empty; signer ready. * **Vault Deposit**: amounts properly scaled; convert ERC‑20 portion to BANK for totals on EVM; signer ready. * **Withdraw Request**: shares in BANK units; (WASM) share denom fetched; signer ready. * **Redeem**: shares in BANK units; `sendToEvm` flag set (EVM path); signer ready. * **Cancel Withdraw Request**: `unlock_epoch` set; signer ready. * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-core/contract-integration#appendix-utility-notes) Appendix: Utility Notes * `**estimateGasWithFallback**` pads gas by 10% and falls back to `5_000_000 / 1 gwei` if estimation fails. * `**receiptToExecuteResult**` normalizes EVM receipts to a CosmJS‑like shape. * `**MsgConvertEvmToCoin**` (WASM path) can optionally be appended if BANK funds are insufficient. * **Smart Allocation** (optional): split requested amount across BANK / ERC‑20 based on balances and user preference; then scale per‑side and stitch back together. [PreviousContract Addresses Referencechevron-left](https://docs.sai.fun/for-devs/sai-core/contracts) [NextSai Keeperchevron-right](https://docs.sai.fun/for-devs/sai-keeper) Last updated 14 days ago * [Sai Contracts – WASM & EVM Integration](https://docs.sai.fun/for-devs/sai-core/contract-integration#sai-contracts-wasm-and-evm-integration) * [Quick Glossary](https://docs.sai.fun/for-devs/sai-core/contract-integration#quick-glossary) * [Collateral / Payment Tokens](https://docs.sai.fun/for-devs/sai-core/contract-integration#collateral-payment-tokens) * [Shared Concepts & Types](https://docs.sai.fun/for-devs/sai-core/contract-integration#shared-concepts-and-types) * [EVM Integration (via Interface Contract)](https://docs.sai.fun/for-devs/sai-core/contract-integration#evm-integration-via-interface-contract) * [1\. Open Perp Trade](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-1.-open-perp-trade) * [2 Close Perp Trade](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-2-close-perp-trade) * [3\. Referral: Create Code](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-3.-referral-create-code) * [4\. Referral: Redeem Code](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-4.-referral-redeem-code) * [5\. Vault: Deposit](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-5.-vault-deposit) * [6\. Vault: Make Withdraw Request](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-6.-vault-make-withdraw-request) * [7\. Vault: Redeem](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-7.-vault-redeem) * [8\. Vault: Cancel Withdraw Request](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-8.-vault-cancel-withdraw-request) * [WASM (Cosmos) Integration](https://docs.sai.fun/for-devs/sai-core/contract-integration#wasm-cosmos-integration) * [1\. Open Perp Trade](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-1.-open-perp-trade-1) * [2\. Close Perp Trade](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-2.-close-perp-trade) * [3\. Referral: Create Code](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-3.-referral-create-code-1) * [4\. Referral: Redeem Code](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-4.-referral-redeem-code-1) * [5\. Vault: Deposit](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-5.-vault-deposit-1) * [6\. Vault: Make Withdraw Request](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-6.-vault-make-withdraw-request-1) * [7\. Vault: Redeem](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-7.-vault-redeem-1) * [8\. Vault: Cancel Withdraw Request](https://docs.sai.fun/for-devs/sai-core/contract-integration#id-8.-vault-cancel-withdraw-request-1) * [Amounts & Decimals (Gotchas)](https://docs.sai.fun/for-devs/sai-core/contract-integration#amounts-and-decimals-gotchas) * [Error Handling & UX Patterns](https://docs.sai.fun/for-devs/sai-core/contract-integration#error-handling-and-ux-patterns) * [Minimal Pseudocode Examples](https://docs.sai.fun/for-devs/sai-core/contract-integration#minimal-pseudocode-examples) * [Preconditions Checklist (per call)](https://docs.sai.fun/for-devs/sai-core/contract-integration#preconditions-checklist-per-call) * [Appendix: Utility Notes](https://docs.sai.fun/for-devs/sai-core/contract-integration#appendix-utility-notes) Copy { "open_trade": { "market_index": "MarketIndex(N)", "leverage": "string", "long": true, "collateral_index": "TokenIndex(N)", "trade_type": "trade|limit|stop", "open_price": "", "tp": "optional string", "sl": "optional string", "slippage_p": "1", "is_evm_origin": true } } Copy { "close_trade": { "trade_index": "UserTradeIndex()" } } Copy { "create_referrer_code": { "code": "" } } Copy { "redeem_referrer_code": { "code": "" } } Copy contract.deposit( wasmMsgBytes, // { "deposit": {} } depositAmountTotalBankUnits, // BANK units (includes ERC‑20 converted -> BANK) useErc20Amount, // ERC‑20 units (token decimals) vaultAddress, // bech32 vault contract collateralErc20, // ERC‑20 address true, // sendToEvm on triggers overrides ) Copy contract.makeWithdrawRequest( vaultAddress, wasmMsgBytes, // { "make_withdraw_request": {} } totalShares, // BANK units to lock (sharesBANK + sharesFromERC20) useErc20Amount, // shares to bridge from ERC‑20 (BANK-scaled already) overrides ) Copy contract.redeem( wasmMsgBytes, // { "redeem": { "shares": "..." } } vaultAddress, shares, // BANK units sendToEvm, // boolean overrides ) Copy { "cancel_withdraw_request": { "unlock_epoch": } } Copy { "open_trade": { "market_index": "MarketIndex(N)", "leverage": "string", "long": true, "collateral_index": "TokenIndex(N)", "trade_type": "trade|limit|stop", "open_price": "", "slippage_p": "1", "tp": "optional string", "sl": "optional string", "is_evm_origin": false } } Copy [{ "denom": "", "amount": "" }] Copy [{ "denom": "", "amount": "" }] Copy { "get_vault_share_denom": {} } Copy [{ "denom": "", "amount": "" }] Copy const msg = { open_trade: { /* ...see schema above... */ is_evm_origin: true } } const wasmMsgBytes = ethers.toUtf8Bytes(JSON.stringify(msg)) const gas = await estimateGasWithFallback( () => contract.openTrade.estimateGas(wasmMsgBytes, collateralIndex, totalAmount, useErc20), { msg } ) const tx = await contract.openTrade(wasmMsgBytes, collateralIndex, totalAmount, useErc20, toTxOverrides(gas)) const res = await tx.wait() // -> map to ExecuteResult if desired Copy await wasm.execute( bech32, saiContracts.perp, { open_trade: { /* ... */, is_evm_origin: false } }, "auto", undefined, [ { denom: collateral.bankDenom, amount: scaledAmount } ] ) Copy const msg = { deposit: {} } const wasmMsgBytes = ethers.toUtf8Bytes(JSON.stringify(msg)) const bankTotal = bankAmount + toBankUnits(erc20Amount, erc20Dec, 6) const tx = await contract.deposit(wasmMsgBytes, bankTotal, erc20Amount, vaultAddr, collateral.evmAddr, true, overrides) await tx.wait() Copy const shareDenom = await wasm.queryContractSmart(vaultAddr, { get_vault_share_denom: {} }) await wasm.execute( bech32, vaultAddr, { make_withdraw_request: {} }, "auto", undefined, [ { denom: shareDenom, amount: shares } ] ) --- # Examples: Subscriptions | Sai.fun Real-time data updates using GraphQL subscriptions over WebSockets. [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#table-of-contents) Table of Contents ------------------------------------------------------------------------------------------------------------------- * [Setup](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#setup) * [Perp Subscriptions](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#perp-subscriptions) * [LP Subscriptions](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#lp-subscriptions) * [Oracle Subscriptions](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#oracle-subscriptions) * [Advanced Patterns](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#advanced-patterns) * [Best Practices](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#best-practices) [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#setup) Setup ------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#apollo-client-with-subscriptions) Apollo Client with Subscriptions Copy import { ApolloClient, InMemoryCache, split, HttpLink } from '@apollo/client' import { GraphQLWsLink } from '@apollo/client/link/subscriptions' import { getMainDefinition } from '@apollo/client/utilities' import { createClient } from 'graphql-ws' const httpLink = new HttpLink({ uri: 'https://sai-keeper.testnet-2.nibiru.fi/graphql' }) const wsLink = new GraphQLWsLink( createClient({ url: 'wss://sai-keeper.testnet-2.nibiru.fi/graphql', connectionParams: { // Add auth headers if needed }, retryAttempts: 5, shouldRetry: () => true }) ) // Split link based on operation type const splitLink = split( ({ query }) => { const definition = getMainDefinition(query) return ( definition.kind === 'OperationDefinition' && definition.operation === 'subscription' ) }, wsLink, httpLink ) const client = new ApolloClient({ link: splitLink, cache: new InMemoryCache() }) ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#urql-with-subscriptions) urql with Subscriptions * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#perp-subscriptions) Perp Subscriptions --------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-1-watch-user-trades) Example 1: Watch User Trades Subscribe to all trade updates for a specific user. ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-2-watch-trade-events) Example 2: Watch Trade Events Subscribe to trade history events (opens, closes, liquidations, etc). ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-3-watch-market-borrowing-rates) Example 3: Watch Market Borrowing Rates Subscribe to real-time borrowing rate updates for a specific market. ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-4-watch-all-markets) Example 4: Watch All Markets Subscribe to updates for all available markets. * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#lp-subscriptions) LP Subscriptions ----------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-5-watch-vault-metrics) Example 5: Watch Vault Metrics Subscribe to real-time vault updates (TVL, APY, share price). ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-6-watch-user-lp-positions) Example 6: Watch User LP Positions Subscribe to user's LP deposit updates. ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-7-watch-deposit-events) Example 7: Watch Deposit Events Subscribe to deposit/withdrawal events. ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-8-watch-withdrawal-requests) Example 8: Watch Withdrawal Requests Subscribe to withdrawal request updates. * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#oracle-subscriptions) Oracle Subscriptions ------------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-9-watch-token-prices) Example 9: Watch Token Prices Subscribe to real-time price updates. ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-10-watch-specific-token-price) Example 10: Watch Specific Token Price ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-11-watch-user-balances) Example 11: Watch User Balances * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#advanced-patterns) Advanced Patterns ------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-12-multiple-subscriptions-manager) Example 12: Multiple Subscriptions Manager ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-13-subscription-with-reconnection-logic) Example 13: Subscription with Reconnection Logic * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#best-practices) Best Practices ------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#id-1.-always-clean-up-subscriptions) 1\. Always Clean Up Subscriptions ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#id-2.-handle-connection-states) 2\. Handle Connection States ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#id-3.-debounce-rapid-updates) 3\. Debounce Rapid Updates ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#id-4.-combine-with-queries-for-initial-data) 4\. Combine with Queries for Initial Data [PreviousExamples: Querieschevron-left](https://docs.sai.fun/for-devs/sai-keeper/examples-queries) [NextClient Setupchevron-right](https://docs.sai.fun/for-devs/sai-keeper/client-setup) Last updated 3 months ago * [Table of Contents](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#table-of-contents) * [Setup](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#setup) * [Apollo Client with Subscriptions](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#apollo-client-with-subscriptions) * [urql with Subscriptions](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#urql-with-subscriptions) * [Perp Subscriptions](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#perp-subscriptions) * [Example 1: Watch User Trades](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-1-watch-user-trades) * [Example 2: Watch Trade Events](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-2-watch-trade-events) * [Example 3: Watch Market Borrowing Rates](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-3-watch-market-borrowing-rates) * [Example 4: Watch All Markets](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-4-watch-all-markets) * [LP Subscriptions](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#lp-subscriptions) * [Example 5: Watch Vault Metrics](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-5-watch-vault-metrics) * [Example 6: Watch User LP Positions](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-6-watch-user-lp-positions) * [Example 7: Watch Deposit Events](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-7-watch-deposit-events) * [Example 8: Watch Withdrawal Requests](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-8-watch-withdrawal-requests) * [Oracle Subscriptions](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#oracle-subscriptions) * [Example 9: Watch Token Prices](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-9-watch-token-prices) * [Example 10: Watch Specific Token Price](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-10-watch-specific-token-price) * [Example 11: Watch User Balances](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-11-watch-user-balances) * [Advanced Patterns](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#advanced-patterns) * [Example 12: Multiple Subscriptions Manager](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-12-multiple-subscriptions-manager) * [Example 13: Subscription with Reconnection Logic](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#example-13-subscription-with-reconnection-logic) * [Best Practices](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#best-practices) * [1\. Always Clean Up Subscriptions](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#id-1.-always-clean-up-subscriptions) * [2\. Handle Connection States](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#id-2.-handle-connection-states) * [3\. Debounce Rapid Updates](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#id-3.-debounce-rapid-updates) * [4\. Combine with Queries for Initial Data](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions#id-4.-combine-with-queries-for-initial-data) Copy import { createClient, subscriptionExchange, fetchExchange } from 'urql' import { createClient as createWSClient } from 'graphql-ws' const wsClient = createWSClient({ url: 'wss://sai-keeper.testnet-2.nibiru.fi/graphql' }) const client = createClient({ url: 'https://sai-keeper.testnet-2.nibiru.fi/graphql', exchanges: [\ fetchExchange,\ subscriptionExchange({\ forwardSubscription: (operation) => ({\ subscribe: (sink) => ({\ unsubscribe: wsClient.subscribe(operation, sink)\ })\ })\ })\ ] }) Copy import { gql } from '@apollo/client' const WATCH_TRADES = gql` subscription WatchTrades($trader: String!) { perpTrades(where: { trader: $trader }) { id isOpen isLong leverage collateralAmount openPrice closePrice sl tp perpBorrowing { baseToken { symbol logoUrl } marketId } state { pnlCollateral pnlPct liquidationPrice positionValue borrowingFeeCollateral } openBlock { block_ts } } } ` // Apollo Client usage const subscription = client.subscribe({ query: WATCH_TRADES, variables: { trader: 'nibi1abc...' } }).subscribe({ next: ({ data }) => { console.log('Trades updated:', data.perpTrades) data.perpTrades.forEach(trade => { const pnlPct = (trade.state.pnlPct * 100).toFixed(2) const status = trade.isOpen ? 'OPEN' : 'CLOSED' console.log(`[${status}] ${trade.perpBorrowing.baseToken.symbol} ${trade.isLong ? 'LONG' : 'SHORT'} ${trade.leverage}x`) console.log(` PnL: ${pnlPct}%`) console.log(` Liquidation: $${trade.state.liquidationPrice.toFixed(2)}`) }) }, error: (error) => { console.error('Subscription error:', error) } }) // Cleanup // subscription.unsubscribe() Copy const WATCH_TRADE_EVENTS = gql` subscription WatchTradeEvents($trader: String!) { perpTradeHistory(where: { trader: $trader }) { id tradeChangeType realizedPnlCollateral realizedPnlPct block { block block_ts } trade { id perpBorrowing { baseToken { symbol } collateralToken { symbol decimals } } isLong leverage openPrice closePrice } } } ` // Usage const subscription = client.subscribe({ query: WATCH_TRADE_EVENTS, variables: { trader: 'nibi1abc...' } }).subscribe({ next: ({ data }) => { const events = data.perpTradeHistory events.forEach(event => { const timestamp = new Date(event.block.block_ts).toLocaleTimeString() const token = event.trade?.perpBorrowing.baseToken.symbol console.log(`[${timestamp}] ${event.tradeChangeType}`) if (event.realizedPnlPct !== null) { const pnlPct = (event.realizedPnlPct * 100).toFixed(2) const decimals = event.trade.perpBorrowing.collateralToken.decimals const pnl = event.realizedPnlCollateral / (10 ** decimals) console.log(` ${token} - Realized PnL: ${pnl.toFixed(2)} (${pnlPct}%)`) } }) // Show notification for important events events.forEach(event => { if (event.tradeChangeType === 'position_liquidated') { showNotification('⚠️ Position Liquidated!', { body: `Your ${event.trade?.perpBorrowing.baseToken.symbol} position was liquidated` }) } if (event.tradeChangeType === 'position_closed_tp') { showNotification('✅ Take Profit Hit!', { body: `TP triggered on ${event.trade?.perpBorrowing.baseToken.symbol}` }) } }) } }) Copy const WATCH_MARKET = gql` subscription WatchMarket($collateralId: Int!, $marketId: Int!) { perpBorrowing(collateralId: $collateralId, marketId: $marketId) { marketId baseToken { symbol } price oiLong oiShort oiMax feesPerHourLong feesPerHourShort openFeePct closeFeePct } } ` // Usage const subscription = client.subscribe({ query: WATCH_MARKET, variables: { collateralId: 1, marketId: 1 } }).subscribe({ next: ({ data }) => { const market = data.perpBorrowing // Calculate funding rate APR const fundingAprLong = market.feesPerHourLong * 24 * 365 * 100 const fundingAprShort = market.feesPerHourShort * 24 * 365 * 100 // Calculate OI utilization const oiTotal = market.oiLong + market.oiShort const utilization = (oiTotal / market.oiMax) * 100 console.log(`${market.baseToken.symbol} Market Update`) console.log(`Price: $${market.price.toFixed(2)}`) console.log(`OI: ${market.oiLong} L / ${market.oiShort} S`) console.log(`Utilization: ${utilization.toFixed(1)}%`) console.log(`Funding APR - Long: ${fundingAprLong.toFixed(2)}% | Short: ${fundingAprShort.toFixed(2)}%`) // Alert on high utilization if (utilization > 90) { console.warn('⚠️ High OI utilization!') } } }) Copy const WATCH_ALL_MARKETS = gql` subscription WatchAllMarkets { perpBorrowings { marketId baseToken { symbol name } collateralToken { symbol } visible } } ` // Usage const subscription = client.subscribe({ query: WATCH_ALL_MARKETS }).subscribe({ next: ({ data }) => { const markets = data.perpBorrowings.filter(m => m.visible) console.log(`Active markets: ${markets.length}`) // Update market selector UI updateMarketList(markets) } }) Copy const WATCH_VAULTS = gql` subscription WatchVaults { lpVaults { address collateralToken { symbol name decimals } tvl sharePrice apy availableAssets currentEpoch revenueInfo { NetProfit TraderLosses Liabilities } } } ` // Usage const subscription = client.subscribe({ query: WATCH_VAULTS }).subscribe({ next: ({ data }) => { data.lpVaults.forEach(vault => { const decimals = vault.collateralToken.decimals const tvl = vault.tvl / (10 ** decimals) const available = vault.availableAssets / (10 ** decimals) const utilization = ((tvl - available) / tvl) * 100 console.log(`${vault.collateralToken.symbol} Vault`) console.log(` TVL: ${tvl.toLocaleString()}`) console.log(` APY: ${vault.apy?.toFixed(2) || 'N/A'}%`) console.log(` Share Price: ${vault.sharePrice.toFixed(6)}`) console.log(` Utilization: ${utilization.toFixed(1)}%`) console.log(` Net Profit: ${vault.revenueInfo.NetProfit}`) }) // Update dashboard updateVaultDashboard(data.lpVaults) } }) Copy const WATCH_USER_LP = gql` subscription WatchUserLP($user: String!) { lpDeposits(where: { depositor: $user }) { depositor shares vault { address collateralToken { symbol decimals } sharePrice apy } } } ` // Usage const subscription = client.subscribe({ query: WATCH_USER_LP, variables: { user: 'nibi1abc...' } }).subscribe({ next: ({ data }) => { let totalValue = 0 data.lpDeposits.forEach(deposit => { const decimals = deposit.vault.collateralToken.decimals const value = (deposit.shares * deposit.vault.sharePrice) / (10 ** decimals) totalValue += value console.log(`${deposit.vault.collateralToken.symbol}: ${value.toFixed(2)}`) }) console.log(`Total LP Value: ${totalValue.toFixed(2)}`) // Update portfolio UI updatePortfolioValue(totalValue) } }) Copy const WATCH_DEPOSIT_EVENTS = gql` subscription WatchDepositEvents($user: String!, $vault: String!) { lpDepositHistory(where: { depositor: $user, vault: $vault }) { id amount shares isWithdraw block { block_ts } vault { collateralToken { symbol decimals } } } } ` // Usage const subscription = client.subscribe({ query: WATCH_DEPOSIT_EVENTS, variables: { user: 'nibi1abc...', vault: 'nibi1vault...' } }).subscribe({ next: ({ data }) => { data.lpDepositHistory.forEach(event => { const decimals = event.vault.collateralToken.decimals const amount = event.amount / (10 ** decimals) const action = event.isWithdraw ? 'Withdrew' : 'Deposited' const timestamp = new Date(event.block.block_ts).toLocaleString() console.log(`[${timestamp}] ${action} ${amount.toFixed(2)} ${event.vault.collateralToken.symbol}`) // Show notification showNotification(`LP ${action}`, { body: `${amount.toFixed(2)} ${event.vault.collateralToken.symbol}`, timestamp: event.block.block_ts }) }) } }) Copy const WATCH_WITHDRAWALS = gql` subscription WatchWithdrawals($user: String!, $vault: String!) { lpWithdrawRequests(where: { depositor: $user, vault: $vault }) { shares status unlockEpoch autoRedeem vault { currentEpoch sharePrice collateralToken { symbol decimals } } } } ` // Usage const subscription = client.subscribe({ query: WATCH_WITHDRAWALS, variables: { user: 'nibi1abc...', vault: 'nibi1vault...' } }).subscribe({ next: ({ data }) => { data.lpWithdrawRequests.forEach(request => { const isReady = request.vault.currentEpoch >= request.unlockEpoch const epochsRemaining = Math.max(0, request.unlockEpoch - request.vault.currentEpoch) const decimals = request.vault.collateralToken.decimals const estimatedValue = (request.shares * request.vault.sharePrice) / (10 ** decimals) console.log(`Withdrawal Request`) console.log(` Status: ${isReady ? 'Ready ✅' : `${epochsRemaining} epochs remaining`}`) console.log(` Estimated Value: ${estimatedValue.toFixed(2)} ${request.vault.collateralToken.symbol}`) console.log(` Auto-redeem: ${request.autoRedeem}`) // Notify when ready if (isReady && !notifiedAlready) { showNotification('✅ Withdrawal Ready!', { body: `Your withdrawal of ${estimatedValue.toFixed(2)} ${request.vault.collateralToken.symbol} is ready` }) notifiedAlready = true } }) } }) Copy const WATCH_PRICES = gql` subscription WatchPrices { tokenPricesUsd { token { id symbol name } priceUsd lastUpdatedBlock { block block_ts } } } ` // Usage const priceCache = new Map() const subscription = client.subscribe({ query: WATCH_PRICES }).subscribe({ next: ({ data }) => { data.tokenPricesUsd.forEach(item => { const oldPrice = priceCache.get(item.token.symbol) const newPrice = item.priceUsd // Calculate price change if (oldPrice) { const change = ((newPrice - oldPrice) / oldPrice) * 100 const arrow = change > 0 ? '↑' : '↓' console.log(`${item.token.symbol}: $${newPrice.toFixed(2)} ${arrow} ${Math.abs(change).toFixed(2)}%`) } else { console.log(`${item.token.symbol}: $${newPrice.toFixed(2)}`) } priceCache.set(item.token.symbol, newPrice) }) // Update price ticker UI updatePriceTicker(data.tokenPricesUsd) } }) Copy const WATCH_TOKEN_PRICE = gql` subscription WatchTokenPrice($tokenId: Int!) { tokenPricesUsd(where: { tokenId: $tokenId }) { token { symbol } priceUsd lastUpdatedBlock { block_ts } } } ` // Usage with price alerts const subscription = client.subscribe({ query: WATCH_TOKEN_PRICE, variables: { tokenId: 1 } // BTC }).subscribe({ next: ({ data }) => { const priceData = data.tokenPricesUsd[0] const price = priceData.priceUsd console.log(`BTC: $${price.toFixed(2)}`) // Price alerts if (price > 50000) { showNotification('🚀 BTC Above $50k!', { body: `Current price: $${price.toFixed(2)}` }) } if (price < 45000) { showNotification('📉 BTC Below $45k', { body: `Current price: $${price.toFixed(2)}` }) } } }) Copy const WATCH_BALANCES = gql` subscription WatchBalances($user: String!) { userBalances(where: { user: $user }) { amount token_info { symbol name decimals type logo } } } ` // Usage const subscription = client.subscribe({ query: WATCH_BALANCES, variables: { user: 'nibi1abc...' } }).subscribe({ next: ({ data }) => { console.log('Balance Update:') data.userBalances.forEach(balance => { const amount = parseFloat(balance.amount) / (10 ** balance.token_info.decimals) console.log(` ${balance.token_info.symbol}: ${amount.toFixed(4)}`) }) // Update wallet UI updateWalletBalances(data.userBalances) } }) Copy class SubscriptionManager { constructor(client) { this.client = client this.subscriptions = new Map() } subscribe(name, query, variables, callback) { // Unsubscribe existing if any this.unsubscribe(name) const subscription = this.client.subscribe({ query, variables }).subscribe({ next: callback, error: (error) => { console.error(`Subscription ${name} error:`, error) // Attempt reconnection setTimeout(() => { this.subscribe(name, query, variables, callback) }, 5000) } }) this.subscriptions.set(name, subscription) } unsubscribe(name) { const sub = this.subscriptions.get(name) if (sub) { sub.unsubscribe() this.subscriptions.delete(name) } } unsubscribeAll() { this.subscriptions.forEach(sub => sub.unsubscribe()) this.subscriptions.clear() } } // Usage const manager = new SubscriptionManager(client) manager.subscribe('trades', WATCH_TRADES, { trader: 'nibi1abc...' }, (data) => { updateTradesUI(data.perpTrades) }) manager.subscribe('prices', WATCH_PRICES, {}, (data) => { updatePricesUI(data.tokenPricesUsd) }) // Cleanup on component unmount // manager.unsubscribeAll() Copy function createResilientSubscription(query, variables, onData) { let subscription = null let reconnectAttempts = 0 const maxReconnectAttempts = 10 function connect() { subscription = client.subscribe({ query, variables }).subscribe({ next: (data) => { reconnectAttempts = 0 // Reset on successful data onData(data) }, error: (error) => { console.error('Subscription error:', error) if (reconnectAttempts < maxReconnectAttempts) { reconnectAttempts++ const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000) console.log(`Reconnecting in ${delay}ms (attempt ${reconnectAttempts})...`) setTimeout(connect, delay) } else { console.error('Max reconnection attempts reached') } }, complete: () => { console.log('Subscription completed') } }) } connect() return { unsubscribe: () => { if (subscription) { subscription.unsubscribe() } } } } // Usage const sub = createResilientSubscription( WATCH_TRADES, { trader: 'nibi1abc...' }, (data) => { console.log('Received trade update:', data.perpTrades) } ) // Cleanup // sub.unsubscribe() Copy useEffect(() => { const subscription = client.subscribe({ query: WATCH_TRADES, variables: { trader } }).subscribe({ next: (data) => setTrades(data.perpTrades) }) // Cleanup function return () => { subscription.unsubscribe() } }, [trader]) Copy const [connectionState, setConnectionState] = useState('connecting') const subscription = client.subscribe({ query: WATCH_PRICES }).subscribe({ next: (data) => { setConnectionState('connected') updatePrices(data) }, error: (error) => { setConnectionState('error') console.error(error) } }) // Show connection status in UI if (connectionState === 'connecting') { return
Connecting to real-time feed...
} Copy import { debounce } from 'lodash' const debouncedUpdate = debounce((data) => { updateUI(data) }, 100) const subscription = client.subscribe({ query: WATCH_PRICES }).subscribe({ next: (data) => { debouncedUpdate(data.tokenPricesUsd) } }) Copy // Fetch initial data with query const { data: initialData } = await client.query({ query: GET_TRADES, variables: { trader } }) setTrades(initialData.perp.trades) // Then subscribe to updates const subscription = client.subscribe({ query: WATCH_TRADES, variables: { trader } }).subscribe({ next: (data) => { setTrades(data.perpTrades) } }) --- # Client Setup | Sai.fun Set up GraphQL clients in various languages to interact with Sai Keeper API. [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#table-of-contents) Table of Contents --------------------------------------------------------------------------------------------------------- * [JavaScript/TypeScript](https://docs.sai.fun/for-devs/sai-keeper/client-setup#javascripttypescript) * [Apollo Client](https://docs.sai.fun/for-devs/sai-keeper/client-setup#apollo-client) * [urql](https://docs.sai.fun/for-devs/sai-keeper/client-setup#urql) * [GraphQL Request](https://docs.sai.fun/for-devs/sai-keeper/client-setup#graphql-request) * [Python](https://docs.sai.fun/for-devs/sai-keeper/client-setup#python) * [Rust](https://docs.sai.fun/for-devs/sai-keeper/client-setup#rust) * [Go](https://docs.sai.fun/for-devs/sai-keeper/client-setup#go) * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#javascript-typescript) JavaScript/TypeScript ----------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#apollo-client) Apollo Client Apollo Client is the most popular GraphQL client for JavaScript applications. #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#installation) Installation Copy npm install @apollo/client graphql # For subscriptions npm install graphql-ws #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#basic-setup-queries-only) Basic Setup (Queries Only) #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#full-setup-with-subscriptions) Full Setup (With Subscriptions) #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#react-integration) React Integration * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#urql) urql urql is a lightweight alternative to Apollo Client. #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#installation-1) Installation #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#setup) Setup #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#react-integration-1) React Integration * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#graphql-request) GraphQL Request Lightweight library for simple queries (no caching or subscriptions). #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#installation-2) Installation #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#usage) Usage * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#python) Python ----------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#gql-library) gql Library Full-featured GraphQL client for Python. #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#installation-3) Installation #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#setup-1) Setup #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#with-subscriptions) With Subscriptions #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#synchronous-version) Synchronous Version * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#rust) Rust ------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#graphql_client) graphql\_client Type-safe GraphQL client for Rust. #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#installation-4) Installation Add to `Cargo.toml`: #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#setup-2) Setup #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#without-code-generation) Without Code Generation * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#go) Go --------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#graphql-package) graphql Package #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#installation-5) Installation #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#setup-3) Setup #### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#with-error-handling) With Error Handling * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/client-setup#environment-variables) Environment Variables ----------------------------------------------------------------------------------------------------------------- For all languages, consider using environment variables for configuration: **JavaScript:** **Python:** **Rust:** **Go:** [PreviousExamples: Subscriptionschevron-left](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions) [NextFilters & Paginationchevron-right](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination) Last updated 3 months ago * [Table of Contents](https://docs.sai.fun/for-devs/sai-keeper/client-setup#table-of-contents) * [JavaScript/TypeScript](https://docs.sai.fun/for-devs/sai-keeper/client-setup#javascript-typescript) * [Apollo Client](https://docs.sai.fun/for-devs/sai-keeper/client-setup#apollo-client) * [urql](https://docs.sai.fun/for-devs/sai-keeper/client-setup#urql) * [GraphQL Request](https://docs.sai.fun/for-devs/sai-keeper/client-setup#graphql-request) * [Python](https://docs.sai.fun/for-devs/sai-keeper/client-setup#python) * [gql Library](https://docs.sai.fun/for-devs/sai-keeper/client-setup#gql-library) * [Rust](https://docs.sai.fun/for-devs/sai-keeper/client-setup#rust) * [graphql\_client](https://docs.sai.fun/for-devs/sai-keeper/client-setup#graphql_client) * [Go](https://docs.sai.fun/for-devs/sai-keeper/client-setup#go) * [graphql Package](https://docs.sai.fun/for-devs/sai-keeper/client-setup#graphql-package) * [Environment Variables](https://docs.sai.fun/for-devs/sai-keeper/client-setup#environment-variables) Copy import { ApolloClient, InMemoryCache, HttpLink, gql } from '@apollo/client' const client = new ApolloClient({ link: new HttpLink({ uri: 'https://sai-keeper.testnet-2.nibiru.fi/graphql' }), cache: new InMemoryCache() }) // Example query const GET_PRICES = gql` query GetPrices { oracle { tokenPricesUsd(limit: 10) { token { symbol } priceUsd } } } ` async function fetchPrices() { const { data, error } = await client.query({ query: GET_PRICES }) if (error) { console.error('Error:', error) return } console.log('Prices:', data.oracle.tokenPricesUsd) } fetchPrices() Copy import { ApolloClient, InMemoryCache, HttpLink, split, gql } from '@apollo/client' import { GraphQLWsLink } from '@apollo/client/link/subscriptions' import { getMainDefinition } from '@apollo/client/utilities' import { createClient } from 'graphql-ws' // HTTP link for queries and mutations const httpLink = new HttpLink({ uri: 'https://sai-keeper.testnet-2.nibiru.fi/graphql' }) // WebSocket link for subscriptions const wsLink = new GraphQLWsLink( createClient({ url: 'wss://sai-keeper.testnet-2.nibiru.fi/graphql', connectionParams: { // Add authentication if needed // authToken: 'your-auth-token' }, retryAttempts: 5, shouldRetry: () => true, on: { connected: () => console.log('WebSocket connected'), closed: () => console.log('WebSocket closed'), error: (error) => console.error('WebSocket error:', error) } }) ) // Split traffic between HTTP and WebSocket const splitLink = split( ({ query }) => { const definition = getMainDefinition(query) return ( definition.kind === 'OperationDefinition' && definition.operation === 'subscription' ) }, wsLink, httpLink ) const client = new ApolloClient({ link: splitLink, cache: new InMemoryCache(), defaultOptions: { watchQuery: { fetchPolicy: 'cache-and-network' } } }) // Example subscription const WATCH_PRICES = gql` subscription WatchPrices { tokenPricesUsd { token { symbol } priceUsd } } ` const subscription = client.subscribe({ query: WATCH_PRICES }).subscribe({ next: ({ data }) => { console.log('Price update:', data.tokenPricesUsd) }, error: (error) => { console.error('Subscription error:', error) } }) // Clean up // subscription.unsubscribe() export default client Copy import { ApolloProvider, useQuery, useSubscription, gql } from '@apollo/client' import client from './apollo-client' // Wrap your app function App() { return ( ) } // Use queries in components function Dashboard() { const GET_TRADES = gql` query GetTrades($trader: String!) { perp { trades(where: { trader: $trader, isOpen: true }) { id isLong leverage state { pnlPct } } } } ` const { loading, error, data } = useQuery(GET_TRADES, { variables: { trader: 'nibi1abc...' } }) if (loading) return
Loading...
if (error) return
Error: {error.message}
return (
{data.perp.trades.map(trade => (
Trade #{trade.id} - PnL: {(trade.state.pnlPct * 100).toFixed(2)}%
))}
) } // Use subscriptions in components function PriceTicker() { const WATCH_PRICES = gql` subscription { tokenPricesUsd { token { symbol } priceUsd } } ` const { data, loading } = useSubscription(WATCH_PRICES) if (loading) return
Connecting...
return (
{data.tokenPricesUsd.map(item => (
{item.token.symbol}: ${item.priceUsd.toFixed(2)}
))}
) } Copy npm install urql graphql # For subscriptions npm install graphql-ws Copy import { createClient, fetchExchange, subscriptionExchange } from 'urql' import { createClient as createWSClient } from 'graphql-ws' const wsClient = createWSClient({ url: 'wss://sai-keeper.testnet-2.nibiru.fi/graphql' }) const client = createClient({ url: 'https://sai-keeper.testnet-2.nibiru.fi/graphql', exchanges: [\ fetchExchange,\ subscriptionExchange({\ forwardSubscription: (operation) => ({\ subscribe: (sink) => ({\ unsubscribe: wsClient.subscribe(operation, sink)\ })\ })\ })\ ] }) export default client Copy import { Provider, useQuery, useSubscription } from 'urql' import client from './urql-client' function App() { return ( ) } function Dashboard() { const QUERY = ` query GetTrades($trader: String!) { perp { trades(where: { trader: $trader, isOpen: true }) { id isLong leverage } } } ` const [result] = useQuery({ query: QUERY, variables: { trader: 'nibi1abc...' } }) const { data, fetching, error } = result if (fetching) return
Loading...
if (error) return
Error: {error.message}
return (
{data.perp.trades.map(trade => (
Trade #{trade.id}
))}
) } Copy npm install graphql-request graphql Copy import { GraphQLClient, gql } from 'graphql-request' const client = new GraphQLClient( 'https://sai-keeper.testnet-2.nibiru.fi/graphql' ) const GET_TRADES = gql` query GetTrades($trader: String!) { perp { trades(where: { trader: $trader, isOpen: true }) { id isLong leverage } } } ` async function fetchTrades(trader: string) { try { const data = await client.request(GET_TRADES, { trader }) console.log('Trades:', data.perp.trades) return data.perp.trades } catch (error) { console.error('Error fetching trades:', error) throw error } } fetchTrades('nibi1abc...') Copy pip install gql[all] Copy from gql import gql, Client from gql.transport.aiohttp import AIOHTTPTransport from gql.transport.websockets import WebsocketsTransport import asyncio # HTTP Transport for queries http_transport = AIOHTTPTransport( url="https://sai-keeper.testnet-2.nibiru.fi/graphql" ) # Create client client = Client( transport=http_transport, fetch_schema_from_transport=True ) # Example query async def get_trades(trader_address): query = gql(""" query GetTrades($trader: String!) { perp { trades(where: { trader: $trader, isOpen: true }) { id isLong leverage state { pnlPct liquidationPrice } perpBorrowing { baseToken { symbol } } } } } """) params = {"trader": trader_address} async with Client(transport=http_transport) as session: result = await session.execute(query, variable_values=params) return result['perp']['trades'] # Run the query trades = asyncio.run(get_trades("nibi1abc...")) for trade in trades: pnl_pct = trade['state']['pnlPct'] * 100 print(f"Trade #{trade['id']}: {trade['perpBorrowing']['baseToken']['symbol']} {trade['leverage']}x - PnL: {pnl_pct:.2f}%") Copy from gql import gql, Client from gql.transport.websockets import WebsocketsTransport import asyncio # WebSocket transport for subscriptions ws_transport = WebsocketsTransport( url="wss://sai-keeper.testnet-2.nibiru.fi/graphql" ) async def watch_prices(): subscription = gql(""" subscription WatchPrices { tokenPricesUsd { token { symbol } priceUsd } } """) async with Client(transport=ws_transport) as session: async for result in session.subscribe(subscription): prices = result['tokenPricesUsd'] for item in prices: print(f"{item['token']['symbol']}: ${item['priceUsd']:.2f}") # Run subscription asyncio.run(watch_prices()) Copy from gql import gql, Client from gql.transport.requests import RequestsHTTPTransport # Synchronous transport transport = RequestsHTTPTransport( url="https://sai-keeper.testnet-2.nibiru.fi/graphql", verify=True, retries=3 ) client = Client(transport=transport, fetch_schema_from_transport=True) # Synchronous query def get_token_prices(): query = gql(""" query GetPrices { oracle { tokenPricesUsd(limit: 10) { token { symbol } priceUsd } } } """) result = client.execute(query) return result['oracle']['tokenPricesUsd'] prices = get_token_prices() for item in prices: print(f"{item['token']['symbol']}: ${item['priceUsd']:.2f}") Copy [dependencies] graphql_client = "0.13" reqwest = { version = "0.11", features = ["json"] } tokio = { version = "1", features = ["full"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" Copy use graphql_client::{GraphQLQuery, Response}; use reqwest; // Define the query #[derive(GraphQLQuery)] #[graphql(\ schema_path = "schema.graphql",\ query_path = "queries/get_trades.graphql",\ response_derives = "Debug"\ )] pub struct GetTrades; #[tokio::main] async fn main() -> Result<(), Box> { let client = reqwest::Client::new(); let variables = get_trades::Variables { trader: "nibi1abc...".to_string(), }; let request_body = GetTrades::build_query(variables); let res = client .post("https://sai-keeper.testnet-2.nibiru.fi/graphql") .json(&request_body) .send() .await?; let response_body: Response = res.json().await?; if let Some(data) = response_body.data { for trade in data.perp.trades { println!("Trade #{}: {}x leverage", trade.id, trade.leverage); } } if let Some(errors) = response_body.errors { for error in errors { eprintln!("Error: {}", error.message); } } Ok(()) } Copy use reqwest; use serde::{Deserialize, Serialize}; use serde_json::json; #[derive(Debug, Serialize, Deserialize)] struct GraphQLRequest { query: String, variables: serde_json::Value, } #[derive(Debug, Deserialize)] struct GraphQLResponse { data: Option, errors: Option>, } #[derive(Debug, Deserialize)] struct GraphQLError { message: String, } #[tokio::main] async fn main() -> Result<(), Box> { let client = reqwest::Client::new(); let query = r#" query GetTrades($trader: String!) { perp { trades(where: { trader: $trader, isOpen: true }) { id isLong leverage } } } "#; let variables = json!({ "trader": "nibi1abc..." }); let request = GraphQLRequest { query: query.to_string(), variables, }; let response = client .post("https://sai-keeper.testnet-2.nibiru.fi/graphql") .json(&request) .send() .await? .json::() .await?; println!("Response: {:#?}", response); Ok(()) } Copy go get github.com/machinebox/graphql Copy package main import ( "context" "fmt" "log" "github.com/machinebox/graphql" ) type Trade struct { ID int `json:"id"` IsLong bool `json:"isLong"` Leverage float64 `json:"leverage"` } type PerpResponse struct { Trades []Trade `json:"trades"` } type Response struct { Perp PerpResponse `json:"perp"` } func main() { // Create client client := graphql.NewClient("https://sai-keeper.testnet-2.nibiru.fi/graphql") // Define query req := graphql.NewRequest(` query GetTrades($trader: String!) { perp { trades(where: { trader: $trader, isOpen: true }) { id isLong leverage } } } `) // Set variables req.Var("trader", "nibi1abc...") // Execute query ctx := context.Background() var response Response if err := client.Run(ctx, req, &response); err != nil { log.Fatal(err) } // Process response for _, trade := range response.Perp.Trades { fmt.Printf("Trade #%d: %fx leverage\n", trade.ID, trade.Leverage) } } Copy package main import ( "context" "fmt" "log" "time" "github.com/machinebox/graphql" ) func fetchTrades(trader string) error { client := graphql.NewClient("https://sai-keeper.testnet-2.nibiru.fi/graphql") req := graphql.NewRequest(` query GetTrades($trader: String!) { perp { trades(where: { trader: $trader, isOpen: true }) { id isLong leverage } } } `) req.Var("trader", trader) // Add timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() var response map[string]interface{} if err := client.Run(ctx, req, &response); err != nil { return fmt.Errorf("query failed: %w", err) } // Check for data perp, ok := response["perp"].(map[string]interface{}) if !ok { return fmt.Errorf("invalid response structure") } trades, ok := perp["trades"].([]interface{}) if !ok { return fmt.Errorf("invalid trades structure") } fmt.Printf("Found %d open trades\n", len(trades)) return nil } func main() { if err := fetchTrades("nibi1abc..."); err != nil { log.Fatal(err) } } Copy # .env file SAI_KEEPER_HTTP_URL=https://sai-keeper.testnet-2.nibiru.fi/graphql SAI_KEEPER_WS_URL=wss://sai-keeper.testnet-2.nibiru.fi/graphql Copy const httpUrl = process.env.SAI_KEEPER_HTTP_URL const wsUrl = process.env.SAI_KEEPER_WS_URL Copy import os http_url = os.getenv('SAI_KEEPER_HTTP_URL') ws_url = os.getenv('SAI_KEEPER_WS_URL') Copy use std::env; let http_url = env::var("SAI_KEEPER_HTTP_URL").unwrap(); Copy import "os" httpUrl := os.Getenv("SAI_KEEPER_HTTP_URL") --- # Filters & Pagination | Sai.fun Master advanced querying patterns including filtering, pagination, and ordering. [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#table-of-contents) Table of Contents --------------------------------------------------------------------------------------------------------------- * [Filter Types](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#filter-types) * [Pagination](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#pagination) * [Ordering](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#ordering) * [Advanced Patterns](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#advanced-patterns) * [Best Practices](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#best-practices) * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#filter-types) Filter Types ----------------------------------------------------------------------------------------------------- Sai Keeper supports various filter types for different data types. ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#string-filters) String Filters **StringFilter** supports equality and pattern matching. Copy input StringFilter { eq: String # Exact match like: String # Pattern match } **Example - Exact Match:** **Example - Pattern Match:** **Usage in JavaScript:** * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#integer-filters) Integer Filters **IntFilter** supports comparison operations. **Example - Fee Amount Range:** **Usage in JavaScript:** * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#float-filters) Float Filters **FloatFilter** for floating-point comparisons. **Example - Filter by Leverage:** * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#time-filters) Time Filters **TimeFilter** for date/time range queries. **Example - Filter by Date Range:** **Usage in JavaScript:** **Helper Function:** * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#enum-filters) Enum Filters Filter by enum values. **Example - Fee Type Filter:** **Example - Trade Type Filter:** * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#pagination) Pagination ------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#basic-pagination) Basic Pagination Use `limit` and `offset` for pagination. **JavaScript Implementation:** * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#fetch-all-pattern) Fetch All Pattern Fetch all results across multiple pages. * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#cursor-based-pagination-alternative) Cursor-Based Pagination Alternative While Sai Keeper uses offset pagination, you can implement cursor-based logic: * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#infinite-scroll-pattern) Infinite Scroll Pattern Implement infinite scroll for UI: * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#ordering) Ordering --------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#order-direction) Order Direction Most queries support `order_by` and `order_desc` parameters. **Order Options by Domain:** **Perp Trades:** * `sequence` - Order by sequence number **Trade History:** * `sequence` - Order by sequence * `trade_id` - Order by trade ID **LP Deposits:** * `depositor` - Order by depositor address * `vault` - Order by vault address **LP Deposit History:** * `depositor` - Order by depositor * `sequence` - Order by sequence * `vault` - Order by vault **LP Withdraw Requests:** * `depositor` - Order by depositor * `unlock_epoch` - Order by unlock epoch * `vault` - Order by vault **Oracle Tokens:** * `id` - Order by token ID * `name` - Order by token name * `permission_group` - Order by permission group **Fee Transactions:** * No explicit ordering parameter, results ordered by ID * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#sorting-examples) Sorting Examples **Newest First:** **Oldest First:** **By Token Name:** * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#advanced-patterns) Advanced Patterns --------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#combined-filters) Combined Filters Combine multiple filters for precise queries. * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#client-side-post-filtering) Client-Side Post-Filtering When server-side filters aren't available, filter client-side: * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#search-implementation) Search Implementation Build a search feature: * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#best-practices) Best Practices --------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#id-1.-use-appropriate-page-sizes) 1\. Use Appropriate Page Sizes **Recommended page sizes:** * Trades: 20-100 * Fee transactions: 50-100 * Trade history: 50-200 * Tokens: 100-500 * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#id-2.-cache-paginated-results) 2\. Cache Paginated Results * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#id-3.-handle-empty-results) 3\. Handle Empty Results * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#id-4.-debounce-filter-changes) 4\. Debounce Filter Changes * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#id-5.-show-loading-states) 5\. Show Loading States * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#id-6.-validate-filter-inputs) 6\. Validate Filter Inputs [PreviousClient Setupchevron-left](https://docs.sai.fun/for-devs/sai-keeper/client-setup) [NextUse Caseschevron-right](https://docs.sai.fun/for-devs/use-cases) Last updated 3 months ago * [Table of Contents](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#table-of-contents) * [Filter Types](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#filter-types) * [String Filters](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#string-filters) * [Integer Filters](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#integer-filters) * [Float Filters](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#float-filters) * [Time Filters](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#time-filters) * [Enum Filters](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#enum-filters) * [Pagination](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#pagination) * [Basic Pagination](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#basic-pagination) * [Fetch All Pattern](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#fetch-all-pattern) * [Cursor-Based Pagination Alternative](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#cursor-based-pagination-alternative) * [Infinite Scroll Pattern](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#infinite-scroll-pattern) * [Ordering](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#ordering) * [Order Direction](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#order-direction) * [Sorting Examples](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#sorting-examples) * [Advanced Patterns](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#advanced-patterns) * [Combined Filters](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#combined-filters) * [Client-Side Post-Filtering](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#client-side-post-filtering) * [Search Implementation](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#search-implementation) * [Best Practices](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#best-practices) * [1\. Use Appropriate Page Sizes](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#id-1.-use-appropriate-page-sizes) * [2\. Cache Paginated Results](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#id-2.-cache-paginated-results) * [3\. Handle Empty Results](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#id-3.-handle-empty-results) * [4\. Debounce Filter Changes](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#id-4.-debounce-filter-changes) * [5\. Show Loading States](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#id-5.-show-loading-states) * [6\. Validate Filter Inputs](https://docs.sai.fun/for-devs/sai-keeper/filters-pagination#id-6.-validate-filter-inputs) Copy query SearchToken { oracle { tokens(where: { name: { eq: "Bitcoin" } }) { id symbol name } } } Copy query SearchByPattern { oracle { tokens(where: { name: { like: "%coin%" } }) { symbol name } } } Copy // Exact match const { data } = await client.query({ query: gql` query SearchToken($name: String!) { oracle { tokens(where: { name: $name }) { symbol name } } } `, variables: { name: "Bitcoin" } }) // Pattern search const searchResults = await client.query({ query: gql` query SearchTokens($pattern: String!) { oracle { tokens(where: { name: $pattern }) { symbol name } } } `, variables: { pattern: "%BTC%" } }) Copy input IntFilter { eq: Int # Equal to gt: Int # Greater than gte: Int # Greater than or equal lt: Int # Less than lte: Int # Less than or equal } Copy query GetLargeFees { fee { feeTransactions( filter: { minAmount: 1000000 # >= 1,000,000 maxAmount: 10000000 # <= 10,000,000 } ) { id totalFeeCharged traderAddress } } } Copy const { data } = await client.query({ query: gql` query GetFeesInRange($min: Int!, $max: Int!) { fee { feeTransactions( filter: { minAmount: $min maxAmount: $max } ) { id totalFeeCharged } } } `, variables: { min: 1000000, max: 10000000 } }) Copy input FloatFilter { eq: Float # Equal to gt: Float # Greater than gte: Float # Greater than or equal lt: Float # Less than lte: Float # Less than or equal } Copy // Note: Direct float filters not used in current schema, // but filter objects use similar patterns const highLeverageTrades = await client.query({ query: gql` query GetHighLeverageTrades($trader: String!) { perp { trades(where: { trader: $trader }) { id leverage isOpen } } } `, variables: { trader: "nibi1abc..." } }) // Filter client-side const filtered = highLeverageTrades.data.perp.trades.filter( trade => trade.leverage > 10 ) Copy input TimeFilter { eq: Time # Exact time gt: Time # After gte: Time # After or at lt: Time # Before lte: Time # Before or at } Copy query GetFeesLastMonth { fee { feeTransactions( filter: { fromDate: "2024-10-01T00:00:00Z" toDate: "2024-10-31T23:59:59Z" } limit: 100 ) { id totalFeeCharged blockTime } } } Copy // Get fees for last 30 days const thirtyDaysAgo = new Date() thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30) const { data } = await client.query({ query: gql` query GetRecentFees($fromDate: Time!, $toDate: Time!) { fee { feeTransactions( filter: { fromDate: $fromDate toDate: $toDate } limit: 100 ) { id totalFeeCharged blockTime } } } `, variables: { fromDate: thirtyDaysAgo.toISOString(), toDate: new Date().toISOString() } }) Copy function getDateRange(days) { const toDate = new Date() const fromDate = new Date() fromDate.setDate(fromDate.getDate() - days) return { fromDate: fromDate.toISOString(), toDate: toDate.toISOString() } } // Usage const last7Days = getDateRange(7) const last30Days = getDateRange(30) const last90Days = getDateRange(90) Copy query GetOpeningFees($trader: String!) { fee { feeTransactions( filter: { traderAddress: $trader feeType: OPENING } limit: 50 ) { id feeType totalFeeCharged } } } Copy // Get only limit orders const { data } = await client.query({ query: gql` query GetLimitOrders($trader: String!) { perp { tradeHistory(where: { trader: $trader }, limit: 100) { id tradeChangeType trade { id tradeType } } } } `, variables: { trader: "nibi1abc..." } }) // Filter for limit_order_created events const limitOrders = data.perp.tradeHistory.filter( h => h.tradeChangeType === 'limit_order_created' ) Copy query GetTradesPaginated($trader: String!, $limit: Int!, $offset: Int!) { perp { trades( where: { trader: $trader } limit: $limit offset: $offset ) { id isOpen leverage } } } Copy async function fetchPage(trader, page = 0, pageSize = 20) { const { data } = await client.query({ query: GET_TRADES_PAGINATED, variables: { trader, limit: pageSize, offset: page * pageSize } }) return data.perp.trades } // Get first page const firstPage = await fetchPage("nibi1abc...", 0, 20) // Get second page const secondPage = await fetchPage("nibi1abc...", 1, 20) // Get third page const thirdPage = await fetchPage("nibi1abc...", 2, 20) Copy async function fetchAllTrades(trader, pageSize = 100) { let allTrades = [] let page = 0 let hasMore = true while (hasMore) { const { data } = await client.query({ query: gql` query GetTrades($trader: String!, $limit: Int!, $offset: Int!) { perp { trades( where: { trader: $trader } limit: $limit offset: $offset ) { id isOpen leverage } } } `, variables: { trader, limit: pageSize, offset: page * pageSize } }) const trades = data.perp.trades allTrades = allTrades.concat(trades) hasMore = trades.length === pageSize page++ console.log(`Fetched page ${page}, total: ${allTrades.length}`) } return allTrades } // Usage const allTrades = await fetchAllTrades("nibi1abc...") console.log(`Total trades: ${allTrades.length}`) Copy async function fetchTradesAfterBlock(trader, afterBlock = 0, limit = 100) { const { data } = await client.query({ query: gql` query GetTrades($trader: String!, $limit: Int!) { perp { trades( where: { trader: $trader } limit: $limit order_desc: false ) { id openBlock { block } } } } `, variables: { trader, limit } }) // Filter trades after cursor block const filtered = data.perp.trades.filter( trade => trade.openBlock.block > afterBlock ) return filtered } // Get first batch const firstBatch = await fetchTradesAfterBlock("nibi1abc...", 0) const lastBlock = firstBatch[firstBatch.length - 1].openBlock.block // Get next batch const nextBatch = await fetchTradesAfterBlock("nibi1abc...", lastBlock) Copy class TradesPaginator { constructor(client, trader, pageSize = 20) { this.client = client this.trader = trader this.pageSize = pageSize this.currentPage = 0 this.allTrades = [] this.hasMore = true } async loadMore() { if (!this.hasMore) { return [] } const { data } = await this.client.query({ query: gql` query GetTrades($trader: String!, $limit: Int!, $offset: Int!) { perp { trades( where: { trader: $trader } limit: $limit offset: $offset order_desc: true ) { id isOpen leverage } } } `, variables: { trader: this.trader, limit: this.pageSize, offset: this.currentPage * this.pageSize } }) const trades = data.perp.trades this.allTrades = this.allTrades.concat(trades) this.hasMore = trades.length === this.pageSize this.currentPage++ return trades } reset() { this.currentPage = 0 this.allTrades = [] this.hasMore = true } getAll() { return this.allTrades } } // Usage const paginator = new TradesPaginator(client, "nibi1abc...", 20) // Load first page const firstPage = await paginator.loadMore() // Load more on scroll window.addEventListener('scroll', async () => { if (isNearBottom() && paginator.hasMore) { const nextPage = await paginator.loadMore() appendToUI(nextPage) } }) Copy query GetTradesOrdered($trader: String!) { perp { trades( where: { trader: $trader } order_by: sequence order_desc: true # Descending (newest first) ) { id openBlock { block_ts } } } } Copy const { data } = await client.query({ query: gql` query GetRecentTrades($trader: String!) { perp { trades( where: { trader: $trader } order_desc: true limit: 20 ) { id openBlock { block_ts } } } } `, variables: { trader: "nibi1abc..." } }) Copy const { data } = await client.query({ query: gql` query GetOldestTrades($trader: String!) { perp { trades( where: { trader: $trader } order_desc: false limit: 20 ) { id openBlock { block_ts } } } } `, variables: { trader: "nibi1abc..." } }) Copy const { data } = await client.query({ query: gql` query GetTokensAlphabetically { oracle { tokens( order_by: name order_desc: false ) { symbol name } } } ` }) Copy const { data } = await client.query({ query: gql` query GetFilteredFees( $trader: String! $feeType: FeeType! $fromDate: Time! $toDate: Time! $minAmount: Int! ) { fee { feeTransactions( filter: { traderAddress: $trader feeType: $feeType fromDate: $fromDate toDate: $toDate minAmount: $minAmount } limit: 100 ) { id totalFeeCharged blockTime } } } `, variables: { trader: "nibi1abc...", feeType: "OPENING", fromDate: "2024-01-01T00:00:00Z", toDate: "2024-12-31T23:59:59Z", minAmount: 1000000 } }) Copy // Fetch all trades const { data } = await client.query({ query: gql` query GetAllTrades($trader: String!) { perp { trades(where: { trader: $trader }, limit: 1000) { id leverage isLong isOpen collateralAmount state { pnlPct } perpBorrowing { baseToken { symbol } } } } } `, variables: { trader: "nibi1abc..." } }) // Filter for high-leverage profitable longs const filtered = data.perp.trades.filter(trade => trade.isLong && trade.leverage > 10 && trade.state.pnlPct > 0.1 && // 10% profit trade.isOpen ) // Group by token const byToken = filtered.reduce((acc, trade) => { const symbol = trade.perpBorrowing.baseToken.symbol if (!acc[symbol]) acc[symbol] = [] acc[symbol].push(trade) return acc }, {}) console.log('Profitable high-leverage longs by token:', byToken) Copy async function searchTrades(trader, searchTerm) { // Fetch all trades const { data } = await client.query({ query: gql` query GetTrades($trader: String!) { perp { trades(where: { trader: $trader }) { id isLong leverage perpBorrowing { baseToken { symbol name } marketId } } } } `, variables: { trader } }) // Search by token symbol or name const searchLower = searchTerm.toLowerCase() return data.perp.trades.filter(trade => { const symbol = trade.perpBorrowing.baseToken.symbol.toLowerCase() const name = trade.perpBorrowing.baseToken.name.toLowerCase() return symbol.includes(searchLower) || name.includes(searchLower) }) } // Usage const btcTrades = await searchTrades("nibi1abc...", "bitcoin") const ethTrades = await searchTrades("nibi1abc...", "eth") Copy // Good: Reasonable page size const PAGE_SIZE = 50 // Bad: Too large, slow queries const PAGE_SIZE = 10000 // Bad: Too small, too many requests const PAGE_SIZE = 5 Copy class CachedPaginator { constructor(client, query, variables) { this.client = client this.query = query this.variables = variables this.cache = new Map() } async getPage(page, pageSize = 20) { const cacheKey = `${page}-${pageSize}` if (this.cache.has(cacheKey)) { return this.cache.get(cacheKey) } const { data } = await this.client.query({ query: this.query, variables: { ...this.variables, limit: pageSize, offset: page * pageSize } }) this.cache.set(cacheKey, data) return data } clearCache() { this.cache.clear() } } Copy async function fetchTradesWithEmptyCheck(trader, page = 0) { const { data } = await client.query({ query: GET_TRADES, variables: { trader, limit: 20, offset: page * 20 } }) const trades = data.perp.trades if (trades.length === 0) { if (page === 0) { console.log('No trades found for this trader') } else { console.log('No more trades to load') } } return trades } Copy import { debounce } from 'lodash' const debouncedSearch = debounce(async (searchTerm) => { const results = await searchTrades(trader, searchTerm) updateUI(results) }, 300) // Wait 300ms after user stops typing // Usage in React function SearchInput() { const handleChange = (e) => { debouncedSearch(e.target.value) } return } Copy async function loadTradesWithUI(trader, page) { try { showLoadingSpinner() const trades = await fetchPage(trader, page) if (trades.length === 0) { showEmptyState() } else { renderTrades(trades) } } catch (error) { showErrorState(error) } finally { hideLoadingSpinner() } } Copy function validateDateRange(fromDate, toDate) { const from = new Date(fromDate) const to = new Date(toDate) if (isNaN(from.getTime()) || isNaN(to.getTime())) { throw new Error('Invalid date format') } if (from > to) { throw new Error('From date must be before to date') } const maxRange = 90 * 24 * 60 * 60 * 1000 // 90 days if (to - from > maxRange) { throw new Error('Date range too large (max 90 days)') } return { fromDate, toDate } } // Usage try { const { fromDate, toDate } = validateDateRange( userInputFrom, userInputTo ) const { data } = await client.query({ query: GET_FEES, variables: { fromDate, toDate } }) } catch (error) { showError(error.message) } --- # Core Concepts | Sai.fun Understanding these core concepts will help you effectively use the Sai Keeper API. [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#table-of-contents) Table of Contents ---------------------------------------------------------------------------------------------------------- * [Protocol Overview](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#protocol-overview) * [Data Models](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#data-models) * [Perpetual Trading](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#perpetual-trading) * [Liquidity Pools](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#liquidity-pools) * [Oracle System](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#oracle-system) * [Fee Structure](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#fee-structure) * [Blocks and Timestamps](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#blocks-and-timestamps) [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#protocol-overview) Protocol Overview ---------------------------------------------------------------------------------------------------------- Sai Protocol is a decentralized perpetual trading platform on Nibiru Chain. The protocol is implemented in Rust and compiled into WebAssembly (Wasm) bytecode, with 5 key contracts managing all operations fully on-chain. **Key components:** * **Perpetual Markets**: Trade with leverage without expiration dates * **Liquidity Vaults**: Users provide liquidity to earn yield * **Oracle Prices**: Reliable price feeds for all tradeable assets * **Fee System**: Transparent fee structure for all operations [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#data-models) Data Models ---------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#hierarchy) Hierarchy [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#perpetual-trading) Perpetual Trading ---------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#what-is-a-perpetual) What is a Perpetual? A perpetual contract (perp) is a derivative that lets you trade an asset with leverage without an expiration date. Unlike futures, perpetuals use a funding rate mechanism (called borrowing fees in Sai) to keep prices anchored to spot prices. ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#trade-lifecycle) Trade Lifecycle 1. **Opening**: Trader opens a position (long or short) 2. **Active**: Position accumulates borrowing fees based on market conditions 3. **Closing**: Position closed by: * User action * Take Profit (TP) trigger * Stop Loss (SL) trigger * Liquidation (if losses exceed threshold) ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#key-concepts) Key Concepts **Leverage**: Multiply your position size **Long vs Short**: * **Long**: Profit when price increases * **Short**: Profit when price decreases **Open Interest (OI)**: Total value of open positions * `oiLong`: Long positions open interest * `oiShort`: Short positions open interest * `oiMax`: Maximum allowed open interest **Borrowing Fees**: Charged per hour based on OI imbalance * Higher fees on the side with more open interest * Helps balance long/short positions * Converted to APR: `feesPerHour × 24 × 365 × 100` ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#trade-types) Trade Types * **Market Trade** (`trade`): Instant execution at current price * **Limit Order** (`limit`): Execute when price reaches target * **Stop Order** (`stop`): Trigger when price hits stop level ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#pnl-calculation) PnL Calculation **Example:** * Long 10x position with $1,000 collateral = $10,000 position value * Entry: $50,000 | Current: $55,000 * PnL = $10,000 × ($55,000 - $50,000) / $50,000 = $1,000 (10%) ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#liquidation) Liquidation A position gets liquidated when losses approach the collateral amount: **Example (10x leverage, 5% maintenance margin):** * Long entry at $50,000 → Liquidation at ~$47,500 * Short entry at $50,000 → Liquidation at ~$52,500 [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#liquidity-pools) Liquidity Pools ------------------------------------------------------------------------------------------------------ ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#how-lp-works) How LP Works Liquidity Providers (LPs) deposit assets into vaults. These vaults: * Serve as counterparty to traders * Earn fees from trading activity * Share profits when traders lose * Cover losses when traders profit **Risk/Reward:** * LPs earn trading fees and a share of trader losses * LPs bear the risk when traders are profitable * Net profit tracked in `revenueInfo` ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#vault-metrics) Vault Metrics **TVL (Total Value Locked)**: Total assets in the vault **Share Price**: Value of one vault share * Increases when vault is profitable * Decreases when vault incurs losses * Initial share price typically starts at 1.0 **APY (Annual Percentage Yield)**: Annualized return * Calculated from historical revenue * Updates based on vault performance * Can fluctuate with market conditions ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#deposit-withdrawal-flow) Deposit/Withdrawal Flow 1. **Deposit**: User deposits → receives shares 2. **Withdrawal Request**: User requests withdrawal * Shares locked until next epoch * `unlockEpoch` indicates when withdrawal available * Can set `autoRedeem: true` for automatic redemption 3. **Withdrawal Execution**: After epoch ends * User redeems shares for assets * Value = `Shares × Current Share Price` ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#epochs) Epochs Time periods for LP operations: * **Duration**: Check `epochDurationDays` or `epochDurationHours` * **Current Epoch**: Track with `currentEpoch` * **Epoch Start**: Unix timestamp of epoch start * **Purpose**: Prevents instant withdrawal attacks, ensures fair pricing ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#revenue-info) Revenue Info Each vault tracks detailed revenue metrics: * `RevenueCumulative`: Total revenue earned all-time * `NetProfit`: Current net profit (revenue - liabilities) * `TraderLosses`: Profits from trader losses * `ClosedPnl`: Realized PnL from closed positions * `CurrentEpochPositiveOpenPnl`: Current unrealized gains * `Liabilities`: Current liabilities (trader unrealized profits) * `Rewards`: Additional rewards received [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#oracle-system) Oracle System -------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#price-feeds) Price Feeds Oracle provides reliable, up-to-date prices for all tokens: **Update Mechanism**: * Prices updated on-chain via oracle validators * Each update recorded with block number and timestamp * Multiple price sources aggregated for accuracy * Updates occur every few blocks (varies by token) **Token Types**: * **Bank tokens** (`TokenType.bank`): Native Cosmos SDK tokens * **ERC20 tokens** (`TokenType.erc20`): Ethereum-compatible tokens ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#using-prices) Using Prices Always check `lastUpdatedBlock` to ensure price freshness: **Best Practice:** [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#fee-structure) Fee Structure -------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#fee-types) Fee Types **Opening Fees** (`FeeType.OPENING`): * Charged when opening a position * Based on position size * Components: vault fee, gov fee **Closing Fees** (`FeeType.CLOSING`): * Charged when closing a position * Based on position size * Components: vault fee, gov fee, trigger fee (if triggered by keeper) ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#fee-components) Fee Components Every fee transaction breaks down into: * **Vault Fee**: Goes to LP vault (largest portion, typically 80-90%) * **Gov Fee**: Protocol governance fee (typically 10-20%) * **Trigger Fee**: Reward for triggering stop/limit orders (if applicable) **Example Fee Breakdown:** ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#fee-multiplier) Fee Multiplier Dynamic multiplier based on market conditions: * Higher multipliers during high volatility or risk periods * Typical range: 1.0x to 2.0x * Protects LP vaults during risky conditions * Tracked in `avgFeeMultiplier` for analytics ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#bad-debt) Bad Debt When a position is liquidated but collateral is insufficient: * Bad debt is tracked per transaction * Absorbed by the protocol/vault * Monitored in fee analytics * Rare occurrence with proper risk management [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#blocks-and-timestamps) Blocks and Timestamps ------------------------------------------------------------------------------------------------------------------ ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#block-structure) Block Structure **Usage:** * `block`: Sequential number, increases monotonically * `block_ts`: Human-readable timestamp * Every important event (trade open/close, deposit, etc.) includes block info ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#time-type) Time Type The `Time` scalar represents timestamps in RFC3339 format: ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#filtering-by-time) Filtering by Time Use `TimeFilter` for date ranges: ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#block-vs-timestamp) Block vs Timestamp * **Block height**: Sequential, deterministic, never changes * **Timestamp**: Human-readable, approximate (block times can vary) * Most queries support both **Best Practice**: Use timestamps for date ranges, blocks for precise event ordering. [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#key-terminology) Key Terminology ------------------------------------------------------------------------------------------------------ ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#trading-terms) Trading Terms * **Collateral**: Asset deposited to open positions * **Margin**: Same as collateral in perpetuals context * **Liquidation**: Forced position closure when losses exceed threshold * **Funding Rate**: Periodic payment between longs and shorts (borrowing fees in Sai) * **Mark Price**: Oracle price used for PnL calculations * **Index Price**: Spot price from oracle ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#lp-terms) LP Terms * **TVL**: Total Value Locked in a vault * **Share Price**: Value of one vault share token * **Epoch**: Time period for LP operations * **APY**: Annual Percentage Yield * **Revenue**: Earnings from trading fees and trader losses ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#fee-terms) Fee Terms * **Opening Fee**: Fee charged when opening a position * **Closing Fee**: Fee charged when closing a position * **Fee Multiplier**: Dynamic multiplier applied to base fees * **Bad Debt**: Losses exceeding available collateral [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#data-freshness) Data Freshness ---------------------------------------------------------------------------------------------------- * **Real-time**: Subscriptions provide live updates as events occur * **Query Latency**: Typically <100ms for queries * **Block Time**: ~1-2 seconds on Nibiru Chain * **Oracle Updates**: Varies by token, typically every few blocks * **Cache**: Some data cached briefly for performance [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#smart-contract-architecture) Smart Contract Architecture ------------------------------------------------------------------------------------------------------------------------------ Sai Protocol consists of 5 key Wasm contracts: 1. **Perp Manager**: Handles perpetual trading operations 2. **LP Vault**: Manages positions and withdrawals 3. **Oracle**: Provides price feeds 4. **Fee Manager**: Processes and distributes fees 5. **Governance**: Protocol parameter management All contracts are fully on-chain and written in Rust for security and performance. [hashtag](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#next-steps) Next Steps -------------------------------------------------------------------------------------------- * Learn about [Perp API](https://docs.sai.fun/for-devs/sai-keeper/api-perp) for trading data * Explore [LP API](https://docs.sai.fun/for-devs/sai-keeper/api-lp) for liquidity pool data * Check [Oracle API](https://docs.sai.fun/for-devs/sai-keeper/api-oracle) for price feeds * Understand [Fee API](https://docs.sai.fun/for-devs/sai-keeper/api-fees) for fee analytics [PreviousSai Keeperchevron-left](https://docs.sai.fun/for-devs/sai-keeper) [NextPerp APIchevron-right](https://docs.sai.fun/for-devs/sai-keeper/api-perp) Last updated 2 months ago * [Table of Contents](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#table-of-contents) * [Protocol Overview](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#protocol-overview) * [Data Models](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#data-models) * [Hierarchy](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#hierarchy) * [Perpetual Trading](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#perpetual-trading) * [What is a Perpetual?](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#what-is-a-perpetual) * [Trade Lifecycle](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#trade-lifecycle) * [Key Concepts](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#key-concepts) * [Trade Types](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#trade-types) * [PnL Calculation](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#pnl-calculation) * [Liquidation](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#liquidation) * [Liquidity Pools](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#liquidity-pools) * [How LP Works](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#how-lp-works) * [Vault Metrics](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#vault-metrics) * [Deposit/Withdrawal Flow](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#deposit-withdrawal-flow) * [Epochs](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#epochs) * [Revenue Info](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#revenue-info) * [Oracle System](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#oracle-system) * [Price Feeds](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#price-feeds) * [Using Prices](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#using-prices) * [Fee Structure](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#fee-structure) * [Fee Types](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#fee-types) * [Fee Components](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#fee-components) * [Fee Multiplier](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#fee-multiplier) * [Bad Debt](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#bad-debt) * [Blocks and Timestamps](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#blocks-and-timestamps) * [Block Structure](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#block-structure) * [Time Type](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#time-type) * [Filtering by Time](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#filtering-by-time) * [Block vs Timestamp](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#block-vs-timestamp) * [Key Terminology](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#key-terminology) * [Trading Terms](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#trading-terms) * [LP Terms](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#lp-terms) * [Fee Terms](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#fee-terms) * [Data Freshness](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#data-freshness) * [Smart Contract Architecture](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#smart-contract-architecture) * [Next Steps](https://docs.sai.fun/for-devs/sai-keeper/core-concepts#next-steps) Copy Query/Subscription ├── perp (Perpetuals) │ ├── trades │ ├── tradeHistory │ └── borrowings ├── lp (Liquidity Pools) │ ├── vaults │ ├── deposits │ └── withdrawRequests ├── oracle (Price Feeds) │ ├── tokens │ └── tokenPricesUsd └── fee (Fee Analytics) ├── feeTransactions ├── feeDailyStats └── summaries Copy Position Value = Collateral × Leverage Copy Unrealized PnL = Position Value × (Current Price - Open Price) / Open Price For Long: PnL = positive when price increases For Short: PnL = positive when price decreases Copy Liquidation Price (Long) = Entry Price × (1 - 1/Leverage + Maintenance Margin) Liquidation Price (Short) = Entry Price × (1 + 1/Leverage - Maintenance Margin) Copy TVL = Available Assets + Assets in Open Positions Copy Share Price = Total Vault Value / Total Shares Outstanding Copy APY = (Net Profit / TVL) × (365 / Epoch Duration Days) × 100 Copy Shares Received = Deposit Amount / Current Share Price Copy { oracle { tokenPricesUsd(where: { tokenId: 1 }) { priceUsd lastUpdatedBlock { block block_ts } } } } Copy const ageSeconds = (Date.now() - new Date(lastUpdatedBlock.block_ts).getTime()) / 1000 if (ageSeconds > 60) { console.warn('Price may be stale') } Copy Total Fee = Vault Fee + Gov Fee + Trigger Fee Copy Position Size: $10,000 Opening Fee: 0.1% = $10 ├─ Vault Fee: $8 (80%) ├─ Gov Fee: $2 (20%) └─ Total: $10 Copy Actual Fee = Base Fee × Fee Multiplier Copy Bad Debt = Total Loss - Collateral Available Copy type Block { block: Int! # Block height/number block_ts: Time! # Block timestamp (RFC3339 format) } Copy 2024-11-03T10:30:00Z Copy { fee { feeTransactions( filter: { fromDate: "2024-01-01T00:00:00Z" toDate: "2024-01-31T23:59:59Z" } ) { id } } } --- # Examples: Queries | Sai.fun Practical examples for common query patterns using the Sai Keeper API. [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#table-of-contents) Table of Contents ------------------------------------------------------------------------------------------------------------- * [Setup](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#setup) * [Perp Queries](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#perp-queries) * [LP Queries](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#lp-queries) * [Oracle Queries](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#oracle-queries) * [Fee Queries](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#fee-queries) * [Advanced Patterns](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#advanced-patterns) [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#setup) Setup ------------------------------------------------------------------------------------- These examples use Apollo Client, but the queries work with any GraphQL client. Copy import { ApolloClient, InMemoryCache, gql } from '@apollo/client' const client = new ApolloClient({ uri: 'https://sai-keeper.testnet-2.nibiru.fi/graphql', cache: new InMemoryCache() }) * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#perp-queries) Perp Queries --------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-1-get-all-open-positions-for-a-trader) Example 1: Get All Open Positions for a Trader ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-2-get-trade-history-with-realized-pnl) Example 2: Get Trade History with Realized PnL ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-3-get-market-information-and-funding-rates) Example 3: Get Market Information and Funding Rates ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-4-list-all-available-markets) Example 4: List All Available Markets * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#lp-queries) LP Queries ----------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-5-get-all-vaults-with-metrics) Example 5: Get All Vaults with Metrics ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-6-get-user-lp-positions) Example 6: Get User LP Positions ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-7-get-vault-deposit-withdrawal-history) Example 7: Get Vault Deposit/Withdrawal History * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#oracle-queries) Oracle Queries ------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-8-get-current-token-prices) Example 8: Get Current Token Prices ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-9-get-specific-token-price-with-freshness-check) Example 9: Get Specific Token Price with Freshness Check * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#fee-queries) Fee Queries ------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-10-get-user-fee-history) Example 10: Get User Fee History ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-11-get-protocol-fee-summary) Example 11: Get Protocol Fee Summary ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-12-get-daily-fee-statistics) Example 12: Get Daily Fee Statistics * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#advanced-patterns) Advanced Patterns ------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-13-combine-multiple-queries) Example 13: Combine Multiple Queries ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-14-pagination-pattern) Example 14: Pagination Pattern ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-15-error-handling-pattern) Example 15: Error Handling Pattern [PreviousFees APIchevron-left](https://docs.sai.fun/for-devs/sai-keeper/api-fees) [NextExamples: Subscriptionschevron-right](https://docs.sai.fun/for-devs/sai-keeper/examples-subscriptions) Last updated 3 months ago * [Table of Contents](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#table-of-contents) * [Setup](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#setup) * [Perp Queries](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#perp-queries) * [Example 1: Get All Open Positions for a Trader](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-1-get-all-open-positions-for-a-trader) * [Example 2: Get Trade History with Realized PnL](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-2-get-trade-history-with-realized-pnl) * [Example 3: Get Market Information and Funding Rates](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-3-get-market-information-and-funding-rates) * [Example 4: List All Available Markets](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-4-list-all-available-markets) * [LP Queries](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#lp-queries) * [Example 5: Get All Vaults with Metrics](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-5-get-all-vaults-with-metrics) * [Example 6: Get User LP Positions](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-6-get-user-lp-positions) * [Example 7: Get Vault Deposit/Withdrawal History](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-7-get-vault-deposit-withdrawal-history) * [Oracle Queries](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#oracle-queries) * [Example 8: Get Current Token Prices](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-8-get-current-token-prices) * [Example 9: Get Specific Token Price with Freshness Check](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-9-get-specific-token-price-with-freshness-check) * [Fee Queries](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#fee-queries) * [Example 10: Get User Fee History](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-10-get-user-fee-history) * [Example 11: Get Protocol Fee Summary](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-11-get-protocol-fee-summary) * [Example 12: Get Daily Fee Statistics](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-12-get-daily-fee-statistics) * [Advanced Patterns](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#advanced-patterns) * [Example 13: Combine Multiple Queries](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-13-combine-multiple-queries) * [Example 14: Pagination Pattern](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-14-pagination-pattern) * [Example 15: Error Handling Pattern](https://docs.sai.fun/for-devs/sai-keeper/examples-queries#example-15-error-handling-pattern) Copy const GET_OPEN_POSITIONS = gql` query GetOpenPositions($trader: String!) { perp { trades( where: { trader: $trader isOpen: true } order_desc: true ) { id isLong leverage collateralAmount openPrice sl tp perpBorrowing { baseToken { symbol name logoUrl } collateralToken { symbol decimals } marketId } openBlock { block_ts } state { pnlCollateral pnlPct pnlCollateralAfterFees liquidationPrice positionValue borrowingFeeCollateral } } } } ` // Usage const { data } = await client.query({ query: GET_OPEN_POSITIONS, variables: { trader: 'nibi1abc...' } }) // Process results data.perp.trades.forEach(trade => { const token = trade.perpBorrowing.baseToken const decimals = trade.perpBorrowing.collateralToken.decimals console.log(`${token.symbol} ${trade.isLong ? 'LONG' : 'SHORT'}`) console.log(`Leverage: ${trade.leverage}x`) console.log(`PnL: ${(trade.state.pnlPct * 100).toFixed(2)}%`) console.log(`Liquidation: $${trade.state.liquidationPrice.toFixed(2)}`) }) Copy const GET_TRADE_HISTORY = gql` query GetTradeHistory($trader: String!, $limit: Int!) { perp { tradeHistory( where: { trader: $trader } limit: $limit order_by: sequence order_desc: true ) { id tradeChangeType realizedPnlCollateral realizedPnlPct block { block block_ts } trade { id isLong leverage openPrice closePrice perpBorrowing { baseToken { symbol } collateralToken { symbol decimals } } } } } } ` // Usage const { data } = await client.query({ query: GET_TRADE_HISTORY, variables: { trader: 'nibi1abc...', limit: 50 } }) // Calculate total realized PnL const totalPnL = data.perp.tradeHistory .filter(h => h.realizedPnlCollateral !== null) .reduce((sum, h) => sum + h.realizedPnlCollateral, 0) console.log(`Total Realized PnL: ${totalPnL}`) Copy const GET_MARKET_INFO = gql` query GetMarketInfo($collateralId: Int!, $marketId: Int!) { perp { borrowing(collateralId: $collateralId, marketId: $marketId) { marketId baseToken { symbol name logoUrl } quoteToken { symbol } collateralToken { symbol decimals } price oiLong oiShort oiMax feesPerHourLong feesPerHourShort openFeePct closeFeePct maxLeverage minLeverage minPositionSizeUSD } } } ` // Usage const { data } = await client.query({ query: GET_MARKET_INFO, variables: { collateralId: 1, marketId: 1 } }) const market = data.perp.borrowing // Calculate funding rate APR const hourlyRateLong = market.feesPerHourLong const aprLong = hourlyRateLong * 24 * 365 * 100 console.log(`${market.baseToken.symbol} Market`) console.log(`Price: $${market.price}`) console.log(`OI Long: ${market.oiLong} | Short: ${market.oiShort}`) console.log(`Max Leverage: ${market.maxLeverage}x`) console.log(`Funding APR Long: ${aprLong.toFixed(2)}%`) Copy const GET_ALL_MARKETS = gql` query GetAllMarkets { perp { borrowings(order_by: base_token_name) { marketId baseToken { symbol name logoUrl tradingViewSymbol } collateralToken { symbol } visible } } } ` // Usage const { data } = await client.query({ query: GET_ALL_MARKETS }) // Filter only visible markets const visibleMarkets = data.perp.borrowings.filter(m => m.visible) console.log(`Available markets: ${visibleMarkets.length}`) visibleMarkets.forEach(m => { console.log(`${m.baseToken.symbol}-${m.collateralToken.symbol} (ID: ${m.marketId})`) }) Copy const GET_ALL_VAULTS = gql` query GetAllVaults { lp { vaults { address collateralToken { symbol name logoUrl decimals } tvl sharePrice apy availableAssets currentEpoch epochStart sharesDenom collateralERC20 sharesERC20 revenueInfo { RevenueCumulative NetProfit TraderLosses CurrentEpochPositiveOpenPnl Liabilities } } epochDurationDays } } ` // Usage const { data } = await client.query({ query: GET_ALL_VAULTS }) data.lp.vaults.forEach(vault => { const decimals = vault.collateralToken.decimals const tvlFormatted = vault.tvl / (10 ** decimals) console.log(`${vault.collateralToken.symbol} Vault`) console.log(`TVL: ${tvlFormatted.toLocaleString()} ${vault.collateralToken.symbol}`) console.log(`APY: ${vault.apy?.toFixed(2) || 'N/A'}%`) console.log(`Share Price: ${vault.sharePrice}`) console.log(`Epoch: ${vault.currentEpoch}`) }) Copy const GET_USER_LP_POSITIONS = gql` query GetUserLPPositions($user: String!) { lp { deposits(where: { depositor: $user }) { depositor shares vault { address collateralToken { symbol decimals } sharePrice apy currentEpoch } } withdrawRequests(where: { depositor: $user }) { depositor shares status unlockEpoch autoRedeem vault { address collateralToken { symbol decimals } sharePrice currentEpoch } } } } ` // Usage const { data } = await client.query({ query: GET_USER_LP_POSITIONS, variables: { user: 'nibi1abc...' } }) // Calculate total portfolio value let totalValue = 0 data.lp.deposits.forEach(deposit => { const decimals = deposit.vault.collateralToken.decimals const value = (deposit.shares * deposit.vault.sharePrice) / (10 ** decimals) totalValue += value console.log(`${deposit.vault.collateralToken.symbol} Vault`) console.log(`Shares: ${deposit.shares}`) console.log(`Value: ${value.toFixed(2)}`) console.log(`APY: ${deposit.vault.apy?.toFixed(2) || 'N/A'}%`) }) // Check pending withdrawals data.lp.withdrawRequests.forEach(request => { const isReady = request.vault.currentEpoch >= request.unlockEpoch const epochsRemaining = Math.max(0, request.unlockEpoch - request.vault.currentEpoch) console.log(`\nPending Withdrawal: ${request.vault.collateralToken.symbol}`) console.log(`Status: ${isReady ? 'Ready' : `${epochsRemaining} epochs remaining`}`) console.log(`Auto-redeem: ${request.autoRedeem}`) }) console.log(`\nTotal LP Value: ${totalValue.toFixed(2)}`) Copy const GET_VAULT_HISTORY = gql` query GetVaultHistory($vaultAddress: String!, $limit: Int!) { lp { depositHistory( where: { vault: $vaultAddress } limit: $limit order_by: sequence order_desc: true ) { id depositor amount shares isWithdraw block { block block_ts } vault { collateralToken { symbol decimals } } } } } ` // Usage const { data } = await client.query({ query: GET_VAULT_HISTORY, variables: { vaultAddress: 'nibi1vault...', limit: 100 } }) data.lp.depositHistory.forEach(event => { const decimals = event.vault.collateralToken.decimals const amount = event.amount / (10 ** decimals) const action = event.isWithdraw ? 'Withdrew' : 'Deposited' const date = new Date(event.block.block_ts).toLocaleDateString() console.log(`${date}: ${event.depositor} ${action} ${amount.toFixed(2)}`) }) Copy const GET_TOKEN_PRICES = gql` query GetTokenPrices { oracle { tokenPricesUsd(limit: 100) { token { id symbol name logoUrl } priceUsd lastUpdatedBlock { block block_ts } } } } ` // Usage const { data } = await client.query({ query: GET_TOKEN_PRICES }) // Create price map const priceMap = {} data.oracle.tokenPricesUsd.forEach(item => { priceMap[item.token.symbol] = { price: item.priceUsd, lastUpdate: item.lastUpdatedBlock.block_ts } }) console.log(`BTC: $${priceMap['BTC'].price.toFixed(2)}`) console.log(`ETH: $${priceMap['ETH'].price.toFixed(2)}`) Copy const GET_TOKEN_PRICE = gql` query GetTokenPrice($tokenId: Int!) { oracle { tokenPricesUsd(where: { tokenId: $tokenId }) { token { symbol name } priceUsd lastUpdatedBlock { block block_ts } } } } ` // Usage const { data } = await client.query({ query: GET_TOKEN_PRICE, variables: { tokenId: 1 } }) const priceData = data.oracle.tokenPricesUsd[0] const lastUpdate = new Date(priceData.lastUpdatedBlock.block_ts) const ageSeconds = (Date.now() - lastUpdate.getTime()) / 1000 console.log(`${priceData.token.symbol}: $${priceData.priceUsd}`) console.log(`Last updated: ${ageSeconds.toFixed(0)}s ago`) if (ageSeconds > 60) { console.warn('⚠️ Price may be stale') } Copy const GET_USER_FEES = gql` query GetUserFees($trader: String!, $limit: Int!) { fee { feeTransactions( filter: { traderAddress: $trader } limit: $limit ) { id feeType totalFeeCharged govFee vaultFee referrerAllocation triggerFee collateralDenom feeMultiplier blockTime tradeId } } } ` // Usage const { data } = await client.query({ query: GET_USER_FEES, variables: { trader: 'nibi1abc...', limit: 50 } }) // Calculate total fees paid const totalFees = data.fee.feeTransactions.reduce((sum, fee) => { return sum + fee.totalFeeCharged }, 0) console.log(`Total fees paid: ${totalFees}`) console.log(`Number of transactions: ${data.fee.feeTransactions.length}`) Copy const GET_PROTOCOL_SUMMARY = gql` query GetProtocolSummary($fromDate: Time, $toDate: Time) { fee { protocolFeeSummary(fromDate: $fromDate, toDate: $toDate) { period { fromDate toDate } totalFees totalOpeningFees totalClosingFees totalGovFees totalVaultFees totalReferrerFees totalTriggerFees totalBadDebt openingCount closingCount uniqueTraders avgFeeMultiplier } } } ` // Usage - Get last 30 days const thirtyDaysAgo = new Date() thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30) const { data } = await client.query({ query: GET_PROTOCOL_SUMMARY, variables: { fromDate: thirtyDaysAgo.toISOString(), toDate: new Date().toISOString() } }) const summary = data.fee.protocolFeeSummary console.log('Protocol Summary (Last 30 Days)') console.log(`Total Fees: ${summary.totalFees}`) console.log(`Gov Fees: ${summary.totalGovFees}`) console.log(`Vault Fees: ${summary.totalVaultFees}`) console.log(`Unique Traders: ${summary.uniqueTraders}`) console.log(`Avg Fee Multiplier: ${summary.avgFeeMultiplier.toFixed(2)}x`) Copy const GET_DAILY_STATS = gql` query GetDailyStats($limit: Int!) { fee { feeDailyStats( filter: { protocolWide: true } limit: $limit ) { date collateralDenom openingFeeTotal closingFeeTotal openingFeeCount closingFeeCount totalBadDebt } } } ` // Usage const { data } = await client.query({ query: GET_DAILY_STATS, variables: { limit: 30 } }) // Create time series for charting const chartData = data.fee.feeDailyStats.map(stat => ({ date: new Date(stat.date).toLocaleDateString(), totalFees: stat.openingFeeTotal + stat.closingFeeTotal, transactions: stat.openingFeeCount + stat.closingFeeCount })) console.log('Daily Fee Data:', chartData) Copy const GET_DASHBOARD_DATA = gql` query GetDashboardData($trader: String!) { perp { trades(where: { trader: $trader, isOpen: true }) { id isLong leverage state { pnlCollateral pnlPct } perpBorrowing { baseToken { symbol } } } } lp { deposits(where: { depositor: $trader }) { shares vault { sharePrice collateralToken { symbol decimals } } } } fee { traderFeeSummary(traderAddress: $trader) { totalFees openingCount closingCount } } } ` // Usage - Get all user data in one query const { data } = await client.query({ query: GET_DASHBOARD_DATA, variables: { trader: 'nibi1abc...' } }) console.log('Open Positions:', data.perp.trades.length) console.log('LP Deposits:', data.lp.deposits.length) console.log('Total Trades:', data.fee.traderFeeSummary.openingCount + data.fee.traderFeeSummary.closingCount) Copy // Fetch all trades with pagination async function fetchAllTrades(trader) { const QUERY = gql` query GetTrades($trader: String!, $limit: Int!, $offset: Int!) { perp { trades( where: { trader: $trader } limit: $limit offset: $offset ) { id isOpen isLong } } } ` let allTrades = [] let offset = 0 const limit = 100 let hasMore = true while (hasMore) { const { data } = await client.query({ query: QUERY, variables: { trader, limit, offset } }) const trades = data.perp.trades allTrades = allTrades.concat(trades) hasMore = trades.length === limit offset += limit console.log(`Fetched ${allTrades.length} trades...`) } return allTrades } // Usage const allTrades = await fetchAllTrades('nibi1abc...') console.log(`Total trades: ${allTrades.length}`) Copy async function queryWithRetry(query, variables, maxRetries = 3) { let lastError for (let i = 0; i < maxRetries; i++) { try { const { data, errors } = await client.query({ query, variables }) if (errors && errors.length > 0) { throw new Error(errors[0].message) } return data } catch (error) { lastError = error console.warn(`Query attempt ${i + 1} failed:`, error.message) if (i < maxRetries - 1) { // Exponential backoff await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i))) } } } throw new Error(`Query failed after ${maxRetries} attempts: ${lastError.message}`) } // Usage try { const data = await queryWithRetry(GET_OPEN_POSITIONS, { trader: 'nibi1abc...' }) console.log('Success:', data) } catch (error) { console.error('Failed:', error.message) } --- # Terms of Use | Sai.fun Last Modified: September 1, 2025 SAI IS A BLOCKCHAIN-ORIENTED TOOL ENABLING CERTAIN ON-CHAIN FUNCTIONALITIES. USING THESE FUNCTIONALITIES (INCLUDING VIA THE INTERFACE OR THE WEBSITE) POSES SIGNIFICANT RISKS TO YOU AND YOUR DIGITAL ASSETS. THIS DOCUMENT CONTAINS VERY IMPORTANT INFORMATION REGARDING THESE RISKS AND YOUR RIGHTS AND OBLIGATIONS, AS WELL AS CONDITIONS, LIMITATIONS, AND EXCLUSIONS THAT MIGHT APPLY TO YOU AND YOUR RIGHTS. PLEASE READ IT CAREFULLY. SAI INTERACTS OR FACILITATES ACCESS TO THIRD PARTY SERVICES THAT ARE INDEPENDENT FROM AND UNAFFILIATED WITH SAI, AND THE USER ASSUMES ALL RISK FROM USE OF SUCH THIRD-PARTY SERVICES. THESE TERMS REQUIRE THE USE OF ARBITRATION ON AN INDIVIDUAL BASIS TO RESOLVE DISPUTES, RATHER THAN JURY TRIALS OR CLASS ACTIONS. BY USING THE WEBSITE, THE INTERFACE, OR ANY OF OUR SERVICES, YOU ACCEPT AND ARE BOUND BY THESE TERMS OF USE. YOU MAY NOT USE OUR WEBSITE, INTERFACE, OR SERVICES IF YOU: (A) DO NOT AGREE TO THESE TERMS; (B) ARE NOT THE OLDER OF: (i) AT LEAST EIGHTEEN (18) YEARS OF AGE OR (ii) LEGAL AGE TO FORM A BINDING CONTRACT; OR (C) ARE PROHIBITED FROM ACCESSING OR USING THE WEBSITE OR SERVICES OR ANY ASSOCIATED FUNCTIONALITIES BY APPLICABLE LAW. YOU REPRESENT TO US THAT YOU ARE: (1) NOT SUBJECT TO SANCTIONS OR OTHERWISE DESIGNATED ON ANY LIST OF PROHIBITED OR RESTRICTED PARTIES, INCLUDING BUT NOT LIMITED TO THE LISTS MAINTAINED BY THE UNITED NATIONS SECURITY COUNCIL, THE U.S. GOVERNMENT (_I.E._, THE SPECIALLY DESIGNATED NATIONALS LIST AND FOREIGN SANCTIONS EVADERS LIST OF THE U.S. DEPARTMENT OF TREASURY AND THE ENTITY LIST OF THE U.S. DEPARTMENT OF COMMERCE), THE EUROPEAN UNION OR ITS MEMBER STATES, THE UNITED KINGDOM, OR OTHER APPLICABLE GOVERNMENT AUTHORITY; AND (2) NOT LOCATED IN ANY COUNTRY SUBJECT TO A COMPREHENSIVE SANCTIONS PROGRAM IMPLEMENTED BY THE UNITED STATES. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-1.-acceptance-of-the-terms-of-use) 1\. Acceptance of the Terms of Use. ------------------------------------------------------------------------------------------------------------------------------------------ These Terms of Use are entered into by and between you ("you" or the "User") and the Liquiditea Corporation and its affiliates (_collectively_, "Sai," "we," "our," or "us"). The following Terms of Use, together with any documents they expressly incorporate by reference (_collectively_, this "Agreement" or these "Terms of Use"), govern Users' access to and use of our Services (as defined below), whether through our website, [https://app.sai.funarrow-up-right](https://app.sai.fun/) (the "Website") or the website of a third party, or through any associated software application (the "Interface"). The User must read the Terms of Use carefully before using the Website or Interface. By using the Website or Interface, the User accepts and agrees to be bound and abide by these Terms of Use and our Privacy Policy, incorporated herein by reference. If the User does not want to agree to these Terms of Use, the Privacy Policy, or any documents that are incorporated herein by reference, the User must not access the Website or use the Interface. The Website and Interface are offered and available to users who are eighteen (18) years of age or older. By using the Website or Interface, the User represents and warrants that the User is at least the higher of legal age to form a binding contract with Sai in the User's applicable jurisdiction or eighteen (18) years of age, and meets all of the eligibility requirements set forth herein. Further, by using the Website or Interface, the User represents and warrants that the User is not a citizen or resident of, nor is located in, any country against which the United States has sanctioned or embargoed or where the use of the Website or Interface is otherwise illegal or impermissible, whether by rule, statute, regulation, bylaw, court adjudication or order, protocol, administrative statement, code, decree, or other directive, requirement or guideline, whether applicable on Sai, the Website or Interface, or on the User (or multiple of the foregoing) by an authority with valid and enforceable jurisdiction ("Applicable Laws"). If the User does not meet all of these requirements, the User must not access or use the Website or Interface. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-2.-prohibited-conduct) 2\. Prohibited Conduct ----------------------------------------------------------------------------------------------------------------- The User may access or use the Website, the Interface, and the Services only for lawful purposes and in accordance with these Terms of Use. The User represents and warrants that the User agrees not to use or access the Website, the Interface, or the Services, including through the use of the User's Wallet or Sai Account: 1. In any way that violates any applicable federal, state, local, or international laws or regulations (including, without limitation, any applicable anti-money laundering, anti-proliferation and anti-terrorism financing laws or any laws regarding the export of data or software to and from certain countries pursuant to Applicable Laws). 2. In any manner, directly or indirectly, designed to cause or to result in, or that has constituted, or which might reasonably be expected to constitute, the unlawful stabilization or manipulation of the price of any digital assets on any of blockchain networks or other blockchains or any other digital assets, including but not limited to fungible digital assets or non-fungible tokens. 3. For the purpose of exploiting, harming, or attempting to exploit or harm minors in any way by exposing them to inappropriate content, asking for personally identifiable information, or otherwise. 4. To transmit, or procure the sending of, any advertising or promotional material, including any "junk mail," "chain letter," "spam," or any other similar solicitation. 5. To impersonate or attempt to impersonate Sai, anyone affiliated with Sai, another user, or any other person or entity (including, without limitation, by using email addresses, screen names, similarly named or commonly misspelled URLs, or associated blockchain identities). 6. To engage in any other conduct that restricts or inhibits anyone's use or enjoyment of the Website, the Interface, or the Services, or which, as determined by us, may harm Sai or Users, or expose them to liability. 7. If the User is a citizen of or otherwise accessing the Website, the Interface, the Wallet, or the Services from the nations of the United States of America, Canada, Cuba, Iran, North Korea, Syria, certain sanctioned areas of Ukraine (including without limitation, the regions of Crimea, Donetsk, and Luhansk), or other countries or geographic regions sanctioned by the United States Department of the Treasury (_collectively_, "Prohibited Jurisdictions"), or if the User is otherwise listed as a Specially Designated National by the United States Department of the Treasury's Office of Foreign Asset Control ("OFAC"). 8. If doing so is illegal or impermissible according to any Applicable Laws, including without limitation those promulgated by the United Nations Security Council, the United Kingdom, the United States (including those prohibiting dealings with sanctioned persons identified by the OFAC as Specially Designated Nationals and Blocked Persons ("SDN"), or other U.S. non-SDN restricted or prohibited parties lists, and those prohibiting dealings with persons organized, resident, or located in comprehensively sanctioned jurisdictions), and/or any other applicable national, provincial, federal, state, municipal or local laws and regulations (each as amended from time to time). 9. To cause the Website, the Interface, and the Services, any of their underlying blockchain networks or technologies, or any other functionality with which they interact to work other than as intended. 10. To damage the reputation of Sai or impair any of Sai's legal rights or interests. 11. To circumvent any filtering or geo-blocking, access control measures, security measures, or content filtering that Sai deploys on the Website, the Interface, or the Services, including, without limitation, by using a VPN. Additionally, the User agrees not to: 1. Be likely to deceive or defraud, or attempt to deceive or defraud, any person, including (without limitation) providing any false, inaccurate, or misleading information (whether directly through the Website, the Interface, or the Services or through an external means that affects the Website, the Interface, or the Services) with the intent to unlawfully obtain the property of another or to provide knowingly or recklessly false information, including in any way that causes inaccuracy among the content on the Website, the Interface, or the Services. 2. Use the Website, the Interface, or the Services to manipulate or defraud any exchange, oracle system, or blockchain network, or the users thereof. 3. Promote any illegal activity, or advocate, promote, or assist any unlawful act. 4. Cause annoyance, inconvenience, or needless anxiety or be likely to upset, embarrass, alarm, or annoy any other person. 5. Impersonate any person, misrepresent the User's identity, or misrepresent its affiliation with any person or organization. 6. Misuse Sai's intellectual property, name, or logo, including any trade or service marks, without express consent from Sai or in any manner that otherwise harms Sai, including any action that implies an untrue endorsement by or affiliation with Sai; 7. Engage in any activity or behavior that violates any Applicable Laws concerning, or otherwise damages, the integrity of the Website, the Interface, or the Services, or any other service or software which relies on the Interface or the Services. 8. Give the impression that they emanate from or are endorsed by us or any other person or entity if this is not the case as it relates to the Website, the Interface, or the Services. 9. Use the Website or the Interface in any manner that could disable, overburden, damage, impair, or interfere with any other party's use of the Website or the Interface, including the ability to engage in real time activities through the Website or the Interface or with the Services. 10. Use any robot, spider, or other automatic device, process, or means to access the Website, the Interface, or the Services for any purpose, including monitoring or copying any of the material on the Website or the Interface. 11. Use any manual process to monitor or copy any of the material on the Website or the Interface, or for any other purpose not expressly authorized in these Terms of Use, without our prior written consent. 12. Use any device, software, or routine that interferes with the proper working of the Website, the Interface, or the Services. 13. Introduce any viruses, Trojan horses, worms, logic bombs, or other material that is malicious or technologically harmful to the Website, the Interface, the Services, the Users, any underlying blockchain, or any of the Services' related utilities or functionalities. 14. Attempt to gain unauthorized access to, interfere with, damage, or disrupt any parts of the Website or the Interface, the server on which the either is stored, or any server, computer, or database connected to the Website or the Interface, including any underlying blockchain. 15. Violate the legal rights (including the rights of publicity and privacy) of others or contain any material that could give rise to any civil or criminal liability under applicable laws or regulations or that otherwise may be in conflict with these Terms of Use and our Privacy Policy. 16. Engage in any activity that would infringe or violate any copyright, trademark, right of publicity or privacy or any other proprietary right under Applicable Law, including but not limited to sales, distribution, or access to licensed materials without the appropriate authorization from the rights holder. 17. Attack the Website, the Interface, the Services, or any of the Services' underlying blockchain networks or technologies, or any other functionality with which the Services interact via a denial-of-service attack or a distributed denial-of-service attack. 18. Use the Website, the Interface, or the Services in any way that is, in our sole discretion, libelous, defamatory, profane, obscene, pornographic, sexually explicit, indecent, lewd, vulgar, suggestive, harassing, stalking, hateful, threatening, offensive, discriminatory, bigoted, abusive, inflammatory, fraudulent, deceptive, or otherwise objectionable or likely or intended to incite, threaten, facilitate, promote, or encourage hate, racial intolerance, or violent acts against others. 19. Use the Website, the Interface, or the Services to harass, abuse, or harm another person or entity, including anyone on the Sai team or its service providers. 20. Encourage or induce any third party to engage in any of the activities prohibited under these Terms. 21. Otherwise attempt to interfere with the proper working of the Website, the Interface, or the Services. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-3.-user-acknowledgements-required-to-access-the-website-the-interface-and-the-services) 3\. User Acknowledgements Required to Access the Website, the Interface, and the Services ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- As a condition to accessing or using the Website, the Interface, or the Services, you acknowledge, understand, and agree to the following: 1. The information provided in the Website, the Interface, and for the Services is for general informational purpose only and Sai reserves the right to update, modify, or amend any contents therein, at its sole discretion and without prior notice. Nothing herein should be used or considered as legal, financial, tax, or any other advice, nor as an instruction or invitation to act in any way by anyone; 2. Sai does not act as an agent for you or any other user of the Website, the Interface, or the Services; 3. You are solely responsible for your use of and access to the Website, the Interface, and the Services, including, without limitation, all of your use of, transfers and receipts of, and all other transactions involving digital assets; 4. to the fullest not prohibited by Applicable Laws, We owe no fiduciary duties or liabilities to you or any other party, and that to the extent any such duties or liabilities may exist at law or in equity, you hereby irrevocably disclaim, waive, and eliminate those duties and liabilities; 5. you are solely responsible for reporting and paying any taxes incurred in connection with your use of the Website, the Interface, and the Services; and 6. we have no control over, or liability for, the delivery, quality, safety, legality, or any other aspect of any digital assets that you may transfer to or from a third party, and we are not responsible for ensuring that an entity with whom you transact completes the transaction or is authorized to do so (or repays any amounts it may be required to repay to you), and if you experience a problem with any transactions in digital assets using the Website, the Interface, or the Services, then you bear the entire risk. ### [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-4.-user-covenants) 4\. User Covenants As a condition to accessing or using the Website, the Interface, or the Services, you covenant to Sai the following: * In connection with interacting with the Website, the Interface, or the Services, you will only engage with digital assets that belong to you; * You will obey all Applicable Laws in connection with using the Website, the Interface, and the Services, and you will not use the Website, the Interface, or the Services if the laws of your country, or any other Applicable Law, prohibit you from doing so; and * In addition to complying with all restrictions, prohibitions, and other provisions of these Terms, you will (i) ensure that, at all times, all information that you provide to us during your use of the Website, the Interface, or the Services is current, complete, and accurate; (ii) maintain the security and confidentiality of your private keys associated with your digital assets, passwords, API keys and other related credentials(iii) not sell NIBI Tokens to persons from Prohibited Jurisdictions; and (v) if you are a U.S. Person, not to seek to acquire NIBI Tokens using the Website, the Interface, or the Services. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-5.-services-the-interface-blockchain-fees) 5\. Services; The Interface; Blockchain Fees. ------------------------------------------------------------------------------------------------------------------------------------------------------------ We provide software that allows users to connect their digital asset wallet to our Website or Interface. As a result, the connection with your wallet relies on third-party services. YOU AGREE TO ASSUME ALL RISK RELATED TO ANY BUGS, ERRORS, VULNERABILITIES, OR EXPLOITS THAT MAY EXIST IN THE SERVICES AND ANY THIRD-PARTY SERVICES (AS DEFINED BELOW), WHETHER FORESEEABLE OR UNFORESEEABLE. YOU ACKNOWLEDGE AND AGREE THAT YOUR USE OR INTERACTION WITH THE WEBSITE, INTERFACE, OR SERVICES IS AT YOUR OWN RISK AND SAI WAIVES ALL LIABILITY OR RESPONSIBILITY AND MAKES NO WARRANTIES RELATED TO THE SERVICES. To use the Services, you will have to create an account (a "Sai Account") via the Website or Interface. You agree that you will not disclose your Sai Account access credentials, including any usernames and passwords associated with your Sai Account, to anyone or otherwise provide anyone else with access to your Sai Account credentials. You are solely responsible for all activities that occur under your Sai Account, or are otherwise referable to your Sai Account credentials, whether or not you know about such activities. Once you create your Sai Account, you will be permitted to connect your digital asset wallet via the supported blockchain networks. You acknowledge and understand that, in certain circumstances, such as if you lose or are otherwise unable to access your login credentials associated with your Sai Account, you may be unable to access the Website, the Interface, or the Services, and to engage in any of the features allowed by those platforms. Your full use and enjoyment of the Website, the Interface, or the Services may require you to pay transactional fees, or other types of fees, both to Sai and possibly as may be required by the underlying blockchain or distributed ledger service or that are used to encourage intended use among the underlying blockchain's participants ("Blockchain Fees"). While certain fees may be set and levied by Sai, including for transactions and activities such as opening and closing a position, or a funding rate associated with a transaction, certain Blockchain Fees are not levied directly by Sai, but rather are determined by your use of the Services and the rules placed by corresponding blockchain communities at large. You acknowledge that Sai has no control over Blockchain Fees, (including without limitation their applicability, payment, amounts, transmission, intended operation, and effectiveness) whether related to your use of the Services or otherwise, and agree that in no event will Sai be responsible to you or any other party for the payment, repayment, refund, disbursement, indemnity, or for any other aspect of your use or transmission of Blockchain Fees. For further information regarding blockchain technology, digital assets, and the associated risks, _see_ the Section below entitled **Nature of Blockchain; Assumption of Risk; Waiver of Claims**. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-6.-monitoring-and-enforcement-termination) 6\. Monitoring & Enforcement; Termination -------------------------------------------------------------------------------------------------------------------------------------------------------- We have the right to: · Take appropriate legal action, including without limitation, referral to law enforcement, for any illegal or unauthorized use of the Website, the Interface, or the Services. · Terminate, prevent, or suspend your access to all or part of the Website, the Interface, or the Services for any or no reason, including without limitation, any violation of these Terms of Use. Without limiting the foregoing, we have the right to cooperate fully with any law enforcement authorities or court order requesting or directing us to disclose the identity or other information of anyone posting any materials on or through the Website, the Interface, or the Services. YOU WAIVE AND HOLD HARMLESS SAI AND ITS AFFILIATES, LICENSEES, AND SERVICE PROVIDERS FROM ANY CLAIMS RESULTING FROM ANY ACTION TAKEN BY SAI/ANY OF THE FOREGOING PARTIES DURING, OR TAKEN AS A CONSEQUENCE OF, INVESTIGATIONS BY EITHER SUCH PARTIES OR LAW ENFORCEMENT AUTHORITIES. However, we cannot review interactions or activities before they are executed through the Interface, and, given the nature of blockchain and functionalities like those offered via the Services, cannot ensure prompt removal or rectification of objectionable interactions or activities after they have been executed. Accordingly, the User agrees that we assume no liability for any action or inaction regarding transmissions, communications, transactions, blockchain operations, or content provided by any User or third party, including any that may cause a malfunction or inaccuracy on the Website or among the Services. We have no liability or responsibility to anyone for any other party's performance or nonperformance of the activities described in this Section, nor for any harms or damages created by others' interactions with any blockchain underlying the Services or reliance on the information or content presented on the Website. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-7.-changes-to-the-terms-of-use) 7\. Changes to the Terms of Use ----------------------------------------------------------------------------------------------------------------------------------- We may revise and update these Terms of Use from time to time in our sole discretion. All changes are effective immediately when we post them and apply to all access to and use of the Website thereafter. However, any changes to the dispute resolution provisions set out in the Section below entitled Governing Law & Jurisdiction will not apply to any disputes for which the parties have actual notice before the date the change is posted on the Website or the Interface. The User's continued use of the Website, the Interface, or the Services following the posting of revised Terms of Use means that the User accepts and agrees to the changes. **The User is expected to check this page each time it accesses this Website, the Interface, or the Services so it is aware of any changes, as they are binding on the User**. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-8.-accessing-the-website-or-the-interface-and-user-security) 8\. Accessing the Website or the Interface & User Security. -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- We reserve the right to withdraw or amend the Website or the Interface, and any other Services or material we provide on the Website or the Interface, in our sole discretion without notice. We will not be liable if for any reason all or any part of the Website, the Interface, the public blockchains underlying the Services, or any of the Services are unavailable at any time or for any period. From time to time, we may restrict access to some parts of or all of the Website or the Interface to Users. The User is responsible for both: 1. Making all arrangements necessary for the User to have access to the Website, the Interface, and the Services. 2. Ensuring that all persons who access the Website, the Interface, or the Services through the User's internet connection are aware of these Terms of Use and comply with them. To access certain Services or some of the resources offered on the Website or the Interface, the User may be asked to provide certain registration details or other information. Other Services or resources offered on the Website or Interface may require the User to utilize certain Web3 capabilities, such a digital asset wallet capable of interacting with the User's web browser or relevant blockchain nodes ("Web3 Utilities"). It is a condition of the User's use of the Website, the Interface, and the Services that the User only operate such Web3 Utilities with a private key(s) that the User created or has the direct, explicit permission of the party who created the relevant private key(s). The User agrees that all information it provides to interact with the Website, Interface, Services, or otherwise, including, but not limited to, through the use of any interactive features on the Website (such as registration information) is correct, current, and complete, and is governed by our Privacy Policy. The User consents to all actions we take with respect to the User's information as is consistent with our Privacy Policy. If the User utilizes a Web3 Utility that relies on a separate username, password, private key, or any other piece of information as part of its security procedures, the User must treat such information as confidential, and the User must not disclose that information to any other person or entity. The User also acknowledges that any identity linked to its Web3 Utility is personal to the User and agrees not to provide any other person with access to such identity. The User also agrees to ensure that it will lock or otherwise prevent its Web3 Utility from unauthorized use on the Website, the Interface, or the Services at the end of each session. The User should use particular caution when accessing the Website, the Interface, or the Services from a public or shared computer so that others are not able to view or record the User's username, password, private key, or other personal information. For further information regarding Magic Keys, Safe Words, and Wallet security, _see_ the Section below entitled Nature of Blockchain; Assumption of Risk; Waiver of Claims. We have the right to disable the User's access, including those associated with a Web3 Utility (such as that represented by a public address), to the Website, the Interface, or the Services, or to block any IP address from accessing the Website, the Interface, or the Services at any time in our sole discretion for any or no reason, including if, in our opinion, the User or that identity has violated any provision of these Terms of Use. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-9.-intellectual-property-rights) 9\. Intellectual Property Rights. -------------------------------------------------------------------------------------------------------------------------------------- Except any open-source software, or other material incorporated in the Website, the Interface, or the Services, the Website and the Interface and their entire contents, features, and functionality (including but not limited to all information, software, text, displays, images, video, and audio, and the design, selection, and arrangement thereof) are owned by Sai, its licensors, or other providers of such material and are protected by United States and international copyright, trademark, patent, trade secret, and other intellectual property or proprietary rights laws. The User must not reproduce, distribute, modify, create derivative works of, publicly display, publicly perform, republish, download, store, or transmit any of the material on the Website or the Interface, except as follows: 1. The User's computer may temporarily store copies of such materials in RAM incidental to the User's accessing and viewing those materials. 2. The User may store files that are automatically cached by the User's web browser for display enhancement purposes. 3. The User may print or download copies of a reasonable number of pages of the Website or the Interface for its own personal, non-commercial use and not for further reproduction, publication, or distribution. 4. If we provide desktop, mobile, or other applications for download, the User may download a single copy to its computer or mobile device, provided the User agrees to be bound by any applicable end user license agreement or other similar agreement for such applications. 5. For any open-source materials provided on the Website, the Interface, or through the Services, the User may perform any activities only as is consistent with the open-source license applicable to such materials. The User must not: 1. Modify copies of any materials from the Website or the Interface. 2. Use any illustrations, photographs, video or audio sequences, or any graphics separately from the accompanying text. 3. Delete or alter any copyright, trademark, or other proprietary rights notices from copies of materials from the Website or the Interface. If the User wishes to make any use of material on the Website or the Interface other than that set out in this Section, it should address its request to: sai { at } nibiru.org. If the User prints, copies, modifies, downloads, or otherwise uses or provides any other person with access to any part of the Website or the Interface in breach of these Terms of Use, the User's right to access the Website and the Interface will stop immediately and the User must, at our option, return or destroy any copies of the materials the User has made. No right, title, or interest in or to the Website, the Interface, or any content on either is transferred to the User, and all rights not expressly granted are reserved by Sai. Notwithstanding anything to the contrary in these Terms of Use, the User may freely use any open-sourced materials up to the limits provided, but in accordance with any requirements placed, by those materials' or properties' applicable licenses. Any use of the Website not expressly permitted by these Terms of Use is a breach of these Terms of Use and may violate copyright, trademark, and other laws. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-10.-trademarks) 10\. Trademarks --------------------------------------------------------------------------------------------------- The Sai name and all related names, logos, product and service names, designs, and slogans are trademarks of Sai or its affiliates or licensors. You must not use such marks without the prior written permission of Sai; provided, however, User is hereby granted a limited, revocable, non-transferable permission and license to use the term "Sai" and any related names (excluding the Sai name), logos (excluding the Company logo), product and service names, designs, and slogans in any way that they desire so long as such usage is not done in a way that: (1) is deceitful, fraudulent, or manipulative; (2) implies any relationship between User and Sai beyond that reasonably typical of the administrator of a website and its users; or (3) to cause confusion in any way to gain digital assets of, or personal information about, another party other than that intended by the Services, the Wallet, the Interface or any related or interacting functionality (for example, you may not use the foregoing marks to execute phishing attacks, spearphishing attacks, social engineering, or in any way that may cause a party to transmit digital assets to an unintended recipient or to reveal private information, like a private key or password). All other names, logos, product and service names, designs, and slogans on the Website and the Interface are the trademarks of their respective owners, if applicable. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-11.-reliance-on-information-posted) 11\. Reliance on Information Posted ------------------------------------------------------------------------------------------------------------------------------------------- The information presented on or through the Website and the Interface is made available solely for general information and education purposes. We do not warrant the accuracy, completeness, or usefulness of this information. Any information posted to the Website, the Interface, or through the Services should not be construed as an intention to form a contract, and in no case should any information be construed as Sai's offer to buy, sell, transact with, or exchange digital assets. Any reliance the User places on such information is strictly at the User's own risk, and as is common in the blockchain space, the User is assuming a high amount of risk related to others or technical harms when operating via the Website, the Interface, and the Services. We disclaim all liability and responsibility arising from any reliance placed on such materials by the User or any other User, by anyone who may be informed of any of the Website's or the Services' contents, or by the actions or omissions of others interacting with the Wallet or any underlying blockchain. The Website, the Interface, and the Services may include content provided by third parties, including without limitation materials provided by other Users, bloggers, and third-party licensors, syndicators, blockchain users, decentralized applications, aggregators, and/or reporting services. All statements, alleged facts, and/or opinions expressed in these materials, and all articles and responses to questions and other content, other than the content provided by Sai, are solely the opinions and the responsibility of the person or entity providing those materials. These materials do not necessarily reflect the opinion of Sai or even the factual status of reality. We are not responsible, or liable to the User or any third party, for the content or accuracy of any materials provided by any third parties, and User agrees that it bears sole and absolute responsibility to evaluate and select any third-party functionality with which it interacts via the Services. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-12.-changes-to-the-website-the-interface-and-the-services) 12\. Changes to the Website, the Interface, and the Services ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- We may update the content on, design of, or functionalities available through the Website or the Interface, or through the Services from time to time, but the Website, the Interface, and the Services are not necessarily complete or up-to-date. Any of the material on the Website or the Interface, or provided through the Services may be out of date at any given time, and we are under no obligation to update such material. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-13.-information-about-the-user-and-the-users-visits-to-the-website-and-the-interface) 13\. Information About the User & The User's Visits to the Website and the Interface ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- All information we collect on the Website or through the Interface or the Services is subject to our Privacy Policy. By using the Website, the Interface, and the Services, the User consents to all actions taken by us with respect to the User's information in compliance with the Privacy Policy. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-14.-warranty-disclaimer) 14\. WARRANTY DISCLAIMER --------------------------------------------------------------------------------------------------------------------- Sai is a developer of software and does not unilaterally offer, operate, or administer any blockchain networks, digital assets, or Dapps. The Services merely attempt to assist Users in more easily participating in blockchain networks generally. Nonetheless, Sai has no oversight on or control over any particular digital asset, Dapp, or blockchain network. The User is responsible for its use of the Services, the functionalities they enable, transactions engaged through the Website or the Interface, and the use of the information derived thereof. The User is solely responsible for complying with all Applicable Laws related to its transactions and activities that directly or indirectly incorporate our provision of the Services. The User acknowledges its understanding that Sai is not registered or licensed with, nor have our Website, Interface, or Services (or the software contained therein) been reviewed by, any financial or banking regulator. Sai may link to or offer "Third-Party Services" on the Website or otherwise through the Services, including without limitation, Dapps, Passkeys, private keys or "Sign In with Apple" and "Google Sign-In" Access Credential services. Any purchase, enabling, or engagement of Third-Party Services, including but not limited to implementation, customization, consulting services, and any exchange of data between the User and any Third-Party Service, is solely between you and the applicable Third-Party Service provider and is subject to the terms and conditions of such Third-Party Service provider. Sai does not warrant, endorse or support Third-Party Services and is not responsible or liable for such Services or any losses or issues that result from the User's use of such services. If the User purchases, enables or engages any Third-Party Service for use in connection with the Services, the User acknowledges that Sai may allow providers of those Third-Party Services to access your data used in connection with the Services as required for the interoperation of such Third-Party Services with the Services. The User represents and warrants that use of any Third-Party Service signifies independent consent to the access and use of your data by the Third-Party Service provider, and that such consent, use, and access is outside of Sai's control. Sai will not be responsible or liable for any disclosure, modification or deletion of data resulting from any such access by Third-Party Service providers. The User understands that we cannot and do not guarantee or warrant that files available for download from the Website, the Interface, or through the Services will be free of viruses or other destructive code. The User is responsible for implementing sufficient procedures and checkpoints to satisfy the User's particular requirements for: (1) an appropriate Web3 Utility; (2) anti-virus protection and accuracy of data input and output; (3) its participation in and use of the Wallet and any of the Services' underlying blockchain and related technologies; and (4) maintaining a means external to our site to reconstruct any lost data. TO THE FULLEST EXTENT PROVIDED BY LAW, WE WILL NOT BE LIABLE FOR ANY LOSS OR DAMAGE CAUSED BY A DISTRIBUTED DENIAL-OF-SERVICE ATTACK, MAN-IN-THE-MIDDLE ATTACK, VIRUSES, OR OTHER TECHNOLOGICALLY HARMFUL MATERIAL THAT MAY INFECT THE USER'S COMPUTER EQUIPMENT, COMPUTER PROGRAMS, DATA, OR OTHER PROPRIETARY MATERIAL DUE TO THE USER'S USE OF THE WEBSITE, THE INTERFACE, OR ANY SERVICES OR ITEMS OBTAINED THROUGH THE WEBSITE OR THE INTERFACE, OR DUE TO THE USER'S DOWNLOADING OF ANY MATERIAL POSTED ON IT, OR ON ANY WEBSITE LINKED TO IT. THE USER'S USE OF THE WEBSITE AND THE INTERFACE, THEIR CONTENT, AND ANY OF THE SERVICES IS AT THE USER'S SOLE RISK. THE WEBSITE, THE INTERFACE, AND THE SERVICES ARE PROVIDED ON AN "AS IS'' AND "AS AVAILABLE" BASIS. TO THE FULLEST EXTENT LEGALLY PERMISSIBLE, WE, NOR ANY PERSON ASSOCIATED WITH SAI, MAKE, AND WE EXPLICITLY DISCLAIM, ANY AND ALL REPRESENTATIONS OR WARRANTIES OF ANY KIND RELATED TO THE WEBSITE, THE INTERFACE, AND THE SERVICES, WHETHER EXPRESS, IMPLIED, OR STATUTORY, INCLUDING (WITHOUT LIMITATION) THE WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE. NEITHER SAINOR ANY PERSON ASSOCIATED WITH SAIMAKES ANY WARRANTY OR REPRESENTATION WITH RESPECT TO THE COMPLETENESS, SECURITY, RELIABILITY, QUALITY, ACCURACY, OR AVAILABILITY OF THE WEBSITE, THE INTERFACE, OR THE SERVICES. SAIAND ANY PERSON ASSOCIATED WITH SAI NOT REPRESENT OR WARRANT THAT: (1) ACCESS TO THE WEBSITE, THE INTERFACE, OR THE SERVICES WILL BE CONTINUOUS, UNINTERRUPTED, TIMELY, WITHOUT DELAY, ERROR-FREE, SECURE, OR FREE FROM DEFECTS; (2) THAT THE INFORMATION CONTAINED OR PRESENTED ON THE WEBSITE OR VIA THE SERVICES IS ACCURATE, RELIABLE, COMPLETE, CONCISE, CURRENT, OR RELEVANT; (3) THAT THE WEBSITE, THE INTERFACE, THE SERVICES, OR ANY SOFTWARE CONTAINED THEREIN WILL BE FREE FROM DEFECTS, MALICIOUS SOFTWARE, ERRORS, OR ANY OTHER HARMFUL ELEMENTS, OR THAT ANY OF SUCH WILL BE CORRECTED; OR (4) THAT THE WEBSITE, THE INTERFACE, OR THE SERVICES WILL MEET THE USER'S EXPECTATIONS. NO INFORMATION OR STATEMENT THAT WE MAKE, INCLUDING DOCUMENTATION OR OUR PRIVATE COMMUNICATIONS, SHOULD BE TREATED AS OFFERING ANY WARRANTY CONCERNING THE WEBSITE, THE INTERFACE, OR THE SERVICES. WE DO NOT ENDORSE, GUARANTEE, OR ASSUME ANY LIABILITY OR RESPONSIBILITY FOR ANY CONTENT, ADVERTISEMENTS, OFFERS, STATEMENTS, OR ACTIONS BY ANY THIRD PARTY EITHER REGARDING THE WEBSITE OR THE SERVICES. THE FOREGOING DOES NOT AFFECT ANY WARRANTIES THAT CANNOT BE EXCLUDED OR LIMITED UNDER APPLICABLE LAW. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-15.-limitation-of-liability) 15\. LIMITATION OF LIABILITY ----------------------------------------------------------------------------------------------------------------------------- TO THE FULLESTEXTENT PROVIDED BY LAW, IN NO EVENT WILL SAI, ITS AFFILIATES, OR THEIR LICENSORS, SERVICE PROVIDERS, EMPLOYEES, AGENTS, OFFICERS, OR DIRECTORS BE LIABLE FOR DAMAGES OF ANY KIND, UNDER ANY LEGAL THEORY, ARISING OUT OF OR IN CONNECTION WITH THE USER'S USE, OR INABILITY TO USE, THE WEBSITE, THE INTERFACE, THE SERVICES, ANY WEBSITES LINKED TO IT, ANY CONTENT ON THE WEBSITE OR SUCH OTHER WEBSITES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES, INCLUDING BUT NOT LIMITED TO, PERSONAL INJURY, PAIN AND SUFFERING, EMOTIONAL DISTRESS, LOSS OF REVENUE, LOSS OF PROFITS, LOSS OF BUSINESS OR ANTICIPATED SAVINGS, LOSS OF USE, LOSS OF GOODWILL, LOSS OF DATA, AND WHETHER CAUSED BY TORT (INCLUDING NEGLIGENCE), BREACH OF CONTRACT, OR OTHERWISE, EVEN IF FORESEEABLE. THIS DISCLAIMER OF LIABILITY EXTENDS TO ANY AND ALL DAMAGES CAUSED BY ANY THIRD PARTY (INCLUDING, WITHOUT LIMITATION, THOSE CAUSED BY FRAUD, DECEIT, OR MANIPULATION), WHETHER OR NOT A USER, OR ANY FAILURE, EXPLOIT, OR VULNERABILITY OF THE WEBSITE, SERVICES, THE APIS, THE USER'S WEB3 UTILITIES, OR THE UNDERLYING BLOCKCHAINS OR RELATED BLOCKCHAIN FUNCTIONALITIES. TO THE FULLEST EXTENT PROVIDED BY LAW, IN NO EVENT WILL THE COLLECTIVE LIABILITY OF SAI AND ITS SUBSIDIARIES AND AFFILIATES, AND THEIR LICENSORS, SERVICE PROVIDERS, EMPLOYEES, AGENTS, OFFICERS, AND DIRECTORS, TO ANY PARTY (REGARDLESS OF THE FORM OF ACTION, WHETHER IN CONTRACT, TORT, OR OTHERWISE) EXCEED THE GREATER OF $100 OR THE AMOUNT YOU HAVE PAID DIRECTLY TO SAI (NOT INCLUDING DIGITAL ASSET DEPOSITS INTO YOUR NON-CUSTODIAL WALLET OR SAI ACCOUNT) FOR THE APPLICABLE CONTENT OR SERVICES IN THE LAST SIX MONTHS OUT OF WHICH LIABILITY AROSE. THE FOREGOING DOES NOT AFFECT ANY LIABILITY THAT CANNOT BE EXCLUDED OR LIMITED UNDER APPLICABLE LAW. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-16.-nature-of-blockchain-assumption-of-risk-waiver-of-claims) 16\. Nature of Blockchain; Assumption of Risk; Waiver of Claims ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Blockchains, digital assets, the Wallet, Smart Accounts, Dapps, Web3 Utilities, and their related technologies and functionalities are still emerging innovations that carry a relatively high amount of foreseeable and unforeseeable risk from security, financial, technical, political, social, and personal safety standpoints. The mere access to and interaction with blockchains requires high degrees of skill and knowledge to operate with a relative degree of safety and proficiency. digital assets are highly volatile in nature due to many diverse factors, including without limitation use and adoption, speculation, manipulation, technology, security, and legal and regulatory developments and application. Further, the speed and cost of transacting with cryptographic technologies, such as blockchains like those underlying the Wallet, is variable and highly volatile. Moreover, the transparent nature of many blockchains means that any interactions the User has with any blockchain may be publicly visible and readable in human form. By accessing and using the Website, the Interface, or the Services, the User acknowledges the foregoing, and agrees and represents that it understands such and other risks involved with blockchains, the Wallet, and related technologies (including without limitation any specific technical language used in this Agreement). The User further represents that it has all knowledge sufficient to work, and is informed of all foreseeable risks, and the possibility of unforeseeable risks, associated with blockchains, digital assets, Web3 Utilities, smart contracts, the Interface, the Wallet, and the Services. The User further acknowledges, and assumes all risk related to the possibility, that any information presented via the Website, Interface, or Services may be inaccurate, possibly due to another party's malicious activities and possibly to the User's severe harm or detriment. The User agrees that we are not responsible for any of these or related risks, do not own or control any blockchain and cannot guarantee the safe or accurate functioning of the Services, and shall not be held liable for any resulting harms, damages, or losses incurred by or against the User experiences while accessing or using the Website or the Services. Accordingly, the User acknowledges the foregoing, represents its understanding of the foregoing, and agrees to assume full responsibility for all of the risks of accessing and using the Website and interacting with the Services, whether mentioned in this Section or otherwise. The User further expressly waives and releases us from any and all liability, claims, causes of action, or damages arising from or in any way relating to the User's use of the Website and the Interface, and the User's interaction with the Services. If the User is a California resident, the User expressly and explicitly waives the benefits and protections of California Civil Code § 1542, which states: "\[a\] general release does not extend to claims that the creditor or releasing party does not know or suspect to exist in his or her favor at the time of executing the release and that, if known by him or her, would have materially affected his or her settlement with the debtor or released party." [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-17.-no-professional-advice) 17\. No Professional Advice --------------------------------------------------------------------------------------------------------------------------- All information or content provided or displayed by the Website (including without limitation, on the Interface) is for informational purposes only and should not be construed as professional advice (including, without limitation, tax, legal, or financial advice). The User should not take, or refrain from taking, any action based on any information or content displayed or provided on the Website, on the Interface, or through the Services. The User should seek independent professional advice from an individual licensed and qualified in the area appropriate for such before the User makes any financial, legal, or other decisions where such is considered prudent. The User acknowledges and agrees that, to the fullest extent permissible by law, it has not relied on Sai, the content on the Website, the Interface, or the Services for any professional advice related to its financial or legal behaviors. Additionally, it is your sole responsibility to determine whether, and to what extent, any taxes apply to any transactions you conduct through the Website, the Interface, the Wallet and/or the Services, and to withhold, collect, report, and remit the correct amounts of taxes to the appropriate tax authorities. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-18.-no-fiduciary-duties) 18\. No Fiduciary Duties --------------------------------------------------------------------------------------------------------------------- These Terms of Use, and the provision of the Website, the Interface, and the Services, are not intended to create any fiduciary duties between us and the User or any third party. To the fullest extent permissible by law, the User agrees that neither the User's use of the Website or the Interface, or of the Services causes us or any User to owe fiduciary duties or liabilities to the User or any third party. Further, the User acknowledges and agrees to the fullest extent such duties or liabilities are afforded by law or by equity, those duties and liabilities are hereby irrevocably disclaimed, waived, and eliminated, and that we and any other User shall be held completely harmless in relation thereof. The User further agrees that the only duties and obligations that we or any User owes the User, and the only rights the User has related to this Agreement or the User's use of the Website, the Interface, or the Services, are those set out expressly in this Agreement or that cannot be waived by law. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-19.-no-insurance) 19\. No Insurance ------------------------------------------------------------------------------------------------------- Your digital asset wallet and digital asset balances contained therein are not checking or savings accounts, and we do not provide any kind of insurance to you against any type of loss, including (without limitation) losses due to decrease in value of assets, assets lost due to a cybersecurity failure, or from your or other individuals' errors or malfeasance. In most jurisdictions digital assets are not legal tender, and most digital assets are not backed by any government. Your digital asset balances are not covered by Federal Deposit Insurance Corporation ("FDIC") or Securities Investor Protection Corporation ("SIPC") protections. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-20.-links-from-the-website-or-the-interface) 20\. Links from the Website or the Interface. -------------------------------------------------------------------------------------------------------------------------------------------------------------- The Website or the Interface may contain links to other sites and resources provided by third parties; these links are provided for convenience only. This includes links contained in advertisements, including banner advertisements and sponsored links. We have no control over the contents of those sites or resources, and the User acknowledges and agrees that we do not and will not accept any responsibility for them or for any loss or damage that may arise from the User's use of them. If the User decides to access any of the third-party websites linked to the Website, the User does so entirely at its own risk and subject to the terms and conditions of use for such websites. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-21.-indemnification) 21\. Indemnification ------------------------------------------------------------------------------------------------------------- The User agrees to defend, indemnify, and hold harmless Sai, its affiliates, licensors, and service providers, and its and their respective officers, directors, employees, contractors, agents, licensors, suppliers, successors, and assigns from and against any claims, liabilities, damages, judgments, awards, losses, costs, expenses, or fees (including reasonable attorneys' fees) arising out of or relating to: (1) the User's violation of these Terms of Use; (2) the User's use of the Website, the Interface, or the Services, including but not limited to the User's interactions with the Interface or other features which incorporate the Services, use of or reliance on the content on the Website or the Interface, services, and products other than as expressly authorized in these Terms of Use; (3) the User's use or reliance on of any information obtained from the Website or the Interface; or (4) any other party's access and use of the Website, the Interface, or the Services with the User's assistance or by using any device or account that the User owns or control. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-22.-governing-law-and-jurisdiction) 22\. Governing Law & Jurisdiction ----------------------------------------------------------------------------------------------------------------------------------------- All matters relating to the Website, the Interface, the Services, and these Terms of Use, and any dispute or claim arising therefrom or related thereto (in each case, including non-contractual disputes or claims), shall be governed by and construed in accordance with the internal laws of Panama without giving effect to any choice or conflict of law provision or rule (whether of Panama or any other jurisdiction). [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-23.-arbitration-class-arbitration-waiver) 23\. Arbitration; Class Arbitration Waiver -------------------------------------------------------------------------------------------------------------------------------------------------------- In the event of a dispute between you, the User, and Sai (each a "Party" and _collectively_, the "Parties") related to these Terms of Use or the breach thereof, the Parties shall participate in at least one (1) live or teleconferenced (_i.e._, using Zoom or a similar videoconferencing software that allows the Parties to communicate in real time) mediation session with the International Center for Dispute Resolution ("ICDR") neutral. The Parties agree to participate in mediation in good faith and the Parties agree to share equally in the cost of such mediation. Should the Dispute not be settled within seven (7) days following the live mediation session, either Party may then commence a binding arbitration administered in Panama, or if the ICDR is unable to administer the arbitration in Panama, then the nearest jurisdiction where it can do so. A single arbitrator shall preside, and proceedings shall be conducted remotely to the maximum extent possible. Each Party shall pay its own expenses in such arbitration, including its attorneys' fees, subject to reapportionment by the arbitrator in a final award. The language of the arbitration shall be English. The prevailing Party shall be entitled to recover reasonable attorneys' fees and other costs incurred in such proceeding in addition to any other relief to which it may be entitled. Any interim or provisional relief that would be available from a court of law shall be available in accordance with the rules of Panama, however, nothing in this Agreement shall preclude the Parties from obtaining preliminary injunctive relief in a court of competent jurisdiction located in Panama or another mutually acceptable jurisdiction if necessary to prevent irreparable harm pending the conclusion of any arbitration. The final arbitration award may be confirmed in a court located in the Panama and the Parties agree to waive any claim of improper venue or _forum non conveniens_. Except as may be required by law, neither a Party nor an arbitrator may disclose the existence, content, or results of any arbitration hereunder without the prior written consent of both parties. The Parties agree to arbitrate solely on an individual basis, and that these Terms of Use do not permit class arbitration or any claims brought as a plaintiff or class member in any class or representative arbitration proceeding. The arbitral tribunal may not consolidate more than one person's claims and may not otherwise preside over any form of a representative or class proceeding. In the event the prohibition on class arbitration is deemed invalid or unenforceable, then the remaining portions of the arbitration agreement will remain in force. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-24.-limitation-on-time-to-file-claims) 24\. Limitation on Time to File Claims ------------------------------------------------------------------------------------------------------------------------------------------------- ANY CAUSE OF ACTION OR CLAIM EITHER SAI OR THE USER MAY HAVE ARISING OUT OF OR RELATING TO THESE TERMS OF USE OR ITS USE OF THE WEBSITE, THE INTERFACE, OR ANY OF THE SERVICES, MUST BE COMMENCED WITHIN SIX (6) MONTHS AFTER THE CAUSE OF ACTION ACCRUES; OTHERWISE, SUCH CAUSE OF ACTION OR CLAIM IS PERMANENTLY BARRED. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-25.-waiver-and-severability) 25\. Waiver & Severability --------------------------------------------------------------------------------------------------------------------------- No waiver by Sai of any term or condition set out in these Terms of Use shall be deemed a further or continuing waiver of such term or condition or a waiver of any other term or condition, and any failure of Sai to assert a right or provision under these Terms of Use shall not constitute a waiver of such right or provision. If any provision of these Terms of Use is held by a court or other tribunal of competent jurisdiction to be invalid, illegal, or unenforceable for any reason, such provision shall be eliminated or limited to the minimum extent such that the remaining provisions of the Terms of Use will continue in full force and effect. [hashtag](https://docs.sai.fun/resources/legal/terms-of-use#id-26.-entire-agreement) 26\. Entire Agreement --------------------------------------------------------------------------------------------------------------- The Terms of Use, the Privacy Policy, and any other document incorporated by reference herein constitute the sole and entire agreement between the User and Sai regarding the Website and supersede all prior and contemporaneous understandings, agreements, representations, and warranties, both written and oral, regarding the Website. [PreviousLegalchevron-left](https://docs.sai.fun/resources/legal) [NextSai Disclosureschevron-right](https://docs.sai.fun/resources/legal/disclosures) Last updated 14 days ago * [1\. Acceptance of the Terms of Use.](https://docs.sai.fun/resources/legal/terms-of-use#id-1.-acceptance-of-the-terms-of-use) * [2\. Prohibited Conduct](https://docs.sai.fun/resources/legal/terms-of-use#id-2.-prohibited-conduct) * [3\. User Acknowledgements Required to Access the Website, the Interface, and the Services](https://docs.sai.fun/resources/legal/terms-of-use#id-3.-user-acknowledgements-required-to-access-the-website-the-interface-and-the-services) * [4\. User Covenants](https://docs.sai.fun/resources/legal/terms-of-use#id-4.-user-covenants) * [5\. Services; The Interface; Blockchain Fees.](https://docs.sai.fun/resources/legal/terms-of-use#id-5.-services-the-interface-blockchain-fees) * [6\. Monitoring & Enforcement; Termination](https://docs.sai.fun/resources/legal/terms-of-use#id-6.-monitoring-and-enforcement-termination) * [7\. Changes to the Terms of Use](https://docs.sai.fun/resources/legal/terms-of-use#id-7.-changes-to-the-terms-of-use) * [8\. Accessing the Website or the Interface & User Security.](https://docs.sai.fun/resources/legal/terms-of-use#id-8.-accessing-the-website-or-the-interface-and-user-security) * [9\. Intellectual Property Rights.](https://docs.sai.fun/resources/legal/terms-of-use#id-9.-intellectual-property-rights) * [10\. Trademarks](https://docs.sai.fun/resources/legal/terms-of-use#id-10.-trademarks) * [11\. Reliance on Information Posted](https://docs.sai.fun/resources/legal/terms-of-use#id-11.-reliance-on-information-posted) * [12\. Changes to the Website, the Interface, and the Services](https://docs.sai.fun/resources/legal/terms-of-use#id-12.-changes-to-the-website-the-interface-and-the-services) * [13\. Information About the User & The User's Visits to the Website and the Interface](https://docs.sai.fun/resources/legal/terms-of-use#id-13.-information-about-the-user-and-the-users-visits-to-the-website-and-the-interface) * [14\. WARRANTY DISCLAIMER](https://docs.sai.fun/resources/legal/terms-of-use#id-14.-warranty-disclaimer) * [15\. LIMITATION OF LIABILITY](https://docs.sai.fun/resources/legal/terms-of-use#id-15.-limitation-of-liability) * [16\. Nature of Blockchain; Assumption of Risk; Waiver of Claims](https://docs.sai.fun/resources/legal/terms-of-use#id-16.-nature-of-blockchain-assumption-of-risk-waiver-of-claims) * [17\. No Professional Advice](https://docs.sai.fun/resources/legal/terms-of-use#id-17.-no-professional-advice) * [18\. No Fiduciary Duties](https://docs.sai.fun/resources/legal/terms-of-use#id-18.-no-fiduciary-duties) * [19\. No Insurance](https://docs.sai.fun/resources/legal/terms-of-use#id-19.-no-insurance) * [20\. Links from the Website or the Interface.](https://docs.sai.fun/resources/legal/terms-of-use#id-20.-links-from-the-website-or-the-interface) * [21\. Indemnification](https://docs.sai.fun/resources/legal/terms-of-use#id-21.-indemnification) * [22\. Governing Law & Jurisdiction](https://docs.sai.fun/resources/legal/terms-of-use#id-22.-governing-law-and-jurisdiction) * [23\. Arbitration; Class Arbitration Waiver](https://docs.sai.fun/resources/legal/terms-of-use#id-23.-arbitration-class-arbitration-waiver) * [24\. Limitation on Time to File Claims](https://docs.sai.fun/resources/legal/terms-of-use#id-24.-limitation-on-time-to-file-claims) * [25\. Waiver & Severability](https://docs.sai.fun/resources/legal/terms-of-use#id-25.-waiver-and-severability) * [26\. Entire Agreement](https://docs.sai.fun/resources/legal/terms-of-use#id-26.-entire-agreement) --- # Liquidity Provider (LP) API | Sai.fun Query and subscribe to liquidity pool data including vaults, deposits, withdrawals, and revenue tracking. [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#table-of-contents) Table of Contents --------------------------------------------------------------------------------------------------- * [LP API Reference](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lp-api-reference) * [Table of Contents](https://docs.sai.fun/for-devs/sai-keeper/api-lp#table-of-contents) * [Queries](https://docs.sai.fun/for-devs/sai-keeper/api-lp#queries) * [vaults](https://docs.sai.fun/for-devs/sai-keeper/api-lp#vaults) * [deposits](https://docs.sai.fun/for-devs/sai-keeper/api-lp#deposits) * [depositHistory](https://docs.sai.fun/for-devs/sai-keeper/api-lp#deposithistory) * [withdrawRequests](https://docs.sai.fun/for-devs/sai-keeper/api-lp#withdrawrequests) * [epochDurationDays](https://docs.sai.fun/for-devs/sai-keeper/api-lp#epochdurationdays) * [epochDurationHours](https://docs.sai.fun/for-devs/sai-keeper/api-lp#epochdurationhours) * [Subscriptions](https://docs.sai.fun/for-devs/sai-keeper/api-lp#subscriptions) * [lpVaults](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpvaults) * [lpDeposits](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpdeposits) * [lpDepositHistory](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpdeposithistory) * [lpWithdrawRequests](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpwithdrawrequests) * [Types Reference](https://docs.sai.fun/for-devs/sai-keeper/api-lp#types-reference) * [LpVault](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpvault) * [LpDeposit](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpdeposit) * [LpDepositHistoryItem](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpdeposithistoryitem) * [LpWithdrawRequest](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpwithdrawrequest) * [RevenueInfo](https://docs.sai.fun/for-devs/sai-keeper/api-lp#revenueinfo) * [Calculations](https://docs.sai.fun/for-devs/sai-keeper/api-lp#calculations) * [Deposit Value](https://docs.sai.fun/for-devs/sai-keeper/api-lp#deposit-value) * [Estimated Withdrawal Value](https://docs.sai.fun/for-devs/sai-keeper/api-lp#estimated-withdrawal-value) * [Time Until Withdrawal](https://docs.sai.fun/for-devs/sai-keeper/api-lp#time-until-withdrawal) * [APY Calculation](https://docs.sai.fun/for-devs/sai-keeper/api-lp#apy-calculation) [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#queries) Queries ------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#vaults) vaults List all liquidity vaults with their metrics. **Signature:** **Parameters:** * `where`: Filter object (optional) * `address`: Filter by vault address * `limit`: Max results * `offset`: Skip results * `order_by`: Sort by `address` * `order_desc`: Sort descending **Returns:** Array of `LpVault` objects **Example - Get All Vaults:** **Example - Get Specific Vault:** **Key Fields Explained:** * `tvl`: Total value locked in the vault * `sharePrice`: Current value of one vault share * `apy`: Annual percentage yield (annualized return) * `availableAssets`: Assets not currently in open positions * `currentEpoch`: Current epoch number * `epochStart`: Unix timestamp when current epoch started **Use Cases:** * Display vault list with metrics * Compare vault performance * Show available liquidity * Calculate potential returns * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#deposits) deposits Get user deposits in vaults. **Signature:** **Parameters:** * `where`: Filter object (optional) * `depositor`: Filter by depositor address * `vault`: Filter by vault address * `limit`: Max results * `offset`: Skip results * `order_by`: Sort by `depositor` or `vault` * `order_desc`: Sort descending **Returns:** Array of `LpDeposit` objects **Example - Get User Deposits:** **Example - Calculate Deposit Value:** To calculate value: **Use Cases:** * Display user's LP positions * Calculate total deposited value * Track share ownership * Portfolio management * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#deposithistory) depositHistory Get historical deposit and withdrawal events. **Signature:** **Parameters:** * `where`: Filter object (optional) * `depositor`: Filter by depositor address * `vault`: Filter by vault address * `limit`: Max results * `offset`: Skip results * `order_by`: Sort by `depositor`, `sequence`, or `vault` * `order_desc`: Sort descending **Returns:** Array of `LpDepositHistoryItem` objects **Example - Get Deposit History:** **Example - Filter Vault History:** **Key Fields:** * `isWithdraw`: `true` for withdrawals, `false` for deposits * `amount`: Amount of collateral deposited/withdrawn * `shares`: Shares minted (deposit) or burned (withdrawal) **Use Cases:** * Build activity timeline * Track deposit/withdrawal events * Audit vault operations * Calculate historical returns * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#withdrawrequests) withdrawRequests Get pending withdrawal requests. **Signature:** **Parameters:** * `where`: Filter object (optional) * `depositor`: Filter by depositor address * `vault`: Filter by vault address * `limit`: Max results * `offset`: Skip results * `order_by`: Sort by `depositor`, `unlock_epoch`, or `vault` * `order_desc`: Sort descending **Returns:** Array of `LpWithdrawRequest` objects **Example - Get User Withdrawals:** **Example - Check Withdrawal Status:** To check if ready: **Key Fields:** * `status`: Withdrawal request status * `unlockEpoch`: Epoch when withdrawal becomes available * `autoRedeem`: If `true`, automatically redeemed when unlocked **Use Cases:** * Display pending withdrawals * Calculate withdrawal timing * Show estimated withdrawal value * Notify when ready * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#epochdurationdays) epochDurationDays Get epoch duration in days. **Signature:** **Example:** * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#epochdurationhours) epochDurationHours Get epoch duration in hours. **Signature:** * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#subscriptions) Subscriptions ------------------------------------------------------------------------------------------- Subscribe to real-time LP data updates. ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpvaults) lpVaults Subscribe to all vault updates. **Signature:** **Example:** **Use Cases:** * Real-time TVL tracking * Live APY updates * Monitor available liquidity * Track epoch changes * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpdeposits) lpDeposits Subscribe to user deposit updates. **Signature:** **Example:** **Use Cases:** * Update user portfolio in real-time * Track share balance changes * Monitor position value * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpdeposithistory) lpDepositHistory Subscribe to deposit/withdrawal events. **Signature:** **Example:** **Use Cases:** * Real-time activity feed * Instant transaction notifications * Live history updates * * * ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpwithdrawrequests) lpWithdrawRequests Subscribe to withdrawal request updates. **Signature:** **Example:** **Use Cases:** * Notify when withdrawal ready * Track withdrawal status * Update UI when unlocked * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#types-reference) Types Reference ----------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpvault) LpVault ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpdeposit) LpDeposit ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpdeposithistoryitem) LpDepositHistoryItem ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpwithdrawrequest) LpWithdrawRequest ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#revenueinfo) RevenueInfo Tracks vault revenue and performance. * * * [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#calculations) Calculations ----------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#deposit-value) Deposit Value ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#estimated-withdrawal-value) Estimated Withdrawal Value ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#time-until-withdrawal) Time Until Withdrawal ### [hashtag](https://docs.sai.fun/for-devs/sai-keeper/api-lp#apy-calculation) APY Calculation APY is calculated from historical revenue: [PreviousPerp APIchevron-left](https://docs.sai.fun/for-devs/sai-keeper/api-perp) [NextOracle APIchevron-right](https://docs.sai.fun/for-devs/sai-keeper/api-oracle) Last updated 3 months ago * [Table of Contents](https://docs.sai.fun/for-devs/sai-keeper/api-lp#table-of-contents) * [Queries](https://docs.sai.fun/for-devs/sai-keeper/api-lp#queries) * [vaults](https://docs.sai.fun/for-devs/sai-keeper/api-lp#vaults) * [deposits](https://docs.sai.fun/for-devs/sai-keeper/api-lp#deposits) * [depositHistory](https://docs.sai.fun/for-devs/sai-keeper/api-lp#deposithistory) * [withdrawRequests](https://docs.sai.fun/for-devs/sai-keeper/api-lp#withdrawrequests) * [epochDurationDays](https://docs.sai.fun/for-devs/sai-keeper/api-lp#epochdurationdays) * [epochDurationHours](https://docs.sai.fun/for-devs/sai-keeper/api-lp#epochdurationhours) * [Subscriptions](https://docs.sai.fun/for-devs/sai-keeper/api-lp#subscriptions) * [lpVaults](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpvaults) * [lpDeposits](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpdeposits) * [lpDepositHistory](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpdeposithistory) * [lpWithdrawRequests](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpwithdrawrequests) * [Types Reference](https://docs.sai.fun/for-devs/sai-keeper/api-lp#types-reference) * [LpVault](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpvault) * [LpDeposit](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpdeposit) * [LpDepositHistoryItem](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpdeposithistoryitem) * [LpWithdrawRequest](https://docs.sai.fun/for-devs/sai-keeper/api-lp#lpwithdrawrequest) * [RevenueInfo](https://docs.sai.fun/for-devs/sai-keeper/api-lp#revenueinfo) * [Calculations](https://docs.sai.fun/for-devs/sai-keeper/api-lp#calculations) * [Deposit Value](https://docs.sai.fun/for-devs/sai-keeper/api-lp#deposit-value) * [Estimated Withdrawal Value](https://docs.sai.fun/for-devs/sai-keeper/api-lp#estimated-withdrawal-value) * [Time Until Withdrawal](https://docs.sai.fun/for-devs/sai-keeper/api-lp#time-until-withdrawal) * [APY Calculation](https://docs.sai.fun/for-devs/sai-keeper/api-lp#apy-calculation) Copy vaults( where: LpVaultsFilter limit: Int offset: Int order_by: LpVaultsOrder order_desc: Boolean ): [LpVault!]! Copy query GetAllVaults { lp { vaults { address collateralDenom collateralToken { symbol name logoUrl decimals } tvl sharePrice apy availableAssets currentEpoch epochStart sharesDenom sharesERC20 collateralERC20 revenueInfo { RevenueCumulative NetProfit TraderLosses ClosedPnl CurrentEpochPositiveOpenPnl Liabilities Rewards } } } } Copy query GetVault($address: String!) { lp { vaults(where: { address: $address }) { address tvl sharePrice apy availableAssets } } } Copy deposits( where: LpDepositsFilter limit: Int offset: Int order_by: LpDepositsOrder order_desc: Boolean ): [LpDeposit!]! Copy query GetUserDeposits($user: String!) { lp { deposits(where: { depositor: $user }) { depositor shares vault { address collateralToken { symbol } sharePrice tvl apy } } } } Copy query CalculateDepositValue($user: String!) { lp { deposits(where: { depositor: $user }) { shares vault { sharePrice collateralToken { symbol decimals } } } } } Copy const value = (shares * sharePrice) / (10 ** decimals) Copy depositHistory( where: LpDepositHistoryFilter limit: Int offset: Int order_by: LpDepositHistoryOrder order_desc: Boolean ): [LpDepositHistoryItem!]! Copy query GetDepositHistory($user: String!) { lp { depositHistory( where: { depositor: $user } order_by: sequence order_desc: true limit: 50 ) { id depositor amount shares isWithdraw vault { address collateralToken { symbol } } block { block block_ts } } } } Copy query GetVaultHistory($vaultAddress: String!) { lp { depositHistory( where: { vault: $vaultAddress } order_by: sequence order_desc: true limit: 100 ) { depositor amount shares isWithdraw block { block_ts } } } } Copy withdrawRequests( where: LpWithdrawRequestsFilter limit: Int offset: Int order_by: LpWithdrawRequestsOrder order_desc: Boolean ): [LpWithdrawRequest!]! Copy query GetWithdrawRequests($user: String!) { lp { withdrawRequests(where: { depositor: $user }) { depositor shares status unlockEpoch autoRedeem vault { address collateralToken { symbol } currentEpoch sharePrice } } } } Copy query CheckWithdrawalReady($user: String!) { lp { withdrawRequests(where: { depositor: $user }) { shares unlockEpoch autoRedeem vault { currentEpoch sharePrice } } } } Copy const isReady = vault.currentEpoch >= unlockEpoch const estimatedValue = (shares * vault.sharePrice) / (10 ** decimals) Copy epochDurationDays: Int! Copy query GetEpochDuration { lp { epochDurationDays epochDurationHours } } Copy epochDurationHours: Int! Copy subscription { lpVaults: [LpVault!]! } Copy subscription WatchVaults { lpVaults { address tvl sharePrice apy availableAssets currentEpoch } } Copy subscription { lpDeposits(where: SubLpDepositsFilter!): [LpDeposit!]! } Copy subscription WatchUserDeposits($user: String!) { lpDeposits(where: { depositor: $user }) { depositor shares vault { address sharePrice } } } Copy subscription { lpDepositHistory(where: SubLpDepositHistoryFilter!): [LpDepositHistoryItem!]! } Copy subscription WatchDepositEvents($user: String!, $vault: String!) { lpDepositHistory(where: { depositor: $user, vault: $vault }) { id amount shares isWithdraw block { block_ts } } } Copy subscription { lpWithdrawRequests(where: SubLpWithdrawRequestsFilter!): [LpWithdrawRequest!]! } Copy subscription WatchWithdrawals($user: String!, $vault: String!) { lpWithdrawRequests(where: { depositor: $user, vault: $vault }) { shares status unlockEpoch vault { currentEpoch } } } Copy type LpVault { address: String! collateralDenom: String! collateralToken: Token! collateralERC20: String! sharesDenom: String! sharesERC20: String! tvl: Int! sharePrice: Float! apy: Float availableAssets: Int! currentEpoch: Int! epochStart: Int! revenueInfo: RevenueInfo! } Copy type LpDeposit { depositor: String! shares: Int! vault: LpVault! } Copy type LpDepositHistoryItem { id: Int! depositor: String! amount: Int! shares: Int! isWithdraw: Boolean! vault: LpVault! block: Block! } Copy type LpWithdrawRequest { depositor: String! shares: Int! status: String! unlockEpoch: Int! autoRedeem: Boolean! vault: LpVault! } Copy type RevenueInfo { RevenueCumulative: Int! # Total revenue earned NetProfit: Int! # Current net profit TraderLosses: Int! # Profits from trader losses ClosedPnl: Int! # Realized PnL from closed positions CurrentEpochPositiveOpenPnl: Int! # Current epoch unrealized gains Liabilities: Int! # Current liabilities (trader profits) Rewards: Int! # Rewards received } Copy // Calculate current value of deposit const depositValue = (shares * sharePrice) / (10 ** decimals) Copy // For pending withdrawal const estimatedValue = (shares * currentSharePrice) / (10 ** decimals) Copy // Calculate epochs remaining const epochsRemaining = unlockEpoch - currentEpoch // Calculate time remaining (assuming epoch duration) const hoursRemaining = epochsRemaining * epochDurationHours Copy // Simplified APY calculation const apy = (revenueInfo.NetProfit / tvl) * (365 / epochDurationDays) --- # Telegram Bot Integration | Sai.fun A step-by-step walkthrough for creating a Telegram bot that queries real data from the SAI protocol. **No prior bot experience needed!** * * * [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#what-youre-about-to-build) What You're About to Build --------------------------------------------------------------------------------------------------------------------- By the end of this guide, you'll have a working Telegram bot that: * Shows perpetual trades for any wallet address * Displays oracle prices for all tokens * Lets users search for specific token prices * Filters trades by asset (BTC only, ETH only, etc.) * Displays results with pagination (Next buttons) **Best part:** No database needed! Everything pulls live data from SAI's GraphQL API. * * * [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#prerequisites-checklist) Prerequisites Checklist ---------------------------------------------------------------------------------------------------------------- Before starting, make sure you have: ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#software-youll-need) Software You'll Need * **Python 3.10+** - [Download herearrow-up-right](https://www.python.org/downloads/) * Check your version: Open terminal and type `python3 --version` * **A code editor** - Any of these work: * [VS Codearrow-up-right](https://code.visualstudio.com/) (recommended for beginners) * PyCharm * Sublime Text * Even Notepad will work! * **Terminal/Command Line** - You'll need to type a few commands ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#accounts-youll-need) Accounts You'll Need * **Telegram account** - To test your bot * **A Telegram bot token** - We'll get this from BotFather (the official Telegram bot creator tool) ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#knowledge-requirements) Knowledge Requirements * Basic Python understanding (if-statements, functions, dictionaries) * How to use terminal/command line (navigating folders, running commands) * **No GraphQL knowledge needed** - We'll explain everything! > **Don't have Python installed?** This is normal! Follow the download link above and choose your operating system. Python installation is straightforward. * * * [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#part-1-get-your-telegram-bot-token-5-minutes) Part 1: Get Your Telegram Bot Token (5 minutes) ------------------------------------------------------------------------------------------------------------------------------------------------------------- Your bot needs a unique token to identify itself to Telegram. Here's how to get one: ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-1-open-botfather-on-telegram) Step 1: Open BotFather on Telegram 1. Open the Telegram app (phone, desktop, or [webarrow-up-right](https://web.telegram.org/) ) 2. Search for `@BotFather` in the search bar 3. Click on it and start a conversation You should see BotFather respond with a welcome message and commands. ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-2-create-a-new-bot) Step 2: Create a New Bot 1. Send this command: `/newbot` 2. BotFather will ask: **"Alright! New bot. How are we going to call it? Please choose a name for your bot."** * Type something like: `My SAI Trading Bot` (this is what appears in Telegram) 3. BotFather will ask: **"Good. Now let's choose a username for your bot. It must end in bot."** * Type something like: `my_sai_trading_bot` (must end with `bot`) * Example: `sai_price_bot`, `trading_bot_sai`, etc. ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-3-copy-your-token) Step 3: Copy Your Token BotFather will send you a message like: **Important:** This token is like your bot's password. **Never share it publicly or commit it to GitHub!** * * * [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#part-2-prepare-your-computer-10-minutes) Part 2: Prepare Your Computer (10 minutes) --------------------------------------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-1-create-a-project-folder) Step 1: Create a Project Folder Open your terminal/command prompt and create a folder for your project: **What this does:** * Creates a new folder called `sai-telegram-bot` * Moves you into that folder (you'll work from here) ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-2-create-a-virtual-environment) Step 2: Create a Virtual Environment A virtual environment is like a sandbox for your project. It keeps your bot's dependencies separate from other Python projects. **On Mac/Linux:** **On Windows:** **What should happen:** * Your terminal prompt should now show `(venv)` at the beginning * Example: `(venv) user@computer sai-telegram-bot %` If you see `(venv)` ✅ - Perfect! You're in the virtual environment. > **What's a virtual environment?** Think of it like a separate Python installation just for this project. If you install packages here, they won't affect your other projects. ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-3-create-your-project-structure) Step 3: Create Your Project Structure Let's create the folders and files you'll need: **What this creates:** * * * [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#part-3-install-required-packages-2-minutes) Part 3: Install Required Packages (2 minutes) --------------------------------------------------------------------------------------------------------------------------------------------------------- Packages are like plugins that give Python extra abilities. We need several for this bot. ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-1-create-requirements.txt) Step 1: Create `requirements.txt` Open your code editor and create a file called `requirements.txt` with this content: **What each package does:** Package Purpose `python-telegram-bot` Library for creating Telegram bots `requests` Makes HTTP requests to APIs `python-dotenv` Loads secret config from .env file `pytz` Handles timezones ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-2-install-the-packages) Step 2: Install the Packages In your terminal (make sure `(venv)` is showing), type: This will download and install all the packages. You should see lots of text scrolling by. Wait for it to finish. **Success looks like:** * * * [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#part-4-set-up-your-secret-token-5-minutes) Part 4: Set Up Your Secret Token (5 minutes) ------------------------------------------------------------------------------------------------------------------------------------------------------- We'll store your bot token in a special file so it's not exposed in your code. ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-1-create-env.example) Step 1: Create `env.example` This file shows what secrets are needed (without actual values): ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-2-create-.env) Step 2: Create `.env` Copy the example file: Now open `.env` in your editor and replace `your_bot_token_here` with your actual token from BotFather: **Security note:** The `.env` file should NEVER be shared or committed to GitHub. If you use Git, add `.env` to your `.gitignore` file. * * * [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#part-5-build-the-graphql-client-intermediate) Part 5: Build the GraphQL Client (Intermediate) ------------------------------------------------------------------------------------------------------------------------------------------------------------- The "GraphQL client" is code that talks to SAI's data API. Don't worry - we'll explain everything! ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#what-is-graphql) What is GraphQL? GraphQL is a way to ask a server for specific data. Think of it like a restaurant menu: * **REST API (old way):** "Give me all the data about trades" (you get everything) * **GraphQL (new way):** "Give me only the trade ID, amount, and price" (you get exactly what you want) ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#create-bot-graphql.py) Create `bot/graphql.py` This file handles all communication with SAI's API. Here's the code: **Key concepts explained:** * `**class SaiGQLClient:**` - A class is like a blueprint. It groups related functions together. * `**def query():**` - The main function that sends queries to the API * `**fetch_trades():**` - Gets trade data for a wallet * `**fetch_prices():**` - Gets token prices * **String variables like** `**$trader**` - These are placeholders that get filled in with real values * * * [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#part-6-create-the-bots-brain-bot-main.py) Part 6: Create the Bot's Brain (`bot/main.py`) -------------------------------------------------------------------------------------------------------------------------------------------------------- This is where your bot comes alive! We'll build this in sections. ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#section-1-imports-and-setup) Section 1: Imports and Setup Create `bot/__init__.py` (can be empty): Create `bot/main.py` - Start with imports and setup: **What this does:** * Imports libraries we installed earlier * Sets up logging so we can debug issues * Imports our GraphQL client * * * ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#section-2-trade-formatting-functions) Section 2: Trade Formatting Functions Add this to `bot/main.py`: **What this does:** * `format_trade_open()` - Displays an open trade with profit/loss info * `format_trade_closed()` - Displays a closed trade with entry/exit prices * The division by `10^6` converts from micro-units (how the API stores numbers) to regular dollars * * * ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#section-3-price-formatting-functions) Section 3: Price Formatting Functions Add this to `bot/main.py`: **What this does:** * `format_prices()` - Formats multiple prices with pagination * `format_price_single()` - Formats a single price nicely * Popular tokens (BTC, ETH, etc.) appear first * * * ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#section-4-command-handlers) Section 4: Command Handlers Add this to `bot/main.py`: **What this does:** * `/start` command shows welcome message * `/help` shows the same message * * * ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#section-5-the-trades-command) Section 5: The Trades Command Add this to `bot/main.py`: * * * ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#section-6-the-prices-commands) Section 6: The Prices Commands Add this to `bot/main.py`: * * * ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#section-7-callback-handler-for-buttons) Section 7: Callback Handler (for buttons) Add this to `bot/main.py`: * * * ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#section-8-start-the-bot) Section 8: Start the Bot Add this to the end of `bot/main.py`: * * * [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#part-7-test-your-bot-5-minutes) Part 7: Test Your Bot! (5 minutes) ---------------------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-1-activate-virtual-environment) Step 1: Activate Virtual Environment Make sure you're in the project folder and the virtual environment is active: ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-2-start-the-bot) Step 2: Start the Bot You should see: **This means your bot is running!** Leave this terminal open. ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-3-test-on-telegram) Step 3: Test on Telegram 1. Open Telegram 2. Search for your bot (the username you created) 3. Send `/start` 4. You should see the welcome message! Try these commands: Command What it does `/start` Shows welcome message `/help` Shows help message `/prices` Shows all token prices `/price BTC` Shows BTC price `/trades nibiru1abc123...` Shows trades (need real wallet address) ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-4-stop-the-bot) Step 4: Stop the Bot When you're done testing, press `Ctrl+C` in the terminal to stop the bot. * * * [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#troubleshooting) Troubleshooting ------------------------------------------------------------------------------------------------ ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#problem-telegram_bot_token-not-found) Problem: "TELEGRAM\_BOT\_TOKEN not found" **Solution:** 1. Check that `.env` file exists in your project root 2. Open it and verify the token is there 3. Make sure there are no spaces: `TELEGRAM_BOT_TOKEN=123456789:ABC...` ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#problem-bot-doesnt-respond-to-commands) Problem: Bot doesn't respond to commands **Solution:** 1. Check that the bot is still running (you should see `🚀 Bot is starting...` in terminal) 2. Try the `/start` command first 3. Check for errors in the terminal ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#problem-modulenotfounderror-no-module-named-bot) Problem: "ModuleNotFoundError: No module named 'bot'" **Solution:** ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#problem-graphql-errors-or-no-prices-found) Problem: GraphQL errors or "No prices found" **Solution:** 1. Check your internet connection 2. Verify the endpoint is correct in `.env`: 3. Test the endpoint manually in your browser (visit the URL) * * * [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#how-it-all-works-together) How It All Works Together -------------------------------------------------------------------------------------------------------------------- Here's the flow when someone uses your bot: * * * [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#next-steps) Next Steps -------------------------------------------------------------------------------------- Your bot is working! Now you can: ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#level-up-your-bot) Level Up Your Bot * Add price alerts ("Notify me when BTC hits $100,000") * Add wallet monitoring ("Show me when this address opens a trade") * Add more markets or data sources ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#deploy-to-production) Deploy to Production * Run your bot 24/7 on a server (AWS, DigitalOcean, etc.) * Use `systemd` or Docker to keep it running ### [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#learn-more) Learn More * [python-telegram-bot docsarrow-up-right](https://python-telegram-bot.org/) * [GraphQL basicsarrow-up-right](https://graphql.org/learn/) * [SAI Protocol docsarrow-up-right](https://docs.sai.fi/) * * * [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#congratulations) Congratulations! 🎉 ---------------------------------------------------------------------------------------------------- You've built your first Telegram bot that: * Connects to the SAI protocol * Queries real live data * Displays prices and trades * Handles pagination * Filters by asset **You're now a bot developer!** * * * [hashtag](https://docs.sai.fun/for-devs/use-cases/tg-guide#common-questions) Common Questions -------------------------------------------------------------------------------------------------- **Q: Can I share my bot with others?** A: Yes! They can find it by searching your bot's username on Telegram and click "Start". **Q: Will my bot keep running if I close my computer?** A: No. You'll need to deploy it to a server to run 24/7. See the "Deploy to Production" section. **Q: How do I update my bot with new features?** A: Edit the Python files, then restart the bot (press Ctrl+C and run `python -m bot.main` again). **Q: Is it safe to share my bot token?** A: No! Treat it like a password. Keep it in `.env` and never commit to GitHub. * * * Happy coding! 🚀 [PreviousUse Caseschevron-left](https://docs.sai.fun/for-devs/use-cases) [NextCommunitychevron-right](https://docs.sai.fun/resources/community) Last updated 15 hours ago * [What You're About to Build](https://docs.sai.fun/for-devs/use-cases/tg-guide#what-youre-about-to-build) * [Prerequisites Checklist](https://docs.sai.fun/for-devs/use-cases/tg-guide#prerequisites-checklist) * [Software You'll Need](https://docs.sai.fun/for-devs/use-cases/tg-guide#software-youll-need) * [Accounts You'll Need](https://docs.sai.fun/for-devs/use-cases/tg-guide#accounts-youll-need) * [Knowledge Requirements](https://docs.sai.fun/for-devs/use-cases/tg-guide#knowledge-requirements) * [Part 1: Get Your Telegram Bot Token (5 minutes)](https://docs.sai.fun/for-devs/use-cases/tg-guide#part-1-get-your-telegram-bot-token-5-minutes) * [Step 1: Open BotFather on Telegram](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-1-open-botfather-on-telegram) * [Step 2: Create a New Bot](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-2-create-a-new-bot) * [Step 3: Copy Your Token](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-3-copy-your-token) * [Part 2: Prepare Your Computer (10 minutes)](https://docs.sai.fun/for-devs/use-cases/tg-guide#part-2-prepare-your-computer-10-minutes) * [Step 1: Create a Project Folder](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-1-create-a-project-folder) * [Step 2: Create a Virtual Environment](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-2-create-a-virtual-environment) * [Step 3: Create Your Project Structure](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-3-create-your-project-structure) * [Part 3: Install Required Packages (2 minutes)](https://docs.sai.fun/for-devs/use-cases/tg-guide#part-3-install-required-packages-2-minutes) * [Step 1: Create requirements.txt](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-1-create-requirements.txt) * [Step 2: Install the Packages](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-2-install-the-packages) * [Part 4: Set Up Your Secret Token (5 minutes)](https://docs.sai.fun/for-devs/use-cases/tg-guide#part-4-set-up-your-secret-token-5-minutes) * [Step 1: Create env.example](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-1-create-env.example) * [Step 2: Create .env](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-2-create-.env) * [Part 5: Build the GraphQL Client (Intermediate)](https://docs.sai.fun/for-devs/use-cases/tg-guide#part-5-build-the-graphql-client-intermediate) * [What is GraphQL?](https://docs.sai.fun/for-devs/use-cases/tg-guide#what-is-graphql) * [Create bot/graphql.py](https://docs.sai.fun/for-devs/use-cases/tg-guide#create-bot-graphql.py) * [Part 6: Create the Bot's Brain (bot/main.py)](https://docs.sai.fun/for-devs/use-cases/tg-guide#part-6-create-the-bots-brain-bot-main.py) * [Section 1: Imports and Setup](https://docs.sai.fun/for-devs/use-cases/tg-guide#section-1-imports-and-setup) * [Section 2: Trade Formatting Functions](https://docs.sai.fun/for-devs/use-cases/tg-guide#section-2-trade-formatting-functions) * [Section 3: Price Formatting Functions](https://docs.sai.fun/for-devs/use-cases/tg-guide#section-3-price-formatting-functions) * [Section 4: Command Handlers](https://docs.sai.fun/for-devs/use-cases/tg-guide#section-4-command-handlers) * [Section 5: The Trades Command](https://docs.sai.fun/for-devs/use-cases/tg-guide#section-5-the-trades-command) * [Section 6: The Prices Commands](https://docs.sai.fun/for-devs/use-cases/tg-guide#section-6-the-prices-commands) * [Section 7: Callback Handler (for buttons)](https://docs.sai.fun/for-devs/use-cases/tg-guide#section-7-callback-handler-for-buttons) * [Section 8: Start the Bot](https://docs.sai.fun/for-devs/use-cases/tg-guide#section-8-start-the-bot) * [Part 7: Test Your Bot! (5 minutes)](https://docs.sai.fun/for-devs/use-cases/tg-guide#part-7-test-your-bot-5-minutes) * [Step 1: Activate Virtual Environment](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-1-activate-virtual-environment) * [Step 2: Start the Bot](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-2-start-the-bot) * [Step 3: Test on Telegram](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-3-test-on-telegram) * [Step 4: Stop the Bot](https://docs.sai.fun/for-devs/use-cases/tg-guide#step-4-stop-the-bot) * [Troubleshooting](https://docs.sai.fun/for-devs/use-cases/tg-guide#troubleshooting) * [Problem: "TELEGRAM\_BOT\_TOKEN not found"](https://docs.sai.fun/for-devs/use-cases/tg-guide#problem-telegram_bot_token-not-found) * [Problem: Bot doesn't respond to commands](https://docs.sai.fun/for-devs/use-cases/tg-guide#problem-bot-doesnt-respond-to-commands) * [Problem: "ModuleNotFoundError: No module named 'bot'"](https://docs.sai.fun/for-devs/use-cases/tg-guide#problem-modulenotfounderror-no-module-named-bot) * [Problem: GraphQL errors or "No prices found"](https://docs.sai.fun/for-devs/use-cases/tg-guide#problem-graphql-errors-or-no-prices-found) * [How It All Works Together](https://docs.sai.fun/for-devs/use-cases/tg-guide#how-it-all-works-together) * [Next Steps](https://docs.sai.fun/for-devs/use-cases/tg-guide#next-steps) * [Level Up Your Bot](https://docs.sai.fun/for-devs/use-cases/tg-guide#level-up-your-bot) * [Deploy to Production](https://docs.sai.fun/for-devs/use-cases/tg-guide#deploy-to-production) * [Learn More](https://docs.sai.fun/for-devs/use-cases/tg-guide#learn-more) * [Congratulations! 🎉](https://docs.sai.fun/for-devs/use-cases/tg-guide#congratulations) * [Common Questions](https://docs.sai.fun/for-devs/use-cases/tg-guide#common-questions) Copy Done! Congratulations on your new bot. You'll find it at t.me/my_sai_trading_bot. You can now add a description, about section and profile picture for your bot, see /help for a list of commands. By the way, when you've finished creating your bot and it's ready to go public, ping our Bot Support if you'd like a chance to be featured on the @BotStoreBot and the Bot Store within Telegram. Here's your token: 123456789:ABCdefGHIjklMNOpqrsTUVwxyz Copy mkdir sai-telegram-bot cd sai-telegram-bot Copy python3 -m venv venv source venv/bin/activate Copy python -m venv venv venv\Scripts\activate Copy mkdir bot touch bot/__init__.py touch bot/main.py touch bot/graphql.py touch requirements.txt touch .env touch env.example Copy sai-telegram-bot/ ├── venv/ # Virtual environment (created automatically) ├── bot/ │ ├── __init__.py # Makes 'bot' a Python package │ ├── main.py # Your bot's main code │ └── graphql.py # Code to talk to SAI's API ├── requirements.txt # List of packages to install ├── .env # Your secret config (token goes here) └── env.example # Template for .env Copy python-telegram-bot==21.4 requests==2.32.3 python-dotenv==1.0.1 pytz==2024.1 Copy pip install -r requirements.txt Copy Successfully installed python-telegram-bot-21.4 requests-2.32.3 python-dotenv-1.0.1 pytz-2024.1 Copy # Telegram Bot Configuration # Get your bot token from @BotFather on Telegram TELEGRAM_BOT_TOKEN=your_bot_token_here # SAI GraphQL Endpoint (the server we get data from) SAI_GRAPHQL_ENDPOINT=https://sai-keeper.nibiru.fi/query Copy cp env.example .env Copy TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrsTUVwxyz SAI_GRAPHQL_ENDPOINT=https://sai-keeper.nibiru.fi/query Copy import os from typing import Any, Dict, List, Optional import requests # Get the GraphQL endpoint from .env file, or use default SAI_GRAPHQL_ENDPOINT = os.environ.get( "SAI_GRAPHQL_ENDPOINT", "https://sai-keeper.nibiru.fi/query" ) class SaiGQLClient: """ This class talks to the SAI GraphQL API. It's like a translator between your bot and SAI's data server. """ def __init__(self, endpoint: Optional[str] = None) -> None: """ Initialize the client. Args: endpoint: URL of the GraphQL server (or None to use default) """ self.endpoint = endpoint or SAI_GRAPHQL_ENDPOINT def query(self, query: str, variables: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: """ Send a GraphQL query to the server. Args: query: The GraphQL query string (ask what data you want) variables: Dynamic values to plug into the query Returns: Dictionary with the response data Raises: RuntimeError if the query fails """ # Send POST request (like sending a letter) to GraphQL server resp = requests.post( self.endpoint, json={"query": query, "variables": variables or {}}, timeout=20, # Wait max 20 seconds for response ) # Check if the HTTP request itself failed resp.raise_for_status() # Convert response from JSON format to Python dictionary try: payload = resp.json() except ValueError as e: # If response wasn't JSON, something went wrong raise RuntimeError( f"Invalid JSON response from {self.endpoint}: {resp.text[:200]}" ) # Check if GraphQL returned an error if "errors" in payload: raise RuntimeError(str(payload["errors"])) # Return just the "data" part of the response return payload.get("data", {}) def fetch_trades( self, trader: str, is_open: Optional[bool] = None, limit: int = 100, base_symbol: Optional[str] = None ) -> List[Dict[str, Any]]: """ Get trades for a specific wallet address. Args: trader: Wallet address (e.g., "nibiru1abc123...") is_open: - True: only open trades - False: only closed trades - None: all trades limit: Max number of trades to return base_symbol: Filter by token (e.g., "BTC" for Bitcoin only) Returns: List of trade dictionaries """ # This is a GraphQL query (in plain English: "give me trades") query = """ query Trades($trader: String!, $isOpen: Boolean, $limit: Int!) { perp { trades( where: { trader: $trader, isOpen: $isOpen } limit: $limit order_by: sequence order_desc: true ) { id trader isOpen isLong leverage openPrice closePrice openCollateralAmount collateralAmount openBlock { block block_ts } closeBlock { block block_ts } state { positionValue liquidationPrice pnlCollateral pnlPct } perpBorrowing { marketId baseToken { id name symbol } quoteToken { id name symbol } } } } } """ # Send the query with the variables data = self.query(query, { "trader": trader, "isOpen": is_open, "limit": limit }) # Extract trades from the response trades = data.get("perp", {}).get("trades", []) # Filter by symbol if user asked for it (e.g., BTC only) if base_symbol: base_symbol_upper = base_symbol.upper() trades = [\ t for t in trades\ if (t.get("perpBorrowing", {}).get("baseToken", {}).get("symbol", "") or "").upper() == base_symbol_upper\ ] return trades def fetch_prices(self, limit: int = 200) -> List[Dict[str, Any]]: """ Get current prices for all tokens from the oracle. Args: limit: Max number of prices to return Returns: List of price dictionaries """ query = """ query Prices($limit: Int!) { oracle { tokenPricesUsd(limit: $limit, order_by: oracle_token_id) { priceUsd token { id name symbol } lastUpdatedBlock { block block_ts } } } } """ data = self.query(query, {"limit": limit}) return data.get("oracle", {}).get("tokenPricesUsd", []) def fetch_price_by_symbol(self, symbol: str) -> Optional[Dict[str, Any]]: """ Get price for one specific token. Args: symbol: Token symbol (e.g., "BTC", "ETH") Returns: Price dictionary if found, None if not """ prices = self.fetch_prices(limit=200) symbol_upper = symbol.upper() for price in prices: token = price.get("token") or {} if (token.get("symbol") or "").upper() == symbol_upper: return price return None Copy # This file makes 'bot' a Python package Copy import os import logging from typing import Optional from dotenv import load_dotenv from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup from telegram.ext import ( Application, CommandHandler, CallbackQueryHandler, ContextTypes, ) from .graphql import SaiGQLClient # Set up logging (so you can see what your bot is doing) logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) Copy def format_trade_open(trade: dict) -> str: """ Format an open trade for display in Telegram. Shows all the important info: position value, profit/loss, etc. """ trade_id = trade.get('id', '?') is_long = trade.get('isLong', False) side = "🟢 LONG" if is_long else "🔴 SHORT" # Extract market info (what's being traded) market = trade.get("perpBorrowing", {}) base_token = market.get("baseToken") or {} quote_token = market.get("quoteToken") or {} base = base_token.get("symbol") or base_token.get("name") or "?" quote = quote_token.get("symbol") or quote_token.get("name") or "?" # Extract trade details leverage = trade.get('leverage') entry_price = trade.get('openPrice') open_collateral = trade.get('openCollateralAmount') # Extract state info (for open trades) state = trade.get('state') or {} position_value = state.get('positionValue') # How much the position is worth liquidation_price = state.get('liquidationPrice') # When you get liquidated pnl = state.get('pnlCollateral') # Profit/Loss in dollars pnl_pct = state.get('pnlPct') # Profit/Loss in percentage open_ts = trade.get("openBlock", {}).get("block_ts") # Build the message lines = [\ f"━━━━━━━━━━━━━━━━",\ f"Trade #{trade_id} ✅ OPEN",\ f"{base}/{quote} | {side} | {leverage}x leverage",\ f"Entry Price: {entry_price}",\ ] if liquidation_price: lines.append(f"Liquidation Price: {liquidation_price}") # Convert from micro units to dollars # (API stores values * 1,000,000, we need to divide back) if position_value is not None: position_value_usd = position_value / (10 ** 6) lines.append(f"Position Value: ${position_value_usd:,.2f}") if pnl is not None: pnl_usd = pnl / (10 ** 6) pnl_sign = "+" if pnl_usd >= 0 else "" lines.append(f"PnL: {pnl_sign}${pnl_usd:,.2f}") if pnl_pct is not None: pnl_sign = "+" if pnl_pct >= 0 else "" lines.append(f"PnL %: {pnl_sign}{pnl_pct:.2f}%") if open_collateral: collateral_usd = open_collateral / (10 ** 6) lines.append(f"Collateral: ${collateral_usd:,.2f}") if open_ts: lines.append(f"Opened: {open_ts}") return "\n".join(lines) def format_trade_closed(trade: dict) -> str: """ Format a closed trade for display. Shows entry/exit prices and when it was opened/closed. """ trade_id = trade.get('id', '?') is_long = trade.get('isLong', False) side = "🟢 LONG" if is_long else "🔴 SHORT" market = trade.get("perpBorrowing", {}) base_token = market.get("baseToken") or {} quote_token = market.get("quoteToken") or {} base = base_token.get("symbol") or base_token.get("name") or "?" quote = quote_token.get("symbol") or quote_token.get("name") or "?" leverage = trade.get('leverage') entry_price = trade.get('openPrice') exit_price = trade.get('closePrice') open_ts = trade.get("openBlock", {}).get("block_ts") close_ts = trade.get("closeBlock", {}).get("block_ts") lines = [\ f"━━━━━━━━━━━━━━━━",\ f"Trade #{trade_id} ❌ CLOSED",\ f"{base}/{quote} | {side} | {leverage}x leverage",\ f"Entry Price: {entry_price}",\ ] if exit_price: lines.append(f"Exit Price: {exit_price}") if open_ts: lines.append(f"Opened: {open_ts}") if close_ts: lines.append(f"Closed: {close_ts}") return "\n".join(lines) Copy # Popular tokens to show first POPULAR_TOKENS = ["BTC", "ETH", "USDT", "USDC", "NIBI", "ATOM", "SOL"] def format_prices(prices: list, start_idx: int = 0, page_size: int = 10) -> tuple: """ Format prices for display with pagination (Next buttons). Args: prices: List of all prices start_idx: Which index to start at page_size: How many prices per page Returns: (formatted_text, has_more_pages) """ if not prices: return "No prices found.", False # Sort prices: popular tokens first, then by ID def sort_key(p): token = p.get("token") or {} symbol = (token.get("symbol") or "").upper() if symbol in POPULAR_TOKENS: return (0, POPULAR_TOKENS.index(symbol)) return (1, token.get("id", 9999)) sorted_prices = sorted(prices, key=sort_key) # Get just this page of prices end_idx = min(start_idx + page_size, len(sorted_prices)) page_prices = sorted_prices[start_idx:end_idx] has_more = end_idx < len(sorted_prices) # Format as text msg_lines = [f"💰 Oracle Prices ({end_idx} of {len(sorted_prices)})\n"] for p in page_prices: token = p.get("token") or {} symbol = token.get("symbol") or token.get("name") or "Unknown" price = p.get("priceUsd") if price is not None: price_str = f"${price:,.2f}" if price >= 1 else f"${price:.8f}" msg_lines.append(f"• {symbol}: {price_str}") return "\n".join(msg_lines), has_more def format_price_single(price_data: dict) -> str: """Format a single price for display.""" token = price_data.get("token") or {} symbol = token.get("symbol") or token.get("name") or "?" price = price_data.get("priceUsd") if price is None: return f"Price not available for {symbol}" price_str = f"${price:,.2f}" if price >= 1 else f"${price:.8f}" return f"💰 {symbol}: {price_str}" Copy async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Handle /start command - shows welcome message.""" text = ( "👋 Welcome to SAI Bot!\n\n" "Available commands:\n" "/trades
- View trades for a wallet\n" "/prices - Show all token prices\n" "/price - Get price for one token\n" "/help - Show this message\n\n" "Examples:\n" "/trades nibiru1abc123def456\n" "/price BTC\n" "/prices" ) await update.effective_message.reply_text(text) async def help_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Handle /help command.""" await start(update, context) Copy async def trades_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """ Handle /trades command. Usage: /trades
/trades
open /trades
closed /trades
btc """ if not context.args: await update.effective_message.reply_text( "Usage: /trades [open|closed] [symbol]\n\n" "Examples:\n" "/trades nibiru1abc123...\n" "/trades nibiru1abc123... open\n" "/trades nibiru1abc123... btc" ) return address = context.args[0].strip() is_open = None # None = all trades symbol = None # Parse additional arguments for arg in context.args[1:]: arg_lower = arg.strip().lower() if arg_lower == "open": is_open = True elif arg_lower == "closed": is_open = False else: symbol = arg.strip().upper() try: # Get trades from SAI gql = SaiGQLClient() trades = gql.fetch_trades( trader=address, is_open=is_open, limit=100, base_symbol=symbol ) if not trades: await update.effective_message.reply_text( f"❌ No trades found for {address}" ) return # Separate open and closed trades open_trades = [t for t in trades if t.get('isOpen')] closed_trades = [t for t in trades if not t.get('isOpen')] # Show open trades first if open_trades: for i, trade in enumerate(open_trades[:5]): # Show first 5 await update.effective_message.reply_text( format_trade_open(trade) ) # Show closed trades if closed_trades: for i, trade in enumerate(closed_trades[:5]): # Show first 5 await update.effective_message.reply_text( format_trade_closed(trade) ) except Exception as e: logger.error(f"Error in trades_cmd: {e}") await update.effective_message.reply_text( f"❌ Error: {str(e)}" ) Copy async def prices_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Handle /prices command - show all prices.""" try: gql = SaiGQLClient() prices = gql.fetch_prices(limit=200) if not prices: await update.effective_message.reply_text("No prices found.") return # Get first page text, has_more = format_prices(prices, start_idx=0, page_size=10) # Add keyboard with "Next" button if there are more prices reply_markup = None if has_more: keyboard = [[InlineKeyboardButton("Next →", callback_data="prices_next")]] reply_markup = InlineKeyboardMarkup(keyboard) await update.effective_message.reply_text(text, reply_markup=reply_markup) except Exception as e: logger.error(f"Error in prices_cmd: {e}") await update.effective_message.reply_text(f"❌ Error: {str(e)}") async def price_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Handle /price command - show single price.""" if not context.args: await update.effective_message.reply_text( "Usage: /price \n\n" "Examples: /price BTC, /price ETH" ) return symbol = context.args[0].strip() try: gql = SaiGQLClient() price_data = gql.fetch_price_by_symbol(symbol) if not price_data: await update.effective_message.reply_text( f"❌ Token '{symbol}' not found" ) return text = format_price_single(price_data) await update.effective_message.reply_text(text) except Exception as e: logger.error(f"Error in price_cmd: {e}") await update.effective_message.reply_text(f"❌ Error: {str(e)}") Copy async def callback_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Handle button clicks (pagination buttons).""" query = update.callback_query if not query: return # Acknowledge the button click await query.answer() # Handle "Next" button for prices if query.data == "prices_next": page = context.user_data.get("prices_page", 0) + 1 context.user_data["prices_page"] = page try: gql = SaiGQLClient() prices = gql.fetch_prices(limit=200) text, has_more = format_prices(prices, start_idx=page * 10, page_size=10) reply_markup = None if has_more: keyboard = [[InlineKeyboardButton("Next →", callback_data="prices_next")]] reply_markup = InlineKeyboardMarkup(keyboard) await query.edit_message_text(text, reply_markup=reply_markup) except Exception as e: await query.edit_message_text(f"❌ Error: {str(e)}") Copy def main() -> None: """Main function - creates and starts the bot.""" # Load environment variables from .env file load_dotenv() # Get bot token token = os.environ.get("TELEGRAM_BOT_TOKEN") if not token: raise RuntimeError("❌ TELEGRAM_BOT_TOKEN not found in .env!") # Create the bot application app = Application.builder().token(token).build() # Register command handlers app.add_handler(CommandHandler("start", start)) app.add_handler(CommandHandler("help", help_cmd)) app.add_handler(CommandHandler("trades", trades_cmd)) app.add_handler(CommandHandler("prices", prices_cmd)) app.add_handler(CommandHandler("price", price_cmd)) # Register button click handler app.add_handler(CallbackQueryHandler(callback_handler)) # Start the bot logger.info("🚀 Bot is starting...") app.run_polling() if __name__ == "__main__": main() Copy # You should see (venv) at the start of your terminal prompt # If not, activate it: # Mac/Linux: source venv/bin/activate # Windows: venv\Scripts\activate Copy python -m bot.main Copy INFO:__main__:🚀 Bot is starting... Copy # Make sure you're in the project root directory cd /path/to/sai-telegram-bot # Make sure you activated the virtual environment source venv/bin/activate # Mac/Linux # or venv\Scripts\activate # Windows # Run with -m flag python -m bot.main Copy SAI_GRAPHQL_ENDPOINT=https://sai-keeper.nibiru.fi/query Copy 1. User sends: /prices ↓ 2. Telegram receives it and forwards to your bot ↓ 3. prices_cmd() function is called ↓ 4. SaiGQLClient queries the SAI GraphQL API ↓ 5. API returns price data ↓ 6. format_prices() formats the data beautifully ↓ 7. Bot sends the message back to the user ↓ 8. User sees prices with a "Next" button ↓ 9. User clicks "Next" ↓ 10. callback_handler() fetches the next page ↓ 11. User sees next page of prices ---