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

@swapper-finance/deposit-sdk

v0.4.0

Published

Easy iframe embedding for Swapper deposit widget with full TypeScript support

Readme

@swapper-finance/deposit-sdk

Easy iframe embedding SDK for the Swapper Finance deposit widget with full TypeScript support.

The SDK provides two integration options: iframe embedding for seamless integration into your application, and modal popup for a focused user experience. Both options support full customization and TypeScript types.

Installation

npm install @swapper-finance/deposit-sdk

Quick Start

Option 1: Embed in Container

import { SwapperIframe } from '@swapper-finance/deposit-sdk';

// Create and mount the iframe
const swapper = new SwapperIframe({
  container: '#swapper-container', // or pass an HTMLElement
  integratorId: 'your-integrator-id',
  dstChainId: '8453',
  dstTokenAddr: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
  depositWalletAddress: '0x2A018F2506acaEEE2C10632514Fc5DCa9eE2c28A',
});

Option 2: Open in Modal Popup

import { openSwapperModal } from '@swapper-finance/deposit-sdk';

// Open in a centered modal with backdrop
const modal = openSwapperModal({
  integratorId: 'your-integrator-id',
  dstChainId: '8453',
  dstTokenAddr: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
  depositWalletAddress: '0x2A018F2506acaEEE2C10632514Fc5DCa9eE2c28A',
});

// Close programmatically if needed
// modal.close();

Configuration Options

Required Parameters

  • integratorId - Your integrator identifier
  • dstChainId - Destination chain ID
  • dstTokenAddr - Destination token address
  • depositWalletAddress - Wallet address for deposits

Optional Parameters

Styling

The SDK supports a two-tier styling system:

const swapper = new SwapperIframe({
  // ... required params
  styles: {
    // Level 1: Theme mode (light or dark)
    themeMode: 'dark',

    // Level 2: Component-specific overrides (highest priority)
    componentStyles: {
      primaryColor: '#FF6B35',
      backgroundColor: '#1A1A1A',
      textColor: '#E8E8E8',
      borderRadius: '12px',
      width: '100%',
      primaryButtonTextColor: '#FFFFFF',
      // ... more style options
    },
  },
});

Custom Contract Calls

import { ContractCallType } from '@swapper-finance/deposit-sdk';

const swapper = new SwapperIframe({
  // ... required params
  customContractCalls: [
    {
      callType: ContractCallType.CALL,
      target: '0x...',
      value: '0',
      callData: '0x...',
      payload: '0x...'
    },
  ],
});

Custom Data

Integrator-scoped data forwarded verbatim to the widget. Use it for values only your app can supply — for example a balance the user holds on an external venue, which the widget has no way to read itself.

The widget interprets these keys per integrator: a partner with a custom home screen reads the keys it knows about and ignores everything else. Unknown or malformed values are dropped without breaking the deposit flow, so you can start sending a field before widget support ships. Values must be JSON-serializable.

const swapper = new SwapperIframe({
  // ... required params
  customData: {
    dex: 'hyperliquid',
    dexBalanceUsd: 1234.56,
    instructionsUrl: 'https://docs.example.com/deposits',
  },
});

Keep it fresh with updateCustomData() — typically on the transaction_success event, once a deposit has changed the value you are displaying.

Connecting Your Wallet (wallet)

Pass a wallet and the widget skips its own connect step and uses yours. Two modes:

Signer mode — hand over an ethers v5/v6 Signer or a viem WalletClient; the SDK normalizes it and reads the address, chain and wallet name itself:

const swapper = new SwapperIframe({
  // ... required params
  wallet: { signer },
});

// Keep it in sync as the user switches account or chain (pass null to disconnect):
swapper.updateSigner(nextSigner);

Handler mode — bridge the requests yourself:

const swapper = new SwapperIframe({
  // ... required params
  wallet: {
    onTransactionRequest: (tx) => wallet.sendTransaction(tx),      // → { hash }
    onChainSwitchRequest: (chainId) => wallet.switchChain(chainId),
    // Optional, see below
    onSignRequest: async (request) =>
      request.method === 'eth_signTypedData_v4'
        ? wallet.signTypedData(request.typedData)
        : wallet.signMessage(request.message, request.isHex),
    autoConnect: { address, chainId },
  },
});
Signing (onSignRequest)

