Installation
pnpm add @nile-markets/sdk viem
npm install @nile-markets/sdk viem
yarn 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 ,
Mode ,
PositionStatus ,
CloseReason ,
FeeType ,
MarginMode ,
PAIR_IDS ,
PAIRS ,
TENORS ,
getPairById ,
getPairByName ,
getTenorBySeconds ,
getTenorByLabel ,
} from "@nile-markets/sdk" ;
// Side enum
Side . LONG // 0 — trader profits when the base/quote rate rises
Side . SHORT // 1 — trader profits when the base/quote rate falls
// Tenors are NOT an enum — use the bundled `TENORS` array
TENORS // [{ seconds: 86400, label: "1D" }, { seconds: 604800, label: "1W" }, ...]
getTenorBySeconds ( 86400 )?. label // "1D"
getTenorByLabel ( "1M" )?. seconds // 2592000
// Protocol mode
Mode . NORMAL // 0 — all operations enabled
Mode . DEGRADED // 1 — degraded — opens blocked, closes/settlements/liquidations allowed
Mode . REDUCE_ONLY // 2 — closes only
Mode . PAUSED // 3 — all operations halted
// Fee type ordinals (match SettlementEngine.FeesCollected)
FeeType . LIQUIDATION_PENALTY // 0
FeeType . EARLY_TERMINATION // 1
FeeType . MATURITY // 2
FeeType . TRADING // 3
// Pair identifiers (keccak256 hashes)
PAIR_IDS . EUR_USD // bytes32 hash of "EUR/USD"
PAIR_IDS . USD_JPY // bytes32 hash of "USD/JPY"
// Bundled pair metadata cache (mirror of contracts/config/sepolia/defaults.json)
PAIRS // [{ pairId, name: "EUR/USD", base: "EUR", quote: "USD", pythFeedId, ... }, ...]
getPairByName ( "EUR/USD" )?. pairId
getPairById ( PAIR_IDS . USD_JPY )?. displayDecimals // 3
The bundled PAIRS and TENORS caches are sourced from the canonical defaults file and travel with each SDK release. For authoritative state at runtime, prefer the on-chain getters (OracleModule.getAllPairs() and Config.getEnabledTenors()) — they always reflect the live registry.
Labels
Pair-agnostic labels ship from the SDK; pair-aware side labels (e.g. "Long EUR" vs "Short USD") come from @nile-markets/shared. Side labels derive from the pair’s base currency — Pyth-native ordering, so USD/JPY → "Long USD", not "Long JPY".
import {
MODE_LABELS ,
CLOSE_REASON_LABELS ,
POSITION_STATUS_LABELS ,
FEE_TYPE_LABELS ,
MARGIN_MODE_LABELS ,
tenorLabel ,
getPairByName ,
} from "@nile-markets/sdk" ;
import { getSideLabel } from "@nile-markets/shared" ;
tenorLabel ( 604800 ) // "1W"
MODE_LABELS [ Mode . NORMAL ] // "Normal"
CLOSE_REASON_LABELS [ CloseReason . LIQUIDATED ] // "Liquidated"
FEE_TYPE_LABELS [ FeeType . TRADING ] // "Trading Fee"
getSideLabel ( getPairByName ( "EUR/USD" ), Side . LONG ) // "Long EUR"
getSideLabel ( getPairByName ( "USD/JPY" ), Side . SHORT ) // "Short USD"
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: "riskCapacityUtilization" ,
}),
]);
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. getForwardByTenor resolves the next fixing timestamp for that tenor automatically:
import { oracleModuleAbi , PAIR_IDS , getTenorByLabel } from "@nile-markets/sdk" ;
import { formatUnits } from "viem" ;
const ORACLE_MODULE = "0x..." ; // See Contract Addresses page
const tenorSeconds = getTenorByLabel ( "1W" ) ! . seconds ; // 604800
const [ fixingTimestamp , forward ] = await publicClient . readContract ({
address: ORACLE_MODULE ,
abi: oracleModuleAbi ,
functionName: "getForwardByTenor" ,
args: [ PAIR_IDS . EUR_USD , tenorSeconds ],
});
console . log ( `EUR/USD 1W forward: ${ formatUnits ( forward . forwardPrice , 18 ) } ` );
console . log ( `Maturity: ${ new Date ( Number ( fixingTimestamp ) * 1000 ). toISOString () } ` );
console . log ( `Published at: ${ new Date ( Number ( forward . publishTimestamp ) * 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 ,
PAIR_IDS ,
getTenorByLabel ,
} 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
const tenorSeconds = getTenorByLabel ( "1W" ) ! . seconds ; // 604800
// 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: [
{
pairId: PAIR_IDS . EUR_USD ,
side: Side . LONG ,
notional ,
tenorSeconds ,
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 Type Decimals Raw Example Human-Readable USDC amounts (notional, margin, fees) 6 1000000000010,000.00 USDC Prices (spot, forward, strike) 18 10800000000000000001.08000 EUR/USD Basis points (fees, utilization) 0 2002.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 ( 500000000 n , 6 ); // "500.0"
const humanPrice = formatUnits ( 1085000000000000000 n , 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 > ;
}
Related Pages
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.