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 , 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 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.