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

flowvault-sdk

v0.1.1

Published

TypeScript SDK for FlowVault — Programmable USDCx Routing on Stacks

Readme

FlowVault SDK

TypeScript SDK for FlowVault — Programmable USDCx Routing on Stacks.

FlowVault SDK wraps every FlowVault smart-contract function into a clean, typed TypeScript API so developers can integrate programmable stablecoin flows in minutes.


Table of Contents


Features

  • Minimal — zero opinions, pure interface layer.
  • Typed — full TypeScript types for every input & output.
  • Deterministic — inputs are validated before any network call.
  • Network configurable"testnet" or "mainnet" with one string.
  • Frontend-friendly — works in Node.js, bundlers, and browser runtimes.

Installation

npm install flowvault-sdk
# or
yarn add flowvault-sdk

The SDK depends on @stacks/transactions and @stacks/network (peer-level — installed automatically).


Quick Start

import { FlowVault, tokenToMicro } from "flowvault-sdk";

// 1. Initialise
const vault = new FlowVault({
  network: "testnet",
  contractAddress: "STD7QG84VQQ0C35SZM2EYTHZV4M8FQ0R7YNSQWPD",
  contractName: "flowvault",
  tokenContractAddress: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM",
  tokenContractName: "usdcx",
  senderKey: process.env.STACKS_PRIVATE_KEY!,
});

// 2. Set routing rules
await vault.setRoutingRules({
  lockAmount: tokenToMicro("100"), // lock 100 USDCx
  lockUntilBlock: 210000,          // until block 210 000
  splitAddress: "ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG",
  splitAmount: tokenToMicro("50"), // send 50 USDCx to split address
});

// 3. Deposit USDCx (routing rules execute automatically)
await vault.deposit(tokenToMicro("500"));

// 4. Read vault state
const state = await vault.getVaultState(
  "ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG"
);
console.log("Locked:", state.lockedBalance);
console.log("Available:", state.unlockedBalance);

// 5. Withdraw unlocked funds
await vault.withdraw(tokenToMicro("200"));

Browser Wallet Mode Example

import { request } from "@stacks/connect";
import { FlowVault, tokenToMicro } from "flowvault-sdk";

const senderAddress = "ST...connected-wallet-address";

const vault = new FlowVault({
  network: "testnet",
  contractAddress: "STD7QG84VQQ0C35SZM2EYTHZV4M8FQ0R7YNSQWPD",
  contractName: "flowvault",
  tokenContractAddress: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM",
  tokenContractName: "usdcx",
  senderAddress,
  contractCallExecutor: async (call) => {
    return request("stx_callContract", {
      contract: `${call.contractAddress}.${call.contractName}`,
      functionName: call.functionName,
      functionArgs: call.functionArgs,
      network: call.network,
      postConditionMode:
        String(call.postConditionMode ?? "allow").toLowerCase().includes("deny")
          ? "deny"
          : "allow",
      postConditions: call.postConditions,
    });
  },
});

await vault.deposit(tokenToMicro("10"));

Configuration

| Parameter | Type | Required | Description | | ----------------------- | -------- | -------- | ---------------------------------------- | | network | string | Yes | "testnet" or "mainnet" | | contractAddress | string | Yes | FlowVault deployer address | | contractName | string | Yes | FlowVault contract name | | tokenContractAddress | string | Yes | USDCx token deployer address | | tokenContractName | string | Yes | USDCx token contract name | | senderKey | string | No* | Private key for signing transactions | | senderAddress | string | No | Sender address for wallet-executor mode | | contractCallExecutor | func | No | Custom write executor (wallet signing) | | postConditions | array | No | Default post-conditions for write calls | | postConditionMode | string | No | "allow" or "deny" post-condition mode |

* Required for state-changing methods (deposit, withdraw, setRoutingRules, clearRoutingRules). Read-only methods work without it.

If you provide contractCallExecutor, write methods use that executor and do not require senderKey.

Default Testnet Addresses

