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

@varla/sdk

v4.0.0

Published

TypeScript SDK for **Varla Protocol** — lending and risk infrastructure for prediction markets.

Readme

@varla/sdk

TypeScript SDK for Varla Protocol — lending and risk infrastructure for prediction markets.

Provides contract ABIs, deployed addresses, typed viem bindings, read-only view helpers, simulate-first write actions, leverage planning, error decoding, and formatting utilities.

Supported chains: Polygon (Polymarket) · BSC (Opinion)


Install

bun add @varla/sdk
# or
npm i @varla/sdk

Peer dependency: viem ^2.0.0


Subpath imports

| Import | Description | | --- | --- | | @varla/sdk | Core: contracts, views, actions, events, batch, format — everything re-exported | | @varla/sdk/views | Read-only on-chain queries (account, pool, oracle, system) | | @varla/sdk/actions | Simulate-first write helpers (deposit, borrow, repay, liquidate, oracle config…) | | @varla/sdk/events | Event log fetching, decoding, and oracle registry sync | | @varla/sdk/contracts | Typed viem contract instances (getVarlaContracts) | | @varla/sdk/abi | Raw ABI constants for all deployed contracts | | @varla/sdk/addresses | Per-chain deployed address books (polygon, bsc) | | @varla/sdk/errors | Custom error decoding across all Varla contracts | | @varla/sdk/leverage | Leverage loop planning, execution, and deleverage | | @varla/sdk/batch | Multicall chunking utilities for RPC-efficient reads | | @varla/sdk/safe | Gnosis Safe MultiSend encoding for proxy wallet users | | @varla/sdk/format | Number formatting (formatWad, formatBps, parseUnits) | | @varla/sdk/types | Shared types (ChainKey, AddressBook) |

import { getVarlaContracts } from "@varla/sdk/contracts";
import * as views from "@varla/sdk/views";
import * as actions from "@varla/sdk/actions";

Quick start

import { createPublicClient, http } from "viem";
import { polygon } from "viem/chains";
import { getVarlaContracts } from "@varla/sdk/contracts";
import * as views from "@varla/sdk/views";

const client = createPublicClient({
  chain: polygon,
  transport: http(process.env.POLYGON_RPC_URL!),
});

const c = getVarlaContracts({ chain: "polygon", client });

Reading on-chain data

All view helpers are async functions that accept a typed viem contract instance and return plain objects.

Lender / pool views

// Pool rates, utilization, and APY
const rates = await views.readPoolRates({ pool: c.pool });

// Pool health score (utilization-based safety signal)
const health = await views.readPoolHealthScore({ pool: c.pool });
// → { kind: "ok", utilizationWad, scoreBps } or { kind: "no-liquidity" }

// Share price (assets per share, WAD precision)
const price = await views.readPoolSharePrice({ pool: c.pool });

// Full pool snapshot (caps, accounting, rates, share price)
const poolSnap = await views.readPoolSnapshot({ pool: c.pool });

// User's lender position: shares, current value, max withdrawable
const lender = await views.readLenderSnapshot({ pool: c.pool, user: "0x..." });
// → { shares, assets, maxWithdrawAssets, maxRedeemShares }

Borrower / core views

// Full account overview: positions, debt, health factor, borrow limits
const account = await views.readAccountSnapshot({ core: c.core, user: "0x..." });

// Just borrow limits + health factor
const limits = await views.readBorrowLimits({ core: c.core, user: "0x..." });

// Health factor (liquidation threshold check)
const hf = await views.readHealthFactor({ core: c.core, user: "0x..." });
// → { healthFactor, canBeLiquidated }

// LTV tier configuration
const tiers = await views.readTieredLtvConfig({ core: c.core });
// → { tier0, tier1, tier2 } (CONSERVATIVE / MODERATE / RISK)

// Effective LTV for a specific position
const ltv = await views.readLtvForPosition({ core: c.core, positionId: 123n });

Wallet position discovery

ERC-1155 has no tokenIdsOf(owner)readWalletPositionIds solves this by reading all oracle-configured position IDs and batch-checking balances via multicall.

// Discover which Varla positions a wallet holds (without an indexer)
const wallet = await views.readWalletPositionIds({
  oracle: c.oracle,         // reads getConfiguredPositions()
  positionsToken: c.ctf,    // ERC-1155 contract
  client,                   // multicall provider
  user: "0x...",
});
// → { user, candidateCount: 150, positionIds: [42n, 99n], balances: [5n, 12n] }

Hypothetical borrow capacity

Computes how much a user could borrow if they deposited their wallet-held positions.

// With explicit position IDs (e.g., from your own indexer)
const capacity = await views.readHypotheticalBorrowCapacity({
  core: c.core,
  oracle: { address: c.oracle.address },
  positionsToken: c.ctf,
  client,
  user: "0x...",
  walletPositionIds: [42n, 99n],  // known IDs
});

