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

@sky-mavis/ronin-dex

v0.0.2

Published

Ronin DEX SDK

Readme

Ronin DEX SDK

Multi-chain swap SDK for integrating token swap flows across EVM and Solana networks.

Why this SDK

  • Aggregates routes across supported providers.
  • Supports same-chain and cross-chain swap flows.
  • Provides quote, build, approve/sign, submit, and receipt lifecycle handling.
  • Emits step-by-step execution events for UI status tracking.
  • Designed for app-level control over providers, accounts, and signer behavior.

Installation

npm install @sky-mavis/ronin-dex

This package relies on peer dependencies (for example ethers, @solana/web3.js, and @uniswap/sdk-core). Install peer dependencies required by your runtime.

Integration model

The SDK is split into two layers:

  • CoreDex: infrastructure/dependency wiring (apiUrl, provider manager, account resolver, signer resolver).
  • Dex: application-facing API (getDexTokens, getQuotes, handle, subscribe, currency helpers).

Typical app lifecycle:

  1. Initialize CoreDex once when wallet/provider context is ready.
  2. Create a Dex instance from that CoreDex.
  3. Resolve token metadata and create/select currencies.
  4. Request quotes with amount, direction, account, and slippage.
  5. Subscribe to quote execution events for UI states.
  6. Execute with dex.handle(...), optionally passing custom network fee settings.

Example-driven integration (same pattern as examples/web)

This section mirrors the web example architecture so teams can adopt it with minimal adaptation.

1) Provider manager (examples/web/src/adapters/provider-manager.ts)

import { JsonRpcProvider } from 'ethers'
import { Connection } from '@solana/web3.js'

const providerCache = new Map<number, JsonRpcProvider | Connection>()

function resolveRpcUrl(rpcUrl: string): string {
  return rpcUrl.startsWith('/') ? `${window.location.origin}${rpcUrl}` : rpcUrl
}

export function createProviderManager() {
  return {
    getProvider: (chainId: number) => {
      const cached = providerCache.get(chainId)
      if (cached) return cached

      const chain = SUPPORTED_CHAINS.find((c) => c.chainId === chainId)
      if (!chain) throw new Error(`Unsupported chain: ${chainId}`)

      const provider =
        chain.blockchain === 'solana'
          ? new Connection(resolveRpcUrl(chain.rpcUrl), 'confirmed')
          : new JsonRpcProvider(resolveRpcUrl(chain.rpcUrl), chainId)

      providerCache.set(chainId, provider)
      return provider
    },
    getChainConfigs: () => SUPPORTED_CHAINS,
  }
}

2) Wallet signers for EVM + Solana

import { BrowserProvider } from 'ethers'
import { WalletTypeEnum, BlockchainEnum } from '@sky-mavis/ronin-dex'
import { Connection, Transaction, VersionedTransaction } from '@solana/web3.js'

// EVM signer using a standard EIP-1193 provider (MetaMask, Ronin Wallet, etc.)
export function createSigner(eip1193Provider: any, address: string) {
  const browserProvider = new BrowserProvider(eip1193Provider)
  return {
    signMessage: async (message: string) =>
      (await browserProvider.getSigner(address)).signMessage(message),
    signTransaction: async (txRequest: any) =>
      (await browserProvider.getSigner(address)).signTransaction(txRequest),
    sendTransaction: async (txRequest: any) =>
      (await browserProvider.getSigner(address)).sendTransaction(txRequest),
    signTypedDataV4: async (message: string) => {
      const parsed = JSON.parse(message)
      return (await browserProvider.getSigner(address)).signTypedData(
        parsed.domain,
        parsed.types,
        parsed.message,
      )
    },
    getAccount: () => ({
      index: 0,
      address,
      type: WalletTypeEnum.OTHER,
      walletId: 'browser-wallet',
      blockchain: BlockchainEnum.EVM,
    }),
    connect: () => createSigner(eip1193Provider, address),
    getTag: () => 'browser-wallet',
  }
}

