Skip to main content

Installation

pnpm add @nile-markets/sdk viem
The SDK has zero runtime dependencies. viem is a peer dependency for type compatibility but is not imported by the SDK itself. You need viem (or ethers) separately to interact with the chain.

What the SDK Exports

ABIs

Typed ABI arrays for every protocol contract, ready to use with viem or wagmi:
import {
  positionManagerAbi,
  marginAccountsAbi,
  poolVaultAbi,
  oracleModuleAbi,
  configAbi,
  modeControllerAbi,
  riskManagerAbi,
  settlementEngineAbi,
  mockUsdcAbi,
} from "@nile-markets/sdk";

Constants

Enums and identifiers that match onchain values:
import { Side, Tenor, Mode, PositionStatus, CloseReason, PAIR_IDS } from "@nile-markets/sdk";

// Side enum
Side.LONG   // 0 — trader profits when EUR/USD rises
Side.SHORT  // 1 — trader profits when EUR/USD falls

// Tenor enum
Tenor.ONE_DAY    // 0 — 24 hours
Tenor.ONE_WEEK   // 1 — 7 days
Tenor.ONE_MONTH  // 2 — 30 days

// Protocol mode
Mode.NORMAL       // 0 — all operations enabled
Mode.DEGRADED     // 1 — closes only, no new positions
Mode.REDUCE_ONLY  // 2 — closes only
Mode.PAUSED       // 3 — all operations halted

// Pair identifiers (keccak256 hashes)
PAIR_IDS.EUR_USD  // bytes32 hash of "EUR/USD"

Labels

Human-readable labels for UI display:
import { TENOR_LABELS, SIDE_LABELS, MODE_LABELS } from "@nile-markets/sdk";

TENOR_LABELS[Tenor.ONE_WEEK]  // "1 Week"
SIDE_LABELS[Side.LONG]        // "Long EUR"
MODE_LABELS[Mode.NORMAL]      // "Normal"

Setting Up a Client

Create a viem public client to read contract state. For write operations, you also need a wallet client.
import { createPublicClient, createWalletClient, http } from "viem";
import { sepolia } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";

// Read-only client
const publicClient = createPublicClient({
  chain: sepolia,
  transport: http("YOUR_RPC_URL"),
});

// Write client (for transactions)
const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY");
const walletClient = createWalletClient({
  account,
  chain: sepolia,
  transport: http("YOUR_RPC_URL"),
});
See Contract Addresses for all deployed addresses on Sepolia.

Reading Contract State

Pool State

Query the PoolVault for total deposits and share supply:
import { poolVaultAbi } from "@nile-markets/sdk";
import { formatUnits } from "viem";

const POOL_VAULT = "0x..."; // See Contract Addresses page

const [totalAssets, totalSupply, utilization] = await Promise.all([
  publicClient.readContract({
    address: POOL_VAULT,
    abi: poolVaultAbi,
    functionName: "totalAssets",
  }),
  publicClient.readContract({
    address: POOL_VAULT,
    abi: poolVaultAbi,
    functionName: "totalSupply",
  }),
  publicClient.readContract({
    address: POOL_VAULT,
    abi: poolVaultAbi,
    functionName: "utilization",
  }),
]);

console.log(`Pool USDC: ${formatUnits(totalAssets, 6)}`);
console.log(`Shares outstanding: ${formatUnits(totalSupply, 6)}`);
console.log(`Share price: ${Number(totalAssets) / Number(totalSupply)}`);
console.log(`Utilization: ${Number(utilization) / 100}%`); // bps → percent

Forward Prices

Read the current EUR/USD forward price for a specific tenor:
import { oracleModuleAbi, PAIR_IDS, Tenor } from "@nile-markets/sdk";
import { formatUnits } from "viem";

const ORACLE_MODULE = "0x..."; // See Contract Addresses page

const forward = await publicClient.readContract({
  address: ORACLE_MODULE,
  abi: oracleModuleAbi,
  functionName: "getForwardPrice",
  args: [PAIR_IDS.EUR_USD, Tenor.ONE_WEEK],
});

console.log(`EUR/USD 1W forward: ${formatUnits(forward.price, 18)}`);
console.log(`Valid: ${forward.isValid}`);
console.log(`Updated: ${new Date(Number(forward.timestamp) * 1000).toISOString()}`);

Protocol Mode

Check the current operating mode before submitting transactions:
import { modeControllerAbi, Mode } from "@nile-markets/sdk";

const MODE_CONTROLLER = "0x..."; // See Contract Addresses page

const mode = await publicClient.readContract({
  address: MODE_CONTROLLER,
  abi: modeControllerAbi,
  functionName: "currentMode",
});

if (mode !== Mode.NORMAL) {
  console.warn("Protocol is not in NORMAL mode — new positions are disabled");
}

Margin Account Balance

Read a trader’s deposited collateral and available balance:
import { marginAccountsAbi } from "@nile-markets/sdk";
import { formatUnits } from "viem";