// Or auto-discover from oracle registry (no walletPositionIds needed)
const capacityAuto = await views.readHypotheticalBorrowCapacity({
  core: c.core,
  oracle: c.oracle,  // must have .read.getConfiguredPositions
  positionsToken: c.ctf,
  client,
  user: "0x...",
  // walletPositionIds omitted → auto-discovers via readWalletPositionIds
});

console.log(capacityAuto.current.maxBorrow);     // from deposited positions
console.log(capacityAuto.wallet.portfolioValue);  // wallet positions value
console.log(capacityAuto.potential.maxBorrow);     // if all wallet positions deposited

Note: Auto-discovery scans all oracle-configured positions (one balanceOf multicall per position). For registries with 500+ positions this is efficient via chunked multicall, but prefer passing explicit walletPositionIds when you have them from an indexer.

Oracle views

// Oracle registry (all configured positions)
const registry = await views.readOracleRegistry({ oracle: c.oracle });

// Price data for a position
const priceData = await views.readOraclePriceData({ oracle: c.oracle, positionId: 123n });

// Position snapshot (price + metadata)
const snap = await views.readPositionSnapshot({ oracle: c.oracle, positionId: 123n });

// Batch: many position snapshots in one multicall
const snaps = await views.readManyPositionSnapshots({
  oracle: c.oracle,
  positionIds: [1n, 2n, 3n],
});

System snapshot

// Everything at once: pool + core + oracle state
const system = await views.readSystemSnapshot({
  core: c.core,
  pool: c.pool,
  oracle: c.oracle,
});

Writing transactions

All write helpers use the simulate-first pattern — they return a SimulatedTx with a pre-validated request you can send via any viem wallet client.

import * as actions from "@varla/sdk/actions";

// Deposit collateral
const sim = await actions.prepareCoreDeposit({
  publicClient: client,
  coreAddress: c.core.address,
  account: "0x...",
  positionId: 123n,
  amount: 1_000_000n,
});

// Send the transaction
const hash = await actions.sendTx({ walletClient, request: sim.request });

Common actions

// Borrow from pool
await actions.prepareCoreBorrow({ publicClient, coreAddress, account, amount });

// Repay debt
await actions.prepareCoreRepay({ publicClient, coreAddress, account, amount });

// Withdraw collateral
await actions.prepareCoreWithdraw({ publicClient, coreAddress, account, positionId, amount });

// ERC-20 approval (for pool deposits)
await actions.prepareErc20Approve({ publicClient, tokenAddress, spender, amount });

// Pool deposit (lender)
await actions.preparePoolDeposit({ publicClient, poolAddress, account, assets });

// Pool withdraw (lender)
await actions.preparePoolWithdraw({ publicClient, poolAddress, account, assets });

Leverage

Plan and execute leveraged positions through iterative borrow → buy → deposit loops.

import { planLeverage, executeLeverage } from "@varla/sdk/leverage";

// Plan a 3× leveraged long position
const plan = planLeverage({
  initialCapital: 100_000_000n, // 100 pUSD (6 decimals)
  tokenPriceE8: 50_000_000n,   // $0.50
  ltvWad: 650_000_000_000_000_000n, // 65% LTV
  targetLeverageWad: 3_000_000_000_000_000_000n, // 3.0×
});

console.log(plan.summary.effectiveLeverageWad); // actual achieved leverage
console.log(plan.summary.estimatedLiquidationPriceE8); // liquidation price
console.log(plan.steps); // ordered steps: buy → deposit → borrow → buy → ...

Safe / proxy wallet integration

Polymarket users hold ERC-1155 positions inside Gnosis Safe proxy wallets. The @varla/sdk/safe subpath provides helpers to batch multiple Varla operations into a single Safe transaction — matching the UX of competitors like Gondor.

Key insight: Varla contracts already accept calls from any msg.sender (EOA or Safe). No contract changes are needed. The SDK helper simply packs multiple prepare*() results into the Safe MultiSend format.

Full example: approve → deposit → borrow in one Safe tx

import { encodeSafeMultiSend } from "@varla/sdk/safe";
import * as actions from "@varla/sdk/actions";

const safeAddress = "0x..."; // user's Gnosis Safe address

// 1. Prepare each step (simulate against the Safe as msg.sender)
const approve = await actions.prepareErc1155SetApprovalForAll({
  publicClient: client,
  tokenAddress: ctfAddress,
  account: safeAddress,
  operator: coreAddress,
  approved: true,
});

const deposit = await actions.prepareCoreDeposit({
  publicClient: client,
  coreAddress: c.core.address,
  account: safeAddress,
  positionIds: [positionId],
  amounts: [amount],
});

const borrow = await actions.prepareCoreBorrow({
  publicClient: client,
  coreAddress: c.core.address,
  account: safeAddress,
  amount: borrowAmount,
});