import { DEFAULT_CONTRACTS } from "flowvault-sdk";

console.log(DEFAULT_CONTRACTS.testnet);
// {
//   contractAddress: "STD7QG84VQQ0C35SZM2EYTHZV4M8FQ0R7YNSQWPD",
//   contractName: "flowvault",
//   tokenContractAddress: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM",
//   tokenContractName: "usdcx",
// }

API Reference

Constructor

const vault = new FlowVault(config: FlowVaultConfig);

Validates configuration and stores network / contract metadata. Throws InvalidConfigurationError if required fields are missing.

Signer Modes

The SDK supports two write-signing modes:

  1. senderKey mode (backend/services):
  • SDK builds, signs, and broadcasts using makeContractCall + broadcastTransaction.
  1. contractCallExecutor mode (browser wallets):
  • SDK builds validated Clarity args and delegates transaction execution to your wallet adapter.
  • Useful with @stacks/connect where the private key is not exposed.

When using contractCallExecutor, pass senderAddress from the connected wallet account so SDK validations and read helpers can use the right principal context.


setRoutingRules

await vault.setRoutingRules(
  rules: RoutingRules,
  options?: TransactionOptions
): Promise<TransactionResult>

Configure routing rules that execute automatically on the next deposit.

Parameters (all in micro-units):

| Field | Type | Description | | ---------------- | ---------------- | ----------------------------------------------- | | lockAmount | MicroAmount | Amount to lock (0 = no lock) | | lockUntilBlock | number | Block height for lock expiry | | splitAddress | string \| null | Recipient of split amount | | splitAmount | MicroAmount | Amount forwarded to split address (0 = no split)|

Validation: Rejects negative values, invalid addresses, zero split-address with positive split-amount, and self-splits.

Important: when lockAmount > 0, lockUntilBlock must be strictly greater than the current chain block.

Recommended pattern:

const current = await vault.getCurrentBlockHeight("ST...sender");
const lockDuration = 144;

await vault.setRoutingRules({
  lockAmount: tokenToMicro("100"),
  lockUntilBlock: current + lockDuration,
  splitAddress: null,
  splitAmount: tokenToMicro("0"),
});

deposit

await vault.deposit(
  amount: MicroAmount,
  options?: TransactionOptions
): Promise<TransactionResult>

Deposit USDCx into the vault. Routing rules (lock / split / hold) are applied automatically.

  • amount — micro-units as bigint, integer number, or numeric string.

withdraw

await vault.withdraw(
  amount: MicroAmount,
  options?: TransactionOptions
): Promise<TransactionResult>

Withdraw unlocked USDCx. Fails on-chain if amount exceeds unlocked balance.

  • amount — micro-units as bigint, integer number, or numeric string.

clearRoutingRules

await vault.clearRoutingRules(options?: TransactionOptions): Promise<TransactionResult>

Delete all routing rules for the caller. Future deposits go straight to unlocked balance.


getVaultState

await vault.getVaultState(userAddress: string): Promise<VaultState>

Read-only. Returns the complete vault state including balances and active routing rules.

interface VaultState {
  totalBalance: number;
  lockedBalance: number;
  unlockedBalance: number;
  lockUntilBlock: number;
  currentBlock: number;
  routingRules: RoutingRules;
}

getRoutingRules

await vault.getRoutingRules(userAddress: string): Promise<RoutingRules | null>

Read-only. Returns the user's routing rules, or null if none are configured.


hasLockedFunds

await vault.hasLockedFunds(userAddress: string): Promise<boolean>

Read-only. Returns true if the user has locked funds that haven't expired.


getCurrentBlockHeight

await vault.getCurrentBlockHeight(senderAddress: string): Promise<number>

Read-only. Returns the current Stacks block height. Useful for calculating lockUntilBlock values.


Routing Model

FlowVault executes three routing operations at deposit time:

  1. LOCK — A fixed amount is locked until a specific block height. Locked funds cannot be withdrawn until the lock expires.
  2. SPLIT — A fixed amount is immediately forwarded to another Stacks principal.
  3. HOLD — The remainder stays in the vault as unlocked balance, available for withdrawal at any time.
