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

@uniformlabs/multiliquid-evm-sdk

v0.1.1

Published

TypeScript SDK for the Multiliquid protocol, built on viem.

Readme

@uniformlabs/multiliquid-evm-sdk

TypeScript SDK for the Multiliquid protocol, built on viem.

Typed, composable access to swap execution, quote previews with simulation, asset/price reads, stablecoin delegate queries, yield operations, and event parsing against the production Multiliquid contracts.

Installation

npm install @uniformlabs/multiliquid-evm-sdk viem

Quick Start

import { createMultiliquidClient, mainnet } from "@uniformlabs/multiliquid-evm-sdk";
import { createPublicClient, createWalletClient, http } from "viem";
import { mainnet as viemMainnet } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";

const account = privateKeyToAccount("0x...");
const publicClient = createPublicClient({ chain: viemMainnet, transport: http() });
const walletClient = createWalletClient({ chain: viemMainnet, transport: http(), account });

const ml = createMultiliquidClient({
  deployment: mainnet,
  publicClient,
  walletClient,
});

// Get a quote
const quote = await ml.quote.swapIntoRWA({
  rwaID: mainnet.assetIds.rwa.ULTRA,
  stablecoinID: mainnet.assetIds.stablecoin.USDC,
  stablecoinAmount: 1_000_000n, // 1 USDC (6 decimals)
  user: account.address,
  simulate: true, // optional: verify the swap would succeed on-chain
});

console.log("RWA output:", quote.rwaAmount);
console.log("Simulation passed:", quote.simulation?.success);

// Execute the swap
const txHash = await ml.swap.swapIntoRWA({
  rwaID: mainnet.assetIds.rwa.ULTRA,
  stablecoinID: mainnet.assetIds.stablecoin.USDC,
  stablecoinAmount: 1_000_000n,
  minRwaAmount: quote.rwaAmount, // use quote output as slippage bound
});

Client Setup

createMultiliquidClient returns a client with seven modules. Pass a publicClient for read-only access, or add a walletClient to enable swap execution and yield accrual.

import { createMultiliquidClient, mainnet } from "@uniformlabs/multiliquid-evm-sdk";

// Read-only (quotes, asset reads, events)
const ml = createMultiliquidClient({
  deployment: mainnet,
  publicClient,
});

// Read + write (adds swap execution, yield accrual)
const ml = createMultiliquidClient({
  deployment: mainnet,
  publicClient,
  walletClient,
});

The SDK ships with mainnet and sepolia chain presets. You can provide a custom ChainDeployment for any EVM chain — see Custom Chains.

Modules

Quotes

Get price quotes for all swap types. Every quote method accepts an optional simulate: true flag that runs an eth_call simulation to verify the swap would succeed (catches blacklist, pause, whitelist, balance, and allowance issues).

// Stablecoin → RWA (exact input)
const quote = await ml.quote.swapIntoRWA({
  rwaID: mainnet.assetIds.rwa.ULTRA,
  stablecoinID: mainnet.assetIds.stablecoin.USDC,
  stablecoinAmount: 1_000_000n,
  user: account.address,
  simulate: true,
});
// quote.rwaAmount, quote.protocolFee, quote.redemptionFee, quote.simulation

// Stablecoin → RWA (exact output)
await ml.quote.swapIntoRWAExactOut({
  rwaID,
  stablecoinID,
  rwaAmount: 500_000n,
  user: account.address,
});

// RWA → Stablecoin (exact input / exact output)
await ml.quote.swapIntoStablecoin({ stablecoinID, rwaID, rwaAmount: 1_000_000n });
await ml.quote.swapIntoStablecoinExactOut({
  stablecoinID,
  rwaID,
  stablecoinAmount: 1_000_000n,
  user,
});

// RWA → RWA (through a stablecoin issuer)
await ml.quote.swapRWAToRWA({
  stablecoinID,
  rwaInID,
  rwaOutID,
  exactOut: false,
  rwaInAmount: 1_000_000n,
  rwaOutAmount: 0n,
});

// Stablecoin → Stablecoin
await ml.quote.swapStablecoinToStablecoin({
  stablecoinInID,
  stablecoinOutID,
  exactOut: false,
  useDelegateForStablecoinOut: true,
  stablecoinInAmount: 1_000_000n,
  stablecoinOutAmount: 0n,
  user,
});

Simulation results are returned on the quote object:

if (quote.simulation?.success) {
  console.log("Gas estimate:", quote.simulation.gasEstimate);
} else if (quote.simulation) {
  console.log("Would revert:", quote.simulation.error);
}

Swaps

Execute swap transactions. Requires a walletClient. All methods return a transaction hash — the SDK does not wait for receipts.

const txHash = await ml.swap.swapIntoRWA({
  rwaID: mainnet.assetIds.rwa.ULTRA,
  stablecoinID: mainnet.assetIds.stablecoin.USDC,
  stablecoinAmount: 1_000_000n,
  minRwaAmount: 950_000n, // slippage protection
});

Available: swapIntoRWA, swapIntoRWAExactOut, swapIntoStablecoin, swapIntoStablecoinExactOut, swapRWAToRWA, swapStablecoinToStablecoin.