// Solana signer using a Ronin-style unified provider
function toBase64(tx: Transaction | VersionedTransaction): string {
  const bytes =
    tx instanceof Transaction
      ? tx.serialize({ requireAllSignatures: false, verifySignatures: false })
      : tx.serialize()
  return Buffer.from(bytes).toString('base64')
}

function parseSignature(payload: unknown): string {
  if (typeof payload === 'string') return payload
  if (payload && typeof payload === 'object') {
    const data = payload as Record<string, unknown>
    if (typeof data.signature === 'string') return data.signature
    if (typeof data.result === 'string') return data.result
  }
  throw new Error('Unable to resolve transaction signature from provider response')
}

export function createSolanaSigner(
  provider: any,
  address: string,
  connection?: Connection,
) {
  return {
    address,
    connection,
    connect: (nextConnection: Connection) =>
      createSolanaSigner(provider, address, nextConnection),
    getAccount: () => ({
      index: 0,
      address,
      type: WalletTypeEnum.OTHER,
      walletId: 'browser-wallet',
      blockchain: BlockchainEnum.SOLANA,
    }),
    getTag: () => 'browser-wallet-solana',
    signTypedDataV4: async () => {
      throw new Error('signTypedDataV4 is not supported for Solana')
    },
    signMessage: async (message: string) => {
      const encoded = Buffer.from(message, 'utf8').toString('base64')
      const response = await provider.request({
        method: 'sol_signMessage',
        params: [{ message: encoded }],
      })
      return parseSignature(response)
    },
    signTransaction: async (txRequest: Transaction | VersionedTransaction) => {
      const response = await provider.request({
        method: 'sol_signTransaction',
        params: [toBase64(txRequest)],
      })
      return parseSignature(response)
    },
    sendTransaction: async (txRequest: Transaction | VersionedTransaction) => {
      const response = await provider.request({
        method: 'sol_signAndSendTransaction',
        params: [toBase64(txRequest)],
      })
      return parseSignature(response)
    },
  }
}

3) DexProvider setup (examples/web/src/context/DexContext.tsx)

import { useEffect, useState } from 'react'
import { CoreDex, Dex } from '@sky-mavis/ronin-dex'
import { createProviderManager } from '../adapters/provider-manager'
import { createSigner, createAccountsGetter, isEvmAddress } from '../adapters/wallet-signer'
import { createSolanaSigner } from '../adapters/solana-signer'

const API_URL = import.meta.env.VITE_API_URL || '/proxy/v3/public'
const RESOLVED_API_URL =
  API_URL && API_URL.startsWith('/') ? `${window.location.origin}${API_URL}` : API_URL

// Inside your provider component, create CoreDex + Dex when wallet context is ready:
function useDexInstance(evmAddress, solanaAddress, provider, solanaProvider) {
  const [dex, setDex] = useState(null)

  useEffect(() => {
    if (!evmAddress || !provider) return

    const core = new CoreDex({
      debug: true,
      apiUrl: RESOLVED_API_URL,
      providerManager: createProviderManager(),
      getAccounts: createAccountsGetter(evmAddress, solanaAddress),
      getSignerForAddress: async (baseAddress) => {
        if (!isEvmAddress(baseAddress) && solanaAddress && solanaProvider) {
          return createSolanaSigner(solanaProvider, solanaAddress)
        }
        return createSigner(provider, evmAddress)
      },
    })

    setDex(new Dex(core))
  }, [evmAddress, solanaAddress, provider, solanaProvider])

  return dex
}

4) Quote fetch flow (examples/web/src/hooks/useQuotes.ts)

import { DEFAULT_SLIPPAGE } from '@sky-mavis/ronin-dex'
import { parseUnits } from 'ethers'

const currencyIn = dex.createDexCurrency({
  chainId: tokenIn.chainId,
  address: tokenIn.address,
  decimals: tokenIn.decimals,
  symbol: tokenIn.symbol,
  name: tokenIn.name,
  standard: tokenIn.isNative ? 'native' : 'erc20',
})

