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

@t402/wdk-multisig

v2.0.0-beta.1

Published

Multi-sig Safe smart accounts with Tether WDK for M-of-N threshold payments

Readme

@t402/wdk-multisig

Multi-sig Safe smart accounts with Tether WDK for M-of-N threshold payments.

Features

  • Multi-Sig Payments: Require M-of-N signatures for USDT0/USDC transfers
  • Gasless Execution: ERC-4337 Account Abstraction with paymaster sponsorship
  • Signature Collection: Async signature collection from multiple owners
  • Batch Payments: Execute multiple transfers in a single transaction
  • Safe Smart Accounts: Uses Gnosis Safe with 4337 module

Installation

npm install @t402/wdk-multisig
# or
pnpm add @t402/wdk-multisig

Peer Dependencies

npm install @tetherto/wdk @tetherto/wdk-wallet-evm

Quick Start

import { createMultiSigWdkGaslessClient } from '@t402/wdk-multisig'
import { createPublicClient, http } from 'viem'
import { arbitrum } from 'viem/chains'

const publicClient = createPublicClient({
  chain: arbitrum,
  transport: http(),
})

// Create a 2-of-3 multi-sig client
const client = await createMultiSigWdkGaslessClient({
  owners: [wdkAccount1, wdkAccount2, wdkAccount3],
  threshold: 2,
  publicClient,
  chainId: 42161,
  bundler: {
    bundlerUrl: 'https://api.pimlico.io/v2/arbitrum/rpc?apikey=YOUR_KEY',
    chainId: 42161,
  },
  paymaster: {
    address: '0x...',
    url: 'https://api.pimlico.io/v2/arbitrum/rpc?apikey=YOUR_KEY',
    type: 'sponsoring',
  },
})

// Execute payment with all signers available
const result = await client.payWithAllSigners(
  { to: '0xRecipient...', amount: 1000000n },
  [signer1, signer2], // Only need 2 for 2-of-3
)

const receipt = await result.wait()
console.log('Payment confirmed:', receipt.txHash)

API Reference

createMultiSigWdkGaslessClient(config)

Creates a multi-sig gasless payment client.

interface CreateMultiSigConfig {
  owners: WdkAccount[] // Array of WDK owner accounts
  threshold: number // Required signatures (M of N)
  publicClient: PublicClient // Viem public client
  chainId: number // Chain ID
  bundler: BundlerConfig // Bundler configuration
  paymaster?: PaymasterConfig // Optional paymaster
  saltNonce?: bigint // Salt for address generation
}

MultiSigWdkGaslessClient

Async Signature Collection Flow

For scenarios where signers are distributed:

// 1. Initiate payment (creates unsigned UserOperation)
const payment = await client.initiatePayment({
  to: '0xRecipient...',
  amount: 1000000n,
})

console.log(`Request ID: ${payment.requestId}`)
console.log(`Needs ${payment.threshold} signatures`)

// 2. Collect signatures from owners (can be async/distributed)
await payment.addSignature(0, signer1) // Owner at index 0
await payment.addSignature(1, signer2) // Owner at index 1

// 3. Submit when threshold is met
const result = await payment.submit()
const receipt = await result.wait()

initiatePayment(params): Promise<MultiSigPaymentResult>

Create a payment request for signature collection.

interface MultiSigPaymentResult {
  requestId: string // Unique request identifier
  sender: Address // Smart account address
  userOpHash: Hex // Hash for signing
  sponsored: boolean // Whether gas is sponsored
  threshold: number // Required signatures
  collectedCount: number // Current signature count
  isReady: boolean // Has enough signatures
  signatures: Map<Address, Hex> // Collected signatures
  addSignature(ownerIndex: number, signer: WDKSigner): Promise<void>
  submit(): Promise<MultiSigSubmitResult>
}

initiateBatchPayment(params): Promise<MultiSigPaymentResult>

Create a batch payment request.

const payment = await client.initiateBatchPayment({
  payments: [
    { to: '0xAlice...', amount: 500000n },
    { to: '0xBob...', amount: 300000n },
  ],
})

payWithAllSigners(params, signers): Promise<MultiSigSubmitResult>

Execute payment when all signers are available locally.

const result = await client.payWithAllSigners(
  { to: '0x...', amount: 1000000n },
  [signer1, signer2], // Provide enough signers for threshold
)

payBatchWithAllSigners(params, signers): Promise<MultiSigSubmitResult>

Execute batch payment with all signers available.

getOwners(): Address[]

Get all owner addresses.

getThreshold(): number

Get the signature threshold.

getPendingRequests(): SignatureRequest[]

Get all pending signature requests.

getPendingOwners(requestId): Address[]

Get owners who haven't signed a specific request.

