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

@cointracker/tax-kit

v2.3.0

Published

CoinTracker SDK for supplying cost basis data

Readme

@cointracker/tax-kit

CoinTracker SDK for supplying cost basis data and integrating tax calculation features into your application.

Installation

npm install @cointracker/tax-kit

Usage

import { TaxKitProvider, useTaxKit } from '@cointracker/tax-kit';

// Wrap your app with the TaxKitProvider
function App() {
  const fetchAccessToken = async () => {
    // Fetch access token from your backend
    const response = await fetch('/api/user-token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
    });
    const data = await response.json();
    return data.access_token;
  };

  return (
    <TaxKitProvider fetchAccessToken={fetchAccessToken}>
      <YourApp />
    </TaxKitProvider>
  );
}

// Use the useTaxKit hook in your components
function YourComponent() {
  const {
    open,
    isSyncingTransactions,
    lastSyncUpdatedTimestamp,
    error,
    numberOfConnections,
    syncingConnections,
    isCalculatingCostBasis,
    disconnectUser,
    isUserConnected,
  } = useTaxKit();

  return (
    <div>
      <button onClick={open}>Connect CoinTracker</button>
      {isSyncingTransactions && <p>Syncing transactions...</p>}
      {syncingConnections && <p>Syncing connections...</p>}
      {isCalculatingCostBasis && <p>Calculating cost basis...</p>}
      {isUserConnected && (
        <div>
          <p>Connected with {numberOfConnections} connection(s)</p>
          <button onClick={disconnectUser}>Disconnect</button>
        </div>
      )}
      {error && <p>Error: {error.message}</p>}
    </div>
  );
}

Hook API Reference

The useTaxKit hook returns the following properties:

  • open - Function to open the CoinTracker modal for the user to connect their account
  • isTaxKitOpen - Boolean indicating if the TaxKit modal is currently open
  • isTaxKitLoading - Boolean indicating if the Tax Kit is currently loading (computing status)
  • isSyncingTransactions - Boolean indicating if the SDK is currently syncing transactions to the partner
  • lastSyncUpdatedTimestamp - Timestamp (ms since epoch) of the last sync update, or null if never synced
  • error - Any error encountered during sync or authentication (TaxKitError | null)
  • numberOfConnections - The number of connections the user has configured (number | null)
  • syncingConnections - Boolean indicating if the SDK is currently syncing connections
  • isCalculatingCostBasis - Boolean indicating if the SDK is currently calculating cost basis
  • taxKitStatus - Current TaxKit synchronization status with title, description, and action label (TaxKitStatusInfo | null)
  • disconnectUser - Function that disconnects the user and resets tax kit to empty state. Returns a Promise
  • isUserConnected - Boolean indicating if the user has started the tax kit flow

TaxKit Status Values

The taxKitStatus object contains a status field with one of these values:

  • GET_STARTED - User needs to connect accounts and start auto-sync
  • SYNCED - Cost basis has successfully been sent to the partner
  • TRANSACTIONS_NEED_REVIEW - Transactions require user review before proceeding
  • UNABLE_TO_SYNC_TO_PARTNER - Sync to partner failed due to an error
  • UPGRADE_PLAN_NEEDED - User has reached their plan limit and needs to upgrade
  • READY_TO_IMPORT - Tax lots are ready to be imported. Awaiting user confirmation.
  • COST_BASIS_FAILED - Cost basis calculation has failed
  • FIX_API_CONNECTIONS - API connections need to be fixed
  • IMPORTING_COST_BASIS - Sending tax lots to the partner
  • SYNCING_WALLETS_AND_CALCULATING_COST_BASIS - Syncing wallets and calculating cost basis
  • ERROR - Error in the tax kit (e.g. authentication error or generic error)

Configuration

TaxKitProvider groups configuration into three optional sub-objects: theme, options, and metadata. Callbacks (onOpenLink, onDisconnectUser, onPartnerImportTransactionsSuccess, onReceivedAdditionalMetadata) and copy stay at the top level.

Theme

Pass color tokens and dark-mode preference under theme:

<TaxKitProvider
  fetchAccessToken={fetchAccessToken}
  theme={{
    mode: 'dark', // optional — defaults to the user's OS preference
    light: {
      radius: '0.5rem',
      spacing: '0.25rem',
      fontSans: '"Inter", sans-serif',
      background: 'rgb(255, 255, 255)',
      foreground: 'rgb(10, 11, 13)',
      primary: 'rgb(0, 82, 255)',
      // ...see ThemeContract for all available tokens
    },
    dark: {
      radius: '0.5rem',
      spacing: '0.25rem',
      fontSans: '"Inter", sans-serif',
      background: 'rgb(10, 11, 13)',
      foreground: 'rgb(245, 248, 255)',
      primary: 'rgb(55, 115, 245)',
    },
  }}
>
  <YourApp />
</TaxKitProvider>

Tip: memoize theme (or its light/dark sub-objects) with useMemo if your parent component re-renders frequently — this avoids redundant CONFIG messages to the iframe.

Partner branding (logo and fonts)

