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

txflow-sdk

v0.4.1

Published

TypeScript SDK for TxFlow perpetual DEX with EIP-712 signing and MCP server

Readme

txflow-sdk

TypeScript SDK for TxFlow perpetual DEX with EIP-712 signing and MCP (Model Context Protocol) server.

Installation

npm install txflow-sdk

Quick Start

TxFlow uses an agent wallet pattern with two distinct wallets:

  • User Wallet: The main wallet that owns the funds. Required for authorization, withdrawals, and transfers. Can be any ethers.AbstractSigner (private key, MetaMask, Privy, Turnkey, etc.).
  • Agent Wallet: A temporary wallet authorized by the user wallet. It only has trading permissions (place/cancel/modify orders, update leverage and margin mode). It cannot move funds.

The user wallet signs an ApproveAgent message to grant the agent wallet trading rights. After that, all trading operations use the agent wallet's key.

import { TxFlowClient } from "txflow-sdk";
import { ethers } from "ethers";

const userWallet = new ethers.Wallet("0xUSER_PRIVATE_KEY");
const agentWallet = new ethers.Wallet("0xAGENT_PRIVATE_KEY");

const client = new TxFlowClient({
  privateKey: agentWallet.privateKey,
});

// Step 1: User wallet authorizes the agent wallet (one-time)
await client.approveAgent(userWallet);

// Step 2: Agent wallet places orders (no user wallet needed)
const result = await client.placeOrder({
  asset: 1,       // Asset index (use getPerpMeta to look up)
  isBuy: true,
  price: "50000",
  size: "0.01",
  reduceOnly: false,
  timeInForce: "gtc",
});

// User wallet is needed for fund operations
await client.withdraw(userWallet, { destination: "0x...", amount: "100" });

Configuration

You can initialize the client with either a privateKey or a signer:

// Option A: Private key (for bots/scripts)
import { TxFlowClient, TESTNET_NETWORK_CONFIG } from "txflow-sdk";

const client = new TxFlowClient({
  privateKey: "0x...", // Agent wallet private key (trading only)
  baseUrl: "https://testnet-api.txflow.net", // API base URL (optional)
  vaultAddress: null, // Vault address if applicable (optional)
  networkConfig: TESTNET_NETWORK_CONFIG, // optional (defaults to testnet)
});

// Option B: Any ethers.AbstractSigner (for browser wallets)
const client = new TxFlowClient({
  signer: myAgentSigner,          // Any ethers.AbstractSigner instance
});

Mainnet Migration

When TxFlow launches mainnet, update the config:

import { TxFlowClient, MAINNET_API_URL, MAINNET_NETWORK_CONFIG } from "txflow-sdk";

const client = new TxFlowClient({
  privateKey: "0x...",
  baseUrl: MAINNET_API_URL, // or the actual mainnet URL
  networkConfig: MAINNET_NETWORK_CONFIG,
});

All signing logic, EIP-712 domains, and API calls adapt automatically based on networkConfig.

Browser Wallet Integration

The SDK accepts any ethers.AbstractSigner for user wallet operations (approveAgent, withdraw, transferToken, sendToken). This makes it compatible with MetaMask, Privy, Turnkey, and other wallet providers.

MetaMask / Injected Wallet

import { TxFlowClient } from "txflow-sdk";
import { ethers } from "ethers";

// Connect to MetaMask
const provider = new ethers.BrowserProvider(window.ethereum);
const userSigner = await provider.getSigner(); // ethers.JsonRpcSigner

// Generate a local agent wallet (trading only, no fund access)
const agentWallet = ethers.Wallet.createRandom();
// Persist agent key in localStorage so the user doesn't re-approve every session
localStorage.setItem("txflow_agent_key", agentWallet.privateKey);

const client = new TxFlowClient({
  privateKey: agentWallet.privateKey,
});

// User's MetaMask signs the one-time agent authorization
await client.approveAgent(userSigner);

// All trading uses the local agent key (no MetaMask popups)
await client.placeOrder({ asset: 1, isBuy: true, price: "50000", size: "0.01" });

// Fund operations still go through MetaMask
await client.withdraw(userSigner, { destination: "0x...", amount: "100" });

Privy Embedded Wallet

import { TxFlowClient } from "txflow-sdk";
import { ethers } from "ethers";
import { usePrivy, useWallets } from "@privy-io/react-auth";

// Inside a React component
const { wallets } = useWallets();
const embeddedWallet = wallets.find((w) => w.walletClientType === "privy");