Most flows only need transactions. Deposit from Polymarket and Deposit from Perps are different: they authorize with a signature — an EIP-712 payload or a personal_sign — and never send a transaction from the user's wallet.

In signer mode this is automatic: whatever your signer implements (signMessage / _signTypedData / signTypedData) is bridged, and the widget is told on connect what it can rely on. In handler mode it's opt-in via onSignRequest.

If the wallet can't sign, nothing breaks — those two flows just fall back to asking the user to connect a wallet inside the widget, exactly as they did before this bridge existed. Either way the widget's top-right wallet button offers Switch Wallet, so the user can always step off the wallet you passed and pick their own.

Two details worth knowing when implementing onSignRequest by hand:

  • request.isHex marks a raw 32-byte digest (e.g. a Gnosis Safe transaction hash). Sign it as bytes — signing the text "0x…" recovers a different address and the Safe rejects it. With ethers, signMessage(getBytes(message)); with viem, signMessage({ message: { raw: message } }).
  • request.typedData is the full eth_signTypedData_v4 payload, so its types include EIP712Domain. viem takes it as-is; ethers derives that entry itself and throws if it's also passed, so strip it first.
Narrowing what we advertise (capabilities)

What the widget is told on connect is inferred, and the inference errs on the generous side in both modes:

  • signer mode — we check which methods your signer implements. On any ethers or viem signer both are always present (prototype methods, injected actions), so both capabilities are advertised regardless of what the wallet behind the transport would accept.
  • handler mode — supplying onSignRequest advertises both, since we can't tell what your handler does with each request.method.

If that's wrong for your wallet — a hardware or embedded wallet that refuses EIP-712, or a handler that only implements personal_sign — say so, and the widget keeps the flows needing the missing piece on its own connect-wallet step instead of offering one it can't finish:

wallet: {
  signer,                                              // or the handler trio
  capabilities: { signMessage: true, signTypedData: false },
}

It only ever narrows: declaring a capability your signer doesn't implement won't make it available.

API Reference

Constructor

new SwapperIframe(options: SwapperIframeOptions)

Methods

mount(container: HTMLElement | string): void

Mounts the iframe to a container element.

swapper.mount('#my-container');
// or
swapper.mount(document.getElementById('my-container'));

updateConfig(config: Partial<SwapperConfig>): void

Updates the configuration dynamically via postMessage.

swapper.updateConfig({
  depositWalletAddress: '0xNewAddress...',
  dstChainId: '1',
});

updateStyles(styles: SwapperStyles): void

Updates only the styles.

swapper.updateStyles({
  themeMode: 'light',
  brandTheme: {
    primaryColor: '#836FFF',
  },
});

updateCustomContractCalls(calls: ContractCall[]): void

Updates custom contract calls.

swapper.updateCustomContractCalls([
  {
    callType: ContractCallType.CALL,
    target: '0x...',
    value: '0',
    callData: '0x...',
    payload: '0x...',
  },
]);

updateCustomData(customData: Record<string, unknown>): void

Merges into the integrator-scoped customData payload — pass only the keys that changed, the rest are preserved. Set a key to undefined to drop it. Also available on SwapperModal, where it is safe to call before the modal is built.

swapper.on('transaction_success', async () => {
  const balanceUsd = await fetchVenueBalance();
  swapper.updateCustomData({ dexBalanceUsd: balanceUsd });
});

getConfig(): SwapperConfig

Returns the current configuration.

const config = swapper.getConfig();
console.log(config.depositWalletAddress);

destroy(): void

Removes the iframe and cleans up event listeners.

swapper.destroy();

Widget Events

The widget emits structured events via postMessage that you can listen to through the SDK. This lets you react to user actions like completed transactions.

Event Envelope

Every event posted from the iframe follows this shape:

interface SwapperWidgetEvent {
  type: "SWAPPER_EVENT";       // discriminator
  version: "1.0";              // protocol version
  name: WidgetEventName;       // e.g. "transaction_success"
  timestamp: string;           // ISO 8601
  payload: WidgetEventPayload; // event-specific data
}

Listening with onEvent (recommended)