theme accepts three optional fields for partners that want their own logo and brand font inside the iframe.

theme.partnerLogo

Renders your logo in the iframe header in place of the SDK's built-in mark. Accepts either a single string URL (used in both modes) or a per-mode object:

theme={{
  // Single URL — same logo in light and dark mode
  partnerLogo: 'https://cdn.your-partner.com/logo.svg',
}}
theme={{
  // Separate logos so a dark logo can be used on light backgrounds, and vice versa
  partnerLogo: {
    light: 'https://cdn.your-partner.com/logo-dark-on-light.svg',
    dark: 'https://cdn.your-partner.com/logo-light-on-dark.svg',
  },
}}

Notes:

  • The image is hosted by you — point at a CORS-accessible URL on your CDN or origin. Prefer SVG; if you ship PNG, keep it small (≤ 128px square). Visual size is locked to the iframe header — there's no risk of oversized images breaking layout — but bandwidth is still on you.
  • Alt text is auto-derived from the partner slug (e.g. "Coinbase logo"); you do not need to pass it.
  • When partnerLogo is unset, the SDK uses its built-in logo for known partner slugs (coinbase, kraken).
theme.fonts

Self-hosted @font-face definitions for woff2/woff/ttf/otf files you serve from your own CDN. Each entry is injected verbatim, so you control format, weight, style, and font-display.

theme={{
  fonts: [
    {
      family: 'Partner Sans',
      src: 'https://cdn.your-partner.com/partner-sans-regular.woff2',
      weight: '400',
      style: 'normal',
    },
    {
      family: 'Partner Sans',
      src: 'https://cdn.your-partner.com/partner-sans-bold.woff2',
      weight: '700',
      style: 'normal',
    },
  ],
  light: { fontSans: '"Partner Sans", sans-serif', /* ...other tokens */ },
  dark: { fontSans: '"Partner Sans", sans-serif', /* ...other tokens */ },
}}
theme.fontStylesheets

External font stylesheet URL(s) — Google Fonts, Adobe Fonts, Bunny Fonts, etc. The iframe injects each one as <link rel="stylesheet">, so the provider handles @font-face rules, unicode-range subsetting, and format negotiation for you.

theme={{
  // Single URL
  fontStylesheets:
    'https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap',
}}
theme={{
  // Multiple URLs
  fontStylesheets: [
    'https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap',
    'https://use.typekit.net/your-kit-id.css',
  ],
  light: { fontSans: '"Inter", sans-serif', /* ... */ },
}}

Use fonts for self-hosted brand fonts where you control the source URLs, fontStylesheets for off-the-shelf web fonts. The two are independent — use either, both, or neither.

Options

Behavior switches (environment, flow, overlay style, UI toggles) live under options:

<TaxKitProvider
  fetchAccessToken={fetchAccessToken}
  options={{
    mode: 'production', // 'production' | 'alpha' | 'mock'
    flow: 'one-way', // 'one-way' | 'continuous'
    overlayMode: 'responsive', // 'drawer' | 'dialog' | 'responsive'
    defaultOpen: false,
    helpLinkUrl: 'https://help.<your-partner>.com/tax',
  }}
>
  <YourApp />
</TaxKitProvider>

Custom Metadata

The SDK supports passing custom metadata between the parent application and the SDK iframe. This is useful for partner-specific data that needs to be quickly implemented without requiring updates to the Tax Kit SDK with new dedicated props.

Passing Metadata to SDK

<TaxKitProvider
  fetchAccessToken={fetchAccessToken}
  metadata={{
    customField: 'any-value',
  }}
>
  <YourApp />
</TaxKitProvider>

Reserved Keys

One key inside metadata has special handling by the SDK:

  • promoCode (string) — applied as the user's promo code.

All other keys are partner-defined and pass through opaquely to the iframe.

<TaxKitProvider
  fetchAccessToken={fetchAccessToken}
  metadata={{
    promoCode: 'PARTNER_2026',
    // arbitrary partner extras are allowed alongside reserved keys
    customField: 'any-value',
  }}
>
  <YourApp />
</TaxKitProvider>

Receiving Metadata from SDK

The SDK can send metadata back to the parent application using the onReceivedAdditionalMetadata callback:

<TaxKitProvider
  fetchAccessToken={fetchAccessToken}
  onReceivedAdditionalMetadata={(metadata) => {
    console.log('Received from SDK:', metadata);
    // Handle partner-specific events or data
  }}
>
  <YourApp />
</TaxKitProvider>

The metadata is typed as Record<string, unknown> allowing flexible partner-specific implementations without SDK changes.

URL Parameter Metadata

The SDK also supports passing metadata via URL parameters on the partner application. Add a cointracker-tax-kit-param query parameter to your page URL, and the SDK will automatically include it in the metadata:

https://your-partner-site.com/tax-center?cointracker-tax-kit-param=my-value

Features

  • Transaction management and tracking
  • Integration with various exchanges and wallets
  • Cost basis calculation
  • Tax form generation support

Error Codes

