npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

xccy-sdk-ts

v0.1.13

Published

High-level TypeScript SDK for XCCY trading actions

Readme

xccy-sdk-ts

npm version

TypeScript SDK for XCCY integrations. Wraps on-chain contracts and HTTP APIs behind a single XccySdk client for trading, margin, simulations, markets, positions, collateral, history, and leaderboard.


Installation

npm i xccy-sdk-ts ethers

Why this SDK

Quick Start

import { JsonRpcProvider, Wallet } from "ethers";
import { XccySdk } from "xccy-sdk-ts";

const provider = new JsonRpcProvider(process.env.RPC_URL!);
const wallet = new Wallet(process.env.PRIVATE_KEY!, provider);

const sdk = new XccySdk({
  chain: "arbitrum",
  wallet, // for write calls
  // apiBaseUrl and addresses are optional if defaults are valid for your env
});

Call APIs on grouped modules (sdk.tradingModules.trading.*, sdk.tradingModules.account.*, sdk.analytics.*, …). XccySdk wires RPC, contracts, and shared runtime context; it also exposes vammManager and collateralEngine getters for low-level access when you need them.

Read-only client

import { XccySdk } from "xccy-sdk-ts";

const sdk = new XccySdk({
  chain: "arbitrum",
  rpcUrl: process.env.RPC_URL!,
});

SDK components

XccySdk

Main facade: builds XccyRuntimeContext, connects contracts via src/contracts.ts, and instantiates the modules below.

TradingModule

Writes (mint, burn, swap)

On-chain writes go through the connected VAMM manager (sdk.vammManager / internal contracts.vammManager). Each method sends a transaction, waits with await tx.wait() by default (optional second argument confirmations?: numbertx.wait(confirmations)), and returns ConfirmedTx<TParsed>:

  • transactionHash, gasUsed, rawLogs, receipt
  • parsed: values decoded from the relevant VAMM event in the receipt (Mint, Burn, or Swap), not from an extra staticCall after mining.

mint — add liquidity for an AccountId in a tick range on key. You pass a token amount via TokenAmountInput (amount decimal string or amountRaw); the SDK converts it to VAMM liquidity (see toLiquidityFromTokenAmount). Tick range can be explicit tickLower / tickUpper or percent helpers tickLowerPercent / tickUpperPercent. parsed: VammMintOutputs — e.g. feeIncurred from the Mint log.

const mintOut = await sdk.tradingModules.trading.mint({
  key: poolKey,
  account,
  tickLower: -120n,
  tickUpper: 120n,
  amount: "1000",
  tokenDecimals: 6,
});
// mintOut.parsed.feeIncurred, mintOut.rawLogs, mintOut.receipt

burn — remove liquidity for the same pool/account/range semantics as mint (same MintBurnParams shape). parsed: VammBurnOutputsamount and feeIncurred from the Burn log.

const burnOut = await sdk.tradingModules.trading.burn({
  key: poolKey,
  account,
  tickLower: -120n,
  tickUpper: 120n,
  amount: "250",
});

swap — execute a swap on key for account using contract-style SwapParams: amountSpecified / sqrtPriceLimitX96 / tickLower / tickUpper (notional direction and limits as on-chain). parsed: VammSwapOutputs — deltas, fee, priceAfterSwap; fixedTokenDeltaUnbalanced matches fixedTokenDelta from the mined Swap event (use simulateSwap for the simulated unbalanced delta).

const swapOut = await sdk.tradingModules.trading.swap({
  key: poolKey,
  params: {
    account,
    amountSpecified: -1000n,
    sqrtPriceLimitX96: 0n,
    tickLower: -600n,
    tickUpper: 600n,
  },
});

AccountModule

Margin, ERC-20 approvals/balances, and Collateral Engine calls go through the typechain instance from connectContracts (CollateralEngineFactory in @xccy/typechains). Helpers in src/helpers/erc20.ts use minimal ERC-20 ABIs for token reads/writes.

  • setAccountOperator / isAccountOperator: the SDK accepts AccountId for consistency, but the on-chain methods take the owner address and operator — the implementation forwards account.owner (per current CE ABI in @xccy/typechains).
  • calculateAccountObligation: the generated contract type exposes an overload; the SDK calls the (account) overload via the full signature string (see implementation).
  • updateMarginBatchNative: talks to the Operator contract at a caller-supplied address via OperatorFactory from @xccy/typechains.

AnalyticsModule