The simplest way to listen for widget events is to pass an onEvent callback in the config. It receives every event (equivalent to .on("*", ...)):

import { SwapperIframe, WidgetEventName } from '@swapper-finance/deposit-sdk';
import type { TransactionSuccessPayload } from '@swapper-finance/deposit-sdk';

const swapper = new SwapperIframe({
  container: '#swapper-container',
  integratorId: 'your-integrator-id',
  dstChainId: '8453',
  dstTokenAddr: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
  depositWalletAddress: '0x2A018F2506acaEEE2C10632514Fc5DCa9eE2c28A',
  onEvent: (event) => {
    if (event.type === WidgetEventName.TRANSACTION_SUCCESS) {
      const payload = event.data as TransactionSuccessPayload;
      console.log('Transaction succeeded:', payload.txHash);
    }
  },
});

This also works with openSwapperModal:

import { openSwapperModal, WidgetEventName } from '@swapper-finance/deposit-sdk';

openSwapperModal({
  integratorId: 'your-integrator-id',
  dstChainId: '8453',
  dstTokenAddr: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
  depositWalletAddress: '0x2A018F2506acaEEE2C10632514Fc5DCa9eE2c28A',
  onEvent: (event) => {
    console.log('Widget event:', event.type, event.data);
  },
});

Listening with on() / off()

For more granular control, you can register handlers on the iframe instance directly:

import { SwapperIframe, WidgetEventName } from '@swapper-finance/deposit-sdk';
import type { TransactionSuccessPayload } from '@swapper-finance/deposit-sdk';

const swapper = new SwapperIframe({
  container: '#swapper-container',
  integratorId: 'your-integrator-id',
  dstChainId: '8453',
  dstTokenAddr: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
  depositWalletAddress: '0x2A018F2506acaEEE2C10632514Fc5DCa9eE2c28A',
});

// Listen for a specific event
swapper.on(WidgetEventName.TRANSACTION_SUCCESS, (event) => {
  const payload = event.data as TransactionSuccessPayload;
  console.log('Transaction succeeded:', payload.txHash);
  console.log('Deposit option:', payload.depositOption); // "walletDeposit" | "transferCrypto" | "depositWithCash"
});

// Listen for all events with wildcard
swapper.on('*', (event) => {
  console.log('Widget event:', event.type, event.data);
});

// Remove a specific handler
const handler = (event) => { /* ... */ };
swapper.on(WidgetEventName.TRANSACTION_SUCCESS, handler);
swapper.off(WidgetEventName.TRANSACTION_SUCCESS, handler);

Listening with window.addEventListener

You can also listen for events directly on the window. Each event is a MessageEvent containing a SwapperWidgetEvent envelope:

import { WIDGET_EVENT_PROTOCOL_VERSION, WidgetEventName } from '@swapper-finance/deposit-sdk';
import type { SwapperWidgetEvent, TransactionSuccessPayload } from '@swapper-finance/deposit-sdk';

window.addEventListener('message', (event: MessageEvent) => {
  if (event.data?.type !== 'SWAPPER_EVENT') return;

  const message = event.data as SwapperWidgetEvent;

  if (message.version !== WIDGET_EVENT_PROTOCOL_VERSION) return;

  if (message.name === WidgetEventName.TRANSACTION_SUCCESS) {
    const payload = message.payload as TransactionSuccessPayload;
    console.log('Tx hash:', payload.txHash);
    console.log('Explorer:', payload.explorerUrl);
  }
});

TransactionSuccessPayload

| Field | Type | Required | Description | |------------------|-------------------|----------|---------------------------------------| | depositOption | DepositFlowType | Yes | "walletDeposit", "transferCrypto", or "depositWithCash" | | txHash | string | No | Transaction hash | | explorerUrl | string | No | Link to block explorer | | tokenSymbol | string | No | Token symbol (e.g. "USDC") | | tokenAddress | string | No | Token contract address | | chainId | string | No | Chain ID of the transaction | | amountReceived | string | No | Amount received |

Flexible Widget Height (Auto-Resize)

The widget's home page can show a variable number of deposit options. With a fixed height this can leave an empty gap below the options. In flexible height mode the home page auto-scales to its content instead; all other pages keep the standard fixed height (560px).