Build Calldata Without Sending

For smart account batching, meta-transactions, or custom submission flows:

const calldata = ml.swap.buildSwapCalldata("swapIntoRWA", {
  rwaID: mainnet.assetIds.rwa.ULTRA,
  stablecoinID: mainnet.assetIds.stablecoin.USDC,
  stablecoinAmount: 1_000_000n,
  minRwaAmount: 950_000n,
});

await walletClient.sendTransaction({
  to: mainnet.addresses.multiliquidSwap,
  data: calldata,
});

Assets

Read-only queries for asset information, prices, fees, and access control. Bulk reads are batched via Multicall3.

// Individual reads
const rwaInfo = await ml.assets.getRWAInfo(mainnet.assetIds.rwa.ULTRA);
const stablecoinInfo = await ml.assets.getStablecoinInfo(mainnet.assetIds.stablecoin.USDC);
const price = await ml.assets.getRWAPrice(mainnet.assetIds.rwa.ULTRA);

// Bulk reads (multicalled — single RPC round-trip)
const allRWAs = await ml.assets.getAllRWAInfo(); // all known RWAs
const allStables = await ml.assets.getAllStablecoinInfo(); // all known stablecoins
const allPrices = await ml.assets.getAllPrices(); // all RWA + stablecoin prices

// Fee configuration
const feeTiers = await ml.assets.getFeeTiers();
const discount = await ml.assets.getDiscountRate(stablecoinID, rwaID);
const redemptionFee = await ml.assets.getRedemptionFee(stablecoinID, rwaID);

// Access control
const paused = await ml.assets.isPaused();
const blacklisted = await ml.assets.isBlacklisted(userAddress);
const whitelisted = await ml.assets.isWhitelistedForRWA(rwaID, from, to, amount);

Delegates

Read from stablecoin delegate contracts. The SDK resolves delegate addresses automatically from the stablecoin ID.

const info = await ml.delegates.getDelegateInfo(mainnet.assetIds.stablecoin.USDC);
// info.type → "balanceSheet" | "yield"
// info.rwaCustodyAddress, info.stablecoinCustodyAddress
// info.rwaColdStorageAddresses, info.stablecoinColdStorageAddresses

const isRwaOk = await ml.delegates.isRWAWhitelisted(stablecoinID, rwaTokenAddress);
const isStableOk = await ml.delegates.isStablecoinWhitelisted(
  stablecoinID,
  backingStablecoinAddress,
);

Yield

Operations for yield-bearing stablecoin delegates (e.g., TSY_YIELD). Throws NotYieldBearingError if called on a non-yield-bearing stablecoin.

const tsyYield = mainnet.assetIds.stablecoin.TSY_YIELD;

// Read yield data
const currentDay = await ml.yield.getCurrentDay(tsyYield);
const rate = await ml.yield.getDailyRate(tsyYield, currentDay - 1n);
const rates = await ml.yield.getDailyRates(tsyYield, 1n, currentDay); // multicalled

// Yield-adjusted amounts
const yieldAmount = await ml.yield.getYieldAmount({
  stablecoinID: tsyYield,
  user: "0x...",
  redeemValue: 1_000_000_000_000_000_000n, // 1 WAD
  stablecoinWithdrawal: true,
});

// Accrue interest (requires walletClient)
await ml.yield.accrueInterest(tsyYield, userAddress);
await ml.yield.batchAccrueInterest(tsyYield, [user1, user2, user3]);
await ml.yield.accrueInterestPaged(tsyYield, userAddress, 365n); // max 365 days per tx

Events

Parse and subscribe to Multiliquid swap events. Events are returned as a discriminated union with six types: SwapIntoRWA, SwapIntoRWAExactOut, SwapIntoStablecoin, SwapIntoStablecoinExactOut, RWAToRWASwap, StablecoinToStablecoinSwap.

// Fetch historical events
const events = await ml.events.getEvents({
  fromBlock: 19_000_000n,
  toBlock: "latest",
  user: "0x...", // optional: filter by user
  rwaID: mainnet.assetIds.rwa.ULTRA, // optional: filter by RWA
  eventTypes: ["SwapIntoRWA"], // optional: filter by type
});

for (const event of events) {
  console.log(event.type, event.rwaAmount, event.stablecoinAmount);
}

// Watch for new events in real-time
const unsubscribe = ml.events.watchEvents({ user: "0x..." }, (event) =>
  console.log("New event:", event.type),
);

// Stop watching
unsubscribe();

// Parse a raw viem log
const parsed = ml.events.parseLog(rawLog);

Multicall

Compose arbitrary read calls into a single RPC round-trip. Fully typed — return types are inferred from the ABI and function name.

import { multiliquidSwapAbi, priceAdapterAbi } from "@uniformlabs/multiliquid-evm-sdk";