REST data: markets, positions, collateral, trades, history, leaderboard, platform metrics. HTTP routes live under src/api/endpoints/ (barrel index.ts: analytics/*, lockYield), with shared client src/api/httpClient.ts.

LockYieldModule

Lock-yield flows via the connected lockYield contract from connectContracts.

Supporting code (for contributors)


Core Concepts

PoolKey

Most trading methods work with a pool key (see PoolKey / @xccy/typechains):

const poolKey = {
  underlyingAsset: "0x...",
  compoundToken: "0x...",
  termStartTimestampWad: 0n,
  termEndTimestampWad: 0n,
  feeWad: 100000000000000n,
  tickSpacing: 10,
};

AccountId

Account tuple used across write/read APIs (AccountId):

const account = {
  owner: "0x...",
  id: 0n,
  isolatedMarginToken: "0x...",
};

Amount semantics

  • swap: pass contract-style amountSpecified (bigint) in swap params.
  • mint / burn, simulateLiquidity, depositMargin / withdrawMargin: pass exactly one of:
    • amount: decimal string in whole tokens (e.g. "1.5"),
    • amountRaw: bigint already in token base units (wei).
  • Do not pass both; conversion for amount uses token decimals() (or tokenDecimals when provided). Implemented in resolveTokenAmountToBaseUnits and toLiquidityFromTokenAmount.

Common Usage

Swap

const swapOut = await sdk.tradingModules.trading.swap({
  key: poolKey,
  params: {
    account,
    amountSpecified: -1000n,
    sqrtPriceLimitX96: 0n,
    tickLower: -600n,
    tickUpper: 600n, // int24 range bounds as bigint
  },
});
// swapOut.rawLogs, swapOut.transactionHash, swapOut.gasUsed, swapOut.receipt

Add liquidity

const mintOut = await sdk.tradingModules.trading.mint({
  key: poolKey,
  account,
  tickLowerPercent: "1.5%", // or tickLower/tickUpper directly
  tickUpperPercent: "3.0%",
  amount: "1000", // user-facing token amount
});

Remove liquidity

const burnOut = await sdk.tradingModules.trading.burn({
  key: poolKey,
  account,
  tickLower: -120n,
  tickUpper: 120n,
  amount: "250", // or amountRaw: …n when you already have base units
});

Deposit margin

await sdk.tradingModules.account.depositMargin({
  account,
  token: "0x...",
  amount: "500", // or amountRaw: 500n * 10n ** 18n when you already have base units
});

Simulation

simulateSwap / simulateLiquidity run staticCall plus extra RPC (e.g. pool state, optional estimateGas) and attach metrics. For a dry-run only (no send), call sdk.vammManager.swap.staticCall / mint.staticCall / burn.staticCall with the same arguments you would pass on-chain.

const swapSim = await sdk.tradingModules.trading.simulateSwap({
  key: poolKey,
  params: {
    account,
    amountSpecified: -1000n,
    sqrtPriceLimitX96: 0n,
    tickLower: -600n,
    tickUpper: 600n,
  },
});

const liqSim = await sdk.tradingModules.trading.simulateLiquidity({
  key: poolKey,
  account,
  amount: "1000",
  tickLowerPercent: "1.5%",
  tickUpperPercent: "3%",
});

Data APIs

Examples (all on AnalyticsModule):

const markets = await sdk.analytics.getMarkets();

const positions = await sdk.analytics.getMyPositions({
  owner: "0x...",
  accountId: 0n,
  isolatedMarginToken: "0x...",
});

const subAccounts = await sdk.analytics.getSubAccounts({ owner: "0x..." });

const collateral = await sdk.analytics.getCollateral({
  owner: "0x...",
  accountId: 0n,
  isolatedMarginToken: "0x...",
});

const floatingHistory = await sdk.analytics.getPoolFloatingRateHistory({
  asset: "0x...",
  resolution: "5m",
  limit: 100,
});

API surface (high level)

tradingModules.trading

swap, mint, burn (each returns ConfirmedTx), simulateSwap, simulateLiquidity, getVammState

tradingModules.account

depositMargin, withdrawMargin, updateMarginBatchNative, calculateAccountObligation, getAccountMarginValues, setAccountOperator, isAccountOperator, approveErc20, getErc20Balance, getErc20Allowance

analytics

getMarkets, getPositions, getMyPositions, getSubAccounts, getCollateral, getCollateralTokens, getRecentTrades, getMyLiquidityTrades, getLiquidityTrades, getPoolRateHistory, getPoolFloatingRateHistory, getPnlHistory, getSettledPositions, getLeaderboard, getLeaderboardPosition, getPlatformMetrics, getUserPerformance

tradingModules.lockYield

openLockYield, isLockYieldPoolApproved, isLockYieldPaused, getLockYieldPositionIds, getLockYieldPositionStats, simulateOpenLockYield, getLockYieldPositions, getLockYieldPoolStats


Error Handling

XccySdkError is thrown for SDK-normalized failures (e.g. simulation / gas estimation, via asSimulateError internally). Use isXccySdkError and optional toBotError for structured logging.

import { isXccySdkError, toBotError } from "xccy-sdk-ts";

try {
  await sdk.tradingModules.trading.simulateSwap({ key: poolKey, params: swapParams });
} catch (e) {
  if (isXccySdkError(e)) {
    console.error(e.code, e.message, e.details);
    // e.g. QUOTE_INSUFFICIENT_COLLATERAL — user-facing message is short;
    // original revert text may appear in e.details?.reason
  } else {
    console.error(e);
  }
}

const bot = toBotError(e); // { name, message, code, retryable, details? }

Types: XccySdkError, XccySdkErrorCode.

Direct ethers calls (e.g. sdk.vammManager.swap.staticCall) throw ethers errors (CALL_EXCEPTION, etc.), not XccySdkError.


Development

npm run build
npm run typecheck
npm test

Smoke scripts (see package.json; set env vars locally, e.g. in .env):

npm run smoke:api
npm run smoke:markets
npm run smoke:quote
npm run smoke:liquidity:quote
npm run smoke:mint          # on-chain mint (needs PRIVATE_KEY, pool env, etc.)
npm run smoke:deposit
npm run smoke:bot:flow
npm run smoke:errors

Node + RPC: RPC_URL is required for smoke scripts (there is no default endpoint in the repo). Put RPC_URL=… in .env as KEY=value (no export prefix) so dotenv in scripts sees it the same as your shell. smoke-mint.ts uses a pinned Arbitrum network (42161, staticNetwork), optional batchMaxCount: 1, and attaches provider.on("error", …) so transient RPC issues during tx polling do not crash the process as an unhandled error event.


Reference style

This README follows the same idea as TacBuild/tac-sdk: documentation, entrypoints, and examples are linked inline so you can jump from the text to the implementation or npm without a separate docs site.