npm package: @spree-finance/spree-evm-sdk
Description: TypeScript SDK for building unsigned transactions for Spree Finance contracts
Author: Spree Finance
Latest version: 0.2.0

## SPREE FINANCE EVM SDK
<p align="center">
  <img src="media/spree_logo.svg" alt="Spree Finance Logo" height=250 width="400" />
</p>

[![npm version](https://img.shields.io/npm/v/@spree-finance/spree-evm-sdk.svg)](https://www.npmjs.com/package/@spree-finance/spree-evm-sdk)
[![npm downloads](https://img.shields.io/npm/dm/@spree-finance/spree-evm-sdk.svg)](https://www.npmjs.com/package/@spree-finance/spree-evm-sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.3+-blue.svg)](https://www.typescriptlang.org/)

TypeScript SDK for building unsigned transactions for Spree Protocol smart contracts on Base and MOCA Devnet. Supports brand hierarchies (Spree parent, Airsp branded child) and multi-network deployments.

## Features

- **Transaction Building**: Build unsigned legacy transactions for offline/hardware wallet signing
- **Read Operations**: Execute `eth_call` for view functions without building transactions
- **Type Safety**: Full TypeScript support with branded `Hex` types
- **Gas Overrides**: Customize gas parameters per transaction
- **Nonce Management**: Automatic nonce handling with optional caching for batch operations
- **Error Handling**: Typed errors for RPC and network failures

## What's Included

The npm package includes:

- **Built Code**: ESM (`dist/index.js`) and CommonJS (`dist/index.cjs`) builds
- **Type Definitions**: Full TypeScript declarations (`dist/index.d.ts`)
- **Contract ABIs**: JSON ABI files for all supported contracts (`contracts/`)

Package size: ~89 KB compressed, ~821 KB unpacked

## Installation

```bash
npm install @spree-finance/spree-evm-sdk
```

Or with Yarn:

```bash
yarn add @spree-finance/spree-evm-sdk
```

Or with pnpm:

```bash
pnpm add @spree-finance/spree-evm-sdk
```

### Peer Dependencies

This SDK requires `ethers` v6 as a peer dependency:

```bash
npm install ethers@^6.0.0
```

### Requirements

- Node.js >= 18.0.0
- TypeScript >= 5.3 (for TypeScript projects)
- ESM support (NodeNext module resolution)

## Integrations

For end-to-end examples (backend-built unsigned tx → wallet signing → broadcasting), see:

- [**Examples**](#examples) - Runnable examples with code snippets below
- [`examples/`](./examples/) - Full examples directory with test suite
- [`INTEGRATION.md`](./INTEGRATION.md) - Integration patterns

## Documentation

- [`AGENTS.md`](./AGENTS.md) - Agent/AI development guide
- [`CLAUDE.md`](./CLAUDE.md) - Claude Code development guide
- [`examples/README.md`](./examples/README.md) - Examples documentation
- [`MIGRATION.md`](./MIGRATION.md) - Migration guide for breaking changes

## Breaking Changes (v0.2.0)

**⚠️ Important**: Version 0.2.0 includes breaking changes to `PendingSPVault`:

1. **`createCampaign()` signature changed** - Now requires 3 additional parameters for supply caps:
   ```typescript
   // New signature (v0.2.0)
   await vault.createCampaign(
     campaignId, spBudget, destinations, whitelist, metadata,
     maxTotalSupply,    // NEW: 0 = unlimited
     maxMintPerUser,    // NEW: 0 = unlimited
     dailyMintLimit     // NEW: 0 = unlimited
   );
   ```

2. **`Campaign` interface expanded** - 10 new fields added (supply caps, fee tracking, metrics)

3. **Backward compatibility** - Use `createCampaignLegacy()` during migration (deprecated, removed in v1.0.0)

See [MIGRATION.md](./MIGRATION.md) for detailed migration instructions.

## Quick Start

### Base Sepolia (Primary Testnet)

```typescript
import { SpreeVault4626 } from '@spree-finance/spree-evm-sdk';

// Initialize the vault wrapper
const vault = new SpreeVault4626({
  rpcUrl: 'https://sepolia.base.org',
  chainId: 84532n,
  contractAddress: '0x...',
  from: '0x...', // Your wallet address
  defaultGasPriceWei: 1_000_000_000n, // 1 gwei
  defaultGasLimit: 500_000n,
});

// Read contract state (uses eth_call)
const balance = await vault.readBalanceOf('0x...');
const totalAssets = await vault.readTotalAssets();
const vaultHealth = await vault.readVaultHealth();

// Build unsigned transaction for signing
const unsignedTx = await vault.deposit(1_000_000n, '0x...');

// Sign with your wallet/hardware device and broadcast
const signedTx = await wallet.signTransaction(unsignedTx);
const txHash = await provider.sendTransaction(signedTx);
```

### MOCA Devnet (Brand Deployments)

```typescript
import {
  PendingSPVault,
  getContractsForBrand,
  MOCA_DEVNET_CONFIG,
} from '@spree-finance/spree-evm-sdk';

// Get brand-specific contract addresses
const spreeContracts = getContractsForBrand('spree', 5151n);
// Returns: { FACTORY, POINTS, COLLATERAL_VAULT, PENDING_SP_VAULT }

const airspContracts = getContractsForBrand('airsp', 5151n);
// Returns: { FACTORY, POINTS, COLLATERAL_VAULT, PENDING_SP_VAULT }

// Initialize with brand-specific address
const pendingSP = new PendingSPVault({
  rpcUrl: MOCA_DEVNET_CONFIG.rpcUrl,
  chainId: MOCA_DEVNET_CONFIG.chainId,
  contractAddress: spreeContracts!.PENDING_SP_VAULT,
  from: userAddress,
  defaultGasPriceWei: 1_000_000_000n,
  defaultGasLimit: 500_000n,
});

// Create campaign on MOCA Devnet
const campaignTx = await pendingSP.createCampaign(
  campaignId,
  spBudget,
  [settlementDestination],
  [transferWhitelist],
  metadata,
  0n, // maxTotalSupply (0 = unlimited)
  0n, // maxMintPerUser (0 = unlimited)
  0n  // dailyMintLimit (0 = unlimited)
);
```

### MOCA Testnet (Spree Deployments)

```typescript
import {
  PendingSPVault,
  getContractsForBrand,
  MOCA_TESTNET_CONFIG,
} from '@spree-finance/spree-evm-sdk';

// Get Spree contract addresses on MOCA Testnet
const spreeContracts = getContractsForBrand('spree', 222888n);
// Returns: { FACTORY, POINTS, COLLATERAL_VAULT, PENDING_SP_VAULT }

// Initialize with testnet configuration
const pendingSP = new PendingSPVault({
  rpcUrl: MOCA_TESTNET_CONFIG.rpcUrl,
  chainId: MOCA_TESTNET_CONFIG.chainId,
  contractAddress: spreeContracts!.PENDING_SP_VAULT,
  from: userAddress,
  defaultGasPriceWei: 1_000_000_000n,
  defaultGasLimit: 500_000n,
});

// Create campaign on MOCA Testnet
const campaignTx = await pendingSP.createCampaign(
  campaignId,
  spBudget,
  [settlementDestination],
  [transferWhitelist],
  metadata,
  0n, // maxTotalSupply (0 = unlimited)
  0n, // maxMintPerUser (0 = unlimited)
  0n  // dailyMintLimit (0 = unlimited)
);
```

## Examples

The `examples/` directory contains 20+ runnable examples demonstrating best practices for using the SDK. These examples show how to build unsigned transactions and perform read-only operations.

### Setup

```bash
cd examples
npm install

# Configure environment
cp .env.example .env
# Edit .env with your values (RPC URL, private key, contract addresses)
```

### Getting Started

#### 1. Read Contract State (Query without transactions)

Query vault metrics using `eth_call` - no transaction needed:

```typescript
import { SpreeVault4626 } from '@spree-finance/spree-evm-sdk';

const vault = new SpreeVault4626({
    rpcUrl: 'https://sepolia.base.org',
    chainId: 84532n,
    contractAddress: '0x...',
    from: '0x...',
    defaultGasPriceWei: 1_000_000_000n,
    defaultGasLimit: 500_000n,
});

// Query vault health metrics
const health = await vault.readVaultHealth();
console.log(`Total Assets: ${health.totalAssets}`);
console.log(`Utilization: ${health.utilization}%`);
console.log(`Is Balanced: ${health.isBalanced}`);

// Run: npm run read:vault-health
```

#### 2. Build Unsigned Transaction (SDK's core purpose)

Build an unsigned deposit transaction for later signing:

```typescript
import { SpreeVault4626 } from '@spree-finance/spree-evm-sdk';

const vault = new SpreeVault4626(config);

// Build unsigned transaction (no signing, no broadcasting)
const unsignedTx = await vault.deposit(1_000_000n, receiverAddress);

// unsignedTx is a hex string ready for:
// - Hardware wallet signing
// - Software wallet signing
// - Storage for later signing
// - Audit before execution

// Run: npm run tx:build-deposit
```

#### 3. Complete Transaction Flow (Build → Sign → Broadcast)

Using ethers v6 to sign and broadcast SDK-built transactions:

```typescript
import { SpreeVault4626 } from '@spree-finance/spree-evm-sdk';
import { Wallet, JsonRpcProvider } from 'ethers';

const vault = new SpreeVault4626(config);
const provider = new JsonRpcProvider(process.env.RPC_URL);
const wallet = new Wallet(process.env.PRIVATE_KEY, provider);

// Step 1: SDK builds unsigned transaction
const unsignedTx = await vault.deposit(1_000_000n, receiverAddress);

// Step 2: Sign with ethers
const signedTx = await wallet.signTransaction(unsignedTx);

// Step 3: Broadcast to network
const txResponse = await provider.broadcastTransaction(signedTx);
console.log(`Transaction hash: ${txResponse.hash}`);

// Run: npm run tx:sign-broadcast
```

#### 4. Advanced Multi-Step Workflow (Campaign creation)

Create a Pending SP campaign with token approvals and boost configuration:

```typescript
import { PendingSPVault, BoostEngine, ERC20 } from '@spree-finance/spree-evm-sdk';

const spToken = new ERC20(config);
const pendingSP = new PendingSPVault(config);
const boostEngine = new BoostEngine(config);

// Step 1: Approve SP tokens for campaign
const approveTx = await spToken.approve(vaultAddress, budget);
const signedApprove = await signTransaction(approveTx);
await broadcastTransaction(signedApprove);

// Step 2: Create campaign
const createTx = await pendingSP.createCampaign(
    campaignId,
    spBudget,
    [settlementDestination],
    [transferWhitelist],
    metadata
);

// Step 3: Configure boost multipliers
const boostTx = await boostEngine.setNamespaceMultipliers(
    namespace,
    [100n, 125n, 150n, 200n, 250n] // Tier 0-4 multipliers
);

// Step 4: Activate campaign
const activateTx = await pendingSP.setCampaignActive(campaignId, true);

// Run: npm run pending-sp:create-campaign
```

### All Examples

| Category | Command | Description |
|----------|---------|-------------|
| **Read Operations** | | |
| | `npm run read:vault-health` | Query vault metrics (total assets, utilization, health) |
| | `npm run read:balances` | Check USDC, SP token, and vault share balances |
| | `npm run read:campaign-info` | View Pending SP campaign details and budget |
| | `npm run read:user-status` | Query user tier, status, and boost multiplier |
| | `npm run read:boost-multiplier` | Check boost multipliers for reward calculation |
| **Transactions** | | |
| | `npm run tx:build-deposit` | Build unsigned deposit transaction |
| | `npm run tx:sign-broadcast` | Complete flow: build, sign, and broadcast |
| | `npm run tx:batch-nonce` | Batch multiple transactions with nonce caching |
| **Pending SP Vault** | | |
| | `npm run pending-sp:create-campaign` | Create campaign with boost integration |
| | `npm run pending-sp:create-basic` | Simple campaign creation |
| | `npm run pending-sp:create-and-mint` | Create campaign and mint tokens |
| | `npm run pending-sp:mint` | Mint pSP tokens to users |
| | `npm run pending-sp:mint-boosted` | Mint with automatic boost calculation |
| | `npm run pending-sp:expire` | Expire campaign tokens |
| | `npm run pending-sp:fund-campaign` | Fund campaign with SP tokens |
| | `npm run pending-sp:user-story` | Complete user workflow demonstration |
| **Factory** | | |
| | `npm run factory:mint` | Mint SP tokens through factory |
| **Rewards** | | |
| | `npm run rewards:claim` | Claim all available rewards in batch |

See [`examples/README.md`](./examples/README.md) for detailed documentation on each example.

## Contract Wrappers

### SpreeVault4626

ERC-4626 compliant yield vault with harvest/rebalance functionality.

```typescript
import { SpreeVault4626 } from '@spree-finance/spree-evm-sdk';

const vault = new SpreeVault4626(config);

// ERC-4626 Core Operations
await vault.deposit(assets, receiver);
await vault.mint(shares, receiver);
await vault.withdraw(assets, receiver, owner);
await vault.redeem(shares, receiver, owner);

// Read Functions
const shares = await vault.readConvertToShares(assets);
const assets = await vault.readConvertToAssets(shares);
const health = await vault.readVaultHealth();

// Strategy Operations
await vault.harvest();
await vault.rebalance();
await vault.emergencyWithdrawAll();

// Admin Functions
await vault.setFees(mintFeeBps, redeemFeeBps, feeReceiver);
await vault.setAdapter(adapterAddress);
await vault.updateParameters(minIdleBufferBps, maxUtilizationBps);
```

### BonusRewardsVault

Epoch-based bonus reward distribution vault.

```typescript
import { BonusRewardsVault } from '@spree-finance/spree-evm-sdk';

const rewards = new BonusRewardsVault(config);

// Claim Rewards
await rewards.claimBonus(epochId);
await rewards.claimBonusBatch([1n, 2n, 3n]);

// Read Functions
const currentEpoch = await rewards.readGetCurrentEpochId();
const claimable = await rewards.readGetClaimableEpochs(user, 10n);
const estimate = await rewards.readBonusEstimateForEpoch(user, epochId);

// TVL Snapshots
await rewards.takeTVLSnapshot();
const history = await rewards.readGetTVLHistory();

// Partner Management
await rewards.registerPartner(partnerAddress);
const revenue = await rewards.readGetPartnerRevenue(partner);
```

### Factory

Vault creation and mint/redeem operations.

```typescript
import { Factory } from '@spree-finance/spree-evm-sdk';

const factory = new Factory(config);

// Vault Management
await factory.createVault(asset, assetToSharesRate);
await factory.pauseVault(asset);
await factory.unpauseVault(asset);

// Mint/Redeem
await factory.mint(asset, amount, receiver, expectBasketMode);
await factory.requestToRedeem(asset, pointsAmount, receiver, expectBasketMode);
await factory.finalizeRedeem(account);

// Configuration
await factory.setMintRate(asset, rate);
await factory.setRedeemRate(asset, rate);
await factory.setGlobalCap(limit);
```

### Points

ERC-20 points token with TWAB tracking.

```typescript
import { Points } from '@spree-finance/spree-evm-sdk';

const points = new Points(config);

// ERC-20 Operations
await points.transfer(to, amount);
await points.approve(spender, amount);
const balance = await points.readBalanceOf(owner);

// Admin Functions
await points.mint(to, amount);
await points.burn(from, amount);
await points.addToTransferWhitelist(account);
```

### PythPriceOracle

Pyth Network price oracle integration.

```typescript
import { PythPriceOracle } from '@spree-finance/spree-evm-sdk';

const oracle = new PythPriceOracle(config);

// Read Prices
const price = await oracle.readGetPrice(asset);
const priceWithAge = await oracle.readGetPriceNoOlderThan(asset, maxAge);

// Configuration
await oracle.setPriceFeed(asset, pythFeedId);
await oracle.setPythSource(pythOracleAddress);
```

### PendingSPVault

Conditional reward token system with campaign-scoped budgets and programmable settlement.

```typescript
import { PendingSPVault } from '@spree-finance/spree-evm-sdk';

const pendingSP = new PendingSPVault(config);

// Campaign Management
await pendingSP.createCampaign(
  campaignId,
  spBudget,
  settlementDestinations,
  transferWhitelist,
  metadata
);
await pendingSP.fundCampaign(campaignId, amount);
await pendingSP.setCampaignActive(campaignId, true);

// Token Operations
await pendingSP.mint(campaignId, to, amount, metadata);
await pendingSP.settle(from, to, amount, campaignId, metadata);
await pendingSP.expire(from, amount, campaignId, metadata);
await pendingSP.transferWithCampaign(to, amount, campaignId);

// Read Functions
const campaign = await pendingSP.readGetCampaign(campaignId);
const balance = await pendingSP.readBalanceOfCampaign(user, campaignId);
const availableBudget = await pendingSP.readGetCampaignAvailableBudget(campaignId);
const userCampaigns = await pendingSP.readGetUserCampaigns(user);
```

#### Mint with Boost

Use `mintBoosted()` for automatic tier-based boost calculation and application:

```typescript
import { PendingSPVault } from '@spree-finance/spree-evm-sdk';

const pendingSP = new PendingSPVault(config);

// Standard mint (no boost)
await pendingSP.mint(campaignId, to, amount, metadata);

// Mint with automatic boost calculation
// Contract calculates boost based on user's tier and mints base + boost amount
await pendingSP.mintBoosted(campaignId, to, amount, metadata);
```

The `mintBoosted()` function:
- Calculates boost amount using the configured BoostEngine
- Mints `baseAmount + boostAmount` in a single transaction
- Emits both `PspMinted` and `BoostedMint` events
- Respects campaign budget with the total (base + boost) amount

#### Complete Campaign Workflow

Full campaign lifecycle with nonce management for batch operations:

```typescript
import { PendingSPVault, ERC20 } from '@spree-finance/spree-evm-sdk';

const spToken = new ERC20(erc20Config);
const pendingSP = new PendingSPVault(config);

// Enable nonce caching for sequential transactions
pendingSP.enableNonceCache();
spToken.enableNonceCache();

// Step 1: Approve SP tokens
const approveTx = await spToken.approve(vaultAddress, budget);
const signedApprove = await signTransaction(approveTx);
await broadcastTransaction(signedApprove);

// Step 2: Create campaign
const createTx = await pendingSP.createCampaign(
  campaignId,
  spBudget,
  [settlementDestination],
  [transferWhitelist],
  metadata
);

// Step 3: Fund campaign
const fundTx = await pendingSP.fundCampaign(campaignId, spBudget);

// Step 4: Set campaign admin and minter
await pendingSP.setCampaignAdmin(campaignId, adminAddress, true);
await pendingSP.setMinter(campaignId, minterAddress, mintAllowance, true);

// Step 5: Activate campaign
const activateTx = await pendingSP.setCampaignActive(campaignId, true);

// Disable nonce caching when done
pendingSP.disableNonceCache();
spToken.disableNonceCache();
```

See the [examples/pending-sp/](./examples/src/pending-sp/) directory for complete implementations including:
- `create-campaign.ts` - Full campaign creation with boost integration
- `create-and-mint.ts` - Create, fund, mint, and settle workflow
- `mint-boosted.ts` - Mint with automatic boost calculation
- `user-story.ts` - Complete user journey demonstration
- `expire.ts` - Token expiration handling

## Brand Configuration

The SDK supports multiple brands with parent-child relationships:

### Available Brands

- **spree**: Root/parent brand
- **airsp**: Branded child of Spree

### Supported Networks

- **Base Sepolia** (chainId: 84532) - Ethereum testnet
- **MOCA Devnet** (chainId: 5151) - Spree/Airsp deployments (active)
- **MOCA Testnet** (chainId: 222888) - Spree deployments (active)

### Using Brand Configuration

```typescript
import {
  getBrandConfig,
  getContractsForBrand,
  getChildBrands,
  ContractRegistry,
} from '@spree-finance/spree-evm-sdk';

// Get brand configuration
const spreeConfig = getBrandConfig('spree');
const airspConfig = getBrandConfig('airsp');

// Get contracts for brand on specific chain
const spreeContracts = getContractsForBrand('spree', 5151n);
// Returns: { FACTORY, POINTS, COLLATERAL_VAULT, PENDING_SP_VAULT }

// Get child brands
const spreeChildren = getChildBrands('spree'); // [airspConfig]

// Use ContractRegistry for brand hierarchy
const registry = new ContractRegistry({ apiBaseUrl: '...' });
const hierarchy = registry.getContractsForBrandHierarchy('spree', 5151);
// Returns: { parent: {...}, children: [{...}] }

// Get specific vault addresses
const pendingSPAddress = registry.getPendingSPVaultAddress('airsp', 5151);
const collateralVaultAddress = registry.getCollateralVaultAddress('spree', 5151);
```

### SpreeStatusRegistry

User tier and status management system with TWAB integration.

```typescript
import { SpreeStatusRegistry } from '@spree-finance/spree-evm-sdk';

const statusRegistry = new SpreeStatusRegistry(config);

// Read User Status
const tier = await statusRegistry.readGetTier(user, namespace);
const status = await statusRegistry.readGetStatus(user, namespace);
const multiplier = await statusRegistry.readGetBoostMultiplier(user, namespace);

// Tier Configuration (admin)
await statusRegistry.createNamespace(namespace, admin);
await statusRegistry.setTierConfig(namespace, tierLevel, {
  spThreshold: 1000_000_000_000n,
  holdingPeriod: 2592000n,
  boostMultiplier: 125n,
});
await statusRegistry.setMaxTier(namespace, 5);

// Tier Updates
await statusRegistry.updateTier(user, namespace);
await statusRegistry.batchUpdateTiers([user1, user2], namespace);

// Oracle/Operator Functions
await statusRegistry.setUserTier(user, namespace, 3, 100n);
await statusRegistry.setTierByOracle(user, namespace, 2, 50n);
```

### BoostEngine

Tier-based reward boosting system with campaign-specific configurations.

```typescript
import { BoostEngine } from '@spree-finance/spree-evm-sdk';

const boostEngine = new BoostEngine(config);

// Read Boost Information
const multiplier = await boostEngine.readGetMultiplier(user, namespace);
const tierMultiplier = await boostEngine.readGetTierMultiplier(namespace, tier);
const boostedAmount = await boostEngine.readCalculateBoostedReward(
  user,
  namespace,
  baseAmount
);

// Configure Namespace Multipliers (admin)
await boostEngine.setNamespaceMultipliers(namespace, [
  100n, // Tier 0
  125n, // Tier 1
  150n, // Tier 2
  200n, // Tier 3
  250n, // Tier 4
]);

// Configure Campaign Boosts (configurator)
await boostEngine.setCampaignBoost(campaignId, {
  baseMultiplier: 100n,
  earlyBirdBonus: 10n,
  earlyBirdCount: 100n,
  loyaltyBonus: 5n,
  referralBonus: 5n,
  maxPerUser: 10_000_000_000n,
  cooldownSeconds: 86400n,
});

// Record Boost Application (recorder)
await boostEngine.recordBoostApplication(user, namespace, campaignId);
```

## Advanced Usage

### Gas Overrides

Customize gas parameters per transaction:

```typescript
const tx = await vault.deposit(1_000_000n, receiver, {
  gasLimit: 300_000n,
  gasPriceWei: 2_000_000_000n,
  valueWei: 0n, // For payable functions
});
```

### Nonce Caching

For batch transaction building:

```typescript
// Enable caching to avoid RPC calls per transaction
vault.enableNonceCache();

const tx1 = await vault.deposit(1_000_000n, receiver);
const tx2 = await vault.withdraw(500_000n, receiver, owner);
const tx3 = await vault.harvest();

// All transactions have sequential nonces without extra RPC calls

vault.disableNonceCache();
```

### Manual Nonce Control

```typescript
vault.setNonce(42n); // Start from specific nonce

const tx = await vault.deposit(1_000_000n, receiver);
// Transaction will have nonce 42

vault.resetNonceCache(); // Clear and fetch fresh on next call
```

### Calldata Encoding

Encode function calls without building full transactions:

```typescript
const calldata = vault.encodeCall('deposit', [1_000_000n, receiver]);
// Use for multicall or direct contract interaction
```

### Error Handling

```typescript
import { RpcError, NetworkError } from '@spree-finance/spree-evm-sdk';

try {
  const tx = await vault.deposit(1_000_000n, receiver);
} catch (error) {
  if (error instanceof NetworkError) {
    console.error('Network issue:', error.message);
  } else if (error instanceof RpcError) {
    console.error('RPC error:', error.code, error.message);
  }
}
```

## API Reference

### Configuration

All contract wrappers accept a `BaseContractConfig`:

```typescript
interface BaseContractConfig {
  rpcUrl: string;          // JSON-RPC endpoint URL
  chainId: bigint;         // Chain ID (e.g., 84532n for Base Sepolia)
  contractAddress: Hex;    // Contract address
  from: Hex;               // Sender address for nonce lookup
  defaultGasPriceWei: bigint;  // Default gas price
  defaultGasLimit: bigint;     // Default gas limit
}
```

### Types

```typescript
type Hex = `0x${string}`;  // Branded hex string

interface TxOverrides {
  gasPriceWei?: bigint;
  gasLimit?: bigint;
  valueWei?: bigint;
}
```

## Development

```bash
# Install dependencies
npm install

# Build
npm run build

# Run SDK tests
npm test
npm run test:coverage

# Run example tests
cd examples
npm install
npm test
npm run test:coverage
```

### Pre-publish Checklist

Before publishing to npm, run the full validation:

```bash
npm run prepublishOnly
```

This runs: lint → test → build

See [`AGENTS.md`](./AGENTS.md) for detailed development guidelines and [`CLAUDE.md`](./CLAUDE.md) for Claude Code specific instructions.

## Changelog

See [CHANGELOG.md](./CHANGELOG.md) for version history and release notes.

## License

MIT