Deposit 1000 USDCx
  ├─ LOCK  200 (until block 200000)
  ├─ SPLIT 300 → ST2CY5V39NHDP...
  └─ HOLD  500 (available immediately)

Utilities

import {
  tokenToMicro,     // "1.5" -> 1500000n
  microToToken,     // 1500000 -> "1.5"
  microToNumber,    // 1500000 -> 1500000 (throws if unsafe)
  isValidAddress,   // "ST..." → true/false
  isBlockInFuture,  // (target, current) → boolean
} from "flowvault-sdk";

Error Handling

All SDK errors extend FlowVaultError:

| Error Class | When | | --------------------------- | --------------------------------------------- | | InvalidAmountError | Negative, NaN, or non-integer amount | | InvalidAddressError | Bad Stacks address format | | InvalidConfigurationError | Missing config fields or bad network | | InvalidRoutingRuleError | Routing rules are invalid or unsafe | | NetworkConfigurationError | Unsupported network selection | | ContractCallError | On-chain rejection (includes error code) | | NetworkError | RPC unreachable, timeouts | | ParsingError | Contract response could not be parsed safely |

ContractCallError includes a code property mapping to FlowVault contract errors:

| Code | Meaning | | ---- | ------------------------------------------ | | 1001 | Invalid amount | | 1002 | Insufficient balance | | 1003 | Funds are locked | | 1004 | Routing amounts exceed deposit | | 1007 | Split address required | | 1008 | Lock block must be in the future | | 1010 | Lock exceeds hold | | 1011 | Cannot split to yourself |


Network Configuration

// Testnet (default for development)
const vault = new FlowVault({ network: "testnet", ... });

// Mainnet
const vault = new FlowVault({ network: "mainnet", ... });

The SDK passes the network string directly to @stacks/transactions which handles RPC endpoint resolution internally.


Post-Conditions (Optional)

You can attach post-conditions to protect token movement during deposit and withdraw.

import { PostConditionMode } from "@stacks/transactions";

const vault = new FlowVault({
  network: "testnet",
  contractAddress: "...",
  contractName: "flowvault",
  tokenContractAddress: "...",
  tokenContractName: "usdcx",
  senderKey: process.env.STACKS_PRIVATE_KEY!,
  postConditionMode: PostConditionMode.Deny,
  postConditions: [/* your post-conditions */],
});

await vault.deposit(tokenToMicro("10"), {
  postConditionMode: "deny",
  postConditions: [/* per-call post-conditions */],
});

Common Integration Notes

  • Use tokenToMicro("...") for all user-entered token amounts.
  • Read methods can run without senderKey; write methods need either senderKey or contractCallExecutor.
  • In wallet mode, return wallet response objects as-is; SDK extracts txid/txId/id automatically.
  • For lock rules, prefer duration-based UX (currentBlock + duration) to avoid stale absolute inputs.

Development

# Install dependencies
npm install

# Run tests
npm test

# Watch mode
npx vitest

# Build
npm run build

Project Structure

flowvault-sdk/
├── src/
│   ├── index.ts          — Public re-exports
│   ├── FlowVault.ts      — Main SDK class
│   ├── types.ts           — TypeScript interfaces
│   ├── errors.ts          — Error classes
│   ├── constants.ts       — Defaults & error map
│   ├── network.ts         — Network helpers
│   └── utils.ts           — Validation, conversion, parsing
├── tests/                 — Vitest test suites (100 tests)
├── docs/                  — Extended documentation
├── dist/                  — Compiled output (generated)
├── package.json
├── tsconfig.json
└── vitest.config.ts

Versioning

Follows Semantic Versioning:

| Version | Meaning | | ------- | ------------------------------ | | 0.1.0 | Initial public SDK | | 0.2.0 | Feature additions | | 1.0.0 | Stable, production-ready API |

Breaking changes will be documented in release notes.


License

MIT