TaxKit provides error codes that can be accessed through the error property returned by the useTaxKit hook. These errors help you handle different failure scenarios in your application.

General TaxKit Errors

These errors are available through the TaxKitError enum and represent high-level SDK state errors:

  • generic_error - A generic error occurred
  • missing_connections - No connections are configured for the user
  • connection_sync_errors - One or more connections have sync errors
  • transactions_needs_review - Transactions require manual review
  • authentication_error - Authentication failed (e.g., invalid or expired access token)

Example Usage

import { useTaxKit, TaxKitError } from '@cointracker/tax-kit';

function YourComponent() {
  const { error } = useTaxKit();

  if (error) {
    // Check error codes using the TaxKitError enum
    switch (error) {
      case TaxKitError.MissingConnections:
        return <p>Please connect an exchange or wallet to get started.</p>;
      case TaxKitError.AuthenticationError:
        return <p>Authentication failed. Please try again.</p>;
      case TaxKitError.ConnectionSyncErrors:
        return (
          <p>
            Some connections have sync errors. Please check your connections.
          </p>
        );
      case TaxKitError.TransactionsNeedsReview:
        return (
          <p>Some transactions require review before they can be processed.</p>
        );
      default:
        return <p>An error occurred: {error}</p>;
    }
  }

  return <div>TaxKit is ready</div>;
}

Migrating from the flat prop shape

In version 2.2.0 the flat prop shape was reorganized into grouped sub-objects. The old props still work and are mapped onto the new shape internally, but each one logs a console.warn once per browser session and will be removed in a future major version.

| Old prop | New location | | ------------------- | ---------------------------- | | apiBaseUrl | options.apiBaseUrl | | hostPageUrl | options.hostPageUrl | | mode | options.mode | | flow | options.flow | | overlayMode | options.overlayMode | | defaultOpen | options.defaultOpen | | hideNavigationBar | options.hideNavigationBar | | hideBackButton | options.hideBackButton | | helpLinkUrl | options.helpLinkUrl | | themeContract | theme.light / theme.dark | | themeMode | theme.mode | | userPromoCode | metadata.promoCode |

When both an old and new prop are passed, the new shape wins. Pass null to metadata.promoCode to explicitly clear a value rather than fall back to the legacy prop.

Removed in 2.2.0: partnerCostBasisMethodsByYear prop

The partnerCostBasisMethodsByYear prop and its iframe-side machinery were removed entirely. The dedicated Record<number, CostBasisMethod> pipe had been built speculatively for per-year cost-basis enforcement, but no UI component ever consumed it. Partners (Coinbase confirmed in writing) pass cost-basis info via metadata.costBasisMethodInfo instead — a different key that the iframe actually reads (CostBasisMethodMismatchWarningOverlay.tsx). Partners who need to pass cost-basis-related metadata should continue using metadata.costBasisMethodInfo or other custom keys.

Removed in 2.2.0: startRoute prop and ?startRoute= URL forwarding

Both the startRoute prop and the ?startRoute= URL forwarding were removed. Investigation showed the feature was effectively a no-op in production: canonical state-driven routing in the iframe overrode startRoute in all but one specific case (/checkout-complete, which itself isn't reached via URL today — the iframe transitions to it via internal xstate events after Stripe payment). Partners who passed startRoute were paying for a feature that wasn't actually navigating users anywhere.

If a real deep-linking need surfaces post-merge, we'll design and ship it as a proper feature.

Removed in 2.2.0: themeClassName prop

The themeClassName prop was removed. The iframe's wrapper now derives its class directly from theme.mode — partners no longer need to think about Tailwind custom variants or pass any className.

Why the change is safe:

  • The iframe SDK's bundled CSS has zero Tailwind utilities using the ct-embedded-dark:* or pro:* custom variants — those variant declarations existed but never matched any utility class, so values like 'ct-embedded-dark' or 'pro dark' were doing nothing meaningful inside the iframe.
  • The functionally important piece — providing a .dark ancestor so base-ui's dark:* Tailwind utilities can fire — is now handled by applying theme.mode (e.g. 'dark') directly to the iframe's wrapper.
  • The parent app's own theme className mechanism (which IS load-bearing for parent-side pro:* utilities under apps/embedded-api/app/) is completely separate and unaffected by this change.

Partners who previously passed themeClassName should remove it. No replacement is needed — theme.mode covers the only thing the prop actually did inside the iframe.

Removed in 2.2.0: overridePartnerLogo prop

The overridePartnerLogo prop was removed. The iframe now resolves the partner logo directly from the JWT-derived partner identity (embeddedHealth.partnerId) via the iframe's icon registry. Partners no longer need to pass a separate logo override — if the JWT identifies your integration as 'kraken', 'coinbase', etc., the matching logo is rendered automatically.

Updated in 2.3.0: if you're a new partner and don't have a hardcoded SVG in the iframe's registry, pass your logo URL directly via theme.partnerLogo — no CoinTracker-side code change needed. The hardcoded registry still acts as the fallback for production partners (Coinbase, Kraken).

License

ISC - Copyright CoinTracker