getSignedOwners(requestId): Address[]

Get owners who have signed a specific request.

getBalance(token?): Promise<bigint>

Get token balance of the multi-sig account.

cleanup(): void

Remove expired signature requests.

Supported Chains

| Chain | Chain ID | USDT0 | USDC | | --------- | -------- | ----- | ---- | | Ethereum | 1 | ✅ | ✅ | | Arbitrum | 42161 | ✅ | ✅ | | Base | 8453 | ✅ | ✅ | | Optimism | 10 | ✅ | ✅ | | Ink | 57073 | ✅ | - | | Berachain | 80084 | ✅ | - | | Unichain | 130 | ✅ | - |

Examples

2-of-3 Treasury Management

// Create 2-of-3 multi-sig for treasury
const treasury = await createMultiSigWdkGaslessClient({
  owners: [ceoAccount, cfoAccount, cooAccount],
  threshold: 2,
  publicClient,
  chainId: 1,
  bundler: bundlerConfig,
  paymaster: paymasterConfig,
})

// CEO initiates payment
const payment = await treasury.initiatePayment({
  to: vendorAddress,
  amount: 50000_000000n, // 50,000 USDT0
})

// Share requestId with other owners
console.log(`Request ID: ${payment.requestId}`)
console.log(`UserOp Hash: ${payment.userOpHash}`)

// CFO signs (can be on different device/service)
await treasury.signWithOwner(payment.requestId, 1, cfoSigner)

// Now threshold is met, submit
const result = await treasury.submitRequest(payment.requestId)

Distributed Signing Service

// Backend service that manages signature collection
class SignatureService {
  private client: MultiSigWdkGaslessClient

  async requestSignature(requestId: string, ownerIndex: number) {
    // Notify owner (email, push notification, etc.)
    const pendingOwners = this.client.getPendingOwners(requestId)
    const request = this.client.getPendingRequests().find((r) => r.id === requestId)

    return {
      requestId,
      userOpHash: request?.userOpHash,
      pendingOwners,
      threshold: this.client.getThreshold(),
    }
  }

  async addSignature(requestId: string, ownerIndex: number, signature: Hex) {
    await this.client.addExternalSignature(requestId, ownerIndex, signature)

    const request = this.client.getPendingRequests().find((r) => r.id === requestId)

    if (request?.isReady) {
      return this.client.submitRequest(requestId)
    }

    return { status: 'pending', collected: request?.collectedCount }
  }
}

Batch Payroll

// Monthly payroll with multi-sig approval
const payroll = await client.initiateBatchPayment({
  payments: [
    { to: employee1, amount: 5000_000000n },
    { to: employee2, amount: 4500_000000n },
    { to: employee3, amount: 6000_000000n },
    { to: contractor1, amount: 3000_000000n },
  ],
})

// HR initiates, Finance approves
await payroll.addSignature(0, hrSigner)
await payroll.addSignature(1, financeSigner)

const result = await payroll.submit()
console.log(`Payroll executed: ${result.userOpHash}`)

Error Handling

import { MultiSigError } from '@t402/wdk-multisig'

try {
  const result = await client.submitRequest(requestId)
} catch (error) {
  if (error instanceof MultiSigError) {
    switch (error.code) {
      case 'REQUEST_NOT_FOUND':
        console.log('Request expired or invalid')
        break
      case 'THRESHOLD_NOT_MET':
        console.log(`Need ${error.threshold} signatures, have ${error.collected}`)
        break
      case 'OWNER_NOT_FOUND':
        console.log('Invalid owner index')
        break
    }
  }
}

Architecture

┌─────────────────────────────────────────────────────────────┐
│                  MultiSigWdkGaslessClient                   │
├─────────────────────────────────────────────────────────────┤
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐       │
│  │   Owner 1    │  │   Owner 2    │  │   Owner 3    │       │
│  │  (WDK Acc)   │  │  (WDK Acc)   │  │  (WDK Acc)   │       │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘       │
│         │                 │                 │               │
│         └─────────────────┼─────────────────┘               │
│                           ▼                                 │
│              ┌────────────────────────┐                     │
│              │  SignatureCollector    │                     │
│              │  (M-of-N threshold)    │                     │
│              └────────────┬───────────┘                     │
│                           ▼                                 │
│              ┌────────────────────────┐                     │
│              │  Safe 4337 Account     │                     │
│              │  (Smart Contract)      │                     │
│              └────────────┬───────────┘                     │
│                           ▼                                 │
│              ┌────────────────────────┐                     │
│              │  Bundler + Paymaster   │                     │
│              │  (ERC-4337)            │                     │
│              └────────────────────────┘                     │
└─────────────────────────────────────────────────────────────┘

License

Apache-2.0