const MARGIN_ACCOUNTS = "0x..."; // See Contract Addresses page
const trader = "0xYOUR_ADDRESS";

const [collateral, locked, available] = await Promise.all([
  publicClient.readContract({
    address: MARGIN_ACCOUNTS,
    abi: marginAccountsAbi,
    functionName: "collateralBalance",
    args: [trader],
  }),
  publicClient.readContract({
    address: MARGIN_ACCOUNTS,
    abi: marginAccountsAbi,
    functionName: "imLockedTotal",
    args: [trader],
  }),
  publicClient.readContract({
    address: MARGIN_ACCOUNTS,
    abi: marginAccountsAbi,
    functionName: "availableBalance",
    args: [trader],
  }),
]);

console.log(`Total collateral: ${formatUnits(collateral, 6)} USDC`);
console.log(`Locked in positions: ${formatUnits(locked, 6)} USDC`);
console.log(`Available to trade: ${formatUnits(available, 6)} USDC`);

Writing Transactions

Approve USDC and Open a Position

Opening a position requires two transactions: approve USDC spending, then call openPosition.
import { positionManagerAbi, mockUsdcAbi, Side, Tenor, PAIR_IDS } from "@nile-markets/sdk";
import { parseUnits } from "viem";

const USDC = "0x...";              // See Contract Addresses page
const POSITION_MANAGER = "0x...";  // See Contract Addresses page

const notional = parseUnits("10000", 6);  // 10,000 USDC notional
const margin = parseUnits("500", 6);      // 500 USDC margin

// Step 1: Approve PositionManager to spend your USDC
const approveHash = await walletClient.writeContract({
  address: USDC,
  abi: mockUsdcAbi,
  functionName: "approve",
  args: [POSITION_MANAGER, margin],
});

// Wait for approval to confirm
await publicClient.waitForTransactionReceipt({ hash: approveHash });

// Step 2: Open the position
const openHash = await walletClient.writeContract({
  address: POSITION_MANAGER,
  abi: positionManagerAbi,
  functionName: "openPosition",
  args: [PAIR_IDS.EUR_USD, Side.LONG, notional, Tenor.ONE_WEEK, margin],
});

const receipt = await publicClient.waitForTransactionReceipt({ hash: openHash });
console.log(`Position opened in tx: ${receipt.transactionHash}`);

Deposit Margin

Deposit USDC collateral into your MarginAccounts balance:
import { marginAccountsAbi, mockUsdcAbi } from "@nile-markets/sdk";
import { parseUnits } from "viem";

const USDC = "0x...";
const MARGIN_ACCOUNTS = "0x...";

const amount = parseUnits("1000", 6); // 1,000 USDC

// Approve MarginAccounts to pull USDC
const approveHash = await walletClient.writeContract({
  address: USDC,
  abi: mockUsdcAbi,
  functionName: "approve",
  args: [MARGIN_ACCOUNTS, amount],
});
await publicClient.waitForTransactionReceipt({ hash: approveHash });

// Deposit
const depositHash = await walletClient.writeContract({
  address: MARGIN_ACCOUNTS,
  abi: marginAccountsAbi,
  functionName: "deposit",
  args: [amount],
});
await publicClient.waitForTransactionReceipt({ hash: depositHash });

Numeric Precision

All onchain values use fixed-point integers. You need to convert between human-readable numbers and raw contract values.
Value TypeDecimalsRaw ExampleHuman-Readable
USDC amounts (notional, margin, fees)61000000000010,000.00 USDC
Prices (spot, forward, strike)1810800000000000000001.08000 EUR/USD
Basis points (fees, utilization)02002.00%
Use viem’s parseUnits and formatUnits for conversion:
import { parseUnits, formatUnits } from "viem";

// Human → contract
const usdcAmount = parseUnits("500.00", 6);    // 500000000n
const price = parseUnits("1.08500", 18);        // 1085000000000000000n

// Contract → human
const humanUsdc = formatUnits(500000000n, 6);   // "500.0"
const humanPrice = formatUnits(1085000000000000000n, 18); // "1.085"
Never use floating-point arithmetic with contract values. Always operate on bigint values and convert to human-readable strings only for display.

Using with wagmi

If you are building a React frontend with wagmi, the SDK ABIs work directly with wagmi hooks:
import { useReadContract, useWriteContract } from "wagmi";
import { poolVaultAbi } from "@nile-markets/sdk";

function PoolStats({ vaultAddress }: { vaultAddress: `0x${string}` }) {
  const { data: totalAssets } = useReadContract({
    address: vaultAddress,
    abi: poolVaultAbi,
    functionName: "totalAssets",
  });

  return <div>Pool: {totalAssets ? formatUnits(totalAssets, 6) : "..."} USDC</div>;
}

Contract Addresses

Sepolia deployment addresses for all contracts.

Types Reference

Full enum and struct definitions used across the protocol.

Quick Start

End-to-end setup from zero to querying protocol state.

Open a Position

Step-by-step tutorial for your first trade.