// Get an ethers signer from Privy
const privyProvider = await embeddedWallet.getEthersProvider();
const userSigner = await privyProvider.getSigner(); // ethers.JsonRpcSigner

// Generate a local agent wallet
const stored = localStorage.getItem("txflow_agent_key");
const agentWallet = stored
  ? new ethers.Wallet(stored)
  : ethers.Wallet.createRandom();
localStorage.setItem("txflow_agent_key", agentWallet.privateKey);

const client = new TxFlowClient({
  privateKey: agentWallet.privateKey,
});

// Privy wallet signs the agent authorization (no browser extension needed)
await client.approveAgent(userSigner);

// Trading uses the local agent key (instant, no signing prompts)
await client.placeOrder({ asset: 1, isBuy: true, price: "50000", size: "0.01" });

// Fund operations go through Privy
await client.withdraw(userSigner, { destination: "0x...", amount: "100" });

API Reference

Trading (Agent Wallet)

These methods are signed by the agent wallet (the privateKey provided in the constructor). The agent must be authorized by the user wallet via approveAgent before use.

placeOrder(params)

await client.placeOrder({
  asset: 1,
  isBuy: true,
  price: "50000",
  size: "0.01",
  reduceOnly: false,
  timeInForce: "gtc", // "gtc" | "ioc" | "alo"
});

placeBatchOrders(params[])

await client.placeBatchOrders([
  { asset: 1, isBuy: true, price: "50000", size: "0.01" },
  { asset: 1, isBuy: false, price: "60000", size: "0.01" },
]);

cancelOrder(params)

await client.cancelOrder({ asset: 1, orderId: "123456" });

cancelOrders(params[])

await client.cancelOrders([
  { asset: 1, orderId: "123456" },
  { asset: 1, orderId: "789012" },
]);

modifyOrder(params)

await client.modifyOrder({
  orderId: "123456",
  asset: 1,
  isBuy: true,
  price: "51000",
  size: "0.02",
});

Account Settings (Agent Wallet)

updateLeverage(params)

await client.updateLeverage({ asset: 1, leverage: 10 });

updateMarginMode(params)

await client.updateMarginMode({ asset: 1, isCross: true });

User Actions (User Wallet)

These methods require the user wallet to be passed explicitly. Any ethers.AbstractSigner is accepted (private key wallet, MetaMask signer, Privy signer, etc.). The agent wallet cannot perform these operations.

approveAgent(userWallet, params?)

Authorizes the agent wallet for trading. Must be called once before the agent can trade.

await client.approveAgent(userWallet, {
  agentAddress: "0x...", // defaults to SDK agent address
  agentName: "MyBot",    // defaults to "TradeAgent"
});

withdraw(userWallet, params)

Withdraw USDC to an external address. Minimum amount is 2 USDC.

await client.withdraw(userWallet, {
  destination: "0x...",
  amount: "100",
});

transferToken(userWallet, params)

Transfer USDC between your own perp and spot accounts.

await client.transferToken(userWallet, {
  amount: "100",
  fromAccountType: "perp",
  toAccountType: "spot",
});

sendToken(userWallet, params)

Send USDC to another user's address.

await client.sendToken(userWallet, {
  toAddress: "0x...",
  amount: "50",
});

Queries

const meta = await client.getPerpMeta();
const state = await client.getUserState("0xUSER_ADDRESS");
const orders = await client.getOpenOrders("0xUSER_ADDRESS");
const book = await client.getL2Book(1);
const assetData = await client.getActiveAssetData("0xUSER_ADDRESS", "1");
const candles = await client.getCandleSnapshot({
  coin: "BTC",
  interval: "1h",
  startTime: Date.now() - 86400000,
  endTime: Date.now(),
});
const fees = await client.getUserFees("0xUSER_ADDRESS");

History

const orderHistory = await client.getOrderHistory({
  user: "0xUSER_ADDRESS",
  startTime: Date.now() - 30 * 86400000,
});
const tradeHistory = await client.getTradeHistory("0xUSER_ADDRESS");
const fundingHistory = await client.getFundingHistory("0xUSER_ADDRESS");
const ledger = await client.getUserNonFundingLedgerUpdates("0xUSER_ADDRESS");

Testnet Faucet

await client.requestFund("0xUSER_ADDRESS");

Sign Without Submitting

Every trading method has a corresponding build* method that returns the signed request payload without sending it to the API. This is useful for custom submission logic, queuing, or inspecting the signed data.

import { TxFlowClient, SignedRequest } from "txflow-sdk";

