> For the complete documentation index, see [llms.txt](https://docs.anchored.finance/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.anchored.finance/trading-api/guides/demo-code.md).

# 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://rwa-api.anchored.finance/rwa/trading";

type HttpMethod = "GET" | "POST" | "DELETE";

interface TradingApiConfig {
    apiKey: string;
    apiSecret: string;
    chainId: number;
    productType: "Anchored";
}

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 { 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: "Anchored",
};

const account = privateKeyToAccount(process.env.TRADING_USER_PRIVATE_KEY as Hex);
const walletClient = createWalletClient({
    account,
    chain: base,
    transport: http(),
});

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 walletClient.sendTransaction({
        to: calldata.toAddress,
        data: calldata.callData,
        value: BigInt(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 {
    createPublicClient,
    createWalletClient,
    erc20Abi,
    http,
    maxUint256,
    type Address,
    type Hex,
    type PublicClient,
    type WalletClient,
} from "viem";

const SPENDER: Address = "0x4f090d817fd83753988a7b0c1d76f170f8461be8"; // Anchored mainnet StockRouter

interface CashCalldata {
    toAddress: Address;
    value: string;
    callData: Hex;
}

async function depositSelfSubmit(
    walletClient: WalletClient,
    publicClient: PublicClient,
    account: ReturnType<typeof privateKeyToAccount>,
    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 walletClient.sendTransaction({
        to: calldata.toAddress,
        data: calldata.callData,
        value: BigInt(calldata.value),
    });

    await tradingApiRequest(config, "POST", "/api/v1/cash/deposits/tx", {
        body: { txHash },
    });

    return txHash;
}
```

Spender addresses: [cash/deposits.md](/trading-api/cash-operations/deposits.md).

## 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 walletClient.sendTransaction({
        to: calldata.toAddress,
        data: calldata.callData,
        value: BigInt(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 walletClient.sendTransaction({
        to: calldata.toAddress,
        data: calldata.callData,
        value: BigInt(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 walletClient.sendTransaction({
        to: calldata.toAddress,
        data: calldata.callData,
        value: BigInt(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: {
            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");
    }

    const signature = await walletClient.signTypedData({
        domain: {
            ...signPayload.domain,
            chainId: BigInt(signPayload.domain.chainId),
        },
        types: signPayload.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](/trading-api/guides/one-click-flow.md).

## 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](/trading-api/authentication/examples.md) for Node.js-only HMAC snippets.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.anchored.finance/trading-api/guides/demo-code.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
