# Introduction

<figure><img src="/files/s4A6QB1lkdi5QvXMlDug" alt=""><figcaption></figcaption></figure>

SynFutures is an onchain trading protocol for crypto and real-world assets bringing stocks, ETFs, and derivatives onchain. Its vision is to become the protocol where every asset can be traded through open, and permissionless infrastructure. By unifying crypto and real-world markets in one accessible trading layer, SynFutures is building the foundation for a more open global financial system.

*A unified trading layer for crypto, real-world assets, and derivatives.*

### RWA

Through its integration with [Anchored](https://anchored.finance/), SynFutures initially offers 50+ real-world asset markets, including US tokenized stocks, ETFs, and other traditional market instruments. By bringing these assets onchain, SynFutures connects familiar financial markets with DeFi-native transparency, accessibility, and permissionless participation.

### Perp

SynFutures’ Perps Engine combines an onchain orderbook with AMM-based liquidity to power transparent, self-custodial derivatives trading. Built for efficient execution, flexible liquidity, and scalable market creation, it will expand from crypto perps into RWA markets over time.

### Battle-tested across 3 versions

The best practices in TradFi and DeFi are implemented to enable users to confidently trade on a protocol that's been tried and tested through multiple market cycles.

* **$300B+** in cumulative trading volume
* **430K+** all-time traders
* **350+** pairs listed

### Backed by Industry Leaders

<figure><img src="/files/n7fmqfeeLWtUv4F2AoYC" alt=""><figcaption></figcaption></figure>


# RWA Overview

SynFutures offers access to tokenized US equities through a DeFi-native trading experience. Through RWA Trading, users can trade tokenized versions of US-listed stocks directly from their Web3 wallet, with each tokenized stock representing 1:1 exposure to the underlying share through regulated brokerage infrastructure.

Powered by [Anchored](https://anchored.finance/), the digital operating system for tokenized stocks, SynFutures brings US stock exposure onchain with dedicated liquidity support for seamless trading. Users can connect their wallet and trade tokenized stocks 24/5 with no KYC required.

> Trade **US stocks** from anywhere, **24/5**, right from your wallet. **No KYC, no brokerage account needed.**


# RWA Partners

RWA Trading on SynFutures is powered by **Anchored**, the global digital operating system for RWAs. Anchored issues tokenized US stocks on **Ethereum**, enabling users to access direct economic exposure to traditional equities through standard ERC-20 tokens.

Each token represents economic exposure to the underlying stock and is backed 1:1 by shares held through regulated brokerage infrastructure. SynFutures brings these tokenized assets into a DeFi-native trading experience, allowing users to trade US stock exposure directly from their Web3 wallet.

### Key Highlights

**1:1 Backed** — Every tokenized stock is fully collateralized by the underlying stock held through regulated brokerage infrastructure.

**Proof of Reserves** — Independent verification helps confirm the 1:1 backing, giving users greater transparency into the reserves behind each tokenized stock.

**ERC-20 Standard** — Tokenized stocks are issued as standard ERC-20 tokens, compatible with Ethereum-supported wallets such as MetaMask, Rabby, Trust Wallet, OKX Wallet, Coinbase Wallet, Ledger, and more.

**Dividends** — Cash dividends are automatically distributed as USDC directly to eligible holders’ wallets. No action required.

Learn more at [anchored.finance](https://anchored.finance) or read the Anchored [Documentation](https://docs.anchored.finance/).

> You can independently verify Anchored's reserves through Accountable, an independent third party, [here](https://accountable.anchored.finance/).

***

#### What You Own

When you hold a tokenized stock, you have **economic exposure** to the underlying stock. This means:

* You benefit from price movements of the underlying stock
* You receive cash dividends (as USDC) when the company pays them
* Your tokens are freely transferable to any wallet on Monad and can be used for DeFi-native use cases

**Note:** Tokenized stocks represent economic exposure only. They do not confer voting rights, shareholder reporting rights, or direct legal ownership of the underlying stock.


# How RWA Trading Works?

When you trade tokenized stocks on SynFutures, your order is filled through dedicated liquidity support connected to Anchored’s issuance and redemption infrastructure.

The flow works like this:

1. **You place a buy or sell order** for a tokenized stock on SynFutures.
2. **A market maker sources liquidity through Anchored** and helps execute the trade against 1:1 backed tokenized stocks.
3. **When the order is filled,** you receive the tokenized stock when buying, or USDC when selling, directly in your wallet.

This is not pool-based liquidity. Instead, dedicated market makers act as the execution layer between SynFutures users and Anchored’s tokenized stock infrastructure, helping provide a smoother trading experience backed by real underlying assets.

{% hint style="info" %}
**Note:** RWA Trading with Anchored’s dedicated execution and liquidity support is only available through the official RWA Trading section on SynFutures. Tokenized stocks traded elsewhere may not have the same execution mechanism, liquidity depth, or reserve-backed settlement flow.
{% endhint %}

#### Trading Hours <a href="#trading-hours" id="trading-hours"></a>

Because tokenized stocks are tied to the underlying US stock market, RWA Trading follows a **24/5 schedule** (Monday through Friday) across four sessions:

<table><thead><tr><th width="249">Session</th><th>Hour (ET)</th><th>Description</th></tr></thead><tbody><tr><td><strong>Overnight</strong></td><td>8:00 PM – 4:00 AM</td><td>Extended hours trading</td></tr><tr><td><strong>Pre-market</strong></td><td>4:00 AM – 9:30 AM</td><td>Early session before market open</td></tr><tr><td><strong>Regular</strong></td><td>9:30 AM – 4:00 PM</td><td>Standard US market hours</td></tr><tr><td><strong>After-hours</strong></td><td>4:00 PM – 8:00 PM</td><td>Post-close trading</td></tr></tbody></table>

**Market closures:**

* All US federal holidays (New Year's Day, MLK Jr. Day, Presidents' Day, Good Friday, Memorial Day, Juneteenth, Independence Day, Labor Day, Thanksgiving, Christmas)
* Weekends (Saturday and Sunday)
* Early closures on the trading day before certain holidays

> Orders placed while the market is closed will not be filled until the next trading session opens.

The team is proactively working with the Anchored team and its market maker network to expand stock trading to 24/7.

#### Order Types <a href="#order-types" id="order-types"></a>

RWA Trading on SynFutures supports familiar order types for trading tokenized stocks:

* **Market Order** — Available during the regular US market session. It executes immediately at the best available price, so users can buy or sell right away.
* **Limit Order** — Can be placed at any time, but only executes during supported trading sessions. It lets users set the price at which they want to buy or sell, and the order only fills when the market reaches that price. This is useful when the market is closed or when users want a specific entry or exit point.


# Getting Started

Follow the simple steps below to get started:

1. **Connect your wallet** to SynFutures.
2. **Go to RWA Trading** from the SynFutures navigation menu.
3. **Deposit USDC** into your RWA trading account/web3 wallet, and receive [**mUSD**](/rwa-trading/faq#what-is-musd).
4. **Choose a tokenized stock** from the available RWA markets.
5. **Place your order.** For a market order, enter the amount you want to buy or sell and confirm. For a limit order, enter both the amount and the price at which you want the order to execute.
6. **Receive your tokens.** Once filled, your tokenized stocks will appear in your SynFutures portfolio.
7. **To sell**, place a sell order for your tokenized stock. Once the order is filled, you will receive USDC back in your wallet.


# FAQ

<details>

<summary><strong>What tokenized stocks and ETFs are available on SynFutures?</strong></summary>

SynFutures currently supports 58 major US-listed tokenized stocks and ETFs, including **TSLA, AAPL, MSTR, AMZN, COIN, HOOD,** and more. Each asset is backed 1:1 by the underlying shares held through regulated custody infrastructure, with more tokenized assets to be added over time.

</details>

<details>

<summary><strong>Do I need to interact with Anchored directly?</strong></summary>

No. You trade entirely through the SynFutures interface. The market maker network handles all interaction with Anchored in the background.

</details>

<details>

<summary><strong>Are my tokenized stocks really backed 1:1?</strong></summary>

Yes. Every tokenized stock issued by Anchored is backed by the actual underlying stock, held in a segregated brokerage account and verified by [Accountable](https://accountable.capital/), an independent auditor, every 24 hours. Users can check the proof-of-reserves [data](https://accountable.anchored.finance/) for independent verification.

</details>

<details>

<summary><strong>What happens to my order if the market is closed?</strong></summary>

**Market Orders** can only be placed and executed during regular US market hours. They are not available during market closures, including weekends and US market holidays.

**Limit Orders** can be placed at any time, but they will only be executed during regular market hours or supported extended trading hours, when pricing and liquidity are available.

</details>

<details>

<summary><strong>Can I transfer my tokenized stocks to another wallet?</strong></summary>

Yes. Tokenized stocks are standard ERC-20 tokens and can be freely transferred to any compatible wallet on Ethereum.

</details>

<details>

<summary><strong>How are dividends paid?</strong></summary>

When dividends are supported for a tokenized stock, eligible cash dividends may be distributed as USDC directly to holders’ wallets. This happens automatically, with no claim or action required, while availability and timing depend on the specific tokenized asset.

</details>

<details>

<summary><strong>Are there any fees?</strong></summary>

These are the fees associated with RWA trading:

Deposit fee: 0 bps\
Withdrawal fee: 21 bps\
Buy Order Fee: 11 bps\
Sell Order Fee: 1 bps

</details>

<details>

<summary><strong>Do I need to complete KYC?</strong></summary>

No. RWA Trading on SynFutures does not require KYC.

</details>

<details>

<summary><strong>Where can I learn more about how tokenized stocks work?</strong></summary>

Users can find additional details about each tokenized stock at the bottom of its trading page on SynFutures. Further information is also available through our partner’s page: [anchored.finance](https://anchored.finance).

</details>

<details>

<summary><strong>Does SynFutures custody my assets?</strong></summary>

Your tokenized stocks are ERC-20 tokens held in your non-custodial wallet, giving you direct control over your assets.

</details>

<details>

<summary><strong>What is mUSD?</strong></summary>

mUSD is a 1:1 USD-backed accounting unit that reflects users’ available purchasing power for RWA Trading on SynFutures. It is required to buy and sell tokenized stocks within the RWA trading experience. mUSD represents credit available exclusively for RWA Trading and is not a cryptocurrency, stablecoin, or transferable virtual asset.

</details>

<details>

<summary><strong>Have RWAs on SynFures been audited?</strong></summary>

Yes. Anchored, SynFutures’ partner for RWA Trading, has completed a smart contract audit with [Sherlock](https://sherlock.xyz/). The audit report is now available and can be [reviewed](https://sherlock-files.ams3.digitaloceanspaces.com/reports/2026.04.02%20-%20Final%20-%20Anchored%20Collaborative%20Audit%20Report%201775117748.pdf) here.

</details>

<details>

<summary><strong>What are the trading hours for RWAs on SynFutures?</strong></summary>

RWA Trading follows a 24/5 schedule from Sunday evening to Friday evening, based on Eastern Time (ET):

**Overnight Session:** 8:00 PM – 4:00 AM ET\
**Pre-Market Session:** 4:00 AM – 9:30 AM ET\
**Regular Market Session:** 9:30 AM – 4:00 PM ET\
**After-Hours Session:** 4:00 PM – 8:00 PM ET

The overnight session follows the NYSE holiday calendar. If US markets are fully closed for a holiday, the overnight session immediately before that holiday will not run. For example, the overnight session is closed on the Wednesday evening before US Thanksgiving and resumes on Thursday evening at 8:00 PM ET.

On US market half-days, the overnight session still runs as usual from 8:00 PM to 4:00 AM ET, even if regular or after-hours trading closes early. For example, on the Friday after US Thanksgiving, the overnight session runs as usual, but the after-hours session does not.

</details>

<details>

<summary><strong>Can I redeem my tokenized stock for the underlying asset?</strong></summary>

Tokenized stocks can only be redeemed for USDC.

</details>

<details>

<summary><strong>How is the price of the tokenized stock determined?</strong></summary>

SynFutures RWA Trading is supported by institutional-grade liquidity and execution through Anchored’s partner and market maker network. Pricing and liquidity are sourced by Anchored’s partners, who work with brokerage firms to support reliable execution for tokenized stock trades.

</details>

<details>

<summary><strong>Does RWA trading use a liquidity pool?</strong></summary>

No. SynFutures RWA Trading uses a dedicated market maker network for liquidity and execution. Market makers source pricing and liquidity from NASDAQ and traditional financial markets.

</details>

<details>

<summary><strong>Are there any restricted regions for RWA trading?</strong></summary>

RWA Trading is unavailable in certain restricted jurisdictions, including the United States, China, and the Philippines.

</details>

<details>

<summary><strong>How to add the tokenized stock to my wallet?</strong></summary>

1. Find the tokenized stock on SynFutures and click the **Add to Wallet** icon beside it.
2. If supported, the token may be imported automatically into your wallet.
3. If auto-import does not work, hover over the icon to copy the token’s onchain address.
4. Paste the address into your wallet’s **Add Custom Token** flow and follow your wallet’s instructions.

</details>


# Perp Overview

Our **Perps Engine** represents a next-generation derivatives market infrastructure built around a **permissionless onchain orderbook**. Evolving beyond traditional AMM-based models, it enables **active market making** through deterministic, transparent, and fully on-chain mechanisms designed for perpetual futures and other derivative products—while maintaining the flexibility to support **passive liquidity provision** for long-tail assets.

### Single-Token Concentrated Liquidity for Derivatives

The Oyster AMM model **facilitates liquidity concentration within specific price ranges and incorporates leverage to increase capital efficiency.** Unlike prevalent spot market-focused liquidity models such as UniSwap v3, the Oyster AMM introduces a margin management and liquidation framework tailored specifically for derivatives. Moreover, this model embraces the concept of two-sided liquidity while utilizing just a single token, eliminating the necessity to provide liquidity for both ends of a token pair. This streamlined approach enhances the efficiency of the trading ecosystem, making it even easier for traders to take advantage of SynFutures’ permissionless listings feature. **This feature enables users to list ‘anything against anything,’ including RWA pairings and Liquid Restaking Tokens (LRTs).**

### Permissionless On-chain Orderbook

**The AMM model democratizes market access, offering automated “market maker” functionality even for niche assets, enhancing diversity.** However, this comes at the expense of capital efficiency as AMMs demand significant liquidity for equivalent price impact compared to order book models. Order book models don’t often offer volatile digital assets due to complex infrastructure and risk management concerns. However, they are ideal for capital efficiency, concentrating liquidity around mid-price. With that in mind, we also introduced an order book model in SynFutures v3. After evaluating off-chain and hybrid alternatives, we chose a Permissionless Onchain Orderbook, guaranteeing transparency, trustlessness, and anti-censorship. The model promises security and robustness by eliminating dependence on centralized administrators to process orders with the potential for “backdoors” and mitigating vulnerabilities across on-off chain systems, including order management matching and executions in alternative models.

### Single Model for Unified Liquidity

**The Oyster AMM introduces an innovative liquidity paradigm by seamlessly integrating concentrated liquidity and order book in a single model, offering a unified liquidity system tailored to active traders and passive liquidity providers.** This cohesive approach ensures traders can enjoy efficient atomic transactions with predictability. Conversely, in systems that combine on-chain AMM with off-chain limit order systems or separate on-chain limit order systems, trade requests are split between these systems, leading to inefficiencies, non-atomic execution, and unpredictability. This dual-process execution risks non-synchronization, potentially leaving the AMM to process the transaction while the order book system falters, introducing potential confusion if the order book operates off-chain.

### Stabilization Mechanism for User Protection

**The Oyster AMM introduces advanced financial risk management mechanisms from past protocol iterations to enhance user protection and price stability.** These mechanisms include a dynamic penalty fee, which discourages price manipulation by imposing penalties for significant deviations between trade prices and mark prices. The dynamic fee system also balances the LP’s risk-reward profile. The other is the stabilized mark prices mechanism, which uses an exponential moving average process to mitigate the risk of sudden price fluctuations and mass liquidations.

<br>


# Pricing

Oyster AMM unifies **both concentrated liquidity and limit orders in a single model.** In smart contract terms, Range and Order can provide liquidity at each price point. For Range covering the same price point, their liquidity, or square root of k, is added for AMM curve-related calculation. For Order at the same price point, their order sizes are added together to cater to the taker’s trade size. In addition, Order is always consumed before liquidity from Range is consumed.

The collection of concentrated liquidity covering a price point and all open limit orders on the same price point is described in a struct called **Pearl** and is stored in the smart contract indexed by price. Oyster AMM can be viewed as the collection of Pearl along with the AMM price curve.

In fact, Pearl serves as the pool for limit orders from makers, which is also the key to the feasibility of the asynchronous design of limit orders in Oyster AMM. In this way, logic on the takers’ side is simplified greatly, where takers take as much as they want and nothing more. With Pearl's fungible approach, the gas cost of a trade is directly proportional to the price impact, i.e., the number of ticks crossed.

For a trade of size S0, the process of trading, or consuming unified liquidity, follows the steps below.

1. Check if there are unfilled limit orders in the Pearl of current price P0.
   1. If not, go to step 2 with S1 = S0.
   2. If so, fill the limit orders as much as possible.
      1. If S0 is fully filled, terminate. (Note that the current price does not change in this case.)
      2. If not, continue to step 2 with the remaining size S1.
2. Find the Pearl at the next price, P1.
   1. Trade for size S1 on the AMM curve between Pearl at P0 and P1.
      1. If S1 is fully filled, terminate. (Note that the current price does change in this case.)
      2. If not, update the current price as P1 and go to step 1 with the remaining size S2.

<div align="right"><figure><img src="/files/8RoKs5BSq0F3Z0A0Mksn" alt="" width="563"><figcaption></figcaption></figure></div>


# Market Order

Trade with Oyster AMM directly. The counterparty can either concentrated liquidity or limit orders.

* **Price Impact:** The estimated change of AMM price after this trade.
* **Limit Price:** The maximum price for a buy trade/The minimum price for a sell trade.
* **Trading Fee:** The fee paid for the trade. Please refer to the Pair Specification section.
* **Additional Fee:** A mechanism to protect liquidity providers. Please refer to the Security section.


# Limit Order

In Oyster AMM, the buy and sell limit orders on single price points are combined with concentrated liquidity to provide pricing for traders. Unlike some spot AMM models, where highly concentrated liquidity is used as a proxy of limit orders, Oyster AMM enables **native limit orders like those in central limited order book systems.** These are defined on single price points and irreversible once filled. The design provides makers with certainty of the status of their limit orders.

## Matching and Overall Pricing Mechanism

To circumvent current smart contract limitations, order matching in Oyster AMM does not follow the traditional centralized limit order book model, which is the **first-in-first-out** principle. Rather, it follows a matching process more suitable for an AMM. The characteristics of the matching and pricing mechanism can be summarized below.

* At a price point where limit orders exist, they are filled before any concentrated liquidity is consumed.
* Trade volume is allocated to multiple limit orders proportionately at the same price. A just-in-time limit order is welcomed.
* rall slippage can be greatly reduced when limit orders exist on the AMM price curve.

## Admissible Limit Order Price

Oyster AMM adopts a tick-based numeral system in which a tick exists at every price p with an **integer power of 1.0001.**

For limit orders, the granularity of prices is set to **5** ticks, i.e., admissible limit order prices are **1.0001^5n,** where n is an integer.

## Maker Fee Rebate

The limit order maker receives a **fee rebate** for filled orders. Please refer to the [Pair Specification](/perp-trading/pair-specifications) section.

## Execution Fees

Once an order is filled, Oyster AMM allows any address to **send a transaction onchain to convert the filled order into a position to complete the processing.** Note that this does not change the ownership of the filled order and position. An **execution fee** is paid to the transaction's sender to compensate for the gas cost. Please refer to the [Pair Specification](/perp-trading/pair-specifications) section.


# Position and Margin

## Position and Margin

### Market order and Filled Limit Order Result in a Position

* Leverage: Position Value / Margin
* Average Price:
  * Oyster AMM tracks the average price of a position and uses that to **calculate realized P\&L when closing the position.**
  * The Average Price of a position **stays the same** after a partial close of the position.
* Realized P\&L = (Traded Price - Average Price) \* Traded Size
* Unrealized P\&L = (Mark Price - Average Price) \* Position Size, note that Position Size is signed in this equation.
* Unrealized Funding, please refer to [Funding section](/perp-trading/funding-for-perpetual-futures)
* Unrealized social loss, please refer to [Liquidations section](/perp-trading/liquidations)
* **Total Unrealized P\&L = Unrealized P\&L + Unrealized Funding + Unrealized social loss**

## Mark Price for Position

Mark Price determines the **unrealized profit and loss and margin requirements of all positions in a pair.** This price also determines the **initial margin requirement to open a position.** This is the price for position risk management.

Mark price is based on **spot index price but also incorporates a daily interest component of the underlying trading pair.** With the segregation of duty of trading and marking, the pricing mechanism in Oyster AMM shields all users from attacks, such as flash loans and price manipulation

### IMR and MMR

* **Initial margin requirement** = Position Value \* Initial Margin Ratio. Used to determine the margin required to open new or increase current positions. Please refer to the Pair Specification section.&#x20;

{% hint style="info" %}
If any single position in a specific asset pair with an initial leverage of 10x or higher exceeds either threshold, the IMR for that pair will automatically double.&#x20;

Major pairs have their thresholds outlined in the table below, while default thresholds for all  other pairs are set at either 20% of total open interest (OI) for that specific pair or USD 100,000 in value.&#x20;

Pairs with leverage lower than 10x will not be subject to this rule.
{% endhint %}

<table><thead><tr><th align="center">Chain</th><th width="269" align="center">Pair</th><th width="207" align="center">Open Interest Percentage Threshold for Each Pair</th><th align="center">USD Value Threshold</th></tr></thead><tbody><tr><td align="center">Base</td><td align="center">BTC-USDC-LINK-PERP</td><td align="center">50%</td><td align="center">$2,000,000</td></tr><tr><td align="center">Base</td><td align="center">ETH-USDC-LINK-PERP</td><td align="center">50%</td><td align="center">$500,000</td></tr><tr><td align="center">Base</td><td align="center">BTC-WETH-LINK-PERP</td><td align="center">50%</td><td align="center">$500,000</td></tr><tr><td align="center">Blast</td><td align="center">BTC-USDB-PYTH-PERP</td><td align="center">50%</td><td align="center">$500,000</td></tr><tr><td align="center">Blast</td><td align="center">USDB-WETH-PYTH-PERP</td><td align="center">50%</td><td align="center">$5,000,000</td></tr><tr><td align="center">Blast</td><td align="center">BTC-WETH-PYTH-PERP</td><td align="center">50%</td><td align="center">$200,000</td></tr><tr><td align="center">Base &#x26; Blast</td><td align="center">Others</td><td align="center">20%</td><td align="center">$100,000</td></tr></tbody></table>

* **Maintenance margin requirement = Position Value \* Maintenance Margin Ratio. Used to determine the margin required to avoid liquidation of the current position. Please refer to the Pair Specification section.**

### Open Position Margin Requirement

* As trade price and mark price vary, a trade could result in **unrealized profit or loss right after execution.**
* In the case of an unrealized profit, this unrealized profit **can not be used to satisfy the initial margin requirement to open this position.**
* In the case of an unrealized loss, this unrealized loss **needs to be covered by an additional margin to satisfy the initial margin requirement to open this position.**

### Increase Position

In addition to the requirements of opening a new position, **if the margin for the existing position falls below its initial margin requirement, the additional margin is required to top up the existing position margin to its initial margin requirement.**

### Close Position

As long as the resulting position **satisfies its maintenance margin requirement,** closing an existing position requires no other margin.

### Leverage

Due to the margin requirement rules mentioned above for open positions, **not all trades can open positions with maximum leverage allowed for the pair.**

### Margin Deposit and Withdraw

* Position margin can be deposit at **any time.**
* Unrealized profit **cannot be withdrawn** from the position margin but can be used to satisfy maintenance margin requirements.
* Margin available for withdrawal = **Max(0, total margin - unrealized profit - initial margin requirement)**
* Withdrawal of realized profit **in a short period of time is throttled to a level set by the admins.** Please refer to [pair specification section.](/perp-trading/pair-specifications) If a user's realized profit reaches the throttle and decides to withdraw in one go, a waiting time is imposed. The user could either contact admin or continue waiting. Once the waiting time is passed, the user's realized profit withdrawal counter is reset.

### Open Interests&#x20;

Total open interests are from two types of positions, the positions inside the Oyster AMM as liquidity inventory and the positions traders hold in their accounts.&#x20;

Long open interests are from all traders long positions. Short open interests are from all traders short positions. They can differ as AMM liquidity is taking the other side of the net position.


# Funding for Perpetual Futures

As perpetual futures do not have a final settlement at maturity to guarantee convergence to the spot market index price, Oyster AMM employs **continuous funding** for its perpetual futures markets. The principle is to expect the deviation to converge in a specified cycle.

$$
FundingFeeRate = \frac{(P\_{\text{fair}} - P\_{\text{spot}})}{P\_{\text{spot}}} \cdot \frac{\Delta t}{Interval}
$$

**∆t** is the time difference in seconds between the current and last timestamp when the funding fee rate is calculated.

**Interval** can be set to different settings based on the trading pair, such as 1 hour (3600 seconds), 8 hours (28800 seconds), or 24 hours (86400 seconds).

The Funding index of a pair is updated when anyone interacts with that pair.

**Unrealized funding payment/income of a position is realized or settled when the position is increased, reduced, or closed or its margin is adjusted.**

## Advanced

Due to limitations of smart contract implementation, liquidities added through Earn (AMM pools) are exempted from the funding income/fees. The smart contracts keeps track of the total OI of long positions and total OI of short positions, excluding the OIs in liquidity.The actual funding payment amount calculation is as follows, let FundingFeeRate be the rate calculated above.

For the case long pays short：

* **FundingFeeRateForLong = -FundingFeeRate**
* **FundingFeeRateForShort = +FundingFeeRate \* totalLongPositionOI / totalShortPositionOI**

For the case short pays long：

* **FundingFeeRateForShort = FundingFeeRate**
* **FundingFeeRateForLong = -FundingFeeRate \* totalShortPositionOI / totalLongPositionOI**

**In other words, the side that pays funding always pays as is, but the side that receives funding may receive more or less funding depending on the position imbalance.**

##

## Implications

In a market scenario where **price rapidly goes up,** totalLongPositionOI is likely to be much larger than totalShortPositionOI as liquidity in Oyster AMM is holding most of the short position until LP's remove those liquidity

* If the long positions need to pay funding, **short positions will receive much more fundings per unit of position**
* If the long positions are to receive funding, **long positions will receive less fundings per unit of position**

In a market scenario where **price rapidly goes down,** totalShortPositionOI is likely to be much larger than totalLongPositionOI as liquidity in Oyster AMM is holding most of the long position until LP's remove those liquidity

* If the short positions need to pay funding, **long positions will receive more fundings per unit of position**
* If the short positions are to receive funding, **short positions will receive less fundings per unit of position**


# Liquidations

When the margin supports a trading position that falls below its **maintenance margin requirement,** that position is subject to **liquidation.**

Liquidation in Oyster AMM is performed primarily via the **taking-over approach,** where the liquidator takes over the target trading position along with the remaining margins and tops up the margin to meet the initial margin requirement. Effectively, the remaining margin of the trading position to be taken over is a potential profit for the liquidator—that is if the liquidator can adequately manage the risk.

In addition, Oyster AMM also allows a liquidation mechanism where trading positions failing the maintenance margin requirement are forced to trade against the Oyster AMM directly to close the position. In this approach, the trading fee is also charged, including the dynamic penalty fee detailed in Security section. If the position has margin balanced remaining after the forced close, these margins **would go to the insurance fund of this pair.**

Both approaches support **partial liquidation,** where the initiator specifies the amount of position to be taken over or forcibly closed. In this way, a big bankrupted position can be taken over by multiple liquidators to improve the stability of the overall design.

In both approaches, there is chance that the target position is bankrupted. In that case, **insurance fund of this pair is firstly used to fill the gap if possible. If the insurance fund is not enough to cover the loss, the loss is socialized to all opposite positions, that is the profiting positions are taxed to cover the loss.** Social loss per LONG/SHORT is tracked by longSocialLossIndex and shortSocialLossIndex.


# Liquidity (LP)

## Concentrated Liquidity

To boost capital eﬀiciency, Oyster AMM employs the **concentrated liquidity approach.** Like concentrated liquidity for the spot market, each liquidity in Oyster AMM only supports trading in a specific price range.

LPs are only required to specify the width of the price range instead of the lower and upper price of the range.&#x20;

## Price Range and Capital Efficiency Boost

The most common definition of capital eﬀiciency is to compare the value of assets required for the same trade size and the slippage.&#x20;

$$
CapitalEfficiencyBoost = \frac{x \cdot P\_c \cdot 2}{
x\_{\text{virtual}} \cdot P\_c \cdot (\alpha \cdot (1 + r\_i) - \sqrt{\alpha}) \cdot {\frac{\sqrt\alpha - 1}{\sqrt\alpha}}
} = \frac{2}{
\left( \sqrt{\alpha} \cdot (1 + r\_i) - 1 \right) \cdot \left({{\sqrt\alpha}-1} \right)
}
$$

<figure><img src="/files/z8UldSUHFQxSbpM8EElW" alt=""><figcaption></figcaption></figure>

The LP continues to receive fee income while the price is in the range.

If the AMM price goes out of the price range of concentrated liquidity, anyone could initiate the removal of that liquidity and convert that into a trading position, which goes to the account of the original owner of the concentrated liquidity. An execution fee is paid to the initiator of the removal transaction to compensate for the gas cost. Please refer to the [Pair Specification](https://docs.synfutures.com/docs/User-Guide/Pair-Specifications) section.

## Net position and margin requirement

When adding liquidity to Oyster AMM, a long position is created for the liquidity, and an offset short position is created for the LP. The sum of these two positions is the net position of the LP.

As the AMM price **deviates** from the price when concentrated liquidity is created, that liquidity would inherently have **an implied net position**, which can be calculated from the equation below. Each concentrated liquidity will always meet the margin requirement for the implied net position within its chosen price range. LPs are allowed to remove the liquidity from their concentrated liquidity anytime, and that is converted into a trading position the size of the implied net position and the remaining margin, along with any fees earned. After concentrated liquidity is removed and converted, the LP can then manage the trading position accordingly.

$$
NetPosition(P) = x\_{\text{virtual}} \cdot \left( \sqrt{\frac{P\_c}{P}}-1 \right), \text{ if } P \in \[P\_a, P\_b]
$$

Oyster AMM mandates that the liquidity maintains a sufficient margin to meet the initial margin requirement for the resulting net position at both price range boundaries. Please refer to the [whitepaper](https://www.synfutures.com/v3-whitepaper.pdf) for an in-depth discussion.

Relationship between supplied margin and liquidity provided.

$$
x\_{real} = \frac{M}{P\_c \cdot (\alpha \cdot (1 + r\_i) - \sqrt{a})}
$$


# Protocol Parameters


# Base Network

* Admissible margin tokens: **WETH, USDC**
* Allowed IMR(MMR): **3%(2%), 10%(4%), 10%(7.5%), 20%(7.5%)**
* Allowed Oracle Types: **Chainlink, DexV2, Emerging**
* Limit Order Tick Spacing: **5**


# Pair Specifications


# Base Network

## Current Pairs

### BTC-USDC-CHAINLINK

* IMR: **3%**
* MMR: **2%**
* Market Order Trading Fee: **0.05%**
* Limit Order Fee Rebate: **0%**
* Execution Fee: **0.02 USDC**
* Minimum Market Trade Value: **166.66 USDC**
* Minimum Limit Order Value: **333 USDC**
* Realized Profit Withdrawal Throttle: **25,000 USDC**
* Max Realized Profit Withdrawal Waiting Time: **24 hours**

### ETH-USDC-CHAINLINK

* IMR: **3%**
* MMR: **2%**
* Market Order Trading Fee: **0.05%**
* Limit Order Fee Rebate: **0%**
* Execution Fee: **0.02 USDC**
* Minimum Market Trade Value: **166.66 USDC**
* Minimum Limit Order Value: **333 USDC**
* Realized Profit Withdrawal Throttle: **25,000 USDC**
* Max Realized Profit Withdrawal Waiting Time: **24 hours**

## Community Listing

{% hint style="danger" %}
Community listing asset pairs often exhibit higher volatility and risk. Users are advised to conduct thorough research and trade at their own discretion when engaging with these listings.
{% endhint %}

{% hint style="warning" %}
In the event a community listing pair experiences insufficient liquidity or trading activity to maintain an efficient price discovery mechanism, SynFutures reserves the right to delist that trading pair.

Effective July 1, 2024, any decisions to delist a community listing pair will be announced to users via the official SynFutures Discord channel 24 hours in advance. Upon delisting, all open positions for the affected pair will be settled based on the prevailing or fair market rate at that time. Subsequently, the remaining balance for each user's settled positions will be credited back to their respective SynFutures accounts.
{% endhint %}

* IMR: **10%**
* MMR: **7.5%**
* Market Order Trading Fee: **0.1%**
* Limit Order Fee Rebate: **0.05%**
* Execution Fee: **0.0001 WETH**
* Minimum Market Trade Value: **0.02 WETH**
* Minimum Limit Order Value: **0.04 WETH**
* Realized Profit Withdrawal Throttle: **12.5 WETH**
* Max Realized Profit Withdrawal Waiting Time: **24 hours**


# Security

##


# Smoothed Spot Index Price

The raw spot price fetched from the underlying oracle **is not used directly but undergoes a specific exponential moving average (EMA) method to smooth the fluctuation.** This ensures that the spot index price cannot be easily **manipulated for the market's stability,** as the fluctuation directly impacts mark price, which determines the safety of all positions.&#x20;

<figure><img src="/files/schsjXJrWCj6sg79BtGf" alt=""><figcaption></figcaption></figure>


# Dynamic Penalty Fees

{% hint style="warning" %}
Under the current contract settings, triggering a penalty fee will result in the transaction being reverted.
{% endhint %}

In a functional derivatives market, it is common for Pfair to deviate from Pspot depending on the liquidity and market situation. However, excessive deviation and unreasonable trading behavior should be discouraged through economic means. The definition of Pmark reflects this principle.

**To protect our LPs and limit order makers while not impacting normal trading behavior,** Oyster AMM employs a stability penalty mechanism. When a trade results in a **higher deviation of fair price to mark price than before the trade,** a stability penalty will be charged based on the following, in addition to the normal trading fees paid to **LPs or limit order makers.**

$$
D(P\_{\text{fair}}, P\_{\text{mark}}) = \frac{\max(P\_{\text{fair}}, P\_{\text{mark}})}{\min(P\_{\text{fair}}, P\_{\text{mark}})} - 1
$$

$$
StabilityPenaltyRatio(P\_{\text{fair}}) = a \cdot D^3 + b \cdot D^2 + c \cdot D + d
$$

Trades that result in deviation less than MMR would have no stability penalty. Trades that result in deviation above MMR would have an increasing stability penalty ratio.


# Bug Bounty

The SynFutures Bug Bounty Program is dedicated to enhancing the security of our websites and applications.&#x20;

To learn more about the program's rules of participation, accepted vulnerabilities, and payout details, please visit <https://hackenproof.com/programs/synfutures-dapp>.

{% hint style="info" %}
SynFutures retains the right to determine the malicious action against the protocol's smart contract and may require extra step of user verification until the event has been resolved.
{% endhint %}

<br>


# Audit

The Audit Report of SynFutures V3 performed by Quantstamp can be found [here](https://www.synfutures.com/Quantstamp-Audit-Report-SynFuturesV3.pdf).


# Perp Launchpad

Unlike traditional launchpads that focus on spot markets, SynFutures Perp Launchpad pioneers the onchain derivatives space by supporting coin margin perp markets with one single token.


# Overview

## What is SynFutures Perp Launchpad?

The SynFutures Perp Launchpad allows projects to launch perpetual futures markets for any native asset on Base. Unlike traditional launchpads that focus on spot markets, our Perp Launchpad pioneers the onchain derivatives space by supporting coin margin perp markets with single token concentrated liquidity, facilitating liquidity optimization to generate consistent yields while managing underlying risks.

## What are the benefits for projects?

Unlock New Trading Opportunities: Enable leveraged speculation and hedging for your token holders.

Access a Vast User Base: Tap into an expansive community behind one of the top perp DEXs in the market.

Increase Visibility and Liquidity: List your token among diverse and trending trading pairs.

## What are the benefits for traders?

Trade Beyond the Ordinary: Be the first to trade new pairs not listed anywhere else.

Leverage Opportunities: Long or short trending emerging tokens, memecoins, and other longtail assets.

Unique Profit Opportunities: Access unique onchain arbitrage opportunities and trading incentives for new listings.


# FAQ

### Are there any fees or charges associated with the Launchpad?

Currently, during our launch promotional period, you will not be charged any fees.

### How will the funds deposited into the Launchpad be used?

The assets in the Launchpad will be utilized for trading corresponding asset pairs and for liquidity provision on SynFutures.

### How do I deposit funds into and withdraw funds from the Launchpad?

Depositing and withdrawing from the Launchpad is a straightforward process. Start by visiting the Launchpad page at <https://oyster.synfutures.com/#/launchpad/base/> to view all available projects. Select a specific project to access its details. On the details page, simply enter the amount you wish to deposit or withdraw in the designated field on the right side.

Please note that deposits must be made directly from your wallet and cannot be transferred from your SynFutures account.

### How long does it typically take for me to receive my withdrawal from the Launchpad?

Withdrawals from the Launchpad are usually processed immediately if the idle funds can cover the requested amount. If not, your withdrawal will be marked as "Pending", which may take up to 24 hours to process. In extreme market conditions, this timeframe may be extended based on the size of your withdrawal relative to the overall deposit and market liquidity.

### What risks should I be aware of?

Derivatives trading carries inherent risks, and you could potentially lose your entire principal investment. Although professional strategies and risk management practices are employed, market volatility can still affect your returns. We recommend investing only what you can afford to lose and staying informed about the risks involved in DeFi and derivatives trading.

### Can I deposit into the Launchpad using Externally Owned Accounts (EOAs) and smart contract wallets?

Yes, you can deposit into the Launchpad using an EOA (such as a regular cryptocurrency wallet) or a smart contract wallet. Both wallets are supported and can be used to participate in the Launchpad.

### Is the Launchpad deployed on Base only?

Currently, the Perp Launchpad is deployed and operating solely on Base. However, there are plans to expand the Perp Launchpad to other networks at a later stage.


# Legal Disclaimer

#### General Notice

SynFutures Perp Launchpad (the “Launchpad”) provides a platform for decentralized application (DApp) developers to launch new projects and for users to participate in these projects by engaging the perpetual markets. The information provided on the Launchpad's website and through its services is for general informational purposes only and should not be considered financial, legal, or investment advice.

#### Risk Acknowledgment

Cryptocurrency investments are inherently risky and subject to market fluctuations. The value of tokens is highly volatile, and investors may lose all or a substantial portion of their investment. Users should conduct their own research, assess their risk tolerance, and consult a financial advisor before making any investment decisions.

#### No Guarantee of Success

The Launchpad does not guarantee the success of any project or the performance of any token issued through its platform. The success of blockchain projects and the utility of their tokens can be affected by a multitude of factors beyond our control.

#### Limitation of Liability

The Launchpad, its affiliates, and its service providers will not be liable for any loss or damage arising from your use of the platform, including, but not limited to, any losses, damages, or claims arising from: (a) user error, such as forgotten passwords or incorrectly construed smart contracts; (b) server failure or data loss; (c) unauthorized access or activities by third parties, including the use of viruses, phishing, brute-forcing, or other means of attack against the platform or cryptocurrency wallets.

#### Amendments

This disclaimer is subject to change at any time without notice. It is the user's responsibility to review it regularly to stay informed of any changes.


# VIP Fee Tier

SynFutures offers tiered fee discounts on select pairs based on monthly taker volume. Discounts are available to all traders, with the discount amount determined by the taker volume threshold achieved.

See the chart below:

| Tier    | Taker Volume (Monthly) | Taker (bps) | Maker (bps) |
| ------- | ---------------------- | ----------- | ----------- |
| Regular | < $1M                  | 5           | 0           |
| VIP 1   | ≥ $1M                  | 4.5         | 0           |
| VIP 2   | ≥ $10M                 | 4           | 0           |
| VIP 3   | ≥ $50M                 | 3           | -0.5        |
| VIP 4   | ≥ $150M                | 2           | -1          |

Fee tier discounts are available on select asset pairs:

* BTC-USDC-PERP
* ETH-USDC-PERP

> Example: If a trader conducts $100 million in taker volume within the BTC-USDC-PERP pair on SynFutures during the calendar month, the discounted fee will amount to a 20,000 USDC ($100 million \* 0.02%).

### VIP Discount Distribution

Discounts will be distributed on the 20th of the sequential month, paid out in margin tokens. If the address is a contract, the discount will be distributed to the last operator.

{% hint style="info" %}
Only taker volume qualifies for discounts. Maker activity is not included. Changes to the receiving wallet, including those caused by hacks or other malicious activity, will not be acknowledged by SynFutures.<br>
{% endhint %}


# FAQ For Perp

### **How does the single-token concentrated liquidity model in SynFutures V3 improve capital efficiency in derivatives trading?**

Capital efficiency is greatly improved by only providing liquidity to a certain price range. Please refer to the [Price Range and Capital Efficiency Boost](https://docs.synfutures.com/oyster-amm/earn-lp#price-range-and-capital-efficiency-boost) section.

### **How are the margin requirements in SynFutures V3's concentrated liquidity model contrasted with traditional two-sided spot liquidity?**

Oyster AMM is a model built for derivatives and, by design, only considers a margin token, while spot liquidity always has tokens as inventory instead of margin.

### **What protections does SynFutures V3's Oyster AMM offer against significant price manipulation and flash loan attacks?**

Oyster AMM uses a stabilized mark price mechanism. Please refer to the [Smoothed Spot Index Price ](/perp-trading/security/smoothed-spot-index-price)section.

The system discourages price manipulation by imposing penalties for significant deviations between trade and mark prices. Please refer to the [Dynamic Penalty Fees](/perp-trading/security/dynamic-penalty-fees) section.

### **Could you describe the dynamic penalty fee system's role in SynFutures V3 and its influence on market stability?**

When a trade results in a higher deviation of fair price to mark price than before the trade, a stability penalty will be charged in addition to the normal trading fees paid to LPs or limit order makers.

Details are in the [Security](/perp-trading/security/dynamic-penalty-fees) section.

### **In what manner does SynFutures V3's exponential moving average process aid in mark price stabilization?**

The raw spot price fetched from the underlying oracle is not used directly but undergoes a specific exponential moving average (EMA) method to smooth the fluctuation. This ensures that the spot index price cannot be easily manipulated for the market's stability, as the fluctuation directly impacts mark price, which determines the safety of all positions.

Details are in the [Security](/perp-trading/security/smoothed-spot-index-price) section.

### **Please explain the integration of the liquidation process with SynFutures V3's stabilization mechanisms.**

Liquidation is based on **mark prices** instead of traded prices and thus resists manipulation within the protocol.

Stabilization mechanisms employed in the protocol also make it resistant to manipulation in the spot market.

### **What impact does the liquidation mechanism have on the overall market liquidity of SynFutures v3?**

The taking-over approach minimizes market liquidity as it is a position transfer instead of a trade.

Forced closure would consume market liquidity as the position to be liquidated is forced to trade with Oyster AMM.

### **What function do exponential moving averages serve in SynFutures V3's risk management framework?**

It is the main stabilization mechanism for the spot index prices and thus also protects mark prices from manipulation.

### **How does SynFutures V3's Oyster AMM liquidity paradigm differ from liquidity systems in its previous versions?**

The introduction of concentrated liquidity hugely increases the capital efficiency of passive liquidity provided

The unified liquidity with limit orders also opens up the possibility of traditional market making and greatly reduces taker slippage, thus improving the taker's trading experience.

### **What unique benefits does the on-chain order book provide in SynFutures V3 compared to off-chain alternatives?**

No keeper is required for the entire order-placing and order-matching process.

### **Could you discuss the rationale behind implementing native irreversible limit orders in SynFutures v3?**

* Users are forced to use extremely narrow-ranged concentrated liquidity to simulate the functionality of a limit order.
* Concentrated liquidity is fundamentally different from a limit order.
* By introducing a native limit order, Oyster AMM's concentrated liquidity implementation is hugely simplified.


# Trade

### Why am I receiving less funding fees than I should have according to the funding fee rate I see on your website?

The "Est. 1H Funding'' displayed on our website represents the estimated funding fee for the paying side only, and it’s updated in real time. However, by hovering your mouse over it, you can also see the funding fee for the receiving side. It's important to understand that the funding fee calculation takes into account the total open interest (OI) of both long and short positions, excluding liquidity.  In short, the side that pays the funding is expected to pay at the fee rate displayed, but the side that receives funding may receive more or less depending on the position imbalance. Therefore, it is possible to receive a funding fee that differs from the rate shown on the website. For more detailed information on how funding fees are calculated, please refer to the documentation provided \[[here](https://synfutures.gitbook.io/synfutures/oyster-amm/funding-for-perpetual-futures)].

<figure><img src="/files/ALP7rMxzmYFsfjw6H3aB" alt=""><figcaption></figcaption></figure>

### What are the fees associated with trading on SynFutures?

Information regarding the fees associated with trading on SynFutures can be found \[[here](https://docs.synfutures.com/oyster-amm/pair-specifications)]. Additionally, for specific trading pairs, you can access the fee details in the info section of the respective trading pair on our website.

<figure><img src="/files/Dq9lpLNwVfEKuGRC0c45" alt=""><figcaption></figcaption></figure>

### Why does my position get liquidated before reaching the liquidation price?

Positions can be liquidated before reaching the specified liquidation price due to social loss. Social loss can occur when a trading position falls below its maintenance margin requirement and is liquidated, and the insurance fund is insufficient to cover the resulting loss. In such cases, the deficit is distributed among all opposite positions, taxing the profiting positions to cover it.

To monitor any potential social loss, you can review it through the unrealized PnL breakdown associated with your position. Keep in mind that highly volatile assets, like community tokens, are more susceptible to social loss. We recommend considering this factor when engaging in trading activities.

For more detailed information on social loss, please refer to \[[here](/perp-trading/liquidations)].

### What’s the difference between Fair Price and Mark Price?

On SynFutures, Fair Price and Mark Price are two distinct prices used to ensure accurate, fair, and secure trading. Here’s how they differ:

Fair Price

* The AMM’s mid price, reflecting the trading pair’s market price.
* Used for: Setting funding rates to balance long and short positions.

Mark Price

* Mark price is based on spot index price but also incorporates a daily interest component of the underlying trading pair. It determines the unrealized profit and loss and margin requirements of all positions in a pair. This price also determines the initial margin requirement to open a position.&#x20;
* Used for:
  * Calculating unrealized profit and loss (PnL).
  * Determining margin requirements and initial margin for opening positions.
  * Triggering liquidations when margin is too low.

### Why would I see a "Fill" button next to my order?

Occasionally, there may be instances where your order is taken but has not yet been filled and converted into a position. In such cases, you will see a "Fill" button next to your order. We recommend that you click the "Fill" button manually to complete the order and avoid any delays. Please note that once an order is taken, it cannot be canceled, even if it hasn't been filled yet.

### What can I do when I am unable to close my position due to "Transaction will be reverted for trades causing significant deviation from the mark price"?

You may be unable to close your position because of a significant difference between the fair price and the mark price. In these situations, you might consider closing your position gradually or waiting until the fair price is closer to the mark price.

### What should I do when I encounter the "Transaction missed deadline" error message?

When you encounter the "Transaction missed deadline" error message, it is usually due to a mismatch between your computer's local time and the accurate time. To address this issue, we suggest adjusting your computer's local time to ensure it is synchronized correctly.

If adjusting the local time doesn't resolve the problem, you can try clicking the setting icon next to the Limit Order and reset the deadline.

<figure><img src="/files/VWWUUFsYh4k0tRP7stlj" alt=""><figcaption></figcaption></figure>

### What is an Emerging Oracle?

An Emerging Oracle is a proprietary solution from SynFutures used when external oracles like Chainlink and Pyth are unavailable. In these cases, we deploy a contract to upload price data. Please note that this data may not have the same level of verification as that from established external oracles.


# Liquidity (LP)

### Why am I seeing less value locked in my liquidity portfolio than the initial amount that I provided?&#x20;

The reduced value is likely due to impermanent loss (IL). This occurs when the token price changes from your deposit price, with larger price changes causing greater losses. The value locked reflects this unrealized loss, a common risk in liquidity provision. For more details, see our[ blog post on impermanent loss](https://knowledgehub.synfutures.com/impermanent-loss-and-the-potential-risks-and-benefits-of-being-a-liquidity-provider-lp/). Assess potential risks carefully before participating.

### Why would I suffer a loss from providing liquidity even when the liquidity is still in range when I remove it?&#x20;

This loss stems from impermanent loss (IL). Even within your set range, a price shift from your deposit point creates a loss upon removal. Narrower ranges boost capital efficiency but heighten sensitivity to price changes, amplifying potential losses. Learn more in our[ blog post on impermanent loss](https://knowledgehub.synfutures.com/impermanent-loss-and-the-potential-risks-and-benefits-of-being-a-liquidity-provider-lp/). Understand this inherent risk when providing liquidity.

### What would happen when my liquidity goes out of the price range I set?

When your liquidity goes out of the price range you set, it will be automatically converted into a net position. The direction of the net position is determined based on which end of the price range it exceeds.

For example, let's consider the WETH/USDB pair with a price range set at 2000 - 4000. If the fair price of WETH reaches 4000 or goes above it, your liquidity will be converted into a short position. On the other hand, if the fair price hits 2000 or falls below it, your liquidity will be converted into a long position.

### Why wasn't the fund returned to my account after I removed my liquidity, even though it was still within the price range when I removed it?

When you remove the liquidity you provided, it is automatically converted into a net position. In order to retrieve the margin, it is advisable to close the position in trade immediately after the liquidity removal.

### Why wasn't the fund returned to my account after my liquidity was removed due to going out of range?

When the liquidity you provide goes beyond the price range you set, it is automatically converted into a net position. In the event that the liquidation price is reached, your position will be liquidated. In such cases, it's important to note that you will not receive the margin back in your account.


# Overview

$F is the token at the center of the SynFutures ecosystem, designed to support community participation and long-term ecosystem growth. It helps align users, contributors, and the Foundation as SynFutures continues to develop.

* **Ticker:** $F
* **Maximum Supply:** 10,000,000,000
* **Token Standard:** ERC-20
* **Contract Addresses**

<table><thead><tr><th width="259.86328125">Network</th><th>Contract Address</th></tr></thead><tbody><tr><td>ETH Mainnet</td><td>0x6e15A54B5EcAc17e58daDedDbe8506a7560252F9</td></tr><tr><td>Base</td><td>0x2c24497d4086490e7ead87cc12597fb50c2e6ed6</td></tr><tr><td>BSC</td><td>0xc9cCbd76c2353e593Cc975F13295e8289d04D3Bb</td></tr></tbody></table>


# $F Token Contract Addresses

| Network     | Contract Address                           |
| ----------- | ------------------------------------------ |
| ETH Mainnet | 0x6e15A54B5EcAc17e58daDedDbe8506a7560252F9 |
| Base        | 0x2c24497d4086490e7ead87cc12597fb50c2e6ed6 |
| BSC         | 0xc9cCbd76c2353e593Cc975F13295e8289d04D3Bb |

For more details on the $F token and the SynFutures Foundation, please refer to [this article](https://knowledgehub.synfutures.com/introducing-synfutures-foundation-and-the-f-token/).


# $F Tokenomics

The F token allocation is as follows:<br>

<figure><img src="/files/uNFvMO1mHr6ca5kyMBuA" alt=""><figcaption></figcaption></figure>

**1. Community — 28.5% | 2,850,000,000 F**

The community allocation will help reward community members, users, and contributors. It will also fuel the expansion and adoption of the SynFutures ecosystem.

**1.1. Airdrop — 7.5% | 750,000,000 F**

SynFutures will airdrop 7.5% of the total supply to users who’ve consistently engaged with SynFutures protocol v1, v2, and v3.&#x20;

**1.2. Ecosystem — 20.5% | 2,050,000,000 F**

Ecosystem growth is crucial to the success of SynFutures. The Foundation has thus allocated 20.5% of the F supply toward expanding SynFutures. Some of the allocation channels include:

* Incentive programs to onboard new users.
* Collaboration with strong partners.
* Grants and funding for third-party developers building on the protocol.
* Community initiatives, hackathons, and other engagement programs.

**1.3. Liquidity Campaigns — 0.5% | 50,000,000 F**

This allocation will help accelerate F token listing and trading activities on multiple initial and follow-on exchange venues. It’ll also ensure wider adoption of F tokens across multiple communities.

**2. Backers & Advisors — 23.5% | 2,350,000,000 F**

SynFutures has reserved a 23.5% allocation for backers and advisors who contribute funds, expertise, and other valuable support to the development of SynFutures.

**3. Foundation Treasury — 25.0% | 2,500,000,000 F**

The SynFutures Foundation Treasury, constituting 25% of the total F supply, will use the funds to support the long-term stability and development of SynFutures.

The Foundation will leverage its treasury allocation to ensure long-term benefits for its users and communities, with the allocation channels including, but not limited to, the following:

* Deploying tokens for strategic partnerships and business development.
* Covering operational expenses and new initiatives.
* Ensuring long-term sustainable development across cycles.

**4. Core Contributors — 15.0% | 1,500,000,000 F**

Since day 1, many core contributors have tirelessly focused on SynFutures’ security, engineering, product, infrastructure, growth, and operations.

The 15% allocation will go toward rewarding their past efforts and ensuring future alignment of incentives for long-term development.

**5. Protocol Development — 5.0% | 500,000,000 F**

It’s still early days for DeFi and there’s much more to build, add, and improve.

5% of the total supply allocated toward Protocol Development will ensure adequate resources to fund ongoing R\&D, engineering, and implementation of new features and upgrades.

Some of the potential allocation channels are as follows:

* Competitive salaries to attract and retain top technical talent.
* Funding for protocol audits, security improvements, and infrastructure scaling.
* Research into novel blockchain technologies, AMMs, and consensus mechanisms.
* Integrations with complementary projects and platforms.

**6. Liquidity — 3.0% | 300,000,000 F**

Liquidity allocation will ensure sufficient liquidity for the F token on exchanges, facilitating price discovery and seamless trading.

Here’s how SynFutures aims to utilize the funds:

* Providing initial liquidity for token trading pairs on exchanges.
* Maintaining healthy token liquidity through various incentive programs.
* Supporting token listings on new exchanges as the protocol expands globally.

For more details on the $F token and the SynFutures Foundation, please refer to [this article](https://knowledgehub.synfutures.com/introducing-synfutures-foundation-and-the-f-token/).


# $F Staking

Staking $F gives holders access to a range of utilities and benefits within the SynFutures ecosystem. These benefits are designed to reward long-term participation and give $F holders a more active role in the protocol’s growth.

Users who want to participate in the SynFutures ecosystem can buy $F on supported exchanges and [stake](https://synfutures.foundation/) their tokens to access available benefits.

### Disclaimer

This content is for informational purposes only and does not constitute financial, investment, legal, or tax advice. Nothing on this page should be interpreted as a recommendation to buy, sell, stake, or hold $F or any other digital asset.

Users should conduct their own research and carefully assess the risks before participating in any token-related activity. Digital assets are volatile and may result in loss of funds.


# Aggregator

SynFutures Spot Aggregator is an advanced onchain DEX aggregator designed to provide users with the best swap rates. Unlike traditional aggregators that rely on offchain calculations, it employs an onchain pricing model to source liquidity from multiple DEX engines, supporting multiple hops and split pools, ensuring accurate and real-time pricing for optimal trade execution.

### Onchain Pricing

The onchain pricing structure allows for precise and instantaneous price discovery, outperforming offchain solutions.

### Comprehensive Liquidity Integration

Liquidity from various protocols is aggregated and presented in an intuitive orderbook format, showcasing liquidity distribution across different price ranges.

### Wide Token Support

Supports exchanges for a wide range of tokens on the Base network. Pools containing anchor tokens valued over $10,000 are utilized as liquidity sources. The current anchor tokens include USDC, USDbC, WETH, cbBTC, and Virtual.

### Protocol Compatibility

Initially integrated with several protocols, including Uniswap V2/V3, Aerodrome V2/V3, PancakeSwap V2/V3, Sushiswap V3, and Alien Base. Growing.

### No Additional Fees

SynFutures does not charge any additional fees to users during the promotion period.

### Future Developments

SynFutures is set to launch its own Spot DEX, enhancing the trading experience further. Stay tuned for updates!<br>


# Supported Exchanges

SynFutures integrates with a variety of exchanges to enhance liquidity sources and ensure swaps are executed at the best rates:

* Uniswap V3
* Uniswap V2
* Pancakeswap V3
* Pancakeswap V2
* Aerodrome V3
* Aerodrome V2
* Sushiswap V3
* Alien Base
* More to come…


# Audit

The Audit Report of SynFutures Spot Aggregator performed by PeckShield can be found [here](https://www.synfutures.com/PeckShield-Audit-Report-Oyster-v1.0.pdf).


# Video Tutorial

## How to trade on SynFutures V3

{% embed url="<https://www.youtube.com/embed/QS8nWmQHmlo?si=0wfVmjHux8x17n5J>" %}

## How to provide liquidity on SynFutures V3

{% embed url="<https://www.youtube.com/embed/BRnHbZjOWK0?si=WFPwT_DVtL1VilHd>" %}


# Written Tutorial

## Connect Wallet

SynFutures currently supports five major wallets, and more are likely to be added in the future. To connect your wallet, complete the following steps:

* Click the ‘Connect Wallet’ button on the top right corner of the page.
* Choose the wallet of your choice.
* The next step will vary slightly depending on the wallet. But the general process is to unlock the wallet with a password and click connect.
* For MetaMask, enter the password when prompted.
* Click ‘Unlock’, 'Next', and ‘Connect'.

<div data-full-width="true"><figure><img src="/files/LQwMpaeyVn9Ij2PUHUmT" alt=""><figcaption></figcaption></figure></div>

## Change network

* You can find the network selection button to the left of your wallet address.
* Click on the drop-down arrow and choose the network of your choice.
* If your wallet is set to a different network, you will be prompted to switch the network on your wallet before placing a trade.&#x20;

<div data-full-width="true"><figure><img src="/files/uNalpQCnJpGQ51EnX2Tl" alt=""><figcaption></figcaption></figure></div>

## Navigation

The site's main page is designed to provide a clean interface and a smooth user experience for traders and LPs

* Market: Lists out all the pairs that are currently available for trade. The USDs Margin tab lists assets paired against a stablecoin, and the Coin-Margin tab lists assets paired against other cryptocurrencies.
* Trade: Allows traders to place market and limit orders, view open positions, open orders, trade history, order history, and funding history.
* Earn: Allows LPs to provide liquidity for different pairs. Available pairs can be sorted by APY, 24H volume, or TVL. New pairs can also be created using the 'Create Pool' option.
* Portfolio: Provides a broad overview of the user's portfolio and previous activity on SynFutures.
* Trading GP: Provides a comprehensive overview of the Grand Prix trading competition with 500,000 USDC prize pool.
* Odyssey: Provides details about the Oyster Odyssey campaign.
* More: SynFutures whitepaper, documentation, academy, and FAQ can all be accessed by clicking 'More.'&#x20;

<div data-full-width="true"><figure><img src="/files/OFn2D02yzbH9GSka6aK2" alt=""><figcaption></figcaption></figure></div>

## Trade

* Choose the right pair
  * After making the deposit, go to the Trade section. You’ll notice a trading pair mentioned prominently right below the SynFutures logo. Click on it to see all the available trading pair options. The dropdown box will show the available pairs, the expiry date or ‘PERP’ for perpetual futures, the current price, and the 24H trading volume.
* Place a market order
  * On the right side of the page is the trading section where users can place either a market order or a limit order. This section is set to market by default. To place a market order, a user needs to do the following.
    * Choose whether to buy or sell.
    * Select the amount for trade. It can be denominated in quote asset or base asset.
    * Choose the required leverage
    * Once completed, check the following details
      * Margin Required
      * Limit Price
      * Estimated Trade Value
      * Price Impact
      * Trading fee
    * After you have verified the details, click the ‘Buy’ or ‘Sell’ option and approve it in the wallet
    * Once the transaction is confirmed, the trade will appear as an open position, which you can monitor by scrolling down&#x20;

<figure><img src="/files/RH4uHubcytfwtc08jCL8" alt=""><figcaption></figcaption></figure>

* Place a limit order
  * The limit order process is the same as the market order process but with one extra step. The user must enter the limit price in USDC before placing the order. The order will only get executed if the market reaches the limit price.
    * Click the ‘Limit’ tab
    * Choose whether to buy or sell.
    * Select the amount for trade.
    * Enter the limit price in base asset.
    * Choose the size of your trade and the required leverage
    * Since limit orders increase liquidity in the market, traders who place limit orders are eligible for a fee rebate if the order is filled. You can check all the details right below the leverage slider
    * Click the ‘Buy’ or ‘Sell’ button and approve it in the wallet
    * An order placed but not filled can be found under the ‘Open Order’ section.&#x20;

<figure><img src="/files/dOITKQvMCwZ6a30D4z7Y" alt=""><figcaption></figcaption></figure>

* Adjust the margin of a position
  * SynFutures allows traders to increase or decrease the margin of an open position. To do this
    * Go to the Portfolio section and click on 'Position.'
    * All the positions that are currently active will be displayed in a list. Choose the position of your choice.
    * This will take you to the Trade section. Next to Margin, click on the 'Transfer' option.
    * A new transfer dialog box will open. Use 'Transfer In' if you want to increase your margin. Use 'Transfer Out' to decrease it.
    * Click 'Confirm' and approve the transaction in your wallet.
* Close Position
  * All the currently active positions can be seen in the position section. SynFutures has made it really simple to close an open position. A trader must find the open position, click ‘Close,’ and confirm it in the wallet. The position will close at the current market price, and the remaining margin will be added to the available balance.&#x20;

<figure><img src="/files/mBdm7FOJG5aj9sYMZa5Z" alt=""><figcaption></figcaption></figure>

## Earn

* Choose the right pair
  * Earn section display the list of all the available pools on SynFutures V3. The pools can be filtered by the base assets, which are USDB and WETH and they can be sorted by APY, 24H Volume, and TVL. Choose the pool of your choice through the list.
  * You can also create a new pool by clicking 'Create Pool' option on the right side of the screen.
* Add liquidity

  * Chose the pair of your choice and click 'Add Liquidity'.
  * This will take you to the liquidity page.
  * Enter the amount that you want to provide as liquidity
  * Select the price range using the slider.
    * Moving the slider left decreases the range, and the right increases it.
    * At the extreme left, the range is small. It provides high capital efficiency and APY rates but also increases the chance of liquidation
    * At the extreme right, the range is large. The chances of getting liquidated are small, but the APY rate is also low.
  * Below the slider is a green histogram showing the current liquidity level available at different ranges.
  * This section is also divided into two areas: Liquidation area and Impermanent Loss area. The red color represents the liquidation area beyond the selected range. If the price reaches this zone, the position faces liquidation risk. The green area represents active liquidity, as long as the price is within this range, liquidity will be active and the liquidity provider only faces the risk of impermanent loss.
  * The next section shows the capital efficiency boost of the selected range and the 'Removal Price' and 'Liquidation Price'.
    * Removal Price refers to the price at which LP's liquidity will get converted into a trading position.
    * Liquidation Price refers to the price at which that trading position will get liquidated.
  * Use these details to choose the range that you’re comfortable with and then click ‘Confirm.’
  * Confirm the transaction on your wallet.
  * You can see the new position under the ‘Liquidity’ section. It will show the total value locked, fees earned, and the liquidation price.

  <div data-full-width="false"><figure><img src="/files/qOCp2nCpo47q8NtIdMlI" alt=""><figcaption></figcaption></figure></div>
* Remove Liquidity
  * Removing liquidity is a two-step process. First,you need to close the liquidity position from the Earn section. This will convert your liquidity position into a trading position. Second, you need close the trading position.

    * Go to the Liquidity section.
    * Click 'Remove' on the position that you want to exit.
    * Click ‘Confirm'.
    * Confirm the transaction on the wallet.
    * Once that’s done, your liquidity gets converted into a trading position.
    * Click the ‘Manage’ position button to take you to the trading section.
    * Here you’ll see a new trading position created by SynFutures.
    * Click ‘Close’ on the position and confirm in your wallet.
    * That’s it. Your liquidity position is now closed and the amount will be transferred to your account balance.&#x20;

    <figure><img src="/files/TGqxOFxTYH7gmiwogPYP" alt=""><figcaption></figcaption></figure>

## Portfolio

* Deposit to account balance.

  * Once your wallet is connected, go to Portfolio.
  * Under Accounts, Click 'Deposit'.
  * The Deposit dialog box will show your wallet balance and an input box. Enter the amount you want to deposit.
  * Click 'Approve USDC to continue'. Approve the transaction in your wallet.
  * Once the approval is completed, click 'Confirm' and approve the transaction in your wallet.
  * After completing the transaction, the Total Value section will reflect the updated amount.&#x20;

  <figure><img src="/files/xw4Bxrz2U0yhaV36NBH9" alt=""><figcaption></figcaption></figure>
* Withdraw from your account balance

  * Withdrawing the account balance is just as easy as a few clicks. There is no lock-in period or large withdrawal fee. All you need to do is
  * Go to the Portfolio section.
  * Click ‘Withdraw'.
  * Choose the amount to withdraw.
  * Click the ‘Withdraw’ button on the dialog box.
  * Confirm the transaction on the wallet.
  * After a few seconds, your transaction will get confirmed and the amount withdrawn to your wallet.<br>

  <figure><img src="/files/FSm4GLH3JUCM1pNkTvnt" alt=""><figcaption></figcaption></figure>


# SDK

To further enhance the trading experience and streamline integrations, we released the V3 SDK. The SDK enables all partners equal access to essential developer tools and resources. Should you have any inquiries during the integration process, please don’t hesitate to contact the SynFutures team via [Discord](https://discord.com/invite/synfutures).

#### Perp Trading

* [NPM package](https://www.npmjs.com/package/@synfutures/sdks-perp)&#x20;
* [Github](https://github.com/SynFutures/sdks/tree/main/packages/perp)


# API

## Overview

The SynFutures Perp API, currently in beta release, facilitates access to comprehensive data pertaining to markets, trading pairs, funding rates, user portfolios, and related data within the SynFutures protocol. Please note that the API is not publicly available at this time. To gain access, an API key is required, which can be obtained by contacting our team.

API URL: <https://api.synfutures.com/v3/public/swagger-third-party#/>

### Authentication

Access to the API requires a valid API key. To obtain an API key, please contact our team on [Discord](https://discord.com/invite/synfutures) or [X](https://x.com/SynFuturesDefi) (former Twitter).

### API Documentation

For detailed API documentation, please refer to <https://api.synfutures.com/v3/public/swagger-third-party#/>.

### Rate Limiting

Each IP address is limited to 1,200 requests per minute. This limit may vary depending on the endpoint and overall system load. Exceeding the rate limit will result in a 429 (Too Many Requests) response.

\
\ <br>


# Smart Contract Addresses


# Base Network

{% hint style="info" %}
Users interact directly with the Gate contract, instrument contracts, and vault contracts. A comprehensive list of all contracts is provided below.
{% endhint %}

## Base Mainnet

<table><thead><tr><th width="280">Contract</th><th>Address</th></tr></thead><tbody><tr><td>Gate</td><td>0x208B443983D8BcC8578e9D86Db23FbA547071270</td></tr><tr><td>Config</td><td>0xB63902d38738e353f3f52AdD203C418A0bFEa172</td></tr><tr><td>Guardian</td><td>0xBe0F37274AdADb32441acDB74791de159B0BD87E</td></tr><tr><td>Observer</td><td>0xDb166a6E454d2a273Cd50CCD6420703564B2a830</td></tr><tr><td>Instrument</td><td></td></tr><tr><td>        BTC-USDC-LINK</td><td>0xec6c44e704eb1932ec5fe1e4aba58db6fee71460</td></tr><tr><td>        ETH-wrsETH-EMG</td><td>0x899194b4d8597cf8c74aa5e37f003d8b72cf373b</td></tr><tr><td>        ETH-DEGEN-LINK</td><td>0x75da1f73fa85ce885fd209e34d6d9334ecaff14f</td></tr><tr><td>        USDC-wcgUSD-EMG</td><td>0x1167525986013bbf615f68b74db6b76490d1c8b4</td></tr><tr><td>        ETH-USDC-LINK</td><td>0x04d72fb4803b4e02f14971e5bd092375eb330749</td></tr><tr><td>        MEW-USDC-LINK</td><td>0xb41303f0382dfe669a71f1f9c13146ff41e4b6dd</td></tr><tr><td>        DOGS-USDC-EMG</td><td>0x53e90a261979b559c21148753132b7a4543930ee</td></tr><tr><td>        PURR-WETH-EMG</td><td>0x3737b9c7251423a38a89548c284fc4358f12b0c2</td></tr><tr><td>        TRUMP-USDC-EMG</td><td>0x38c4c68172eba6f1d322230768d5cf3900ab4a81</td></tr><tr><td>        BTC-WETH-LINK</td><td>0x206a0a4fa891b9139770d70d33ba32c15f1c61df</td></tr><tr><td>        BTC-pumpBTC-EMG</td><td>0x200e09e1e6235732ac81d472cfa6b6833836fa6c</td></tr><tr><td>        SUI-USDC-EMG</td><td>0x46D7Ea885a76993Ff0549B87C9b0174324ae383B</td></tr><tr><td>        ETH-pumpBTC-EMG</td><td>0x7C83C8121B4D7774bf3a95B3dc8a4f89c9394ded</td></tr><tr><td>        GOAT-USDC-EMG</td><td>0x8065b6EA05A4082e2aBf36AF55aa796365610912</td></tr><tr><td>        USDC-LRDS-EMG</td><td>0xF8559F9F2c6e8e3e89A09DBEcbA039CdF3f9A568</td></tr><tr><td>        USDC-VIRTUAL-EMG</td><td>0x620D1D6453873a4E36570aD6dDcf850aD0cd3952</td></tr><tr><td>        USDC-ALB-EMG</td><td>0xb146f1e409d45862354bebc3acdfb31e0e1488dc</td></tr><tr><td>        ME-USDC-EMG</td><td>0x62d01059a2d92ddecc2b8bb775e95eccda4f4faa</td></tr><tr><td>        MOVE-USDC-EMG</td><td>0x9f824a6b5fc8b67de00bd854cb27ee8496a441db</td></tr><tr><td>        USDC-TRVL-EMG</td><td>0x32bdb0d18d6cc94d282e1c92e7423007e9a51425</td></tr><tr><td>        PENGU-USDC-EMG</td><td>0x55de64171513397770eb48a799e6f2670e6e0e2d</td></tr><tr><td>        ETH-TRVL-EMG</td><td>0x9c57b8b2b1c7960284549cb67d12c65abcdce0b5</td></tr><tr><td>        BTC-USR-EMG</td><td>0x62ba13b3e351b4379964de33437d0afd32ab18b0</td></tr><tr><td>        ETH-WELL-LINK</td><td>0x3dc806ca12cfd23c7f6b42b88e4a21f625f6ed9d</td></tr><tr><td>        AIXBT-USDC-EMG</td><td>0x53aafea033a392c4293e609607f3962cbb3fb569</td></tr><tr><td>Vault</td><td></td></tr><tr><td>        pumpBTC</td><td>0x90471F8c9c9A01a6A3feA20d17B9A083e271ceC6</td></tr><tr><td>        LRDS-LRDS/USDC</td><td>0x666F5036cE5Ed9893feaF928703d277b3CD9E997</td></tr><tr><td>        VIRTUAL-VIRTUAL/USDC</td><td>0xCDF9714E0E074c161a36aB0b2881Ca73af1ec11B</td></tr><tr><td>        ALB-ALB/USDC</td><td>0xB39c8375AbC3173f9843c8d8B943649728D1eA45</td></tr><tr><td>        USR-BTC/USR</td><td>0xFDc1bb2f117e59d2A5681345A1711EE2dd1CE4ba</td></tr><tr><td>        TRVL-ETH/TRVL</td><td>0x89C2E7c2472ce329906D823810111b3187661A50</td></tr><tr><td>        WELL-WELL/ETH</td><td>0xabc244a47Fd517d60E916f6Ac2A3862B4dDBdb3f</td></tr><tr><td>ChainlinkMarket</td><td>0x6926cC6875D3721c13325d301B4170A57F2C0b18</td></tr><tr><td>EmergingMarket</td><td>0x8F76920D741A6E2d324c9CC8878cd5a9371E81CD</td></tr><tr><td>PythMarket</td><td>0xBA2593A538df42f4673A5c72A444d70d6B91f3D9</td></tr><tr><td>DexV2Market</td><td>0xc3eC131979979baC0a9a355129671151CfA5725E</td></tr><tr><td>EmergingFeederFactory</td><td>0xE73d8117fdBD05aD7d632A3798c6925BFCdCF8AA</td></tr><tr><td>PythFeederFactory</td><td>0x0b54c57fe93B9d71c29Bd5d466bAa2117bbF4D1D</td></tr></tbody></table>


# Blast Network

{% hint style="info" %}
Users interact directly with the Gate contract and instrument contracts. A comprehensive list of all contracts is provided below.
{% endhint %}

## Blast Mainnet

<table><thead><tr><th width="279">Contract</th><th>Address</th></tr></thead><tbody><tr><td>Gate</td><td>0x6A372dBc1968f4a07cf2ce352f410962A972c257</td></tr><tr><td>Config</td><td>0x03f2E7452095a708ff19516eDe92F757adE2816c</td></tr><tr><td>Guardian</td><td>0xB85b77f32DBDb7e4895b288e70770C90E232C751</td></tr><tr><td>Observer</td><td>0x730D6aaD0DD58f5d5d25AfEbD13d5A2bf76aC194</td></tr><tr><td>Instrument</td><td>0x245bb4abB5c7f09b785E3483057C130a52FAbb7A</td></tr><tr><td>       USDC-USDB-PYTH</td><td>0xb0ceff252f18710a3315e735b5e26481840ad286</td></tr><tr><td>       BTC-USDB-PYTH</td><td>0x5430561b09c627264549fdb3a6154c34f5cabea7</td></tr><tr><td>       USDB-WETH-PYTH</td><td>0xeb9e8822142fc10c38faab7bb6c635d22eb20ff8</td></tr><tr><td>       STETH-WETH-PYTH</td><td>0x99660f7ade18a02f1f88f2bfc7a2515ceff9c9c6</td></tr><tr><td>       BTC-WETH-PYTH</td><td>0x0e1b878f5eddb7170b0a25ca63cb985291eb53d8</td></tr><tr><td>PythMarket</td><td>0x938FBB079CA6CF07943110F18984b53b3A07840f</td></tr><tr><td>DexV2Market</td><td>0x409204744A267Df16Ed2a96F7686EDe029e6fba7</td></tr><tr><td>EmergingMarket</td><td>0x29c4C77feDa2Bd90933fcFaEaF531612F51c3F26</td></tr><tr><td>Blast Point Operator</td><td>0xAB8F92daf3f6682C44AdBA3A27e6d397fBf1ed64</td></tr></tbody></table>

## Blast Sepolia

<table><thead><tr><th width="284">Contract</th><th>Address</th></tr></thead><tbody><tr><td>Gate</td><td>0xeBFaDdaF4b35cC58AD25D0e5fA94007D37b1e838</td></tr><tr><td>Config</td><td>0x990b8fe452002EFAc8779D257f066A6931f0a4c2</td></tr><tr><td>Guardian</td><td>0x715968669c65e80253bbec25157576756db8904e</td></tr><tr><td>Observer</td><td>0xF411AF7017b7BdFedDDA52207d0221426646E3C1</td></tr><tr><td>Instrument</td><td>0x2806d9ffBA7613E87a6C20089D43A25Fee1D8690</td></tr><tr><td>PythMarket</td><td>0x0a44EC81eA4Dd0E9b05AdBCA547366b654476C82</td></tr><tr><td>DexV2Market</td><td>0xC135174E646920CD12091487878a6B9E916Aa3d6</td></tr><tr><td>EmergingMarket</td><td>0x9Ba497f17C3FB98bfAd4c7C8C4546b55F01Ec92d</td></tr><tr><td>Blast Point Operator</td><td>0x878d1bef362460030C7052Be1444a6FED6532380</td></tr></tbody></table>


# Monad Network

{% hint style="info" %}
Users interact directly with the Gate contract, instrument contracts, and vault contracts. A comprehensive list of all contracts is provided below.
{% endhint %}

## Monad Testnet

<table><thead><tr><th width="280">Contract</th><th>Address</th></tr></thead><tbody><tr><td>Gate</td><td>0x034a4f1056A07680205B3eA493004c8ab8a08123</td></tr><tr><td>Config</td><td>0x3af980f4a4554C4610140f1f2611DC4fF3439db2</td></tr><tr><td>Guardian</td><td>0x727B0Fe01214fdfabBa7d85FfDF83884d244B837</td></tr><tr><td>Observer</td><td>0x5437a703EbD4B4748cA0AeB79166b66a9F5E4f81</td></tr><tr><td>Instrument</td><td></td></tr><tr><td>        MON-USDC-EMG</td><td></td></tr><tr><td>        USDC-DAK-EMG</td><td></td></tr><tr><td>        USDC-YAKI-EMG</td><td></td></tr><tr><td>        USDC-CHOG-EMG</td><td></td></tr><tr><td>ChainlinkMarket</td><td>0x5E63b810Fde2A2a191C0B2E76BaB62f253D3f606</td></tr><tr><td>EmergingMarket</td><td>0x30c94eD52F9589b2aE4E31bD6aE07Bee35874397</td></tr><tr><td>PythMarket</td><td>0xB63902d38738e353f3f52AdD203C418A0bFEa172</td></tr><tr><td>DexV2Market</td><td>0xB619865bd03C42c06FB5c7BdeD17DFcA0a7A5e7C</td></tr><tr><td>EmergingFeederFactory</td><td>0xAaeA67DFf0DA21a0B19C67eDfD8988A82480Ef8d</td></tr><tr><td>PythFeederFactory</td><td>0x9674e2F58485B987d7d537a059FB002aE1d44959</td></tr></tbody></table>


# White Paper

## SynFutures V3 White Paper

{% file src="/files/TwtpILXJagqsrMzGiVmk" %}

## MiCA

{% file src="/files/eKcBhGVYWdxyNJCHYPtI" %}


# Audit Report

## SynFutures V3 Audit Report

{% file src="/files/nONVlJdpp2ln6RBHHqEI" %}

## SynFutures V3 Upgrade Audit Report

{% file src="/files/LyBEjRX8EJFFZQsrVkmR" %}

## Aggregator Audit Report

{% file src="/files/fDqkAZb56mMRXwgoe5mJ" %}

## Governance Audit Report

{% file src="/files/UjYWTqZ6INn4yLUKKicR" %}


# Patent

## ON-CHAIN ROUTING

{% file src="/files/0EVvGprcQJG8giNtPJBP" %}


# Legacy Links

## V2

* dApp - <https://v2.synfutures.com>
* White paper - <https://www.synfutures.com/v2-whitepaper.pdf>
* Audit report - <https://www.synfutures.com/PeckShield-Audit-Report-SynFuturesV2-v1.0.pdf>

## V1

* dApp - <https://v1.synfutures.com>
* White paper - <https://www.synfutures.com/synfutures-whitepaper.pdf>
* Tech paper - <https://www.synfutures.com/synfutures-v1-techpaper.pdf>
* Audit report - <https://www.synfutures.com/peckshield-audit-report-synfutures-v1.1.pdf>


# Contacts

To stay safe, only rely on SynFutures’ official [Twitter/X](https://x.com/SynFuturesDefi) and [Discord](https://discord.com/invite/synfutures) channels for announcements, updates, and support. Korean users can also join the official Korean Telegram channel ([@synfutureskorean](https://t.me/synfutureskorean)) for localized updates. If you receive suspicious outreach, do not engage and verify through official channels first.


# RWA API

Documentation for integrators using the **Synfutures** RWA API to build stock trading, cash movement, and portfolio services.

Authenticate with API key + HMAC on every request. Choose **self-submit on-chain** or **One Click delegated** execution for orders, cash operations, and stock operations.

Current mainnet deployments are on Base, Monad, and Ethereum. Use `x-api-p: Synfutures` for the Synfutures product unless your API key is explicitly provisioned for another product context.

## Base URL

Production:

```
https://base-api.synfutures.com/rwa/trading
```

All paths start with `/api/v1`. Example:

```
GET https://base-api.synfutures.com/rwa/trading/api/v1/symbols
```

For HMAC authentication, the `URI` uses `/api/v1/...` only — no domain or `/rwa/trading` prefix.

## Live reference

| Resource         | URL                                                                |
| ---------------- | ------------------------------------------------------------------ |
| OpenAPI snapshot | `reference/openapi.snapshot.json` (refreshed 2026-07-03 from test) |
| Live OpenAPI     | `https://base-api.synfutures.com/rwa/trading/api-docs`             |

## Reader path

| Step | Topic                                          | Doc                                                                                                                                          |
| ---- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| 1    | Product and contract mental model              | [getting-started/product-and-contracts.md](/rwa-trading-apis/getting-started/product-and-contracts)                                          |
| 2    | API integration playbook                       | [getting-started/trader-questions.md](/rwa-trading-apis/getting-started/trader-questions)                                                    |
| 3    | Setup and response format                      | [getting-started/](https://github.com/AnchoredLabs/anchored-knowledge/tree/api-doc/synfutures/02-products/rwa/api/getting-started/README.md) |
| 4    | API key and HMAC authentication                | [authenticate/](https://github.com/AnchoredLabs/anchored-knowledge/tree/api-doc/synfutures/02-products/rwa/api/authenticate/README.md)       |
| 5    | Holdings and positions                         | [portfolio/](https://github.com/AnchoredLabs/anchored-knowledge/tree/api-doc/synfutures/02-products/rwa/api/portfolio/README.md)             |
| 6    | Symbols, quotes, history                       | [market/](https://github.com/AnchoredLabs/anchored-knowledge/tree/api-doc/synfutures/02-products/rwa/api/market/README.md)                   |
| 7    | Market and limit orders                        | [trading/](https://github.com/AnchoredLabs/anchored-knowledge/tree/api-doc/synfutures/02-products/rwa/api/trading/README.md)                 |
| 8    | Cash deposits and withdrawals                  | [cash/](/rwa-trading-apis/cash-operations/cash)                                                                                              |
| 9    | Stock deposits and withdrawals                 | [stock/](/rwa-trading-apis/stock-operations/stock)                                                                                           |
| 10   | First API trade quickstart                     | [guides/quickstart-first-trade.md](/rwa-trading-apis/guides/quickstart-first-trade)                                                          |
| 11   | End-to-end flows                               | [guides/](/rwa-trading-apis/guides/guides)                                                                                                   |
| 12   | Demo code (Node.js + viem)                     | [guides/demo-code.md](/rwa-trading-apis/guides/demo-code)                                                                                    |
| 13   | Status, troubleshooting, and production checks | [reference/](/rwa-trading-apis/reference/reference)                                                                                          |

## Execution modes

| Mode        | Who signs on-chain         | Use when                                              |
| ----------- | -------------------------- | ----------------------------------------------------- |
| Self-submit | Your wallet or custodian   | You control private keys; call `/calldata` then `/tx` |
| One Click   | Server via delegated proxy | User signs EIP-712 once; call `/send` endpoints       |

See [guides/self-submit-on-chain.md](/rwa-trading-apis/guides/self-submit-on-chain) and [guides/one-click-flow.md](/rwa-trading-apis/guides/one-click-flow).

## Production Helpers

| Need                           | Doc                                                                                               |
| ------------------------------ | ------------------------------------------------------------------------------------------------- |
| Debug stuck requests or txs    | [reference/troubleshooting.md](/rwa-trading-apis/reference/troubleshooting)                       |
| Interpret lifecycle states     | [reference/status-model.md](/rwa-trading-apis/reference/status-model)                             |
| Map API endpoints to contracts | [reference/api-contract-map.md](/rwa-trading-apis/reference/api-contract-map)                     |
| Choose chain/product context   | [reference/environments-and-chains.md](/rwa-trading-apis/reference/environments-and-chains)       |
| Review sample responses        | [reference/sample-responses.md](/rwa-trading-apis/reference/sample-responses)                     |
| Test before production         | [reference/integration-test-checklist.md](/rwa-trading-apis/reference/integration-test-checklist) |


# Introduction

The RWA API is a RESTful API for Synfutures tokenized stock trading. It covers market data, portfolio, orders, cash deposits and withdrawals, and optional One Click delegated execution.

## Capabilities

* **43 endpoints** across market data, portfolio, orders, cash, stock, and One Click
* **Market and limit orders** with self-submit or delegated execution
* **Deposits and withdrawals** of cash tokens
* **Portfolio** balances and stock positions
* **Multi-chain mainnet**: Base, Monad, and Ethereum
* **Testnets**: Monad Testnet and Base Sepolia
* **HMAC-SHA256** request authentication

## Audience

Partner developers integrating RWA trading APIs for order submission, cash operations, and portfolio reconciliation.

Read [product-and-contracts.md](/rwa-trading-apis/getting-started/product-and-contracts) first for the on-chain model (`StockRouter`, `Cashier`, `Stock`, settlement, portfolio balances). Then use [trader-questions.md](/rwa-trading-apis/getting-started/trader-questions) as the practical API integration playbook.

## Before you begin

Obtain an API key and secret, configure IP whitelist, and confirm `chainId + productType + access` scopes. See [authenticate/headers-and-permissions.md](/rwa-trading-apis/authentication/headers-and-permissions).

## Document version

* Doc version: 1.0
* Last updated: 2026-07-03
* Derived from `synfutures-gitbook` with OpenAPI cross-check


# Conventions

## Base URL

Production environment:

```
https://base-api.synfutures.com/rwa/trading
```

All API paths start with `/api/v1`:

```
GET /api/v1/symbols
```

Full production URL:

```
https://base-api.synfutures.com/rwa/trading/api/v1/symbols
```

> **HMAC authentication note:** For HMAC calculation, the `URI` only uses `/api/v1/symbols` — not the domain or `/rwa/trading` prefix.

## Unified response structure

All endpoints return:

| Field    | Type                  | Description                       |
| -------- | --------------------- | --------------------------------- |
| `code`   | number                | Business status. `200` = success. |
| `errMsg` | string                | Error message. Empty on success.  |
| `data`   | object / array / null | Payload.                          |
| `uuid`   | string / null         | Request UUID.                     |
| `t`      | number / null         | Server timestamp.                 |

Success example:

```json
{
  "code": 200,
  "errMsg": "",
  "data": {
    "symbol": "AAPL"
  },
  "uuid": null,
  "t": null
}
```

### Pagination

Paginated `data`:

| Field   | Type   | Description             |
| ------- | ------ | ----------------------- |
| `total` | number | Total matching records. |
| `rows`  | array  | Current page.           |

Parameters:

| Parameter | Default | Constraints                        |
| --------- | ------- | ---------------------------------- |
| `page`    | `1`     | Treated as `1` if < 1.             |
| `limit`   | `10`    | Treated as `10` if < 1; max `100`. |

## Error handling

Business errors use HTTP `400` with `code` and `errMsg` in the body.

| code    | errMsg                  | Common cause                                                  |
| ------- | ----------------------- | ------------------------------------------------------------- |
| `401`   | `Unauthorized`          | Invalid key, signature, nonce, timestamp, IP, or permissions. |
| `500`   | `Server internal error` | Validation failure, missing config, downstream error.         |
| `10000` | `Unsupported chainId`   | `x-api-chain-id` not supported.                               |
| `10001` | `Unsupported product`   | `x-api-p` not in public list.                                 |
| `10002` | `ChainId is missing`    | Missing chain ID header.                                      |
| `10003` | `Product Id is missing` | Missing product type header.                                  |


# RWA Product Overview

This page explains the contract and accounting model behind the RWA API. The API does not replace on-chain rules. It helps you **build `StockRouter` calldata**, **submit or delegate transactions**, and **track state** across wallet balances, `Cashier`, `Stock`, and asynchronous settlement.

If you are debugging a concrete trade, start with [trader-questions.md](/rwa-trading-apis/getting-started/trader-questions). This page explains the system design; the playbook turns it into request, execution, and reconciliation checks.

## Overall Design

The RWA API supports funding, market and limit orders, cash and stock movement between wallet and contracts, and portfolio state queries. It behaves like a trading account at the API layer, but accounting and permissions are enforced by on-chain contracts. The API prepares the right transactions and tracks asynchronous results after those transactions are mined.

The system is built from on-chain contracts plus backend processing. The contracts hold accounting state and enforce the transaction path. Backend processing connects that on-chain state to market execution, final settlement, status updates, and API indexing.

Core on-chain contracts:

| Contract         | Role                                                                                                                                                                        |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `StockRouter`    | Public entrypoint. It receives transactions, uses ERC-20 allowance as spender when wallet tokens are needed, and calls `Cashier` or `Stock` with the original user address. |
| `Cashier`        | Tracks `mUSD`. The API surfaces this as `mUsdBalance`. Deposits increase it; withdrawals deduct it.                                                                         |
| `Stock`          | Tracks stock order state and stock tokens credited to users as `Stock.stockBalance`, surfaced by the API as `exchangeBalance`. Plain sells spend this balance.              |
| `OneClickRouter` | Optional delegated execution router. It forwards approved One Click actions to `StockRouter` while preserving the original user address.                                    |

With these contracts, a user can:

| User need       | What the product supports                                                                                                        |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Fund account    | Convert approved USDC into `mUSD`.                                                                                               |
| Buy stock       | Use `mUSD`, or use `/orders/with-deposit/*` to deposit cash and buy in one transaction.                                          |
| Sell stock      | Sell from `exchangeBalance`, or use `/orders/with-deposit/*` to deposit approved wallet stock and sell in one transaction.       |
| Withdraw cash   | Convert `mUSD` back to USDC.                                                                                                     |
| Move stock      | Use `/stock/deposits/*` to deposit wallet stock into `Stock`, or `/stock/withdrawals/*` to withdraw `exchangeBalance` to wallet. |
| Track portfolio | Read wallet balances, `mUsdBalance`, `exchangeBalance`, orders, and cash operation status.                                       |

The API prepares `StockRouter` transactions and indexes the lifecycle after submission. A mined transaction is not always the final business state; orders, cash operations, and portfolio balances can update after backend processing and settlement.

## Core Accounting Concepts

### mUSD

`mUSD` is a 1:1 USD-backed accounting unit reflecting real-time purchasing power. It is a digital representation of credit exclusively for Synfutures RWA trading. `mUSD` is not a cryptocurrency, stablecoin, or transferable virtual asset. In this API guide, treat `mUSD` as the accounting balance a user can use to buy RWA stock or request a withdrawal.

| Concept              | Where it lives   | What it means                                                                                                                                                                         |
| -------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mUSD`               | `Cashier`        | Used for buys and withdrawals. The API surfaces this as `mUsdBalance`.                                                                                                                |
| USDC wallet balance  | User wallet      | ERC-20 cash token. Must be deposited before it becomes `mUSD`.                                                                                                                        |
| Stock wallet balance | User wallet      | ERC-20 stock token held directly by the user. `StockRouter` can pull it only after user approval.                                                                                     |
| `Stock.stockBalance` | `Stock` contract | Accounting record for ERC-20 stock tokens held by `Stock` and credited to the user. The API surfaces this as `exchangeBalance`. Bought stock lands here; plain sells spend from here. |
| `walletAllowance`    | ERC-20 allowance | Whether `StockRouter` can pull a wallet token for a deposit or other token-pulling flow.                                                                                              |

Stock `walletBalance` and `exchangeBalance` are different. `walletBalance` is the ERC-20 stock token in the user's wallet. `exchangeBalance` is the API view of `Stock.stockBalance`, where stock is held by `Stock` and credited to the user. Plain sells spend `exchangeBalance`; wallet-held stock must be approved before `StockRouter` can pull it into `Stock` for selling.

### Deposit and Withdrawal Conversion

Cash movement is not just an on-chain token transfer. Deposited USDC ultimately needs to become USD usable for trading and settlement, and withdrawals need cash to come back before USDC can be paid out. That final cash path can take time.

To reduce API-visible waiting time, `Cashier` uses pre-funded buffers. When a buffer can safely cover the request, `Cashier` can apply the result immediately while final cash settlement catches up later.

Deposits convert wallet cash tokens into `mUSD`. The user first approves `StockRouter` as the cash token spender, then submits a deposit; `StockRouter` transfers the token to `Cashier`. If the deposit is instant, `Cashier` credits `mUSD` immediately. If it is queued, `mUSD` is credited after final cash settlement.

Withdrawals convert `mUSD` back into wallet cash tokens. The user requests a withdrawal through `StockRouter`, and `Cashier` deducts `mUSD`. If the withdrawal is instant, USDC is sent to the wallet immediately. If it is queued, USDC is sent after final cash settlement.

A mined transaction means the request reached the contracts. For queued operations, the final `mUSD` credit or USDC payout can happen later. See [cash/buffer-mechanism.md](/rwa-trading-apis/cash-operations/buffer-mechanism) for buffer rules.

## Buy and Sell Paths

* **Plain buy:** spends existing `mUSD`.
* **Order-with-deposit buy:** uses approved cash token allowance, requires instant deposit, credits `mUSD`, then places the buy in one transaction.
* **Plain sell:** spends existing `exchangeBalance`, the API view of `Stock.stockBalance`.
* **Order-with-deposit sell:** transfers approved wallet stock into `Stock`, then places the sell in one transaction.

Buy paths lock `mUSD` before execution. A plain buy uses existing `mUSD`; an order-with-deposit buy first transfers approved cash token to `Cashier` and credits `mUSD`, but only when instant deposit is available and the buffer can cover the conversion.

```mermaid
sequenceDiagram
    participant U as User
    participant R as Contract: StockRouter
    participant C as Contract: Cashier
    participant S as Contract: Stock
    participant B as Backend bots

    alt Plain buy
        U->>R: place buy order
        R->>C: Lock existing mUSD
    else Order-with-deposit buy
        U->>R: deposit cash and place buy
        R->>C: Transfer cash token and credit mUSD
        R->>C: Lock mUSD
    end

    B->>B: Execute order
    B->>C: Apply mUSD spend or refund
    B->>S: Credit bought stock
    U->>U: Query API for order and balances
```

Sell paths lock stock before execution. A plain sell uses existing `exchangeBalance`; an order-with-deposit sell first transfers approved wallet stock into `Stock` and credits it there.

```mermaid
sequenceDiagram
    participant U as User
    participant R as Contract: StockRouter
    participant S as Contract: Stock
    participant C as Contract: Cashier
    participant B as Backend bots

    alt Plain sell
        U->>R: place sell order
        R->>S: Lock exchangeBalance
    else Order-with-deposit sell
        U->>R: deposit wallet stock and place sell
        R->>S: Transfer wallet stock and credit stockBalance
        R->>S: Lock stockBalance
    end

    B->>B: Execute order
    B->>S: Return unfilled stock if needed
    B->>C: Credit mUSD proceeds
    U->>U: Query API for order and balances
```

Market buys specify maximum `mUSD` to spend; market sells specify stock quantity. Limit orders specify quantity, limit price, and `timeInForce`; current production contracts accept only `DAY`. Limit orders can remain open and can be canceled while open. `txHash` is available as soon as the transaction is broadcast. `orderId` is created by the on-chain placement transaction and becomes available after the transaction is mined and indexed.

Cash endpoints are only for cash-token flows into or out of `Cashier`. Use `/stock/deposits/*` and `/stock/withdrawals/*` for stock token movement.

Any router flow that pulls wallet tokens requires ERC-20 **approve** first. Cash deposits require approval for the cash token; stock deposits require approval for the stock token. See [cash/deposits.md](/rwa-trading-apis/cash-operations/deposits).

## Fees

Fees can appear in both cash operations and order results. Do not hardcode fee assumptions; read the returned fields from operation and order responses.

| Area            | Common fields                                          | Meaning                                                              |
| --------------- | ------------------------------------------------------ | -------------------------------------------------------------------- |
| Cash operations | `amount`, `creditAmount`, `feeAmount`                  | Token amount, resulting `mUSD`, and cash operation fee when present. |
| Order history   | `mintFee`, `protocolFee`, `settlePay`, `settleReceive` | Trading/mint/protocol fees and final fill amounts when present.      |

For deposits, the credited `mUsdBalance` may differ from the token amount if a fee applies. For orders, final received stock or cash depends on traditional-market execution: orders can fill at the executed price, partially fill, or receive no fill. Use settlement fields such as `settlePay`, `settleReceive`, `mintFee`, and `protocolFee` as the authoritative final amounts, not only the original order input.

## Contract stack (integrator view)

State-changing transactions go through **`StockRouter`**. When wallet tokens are needed, `StockRouter` uses the user's ERC-20 allowance as spender and transfers the tokens to `Cashier` or `Stock`; it does not custody deposits as the final destination.

```mermaid
flowchart TB
    User[User wallet]
    OCR[OneClickRouter optional]
    Router["StockRouter<br/>API calldata target"]

    Cashier["Cashier<br/>mUSD"]
    Stock["Stock<br/>orders and stockBalance"]

    User -->|"direct tx"| Router
    User -.->|"EIP-712 delegate"| OCR
    OCR -->|"ERC-2771 forward"| Router

    Router --> Cashier
    Router --> Stock

    Cashier -.- C1["cash deposits and withdrawals<br/>change mUSD"]
    Stock -.- S1["orders<br/>stockBalance"]
```

Use `x-api-p: Synfutures` for Synfutures RWA trading. The active API docs do not require other product contexts.

## Router operations map to API flows

Calldata responses give `toAddress`, `value`, and `callData` for your wallet client. `toAddress` is the `StockRouter` address. See [guides/demo-code.md](/rwa-trading-apis/guides/demo-code).

| User action              | Router method      | Self-submit endpoint                         | One Click endpoint              |
| ------------------------ | ------------------ | -------------------------------------------- | ------------------------------- |
| Deposit cash             | `deposit`          | `POST /cash/deposits/calldata` then `/tx`    | `POST /cash/deposits/send`      |
| Withdraw cash            | `withdraw`         | `POST /cash/withdrawals/calldata` then `/tx` | `POST /cash/withdrawals/send`   |
| Market buy or plain sell | `placeMarketOrder` | `POST /orders/calldata` then `/tx`           | `POST /orders/send`             |
| Limit buy or plain sell  | `placeLimitOrder`  | `POST /orders/calldata` then `/tx`           | `POST /orders/send`             |
| Cancel limit             | `cancelLimitOrder` | `DELETE /orders/{orderId}/calldata`          | `DELETE /orders/{orderId}/send` |

Stock deposit and withdrawal are exposed through `/stock/deposits/*` and `/stock/withdrawals/*`.

## Portfolio API vs on-chain state

Use `GET /users/{userId}/balance` as the API view of wallet and exchange state.

| Field             | Meaning                                   | Integration note                                                         |
| ----------------- | ----------------------------------------- | ------------------------------------------------------------------------ |
| `mUsdBalance`     | `mUSD` tracked by `Cashier`               | Use for buy power and withdrawable credit                                |
| `walletBalance`   | ERC-20 tokens in wallet                   | Custody balance; `StockRouter` needs allowance before it can pull tokens |
| `exchangeBalance` | API name for `Stock.stockBalance`         | Use for plain sell availability                                          |
| `walletAllowance` | Token approval to spender / `StockRouter` | Check before token-pulling flows                                         |

Bought stock normally appears as `exchangeBalance` first because the ERC-20 stock tokens are held by `Stock` and credited to the user there. A user can withdraw stock from `Stock.stockBalance` to wallet custody. If wallet-held stock should be sold, the user must approve `StockRouter`; the selected flow can then deposit the stock into `Stock.stockBalance` separately or as part of the sell transaction. For API checks, use `mUsdBalance` for buy power and `exchangeBalance` for sell power.

## Two execution modes (same contracts)

| Mode        | How it works                                                                                                                |
| ----------- | --------------------------------------------------------------------------------------------------------------------------- |
| Self-submit | API returns calldata; the user wallet signs and broadcasts the `StockRouter` transaction; the integration records `txHash`. |
| One Click   | User signs delegation once; `/send` submits through `OneClickRouter` to the same `StockRouter` methods.                     |

One Click is scoped for safety: the delegate wallet is bound to one user wallet, limited to supported RWA router actions, cannot freely transfer user ERC-20 tokens, and can be disabled.

## Design principles

| Principle           | What it means for integrators                                                                                                                                               |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Router-only entry   | API calldata targets `StockRouter`; state-changing integrations should not call `Cashier` or `Stock` directly.                                                              |
| Lock then settle    | Open orders lock `mUSD` or `Stock.stockBalance`; final balances update asynchronously.                                                                                      |
| Off-chain execution | Quotes and fills come from trading services; chain state catches up asynchronously.                                                                                         |
| Wallet vs exchange  | Portfolio shows both wallet and exchange balances; plain sells use `exchangeBalance`, while wallet-held stock can be pulled into `Stock` by an approved `StockRouter` flow. |
| Cash timing         | Deposits/withdrawals can be instant or queued; watch `operationStatus`.                                                                                                     |

## Where to go next

| Step            | Doc                                                                                                                                       |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Authenticate    | [../authenticate/](https://github.com/AnchoredLabs/anchored-knowledge/tree/api-doc/synfutures/02-products/rwa/api/authenticate/README.md) |
| Trader playbook | [trader-questions.md](/rwa-trading-apis/getting-started/trader-questions)                                                                 |
| Portfolio       | [../portfolio/overview.md](/rwa-trading-apis/portfolio/overview)                                                                          |
| Trading         | [../trading/overview.md](/rwa-trading-apis/trading/overview)                                                                              |
| Cash            | [../cash/](/rwa-trading-apis/cash-operations/cash)                                                                                        |
| Demo code       | [../guides/demo-code.md](/rwa-trading-apis/guides/demo-code)                                                                              |
| Reference       | [../reference/](/rwa-trading-apis/reference/reference)                                                                                    |

Deep contract docs: [synfutures-contracts/docs/contract-architecture.md](https://github.com/SynfuturesLabs/synfutures-contracts/blob/main/docs/contract-architecture.md) · [synfutures-contracts/docs/flows/](https://github.com/SynfuturesLabs/synfutures-contracts/blob/main/docs/flows/README.md)


# API Integration Playbook

Use this page as the practical operating guide for an RWA API integration. Before submitting orders, moving cash, or reconciling portfolio state, verify these request and state checks.

The most important habit is to separate **wallet state**, **mUSD**, **exchangeBalance**, and **final settlement state**. The API connects these systems, but they do not update at the same time.

## Integration Mental Model

```mermaid
sequenceDiagram
    participant Client as Integrator client
    participant API as Trading API
    participant Wallet as User wallet
    participant OCR as OneClickRouter
    participant Router as StockRouter
    participant Cashier as Cashier
    participant Stock as Stock

    Client->>API: GET state
    API-->>Client: mUsdBalance, exchangeBalance, orders

    alt Self-submit
        rect rgb(232, 244, 255)
            Client->>API: POST /calldata
            API-->>Client: toAddress, value, callData
            Client->>Wallet: submit transaction
            Wallet->>Router: execute calldata
            Client->>API: POST /tx with txHash
        end
    else One Click
        rect rgb(246, 239, 255)
            Client->>API: POST /send
            API->>OCR: submit delegated tx
            OCR->>Router: forward as user
        end
    end

    Router->>Cashier: update mUSD
    Router->>Stock: update orders / stockBalance
    Cashier-->>API: indexed state
    Stock-->>API: indexed state
```

Diagram color key: blue is self-submit, purple is One Click.

For self-submit requests, call the API to build `StockRouter` calldata, submit the transaction with the user wallet, then record `txHash`. For One Click requests, call `/send`; the API submits through `OneClickRouter`. For state reads, the API indexes `Cashier` and `Stock` into fields such as `mUsdBalance`, orders, and `exchangeBalance`.

`StockRouter` enforces on-chain entry, `Cashier` accounts for `mUSD`, and `Stock` accounts for orders and `Stock.stockBalance`. Build integrations around the async lifecycle instead of assuming a trade is final when the transaction is mined.

## Discover Tradable Stocks

Start every trading flow from the symbol list. Treat a stock token as tradable only when the API marks it tradable for the current `chainId` and `x-api-p`.

1. Call `GET /api/v1/symbols`.
2. Keep only symbols where `tradable` is `true`.
3. Call `GET /api/v1/config` for fee rates, cashier buffer state, and per-token deposit/withdraw limits.
4. Use `contractAddress` as `stockAddress` when building orders.
5. Refresh quote context with `GET /api/v1/prices/{symbol}`.
6. Check `GET /api/v1/corporate-action?symbol=AAPL` when splits/dividends may affect downstream accounting.

Do not hardcode token addresses unless your release process also verifies they match the active API response.

## Check Buying Power

For plain buys, the important value is `mUsdBalance`, not the cash token `walletBalance`. A user can have USDC in their wallet and still have no `mUSD` until a deposit happens. If the cash token is approved and instant deposit is available, `/orders/with-deposit/*` can deposit and place the buy in one transaction.

| Field             | API meaning                                       |
| ----------------- | ------------------------------------------------- |
| `mUsdBalance`     | `mUSD` available for buys and withdrawals         |
| `walletBalance`   | ERC-20 tokens in wallet; not automatically `mUSD` |
| `walletAllowance` | Whether deposit can succeed on-chain              |

Use `mUsdBalance` for plain market buys and limit buys. If it is insufficient, deposit cash first. When cash token approval and instant deposit conditions are met, `/orders/with-deposit/*` can deposit and place the buy in one transaction.

## Check Stock Available To Sell

For plain sells, the important value is `exchangeBalance`. `walletBalance` means the user owns an ERC-20 stock token in their wallet, but it is not yet credited inside `Stock`.

Do not treat wallet stock as plain-sell balance. A plain sell uses `exchangeBalance`, the API view of `Stock.stockBalance`. To sell wallet-held stock with separate calls, approve the stock token and call `/stock/deposits/*` first. To deposit and sell in one transaction, use `/orders/with-deposit/*` after stock token approval.

## Prepare Deposits Correctly

Deposit means: **wallet cash token -> mUSD -> buy power**. Track the conversion so the integration does not treat wallet balance as spendable `mUSD`.

Before building deposit calldata:

1. Check wallet cash balance.
2. Check allowance with `GET /api/v1/users/{userId}/balance?spenderAddress={spender}`.
3. Request ERC-20 approval from the owner wallet if `walletAllowance` is too low.
4. For self-submit, build deposit calldata and submit the returned `StockRouter` transaction.
5. For One Click, call `/cash/deposits/send` after delegation is enabled.
6. Record `txHash` for self-submit flows and poll the deposit operation.

If allowance is too low, `/cash/deposits/calldata` can still return calldata, but the chain transaction will fail.

## Place A Market Buy

```mermaid
sequenceDiagram
    participant T as Client
    participant API as Trading API
    participant R as StockRouter
    participant C as Cashier
    participant TS as Trading service

    alt Self-submit
        rect rgb(232, 244, 255)
            T->>API: POST /orders/calldata
            T->>R: submit returned calldata
        end
    else One Click
        rect rgb(246, 239, 255)
            T->>API: POST /orders/send
            API->>R: submit via OneClickRouter
        end
    end
    R->>R: placeMarketOrder buy notional
    R->>C: Lock max mUSD spend
    TS->>TS: Execute at best available price
    TS->>C: Apply mUSD spend or refund
```

Market buy input is `notional`: the maximum `mUSD` amount to spend. The final fill may spend less and refund the rest. The order is not economically final until settlement has credited stock and refunded unused `mUSD`.

## Place A Market Sell

```mermaid
sequenceDiagram
    participant T as Client
    participant API as Trading API
    participant R as StockRouter
    participant Stock as Stock
    participant TS as Trading service
    participant C as Cashier

    alt Self-submit
        rect rgb(232, 244, 255)
            T->>API: POST /orders/calldata
            T->>R: submit returned calldata
        end
    else One Click
        rect rgb(246, 239, 255)
            T->>API: POST /orders/send
            API->>R: submit via OneClickRouter
        end
    end
    R->>R: placeMarketOrder sell quantity
    R->>Stock: Lock max stock quantity
    TS->>TS: Execute at best available price
    TS->>Stock: Apply filled and unfilled result
    TS->>C: Credit mUSD proceeds
```

Market sell input is `quantity`: the maximum stock quantity to sell. `Stock` locks that quantity first; settlement applies only the filled amount and returns any unfilled stock to `Stock.stockBalance`.

## Place A Limit Order

```mermaid
sequenceDiagram
    participant T as Client
    participant API as Trading API
    participant R as StockRouter
    participant S as Stock
    participant C as Cashier
    participant B as Backend bots

    alt Self-submit
        rect rgb(232, 244, 255)
            T->>API: POST /orders/calldata
            T->>R: submit returned calldata
        end
    else One Click
        rect rgb(246, 239, 255)
            T->>API: POST /orders/send
            API->>R: submit via OneClickRouter
        end
    end
    R->>S: placeLimitOrder
    alt Buy
        S->>C: Lock mUSD
    else Sell
        S->>S: Lock stockBalance
    end
    B->>B: Execute when market conditions match
    alt Filled or partial
        B->>C: Apply mUSD spend/refund
        B->>S: Apply stock fill/remainder
    else Open
        B-->>API: Keep order in openOrder
    else Cancel
        T->>API: DELETE /orders/{orderId}/calldata or /send
        API->>R: cancelLimitOrder
        B->>C: Refund locked mUSD if needed
        B->>S: Return locked stock if needed
    end
```

Limit orders have a `price`, `quantity`, and `timeInForce`. Current production contracts accept only `DAY`; backend processing and settlement handle the practical lifecycle after placement.

Treat a limit order as an open commitment that locks `mUSD` or stock until fill, cancel, expiry, or settlement. Keep it separate from final portfolio balances.

## Reconcile Order Finality

A mined transaction means the order was accepted on-chain; it does not mean execution and settlement are final. Watch both mapping state and order detail:

1. `POST /api/v1/orders/tx` records a self-submitted tx.
2. `GET /api/v1/orders/tx/{txHash}` finds the tx mapping.
3. The indexer backfills `orderId`.
4. `GET /api/v1/orders/{orderId}` tracks open or historical state.
5. Portfolio balances update after settlement.

An order is economically final only after settlement updates `mUSD` and `exchangeBalance`.

## Understand Instant Vs Queued Cash

```mermaid
sequenceDiagram
    participant T as Client
    participant API as Trading API
    participant R as StockRouter
    participant C as Cashier
    participant B as Backend bots

    alt Self-submit
        rect rgb(232, 244, 255)
            T->>API: POST cash calldata endpoint
            T->>R: submit returned calldata
        end
    else One Click
        rect rgb(246, 239, 255)
            T->>API: POST cash send endpoint
            API->>R: submit via OneClickRouter
        end
    end
    R->>C: Record cash operation
    alt Buffer can cover operation
        C->>C: Apply mUSD credit or token payout
        API-->>T: operationStatus settled
    else Buffer cannot cover operation
        C->>C: Mark processing
        B->>B: Complete final cash settlement
        B->>C: Settle operation
        API-->>T: operationStatus settled
    end
```

Cash can be instant when buffers are available. Otherwise it is queued until final settlement completes. Track status with:

* `GET /api/v1/cash/deposits/{operationId}`
* `GET /api/v1/cash/withdrawals/{operationId}`

Handle both instant and queued outcomes. A queued operation is not necessarily failed; it may be waiting for final cash settlement.

The underlying contracts use two buffers: a global `creditBuffer` for instant deposits and a per-token `withdrawalBuffer` for instant withdrawals. Deposits and withdrawals can replenish each other's buffers. See [cash/buffer-mechanism.md](/rwa-trading-apis/cash-operations/buffer-mechanism).

## Choose Execution Mode

Both modes call the same router methods. One Click only changes who submits the router transaction.

| Mode        | Use when                                           | Trade-off                                                |
| ----------- | -------------------------------------------------- | -------------------------------------------------------- |
| Self-submit | You manage wallets, custody, or transaction policy | More control; must record `txHash`                       |
| One Click   | You use delegated transaction submission           | Requires user delegation and API key bound `userAddress` |

Use Self-submit when your integration owns transaction policy or custody. Use One Click when the integration needs delegated submission after explicit authorization.

## Treat API Calldata As A Router Instruction

For self-submit, use `toAddress`, `value`, and `callData` exactly as returned by the API. Do not reconstruct calldata by hand unless you are deliberately bypassing the partner API. The API output is already shaped for the correct router method and product context.

## Pre-Trade Checklist

Before placing an order, confirm:

* Symbol is tradable for the selected chain/product.
* `x-api-chain-id`, wallet network, and `x-api-p` match.
* Portfolio has been refreshed.
* Plain buys use `mUsdBalance`; plain sells use `exchangeBalance`.
* Order-with-deposit flows require token allowance and, for buys, instant deposit availability.
* Deposit allowance is sufficient if funding is required.
* Execution mode is selected.
* Self-submit flows record `txHash`; One Click flows have `/1ct/status = enable`.

If a trade fails or appears stuck, check these in order: API response `code`, tx receipt, `/orders/tx/{txHash}`, `/orders/{orderId}`, then portfolio balances.

## Related Docs

| Need                             | Doc                                                                                                                                    |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| First API trade flow             | [../guides/quickstart-first-trade.md](/rwa-trading-apis/guides/quickstart-first-trade)                                                 |
| How do I authenticate?           | [authenticate/](https://github.com/AnchoredLabs/anchored-knowledge/tree/api-doc/synfutures/02-products/rwa/api/authenticate/README.md) |
| How do I read balances?          | [portfolio/overview.md](/rwa-trading-apis/portfolio/overview)                                                                          |
| How do I deposit?                | [cash/deposits.md](/rwa-trading-apis/cash-operations/deposits)                                                                         |
| How do I place or cancel orders? | [trading/place-and-cancel-orders.md](/rwa-trading-apis/trading/place-and-cancel-orders)                                                |
| How do I track status?           | [trading/order-status-and-history.md](/rwa-trading-apis/trading/order-status-and-history)                                              |
| How do I submit with viem?       | [guides/demo-code.md](/rwa-trading-apis/guides/demo-code)                                                                              |
| How do I debug issues?           | [../reference/troubleshooting.md](/rwa-trading-apis/reference/troubleshooting)                                                         |


# Headers & Permissions

## Required headers

All `/api/v1/**` endpoints require:

| Header           | Required | Description                                             |
| ---------------- | -------- | ------------------------------------------------------- |
| `x-api-key`      | Yes      | Partner API key.                                        |
| `x-api-ts`       | Yes      | Unix ms timestamp. ≤ **45 seconds** drift from UTC.     |
| `x-api-nonce`    | Yes      | Unique per key within the 45s window. UUID recommended. |
| `x-api-sign`     | Yes      | HMAC-SHA256 authentication value. Case-insensitive hex. |
| `x-api-chain-id` | Yes      | Chain ID for this request.                              |
| `x-api-p`        | Yes      | Product type for this request.                          |

## Supported chain IDs

| Value      | Chain            |
| ---------- | ---------------- |
| `8453`     | Base Mainnet     |
| `1`        | Ethereum Mainnet |
| `143`      | Monad Mainnet    |
| `11155111` | Ethereum Sepolia |
| `10143`    | Monad Testnet    |
| `84532`    | Base Sepolia     |

## Supported product types

| Value        | Description         |
| ------------ | ------------------- |
| `Synfutures` | Synfutures product. |

> Enum values are case-sensitive. These docs focus the Synfutures RWA API; use `x-api-p: Synfutures`.

## API key validation

| Check        | Rule                                                                                                                               |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| API key      | Must exist and be `active`.                                                                                                        |
| Expiry       | `expires_at` null or in the future.                                                                                                |
| IP whitelist | **Required — an empty whitelist rejects every request (`401`); it does not mean "no restriction".** Exact IP match only (no CIDR). |
| Scope        | Must match `chainId + productType + access`.                                                                                       |

> **Before your first request, add your egress IP to the key's whitelist.** A key with an empty/unset whitelist rejects *all* requests with `401 Unauthorized` — the single most common first-integration failure. Behind a gateway/proxy the server matches the IP it resolves (see below, usually the `X-Forwarded-For` value), not your machine's local IP.

### Client IP resolution

1. First non-empty IP in `X-Forwarded-For`
2. `X-Real-IP`
3. TCP `remoteAddr`

## Permission levels

| access      | Allowed operations       |
| ----------- | ------------------------ |
| `READ_ONLY` | `GET` only.              |
| `WRITABLE`  | `GET`, `POST`, `DELETE`. |


# HMAC Authentication

This page describes API request authentication. It is not wallet signing. Every private endpoint request must include an HMAC value in `x-api-sign`.

## Authentication payload

Five lines joined by `\n`:

```
METHOD
URI
TIMESTAMP
NONCE
RAW_BODY
```

| Field       | Description                                               |
| ----------- | --------------------------------------------------------- |
| `METHOD`    | Uppercase HTTP method.                                    |
| `URI`       | Path with sorted query string. No domain or context path. |
| `TIMESTAMP` | Same as `x-api-ts` (ms).                                  |
| `NONCE`     | Same as `x-api-nonce`.                                    |
| `RAW_BODY`  | Raw body string, or empty if none.                        |

## Query string sorting

Parameters sorted by name ascending. Example request:

```
GET /api/v1/orders?page=1&limit=10
```

Authenticated `URI`:

```
/api/v1/orders?limit=10&page=1
```

> Avoid duplicate query parameter names when building the authentication payload.

## Body authentication

JSON requests use `Content-Type: application/json`. `RAW_BODY` must match the sent body exactly (spaces, field order).

`GET` and bodyless `POST` use empty `RAW_BODY`.

## HMAC-SHA256

```
x-api-sign = hex(HMAC_SHA256(authenticationPayload, apiSecret))
```

UTF-8 encoding throughout.

### Python

```python
import hmac
import hashlib
from urllib.parse import urlencode

def canonical_uri(path: str, query: dict = None) -> str:
    if not query:
        return path
    sorted_pairs = sorted(query.items(), key=lambda x: x[0])
    return f"{path}?{urlencode(sorted_pairs)}"

def sign_trading_api(
    method: str,
    path: str,
    query: dict = None,
    timestamp: int = None,
    nonce: str = None,
    raw_body: str = "",
    api_secret: str = "",
) -> str:
    import time
    import uuid
    timestamp = timestamp or int(time.time() * 1000)
    nonce = nonce or str(uuid.uuid4())
    uri = canonical_uri(path, query)
    payload = f"{method.upper()}\n{uri}\n{timestamp}\n{nonce}\n{raw_body}"
    return hmac.new(
        api_secret.encode("utf-8"),
        payload.encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()
```

### cURL

cURL + openssl works for a quick check, but shell HMAC has sharp edges (trailing-newline stripping by `$(...)`, secrets starting with `-`, `date +%s%3N` on macOS/BSD, `PATH` clobbering). For production, prefer the Python example above or the Node `crypto` version in [examples.md](/rwa-trading-apis/authentication/examples).

```bash
METHOD="GET"
URI="/api/v1/symbols"
# Unix milliseconds. GNU date works directly; BSD (macOS) date emits a literal "N",
# so fall back based on whether the output is all digits (do NOT rely on exit code —
# BSD date exits 0 even for %3N).
TIMESTAMP=$(date +%s%3N)
case "$TIMESTAMP" in
  ''|*[!0-9]*)
    if command -v gdate >/dev/null 2>&1; then TIMESTAMP=$(gdate +%s%3N)
    else TIMESTAMP=$(python3 -c 'import time; print(int(time.time()*1000))'); fi ;;
esac
NONCE=$(uuidgen | tr '[:upper:]' '[:lower:]')
RAW_BODY=""
# Pipe printf STRAIGHT into openssl. Three gotchas this avoids:
#  1) Join with REAL newlines (printf format), not the two-character "\n".
#  2) PAYLOAD=$(printf ...) would strip the trailing newline -> wrong sig for empty-body (GET/DELETE) requests.
#  3) -macopt "key:..." (not -hmac "...") so a secret starting with "-" isn't parsed as an openssl option.
HMAC_AUTH=$(printf '%s\n%s\n%s\n%s\n%s' "$METHOD" "$URI" "$TIMESTAMP" "$NONCE" "$RAW_BODY" \
  | openssl dgst -sha256 -mac HMAC -macopt "key:$API_SECRET" -hex | awk '{print $NF}')

curl -X GET "https://base-api.synfutures.com/rwa/trading/api/v1/symbols" \
  -H "x-api-key: ${API_KEY}" \
  -H "x-api-ts: ${TIMESTAMP}" \
  -H "x-api-nonce: ${NONCE}" \
  -H "x-api-sign: ${HMAC_AUTH}" \
  -H "x-api-chain-id: 8453" \
  -H "x-api-p: Synfutures"
```


# Examples

Node.js HMAC authentication snippets. For a full integration sample with **viem** (self-submit tx, ERC-20 approve, One Click `signTypedData`), see [guides/demo-code.md](/rwa-trading-apis/guides/demo-code).

## HMAC helper

```js
import crypto from "node:crypto";

function canonicalUri(path, query = {}) {
  const pairs = [];
  for (const key of Object.keys(query).sort()) {
    const value = query[key];
    if (Array.isArray(value)) {
      for (const item of value) pairs.push(`${key}=${item}`);
    } else if (value !== undefined && value !== null) {
      pairs.push(`${key}=${value}`);
    }
  }
  return pairs.length === 0 ? path : `${path}?${pairs.join("&")}`;
}

function signTradingApi({ method, path, query, timestamp, nonce, rawBody, apiSecret }) {
  const uri = canonicalUri(path, query);
  const payload = [
    method.toUpperCase(),
    uri,
    String(timestamp),
    nonce,
    rawBody ?? ""
  ].join("\n");

  return crypto
    .createHmac("sha256", apiSecret)
    .update(payload, "utf8")
    .digest("hex");
}
```

## Signed POST example

```js
const body = JSON.stringify({
  stockAddress: "0x0000000000000000000000000000000000000001",
  side: "Buy",
  type: "Market",
  notional: "10",
  deadline: 1893456000
});

const timestamp = Date.now();
const nonce = crypto.randomUUID();
const hmacAuth = signTradingApi({
  method: "POST",
  path: "/api/v1/orders/calldata",
  query: {},
  timestamp,
  nonce,
  rawBody: body,
  apiSecret: process.env.TRADING_API_SECRET
});

const headers = {
  "content-type": "application/json",
  "x-api-key": process.env.TRADING_API_KEY,
  "x-api-ts": String(timestamp),
  "x-api-nonce": nonce,
  "x-api-sign": hmacAuth,
  "x-api-chain-id": "8453",
  "x-api-p": "Synfutures"
};

const response = await fetch(
  "https://base-api.synfutures.com/rwa/trading/api/v1/orders/calldata",
  { method: "POST", headers, body }
);
```

## cURL with HMAC

```bash
API_KEY="your-api-key"
API_SECRET='your-api-secret'   # single-quote it: a secret containing shell metacharacters breaks when double-quoted or sourced
METHOD="POST"
URI="/api/v1/orders/calldata"   # do NOT name this variable PATH — it clobbers the shell's $PATH
# Unix milliseconds. GNU date works directly; BSD (macOS) date emits a literal "N",
# so fall back based on whether the output is all digits (do NOT rely on exit code —
# BSD date exits 0 even for %3N).
TIMESTAMP=$(date +%s%3N)
case "$TIMESTAMP" in
  ''|*[!0-9]*)
    if command -v gdate >/dev/null 2>&1; then TIMESTAMP=$(gdate +%s%3N)
    else TIMESTAMP=$(python3 -c 'import time; print(int(time.time()*1000))'); fi ;;
esac
NONCE=$(uuidgen | tr '[:upper:]' '[:lower:]')
BODY='{"stockAddress":"0x0000000000000000000000000000000000000001","side":"Buy","type":"Market","notional":"10","deadline":1893456000}'

# Pipe printf STRAIGHT into openssl. Gotchas this avoids:
#  1) Join with REAL newlines (printf format), not the two-character "\n".
#  2) PAYLOAD=$(printf ...) would strip the trailing newline (breaks empty-body GET/DELETE).
#  3) -macopt "key:..." (not -hmac "...") so a secret starting with "-" isn't parsed as an option.
HMAC_AUTH=$(printf '%s\n%s\n%s\n%s\n%s' "$METHOD" "$URI" "$TIMESTAMP" "$NONCE" "$BODY" \
  | openssl dgst -sha256 -mac HMAC -macopt "key:$API_SECRET" -hex | awk '{print $NF}')

curl -X ${METHOD} "https://base-api.synfutures.com/rwa/trading${URI}" \
  -H "Content-Type: application/json" \
  -H "x-api-key: ${API_KEY}" \
  -H "x-api-ts: ${TIMESTAMP}" \
  -H "x-api-nonce: ${NONCE}" \
  -H "x-api-sign: ${HMAC_AUTH}" \
  -H "x-api-chain-id: 143" \
  -H "x-api-p: Synfutures" \
  -d "${BODY}"
```

See also [signature.md](/rwa-trading-apis/authentication/signature) for Python and HMAC authentication details, and [guides/demo-code.md](/rwa-trading-apis/guides/demo-code) for viem integration.


# Holdings & Positions

Use these endpoints to monitor a user's `mUSD`, wallet token balances, and `exchangeBalance`.

`userId` is currently the user's wallet address (trimmed, lowercased).

For API-level interpretation of `mUsdBalance`, `walletBalance`, and `exchangeBalance`, read [getting-started/trader-questions.md](/rwa-trading-apis/getting-started/trader-questions).

## Endpoints

| Method | Path                               | Description             |
| ------ | ---------------------------------- | ----------------------- |
| `GET`  | `/api/v1/users/{userId}`           | User info               |
| `GET`  | `/api/v1/users/{userId}/balance`   | Cash and token balances |
| `GET`  | `/api/v1/users/{userId}/positions` | Stock positions         |

## Query user info

```
GET /api/v1/users/{userId}
```

Response `data`:

| Field         | Type   | Description               |
| ------------- | ------ | ------------------------- |
| `userId`      | string | User ID (wallet address). |
| `userAddress` | string | Wallet address.           |

## Query balances

```
GET /api/v1/users/{userId}/balance
```

| Parameter        | Type   | Required | Default       | Description                  |
| ---------------- | ------ | -------- | ------------- | ---------------------------- |
| `spenderAddress` | string | No       | `0x000...000` | Spender for allowance query. |

Response `data`:

| Field           | Type   | Description   |
| --------------- | ------ | ------------- |
| `chainId`       | number | Chain ID.     |
| `productType`   | string | Product type. |
| `address`       | string | User wallet.  |
| `mUsdBalance`   | string | `mUSD`.       |
| `tokenBalances` | array  | Token list.   |

### tokenBalances item

| Field             | Type          | Description                                            |
| ----------------- | ------------- | ------------------------------------------------------ |
| `address`         | string        | Token address.                                         |
| `name`            | string        | Token name.                                            |
| `symbol`          | string        | Token symbol.                                          |
| `stockSymbol`     | string / null | Stock symbol (stock tokens).                           |
| `isStock`         | boolean       | Stock token flag.                                      |
| `decimals`        | number        | Decimals.                                              |
| `price`           | number / null | Price (stock tokens).                                  |
| `walletBalance`   | string        | Wallet balance (human-readable).                       |
| `walletAllowance` | string        | Allowance to spender.                                  |
| `exchangeBalance` | string        | API view of `Stock.stockBalance`; used by plain sells. |
| `logoUrl`         | string        | Logo URL.                                              |

## Query positions

```
GET /api/v1/users/{userId}/positions
```

Same query parameters as `/balance`. Returns `data` as a `tokenBalances` array.

Use `spenderAddress` when checking deposit allowance — see [cash/deposits.md](/rwa-trading-apis/cash-operations/deposits).


# On-Chain Market Config

## Get market config

```
GET /api/v1/config
```

Returns on-chain exchange configuration for the current `chainId + productType` scope: cashier buffers, exchange fee settings, and per-token cashier limits/rates.

Permission: `READ_ONLY`. No query parameters.

This endpoint is separate from direct on-chain stock reference-price reads. For the `StockOracle` contract interface, see [Stock Oracle](/rwa-trading-apis/market-data/stock-oracle).

## Response

Standard envelope. `data` contains:

| Field                | Type   | Description                                                    |
| -------------------- | ------ | -------------------------------------------------------------- |
| `cashierBuffer`      | object | Cashier-level buffer snapshot for instant deposit eligibility. |
| `exchangeFeeConfig`  | object | Exchange fee and minimum order settings.                       |
| `cashierTokenConfig` | object | Map of lowercase token address → per-token cashier config.     |

### Field naming note

`cashierBuffer` and each `cashierTokenConfig` entry use `chain`. `exchangeFeeConfig` uses `chainId`. Both represent the scoped chain ID.

### Value encoding

| Area                                                                 | Format                                | Example                                   |
| -------------------------------------------------------------------- | ------------------------------------- | ----------------------------------------- |
| `cashierBuffer.creditBuffer`, `creditBufferCapacity`, `totalBalance` | Raw WAD integer strings (18 decimals) | `"743810467000000000000"` ≈ 743.81 `mUSD` |
| `cashierTokenConfig.withdrawalBuffer`, `withdrawalBufferCapacity`    | Raw integer strings in token decimals | `"253689533"` with `decimals: 6`          |
| `exchangeFeeConfig.mintFeeRate`, `protocolFeeRate`, `minOrderValue`  | Human-readable decimal strings        | `"0"`, `"0.001"`                          |
| `cashierTokenConfig.depositRate`, `withdrawalRate`                   | Human-readable decimal strings        | `"1"`, `"0.9969"`                         |

Do not parse WAD or token-amount fields as human-readable decimals.

### `cashierBuffer`

| Field                     | Type   | Description                                                                                                                                                               |
| ------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `chain`                   | number | Chain ID for the scoped context.                                                                                                                                          |
| `productType`             | string | Product type, e.g. `Synfutures`.                                                                                                                                          |
| `address`                 | string | Cashier contract address. Matches `cashierAddress` in `cashierTokenConfig` entries.                                                                                       |
| `instantThresholdDivisor` | string | Divisor used to cap instant operation size relative to buffer capacity. See [buffer mechanism — Instant Eligibility](/rwa-trading-apis/cash-operations/buffer-mechanism). |
| `creditBuffer`            | string | Current `mUSD` credit buffer balance (WAD).                                                                                                                               |
| `creditBufferCapacity`    | string | Maximum `mUSD` credit buffer capacity (WAD).                                                                                                                              |
| `totalBalance`            | string | Total cashier balance snapshot (WAD).                                                                                                                                     |

Per-token `withdrawalBuffer` values live under `cashierTokenConfig`, not in this object.

### `exchangeFeeConfig`

| Field             | Type   | Description                                                                                               |
| ----------------- | ------ | --------------------------------------------------------------------------------------------------------- |
| `chainId`         | number | Chain ID.                                                                                                 |
| `productType`     | string | Product type.                                                                                             |
| `mintFeeRate`     | string | Mint fee rate, human-readable (e.g. `0.001` = 0.1%). `"0"` means no mint fee.                             |
| `protocolFeeRate` | string | Protocol fee rate, human-readable. `"0"` means no protocol fee.                                           |
| `minOrderValue`   | string | Minimum order notional in USD, human-readable (e.g. `"1.00"`). `"0"` means no minimum enforced in config. |
| `txHash`          | string | Tx hash of the latest on-chain config update. May be empty when no update tx is recorded.                 |
| `updatedAt`       | number | Unix seconds when config was last updated.                                                                |

Use `minOrderValue`, `mintFeeRate`, and `protocolFeeRate` when validating or displaying order economics before calling order endpoints.

### `cashierTokenConfig` entries

Map keys are lowercase ERC-20 token addresses. Each value includes:

| Field                      | Type    | Description                                                             |
| -------------------------- | ------- | ----------------------------------------------------------------------- |
| `chain`                    | number  | Chain ID.                                                               |
| `productType`              | string  | Product type.                                                           |
| `cashierAddress`           | string  | Cashier contract address.                                               |
| `tokenAddress`             | string  | ERC-20 token address.                                                   |
| `minAmount`                | string  | Minimum deposit/withdraw amount in whole tokens.                        |
| `decimals`                 | number  | Token decimals.                                                         |
| `depositPaused`            | boolean | Deposit flow paused for this token.                                     |
| `withdrawPaused`           | boolean | Withdraw flow paused for this token.                                    |
| `depositRate`              | string  | Deposit rate, human-readable (e.g. `1` = no fee).                       |
| `withdrawalRate`           | string  | Withdrawal rate, human-readable (e.g. `0.9969`).                        |
| `withdrawalBuffer`         | string  | Current on-chain token buffer for instant withdrawals (token decimals). |
| `withdrawalBufferCapacity` | string  | Maximum token buffer capacity for instant withdrawals (token decimals). |

Check `depositPaused`, `withdrawPaused`, and buffer fields before building deposit or withdrawal calldata. See [cash deposits](/rwa-trading-apis/cash-operations/deposits) and [withdrawals](/rwa-trading-apis/cash-operations/withdrawals).

## Integration notes

* For instant vs queued cash behavior, pair buffer fields here with [buffer mechanism](/rwa-trading-apis/cash-operations/buffer-mechanism).

Example payload: [sample-responses.md — Market config](/rwa-trading-apis/reference/sample-responses).


# Stock Oracle

## Contract Overview

`StockOracle` stores one latest price payload per supported `bytes32 priceId`. The same oracle proxy can serve all stock symbols on a chain; it does not deploy one feed contract per symbol.

The oracle supports:

* single and batch price reads
* freshness-checked reads
* session-aware payloads
* bid and ask values alongside the main oracle price

All price fields use `8` decimals.

## Deployment and Backend Enablement

The integration draft records the intended mainnet proxy address as the same deterministic address on Ethereum, Monad, and Base:

| Network  | Chain ID | StockOracle proxy                            |
| -------- | -------: | -------------------------------------------- |
| Ethereum |      `1` | `0x037848af338c38e1e0ab722be80bf4c2e612a1f7` |
| Monad    |    `143` | `0x037848af338c38e1e0ab722be80bf4c2e612a1f7` |
| Base     |   `8453` | `0x037848af338c38e1e0ab722be80bf4c2e612a1f7` |

The draft also records implementation address `0x8745912828473e9bef9d843ce8629bee3dcb9fec` for reference only. Consumers should integrate with the proxy address, not the implementation address.

## Price IDs

Each supported asset is identified by `bytes32 priceId`.

The standard derived ID is:

```
priceId = keccak256(uppercase(trim(symbol)))
```

For example, the backend derives `AAPL` from the uppercase symbol bytes. Deployment scripts may also accept an explicit `bytes32 priceId`; if omitted, they use the same uppercase-symbol derivation.

Before reading a price, consumers can check:

```solidity
function isSupportedPriceId(bytes32 priceId) external view returns (bool);
function hasPrice(bytes32 priceId) external view returns (bool);
function description(bytes32 priceId) external view returns (string memory);
```

`isSupportedPriceId=true` means the ID is registered. `hasPrice=true` means at least one price has been published.

## Price Payload

Oracle reads return:

```solidity
enum Session {
    UNKNOWN,
    PRE_MARKET,
    REGULAR,
    POST_MARKET,
    OVERNIGHT,
    CLOSED
}

struct PricePayload {
    int128 price;
    int128 bestBid;
    int128 bestAsk;
    uint64 feedUpdateTimestamp;
    uint64 publishedAt;
    uint80 roundId;
    Session session;
}
```

| Field                 | Meaning                                                                 |
| --------------------- | ----------------------------------------------------------------------- |
| `price`               | Main oracle price, 8 decimals. Backend currently uses bid/ask midpoint. |
| `bestBid`             | Best bid price, 8 decimals.                                             |
| `bestAsk`             | Best ask price, 8 decimals.                                             |
| `feedUpdateTimestamp` | Source quote timestamp, in Unix seconds. Use this for freshness.        |
| `publishedAt`         | On-chain publish timestamp for this oracle round, in Unix seconds.      |
| `roundId`             | Monotonic oracle update round for this `priceId`.                       |
| `session`             | Market session associated with the source quote.                        |

Consumers should use `feedUpdateTimestamp` for staleness checks. `publishedAt` only says when the payload was written on-chain.

## Read APIs

Single-asset reads:

```solidity
function latestPrice(bytes32 priceId)
    external
    view
    returns (PricePayload memory);

function latestPriceNoOlderThan(bytes32 priceId, uint64 maxAge)
    external
    view
    returns (PricePayload memory);
```

Batch reads:

```solidity
function latestPrices(bytes32[] calldata priceIds)
    external
    view
    returns (PricePayload[] memory);

function latestPricesNoOlderThan(bytes32[] calldata priceIds, uint64 maxAge)
    external
    view
    returns (PricePayload[] memory);
```

Prefer `latestPriceNoOlderThan` or `latestPricesNoOlderThan` when the consuming contract must fail closed on stale source data. `maxAge` is denominated in seconds.

Current backend feeder comments note that the source quote can be about 15 minutes delayed. With the default 10-minute heartbeat, a consumer using `latestPriceNoOlderThan(maxAge)` should choose a `maxAge` that accounts for feed delay plus heartbeat and integration-specific risk tolerance; 25 minutes / 1500 seconds is a practical lower-bound example before adding any product-specific buffer.

## Read-Side Reverts

| Error               | Meaning                                                      |
| ------------------- | ------------------------------------------------------------ |
| `PriceIdNotFound`   | The `priceId` is not registered.                             |
| `PriceNotAvailable` | The `priceId` is registered but no price has been published. |
| `PriceTooOld`       | `block.timestamp - feedUpdateTimestamp > maxAge`.            |

## Write-Side Constraints

The backend feeder and deployment scripts are expected to satisfy these constraints before publishing:

| Constraint       | Contract behavior                                                                                                                                                    |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Registering IDs  | `registerPriceId` and `batchRegisterPriceIds` require `DEFAULT_ADMIN_ROLE`.                                                                                          |
| Updating prices  | `updatePrice` and `batchUpdatePrice` require `OPERATOR_ROLE`.                                                                                                        |
| Price validity   | `price`, `bestBid`, and `bestAsk` must be positive and fit into `int128`.                                                                                            |
| Spread validity  | `bestBid` cannot exceed `bestAsk`.                                                                                                                                   |
| Source timestamp | `feedUpdateTimestamp` cannot be in the future and cannot roll back below the current on-chain value.                                                                 |
| Same timestamp   | If `feedUpdateTimestamp` equals the current on-chain timestamp, the update is allowed only for a `CLOSED` carried-forward update with unchanged price, bid, and ask. |

These details are mainly useful for debugging feeder reverts and operational incidents. Normal readers do not need write permissions.

## Session Handling

Consumers should treat `session` as part of the risk model, not just display metadata.

Regular-session prices are generally the most liquid. Pre-market, post-market, and overnight prices may have wider spreads or lower coverage. `CLOSED` means the oracle carried forward the latest on-chain price into a closed-market state; it should not be treated the same as an active quote unless the integration explicitly supports that behavior.

For lending or risk-sensitive integrations, define per-asset and per-session controls for:

* maximum accepted age
* accepted spread or spread-derived haircut
* whether `CLOSED` prices are allowed
* fallback behavior when `PriceIdNotFound`, `PriceNotAvailable`, or `PriceTooOld` occurs


# Symbols & Tradability

## List tradable symbols

```
GET /api/v1/symbols
```

Returns `data` array items:

| Field                 | Type    | Description                          |
| --------------------- | ------- | ------------------------------------ |
| `symbol`              | string  | Stock symbol, e.g. `AAPL`.           |
| `contractAddress`     | string  | On-chain token address.              |
| `contractSymbol`      | string  | Contract symbol.                     |
| `contractName`        | string  | Contract name.                       |
| `decimals`            | number  | Contract decimals.                   |
| `onChainDecimals`     | number  | On-chain decimals.                   |
| `tradable`            | boolean | Tradable flag.                       |
| `fractionable`        | boolean | Fractional shares supported.         |
| `overnightTradable`   | boolean | Overnight session supported.         |
| `fractionalEhEnabled` | boolean | Fractional extended-hours supported. |
| `price`               | number  | Latest price.                        |
| `change24H`           | number  | 24h price change.                    |
| `change24HPercent`    | number  | 24h change percent.                  |
| `logoUrl`             | string  | Logo URL.                            |
| `lastUpdateTimestamp` | number  | Cache update (Unix seconds).         |
| `name`                | string  | Company name.                        |
| `pdfUrl`              | string  | Prospectus PDF URL.                  |
| `volume24H`           | number  | 24h volume.                          |

Use `contractAddress` as `stockAddress` when placing orders.

## Underlying stock quote

```
GET /api/v1/underlying/{symbol}/price
```

| Parameter | Type   | Description   |
| --------- | ------ | ------------- |
| `symbol`  | string | Stock symbol. |

Response fields (returned at the **top level** of `data`, not wrapped in a `basicInfo` object):

| Field                 | Type    | Description                  |
| --------------------- | ------- | ---------------------------- |
| `symbol`              | string  | Stock symbol.                |
| `contractAddress`     | string  | Token contract address.      |
| `contractSymbol`      | string  | Contract symbol.             |
| `onChainDecimals`     | number  | On-chain decimals.           |
| `name`                | string  | Company name.                |
| `exchange`            | string  | Exchange.                    |
| `status`              | string  | `active` or `inactive`.      |
| `price`               | number  | Latest price.                |
| `change24H`           | number  | 24h change.                  |
| `change24HPercent`    | number  | 24h change percent.          |
| `volume24H`           | number  | 24h volume.                  |
| `tradable`            | boolean | Tradable flag.               |
| `fractionable`        | boolean | Fractional shares.           |
| `lastUpdateTimestamp` | number  | Cache update (Unix seconds). |


# Quotes & History

## Real-time quote

```
GET /api/v1/prices/{symbol}
```

| Parameter | Type   | Description                                                         |
| --------- | ------ | ------------------------------------------------------------------- |
| `symbol`  | string | Stock symbol, token symbol, or token address. Uppercased by server. |

Response `data`:

| Field       | Type          | Description                                              |
| ----------- | ------------- | -------------------------------------------------------- |
| `basicInfo` | object        | Underlying quote (chain-agnostic).                       |
| `onChain`   | object / null | On-chain token info for current `chainId + productType`. |

For direct on-chain reference-price reads, see [Stock Oracle](/rwa-trading-apis/market-data/stock-oracle).

## Historical K-line

```
GET /api/v1/prices/{symbol}/history
```

| Parameter    | Type   | Required | Default | Description                                    |
| ------------ | ------ | -------- | ------- | ---------------------------------------------- |
| `startTime`  | number | No       | —       | Start (Unix seconds).                          |
| `endTime`    | number | Yes      | —       | End (Unix seconds).                            |
| `interval`   | string | Yes      | —       | `1m`, `3m`, `5m`, `15m`, `1h`, `1d`, `1w`.     |
| `adjustment` | string | No       | `all`   | `raw`, `split`, `dividend`, `spin-off`, `all`. |
| `limit`      | number | No       | `100`   | Max bars. Server max `5000`.                   |

Bar fields:

| Field       | Type   | Description              |
| ----------- | ------ | ------------------------ |
| `open`      | number | Open.                    |
| `high`      | number | High.                    |
| `low`       | number | Low.                     |
| `close`     | number | Close.                   |
| `volume`    | number | Volume.                  |
| `timestamp` | number | Bar time (Unix seconds). |

## Corporate actions

```
GET /api/v1/corporate-action
```

Query stock corporate actions (splits, dividends, etc.) for planning and display.

| Parameter   | Type   | Required | Description                                                                  |
| ----------- | ------ | -------- | ---------------------------------------------------------------------------- |
| `symbol`    | string | Yes      | Stock symbol without `a` prefix, e.g. `AAPL`.                                |
| `startTime` | number | No       | Start filter (Unix seconds). Filters on action `time`.                       |
| `endTime`   | number | No       | End filter (Unix seconds). Filters on action `time`.                         |
| `types`     | array  | No       | Filter by corporate action type. Repeat param or comma-separated per client. |

Allowed `types`:

| Value             | Description     |
| ----------------- | --------------- |
| `reverse_splits`  | Reverse split.  |
| `forward_splits`  | Forward split.  |
| `unit_splits`     | Unit split.     |
| `stock_dividends` | Stock dividend. |
| `cash_dividends`  | Cash dividend.  |

Example:

```
GET /api/v1/corporate-action?symbol=AAPL&types=cash_dividends&startTime=1704067200
```

### Response

Standard envelope. `data` is an array of `BrokerCorporateAction` objects, newest first when filtered by time.

| Field         | Type   | Description                                            |
| ------------- | ------ | ------------------------------------------------------ |
| `symbol`      | string | Stock symbol.                                          |
| `type`        | string | One of the `types` values above.                       |
| `time`        | string | Action timestamp (ISO-8601 date-time).                 |
| `exDate`      | string | Ex-dividend or ex-split date.                          |
| `recordDate`  | string | Record date.                                           |
| `payableDate` | string | Payable date.                                          |
| `processDate` | string | Processing date.                                       |
| `info`        | object | Type-specific broker metadata. Shape varies by `type`. |

### `info` object (varies by type)

| Field                 | Typical for | Description              |
| --------------------- | ----------- | ------------------------ |
| `rate`                | dividends   | Dividend rate per share. |
| `special`             | dividends   | Special dividend flag.   |
| `cusip`               | dividends   | CUSIP identifier.        |
| `oldRate` / `newRate` | splits      | Split ratio components.  |

Example response:

```json
{
  "code": 200,
  "errMsg": "",
  "data": [
    {
      "symbol": "AAPL",
      "type": "cash_dividends",
      "time": "2024-05-10T00:00:00Z",
      "exDate": "2024-05-10",
      "recordDate": "2024-05-13",
      "payableDate": "2024-05-16",
      "processDate": "2024-05-16",
      "info": {
        "rate": 0.25,
        "special": false,
        "cusip": "037833100"
      }
    }
  ],
  "uuid": null,
  "t": null
}
```

> Schema: `BrokerCorporateAction` in [openapi.snapshot.json](https://github.com/AnchoredLabs/anchored-knowledge/tree/api-doc/synfutures/02-products/rwa/api/reference/openapi.snapshot.json).


# Overview

Before placing orders, review the trader integration playbook in [getting-started/trader-questions.md](/rwa-trading-apis/getting-started/trader-questions): tradable symbol, correct balance type, execution mode, and status tracking path.

## Order types

| Type        | Side            | Required fields                    |
| ----------- | --------------- | ---------------------------------- |
| Market buy  | `Buy`           | `notional`                         |
| Market sell | `Sell`          | `quantity`                         |
| Limit       | `Buy` or `Sell` | `quantity`, `price`, `timeInForce` |

All orders require `stockAddress` (from [symbols list](/rwa-trading-apis/market-data/symbols-and-tradability)) and `deadline` (Unix seconds, ≤ `4294967295`).

> Use **strings** for `quantity`, `notional`, and `price` to avoid JS precision loss.

## Execution modes

| Mode        | Place order             | Cancel limit                        | Record tx         |
| ----------- | ----------------------- | ----------------------------------- | ----------------- |
| Self-submit | `POST /orders/calldata` | `DELETE /orders/{orderId}/calldata` | `POST /orders/tx` |
| One Click   | `POST /orders/send`     | `DELETE /orders/{orderId}/send`     | Auto-recorded     |

One Click requires enabled delegation — see [one-click-delegated.md](/rwa-trading-apis/trading/one-click-delegated) and [guides/one-click-flow.md](/rwa-trading-apis/guides/one-click-flow).

## Typical flow

1. Check [portfolio](/rwa-trading-apis/portfolio/overview) for buying power and holdings.
2. Get quote from [market](/rwa-trading-apis/market-data/quotes-and-history) if needed.
3. Build or send order — [place-and-cancel-orders.md](/rwa-trading-apis/trading/place-and-cancel-orders).
4. Track status — [order-status-and-history.md](/rwa-trading-apis/trading/order-status-and-history).

Self-submit sequence: see [guides/self-submit-on-chain.md](/rwa-trading-apis/guides/self-submit-on-chain).


# Place & Cancel Orders

## Build order calldata

```
POST /api/v1/orders/calldata
```

| Field          | Type            | Required    | Description                                                   |
| -------------- | --------------- | ----------- | ------------------------------------------------------------- |
| `stockAddress` | string          | Yes         | Stock token address (EVM).                                    |
| `side`         | string          | Yes         | `Buy` or `Sell`.                                              |
| `type`         | string          | Yes         | `Market` or `Limit`.                                          |
| `quantity`     | string / number | Conditional | Limit orders; market sells.                                   |
| `notional`     | string / number | Conditional | Market buys.                                                  |
| `price`        | string / number | Conditional | Limit orders.                                                 |
| `timeInForce`  | string          | Conditional | Limit orders. See [enums](/rwa-trading-apis/reference/enums). |
| `deadline`     | number          | Yes         | Tx deadline (Unix seconds).                                   |

### Market buy

```json
{
  "stockAddress": "0x0000000000000000000000000000000000000001",
  "side": "Buy",
  "type": "Market",
  "notional": "10",
  "deadline": 1893456000
}
```

### Limit sell

```json
{
  "stockAddress": "0x0000000000000000000000000000000000000001",
  "side": "Sell",
  "type": "Limit",
  "quantity": "1.25",
  "price": "180.50",
  "timeInForce": "DAY",
  "deadline": 1893456000
}
```

### Limit buy

```json
{
  "stockAddress": "0x0000000000000000000000000000000000000001",
  "side": "Buy",
  "type": "Limit",
  "quantity": "0.05",
  "price": "180.50",
  "timeInForce": "DAY",
  "deadline": 1893456000
}
```

> Limit orders (**buy and sell**) use `quantity` + `price`; `notional` is only for **market buys**. Production limit orders accept only `timeInForce: "DAY"` — other values revert on-chain.

Response `data`:

| Field         | Type   | Description                              |
| ------------- | ------ | ---------------------------------------- |
| `chainId`     | number | Chain ID.                                |
| `productType` | string | Product type.                            |
| `toAddress`   | string | Router address.                          |
| `value`       | string | Native value (`"0"`).                    |
| `callData`    | string | Encoded calldata.                        |
| `method`      | string | `placeMarketOrder` or `placeLimitOrder`. |

Submit `toAddress` + `callData` on-chain, then record via [order-status-and-history.md](/rwa-trading-apis/trading/order-status-and-history).

## One Click place order

```
POST /api/v1/orders/send
```

Same body as `/calldata`, plus optional `gasLimit` (set a buffer — the API broadcasts through `OneClickRouter` and pays gas). Requires One Click enabled and API key `userAddress` configured.

The response is the **order-tx mapping**, not calldata: `{ id, chainId, productType, operationType, orderId, txHash, status, ... }`. `orderId` is `null` until the indexer backfills it — poll `GET /orders/tx/{txHash}` or `GET /orders/{orderId}` (once populated).

## Build Order-With-Deposit Calldata

```
POST /api/v1/orders/with-deposit/calldata
```

Use this endpoint when the order transaction should also use `StockRouter` as the approved spender before placing the order. For buys, `StockRouter` transfers the cash token to `Cashier`; for sells, it transfers the stock token to `Stock`.

| Field                 | Type            | Required    | Description                                                                                                |
| --------------------- | --------------- | ----------- | ---------------------------------------------------------------------------------------------------------- |
| `stockAddress`        | string          | Yes         | Stock token address.                                                                                       |
| `depositAmount`       | string / number | Yes         | Human-readable deposit amount. For buys, this is cash token amount. For sells, this is stock token amount. |
| `depositTokenAddress` | string          | Conditional | Cash token address. Required for buy orders.                                                               |
| `side`                | string          | Yes         | `Buy` or `Sell`.                                                                                           |
| `type`                | string          | Yes         | `Market` or `Limit`.                                                                                       |
| `quantity`            | string / number | Conditional | Limit orders; market sells.                                                                                |
| `notional`            | string / number | Conditional | Market buys.                                                                                               |
| `price`               | string / number | Conditional | Limit orders.                                                                                              |
| `timeInForce`         | string          | Conditional | Limit orders.                                                                                              |
| `deadline`            | number          | Yes         | Tx deadline (Unix seconds).                                                                                |

For buy orders, the cash token must be approved for `StockRouter`, and the cash deposit must be eligible for instant deposit; otherwise the combined transaction reverts. For sell orders, the wallet stock token must be approved for `StockRouter`.

### Market buy with deposit

```json
{
  "stockAddress": "0x0000000000000000000000000000000000000001",
  "depositTokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  "depositAmount": "100",
  "side": "Buy",
  "type": "Market",
  "notional": "10",
  "deadline": 1893456000
}
```

### Market sell with stock deposit

```json
{
  "stockAddress": "0x0000000000000000000000000000000000000001",
  "depositAmount": "1.25",
  "side": "Sell",
  "type": "Market",
  "quantity": "1.25",
  "deadline": 1893456000
}
```

## One Click Order With Deposit

```
POST /api/v1/orders/with-deposit/send
```

Same body as `/with-deposit/calldata`, plus optional `gasLimit`. Requires One Click enabled and API key `userAddress` configured.

## Cancel limit order

### Self-submit cancel calldata

```
DELETE /api/v1/orders/{orderId}/calldata?deadline=1893456000
```

> Use the `orderId` **exactly as returned by the API** — a `0x`-prefixed 32-byte hex string (66 characters), e.g. `0x00000000000049fb0001000049d0d92daa26b7f121276e7034af17aed9e4c71e`. Put it in the path as-is: `DELETE /api/v1/orders/0x00000000000049fb0001000049d0d92daa26b7f121276e7034af17aed9e4c71e/calldata?deadline=1893456000`

### One Click cancel

```
DELETE /api/v1/orders/{orderId}/send?deadline=1893456000
```

Returns `uuid` and `txHash`.

### Cancel rules

* Order belongs to current API key
* Order is open
* Limit orders only
* No pending cancel in flight
* API key `userAddress` matches order user


# Order Status & History

## Record self-submitted order tx

```
POST /api/v1/orders/tx
```

| Field    | Type   | Required | Description                |
| -------- | ------ | -------- | -------------------------- |
| `txHash` | string | Yes      | On-chain transaction hash. |

Indexer backfills `orderId` from chain events.

> Same `txHash` from the same API key returns the existing mapping. A `txHash` already owned by another key returns an error.

## Query orders

### List mappings

```
GET /api/v1/orders?page=1&limit=10
```

### By tx hash

```
GET /api/v1/orders/tx/{txHash}
```

### By order ID

```
GET /api/v1/orders/{orderId}
```

`orderId` must belong to a mapping recorded by the current API key.

## Order detail fields

**openOrder:**

| Field           | Type   | Description                                                       |
| --------------- | ------ | ----------------------------------------------------------------- |
| `orderId`       | string | On-chain order ID.                                                |
| `userAddress`   | string | User address.                                                     |
| `side`          | string | `Buy` or `Sell`.                                                  |
| `type`          | string | `Market` or `Limit`.                                              |
| `tif`           | string | Time in force.                                                    |
| `symbol`        | string | Symbol.                                                           |
| `placeNotional` | string | Order notional (`0` for limit orders, which use `placeQuantity`). |
| `placeQuantity` | string | Order quantity.                                                   |
| `pay`           | string | Payment amount.                                                   |
| `placePrice`    | string | Order (limit) price.                                              |
| `status`        | string | `placing` or `canceling`.                                         |

**historyOrder** adds:

| Field           | Type          | Description      |
| --------------- | ------------- | ---------------- |
| `settleTxHash`  | string / null | Settlement tx.   |
| `settlePrice`   | string        | Execution price. |
| `settlePay`     | string / null | Actual payment.  |
| `settleReceive` | string / null | Actual received. |
| `mintFee`       | string        | Mint fee.        |
| `protocolFee`   | string        | Protocol fee.    |

See [reference/enums.md](/rwa-trading-apis/reference/enums) for status values.


# One Click Delegated

One Click lets the server submit transactions via an authorized proxy wallet. Required before calling any `/send` endpoint.

For user-facing safety copy, signer checks, and delegation UX, read [one-click-security-ux.md](/rwa-trading-apis/trading/one-click-security-ux).

## Query status

```
GET /api/v1/1ct/status
```

| Field       | Type   | Description                |
| ----------- | ------ | -------------------------- |
| `status`    | string | `/send` requires `enable`. |
| `delegatee` | string | Proxy wallet address.      |

## Prepare EIP-712 payload

```
POST /api/v1/1ct/prepare
```

| Field      | Type   | Required | Description                        |
| ---------- | ------ | -------- | ---------------------------------- |
| `deadline` | number | Yes      | Signature deadline (Unix seconds). |

Response `data`:

| Field         | Type   | Description         |
| ------------- | ------ | ------------------- |
| `delegatee`   | string | Proxy address.      |
| `signPayload` | object | EIP-712 typed data. |
| `nonce`       | number | Delegate nonce.     |
| `deadline`    | number | Same as request.    |

Example `signPayload`:

```json
{
  "domain": {
    "name": "OneClickRouter",
    "version": "1",
    "chainId": 10143,
    "verifyingContract": "0x..."
  },
  "types": {
    "Delegate": [
      { "name": "user", "type": "address" },
      { "name": "delegatee", "type": "address" },
      { "name": "nonce", "type": "uint64" },
      { "name": "deadline", "type": "uint32" }
    ]
  },
  "primaryType": "Delegate",
  "message": {
    "user": "0x...",
    "delegatee": "0x...",
    "nonce": 0,
    "deadline": 1893456000
  }
}
```

> The live `signPayload.types` also contains an `EIP712Domain` entry (omitted above for brevity). **ethers** ignores it — pass only `{ Delegate: ... }` as shown below. **viem** derives the domain itself and *throws* if `EIP712Domain` is present in `types`, so strip it first (see [guides/demo-code.md](/rwa-trading-apis/guides/demo-code)).

## Enable

```
POST /api/v1/1ct/enable
```

| Field       | Type   | Required | Description                                  |
| ----------- | ------ | -------- | -------------------------------------------- |
| `signature` | string | Yes      | 65-byte hex EIP-712 signature (`0x` prefix). |
| `deadline`  | number | Yes      | Must match `/prepare` response.              |

### ethers v6 signing

```js
import { Wallet } from "ethers";

const { signPayload, deadline } = prepareResponse.data;
const wallet = new Wallet(process.env.USER_PRIVATE_KEY);

if (wallet.address.toLowerCase() !== signPayload.message.user.toLowerCase()) {
  throw new Error("signer address must match signPayload.message.user");
}

const signature = await wallet.signTypedData(
  { ...signPayload.domain, chainId: BigInt(signPayload.domain.chainId) },
  { Delegate: signPayload.types.Delegate },
  {
    ...signPayload.message,
    nonce: BigInt(signPayload.message.nonce),
    deadline: BigInt(signPayload.message.deadline),
  }
);
```

## Disable

```
POST /api/v1/1ct/disable
```

No body. Use empty string for HMAC `RAW_BODY`.

## Send endpoints after enable

| Operation                | Endpoint                           | Method |
| ------------------------ | ---------------------------------- | ------ |
| Place order              | `/api/v1/orders/send`              | POST   |
| Place order with deposit | `/api/v1/orders/with-deposit/send` | POST   |
| Cancel order             | `/api/v1/orders/{orderId}/send`    | DELETE |
| Cash deposit             | `/api/v1/cash/deposits/send`       | POST   |
| Cash withdrawal          | `/api/v1/cash/withdrawals/send`    | POST   |
| Stock deposit            | `/api/v1/stock/deposits/send`      | POST   |
| Stock withdrawal         | `/api/v1/stock/withdrawals/send`   | POST   |

Flow: build calldata internally → submit via One Click → auto-record tx on success.

See [guides/one-click-flow.md](/rwa-trading-apis/guides/one-click-flow).


# One Click Security and API Semantics

One Click reduces repeated wallet prompts by letting a delegated service submit router transactions for a user. It does **not** change the underlying trading contracts: orders, deposits, withdrawals, and cancels still go through `StockRouter`.

## Mental Model

```mermaid
flowchart LR
    U[User wallet] -->|signs EIP-712 Delegate| API[Trading API]
    API -->|enable| OCR[OneClickRouter]
    D[Delegatee service] -->|execute as user| OCR
    OCR -->|ERC-2771 forward| R[StockRouter]
    R --> S[Cashier and Stock]
```

The user signs a typed delegation message. The delegatee service can then forward allowed router calls through `OneClickRouter`. The downstream router resolves the original user through ERC-2771 forwarding.

## Authorization Scope

Before enabling One Click, the integration should treat the delegation as a scoped transaction authorization:

* The delegatee can submit supported RWA trading router calls.
* The delegatee can place orders, cancel orders, deposit, and withdraw through allowed `/send` flows.
* The authorization is tied to the chain, OneClickRouter, delegatee, nonce, and deadline in the EIP-712 payload.
* Delegation can be disabled.

## API Flow

```mermaid
sequenceDiagram
    participant Client
    participant API as Trading API
    participant User
    participant OCR as OneClickRouter

    Client->>API: GET /1ct/status
    API-->>Client: status and delegatee
    Client->>API: POST /1ct/prepare
    API-->>Client: EIP-712 signPayload
    Client->>User: Request typed-data signature
    User-->>Client: signature
    Client->>API: POST /1ct/enable
    API->>OCR: delegateBySig
    API-->>Client: enabled
```

## Implementation Rules

| Rule                                            | Why                                                                      |
| ----------------------------------------------- | ------------------------------------------------------------------------ |
| Verify signer equals `signPayload.message.user` | Prevent enabling delegation for the wrong wallet                         |
| Use the returned `deadline` exactly             | It is part of the signed payload                                         |
| Treat nonce as single-use                       | Reusing old signatures should fail                                       |
| Check `/1ct/status` before `/send`              | Avoid avoidable send failures                                            |
| Keep `userAddress` binding explicit             | `/send` uses the API key bound user, not arbitrary request `userAddress` |
| Always support disable                          | Users need a clear recovery path                                         |

## API States

Treat One Click as a small API state machine: not enabled, preparing, signature requested, enabled, and disable pending. The only state that should allow `/send` endpoints is `Enabled`.

State mapping:

| State               | Integration handling                                       |
| ------------------- | ---------------------------------------------------------- |
| Not enabled         | Do not call `/send`; use `/calldata` or prepare delegation |
| Preparing           | Call `/1ct/prepare` and request typed-data signature       |
| Signature requested | Wait for a valid user signature                            |
| Enabled             | `/send` endpoints can be used                              |
| Disable pending     | Do not assume future `/send` requests will succeed         |

## Deposit Caveat

One Click does not remove ERC-20 approval requirements. For `/cash/deposits/send`, the API key bound `userAddress` must still approve the cash token spender.

## Risk Boundaries

One Click should be treated as a powerful trading authorization:

* Do not hide the delegatee address.
* Do not reuse signatures across chains or routers.
* Do not submit `/send` for a different user than the bound `userAddress`.
* Log `uuid`, `txHash`, and operation/order IDs for audit.

## Related Docs

| Need                   | Doc                                                                            |
| ---------------------- | ------------------------------------------------------------------------------ |
| Endpoint details       | [one-click-delegated.md](/rwa-trading-apis/trading/one-click-delegated)        |
| End-to-end flow        | [../guides/one-click-flow.md](/rwa-trading-apis/guides/one-click-flow)         |
| viem typed-data sample | [../guides/demo-code.md](/rwa-trading-apis/guides/demo-code)                   |
| Troubleshooting        | [../reference/troubleshooting.md](/rwa-trading-apis/reference/troubleshooting) |


# Overview

Docs for deposits, withdrawals, and cash operation status.

| Need                              | Doc                                                                       |
| --------------------------------- | ------------------------------------------------------------------------- |
| Understand instant vs queued cash | [buffer-mechanism.md](/rwa-trading-apis/cash-operations/buffer-mechanism) |
| Deposit cash into `mUSD`          | [deposits.md](/rwa-trading-apis/cash-operations/deposits)                 |
| Withdraw `mUSD` to tokens         | [withdrawals.md](/rwa-trading-apis/cash-operations/withdrawals)           |
| Track deposit/withdraw status     | [operation-status.md](/rwa-trading-apis/cash-operations/operation-status) |

API model: deposits turn wallet cash tokens into `mUsdBalance`; withdrawals turn `mUsdBalance` back into wallet cash tokens. Buffers only determine whether that happens instantly or after queued settlement.


# Deposits

Deposits turn wallet cash tokens into `mUSD` (`mUsdBalance`). See [getting-started/trader-questions.md](/rwa-trading-apis/getting-started/trader-questions) for the API flow and the distinction between wallet balance and `mUSD`.

Deposits may complete instantly if the Cashier `creditBuffer` can cover the `mUSD` credit and the token withdrawal buffer has room. Otherwise they are queued for final cash settlement. See [buffer-mechanism.md](/rwa-trading-apis/cash-operations/buffer-mechanism).

## Pre-authorization

Deposits transfer cash tokens from the user wallet to `Cashier` through `StockRouter`. The user must `approve` `StockRouter` as the spender before deposit:

```
ERC20(tokenAddress).approve(stockRouter, rawTokenAmount)
```

`rawTokenAmount = tokenAmount × 10^tokenDecimals`

### Mainnet cash token and approval spender addresses

| `x-api-chain-id` | Chain            | Cash token                                   | Approval spender (`StockRouter`)             |
| ---------------- | ---------------- | -------------------------------------------- | -------------------------------------------- |
| `8453`           | Base Mainnet     | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | `0x4f090d817fd83753988a7b0c1d76f170f8461be8` |
| `143`            | Monad Mainnet    | `0x754704Bc059F8C67012fEd69BC8A327a5aafb603` | `0x4f090d817fd83753988a7b0c1d76f170f8461be8` |
| `1`              | Ethereum Mainnet | `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` | `0x4f090d817fd83753988a7b0c1d76f170f8461be8` |

Check allowance:

```
GET /api/v1/users/{userId}/balance?spenderAddress={spender}
```

Use `tokenBalances[].walletAllowance`.

> Insufficient allowance: `/cash/deposits/calldata` still returns calldata, but the on-chain tx will fail.

Approval owner:

* `/cash/deposits/send` (One Click): API key bound `userAddress`
* `/cash/deposits/calldata` (self-submit): transaction sender wallet

## Build deposit calldata

```
POST /api/v1/cash/deposits/calldata
```

| Field          | Type            | Required | Description              |
| -------------- | --------------- | -------- | ------------------------ |
| `userAddress`  | string          | Yes      | User wallet.             |
| `tokenAddress` | string          | Yes      | Cash token address.      |
| `tokenAmount`  | string / number | Yes      | Amount (human-readable). |

Returns `method: deposit`, `value: "0"`.

## One Click deposit

```
POST /api/v1/cash/deposits/send
```

| Field          | Type            | Required | Description         |
| -------------- | --------------- | -------- | ------------------- |
| `tokenAddress` | string          | Yes      | Cash token address. |
| `tokenAmount`  | string / number | Yes      | Deposit amount.     |
| `gasLimit`     | number          | No       | Optional gas limit. |

Uses API key bound `userAddress` — not a request body field.

## Record deposit tx

```
POST /api/v1/cash/deposits/tx
```

| Field    | Type   | Required | Description      |
| -------- | ------ | -------- | ---------------- |
| `txHash` | string | Yes      | Deposit tx hash. |

## Query deposits

```
GET /api/v1/cash/deposits?page=1&limit=10
GET /api/v1/cash/deposits/{operationId}
```

`operationId` is backfilled by the indexer after parsing chain events.

See [operation-status.md](/rwa-trading-apis/cash-operations/operation-status) for response fields.


# Withdrawals

Withdrawals deduct `mUSD` (`mUsdBalance`) and return cash tokens to the user. They may complete instantly if the token withdrawal buffer has enough liquidity; otherwise they are queued for final cash settlement. See [buffer-mechanism.md](/rwa-trading-apis/cash-operations/buffer-mechanism).

## Build withdrawal calldata

```
POST /api/v1/cash/withdrawals/calldata
```

| Field          | Type            | Required | Description                          |
| -------------- | --------------- | -------- | ------------------------------------ |
| `userAddress`  | string          | Yes      | User wallet.                         |
| `tokenAddress` | string          | Yes      | Cash token address.                  |
| `creditAmount` | string / number | Yes      | Credit to withdraw (human-readable). |

Returns `method: withdraw`, `value: "0"`.

## One Click withdrawal

```
POST /api/v1/cash/withdrawals/send
```

Same body fields as calldata (no `userAddress` in body — uses API key bound address). Optional `gasLimit`.

Requires One Click enabled — see [trading/one-click-delegated.md](/rwa-trading-apis/trading/one-click-delegated).

## Record withdrawal tx

```
POST /api/v1/cash/withdrawals/tx
```

| Field    | Type   | Required | Description         |
| -------- | ------ | -------- | ------------------- |
| `txHash` | string | Yes      | Withdrawal tx hash. |

## Query withdrawals

```
GET /api/v1/cash/withdrawals?page=1&limit=10
GET /api/v1/cash/withdrawals/{operationId}
```

See [operation-status.md](/rwa-trading-apis/cash-operations/operation-status) for response fields.


# Operation Status

Shared fields for deposit and withdrawal detail responses.

Cash operations can be instant or queued depending on Cashier buffers. See [buffer-mechanism.md](/rwa-trading-apis/cash-operations/buffer-mechanism) for the buffer mechanism explanation.

## Response shape

`TradingCashOperationDetailRespDto`:

| Field                 | Type          | Description                |
| --------------------- | ------------- | -------------------------- |
| `mappingStatus`       | string / null | TX mapping status.         |
| `operationStatus`     | string / null | On-chain operation status. |
| `mapping`             | object        | Mapping record.            |
| `depositOperation`    | object / null | Deposit details.           |
| `withdrawalOperation` | object / null | Withdrawal details.        |

## depositOperation fields

| Field          | Type          | Description                                 |
| -------------- | ------------- | ------------------------------------------- |
| `operationId`  | string        | Deposit operation ID.                       |
| `userAddress`  | string        | User address.                               |
| `tokenAddress` | string        | Token address.                              |
| `status`       | string        | Operation status.                           |
| `isInstant`    | boolean       | Whether the operation used instant buffers. |
| `amount`       | number        | Token amount.                               |
| `creditAmount` | number        | `mUSD` amount.                              |
| `feeAmount`    | number        | Fee amount.                                 |
| `createTxHash` | string        | Creation tx.                                |
| `settleTxHash` | string / null | Settlement tx.                              |

## withdrawalOperation fields

| Field                | Type          | Description                                  |
| -------------------- | ------------- | -------------------------------------------- |
| `operationId`        | string        | Withdrawal operation ID.                     |
| `userAddress`        | string        | User address.                                |
| `tokenAddress`       | string        | Token address.                               |
| `status`             | string        | Operation status.                            |
| `isInstant`          | boolean       | Whether the operation used instant buffers.  |
| `creditAmount`       | number        | `mUSD` amount deducted.                      |
| `amount`             | number        | Token amount to pay out.                     |
| `feeAmount`          | number        | Fee amount.                                  |
| `failedPayoutAmount` | number / null | Token amount that failed to pay out, if any. |
| `createTxHash`       | string        | Creation tx.                                 |
| `settleTxHash`       | string / null | Settlement tx.                               |

`isInstant` indicates whether the operation used Cashier buffers. Queued operations can remain `processing` until final cash settlement completes.

## Status enums

| operationStatus | Description |
| --------------- | ----------- |
| `requested`     | Requested.  |
| `processing`    | Processing. |
| `settled`       | Settled.    |
| `closed`        | Closed.     |

`mappingStatus` reuses order mapping statuses — see [reference/enums.md](/rwa-trading-apis/reference/enums).

## Query endpoints

| Operation | List                           | Detail                                       |
| --------- | ------------------------------ | -------------------------------------------- |
| Deposit   | `GET /api/v1/cash/deposits`    | `GET /api/v1/cash/deposits/{operationId}`    |
| Withdraw  | `GET /api/v1/cash/withdrawals` | `GET /api/v1/cash/withdrawals/{operationId}` |

After self-submit, poll by `operationId` once the indexer backfills it from `txHash`.


# Buffer Mechanism

The stock contracts use a dual-buffer design so small deposits and withdrawals can settle instantly without waiting for the full asynchronous cash settlement path. This page explains the mechanism from an API integration perspective.

## Why Buffers Exist

Deposits and withdrawals may need asynchronous cash settlement. Waiting for that full path on every operation would add latency to every API flow, so `Cashier` can use pre-funded liquidity to apply the result first and let final settlement catch up later.

The Cashier therefore keeps pre-funded buffers:

| Buffer             | Unit                             | Used for            | API-visible result                |
| ------------------ | -------------------------------- | ------------------- | --------------------------------- |
| `creditBuffer`     | `mUSD`, 18-decimal WAD           | Instant deposits    | `mUsdBalance` updates immediately |
| `withdrawalBuffer` | Per-token amount, token decimals | Instant withdrawals | Cash token transfers immediately  |

If the relevant buffer can cover the operation safely, the operation is instant. Otherwise it is queued and settled later.

## Deposit Path

```mermaid
sequenceDiagram
    participant U as User
    participant R as StockRouter
    participant C as Cashier

    U->>R: deposit token
    R->>C: deposit(user, token, amount)
    alt creditBuffer available and withdrawalBuffer has room
        C->>C: reduce creditBuffer
        C->>C: increase withdrawalBuffer
        C->>U: credit mUsdBalance instantly
    else buffer criteria not met
        C->>C: record pending deposit
        Note over C: final settlement completes later
        C->>U: credit mUsdBalance later
    end
```

On an instant deposit:

* Cash token moves in from the wallet.
* Cashier consumes `creditBuffer`.
* `mUsdBalance` updates immediately.
* The token amount, net of fee, replenishes that token's `withdrawalBuffer` up to capacity.

On a queued deposit:

* Cashier records a pending deposit operation.
* `mUSD` is not credited immediately.
* Cashier credits `mUSD` after final settlement.

## Withdrawal Path

```mermaid
sequenceDiagram
    participant U as User
    participant R as StockRouter
    participant C as Cashier

    U->>R: withdraw credit
    R->>C: withdraw(user, token, creditAmount)
    C->>C: deduct mUsdBalance
    alt withdrawalBuffer available
        C->>C: reduce withdrawalBuffer
        C->>C: replenish creditBuffer
        C->>U: transfer token instantly
    else buffer criteria not met
        C->>C: record pending withdrawal
        Note over C: final settlement completes later
        C->>U: transfer token later
    end
```

On an instant withdrawal:

* `mUsdBalance` is deducted first.
* Cashier pays the token from `withdrawalBuffer`.
* The deducted credit replenishes `creditBuffer` up to capacity.

On a queued withdrawal:

* `mUsdBalance` is deducted when the withdrawal is requested.
* Cashier records a pending withdrawal operation.
* Cashier transfers tokens after final settlement.

## Instant Eligibility

The contracts intentionally avoid draining buffers in one operation. An operation must be small enough relative to the current buffer and the buffer must have enough available capacity.

| Operation  | Instant requirements, simplified                                                                                                               |
| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| Deposit    | Deposit credit is no more than `creditBuffer / instantThresholdDivisor`, `creditBuffer` can cover it, and the token withdrawal buffer has room |
| Withdrawal | Withdrawal token value is no more than `withdrawalBuffer / instantThresholdDivisor`, and `withdrawalBuffer` can cover it                       |

Synfutures production uses `instantThresholdDivisor = 5`, so an instant operation is normally limited to at most 20% of the relevant current buffer. Operators can configure this value.

## Cross-Buffer Replenishment

The two buffers feed each other:

| Operation          | Consumes                 | Replenishes              |
| ------------------ | ------------------------ | ------------------------ |
| Instant deposit    | `creditBuffer`           | Token `withdrawalBuffer` |
| Instant withdrawal | Token `withdrawalBuffer` | `creditBuffer`           |

This is why deposits help future withdrawals and withdrawals help future deposits. Buffer manager operations can also rebalance or bootstrap buffers without changing the API flow.

## API-Visible Results

| Result                         | Likely meaning                                          | Integration guidance                      |
| ------------------------------ | ------------------------------------------------------- | ----------------------------------------- |
| `isInstant = true`             | Operation used available buffer                         | Treat operation as instantly applied      |
| `operationStatus = processing` | Operation is queued or settlement path is still running | Continue polling; do not treat as failure |
| `operationStatus = settled`    | Cashier has applied final credit or payout              | Refresh portfolio state                   |

For deposits, track when `mUsdBalance` updates. For withdrawals, track when tokens arrive in the wallet. The buffer mechanism only changes timing, not the eventual accounting path.

## Integration Guidance

* Do not assume every deposit or withdrawal is instant.
* Treat queued cash operations as normal product behavior.
* Poll the operation detail endpoint and refresh portfolio after final status.
* If a large operation is queued, retrying the same amount does not make it instant; it is constrained by current buffer size and capacity.

Related docs:

| Need                       | Doc                                                                                                  |
| -------------------------- | ---------------------------------------------------------------------------------------------------- |
| Live buffer and fee config | [../market/on-chain-config.md](/rwa-trading-apis/market-data/on-chain-config) (`GET /api/v1/config`) |
| Deposit endpoints          | [deposits.md](/rwa-trading-apis/cash-operations/deposits)                                            |
| Withdrawal endpoints       | [withdrawals.md](/rwa-trading-apis/cash-operations/withdrawals)                                      |
| Cash status fields         | [operation-status.md](/rwa-trading-apis/cash-operations/operation-status)                            |
| Status model               | [../reference/status-model.md](/rwa-trading-apis/reference/status-model)                             |


# Overview

Docs for moving stock tokens between wallet balance and `Stock.stockBalance` (`exchangeBalance`).

| Need                             | Doc                                                                                        |
| -------------------------------- | ------------------------------------------------------------------------------------------ |
| Deposit stock token into `Stock` | [deposits-and-withdrawals.md](/rwa-trading-apis/stock-operations/deposits-and-withdrawals) |
| Withdraw stock token to wallet   | [deposits-and-withdrawals.md](/rwa-trading-apis/stock-operations/deposits-and-withdrawals) |


# Deposits & Withdrawals

Stock endpoints move ERC-20 stock tokens between wallet balance and `Stock.stockBalance`. The API surfaces `Stock.stockBalance` as `exchangeBalance`.

Cash endpoints are separate. Use `/cash/**` only for cash-token flows into or out of `Cashier`.

## Build Stock Deposit Calldata

```
POST /api/v1/stock/deposits/calldata
```

| Field          | Type            | Required | Description                        |
| -------------- | --------------- | -------- | ---------------------------------- |
| `userAddress`  | string          | Yes      | User wallet.                       |
| `tokenAddress` | string          | Yes      | Stock token contract address.      |
| `tokenAmount`  | string / number | Yes      | Human-readable stock token amount. |

The user wallet must approve `StockRouter` for the stock token before the transaction can move wallet stock into `Stock`.

## One Click Stock Deposit

```
POST /api/v1/stock/deposits/send
```

| Field          | Type            | Required | Description                        |
| -------------- | --------------- | -------- | ---------------------------------- |
| `tokenAddress` | string          | Yes      | Stock token contract address.      |
| `tokenAmount`  | string / number | Yes      | Human-readable stock token amount. |
| `gasLimit`     | number          | No       | Optional gas limit.                |

Uses API key bound `userAddress`; do not include `userAddress` in the request body.

## Record Stock Deposit Tx

```
POST /api/v1/stock/deposits/tx
```

| Field    | Type   | Required | Description            |
| -------- | ------ | -------- | ---------------------- |
| `txHash` | string | Yes      | Stock deposit tx hash. |

## Query Stock Deposits

```
GET /api/v1/stock/deposits?page=1&limit=10
GET /api/v1/stock/deposits/{operationId}
```

## Build Stock Withdrawal Calldata

```
POST /api/v1/stock/withdrawals/calldata
```

| Field          | Type            | Required | Description                        |
| -------------- | --------------- | -------- | ---------------------------------- |
| `userAddress`  | string          | Yes      | User wallet.                       |
| `tokenAddress` | string          | Yes      | Stock token contract address.      |
| `tokenAmount`  | string / number | Yes      | Human-readable stock token amount. |

Withdrawals deduct `Stock.stockBalance` and transfer ERC-20 stock tokens to the wallet.

## One Click Stock Withdrawal

```
POST /api/v1/stock/withdrawals/send
```

| Field          | Type            | Required | Description                        |
| -------------- | --------------- | -------- | ---------------------------------- |
| `tokenAddress` | string          | Yes      | Stock token contract address.      |
| `tokenAmount`  | string / number | Yes      | Human-readable stock token amount. |
| `gasLimit`     | number          | No       | Optional gas limit.                |

Uses API key bound `userAddress`; do not include `userAddress` in the request body.

## Record Stock Withdrawal Tx

```
POST /api/v1/stock/withdrawals/tx
```

| Field    | Type   | Required | Description               |
| -------- | ------ | -------- | ------------------------- |
| `txHash` | string | Yes      | Stock withdrawal tx hash. |

## Query Stock Withdrawals

```
GET /api/v1/stock/withdrawals?page=1&limit=10
GET /api/v1/stock/withdrawals/{operationId}
```

`operationId` is an API/indexer identifier for looking up the recorded stock operation. The `Stock` contract emits deposit and withdrawal events, but those events do not include a contract-native `operationId`.


# Overview

End-to-end guides for RWA API integrations.

| Need                       | Doc                                                                          |
| -------------------------- | ---------------------------------------------------------------------------- |
| First successful trade     | [quickstart-first-trade.md](/rwa-trading-apis/guides/quickstart-first-trade) |
| Self-submit flow           | [self-submit-on-chain.md](/rwa-trading-apis/guides/self-submit-on-chain)     |
| One Click flow             | [one-click-flow.md](/rwa-trading-apis/guides/one-click-flow)                 |
| TypeScript / viem examples | [demo-code.md](/rwa-trading-apis/guides/demo-code)                           |

Start with the quickstart, then use self-submit or One Click depending on who broadcasts router transactions.


# Quickstart: First API Trade

This guide walks through the smallest API flow for a partner integration:

1. Discover a tradable stock.
2. Confirm `mUSD`.
3. Deposit if needed.
4. Place a market buy.
5. Record and track the transaction.
6. Confirm the portfolio changed after settlement.

Use this as the smoke test before adding limit orders, withdrawals, or One Click.

## Flow

```mermaid
flowchart TB
    A[Configure API key] --> B[GET symbols]
    B --> C[Pick tradable symbol]
    C --> D[GET user balance]
    D --> E{Enough mUSD}
    E -->|no| F[Approve cash token]
    F --> G[Deposit cash]
    G --> H[Wait for mUSD]
    E -->|yes| I[Build market buy calldata]
    H --> I
    I --> J[Submit router transaction]
    J --> K[Record txHash]
    K --> L[Wait for orderId]
    L --> M[Poll order detail]
    M --> N[Confirm portfolio]
```

## 1. Configure Request Context

Every request must include:

| Header           | Example                             |
| ---------------- | ----------------------------------- |
| `x-api-key`      | Partner API key                     |
| `x-api-ts`       | Current Unix milliseconds           |
| `x-api-nonce`    | UUID                                |
| `x-api-sign`     | HMAC-SHA256 authentication value    |
| `x-api-chain-id` | `8453`, `143`, `1`, or target chain |
| `x-api-p`        | `Synfutures` for the Synfutures API |

Use the exact `/api/v1/...` path in the HMAC payload. Do not include the domain or `/rwa/trading`.

## 2. Discover a Tradable Stock

```
GET /api/v1/symbols
```

Pick an item where `tradable` is `true`. Relevant response fields:

| Field             | Use                               |
| ----------------- | --------------------------------- |
| `symbol`          | Symbol and quote lookup           |
| `contractAddress` | `stockAddress` for order requests |
| `price`           | Quote context                     |

Optional quote refresh:

```
GET /api/v1/prices/{symbol}
```

## 3. Check Portfolio

```
GET /api/v1/users/{userId}/balance
```

For a plain market buy, check `mUsdBalance`, the API field for `mUSD`. Wallet USDC is not buying power until it is deposited into `mUSD`. If the cash token is approved and instant deposit is available, `/api/v1/orders/with-deposit/calldata` can deposit and place the buy in one transaction.

## 4. Deposit Cash If Needed

First check allowance:

```
GET /api/v1/users/{userId}/balance?spenderAddress={spender}
```

If allowance is too low, have the user approve the spender from [cash/deposits.md](/rwa-trading-apis/cash-operations/deposits).

Then build deposit calldata:

```
POST /api/v1/cash/deposits/calldata
```

Example body:

```json
{
  "userAddress": "0x1111111111111111111111111111111111111111",
  "tokenAddress": "0x2222222222222222222222222222222222222222",
  "tokenAmount": "100"
}
```

Submit the returned `toAddress`, `value`, and `callData` on-chain. Then record:

```
POST /api/v1/cash/deposits/tx
```

Poll deposit detail until `operationStatus` is `settled` or `mUsdBalance` is updated.

## 5. Place a Market Buy

Build order calldata:

```
POST /api/v1/orders/calldata
```

Example body:

```json
{
  "stockAddress": "0x3333333333333333333333333333333333333333",
  "side": "Buy",
  "type": "Market",
  "notional": "10",
  "deadline": 1893456000
}
```

Submit the returned `StockRouter` transaction with your wallet client.

## 6. Record and Track the Order

Record the self-submitted tx:

```
POST /api/v1/orders/tx
```

Example:

```json
{
  "txHash": "0xabc..."
}
```

Then track:

```
GET /api/v1/orders/tx/{txHash}
GET /api/v1/orders/{orderId}
```

The transaction can be mined before execution and settlement are final. Treat the order as final only after it moves into history and portfolio balances reflect the settlement.

## 7. Confirm Portfolio

After settlement:

```
GET /api/v1/users/{userId}/balance
GET /api/v1/users/{userId}/positions
```

Expected result:

* `mUsdBalance` decreases by filled notional plus fees, with unspent `mUSD` refunded.
* The bought stock appears as `exchangeBalance`.
* Order detail shows settlement fields such as `settlePrice`, `settlePay`, and `settleReceive`.

## Next Steps

| Need                | Doc                                                                            |
| ------------------- | ------------------------------------------------------------------------------ |
| viem implementation | [demo-code.md](/rwa-trading-apis/guides/demo-code)                             |
| Debug failures      | [../reference/troubleshooting.md](/rwa-trading-apis/reference/troubleshooting) |
| Understand statuses | [../reference/status-model.md](/rwa-trading-apis/reference/status-model)       |
| Add One Click       | [one-click-flow.md](/rwa-trading-apis/guides/one-click-flow)                   |


# Self-Submit On-Chain

For integrators who sign and broadcast transactions with their own wallet or custodian.

## Workflow

```mermaid
sequenceDiagram
    participant Client
    participant API
    participant Chain
    participant Indexer

    Client->>API: POST /orders/calldata
    API-->>Client: calldata + toAddress
    Client->>Chain: Submit transaction
    Chain-->>Client: txHash
    Client->>API: POST /orders/tx { txHash }
    API-->>Client: Mapping record
    Indexer->>API: Parse on-chain events, backfill orderId
    Client->>API: GET /orders/{orderId}
    API-->>Client: Order status
```

## Steps

1. Call `/calldata` for the operation:
   * Orders: `POST /api/v1/orders/calldata`
   * Deposits: `POST /api/v1/cash/deposits/calldata`
   * Withdrawals: `POST /api/v1/cash/withdrawals/calldata`
2. Submit `toAddress`, `value`, and `callData` on-chain.
3. Call the matching `/tx` endpoint with `txHash`.
4. Indexer backfills `orderId` or `operationId`.
5. Poll status endpoints until settled.

> **Use a reliable RPC node.** `estimateGas`, `eth_call`, and `waitForTransactionReceipt` hit your RPC provider, not the Synfutures API. Free/public endpoints (including viem's default `http()` transport and free-tier providers) may rate-limit or reject `eth_call` and gas estimation, failing *before* the API is involved. Pass your own node URL to `http("<url>")`.
>
> **Add a gas buffer.** Default `estimateGas` can under-estimate router calls on some chains (e.g. Monad) and the tx reverts out-of-gas. Multiply the estimate (e.g. `× 1.5–2`) before submitting.

## Record tx endpoints

| Operation   | Endpoint                           |
| ----------- | ---------------------------------- |
| Place order | `POST /api/v1/orders/tx`           |
| Deposit     | `POST /api/v1/cash/deposits/tx`    |
| Withdraw    | `POST /api/v1/cash/withdrawals/tx` |

## Status queries

| Operation | Endpoints                                                        |
| --------- | ---------------------------------------------------------------- |
| Order     | `GET /api/v1/orders/tx/{txHash}`, `GET /api/v1/orders/{orderId}` |
| Deposit   | `GET /api/v1/cash/deposits/{operationId}`                        |
| Withdraw  | `GET /api/v1/cash/withdrawals/{operationId}`                     |

> Same `txHash` from the same API key is idempotent. A `txHash` already recorded by another API key returns an error.

## Deposit prerequisite

Complete ERC-20 `approve` before deposit calldata execution. See [cash/deposits.md](/rwa-trading-apis/cash-operations/deposits) and [demo-code.md](/rwa-trading-apis/guides/demo-code).


# One Click Flow

For API keys with a bound user wallet address. The server submits transactions after the user delegates once.

## Authorization

```mermaid
sequenceDiagram
    participant User
    participant Client
    participant API
    participant Chain

    Client->>API: GET /1ct/status
    API-->>Client: { status: "disabled" }
    Client->>API: POST /1ct/prepare { deadline }
    API-->>Client: signPayload (EIP-712)
    Client->>User: Request signature
    User->>Client: signature (65 bytes)
    Client->>API: POST /1ct/enable { signature, deadline }
    API->>Chain: delegateBySig
    API-->>Client: { status: "enable" }
```

## Steps

1. `GET /api/v1/1ct/status` — check delegation.
2. If not enabled: `POST /api/v1/1ct/prepare` → user signs EIP-712 → `POST /api/v1/1ct/enable`.
3. Call `/send` endpoints for orders and cash.

Details: [trading/one-click-delegated.md](/rwa-trading-apis/trading/one-click-delegated). viem example: [demo-code.md](/rwa-trading-apis/guides/demo-code).

## Send endpoints

| Operation    | Endpoint                               |
| ------------ | -------------------------------------- |
| Place order  | `POST /api/v1/orders/send`             |
| Cancel order | `DELETE /api/v1/orders/{orderId}/send` |
| Deposit      | `POST /api/v1/cash/deposits/send`      |
| Withdraw     | `POST /api/v1/cash/withdrawals/send`   |

## Deposit prerequisite

User wallet must `approve` the cash token spender before `/cash/deposits/send`.

Approval owner is the API key bound `userAddress`. See [cash/deposits.md](/rwa-trading-apis/cash-operations/deposits) for spender addresses.


# Demo Code

TypeScript examples for RWA API integration. HMAC authentication uses Node `crypto`; on-chain actions use [viem](https://viem.sh).

```bash
npm install viem
```

Set environment variables:

```bash
export TRADING_API_KEY="your-api-key"
export TRADING_API_SECRET="your-api-secret"
export TRADING_USER_PRIVATE_KEY="0x..."   # self-submit / One Click user wallet
```

## API client with HMAC

```ts
import crypto from "node:crypto";

const BASE_URL = "https://base-api.synfutures.com/rwa/trading";

type HttpMethod = "GET" | "POST" | "DELETE";

interface TradingApiConfig {
    apiKey: string;
    apiSecret: string;
    chainId: number;
    productType: "Synfutures";
}

function canonicalUri(path: string, query: Record<string, string | string[] | undefined> = {}): string {
    const pairs: string[] = [];
    for (const key of Object.keys(query).sort()) {
        const value = query[key];
        if (value === undefined || value === null) continue;
        if (Array.isArray(value)) {
            for (const item of value) pairs.push(`${key}=${item}`);
        } else {
            pairs.push(`${key}=${value}`);
        }
    }
    return pairs.length === 0 ? path : `${path}?${pairs.join("&")}`;
}

function signTradingApi(
    apiSecret: string,
    method: HttpMethod,
    path: string,
    query: Record<string, string | string[] | undefined>,
    timestamp: number,
    nonce: string,
    rawBody: string,
): string {
    const uri = canonicalUri(path, query);
    const payload = [method, uri, String(timestamp), nonce, rawBody].join("\n");
    return crypto.createHmac("sha256", apiSecret).update(payload, "utf8").digest("hex");
}

async function tradingApiRequest<T>(
    config: TradingApiConfig,
    method: HttpMethod,
    path: string,
    options: {
        query?: Record<string, string | string[] | undefined>;
        body?: unknown;
    } = {},
): Promise<T> {
    const query = options.query ?? {};
    const rawBody = options.body === undefined ? "" : JSON.stringify(options.body);
    const timestamp = Date.now();
    const nonce = crypto.randomUUID();
    const signature = signTradingApi(config.apiSecret, method, path, query, timestamp, nonce, rawBody);

    const url = new URL(`${BASE_URL}${canonicalUri(path, query)}`);
    const response = await fetch(url, {
        method,
        headers: {
            "content-type": "application/json",
            "x-api-key": config.apiKey,
            "x-api-ts": String(timestamp),
            "x-api-nonce": nonce,
            "x-api-sign": signature,
            "x-api-chain-id": String(config.chainId),
            "x-api-p": config.productType,
        },
        body: method === "GET" || method === "DELETE" ? undefined : rawBody,
    });

    const json = await response.json() as { code?: number; errMsg?: string; data?: T };
    if (json.code !== 200) {
        throw new Error(json.errMsg || `API error ${json.code}`);
    }
    return json.data as T;
}
```

## Self-submit market buy (viem)

Build calldata from the API, broadcast with viem, then record the tx hash.

```ts
import { createPublicClient, createWalletClient, http, type Address, type Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";

interface OrderCalldata {
    chainId: number;
    productType: string;
    toAddress: Address;
    value: string;
    callData: Hex;
    method: string;
}

const config: TradingApiConfig = {
    apiKey: process.env.TRADING_API_KEY!,
    apiSecret: process.env.TRADING_API_SECRET!,
    chainId: 8453,
    productType: "Synfutures",
};

const account = privateKeyToAccount(process.env.TRADING_USER_PRIVATE_KEY as Hex);
const walletClient = createWalletClient({
    account,
    chain: base,
    transport: http(),
});
const publicClient = createPublicClient({ chain: base, transport: http() });

// Router calls under-estimate gas on some chains (e.g. Monad): the default
// estimate can leave the tx short and it reverts out-of-gas. Add a buffer.
async function submitTx(to: Address, callData: Hex, value: string): Promise<Hex> {
    const gas = (await publicClient.estimateGas({
        account: account.address,
        to,
        data: callData,
        value: BigInt(value),
    })) * 2n;
    return walletClient.sendTransaction({ to, data: callData, value: BigInt(value), gas });
}

async function placeMarketBuySelfSubmit(stockAddress: Address, notional: string): Promise<Hex> {
    const calldata = await tradingApiRequest<OrderCalldata>(config, "POST", "/api/v1/orders/calldata", {
        body: {
            stockAddress,
            side: "Buy",
            type: "Market",
            notional,
            deadline: Math.floor(Date.now() / 1000) + 3600,
        },
    });

    const txHash = await submitTx(calldata.toAddress, calldata.callData, calldata.value);

    await tradingApiRequest(config, "POST", "/api/v1/orders/tx", {
        body: { txHash },
    });

    return txHash;
}
```

## Deposit with ERC-20 approve (viem)

Approve the spender, then self-submit deposit calldata.

```ts
import { erc20Abi, maxUint256, type Address, type Hex } from "viem";

// Reuses the module-level `walletClient`, `publicClient`, `account`, `config`,
// and `submitTx` defined above.
const SPENDER: Address = "0x4f090d817fd83753988a7b0c1d76f170f8461be8"; // Synfutures mainnet StockRouter

interface CashCalldata {
    chainId: number;
    productType: string;
    toAddress: Address;
    value: string;
    callData: Hex;
    method: string;
}

async function depositSelfSubmit(tokenAddress: Address, tokenAmount: string): Promise<Hex> {
    const approveHash = await walletClient.writeContract({
        address: tokenAddress,
        abi: erc20Abi,
        functionName: "approve",
        args: [SPENDER, maxUint256],
    });
    await publicClient.waitForTransactionReceipt({ hash: approveHash });

    const calldata = await tradingApiRequest<CashCalldata>(config, "POST", "/api/v1/cash/deposits/calldata", {
        body: {
            userAddress: account.address,
            tokenAddress,
            tokenAmount,
        },
    });

    const txHash = await submitTx(calldata.toAddress, calldata.callData, calldata.value);

    await tradingApiRequest(config, "POST", "/api/v1/cash/deposits/tx", {
        body: { txHash },
    });

    return txHash;
}
```

Spender addresses: [cash/deposits.md](/rwa-trading-apis/cash-operations/deposits).

## Order with deposit (self-submit)

Use `/orders/with-deposit/calldata` when the same transaction should deposit an approved token and place an order. For buy orders, the cash deposit must be eligible for instant deposit. The token must be approved before submitting the returned calldata.

```ts
async function placeMarketBuyWithDepositSelfSubmit(
    cashTokenAddress: Address,
    stockAddress: Address,
    depositAmount: string,
    notional: string,
): Promise<Hex> {
    const calldata = await tradingApiRequest<OrderCalldata>(config, "POST", "/api/v1/orders/with-deposit/calldata", {
        body: {
            stockAddress,
            depositTokenAddress: cashTokenAddress,
            depositAmount,
            side: "Buy",
            type: "Market",
            notional,
            deadline: Math.floor(Date.now() / 1000) + 3600,
        },
    });

    const txHash = await submitTx(calldata.toAddress, calldata.callData, calldata.value);

    await tradingApiRequest(config, "POST", "/api/v1/orders/tx", {
        body: { txHash },
    });

    return txHash;
}
```

## Stock deposit and withdrawal (self-submit)

Stock endpoints move ERC-20 stock tokens between wallet balance and `exchangeBalance`. Approve `StockRouter` for the stock token before calling stock deposit calldata.

```ts
async function depositStockSelfSubmit(stockTokenAddress: Address, tokenAmount: string): Promise<Hex> {
    const calldata = await tradingApiRequest<OrderCalldata>(config, "POST", "/api/v1/stock/deposits/calldata", {
        body: {
            userAddress: account.address,
            tokenAddress: stockTokenAddress,
            tokenAmount,
        },
    });

    const txHash = await submitTx(calldata.toAddress, calldata.callData, calldata.value);

    await tradingApiRequest(config, "POST", "/api/v1/stock/deposits/tx", {
        body: { txHash },
    });

    return txHash;
}

async function withdrawStockSelfSubmit(stockTokenAddress: Address, tokenAmount: string): Promise<Hex> {
    const calldata = await tradingApiRequest<OrderCalldata>(config, "POST", "/api/v1/stock/withdrawals/calldata", {
        body: {
            userAddress: account.address,
            tokenAddress: stockTokenAddress,
            tokenAmount,
        },
    });

    const txHash = await submitTx(calldata.toAddress, calldata.callData, calldata.value);

    await tradingApiRequest(config, "POST", "/api/v1/stock/withdrawals/tx", {
        body: { txHash },
    });

    return txHash;
}
```

## One Click enable (viem signTypedData)

User signs the EIP-712 payload from `/1ct/prepare`, then submits to `/1ct/enable`.

```ts
interface OneClickPrepare {
    delegatee: Address;
    signPayload: {
        domain: {
            name: string;
            version: string;
            chainId: number;
            verifyingContract: Address;
        };
        types: {
            // /1ct/prepare also returns EIP712Domain here; it must be stripped
            // before passing to viem signTypedData (viem derives it from `domain`).
            EIP712Domain?: Array<{ name: string; type: string }>;
            Delegate: Array<{ name: string; type: string }>;
        };
        primaryType: "Delegate";
        message: {
            user: Address;
            delegatee: Address;
            nonce: bigint | number;
            deadline: bigint | number;
        };
    };
    nonce: number;
    deadline: number;
}

async function enableOneClick(): Promise<void> {
    const deadline = Math.floor(Date.now() / 1000) + 3600;
    const prepared = await tradingApiRequest<OneClickPrepare>(config, "POST", "/api/v1/1ct/prepare", {
        body: { deadline },
    });

    const { signPayload } = prepared;
    if (account.address.toLowerCase() !== signPayload.message.user.toLowerCase()) {
        throw new Error("signer must match signPayload.message.user");
    }

    // viem derives EIP712Domain from `domain`; leaving it in `types` throws, so strip it.
    const types = { ...signPayload.types };
    delete types.EIP712Domain;
    const signature = await walletClient.signTypedData({
        domain: {
            ...signPayload.domain,
            chainId: BigInt(signPayload.domain.chainId),
        },
        types,
        primaryType: signPayload.primaryType,
        message: {
            ...signPayload.message,
            nonce: BigInt(signPayload.message.nonce),
            deadline: BigInt(signPayload.message.deadline),
        },
    });

    await tradingApiRequest(config, "POST", "/api/v1/1ct/enable", {
        body: { signature, deadline: prepared.deadline },
    });
}
```

After enable, use `/send` endpoints — see [one-click-flow.md](/rwa-trading-apis/guides/one-click-flow).

## One Click order (via `/send`)

Once One Click is enabled, place orders with `/orders/send`. The API builds the calldata and broadcasts it through `OneClickRouter`, paying gas on the user's behalf — no per-order wallet signature or self-broadcast. The body is the same as `/orders/calldata`, plus an optional `gasLimit` (set a buffer; router calls can under-estimate gas on some chains). The response is the order-tx mapping, not calldata: poll it (or `/orders/tx/{txHash}`) until `orderId` is populated.

```ts
interface OneClickOrderTx {
    id: string;
    chainId: number;
    productType: string;
    operationType: string;      // "trade"
    orderId: string | null;     // null until indexed, then the on-chain order id
    txHash: string;
    status: string;             // "pending" -> "new" -> ...
    createAt: string;
    updateAt: string;
}

async function placeLimitBuyOneClick(
    stockAddress: Address,
    quantity: string,
    price: string,
): Promise<OneClickOrderTx> {
    return tradingApiRequest<OneClickOrderTx>(config, "POST", "/api/v1/orders/send", {
        body: {
            stockAddress,
            side: "Buy",
            type: "Limit",
            quantity,
            price,
            timeInForce: "DAY", // production limit orders only accept DAY
            deadline: Math.floor(Date.now() / 1000) + 3600,
            gasLimit: 800000,   // gas buffer; API broadcasts via OneClickRouter
        },
    });
}
```

Cancel an open One Click order the same way — `DELETE /api/v1/orders/{orderId}/send` (query `deadline` required, `gasLimit` optional) — which also broadcasts through the router.

## Query portfolio

```ts
interface UserBalance {
    chainId: number;
    productType: string;
    address: Address;
    mUsdBalance: string;
    tokenBalances: Array<{
        symbol: string;
        stockSymbol?: string;
        isStock: boolean;
        walletBalance: string;
        exchangeBalance: string;
        price?: number;
    }>;
}

async function getPortfolio(userId: Address): Promise<UserBalance> {
    return tradingApiRequest<UserBalance>(
        config,
        "GET",
        `/api/v1/users/${userId.toLowerCase()}/balance`,
    );
}
```

## Query corporate actions

```ts
interface BrokerCorporateAction {
    symbol: string;
    type: "reverse_splits" | "forward_splits" | "unit_splits" | "stock_dividends" | "cash_dividends";
    time: string;
    exDate: string;
    recordDate: string;
    payableDate: string;
    processDate: string;
    info: Record<string, unknown>;
}

async function getCorporateActions(symbol: string): Promise<BrokerCorporateAction[]> {
    return tradingApiRequest<BrokerCorporateAction[]>(config, "GET", "/api/v1/corporate-action", {
        query: { symbol, types: ["cash_dividends"] },
    });
}
```

See also [authenticate/examples.md](/rwa-trading-apis/authentication/examples) for Node.js-only HMAC snippets.


# Overview

Reference docs for production RWA API integrations.

| Need                                | Doc                                                                                                                                                     |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| All endpoints                       | [endpoint-index.md](/rwa-trading-apis/reference/endpoint-index)                                                                                         |
| Enums and constraints               | [enums.md](/rwa-trading-apis/reference/enums)                                                                                                           |
| Status and UI state model           | [status-model.md](/rwa-trading-apis/reference/status-model)                                                                                             |
| Troubleshooting                     | [troubleshooting.md](/rwa-trading-apis/reference/troubleshooting)                                                                                       |
| API endpoint to contract effect     | [api-contract-map.md](/rwa-trading-apis/reference/api-contract-map)                                                                                     |
| Environments, chains, product types | [environments-and-chains.md](/rwa-trading-apis/reference/environments-and-chains)                                                                       |
| Sample responses                    | [sample-responses.md](/rwa-trading-apis/reference/sample-responses)                                                                                     |
| Production test checklist           | [integration-test-checklist.md](/rwa-trading-apis/reference/integration-test-checklist)                                                                 |
| Live OpenAPI snapshot               | [openapi.snapshot.json](https://github.com/AnchoredLabs/anchored-knowledge/tree/api-doc/synfutures/02-products/rwa/api/reference/openapi.snapshot.json) |


# Endpoint Index

**43 endpoints** for RWA trading integrators. Permission: `READ_ONLY` or `WRITABLE` per API key scope.

Base path: `/api/v1`. Full URL prefix: `https://base-api.synfutures.com/rwa/trading`.

## Market data (6)

| Method | Path                                | Permission | Description            |
| ------ | ----------------------------------- | ---------- | ---------------------- |
| `GET`  | `/api/v1/config`                    | READ\_ONLY | On-chain market config |
| `GET`  | `/api/v1/symbols`                   | READ\_ONLY | Tradable symbols       |
| `GET`  | `/api/v1/prices/{symbol}`           | READ\_ONLY | Real-time quote        |
| `GET`  | `/api/v1/prices/{symbol}/history`   | READ\_ONLY | K-line history         |
| `GET`  | `/api/v1/underlying/{symbol}/price` | READ\_ONLY | Underlying quote       |
| `GET`  | `/api/v1/corporate-action`          | READ\_ONLY | Corporate actions      |

## Portfolio (3)

| Method | Path                               | Permission | Description |
| ------ | ---------------------------------- | ---------- | ----------- |
| `GET`  | `/api/v1/users/{userId}`           | READ\_ONLY | User info   |
| `GET`  | `/api/v1/users/{userId}/balance`   | READ\_ONLY | Balances    |
| `GET`  | `/api/v1/users/{userId}/positions` | READ\_ONLY | Positions   |

## Orders (10)

| Method   | Path                                   | Permission | Description                       |
| -------- | -------------------------------------- | ---------- | --------------------------------- |
| `GET`    | `/api/v1/orders`                       | READ\_ONLY | Order list                        |
| `POST`   | `/api/v1/orders/calldata`              | WRITABLE   | Build order calldata              |
| `POST`   | `/api/v1/orders/send`                  | WRITABLE   | One Click order                   |
| `POST`   | `/api/v1/orders/with-deposit/calldata` | WRITABLE   | Build order-with-deposit calldata |
| `POST`   | `/api/v1/orders/with-deposit/send`     | WRITABLE   | One Click order with deposit      |
| `POST`   | `/api/v1/orders/tx`                    | WRITABLE   | Record order tx                   |
| `GET`    | `/api/v1/orders/tx/{txHash}`           | READ\_ONLY | Order by tx                       |
| `GET`    | `/api/v1/orders/{orderId}`             | READ\_ONLY | Order detail                      |
| `DELETE` | `/api/v1/orders/{orderId}/calldata`    | WRITABLE   | Cancel calldata                   |
| `DELETE` | `/api/v1/orders/{orderId}/send`        | WRITABLE   | One Click cancel                  |

## Cash (10)

| Method | Path                                     | Permission | Description        |
| ------ | ---------------------------------------- | ---------- | ------------------ |
| `POST` | `/api/v1/cash/deposits/calldata`         | WRITABLE   | Deposit calldata   |
| `POST` | `/api/v1/cash/deposits/send`             | WRITABLE   | One Click deposit  |
| `POST` | `/api/v1/cash/deposits/tx`               | WRITABLE   | Record deposit tx  |
| `GET`  | `/api/v1/cash/deposits`                  | READ\_ONLY | Deposit list       |
| `GET`  | `/api/v1/cash/deposits/{operationId}`    | READ\_ONLY | Deposit detail     |
| `POST` | `/api/v1/cash/withdrawals/calldata`      | WRITABLE   | Withdraw calldata  |
| `POST` | `/api/v1/cash/withdrawals/send`          | WRITABLE   | One Click withdraw |
| `POST` | `/api/v1/cash/withdrawals/tx`            | WRITABLE   | Record withdraw tx |
| `GET`  | `/api/v1/cash/withdrawals`               | READ\_ONLY | Withdraw list      |
| `GET`  | `/api/v1/cash/withdrawals/{operationId}` | READ\_ONLY | Withdraw detail    |

## Stock (10)

| Method | Path                                      | Permission | Description                |
| ------ | ----------------------------------------- | ---------- | -------------------------- |
| `POST` | `/api/v1/stock/deposits/calldata`         | WRITABLE   | Stock deposit calldata     |
| `POST` | `/api/v1/stock/deposits/send`             | WRITABLE   | One Click stock deposit    |
| `POST` | `/api/v1/stock/deposits/tx`               | WRITABLE   | Record stock deposit tx    |
| `GET`  | `/api/v1/stock/deposits`                  | READ\_ONLY | Stock deposit list         |
| `GET`  | `/api/v1/stock/deposits/{operationId}`    | READ\_ONLY | Stock deposit detail       |
| `POST` | `/api/v1/stock/withdrawals/calldata`      | WRITABLE   | Stock withdrawal calldata  |
| `POST` | `/api/v1/stock/withdrawals/send`          | WRITABLE   | One Click stock withdrawal |
| `POST` | `/api/v1/stock/withdrawals/tx`            | WRITABLE   | Record stock withdrawal tx |
| `GET`  | `/api/v1/stock/withdrawals`               | READ\_ONLY | Stock withdrawal list      |
| `GET`  | `/api/v1/stock/withdrawals/{operationId}` | READ\_ONLY | Stock withdrawal detail    |

## One Click (4)

| Method | Path                  | Permission | Description        |
| ------ | --------------------- | ---------- | ------------------ |
| `GET`  | `/api/v1/1ct/status`  | READ\_ONLY | Delegation status  |
| `POST` | `/api/v1/1ct/prepare` | WRITABLE   | EIP-712 payload    |
| `POST` | `/api/v1/1ct/enable`  | WRITABLE   | Enable delegation  |
| `POST` | `/api/v1/1ct/disable` | WRITABLE   | Disable delegation |

## Doc map

| Module    | Doc                                                                                                                          |
| --------- | ---------------------------------------------------------------------------------------------------------------------------- |
| Market    | [market/](https://github.com/AnchoredLabs/anchored-knowledge/tree/api-doc/synfutures/02-products/rwa/api/market/README.md)   |
| Portfolio | [portfolio/overview.md](/rwa-trading-apis/portfolio/overview)                                                                |
| Orders    | [trading/](https://github.com/AnchoredLabs/anchored-knowledge/tree/api-doc/synfutures/02-products/rwa/api/trading/README.md) |
| Cash      | [cash/](/rwa-trading-apis/cash-operations/cash)                                                                              |
| Stock     | [stock/](/rwa-trading-apis/stock-operations/stock)                                                                           |
| One Click | [trading/one-click-delegated.md](/rwa-trading-apis/trading/one-click-delegated)                                              |

## Related reference

| Need                          | Doc                                                                                     |
| ----------------------------- | --------------------------------------------------------------------------------------- |
| API to contract effects       | [api-contract-map.md](/rwa-trading-apis/reference/api-contract-map)                     |
| Environment and chain context | [environments-and-chains.md](/rwa-trading-apis/reference/environments-and-chains)       |
| Status model                  | [status-model.md](/rwa-trading-apis/reference/status-model)                             |
| Troubleshooting               | [troubleshooting.md](/rwa-trading-apis/reference/troubleshooting)                       |
| Sample responses              | [sample-responses.md](/rwa-trading-apis/reference/sample-responses)                     |
| Integration tests             | [integration-test-checklist.md](/rwa-trading-apis/reference/integration-test-checklist) |

OpenAPI snapshot: [openapi.snapshot.json](https://github.com/AnchoredLabs/anchored-knowledge/tree/api-doc/synfutures/02-products/rwa/api/reference/openapi.snapshot.json) (refreshed 2026-07-03 from live; server `https://base-api.synfutures.com/rwa/trading`)


# API ↔ Contract Map

This page maps partner API endpoints to the contract-level effect they prepare, submit, or query. Use it when debugging or explaining why an API call changes `mUSD`, `Stock.stockBalance`, or order state.

## Overview

```mermaid
flowchart TB
    API[Trading API] --> Router[StockRouter]
    Router --> Cashier[Cashier]
    Router --> Stock[Stock]
    Stock --> Cashier
```

## Market and Portfolio

| Endpoint                                | Contract relationship                        | Effect                                                                         |
| --------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------ |
| `GET /api/v1/config`                    | Reads on-chain Cashier/Exchange config cache | Returns fee rates, buffer state, and per-token cashier limits; no state change |
| `GET /api/v1/symbols`                   | Reads backend/token registry                 | Lists tradable stock tokens and contract addresses                             |
| `GET /api/v1/prices/{symbol}`           | Off-chain quote plus on-chain token context  | Quote display; no state change                                                 |
| `GET /api/v1/prices/{symbol}/history`   | Market data                                  | Historical display; no state change                                            |
| `GET /api/v1/underlying/{symbol}/price` | Market data                                  | Underlying quote; no state change                                              |
| `GET /api/v1/corporate-action`          | Corporate action data                        | Split/dividend data; no state change                                           |
| `GET /api/v1/users/{userId}/balance`    | Cashier + token balances + allowance         | Portfolio, buy power, deposit readiness                                        |
| `GET /api/v1/users/{userId}/positions`  | Indexed Stock state                          | Stock positions                                                                |

## Orders

| Endpoint                                    | Contract relationship                                                                                                       | Effect                                           |
| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| `POST /api/v1/orders/calldata`              | Encodes `StockRouter.placeMarketOrder` or `placeLimitOrder`                                                                 | Returns tx data; no state change until submitted |
| `POST /api/v1/orders/send`                  | OneClickRouter forwards to StockRouter                                                                                      | Places order and auto-records tx                 |
| `POST /api/v1/orders/with-deposit/calldata` | Encodes `StockRouter.depositAndMarketBuy`, `depositAndLimitBuy`, `depositStockAndMarketSell`, or `depositStockAndLimitSell` | Returns tx data for combined deposit + order     |
| `POST /api/v1/orders/with-deposit/send`     | OneClickRouter forwards combined deposit + order                                                                            | Submits combined flow and auto-records tx        |
| `POST /api/v1/orders/tx`                    | API/indexer mapping                                                                                                         | Links self-submitted tx to API key               |
| `GET /api/v1/orders`                        | API order mapping / indexed state                                                                                           | Lists mapped orders                              |
| `GET /api/v1/orders/tx/{txHash}`            | API tx mapping                                                                                                              | Finds order by tx                                |
| `GET /api/v1/orders/{orderId}`              | Indexed Stock state                                                                                                         | Shows open or historical order                   |
| `DELETE /api/v1/orders/{orderId}/calldata`  | Encodes `StockRouter.cancelLimitOrder`                                                                                      | Returns cancel tx data                           |
| `DELETE /api/v1/orders/{orderId}/send`      | OneClickRouter forwards cancel                                                                                              | Cancels via delegated execution                  |

## Cash

| Endpoint                                     | Contract relationship              | Effect                             |
| -------------------------------------------- | ---------------------------------- | ---------------------------------- |
| `POST /api/v1/cash/deposits/calldata`        | Encodes `StockRouter.deposit`      | Returns deposit tx data            |
| `POST /api/v1/cash/deposits/send`            | OneClickRouter forwards deposit    | Deposits from bound user           |
| `POST /api/v1/cash/deposits/tx`              | API/indexer mapping                | Links self-submitted deposit tx    |
| `GET /api/v1/cash/deposits`                  | Indexed Cashier state              | Lists deposits                     |
| `GET /api/v1/cash/deposits/{operationId}`    | Cash operation state               | Deposit detail and status          |
| `POST /api/v1/cash/withdrawals/calldata`     | Encodes `StockRouter.withdraw`     | Returns withdrawal tx data         |
| `POST /api/v1/cash/withdrawals/send`         | OneClickRouter forwards withdrawal | Withdraws from bound user          |
| `POST /api/v1/cash/withdrawals/tx`           | API/indexer mapping                | Links self-submitted withdrawal tx |
| `GET /api/v1/cash/withdrawals`               | Indexed Cashier state              | Lists withdrawals                  |
| `GET /api/v1/cash/withdrawals/{operationId}` | Cash operation state               | Withdrawal detail and status       |

## Stock

| Endpoint                                      | Contract relationship                    | Effect                                   |
| --------------------------------------------- | ---------------------------------------- | ---------------------------------------- |
| `POST /api/v1/stock/deposits/calldata`        | Encodes `StockRouter.depositStock`       | Returns stock deposit tx data            |
| `POST /api/v1/stock/deposits/send`            | OneClickRouter forwards stock deposit    | Deposits stock from bound user           |
| `POST /api/v1/stock/deposits/tx`              | API/indexer mapping                      | Links self-submitted stock deposit tx    |
| `GET /api/v1/stock/deposits`                  | Indexed Stock state                      | Lists stock deposits                     |
| `GET /api/v1/stock/deposits/{operationId}`    | Stock operation state                    | Stock deposit detail                     |
| `POST /api/v1/stock/withdrawals/calldata`     | Encodes `StockRouter.withdrawStock`      | Returns stock withdrawal tx data         |
| `POST /api/v1/stock/withdrawals/send`         | OneClickRouter forwards stock withdrawal | Withdraws stock from bound user          |
| `POST /api/v1/stock/withdrawals/tx`           | API/indexer mapping                      | Links self-submitted stock withdrawal tx |
| `GET /api/v1/stock/withdrawals`               | Indexed Stock state                      | Lists stock withdrawals                  |
| `GET /api/v1/stock/withdrawals/{operationId}` | Stock operation state                    | Stock withdrawal detail                  |

For stock deposit and withdrawal detail endpoints, `operationId` is an API/indexer lookup identifier. It is not emitted as a native field by `Stock.depositStock` or `Stock.withdrawStock` events.

## One Click

| Endpoint                   | Contract relationship                  | Effect                 |
| -------------------------- | -------------------------------------- | ---------------------- |
| `GET /api/v1/1ct/status`   | Reads OneClickRouter delegate state    | Shows delegation state |
| `POST /api/v1/1ct/prepare` | Builds EIP-712 delegate payload        | No state change        |
| `POST /api/v1/1ct/enable`  | Calls `delegateBySig` through operator | Enables delegatee      |
| `POST /api/v1/1ct/disable` | Calls undelegate flow                  | Disables delegation    |

## Invariants for Integrators

* API calldata targets `StockRouter`; do not call Cashier or Stock directly.
* Buy orders lock `mUSD` in Cashier.
* Plain sell orders lock `Stock.stockBalance`, surfaced by the API as `exchangeBalance`.
* Order settlement updates `mUSD` and `exchangeBalance` asynchronously after execution.
* Cash operations can be instant or queued depending on Cashier buffers.


# Enums & Constraints

## Header enums

| Field            | Allowed values                                   |
| ---------------- | ------------------------------------------------ |
| `x-api-chain-id` | `8453`, `1`, `143`, `11155111`, `10143`, `84532` |
| `x-api-p`        | `Synfutures`                                     |

## Permission enums

| Field    | Values      | Description        |
| -------- | ----------- | ------------------ |
| `access` | `READ_ONLY` | GET only.          |
| `access` | `WRITABLE`  | GET, POST, DELETE. |

## Order enums

| Field           | Values                         | Description                                                                                                                                                                    |
| --------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `side`          | `Buy`, `Sell`                  | Direction.                                                                                                                                                                     |
| `type`          | `Market`, `Limit`              | Order type.                                                                                                                                                                    |
| `timeInForce`   | `DAY`                          | Limit TIF. The contract enum includes `GTC`, `OPG`, `IOC`, `FOK`, `GTX`, `GTD`, and `CLS` for forward compatibility, but current production contracts reject non-`DAY` values. |
| `operationType` | `trade`, `deposit`, `withdraw` | Mapping type.                                                                                                                                                                  |

> Production limit orders currently only accept `DAY`; other `timeInForce` values will make the order transaction revert.

### Order status (mapped)

| Value            | Description             |
| ---------------- | ----------------------- |
| `Init`           | Initial.                |
| `Pending`        | Recorded or processing. |
| `New`            | New order identified.   |
| `Partial filled` | Partial fill.           |
| `Filled`         | Filled.                 |
| `Canceled`       | Canceled.               |
| `Expired`        | Expired.                |
| `Rejected`       | Rejected.               |
| `Failed`         | Failed.                 |

### On-chain status

| Value       | Description       |
| ----------- | ----------------- |
| `placing`   | Processing.       |
| `canceling` | Cancel in flight. |

### Broker raw status

```
init, pending, failed, new, partially_filled, filled, done_for_day,
canceled, expired, replaced, pending_cancel, pending_replace, accepted,
pending_new, accepted_for_bidding, stopped, rejected, suspended, calculated
```

## Cash status

### operationStatus

| Value        | Description |
| ------------ | ----------- |
| `requested`  | Requested.  |
| `processing` | Processing. |
| `settled`    | Settled.    |
| `closed`     | Closed.     |

### mappingStatus

Same as order mapping status enum above.

## Market data enums

### K-line interval

| Value | Description |
| ----- | ----------- |
| `1m`  | 1 minute    |
| `3m`  | 3 minutes   |
| `5m`  | 5 minutes   |
| `15m` | 15 minutes  |
| `1h`  | 1 hour      |
| `1d`  | 1 day       |
| `1w`  | 1 week      |

### Adjustment type

| Value      | Description    |
| ---------- | -------------- |
| `raw`      | No adjustment. |
| `split`    | Split.         |
| `dividend` | Dividend.      |
| `spin-off` | Spin-off.      |
| `all`      | All (default). |

### Corporate action types

| Value             | Description     |
| ----------------- | --------------- |
| `reverse_splits`  | Reverse split.  |
| `forward_splits`  | Forward split.  |
| `unit_splits`     | Unit split.     |
| `stock_dividends` | Stock dividend. |
| `cash_dividends`  | Cash dividend.  |

## Value constraints

| Field          | Constraint                                                                                                                       |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `stockAddress` | Valid EVM address. Lowercased by server.                                                                                         |
| `userAddress`  | Valid EVM address.                                                                                                               |
| `tokenAddress` | Valid EVM address.                                                                                                               |
| `quantity`     | > 0 in API input. Stock order quantities must encode to an 18-decimal WAD multiple of `1e9` (broker-facing 9-decimal precision). |
| `notional`     | > 0. Market-buy USD amount must use the production USD step: `0.01` when amount is `>= 1`, or `0.0001` when amount is `< 1`.     |
| `price`        | > 0. Limit price must use the production USD step: `0.01` when price is `>= 1`, or `0.0001` when price is `< 1`.                 |
| `tokenAmount`  | > 0, decimals ≤ token decimals, raw ≤ uint96.                                                                                    |
| `creditAmount` | > 0, up to 18 decimals, raw ≤ uint96.                                                                                            |
| `deadline`     | Unix seconds, > 0, ≤ `4294967295`.                                                                                               |
| `orderId`      | bytes32 hex (64 chars) for cancel.                                                                                               |
| `signature`    | 65-byte hex for One Click enable.                                                                                                |
| `gasLimit`     | Optional; server estimates if empty or ≤ 0.                                                                                      |


# Environments & Chains

Use this page to choose the correct base URL, chain ID, product type, and spender assumptions. These docs focus the Synfutures RWA API.

## Base URL

Production Trading API:

```
https://base-api.synfutures.com/rwa/trading
```

All endpoint paths start with `/api/v1`.

HMAC authentication uses the path only:

```
/api/v1/symbols
```

Do not sign the domain or `/rwa/trading` prefix.

## Product Context

Every request must include both:

| Header           | Meaning                                                   |
| ---------------- | --------------------------------------------------------- |
| `x-api-chain-id` | Chain context for the request                             |
| `x-api-p`        | Product context. Use `Synfutures` for the Synfutures API. |

The API key must have a matching `chainId + productType + access` permission.

## Supported Chain IDs

| Chain ID   | Chain            |
| ---------- | ---------------- |
| `8453`     | Base Mainnet     |
| `1`        | Ethereum Mainnet |
| `143`      | Monad Mainnet    |
| `11155111` | Ethereum Sepolia |
| `10143`    | Monad Testnet    |
| `84532`    | Base Sepolia     |

## Mainnet Contract Addresses

Synfutures production uses the same deterministic proxy addresses on Base, Monad, and Ethereum:

| Contract         | Address                                      |
| ---------------- | -------------------------------------------- |
| `StockRouter`    | `0x4f090d817fd83753988a7b0c1d76f170f8461be8` |
| `Cashier`        | `0x8c1b182bb0fe4e8407404ffe37c974d6dacef3a9` |
| `Stock`          | `0x6d202d2f78aa26a7db51491416abf7f7a5003aac` |
| `OneClickRouter` | `0x102c30ac544aed8cbf838fdc1b1677ff16df0e65` |

## Mainnet Cash Tokens and Approval Spenders

| Chain ID | Chain            | Cash token                                   | Approval spender (`StockRouter`)             |
| -------- | ---------------- | -------------------------------------------- | -------------------------------------------- |
| `8453`   | Base Mainnet     | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | `0x4f090d817fd83753988a7b0c1d76f170f8461be8` |
| `143`    | Monad Mainnet    | `0x754704Bc059F8C67012fEd69BC8A327a5aafb603` | `0x4f090d817fd83753988a7b0c1d76f170f8461be8` |
| `1`      | Ethereum Mainnet | `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` | `0x4f090d817fd83753988a7b0c1d76f170f8461be8` |

Production cash config from `synfutures-contracts` `config/production`:

| Config                     | Value                    |
| -------------------------- | ------------------------ |
| Deposit rate               | `1`                      |
| Withdrawal rate            | `0.9979`                 |
| Minimum amount             | `90` cash-token units    |
| `creditBufferCapacity`     | `20000` `mUSD`           |
| `withdrawalBufferCapacity` | `10000` cash-token units |
| `instantThresholdDivisor`  | `5`                      |

## Request Context Checklist

Before sending state-changing requests, confirm:

| Item             | Expected                                      |
| ---------------- | --------------------------------------------- |
| Base URL         | `https://base-api.synfutures.com/rwa/trading` |
| HMAC URI         | `/api/v1/...` only                            |
| `x-api-chain-id` | Matches wallet network                        |
| `x-api-p`        | Exact product value                           |
| API key scope    | Allows requested chain/product/access         |
| Deposit spender  | Matches chain/product                         |
| Wallet allowance | Sufficient before deposit                     |

## Common Mismatches

| Mismatch                    | Symptom                         | Fix                                        |
| --------------------------- | ------------------------------- | ------------------------------------------ |
| Wallet on wrong chain       | Tx fails or hits wrong contract | Switch wallet to `x-api-chain-id`          |
| API key lacks product scope | `Unauthorized`                  | Grant correct `chainId + x-api-p + access` |
| Wrong `x-api-p` casing      | `Unsupported product`           | Use exact `Synfutures`                     |
| Wrong spender               | Deposit tx reverts              | Use spender for chain/product              |
| Wrong HMAC URI              | `Unauthorized`                  | Authenticate `/api/v1/...` only            |


# Status Model

This page explains how to interpret order and cash operation states.

## Order State Model

```mermaid
flowchart TB
    A[Build calldata or send] --> B[Tx submitted]
    B --> C[Tx mapping]
    C --> D[Order ID backfilled]
    D --> E{Order type}
    E -->|Market| F[placing]
    E -->|Limit| G[placing or open]
    F --> H[Execution result]
    G --> I{Fill or cancel}
    I -->|fill| H
    I -->|cancel| J[canceling]
    H --> K[historyOrder]
    J --> K
```

## API Objects

| Object         | Meaning                                               | Integration use                            |
| -------------- | ----------------------------------------------------- | ------------------------------------------ |
| `mapping`      | API key to tx/order linkage                           | Transaction accepted / waiting for indexer |
| `openOrder`    | On-chain order still active or canceling              | Open order or cancel in progress           |
| `historyOrder` | Settled, canceled, rejected, expired, or failed order | Final result                               |

## Order Statuses

| Status           | Meaning                             | Suggested handling |
| ---------------- | ----------------------------------- | ------------------ |
| `Init`           | Mapping or order initialized        | Pending            |
| `Pending`        | Processing                          | Pending            |
| `New`            | Order recognized                    | Open               |
| `Partial filled` | Some quantity filled                | Partially filled   |
| `Filled`         | Fully filled                        | Filled             |
| `Canceled`       | Canceled                            | Canceled           |
| `Expired`        | Expired by TIF/execution rules      | Expired            |
| `Rejected`       | Rejected by execution or validation | Failed / rejected  |
| `Failed`         | Processing failed                   | Failed             |

On-chain transient statuses:

| Status      | Meaning                   | Suggested handling |
| ----------- | ------------------------- | ------------------ |
| `placing`   | Order placement is active | Placing            |
| `canceling` | Cancel request is active  | Canceling          |

## Market Order Expectations

Market orders are intended for immediate execution, but settlement is still async.

```mermaid
flowchart LR
    A[Market order placed] --> B[Execution result]
    B --> C{Fill size}
    C -->|full| D[Filled]
    C -->|partial| E[Partial filled plus refund]
    C -->|zero| F[No fill plus refund]
```

Integration guidance:

* Do not show market order as final at tx mining time.
* Treat `placing` as pending until settlement appears.
* Refresh portfolio after final status.

## Limit Order Expectations

Limit orders can remain open.

```mermaid
flowchart TB
    A[Limit order placed] --> B[Open]
    B --> C{Execution event}
    C -->|fill| D[Filled or partial]
    C -->|cancel request| E[Canceling]
    C -->|expiry| F[Expired]
    E --> G[Canceled or partial cancel settlement]
```

Integration guidance:

* Show open limit orders separately from final history.
* Allow cancel only for open limit orders that are not already canceling.
* Current production contracts accept only `DAY` limit orders; treat expiry/fill/cancel transitions as asynchronous backend and settlement behavior after the order is placed.

## Cash Operation State Model

Cash operations use Cashier buffers for instant settlement when possible. If the relevant buffer cannot cover the operation safely, the operation follows the queued settlement path. See [../cash/buffer-mechanism.md](/rwa-trading-apis/cash-operations/buffer-mechanism).

```mermaid
flowchart TB
    A[Cash request tx] --> B[Mapping]
    B --> C[Operation created]
    C --> D{Instant buffer}
    D -->|available| E[settled]
    D -->|not available| F[processing]
    F --> G[Final cash settlement]
    G --> E
    E --> H[Portfolio updated]
```

## Cash Statuses

| `operationStatus` | Meaning                         | Suggested handling |
| ----------------- | ------------------------------- | ------------------ |
| `requested`       | Operation created               | Requested          |
| `processing`      | Waiting on settlement path      | Processing         |
| `settled`         | `mUSD` applied or cash paid out | Complete           |
| `closed`          | Closed lifecycle                | Closed             |

`mappingStatus` reuses the order mapping status enum. Use it to explain whether the tx was recorded and recognized.

## Portfolio Refresh Rules

Refresh portfolio after:

* Deposit operation reaches `settled`.
* Withdrawal operation reaches `settled` or `closed`.
* Order moves from `openOrder` to `historyOrder`.
* Cancel settlement completes.

Treat the status endpoint as authoritative for lifecycle and the portfolio endpoint as authoritative for balances.


# Sample Responses

These examples are representative. Use the live OpenAPI snapshot for exact schema names and the API response for production values.

## Unified Envelope

```json
{
  "code": 200,
  "errMsg": "",
  "data": {},
  "uuid": "b3d8d0b5-2f93-4f4e-bf06-2f0e5a9f6d1f",
  "t": 1782390000000
}
```

## Symbols

```json
{
  "code": 200,
  "errMsg": "",
  "data": [
    {
      "symbol": "AAPL",
      "contractAddress": "0x3333333333333333333333333333333333333333",
      "contractSymbol": "aAAPL",
      "contractName": "Synfutures Apple Inc.",
      "decimals": 18,
      "onChainDecimals": 18,
      "tradable": true,
      "fractionable": true,
      "overnightTradable": false,
      "fractionalEhEnabled": false,
      "price": 180.5,
      "change24H": 1.2,
      "change24HPercent": 0.67,
      "logoUrl": "https://example.com/aapl.png",
      "lastUpdateTimestamp": 1782390000,
      "name": "Apple Inc.",
      "pdfUrl": "https://example.com/aapl.pdf",
      "volume24H": 1234567
    }
  ],
  "uuid": null,
  "t": null
}
```

## Portfolio Balance

```json
{
  "code": 200,
  "errMsg": "",
  "data": {
    "chainId": 143,
    "productType": "Synfutures",
    "address": "0x1111111111111111111111111111111111111111",
    "mUsdBalance": "100.00",
    "tokenBalances": [
      {
        "address": "0x2222222222222222222222222222222222222222",
        "name": "USD Coin",
        "symbol": "USDC",
        "stockSymbol": null,
        "isStock": false,
        "decimals": 6,
        "price": null,
        "walletBalance": "250.00",
        "walletAllowance": "1000.00",
        "exchangeBalance": "0",
        "logoUrl": "https://example.com/usdc.png"
      },
      {
        "address": "0x3333333333333333333333333333333333333333",
        "name": "Synfutures Apple Inc.",
        "symbol": "aAAPL",
        "stockSymbol": "AAPL",
        "isStock": true,
        "decimals": 18,
        "price": 180.5,
        "walletBalance": "0.25",
        "walletAllowance": "0",
        "exchangeBalance": "1.50",
        "logoUrl": "https://example.com/aapl.png"
      }
    ]
  },
  "uuid": null,
  "t": null
}
```

Interpretation:

* `mUsdBalance` is `mUSD` for buys and withdrawals.
* `walletBalance` is wallet-held ERC-20.
* `exchangeBalance` is the API view of `Stock.stockBalance`, used by plain sells.
* `walletAllowance` matters before deposits.

## Order Calldata

```json
{
  "code": 200,
  "errMsg": "",
  "data": {
    "chainId": 143,
    "productType": "Synfutures",
    "toAddress": "0x4444444444444444444444444444444444444444",
    "value": "0",
    "callData": "0xabcdef...",
    "method": "placeMarketOrder"
  },
  "uuid": null,
  "t": null
}
```

Submit `toAddress`, `value`, and `callData` exactly as returned.

## Order Detail

```json
{
  "code": 200,
  "errMsg": "",
  "data": {
    "openOrder": null,
    "historyOrder": {
      "orderId": "0x0000000000000001000200001111111111111111111111111111111111111111",
      "userAddress": "0x1111111111111111111111111111111111111111",
      "side": "Buy",
      "type": "Market",
      "tif": null,
      "symbol": "AAPL",
      "placeNotional": "10.00",
      "placeQuantity": null,
      "pay": "10.00",
      "placePrice": null,
      "status": "Filled",
      "settleTxHash": "0xdef...",
      "settlePrice": "180.50",
      "settlePay": "9.95",
      "settleReceive": "0.0551",
      "mintFee": "0.01",
      "protocolFee": "0.01"
    }
  },
  "uuid": null,
  "t": null
}
```

## Deposit Detail

```json
{
  "code": 200,
  "errMsg": "",
  "data": {
    "mappingStatus": "Filled",
    "operationStatus": "settled",
    "mapping": {
      "txHash": "0xabc...",
      "operationType": "deposit"
    },
    "depositOperation": {
      "operationId": "0x1234...",
      "userAddress": "0x1111111111111111111111111111111111111111",
      "tokenAddress": "0x2222222222222222222222222222222222222222",
      "status": "settled",
      "isInstant": true,
      "amount": 100,
      "creditAmount": 100,
      "feeAmount": 0,
      "createTxHash": "0xabc...",
      "settleTxHash": "0xdef..."
    },
    "withdrawalOperation": null
  },
  "uuid": null,
  "t": null
}
```

## One Click Status

```json
{
  "code": 200,
  "errMsg": "",
  "data": {
    "status": "enable",
    "delegatee": "0x5555555555555555555555555555555555555555"
  },
  "uuid": null,
  "t": null
}
```

## Error Response

```json
{
  "code": 401,
  "errMsg": "Unauthorized",
  "data": null,
  "uuid": "b3d8d0b5-2f93-4f4e-bf06-2f0e5a9f6d1f",
  "t": 1782390000000
}
```

Use `uuid` when escalating issues.

## Market config

```json
{
  "code": 200,
  "data": {
    "cashierBuffer": {
      "chain": 10143,
      "productType": "MondayTrade",
      "address": "0x....",
      "instantThresholdDivisor": "1",
      "creditBuffer": "743810467000000000000",
      "creditBufferCapacity": "1000000000000000000000",
      "totalBalance": "156027003229608600000"
    },
    "exchangeFeeConfig": {
      "chainId": 10143,
      "productType": "MondayTrade",
      "mintFeeRate": "0",
      "protocolFeeRate": "0",
      "minOrderValue": "0",
      "txHash": "",
      "updatedAt": 1778477709
    },
    "cashierTokenConfig": {
      "0x534b2f3a21130d7a60830c2df862319e593943a3": {
        "chain": 10143,
        "productType": "MondayTrade",
        "cashierAddress": "0x....",
        "tokenAddress": "0x534b2f3a21130d7a60830c2df862319e593943a3",
        "minAmount": "1",
        "decimals": 6,
        "depositPaused": false,
        "withdrawPaused": false,
        "depositRate": "1",
        "withdrawalBuffer": "253689533",
        "withdrawalBufferCapacity": "1000000000",
        "withdrawalRate": "0.9969"
      }
    }
  },
  "errMsg": "",
  "t": null,
  "uuid": null
}

```

> Field names in `cashierBuffer` match the live OpenAPI schema (`CashierBufferRespDto`).


# Troubleshooting

Use this guide when an API request fails, a transaction reverts, or an order/cash operation appears stuck.

## First Triage

Start with the layer where the failure appears:

1. If API `code` is not `200`, fix request auth, scope, or parameters first.
2. If API succeeds but no transaction is submitted, inspect the calldata response and wallet client.
3. If the transaction is submitted but reverts, debug allowance, balance, deadline, chain, and router target.
4. If the transaction succeeds but the API cannot find it, record `txHash` or wait for indexer backfill.
5. If mapping exists but state is not final, poll order or cash detail endpoints.

## API Auth Fails

Symptoms:

* HTTP `400` with `code: 401`
* `Unauthorized`
* Signature, nonce, timestamp, IP, or permission errors

All of these return the **same** `401 Unauthorized` — the response body does **not** say which one failed. Rule them out one by one; for a new integration, check the IP whitelist and timestamp first.

| Check                                 | Fix                                                                                                                                                       |
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| API key invalid, inactive, or expired | Confirm the key exists, is `active`, and `expires_at` is unset or future                                                                                  |
| Empty or mismatched IP whitelist      | Add your egress IP ([headers-and-permissions.md](/rwa-trading-apis/authentication/headers-and-permissions)); an empty whitelist rejects everything        |
| `x-api-ts` outside 45-second window   | Use current UTC milliseconds                                                                                                                              |
| Reused `x-api-nonce`                  | Use a unique UUID per request                                                                                                                             |
| Wrong HMAC URI                        | Authenticate `/api/v1/...`, not the full URL                                                                                                              |
| Literal `\n` in signed payload        | Join the 5 lines with **real newlines**, not the two-character `\n` (a common shell bug — see [signature.md](/rwa-trading-apis/authentication/signature)) |
| Query order mismatch                  | Sort query params by name before HMAC authentication                                                                                                      |
| Body mismatch                         | Sign the exact JSON string sent                                                                                                                           |
| Missing scope                         | Confirm `chainId + productType + access` permission                                                                                                       |

## Calldata Builds But Chain Transaction Reverts

Common causes:

| Symptom                                       | Likely cause                                                                                            | Fix                                                                                            |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| Deposit tx reverts                            | Insufficient ERC-20 allowance                                                                           | Check `walletAllowance`; approve spender                                                       |
| Deposit tx reverts                            | Wallet token balance too low                                                                            | Check `walletBalance`                                                                          |
| Order tx reverts                              | Plain buy has insufficient `mUsdBalance`                                                                | Deposit first, use `/orders/with-deposit/*` when eligible, or reduce `notional`                |
| Sell tx reverts                               | Plain sell has insufficient `exchangeBalance`, or order-with-deposit sell lacks stock allowance/balance | Approve stock, use `/orders/with-deposit/*`, or reduce quantity                                |
| Cancel tx reverts                             | Order not open or already canceling                                                                     | Query `/orders/{orderId}` first                                                                |
| Tx reverts out-of-gas (`gasUsed == gasLimit`) | Default `estimateGas` is too low on some chains (e.g. Monad) for router calls                           | Self-submit: add a gas buffer (`estimateGas × 1.5–2`). One Click: pass a sufficient `gasLimit` |
| Deadline error                                | `deadline` expired                                                                                      | Use a future Unix seconds value                                                                |
| Wrong chain                                   | Wallet on different chain than `x-api-chain-id`                                                         | Switch wallet network                                                                          |

## Deposit Not Credited Yet

A mined deposit transaction still needs API mapping and, for queued deposits, final cash settlement before `mUsdBalance` updates.

If the transaction succeeded:

1. Confirm `POST /api/v1/cash/deposits/tx` was called with the correct `txHash`.
2. Poll `GET /api/v1/cash/deposits/{operationId}`.
3. Check `operationStatus`.
4. Refresh `GET /api/v1/users/{userId}/balance`.

Queued deposits can remain `processing` while final cash settlement is still pending.

## Withdrawal Not Paid Out Yet

Withdrawals can be instant or queued. If queued:

1. `mUSD` is deducted first.
2. Final cash settlement completes later.
3. Cashier transfers tokens to the user when settlement completes.

Poll:

```
GET /api/v1/cash/withdrawals/{operationId}
```

Do not treat `processing` as failed unless the operation has an explicit failure status or support confirms an issue.

## Order Tx Mined But No Order ID

This usually means the indexer has not backfilled the mapping yet.

Check:

```
GET /api/v1/orders/tx/{txHash}
```

If missing:

1. Confirm the tx was sent to the returned `toAddress`.
2. Confirm the tx succeeded on the same chain as `x-api-chain-id`.
3. Confirm `POST /api/v1/orders/tx` was called by the same API key.
4. Wait for indexer backfill and retry.

## Order Open Too Long

For limit orders, open state can be normal. Practical lifecycle rules depend on `timeInForce` and backend execution.

> Production limit orders currently only accept `DAY`; other `timeInForce` values will make the order transaction revert.

For market orders, long open time usually means one of:

* Execution result not yet received.
* Final order result has not settled on-chain.
* Indexer/API has not refreshed status.

Use:

```
GET /api/v1/orders/{orderId}
```

Then inspect `openOrder.status` and history fields.

## One Click Send Fails

Check:

| Check                              | Fix                                                  |
| ---------------------------------- | ---------------------------------------------------- |
| `/1ct/status` is not `enable`      | Run prepare/sign/enable flow                         |
| API key has no bound `userAddress` | Configure API key user binding                       |
| Signer mismatch                    | EIP-712 signer must equal `signPayload.message.user` |
| Expired deadline                   | Prepare and sign a fresh payload                     |
| Deposit send fails                 | Bound user must approve cash token spender           |

## Escalation Data

When escalating to backend/support, include:

* Environment base URL
* `x-api-chain-id`
* `x-api-p`
* API response `code`, `errMsg`, `uuid`, `t`
* `txHash`
* `orderId` or `operationId`
* Wallet address / `userId`
* Request path and body (without API secret)


# Integration Test Checklist

Use this checklist before granting production access or enabling real user trading.

## Test Accounts and Scope

| Check                   | Expected                                |
| ----------------------- | --------------------------------------- |
| API key active          | Requests authenticate successfully      |
| IP whitelist configured | Requests only work from approved IPs    |
| `READ_ONLY` key         | GET works; POST/DELETE fails            |
| `WRITABLE` key          | GET/POST/DELETE work for approved scope |
| Product scope           | Only configured `x-api-p` works         |
| Chain scope             | Only configured `x-api-chain-id` works  |

## Authentication Tests

Required cases:

* Valid GET with sorted query params.
* Valid POST with exact raw body.
* Invalid timestamp older than 45 seconds.
* Duplicate nonce within the replay window.
* Body changed after signature.
* Missing `x-api-chain-id`.
* Wrong `x-api-p` casing.

Expected result: valid requests pass, and each invalid case fails with a clear auth or validation error.

## Market and Portfolio Tests

| Test                          | Expected                                                                                        |
| ----------------------------- | ----------------------------------------------------------------------------------------------- |
| `GET /config`                 | Returns `cashierBuffer`, `exchangeFeeConfig`, and `cashierTokenConfig` for scoped chain/product |
| `GET /symbols`                | Returns at least one known tradable stock                                                       |
| `GET /prices/{symbol}`        | Returns quote context                                                                           |
| `GET /corporate-action`       | Returns list or empty list without error                                                        |
| `GET /users/{userId}/balance` | Returns `mUsdBalance` and `tokenBalances`                                                       |
| `spenderAddress` query        | Returns `walletAllowance` for spender                                                           |

## Cash Tests

Required cases:

* Deposit calldata with valid `userAddress`, `tokenAddress`, `tokenAmount`.
* Deposit tx succeeds after approval.
* Deposit tx reverts or is blocked when allowance is missing.
* `POST /cash/deposits/tx` is idempotent for the same API key and tx.
* Deposit detail eventually reaches final status.
* Withdrawal calldata builds for available `mUsdBalance`.
* Withdrawal detail can be polled by `operationId`.

## Stock Movement Tests

Required cases:

* Stock deposit calldata builds for valid `userAddress`, `tokenAddress`, `tokenAmount`.
* Stock deposit tx succeeds after stock token approval.
* Stock withdrawal calldata builds for available `exchangeBalance`.
* Stock deposit and withdrawal details can be polled by `operationId`.

## Order Tests

| Test                             | Expected                                                                                                                                                                                                         |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Market buy calldata              | Returns router `toAddress`, `value`, `callData`                                                                                                                                                                  |
| Market sell calldata             | Plain sell requires sufficient `exchangeBalance`; wallet-held stock can be moved with `/stock/deposits/*` first                                                                                                  |
| Order-with-deposit buy calldata  | Requires cash token allowance and instant deposit eligibility                                                                                                                                                    |
| Order-with-deposit sell calldata | Requires stock token allowance and wallet stock balance                                                                                                                                                          |
| Limit buy calldata               | Requires `quantity`, `price`, and `timeInForce`; current production contracts accept only `DAY`                                                                                                                  |
| Limit sell calldata              | Requires `quantity`, `price`, and `timeInForce`; current production contracts accept only `DAY`. Plain sell requires sufficient `exchangeBalance`; wallet-held stock can be moved with `/stock/deposits/*` first |
| Cancel calldata                  | Works only for open limit orders                                                                                                                                                                                 |
| Record order tx                  | Backfills mapping/orderId                                                                                                                                                                                        |
| Order detail                     | Moves from open to history after settlement                                                                                                                                                                      |

Include negative tests:

* Missing `notional` for market buy.
* Missing `quantity` for market sell.
* Missing `price` for limit order.
* Non-`DAY` `timeInForce` for limit order.
* Expired `deadline`.
* Cancel market order.
* Cancel order owned by different API key.

## One Click Tests

Required cases:

* Status before enable.
* Prepare returns signer-matching `signPayload`.
* Signature by wrong wallet is rejected.
* Expired deadline is rejected.
* Enable succeeds with correct signature.
* `/send` order succeeds after enable.
* Deposit `/send` fails without token approval.
* Disable succeeds and blocks future `/send`.

## Reconciliation Tests

After each state-changing test, verify:

* API response `code` is `200`.
* Tx receipt succeeded.
* Mapping endpoint finds tx.
* Detail endpoint reaches expected state.
* Portfolio reflects final balances.
* Fees and partial fills are shown correctly when present.

## Production Readiness

Before go-live:

* Store API secrets securely.
* Log request path, response `uuid`, txHash, orderId, operationId.
* Never log API secret, wallet private key, or raw authorization material.
* Monitor delayed `processing` operations.
* Document queued cash behavior and async order settlement.
* Provide a manual support path for stuck tx/order/operation IDs.