const client = new TxFlowClient({ privateKey: "0x..." });

const signed: SignedRequest = await client.buildPlaceOrder({
  asset: 1,
  isBuy: true,
  price: "50000",
  size: "0.01",
});

// signed = { action, signature: { r, s, v }, nonce, vaultAddress? }
// You now have full control over when and how to submit it.

// Submit later when ready:
const result = await client.submitSignedRequest(signed);

Available build* methods:

| Method | Description | |---|---| | buildPlaceOrder(params) | Sign a single order | | buildPlaceBatchOrders(params[]) | Sign a batch of orders | | buildCancelOrder(params) | Sign an order cancellation | | buildCancelOrders(params[]) | Sign multiple cancellations | | buildModifyOrder(params) | Sign an order modification | | buildUpdateLeverage(params) | Sign a leverage update | | buildUpdateMarginMode(params) | Sign a margin mode update |

All build* methods accept the same parameters as their corresponding action methods and return a SignedRequest object.

Advanced: Low-Level Signing

For custom integrations, the signing primitives are exported directly:

import {
  actionHash,
  signL1Action,
  createSign,
  signUserAction,
  TESTNET_NETWORK_CONFIG,
  getL1ActionDomain,
  getUserActionDomain,
  L1_ACTION_TYPES,
  APPROVE_AGENT_TYPES,
} from "txflow-sdk";

// Compute action hash (MessagePack + nonce + vault encoding + keccak256)
const hash = actionHash(action, vaultAddress, nonce);

// Sign an L1 action (order, cancel, modify, leverage, margin mode)
const sig = await signL1Action(wallet, action, vaultAddress, nonce, TESTNET_NETWORK_CONFIG);

// Sign a user action (approve, withdraw, transfer)
const domain = getUserActionDomain(TESTNET_NETWORK_CONFIG);
const sig = await signUserAction(wallet, domain, types, message);

Signing Algorithm

  1. Remove trailing zeros from p and s string fields recursively
  2. MessagePack encode the action with BigInt64 support
  3. Append nonce as 8-byte big-endian uint64
  4. Append vault flag: 0x00 if null (9 extra bytes), or 0x01 + 20-byte address (29 extra bytes)
  5. Keccak256 hash the buffer
  6. Sign via EIP-712 typed data with the L1 action domain

MCP Server

The SDK includes an MCP server for AI agent integration (e.g., Claude Desktop, Cursor, OpenClaw).

Setup

Build the project first:

npm install && npm run build

Configuration

Add to your MCP client config (e.g., claude_desktop_config.json):

{
  "mcpServers": {
    "txflow": {
      "command": "node",
      "args": ["/path/to/txflow-sdk/dist/mcp.js"],
      "env": {
        "TXFLOW_AGENT_PRIVATE_KEY": "0xAGENT_PRIVATE_KEY",
        "TXFLOW_USER_PRIVATE_KEY": "0xUSER_PRIVATE_KEY",
        "TXFLOW_BASE_URL": "https://testnet-api.txflow.net"
      }
    }
  }
}

Or if installed globally via npm:

{
  "mcpServers": {
    "txflow": {
      "command": "txflow-mcp",
      "env": {
        "TXFLOW_AGENT_PRIVATE_KEY": "0xAGENT_PRIVATE_KEY",
        "TXFLOW_USER_PRIVATE_KEY": "0xUSER_PRIVATE_KEY"
      }
    }
  }
}

Environment Variables

| Variable | Required | Description | |---|---|---| | TXFLOW_AGENT_PRIVATE_KEY | Yes | Agent wallet private key (trading only: orders, cancels, leverage, margin) | | TXFLOW_USER_PRIVATE_KEY | No | User wallet private key (for approve_agent, withdrawals, and transfers) | | TXFLOW_BASE_URL | No | API URL (default: https://testnet-api.txflow.net) | | TXFLOW_NETWORK | No | testnet or mainnet (default: testnet) |

Available Tools

| Tool | Description | |---|---| | place_order | Place a perpetual futures order | | cancel_order | Cancel an open order | | cancel_orders | Cancel multiple orders | | modify_order | Modify an existing order | | update_leverage | Update leverage for an asset | | update_margin_mode | Switch cross/isolated margin | | approve_agent | Authorize agent wallet (requires user key) | | get_open_orders | List open orders for a user | | get_user_state | Get account state, positions, balances | | get_perp_meta | Get available assets and their indices | | get_l2_book | Get order book depth for a coin |

License

MIT