// 2. Encode as a single Safe MultiSend batch
const batch = encodeSafeMultiSend([approve, deposit, borrow]);
// batch.to    → Safe MultiSend contract address
// batch.data  → encoded multiSend(bytes) calldata
// batch.value → total ETH value (usually 0n)
// batch.count → 3

// 3. Submit via Safe SDK (or Safe Apps SDK in a dApp)
const safeTx = await safeSdk.createTransaction({
  transactions: [{
    to: batch.to,
    data: batch.data,
    value: batch.value.toString(),
    operation: 1, // DELEGATECALL to MultiSend
  }],
});
await safeSdk.executeTransaction(safeTx);

API

| Function | Description | | --- | --- | | encodeSafeMultiSend(inputs, options?) | Encode transactions into MultiSend format. Accepts raw { to, data, value } or prepare*() results. | | encodeSafeMultiSendCallOnly(inputs) | Same but uses the "call only" MultiSend (no DELEGATECALL in sub-txs). | | SAFE_MULTISEND_ADDRESS | Canonical MultiSend address (0x3886…) — same on all EVM chains. | | SAFE_MULTISEND_CALL_ONLY_ADDRESS | Canonical MultiSend "call only" address (0x9641…). |

Optimization: When the batch contains a single transaction, encodeSafeMultiSend returns it directly without a MultiSend wrapper.

Important: Always pass account: safeAddress (not the EOA signer) to prepare*() helpers. The contracts track positions and debt under msg.sender, which will be the Safe address.


Error decoding

Automatically decode custom Solidity errors from any Varla contract.

import { decodeVarlaError, formatVarlaError } from "@varla/sdk/errors";

try {
  await publicClient.simulateContract({ /* ... */ });
} catch (err) {
  const decoded = decodeVarlaError(err);
  if (decoded) {
    console.log(formatVarlaError(decoded));
    // → "VarlaCore.InsufficientCollateral(required=1000, actual=500)"
    console.log(decoded.contract, decoded.name, decoded.args, decoded.selector);
  }
}

Formatting utilities

import { formatWad, formatBps, formatPriceE8, parseUnits, parseWad } from "@varla/sdk/format";

formatWad(1_500_000_000_000_000_000n);           // "1.5"
formatWad(1_500_000_000_000_000_000n, { fractionDigits: 4 }); // "1.5"
formatBps(1500);                                  // "15%"
formatBps(1234);                                  // "12.34%"
formatPriceE8(50_000_000n);                       // "0.5"

parseUnits("1.5", 18);   // 1_500_000_000_000_000_000n
parseUnits("100", 6);    // 100_000_000n
parseWad("2.5");          // 2_500_000_000_000_000_000n

Multicall batching

Chunk large read batches to stay within RPC provider limits.

import { multicallChunks } from "@varla/sdk/batch";

const results = await multicallChunks({
  client,
  contracts: [
    { address: poolAddr, abi: poolAbi, functionName: "totalAssets" },
    { address: poolAddr, abi: poolAbi, functionName: "totalSupply" },
    // ... hundreds of calls
  ],
  chunkSize: 64, // calls per multicall (default: 128)
});

Events & indexing

import {
  getEventLogsChunked,
  syncOracleRegistryViaEvents,
  getRecentEvents,
  ORACLE_EVENTS,
  CORE_EVENTS,
  POOL_EVENTS,
} from "@varla/sdk/events";

// Fetch oracle events in manageable chunks
const logs = await getEventLogsChunked({
  client,
  address: c.oracle.address,
  events: ORACLE_EVENTS,
  fromBlock: 50_000_000n,
  toBlock: "latest",
  chunkSize: 10_000,
});

// Reconstruct oracle registry from events (no paginated RPC needed)
const registry = await syncOracleRegistryViaEvents({
  client,
  oracleAddress: c.oracle.address,
  fromBlock: 50_000_000n,
});

Chain metadata

import { getChainMeta } from "@varla/sdk";

const meta = getChainMeta("polygon");
// → { chain: "polygon", platform: "polymarket", collateral: { kind: "PUSD", symbol: "pUSD", decimals: 6 } }

const bscMeta = getChainMeta("bsc");
// → { chain: "bsc", platform: "opinion", collateral: { kind: "USDT", symbol: "USDT", decimals: 18 } }

Testing

cd packages/sdk

bun run test:quick    # build + run all tests
bun run test:types    # typecheck only (no runtime)
bun run test:rpc      # RPC integration tests (needs POLYGON_RPC_URL, BSC_RPC_URL)
bun run test:rpc:slow # slow RPC tests (opt-in via SDK_SLOW_RPC_TESTS=1)

Contributing

This package lives in the varla-protocol monorepo under packages/sdk/.

bun install                           # install from repo root
cd packages/sdk && bun run prepack    # regenerate artifacts + build
cd packages/sdk && bun run test:quick # verify

See AGENTS.md for the complete auto-generated API reference.

License

MIT