npm package: @initia/utils
Description: TypeScript utility library for Interwoven applications
Author: Initia
Latest version: 2.0.0

# @initia/utils

TypeScript utility library for Interwoven applications

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Usage](#usage)
- [API Reference](#api-reference)
- [Contributing](#contributing)
- [License](#license)

## Features

- **Number Formatting**: Number formatting with BigNumber.js for blockchain amounts
- **Address Utilities**: Conversion between bech32 and hex address formats
- **BCS Serialization**: Extended Binary Canonical Serialization for Move types
- **Object Utilities**: Deterministic object address generation and IBC denom calculation
- **Text Processing**: String truncation for UI display

## Installation

```bash
npm install @initia/utils
# or
yarn add @initia/utils
# or
pnpm add @initia/utils
```

## Usage

### Text Utilities

```typescript
import { truncate } from "@initia/utils";

// Truncate long strings (useful for addresses)
truncate("0x1234567890abcdef1234567890abcdef12345678");
// "0x1234...345678"

// Custom truncation lengths
truncate("long_string_here", [3, 4]);
// "lon...here"
```

### Number Formatting

```typescript
import { formatNumber, formatAmount, formatPercent } from "@initia/utils";

// Basic number formatting
formatNumber("1234.567"); // "1,234.56"
formatNumber("1234.567", { dp: 3 }); // "1,234.567"
formatNumber("1234.567", { dp: 0 }); // "1,234"

// Trailing zeros are preserved when dp is explicitly specified
formatNumber("46.6", { dp: 2 }); // "46.60"
formatNumber("1000", { dp: 3 }); // "1,000.000"

// With abbreviations
formatNumber("1234", { abbr: true }); // "1.23K"
formatNumber("1234567", { abbr: true }); // "1.23M"
formatNumber("1234567890", { abbr: true }); // "1.23B"
formatNumber("1234567890123", { abbr: true }); // "1.23T"

// Format blockchain amounts (converts from base units)
formatAmount("1000000", { decimals: 6 }); // "1.000000"
formatAmount("1234567890", { decimals: 6 }); // "1,234.567890"
formatAmount("1234567890", { decimals: 6, dp: 3 }); // "1,234.567"
formatAmount("1000000000", { decimals: 6, dp: 3 }); // "1,000.000" (trailing zeros preserved)
formatAmount("1234567890", { decimals: 6, abbr: true }); // "1.234567K"

// Percentage formatting
formatPercent("0.123"); // "12.30%"
formatPercent("1.23"); // "123%"
formatPercent("0.1234567", { dp: 3 }); // "12.345%"
formatPercent("0.1", { dp: 2 }); // "10.00%" (trailing zeros preserved)

// Handling invalid values with fallback
formatNumber(undefined); // ""
formatNumber(undefined, { fallback: "N/A" }); // "N/A"
formatAmount("", { fallback: "0" }); // "0"
formatPercent(NaN, { fallback: "Invalid" }); // "Invalid"

// Custom rounding modes (default is ROUND_DOWN)
formatNumber("1.236", { dp: 2 }); // "1.23" (ROUND_DOWN)
formatNumber("1.236", { dp: 2, roundingMode: BigNumber.ROUND_UP }); // "1.24"
formatNumber("1.235", { dp: 2, roundingMode: BigNumber.ROUND_HALF_UP }); // "1.24"
```

### Base Unit Conversion

```typescript
import { toBaseUnit, fromBaseUnit } from "@initia/utils";

// Convert base units to display amount (removes trailing zeros)
fromBaseUnit("1500000", { decimals: 6 }); // "1.5"
fromBaseUnit("1", { decimals: 6 }); // "0.000001"
fromBaseUnit("1234567890", { decimals: 6 }); // "1234.56789"

// Convert display amount to base units
toBaseUnit("1.5", { decimals: 6 }); // "1500000"
toBaseUnit("0.000001", { decimals: 6 }); // "1"
toBaseUnit("1234.56789", { decimals: 6 }); // "1234567890"

// Note: toBaseUnit rounds down fractional amounts
toBaseUnit("1.9999999", { decimals: 6 }); // "1999999"
```

### Address Utilities

```typescript
import { InitiaAddress } from "@initia/utils";

// Convert between address formats
const address1 = InitiaAddress("init1wlvk4e083pd3nddlfe5quy56e68atra3gu9xfs");
address1.hex; // "0x77d96ae5e7885B19b5Bf4e680E129ACe8fD58fB1"
address1.rawHex; // "77d96ae5e7885b19b5bf4e680e129ace8fd58fb1"

const address2 = InitiaAddress("0x77d96ae5e7885B19b5Bf4e680E129ACe8fD58fB1");
address2.bech32; // "init1wlvk4e083pd3nddlfe5quy56e68atra3gu9xfs"
address2.rawHex; // "77d96ae5e7885b19b5bf4e680e129ace8fd58fb1"

// 32-byte Move addresses with leading zeros stripped
const moveAddress = InitiaAddress(
  "0x0a662fe5131dcbc531a95f866436e6f586622d1a4dbd03f02e95e9c5504ff467",
);
moveAddress.hex; // "0xa662fe5131dcbc531a95f866436e6f586622d1a4dbd03f02e95e9c5504ff467"
moveAddress.rawHex; // "0a662fe5131dcbc531a95f866436e6f586622d1a4dbd03f02e95e9c5504ff467"
moveAddress.bech32; // "init1pfnzlegnrh9u2vdft7rxgdhx7krxytg6fk7s8upwjh5u25z073ns0k5njh"

// Validate addresses
InitiaAddress.validate("init1wlvk4e083pd3nddlfe5quy56e68atra3gu9xfs"); // true
InitiaAddress.validate("0x77d96ae5e7885B19b5Bf4e680E129ACe8fD58fB1"); // true
InitiaAddress.validate("invalid-address"); // false

// Compare addresses (handles different formats)
InitiaAddress.equals(
  "init1wlvk4e083pd3nddlfe5quy56e68atra3gu9xfs",
  "0x77d96ae5e7885B19b5Bf4e680E129ACe8fD58fB1",
); // true

// Convert to bytes
const address = InitiaAddress("0x1");
address.bytes; // Uint8Array(20)
const address32 = InitiaAddress("0x1", 32);
address32.bytes; // Uint8Array(32)

// Special handling for address 0x1
const specialAddress = InitiaAddress("0x1");
specialAddress.hex; // "0x1"
specialAddress.bech32; // "init1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpqr5e3d"

// Empty or invalid addresses throw errors
try {
  InitiaAddress(""); // throws "address is required"
} catch (e) {
  console.error(e.message);
}

try {
  InitiaAddress("invalid-address"); // throws "invalid address"
} catch (e) {
  console.error(e.message);
}

// Destructuring support
const { bech32, hex, rawHex } = InitiaAddress("0x1");
console.log(bech32); // "init1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpqr5e3d"
console.log(hex); // "0x1"
console.log(rawHex); // "0000000000000000000000000000000000000001"
```

### BCS (Binary Canonical Serialization)

```typescript
import { bcs, resolveBcsType } from "@initia/utils";

// Serialize addresses
const serialized = bcs.address().serialize("0x1").toBase64();
// "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE="

// Serialize various types
bcs.u64().serialize(123);
bcs.string().serialize("Hello, world!");
bcs.vector(bcs.u8()).serialize([1, 2, 3]);

// Special Move types
bcs.fixedPoint32().serialize(1.5); // Fixed-point with 2^32 scaling
bcs.fixedPoint64().serialize(1.5); // Fixed-point with 2^64 scaling
bcs.decimal128().serialize(1.5); // Decimal with 10^18 scaling
bcs.bigdecimal().serialize("123.456"); // Arbitrary precision decimal

// Resolve Move-style type strings dynamically
const addressType = resolveBcsType("0x1::string::String"); // returns bcs.string()
const optionType = resolveBcsType("0x1::option::Option<u64>"); // returns bcs.option(bcs.u64())
```

### Move Module Utilities

```typescript
import { createMoveClient } from "@initia/utils";

// Create a Move client with REST URL
const moveClient = createMoveClient("https://rest.initia.xyz");

// Query Move module view functions with type safety
interface UserData {
  name: string;
  balance: string;
}

const result = await moveClient.viewFunction<UserData>({
  moduleAddress: "0x1",
  moduleName: "module_name",
  functionName: "function_name",
  typeArgs: [], // optional, defaults to []
  args: ["arg1", "arg2"], // optional, defaults to []
});
```

### Object Utilities

```typescript
import {
  createObjectAddress,
  createUserDerivedObjectAddress,
  denomToMetadata,
  getIbcDenom,
  removeLeadingZeros,
} from "@initia/utils";

// Create deterministic object addresses
const objectAddress = createObjectAddress("0x1", "uinit");
// "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"

// Create user-derived object addresses
const derivedAddress = createUserDerivedObjectAddress(
  "0x77d96ae5e7885B19b5Bf4e680E129ACe8fD58fB1",
  "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9",
);
// "0x350045f8766ac3ff7e58ee316786cf1646765f435824345bc9f79d9626c11396"

// Get metadata address for denoms
denomToMetadata("uinit"); // "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"
denomToMetadata("move/0x123...abc"); // "0x123...abc"

// Get IBC denom hash
getIbcDenom(
  "channel-0",
  "l2/771d639f30fbe45e3fbca954ffbe2fcc26f915f5513c67a4a2d0bc1d635bdefd",
);
// "ibc/82EB1C694C571F954E68BFD68CFCFCD6123B0EBB69AAA8BAB7A082939B45E802"

// Remove leading zeros from hex strings
removeLeadingZeros("0x0000123"); // "0x123"
removeLeadingZeros("0000abc"); // "abc"
removeLeadingZeros("0x0"); // "0x0"
```

## API Reference

### Text Utilities

**truncate(str?, lengths?)**

Truncates a string, keeping the beginning and end.

- `str`: `string` - The string to truncate (default: "")
- `lengths`: `[number, number]` - Characters to keep at [start, end] (default: [6, 6])

### Number Formatting

**formatNumber(value, options?)**

Formats a number with thousands separators and decimal places. When `dp` is explicitly specified, trailing zeros are preserved.

- `value`: `number | string | bigint | BigNumber` - The value to format
- `options.dp`: `number` - Decimal places (default: 2). When specified, trailing zeros are preserved
- `options.abbr`: `boolean` - Use abbreviations (K, M, B, T) (default: false)
- `options.fallback`: `string` - Value to return for invalid inputs (default: "")
- `options.roundingMode`: `BigNumber.RoundingMode` - Rounding mode (default: BigNumber.ROUND_DOWN)

**formatAmount(value, options?)**

Formats a blockchain amount, converting from base units to display units. When `dp` is explicitly specified, trailing zeros are preserved.

- `value`: `number | string | bigint | BigNumber` - The value in base units
- `options.decimals`: `number` - Token decimals (default: 0)
- `options.dp`: `number` - Decimal places (default: min(decimals, 6)). When specified, trailing zeros are preserved
- `options.abbr`: `boolean` - Use abbreviations (default: false)
- `options.fallback`: `string` - Value to return for invalid inputs (default: "")
- `options.roundingMode`: `BigNumber.RoundingMode` - Rounding mode (default: BigNumber.ROUND_DOWN)

**formatPercent(value, options?)**

Formats a decimal as a percentage. When `dp` is explicitly specified, trailing zeros are preserved.

- `value`: `number | string | bigint | BigNumber` - The decimal value (0.1 = 10%)
- `options.dp`: `number` - Decimal places (default: 2 for <100%, 0 for ≥100%). When specified, trailing zeros are preserved
- `options.fallback`: `string` - Value to return for invalid inputs (default: "")
- `options.roundingMode`: `BigNumber.RoundingMode` - Rounding mode (default: BigNumber.ROUND_DOWN)

### Unit Conversion

**toBaseUnit(value, options?)**

Converts a display amount to base units.

- `value`: `number | string | bigint | BigNumber` - The display amount
- `options.decimals`: `number` - Token decimals (default: 0)
- `options.fallback`: `string` - Value to return for invalid inputs (default: "")
- `options.roundingMode`: `BigNumber.RoundingMode` - Rounding mode (default: BigNumber.ROUND_DOWN)

**fromBaseUnit(value, options?)**

Converts base units to a display amount with trailing zeros removed.

- `value`: `number | string | bigint | BigNumber` - The amount in base units
- `options.decimals`: `number` - Token decimals (default: 0)
- `options.fallback`: `string` - Value to return for invalid inputs (default: "")
- `options.roundingMode`: `BigNumber.RoundingMode` - Rounding mode (default: BigNumber.ROUND_DOWN)

### Address Utilities

**InitiaAddress(address, byteLength?)**

Creates an InitiaAddress instance. Can be called with or without the `new` keyword.

- `address`: `string` - The address (hex or bech32) to convert (required)
- `byteLength`: `number` - Target byte length for the bytes property (default: 20)
- Throws: `Error` - "address is required" for empty addresses, "invalid address" for invalid addresses

Instance Properties:

- `bech32`: `string` - The address in bech32 format
- `hex`: `string` - The address in hex format (checksummed for 20-byte, leading zeros stripped for 32-byte)
- `rawHex`: `string` - The address in raw hex format (lowercase, no prefix, full representation)
- `bytes`: `Uint8Array` - The address as bytes (length determined by constructor parameter)

**InitiaAddress.validate(address)**

Validates an address.

- `address`: `string` - The address to validate
- Returns: `boolean` - false if address is empty or invalid, true if valid

**InitiaAddress.equals(address1, address2)**

Compares two addresses regardless of format.

- `address1`: `string` - First address
- `address2`: `string` - Second address
- Returns: `boolean` - true if addresses are equal, handles empty/invalid addresses gracefully

### BCS Serialization

**bcs**

Extended BCS serializer that includes all standard types from `@mysten/bcs` plus:

- `bcs.address()` - 32-byte address serializer
- `bcs.object()` - Alias for address serializer
- `bcs.fixedPoint32()` - Fixed-point number with 2^32 scaling
- `bcs.fixedPoint64()` - Fixed-point number with 2^64 scaling
- `bcs.decimal128()` - 128-bit decimal with 10^18 scaling
- `bcs.decimal256()` - 256-bit decimal with 10^18 scaling
- `bcs.bigdecimal()` - Arbitrary precision decimal

**resolveBcsType(typeStr)**

Resolves Move-style type strings to BCS types.

- `typeStr`: `string` - Move type string (e.g., "0x1::string::String")
- Returns: `BcsType` - Corresponding BCS type

### Move Module Utilities

**createMoveClient(restUrl)**

Creates a Move client instance for querying view functions.

- `restUrl`: `string` - The REST API base URL
- Returns: `MoveClient` - A client instance with methods for querying Move modules

**MoveClient.viewFunction(params)**

Queries Move module view functions via REST API.

- `params.moduleAddress`: `string` - The Move module address
- `params.moduleName`: `string` - The Move module name
- `params.functionName`: `string` - The view function name
- `params.typeArgs`: `string[]` - Type arguments (optional, default: [])
- `params.args`: `string[]` - Function arguments (optional, default: [])
- Returns: `Promise<T>` - The parsed response data
- Throws: `Error` - If the response contains an error message
- Throws: `HTTPError` - If the HTTP request fails (original ky error)

### Object Utilities

**createObjectAddress(source, seed)**

Creates a deterministic object address.

- `source`: `string` - Source address
- `seed`: `string` - Seed string
- Returns: `string` - Object address (hex)

**createUserDerivedObjectAddress(source, derivedFrom)**

Creates a user-derived object address.

- `source`: `string` - Source address
- `derivedFrom`: `string` - Address to derive from
- Returns: `string` - Derived object address (hex)

**denomToMetadata(denom)**

Gets the metadata address for a denomination.

- `denom`: `string` - The denomination
- Returns: `string` - Metadata address

**getIbcDenom(channelId, denom)**

Calculates the IBC denom hash.

- `channelId`: `string` - IBC channel ID
- `denom`: `string` - Original denomination
- Returns: `string` - IBC denom (e.g., "ibc/HASH...")

**removeLeadingZeros(hex)**

Removes leading zeros from a hex string while preserving the 0x prefix if present.

- `hex`: `string` - The hex string to process
- Returns: `string` - The hex string without leading zeros

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.

## License

MIT