SwapperModal / openSwapperModal enable this automatically — nothing to configure. The modal container smoothly animates to the widget's requested height (clamped to 90% of the viewport):

import { openSwapperModal } from '@swapper-finance/deposit-sdk';

openSwapperModal({
  integratorId: 'your-id',
  dstChainId: '8453',
  dstTokenAddr: '0x...',
  depositWalletAddress: '0x...',
  // Home page auto-sizes; other pages stay at the fixed height.
});

Notes:

  • Users with prefers-reduced-motion get an instant snap instead of the animation.
  • If the deployed widget doesn't support flexible height yet, it simply keeps the fixed height — safe to use ahead of the widget deploy.

Modal API Reference

Opening a Modal

Quick Function: openSwapperModal(options)

import { openSwapperModal } from '@swapper-finance/deposit-sdk';

const modal = openSwapperModal({
  // Required configuration
  integratorId: 'your-id',
  dstChainId: '8453',
  dstTokenAddr: '0x...',
  depositWalletAddress: '0x...',
  
  // Optional: Widget styling
  styles: {
    themeMode: 'light',
  },
  
  // Optional: Modal styling
  modalStyle: {
    overlayColor: 'rgba(0, 0, 0, 0.8)',
    borderRadius: '16px'
  },
  
  // Optional: Callback when closed
  onClose: () => {
    console.log('Modal was closed');
  },
});

// Close programmatically
modal.close();

Modal Options

modalStyle Configuration

interface ModalStyle {
  /**
   * Modal width (default: '450px')
   */
  width?: string;
  
  /**
   * Modal height (default: '560px')
   */
  height?: string;
  
  /**
   * Background overlay color (default: 'rgba(0, 0, 0, 0.7)')
   */
  overlayColor?: string;
  
  /**
   * Modal border radius (default: '16px')
   */
  borderRadius?: string;
  
  /**
   * Z-index for modal (default: 10000)
   */
  zIndex?: number;
  
  /**
   * Show close button (default: false)
   * Set to true to show a close button
   */
  showCloseButton?: boolean;
}

Modal Class Methods

SwapperModal Class

For more control, use the SwapperModal class directly:

import { SwapperModal } from '@swapper-finance/deposit-sdk';

// Create modal instance
const modal = new SwapperModal({
  integratorId: 'your-id',
  dstChainId: '8453',
  dstTokenAddr: '0x...',
  depositWalletAddress: '0x...',
  modalStyle: {
    showCloseButton: true, // Optional: show close button
  },
  onClose: () => {
    console.log('Modal closed');
  },
});

// Open modal
modal.open();

// Close modal — hides the modal (the widget stays loaded for an instant
// reopen) and tells the widget to return to its home screen, so the next
// open() starts fresh instead of resuming on the previously selected option
modal.close();

// Check if open
if (modal.isModalOpen()) {
  console.log('Modal is open');
}

// Get iframe instance
const iframe = modal.getIframe();

// Destroy completely
modal.destroy();

Preloading the Modal

Build the modal hidden and load the widget in the background so the first open() is instant:

import { preloadSwapperModal } from '@swapper-finance/deposit-sdk';

// e.g. when your page mounts
const modal = preloadSwapperModal({
  integratorId: 'your-id',
  dstChainId: '8453',
  dstTokenAddr: '0x...',
  depositWalletAddress: '0x...',
});

// later, on user action — opens instantly
button.addEventListener('click', () => modal.open());

The equivalent with the class API is new SwapperModal(options) followed by modal.preload(), or in one step with the preload option:

const modal = new SwapperModal({
  // ...config
  preload: true, // build hidden + load the widget immediately
});

If something changed between preload and open (e.g. the deposit address), pass a config patch to open() — it is applied to the live widget right before it becomes visible:

modal.open({ depositWalletAddress: '0x...newAddress' });

The modal also exposes updateConfig(patch) and updateSigner(signer) directly (no need to go through getIframe()); both are safe to call before the modal has been built.

During preload the widget loads everything (bundle, chains, tokens, UI) but does not create a smart wallet authorization — otherwise every visitor would get one, including those who never open the widget. The authorization process starts on the first open(), which signals the widget that it is now visible.

If you embed SwapperIframe directly in a hidden/off-screen container, you can get the same behavior manually:

const iframe = new SwapperIframe({
  // ...config
  deferSmartWalletAuth: true,
});

// when your UI actually reveals the widget:
iframe.notifyWidgetOpened();

Preloading an Inline Embed

For widgets embedded in the page layout (not a modal), use SwapperEmbed. The iframe loads immediately on a hidden position: fixed host; calling showIn(target) moves the host over the target element's bounding box, so the already-loaded widget appears there instantly. (The iframe is never reparented in the DOM — that would force a reload.) The host mirrors the target's drawable box — bounding rect, corner radii, border widths and stacking position (computed from the target's outermost stacking-context ancestor, or the zIndex option if you pass one) — and follows it across scrolling, window resizes and target size changes.

import { preloadSwapperEmbed } from '@swapper-finance/deposit-sdk';

// e.g. when your page mounts — starts loading in the background
const embed = preloadSwapperEmbed({
  integratorId: 'your-id',
  dstChainId: '8453',
  dstTokenAddr: '0x...',
  depositWalletAddress: '0x...',
});

// later, on user action — the widget appears over the placeholder instantly
button.addEventListener('click', () => embed.showIn('#widget-placeholder'));

// hide (widget stays loaded and routes back to its home screen,
// so the next showIn() starts fresh) / full teardown
embed.hide();
embed.destroy();

The target element is a placeholder: size it to the box the widget should occupy (e.g. 450px × 560px). Like the modal preload, smart wallet authorization is deferred until the first showIn().

Pass flexibleHeight: true to let the widget's home page auto-scale: the embed animates the placeholder's height to the widget's requested value, so the surrounding page layout reflows with it.

Available Style Properties

ComponentStyles

interface ComponentStyles {
  // Layout
  width?: string;          // Widget width (default: 450px)
  border?: string;         // Border style (e.g. "1px solid #ccc")
  borderRadius?: string;   // Border radius (default: 24px)

  // Brand colors
  primaryColor?: string;   // Main brand color (buttons, links, primary accents)
  accentColor?: string;    // Secondary accent color for highlights
  sphereColor?: string;    // Color of the decorative sphere

  // Backgrounds & surfaces
  backgroundColor?: string;  // Widget background color
  surfaceColor?: string;     // Surface color (cards, inputs)
  surfaceAltColor?: string;  // Alternative surface color

  // Status colors
  successColor?: string;        // Success state color
  successAltColor600?: string;  // Alternative success color shade
  errorColor?: string;          // Error state color
  warningColor?: string;        // Warning state color

  // Typography
  textColor?: string;               // Main text color
  primaryButtonTextColor?: string;  // Text color on primary buttons
}

Examples

Modal Popup Usage

<!DOCTYPE html>
<html>
<head>
  <title>Swapper Modal</title>
</head>
<body>
  <button id="open-modal">Open Swapper</button>
  
  <script type="module">
    import { openSwapperModal } from '@swapper-finance/deposit-sdk';
    
    document.getElementById('open-modal').onclick = () => {
      openSwapperModal({
        integratorId: 'your-integrator-id',
        dstChainId: '8453',
        dstTokenAddr: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
        depositWalletAddress: '0x2A018F2506acaEEE2C10632514Fc5DCa9eE2c28A',
        modalStyle: {
          width: '600px',
          height: '800px',
        },
        onClose: () => {
          console.log('Modal closed');
        },
      });
    };
  </script>
</body>
</html>

React Example (Embedded)

import { useEffect, useRef } from 'react';
import { SwapperIframe } from '@swapper-finance/deposit-sdk';

function SwapperWidget() {
  const containerRef = useRef<HTMLDivElement>(null);
  const swapperRef = useRef<SwapperIframe | null>(null);

  useEffect(() => {
    if (containerRef.current && !swapperRef.current) {
      swapperRef.current = new SwapperIframe({
        container: containerRef.current,
        integratorId: 'your-integrator-id',
        dstChainId: '8453',
        dstTokenAddr: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
        depositWalletAddress: '0x2A018F2506acaEEE2C10632514Fc5DCa9eE2c28A',
      });
    }

    return () => {
      swapperRef.current?.destroy();
      swapperRef.current = null;
    };
  }, []);

  return <div ref={containerRef} />;
}