const currencyOut = dex.createDexCurrency({
  chainId: tokenOut.chainId,
  address: tokenOut.address,
  decimals: tokenOut.decimals,
  symbol: tokenOut.symbol,
  name: tokenOut.name,
  standard: tokenOut.isNative ? 'native' : 'erc20',
})

const result = await dex.getQuotes({
  currencyIn,
  currencyOut,
  account: <USER_ACCOUNT>, // 0x...
  amount: parseUnits(<USER_INPUT_AMOUNT>, tokenIn.decimals).toString(), // raw amount
  direction: 'exactIn',
  slippageToleranceBps: DEFAULT_SLIPPAGE, // 0.5%
})

const firstQuote = result[0]

5) Execute + track state (examples/web/src/hooks/useSwap.ts)

const unsubscribe = dex.subscribe(quote.quoteId, (event) => {
  switch (event.state) {
    case 'computing':
      setStatus('building')
      break
    case 'authorizing':
    case 'approving-token':
    case 'signing-permit':
      setStatus('approving')
      break
    case 'signing-transaction':
    case 'populating-transaction':
      setStatus('signing')
      break
    case 'transaction-submitted':
      setStatus('submitted')
      break
    case 'waiting-for-receipt':
      setStatus('waiting')
      break
    case 'success':
      setStatus('success')
      break
    case 'failed':
    case 'authorize-failed':
    case 'approve-token-failed':
    case 'sign-permit-failed':
      setStatus('failed')
      break
  }
})

try {
  await dex.handle(quote, { waitForReceipt: true })
} finally {
  unsubscribe()
}

6) Token search flow (examples/web/src/hooks/useTokens.ts)

const response = query.trim()
  ? await dex.getDexTokens(chainId, { query: query.trim() })
  : await dex.getDexTokens(chainId)

const tokenList = response.ids.map((id) => response.data[id])

Event-driven execution states

For product and support stakeholders, these are the states that are usually mapped into UX:

  • Preparation: computing, populating-transaction
  • Approval/authorization: authorizing, approving-token, signing-permit
  • Signing/submission: signing-transaction, transaction-submitted
  • Confirmation: waiting-for-receipt, success
  • Failure paths: failed, authorize-failed, approve-token-failed, sign-permit-failed

Subscribe by quoteId and always unsubscribe after completion or timeout.

Provider-specific approval behavior (important)

Approval/permit handling differs by route provider today:

  • KyberSwap: /build currently returns swap calldata only, so the SDK performs approval/permit handling internally before swap submission.
  • Relay / Li.fi: /build already returns the full execution sequence (for example approve + swap), so the SDK does not add an extra local approval step.

What this means for integrators:

  • Do not assume a fixed transaction count per quote; it can vary by provider and token allowance state.
  • Drive UX from SDK execution events (authorizing, approving-token, signing-permit, transaction-submitted, waiting-for-receipt) instead of hardcoding provider behavior.
  • If you call build(...) directly, execute transactions in the returned order as-is.

Operational guidance for production teams

  • Keep apiUrl explicit per environment (dev/staging/prod).
  • Set sensible quote/handle timeouts and surface clear retry messaging.
  • Persist quoteId, provider, selected route, and tx hash for support traceability.
  • Log SDK events through your app logger for observability and incident triage.
  • Validate account-chain compatibility before calling handle(...).
  • Use simulation/preflight checks in wallet UX when available.

Example web app

A reference integration exists in examples/web.

Run locally

# from repository root
npm install
cd examples/web
npm install
npm run dev

Environment

examples/web/.env uses:

  • VITE_PROXY_TARGET: upstream host for API and RPC proxying.
  • VITE_API_URL: API path consumed by CoreDex (/proxy/v3/public in dev).
  • VITE_SOLANA_RPC_URL: proxied Solana RPC path (/solana-rpc in dev).

examples/web/vite.config.ts configures proxy routes for /proxy and /solana-rpc.

License

ISC