const [rwaInfo, price] = await ml.multicall.read([
  {
    address: mainnet.addresses.multiliquidSwap,
    abi: multiliquidSwapAbi,
    functionName: "rwaInfo",
    args: [mainnet.assetIds.rwa.ULTRA],
  },
  {
    address: "0x21A20895ec8262b749eB16dE62BBE6fe23345187",
    abi: priceAdapterAbi,
    functionName: "getPrice",
  },
]);

Error Handling

All contract interactions are wrapped in typed error decoding. Revert reasons are parsed into specific error subclasses with structured fields.

import {
  MultiliquidError,
  MultiliquidContractError,
  UserBlacklistedError,
  UserNotWhitelistedError,
  ContractPausedError,
  InsufficientRWAOutputError,
} from "@uniformlabs/multiliquid-evm-sdk";

try {
  await ml.swap.swapIntoRWA({ ... });
} catch (error) {
  if (error instanceof UserBlacklistedError) {
    console.log("Blacklisted user:", error.args.user);
  } else if (error instanceof UserNotWhitelistedError) {
    console.log("Not whitelisted:", error.args.rwaID, error.args.user);
  } else if (error instanceof ContractPausedError) {
    console.log("Contract is paused");
  } else if (error instanceof InsufficientRWAOutputError) {
    console.log("Slippage exceeded");
  } else if (error instanceof MultiliquidContractError) {
    console.log("Contract error:", error.errorName, error.args);
  } else if (error instanceof MultiliquidError) {
    console.log("SDK error:", error.message);
  }
}

A standalone decodeMultiliquidError(data) function is also exported for decoding raw revert data outside the SDK's call wrappers.

Error Classes

| Class | Solidity Error | Structured Args | | ----------------------------------- | ------------------------------ | ------------------------------------------------- | | RWANotAcceptedError | RWANotAccepted | — | | StablecoinNotAcceptedError | StablecoinNotAccepted | — | | InsufficientRWAOutputError | InsufficientRWAOutput | — | | InsufficientStablecoinOutputError | InsufficientStablecoinOutput | — | | InsufficientRWAInputError | InsufficientRWAInput | — | | InsufficientStablecoinInputError | InsufficientStablecoinInput | — | | UserBlacklistedError | UserBlacklisted | { user } | | UserNotWhitelistedError | UserNotWhitelisted | { rwaID, user } | | StablecoinPriceOutsideBandError | StablecoinPriceOutsideBand | { stablecoinID, adminValue, oraclePrice, band } | | PriceAdapterNotSetError | PriceAdapterNotSet | { tokenID } | | ContractPausedError | EnforcedPause | — | | NotYieldBearingError | (SDK-level) | — | | NoWalletClientError | (SDK-level) | — |

Custom Chains

Provide your own ChainDeployment for any EVM chain where Multiliquid is deployed:

import { createMultiliquidClient } from "@uniformlabs/multiliquid-evm-sdk";
import type { ChainDeployment } from "@uniformlabs/multiliquid-evm-sdk";

const myChain: ChainDeployment = {
  chainId: 42161,
  addresses: {
    multiliquidSwap: "0x...",
    // Optional: preset delegate/adapter/token addresses to avoid on-chain lookups
    delegates: { "0xstablecoinId...": "0xdelegateAddr..." },
    priceAdapters: { "0xrwaId...": "0xadapterAddr..." },
    tokens: { "0xassetId...": "0xtokenAddr..." },
    multicall3: "0x...", // defaults to canonical 0xcA11bde05977b3631167028862bE2a173976CA11
  },
  assetIds: {
    rwa: { MY_RWA: "0x..." },
    stablecoin: { MY_STABLE: "0x..." },
  },
};

const ml = createMultiliquidClient({ deployment: myChain, publicClient });

Constants

import { WAD, ROLES } from "@uniformlabs/multiliquid-evm-sdk";

WAD; // 1_000_000_000_000_000_000n (1e18)
ROLES.DEFAULT_ADMIN; // bytes32(0)
ROLES.OPERATOR; // keccak256("OPERATOR_ROLE")
ROLES.BLACKLISTER; // keccak256("BLACKLISTER_ROLE")

ABIs

All contract ABIs are exported as const assertions for direct use with viem:

import {
  multiliquidSwapAbi,
  stablecoinDelegateBaseAbi,
  treasuryDelegateAbi,
  balanceSheetDelegateAbi,
  priceAdapterAbi,
  yieldBearingDelegateAbi,
  whitelistAdapterAbi,
  erc20Abi,
} from "@uniformlabs/multiliquid-evm-sdk";

Supported Assets

Mainnet

RWAs: ULTRA, JTRSY, WTGXX, BENJI, USTB, VBILL

Stablecoins: USDC, TSY_YIELD

Access IDs via mainnet.assetIds.rwa.ULTRA, mainnet.assetIds.stablecoin.USDC, etc.

Sepolia Testnet

RWAs: MOCK_RWA_WHITELIST, MOCK_RWA_NO_WHITELIST

Stablecoins: USDC, TSY_YIELD

Access IDs via sepolia.assetIds.rwa.MOCK_RWA_WHITELIST, etc.

Requirements

  • Node.js >= 18
  • viem >= 2.x
  • TypeScript >= 5.x (recommended)

License

ISC