npm package: @manifoldxyz/js-ts-utils
Description: JS and TS Utils for Manifold Devs
Author: manifold.xyz
Latest version: 9.6.2

# @manifoldxyz/js-ts-utils

![Build Status](https://github.com/manifoldxyz/js-ts-utils/actions/workflows/build.yml/badge.svg)

A TypeScript utility library providing common helpers and constants for Manifold's frontend applications and services.

## What is this library?

This is a shared utility library containing **pure JavaScript/TypeScript functions** with minimal external dependencies. Use this library when you need common functionality that isn't specific to any single repository.

**When to add code here:**
- Reusable utility functions needed across multiple Manifold repos
- Common constants (addresses, network configs, app IDs)
- Platform-agnostic helpers (string formatting, URL generation, etc.)

**When NOT to add code here:**
- Repository-specific logic
- React components or hooks (use `sdk-react` instead)
- Complex business logic with many dependencies

**Dependency policy:**
- Keep external dependencies minimal
- Avoid heavy frameworks or libraries
- Prefer pure JS/TS implementations when practical

## Prerequisites

- **Node.js** >= 20.0.0
- **npm** (included with Node.js)

## Installation

```sh
npm install @manifoldxyz/js-ts-utils
```

## Architecture

### Project Structure

```
src/
  index.ts                    # Barrel export of all modules as namespaces
  lib/
    apps/
      constants.ts            # AppId enum, APP_METADATA, collector/token mint app IDs
      index.ts                # getInstanceLink, getAppName, isKnownAppId
    arweave.ts                # Arweave gateway URL utilities
    constants.ts              # NULL_ADDRESS, NULL_BYTES32
    cookies.ts                # Browser cookie management (setCookie, getCookie, removeCookie)
    countdown.ts              # Timer/countdown formatting for UI display
    currency.ts               # Currency type, ERC20 configs per network, formatCurrency
    error.ts                  # Error coercion utilities (coerceErrorMessageFromUnknown)
    media.ts                  # Content-type detection regexes, SVG encoding
    misc.ts                   # General helpers (sleep, noop, isJSON, poll)
    network.ts                # NetworkId enum, NETWORK_CONFIGS, chain resolution
    platform.ts               # NFT marketplace platform definitions with icons/URLs
    providerEvents.ts         # Web3 wallet provider event debouncing
    string.ts                 # String formatting (abbreviateAddress, classnames/twMerge)
    urls.ts                   # URL generators for marketplaces and block explorers
    web3TransactionError.ts   # Ethers.js error parsing → TransactionErrors enum
  assets/                     # SVG logos for marketplace platforms
tests/
  *.test.ts                   # Test files mirror src/lib/ structure
  mocks.ts                    # Shared test mocks
```

### Build System

- **Vite** builds dual-format output (ESM + CJS) to `dist/`
- **vite-plugin-dts** generates TypeScript declarations
- Each module has its own entry point — individually importable via package.json `exports` map
- Path alias `@` → `./src` (configured in tsconfig.json and vitest.config.ts)

### Supported Networks (NetworkId enum)

Mainnet (1), Optimism (10), Base (8453), Sepolia (11155111), Shape (360), ApeChain (33139)

## Modules

### Apps
Manifold Studio app ID constants and instance link helpers.

```ts
import { Apps } from '@manifoldxyz/js-ts-utils';
// or
import { AppId, getInstanceLink, getAppName } from '@manifoldxyz/js-ts-utils/apps';

Apps.AppId.Claim // 2522713783
Apps.getAppName(AppId.Claim) // "Edition Page"
Apps.getInstanceLink({ preview, workspaceSlug }) // "https://manifold.xyz/@workspace/id/123"
```

### Constants
Common blockchain constants.

```ts
import { Constants } from '@manifoldxyz/js-ts-utils';
// or
import { NULL_ADDRESS, NULL_BYTES32 } from '@manifoldxyz/js-ts-utils/constants';

Constants.NULL_ADDRESS // "0x0000000000000000000000000000000000000000"
Constants.NULL_BYTES32 // "0x0000000000000000000000000000000000000000000000000000000000000000"
```

### Cookies
Browser cookie management utilities.

```ts
import { Cookies } from '@manifoldxyz/js-ts-utils';
// or
import { setCookie, getCookie, removeCookie } from '@manifoldxyz/js-ts-utils/cookies';

Cookies.setCookie('name', 'value', 7) // expires in 7 days
Cookies.getCookie('name') // "value"
Cookies.removeCookie('name')
```

### Countdown
Timer and countdown utilities for UI display.

```ts
import { Countdown } from '@manifoldxyz/js-ts-utils';
// or
import { getCountdownFromTimestamp, getCountdownFromDuration } from '@manifoldxyz/js-ts-utils/countdown';

Countdown.getCountdownFromTimestamp(Date.now() + 86400000) // "01D : 00H : 00M : 00S"
Countdown.getCountdownFromDuration(3600000, false) // "01H : 00M : 00S"
```

### Currency
Currency definitions, ERC20 addresses, and display formatting.

```ts
import { Currency } from '@manifoldxyz/js-ts-utils';
// or
import { CURRENCIES, formatCurrencyDisplay, getERC20Address } from '@manifoldxyz/js-ts-utils/currency';

Currency.CURRENCIES.USDC // { coinGeckoId, symbol, erc20: { [networkId]: { address, decimals } } }
Currency.formatCurrencyDisplay('1000000', 'USDC', 1) // "1.00"
```

### Media
Media type detection and SVG encoding utilities.

```ts
import { Media } from '@manifoldxyz/js-ts-utils';
// or
import { isVideo, isImage, isAudio, is3DModel, encodeSVGDataString } from '@manifoldxyz/js-ts-utils/media';

Media.isVideo.test('video/mp4') // true
Media.isImage.test('image/png') // true
Media.encodeSVGDataString(svgData) // encoded SVG for img src
```

### Misc
General purpose utilities.

```ts
import { Misc } from '@manifoldxyz/js-ts-utils';
// or
import { sleep, noop, isJSON, poll } from '@manifoldxyz/js-ts-utils/misc';

await Misc.sleep(1000) // wait 1 second
Misc.isJSON('{"valid": true}') // true
await Misc.poll({ fetch, validate, interval: 1000, maxAttempts: 10, errorMessage: '...' })
```

### Network
Blockchain network configurations, IDs, and utilities.

```ts
import { Network } from '@manifoldxyz/js-ts-utils';
// or
import { NetworkId, NETWORK_CONFIGS, isSupportedNetwork, isTestnet } from '@manifoldxyz/js-ts-utils/network';

Network.NetworkId.Mainnet // 1
Network.NetworkId.Base // 8453
Network.NETWORK_CONFIGS[NetworkId.Mainnet] // { chainId, rpcUrls, nativeCurrency, ... }
Network.isSupportedNetwork(1) // true
Network.isTestnet(11155111) // true (Sepolia)
Network.getDisplayNameForNetworkId(1) // "ethereum"
Network.resolveChainIdByName('base') // 8453
```

### Platform
NFT marketplace platform definitions with icons and URL generators.

```ts
import { Platform } from '@manifoldxyz/js-ts-utils';
// or
import { Platform as PlatformEnum, PLATFORM_MAP } from '@manifoldxyz/js-ts-utils/platform';

Platform.Platform.OpenSea // enum value
Platform.PLATFORM_MAP[Platform.OpenSea] // { iconSrc, name, url: (props) => string }
```

### ProviderEvents
Web3 wallet provider event handling.

```ts
import { ProviderEvents } from '@manifoldxyz/js-ts-utils';
// or
import setProviderEventsHandler from '@manifoldxyz/js-ts-utils/providerEvents';

ProviderEvents.default(() => {
  // Called when wallet address, chain, or provider changes
  console.log('Provider changed');
});
```

### StringUtils
String manipulation and formatting utilities.

```ts
import { StringUtils } from '@manifoldxyz/js-ts-utils';
// or
import { abbreviateAddress, classnames } from '@manifoldxyz/js-ts-utils/stringUtils';

StringUtils.abbreviateAddress('0x1234567890abcdef1234567890abcdef12345678') // "0x1234...5678"
StringUtils.abbreviate('hello world', 3, 3) // "hel...rld"
StringUtils.classnames('btn', isActive && 'btn-active') // Tailwind class merging
```

### URLs
URL generators for NFT marketplaces and block explorers.

```ts
import { URLs } from '@manifoldxyz/js-ts-utils';
// or
import { getOpenSeaTokenURL, getEtherscanAddressURL } from '@manifoldxyz/js-ts-utils/urls';

URLs.getOpenSeaTokenURL(1, contractAddress, tokenId)
URLs.getEtherscanAddressURL(1, address)
URLs.getLooksRareTokenURL(1, contractAddress, tokenId)
URLs.getRaribleTokenURL(1, contractAddress, tokenId)
```

### Web3TransactionError
Ethers.js error parsing utilities.

```ts
import { Web3TransactionError } from '@manifoldxyz/js-ts-utils';
// or
import { parseEthersError, TransactionErrors } from '@manifoldxyz/js-ts-utils/web3TransactionError';

const errorType = Web3TransactionError.parseEthersError(error);
if (errorType === Web3TransactionError.TransactionErrors.REJECTED) {
  // User rejected the transaction
}
```

## Commands

```sh
npm run test       # run the test suite in watch mode (vitest)
npm run build      # build the library in ./dist (vite)
npm run format     # run prettier on the files
npm run coverage   # run tests with coverage reporting (v8)
npm run lint       # run eslint
npm run lint:fix   # run eslint with auto-fix
```

## External Dependencies

### Internal Packages

| Package | Version | Purpose |
|---------|---------|---------|
| `@manifoldxyz/frontend-provider-types` | ^2.0.0 | Web3 wallet provider type definitions |
| `@manifoldxyz/contract-abis` | ^1.0.0 | Contract ABI types (dev only) |
| `@manifoldxyz/eslint-config` | ^1.2.3 | Shared ESLint configuration (dev only) |
| `@manifoldxyz/lint-configs` | ^1.1.1 | Shared lint configurations (dev only) |
| `@manifoldxyz/studio-apps-client` | ^2.1.1 | Studio apps client types (dev only, type imports restricted) |

### Third-Party Services

| Package | Purpose | Config |
|---------|---------|--------|
| `ethers` v6 + `@ethersproject/*` v5 | Ethereum blockchain interaction, BigNumber math, address utils | — |
| `clsx` + `tailwind-merge` | CSS class name merging with Tailwind dedup | — |
| `decimal.js` | Precise decimal arithmetic for currency formatting | — |
| `posthog-js` | Analytics telemetry (dev only, type imports restricted at runtime) | — |

### External APIs (used in utility functions)

| Service | Module | Usage |
|---------|--------|-------|
| Arweave Gateway | `arweave.ts` | URL generation for Arweave-stored NFT assets |
| OpenSea | `urls.ts` | Token/collection URL generation |
| LooksRare | `urls.ts` | Token URL generation |
| Rarible | `urls.ts` | Token URL generation |
| Magic Eden | `urls.ts` | Token URL generation |
| Etherscan/Block Explorers | `urls.ts` | Address/transaction URL generation |

> **Note:** This is a pure utility library — it generates URLs but does not make HTTP requests to these services (except `misc.ts:poll()` which is a generic polling helper used by consumers).

## Contributing

Please make a Pull Request for all proposed changes. Your pull request should always include unit test additions or modifications. Your pull request must pass all unit tests and linting tests. Once reviewed and passing, you can merge.

An example PR: https://github.com/manifoldxyz/js-ts-utils/pull/1

### Adding a New Module

1. Create source file: `src/lib/foo.ts`
2. Barrel export in `src/index.ts`: add `import * as Foo from './lib/foo'` and include in `export {}`
3. Vite build entry in `vite.config.ts`: add `foo: resolve(__dirname, 'src/lib/foo.ts')`
4. Package exports in `package.json`: add `"./foo"` entry with `import`, `require`, `types` fields
5. Bump version in `package.json`
6. Create tests: `tests/foo.test.ts`

## Releasing

1. Bump the version in package.json
2. Any merge or commit of that change to main will trigger the CI release action
3. This will create a new version on npm matching the package.json (will not overwrite) and will create a new GH release for the version (will overwrite)

## Coverage

If you run `npm run coverage` and the file you are modifying now has either _less coverage_ or does not have _near 90% coverage_, **please add the missing coverage**.

## Migration Guide

### 7.1.0 -> 7.2.0

- **Breaking:** `getInstanceLink`'s first argument is now an object with `preview` and `workspaceSlug` properties
- **Breaking:** `PLATFORM_MAP[Platform.Manifold].url` generates new Manifold.xyz links if contract id and workspace identifier are provided

### 3.0.x -> 4.0.0

- **Breaking:** `getNetworkNameForNetworkId` is now `getDisplayNameForNetworkId`

### 2.0.0 -> 2.1.0

- **New:** Added `Telemetry` module (since removed)
- **New:** Added `Apps` module

### 1.3.2 -> 1.4.0

- **Breaking:** `getManifoldGalleryBaseURL` is now `getManifoldMarketplaceBaseURL`
- **Breaking:** `getManifoldGalleryTokenURL` is now `getManifoldMarketplaceTokenURL`
- **Breaking:** `PLATFORM_MAP[Platform.Manifold].url` generates new Manifold.gallery links

### < 1.0.0 -> 1.0.0

If you are not using `"type": "module"` in your package.json, you will have to use the CommonJS builds of this library which are only available in version 1.0.0 and greater.

### 0.2.x -> 0.3.x

This library now uses ESM.

- `network` module:
  - **Breaking:** Types are now `PascalCase` and singular instead of `ALL_CAPS_SNAKE_CASE` and plural
  - **Breaking:** `SUPPORTED_NETWORKS_ARRAY` is now a `const` array with properly inferred types
  - **Breaking:** `NETWORK_IDS` and `NETWORK_NAMES` enums replaced by `NetworkId` enum
  - **New:** Added `MAIN_NETWORKS`, `TEST_NETWORKS` arrays with corresponding types
  - **New:** Added `resolveChainIdByName` function