Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.boltliquidity.io/llms.txt

Use this file to discover all available pages before exploring further.

A type-safe CosmWasm client for interacting with Bolt’s zero slippage execution layer on Archway. Query oracle prices, inspect pool state, simulate swaps off-chain, and execute trades programmatically.

Quickstart

1

Prerequisites

Before using the SDK, make sure you have:
  • Node.js 18+ installed
  • An Archway-compatible wallet (Keplr, Leap, etc.) for signing transactions
  • Access to Archway RPC endpoints (the client uses https://rpc.mainnet.archway.io for mainnet and https://rpc.constantine.archway.io for testnet by default)
2

Installation

npm install @bolt-liquidity-hq/cosmwasm-client
3

Initialize the Bolt client

The SDK supports different environments:
type Environment = 'mainnet' | 'testnet';
import { BoltCosmWasmClient } from '@bolt-liquidity-hq/cosmwasm-client';

const client = new BoltCosmWasmClient();
4

Query all assets

// Get all supported assets
const assets = await client.getAssets();
assets.forEach((asset) => {
  console.log(`${asset.symbol} (${asset.name}): ${asset.denom}`);
  console.log(`  Decimals: ${asset.decimals}`);
});

// Find a specific asset
const archAsset = assets.find((a) => a.symbol === 'ARCH');
console.log(`ARCH denom: ${archAsset?.denom}`); // "aarch"
5

Execute a swap

import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing';

// Get signer from wallet (implementation depends on wallet)
const signer = await DirectSecp256k1HdWallet.fromMnemonic("my mnemonic goes here", {
  prefix: 'archway',
});

// Execute a swap: exactly 1 ARCH for USDC
const result = await client.swap({
  assetIn: 'aarch',
  amountIn: '1000000000000000000', // 1 ARCH (18 decimals)
  assetOut: 'ibc/43897B9739BD63E3A08A88191999C632E052724AB96BD4C74AE31375C991F48D', // USDC
  minimumAmountOut: '1900000', // Minimum 1.9 USDC (6 decimals)
}, signer);

console.log(`Swapped 1 ARCH for ${result.amountOut} USDC`);
console.log(`Transaction hash: ${result.txHash}`);
console.log(`Gas cost: ${result.txOutput.gasUsed}`);
console.log(`Status: ${result.txOutput.code === 0 ? 'success' : 'failed'}`);

API

Client initialization

import { BoltCosmWasmClient } from '@bolt-liquidity-hq/cosmwasm-client';

// Initialise client
const client = new BoltCosmWasmClient({
  environment: 'mainnet', // 'testnet' | 'mainnet'
  customOverride: {
    chainConfig: {
      rpcEndpoint: 'https://my-custom-rpc-endpoint...', // Remove this if you want to use the default public RPC endpoint
    }
  }
});
import { BoltCosmWasmClient } from '@bolt-liquidity-hq/cosmwasm-client';

// Initialise client with full custom configuration
const customClient = new BoltCosmWasmClient({
  customOverride: {
    chainConfig: {
      id: 'archway-custom-1',
      name: 'Archway Custom',
      rpcEndpoint: 'https://custom-rpc.example.com',
      restEndpoint: 'https://custom-rest.example.com'
    },
    contracts: {
      oracle: 'archway1custom_oracle...',
      router: 'archway1custom_router...'
    },
    nativeTokenDenom: 'aarch',
    assetsConfig: {
      'aarch': {
        symbol: 'ARCH',
        name: 'Archway',
        chainId: 'archway-custom-1',
        denom: 'aarch',
        decimals: 18,
        logo: 'https://example.com/arch.png',
        coingeckoId: 'archway'
      }
    }
  }
});
Environment configuration
ParameterTypeDefaultDescription
environmentmainnet | testnetmainnetNetwork to connect to
Override chain configuration
ParameterTypeDefault (mainnet)Default (testnet)Description
chainConfig.idstringarchway-1constantine-3Chain identifier
chainConfig.namestringArchwayArchway TestnetHuman-readable chain name
chainConfig.rpcEndpointstringhttps://rpc.mainnet.archway.io/https://rpc.constantine.archway.io/RPC endpoint URL
chainConfig.restEndpointstringhttps://api.mainnet.archway.io/https://api.constantine.archway.io/REST endpoint URL
Override Outpost contract addresses
ParameterTypeDescription
contracts.oraclestringOracle contract address
contracts.routerstringRouter contract address
Override native token denomination
ParameterTypeDescription
nativeTokenDenomstringNative token denomination (default: aarch)
Override assets configuration
ParameterTypeDescription
assetsConfigRecord<string, Asset>Custom asset configurations indexed by denomination
type Asset = {
  symbol: string;        // Asset symbol (e.g., 'ARCH', 'USDC')
  name: string;          // Full asset name (e.g., 'Archway', 'Circle USDC')
  chainId: string;       // Chain identifier
  denom: string;         // Asset denomination
  decimals: number;      // Number of decimal places
  logo?: string;         // Optional logo URL
  coingeckoId?: string;  // Optional CoinGecko identifier
};
Override clients
ParameterTypeDescription
cosmWasmClientCosmWasmClient | ArchwayClientPre-existing query client instance for blockchain interactions
signerOfflineSignerPre-existing signer
signingCosmWasmClientSigningCosmWasmClient | SigningArchwayClientPre-existing signing client instance for signing blockchain transactions
import { BoltCosmWasmClient } from '@bolt-liquidity-hq/cosmwasm-client';
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate';

// Create custom CosmWasm client with specific configuration
const customCosmWasmClient = CosmWasmClient.connect('https://custom-rpc.example.com');

const client = new BoltCosmWasmClient({
  cosmWasmClient: customCosmWasmClient
});

Get oracle config

getOracleConfig() retrieves the current configuration settings from the Bolt Oracle smart contract on Archway. This configuration governs how price feeds are managed, including update thresholds, expiration times, and administrative settings.
async getOracleConfig(): Promise<OracleConfig>
Usage example
import { BoltCosmWasmClient } from '@bolt-liquidity-hq/cosmwasm-client';

const client = new BoltCosmWasmClient();

const config = await client.getOracleConfig();

console.log('Oracle Admin:', config.admin);
console.log('Price Threshold:', config.priceThresholdRatio);
console.log('Price Expiry (seconds):', config.priceExpireTime?.secs);
// Cache configuration for performance
let cachedConfig: OracleConfig | null = null;
let lastFetchTime = 0;
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes

async function getCachedOracleConfig() {
  const now = Date.now();

  if (cachedConfig && (now - lastFetchTime) < CACHE_DURATION) {
    return cachedConfig;
  }

  cachedConfig = await client.getOracleConfig();
  lastFetchTime = now;
  return cachedConfig;
}
async function monitorOracleConfig() {
  const config = await client.getOracleConfig();

  setInterval(async () => {
    const newConfig = await client.getOracleConfig();

    if (newConfig.priceThresholdRatio !== config.priceThresholdRatio) {
      console.log('Price threshold changed:', newConfig.priceThresholdRatio);
    }

    if (newConfig.priceExpireTime?.secs !== config.priceExpireTime?.secs) {
      console.log('Price expiry time changed:', newConfig.priceExpireTime?.secs);
    }
  }, 60000); // Check every minute
}

Query prices and assets

getAssets() retrieves all unique assets available in the Bolt execution layer by querying oracle asset pairs and enriching them with additional metadata from the client’s asset configuration.
async getAssets(): Promise<Asset[]>
Usage example
import { BoltCosmWasmClient } from '@bolt-liquidity-hq/cosmwasm-client';

const client = new BoltCosmWasmClient();

const assets = await client.getAssets();

console.log('Available assets:', assets.length);
assets.forEach(asset => {
  console.log(`${asset.symbol}: ${asset.name} (${asset.decimals} decimals)`);
});

// Find a specific asset
const archAsset = assets.find((a) => a.symbol === 'ARCH');
console.log(`ARCH denom: ${archAsset?.denom}`); // "aarch"

getAllOracleAssetPairs() queries the oracle smart contract to retrieve all supported asset pairs with automatic pagination. Each asset pair represents a base/quote relationship that can be used for price queries and swaps.
async getAllOracleAssetPairs(): Promise<OracleAssetPair[]>
Usage example
const assetPairs = await client.getAllOracleAssetPairs();

console.log('Supported trading pairs:', assetPairs.length);
assetPairs.forEach(pair => {
  console.log(`${pair.base.symbol}/${pair.quote.symbol} (${pair.base.precision}/${pair.quote.precision} decimals)`);
});

getAllPrices() queries the oracle smart contract to retrieve all available price feeds with automatic pagination. More efficient than making multiple individual price queries when you need prices for multiple pairs.
async getAllPrices(): Promise<Price[]>
Usage example
const allPrices = await client.getAllPrices();

console.log('Total prices available:', allPrices.length);
allPrices.forEach(price => {
  console.log(`Price: ${price.price}, Expires: ${new Date(Number(price.expiryTime) / 1000000)}`);
});

getPrice() queries the oracle smart contract to retrieve the current price for a specific asset pair. Fetches the latest price feed that is updated by authorized price feeders and validated against configured thresholds.
async getPrice(baseDenom: string, quoteDenom: string): Promise<InvertiblePrice>
Usage example
// Get ARCH/USDC price
const price = await client.getPrice(
  'aarch',
  'ibc/43897B9739BD63E3A08A88191999C632E052724AB96BD4C74AE31375C991F48D' // USDC
);

console.log(`ARCH/USDC Price: ${price.price}`);
console.log(`Expires at: ${new Date(Number(price.expiryTime) / 1000000)}`);
console.log(`Asset Pair: ${price.assetPair}`);

Verify pool liquidity

getPoolBaseLiquidity() queries the router smart contract to retrieve the total base asset liquidity for a specific pool. Fetches current liquidity levels for the base asset in the specified pool.
async getPoolBaseLiquidity(poolContractAddress: string): Promise<BaseLiquidityDetails>
Usage example
import { BoltCosmWasmClient } from '@bolt-liquidity-hq/cosmwasm-client';

const client = new BoltCosmWasmClient();

const poolAddress = "archway1f1e...";
const liquidity = await client.getPoolBaseLiquidity(poolAddress);

console.log(`Base liquidity: ${liquidity.baseLiquidity.amount} ${liquidity.baseLiquidity.denom}`);
console.log(`Total shares: ${liquidity.totalShares}`);

getAllBaseAssetsLiquidity() queries the router smart contract to retrieve the base asset liquidity across all pools. Fetches current liquidity levels for all the base assets in their respective pools.
async getAllBaseAssetsLiquidity(): Promise<Record<Address, BaseLiquidityDetails>>
Usage example
import { BoltCosmWasmClient } from '@bolt-liquidity-hq/cosmwasm-client';

const client = new BoltCosmWasmClient();

const liquidity = await client.getAllBaseAssetsLiquidity();

console.log('Base asset liquidity:', Object.keys(liquidity).length);
Object.entries(liquidity).forEach(([denom, details]) => {
  console.log(`${denom}: ${details.baseLiquidity.amount} (${details.totalShares} shares)`);
});

Get Router contract config

getRouterConfig() queries the router smart contract to retrieve its current configuration settings. The Router is the entry-point contract unique to the Archway deployment that governs how swaps are executed, fees are collected, and new pools are deployed.
async getRouterConfig(): Promise<RouterConfig>
Usage example
const config = await client.getRouterConfig();

console.log('Router Admin:', config.admin);
console.log('Price Oracle:', config.defaultPriceOracleContract);
console.log('Protocol Fee Recipient:', config.defaultProtocolFeeRecipient);
console.log('Protocol Fee:', config.defaultProtocolFee);
console.log('LP Fee:', config.defaultLpFee);

Get liquidity pools information

getAllPools() queries the router smart contract to retrieve information about all deployed pools with automatic pagination. Fetches a comprehensive list of all pools in the Bolt execution layer on Archway.
async getAllPools(): Promise<Pool[]>
Usage example
const allPools = await client.getAllPools();

console.log(`Total pools available: ${allPools.length}`);

// Display all pools and their trading pairs
allPools.forEach((pool, index) => {
  console.log(`\nPool ${index + 1}: ${pool.poolAddress}`);
  console.log(`  Base asset: ${pool.baseDenom}`);
  console.log(`  Quote assets: ${pool.quoteDenoms.length}`);
  pool.quoteDenoms.forEach(quote => {
    console.log(`    - ${quote}`);
  });
});

// Find pools for specific base asset
const archPools = allPools.filter(pool =>
  pool.baseDenom === "aarch"
);
console.log(`ARCH pools found: ${archPools.length}`);

getPoolConfig() retrieves the configuration settings of a liquidity pool contract. Queries the contract to fetch its current configuration parameters, including fees, LP settings, and oracle integration.
async getPoolConfig(contractAddress: Address): Promise<PoolConfig>
Usage example
const poolAddress = "archway1f1e...";
const config = await client.getPoolConfig(poolAddress);

console.log('Pool Configuration:');
console.log('  Price Oracle:', config.priceOracleContract);
console.log('  Protocol Fee:', config.protocolFee);
console.log('  LP Fee:', config.lpFee);
console.log('  Allowance Mode:', config.allowanceMode);
console.log('  Min Base Out:', config.minBaseOut);

getPoolByDenom() queries the router smart contract to retrieve pool information for a specific base asset on Archway. Fetches pool details for the pool matching the provided asset denomination.
async getPoolByDenom(baseDenom: string): Promise<Pool>
Usage example
// Get pool for ATOM base asset
const pool = await client.getPoolByDenom('ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2');

console.log('Pool Address:', pool.poolAddress);
console.log('Base Asset:', pool.baseDenom);
console.log('Quote Assets:', pool.quoteDenoms.length);
pool.quoteDenoms.forEach(quote => {
  console.log(`  - ${quote}`);
});

getPoolConfigByDenom() retrieves pool configuration by base asset denomination. This is a convenience function that combines pool lookup and configuration retrieval.
async getPoolConfigByDenom(baseDenom: string): Promise<PoolConfig>
Usage example
// Get pool configuration for ATOM base asset
const config = await client.getPoolConfigByDenom('ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2');

console.log('Price Oracle:', config.priceOracleContract);
console.log('Protocol Fee Recipient:', config.protocolFeeRecipient);
console.log('Protocol Fee:', config.protocolFee);
console.log('LP Fee:', config.lpFee);
console.log('Allowance Mode:', config.allowanceMode);
console.log('Authorized LPs:', config.lps.length);
console.log('Min Base Out:', config.minBaseOut);

Simulate a swap

simulateSwap() simulates a swap operation to calculate the expected output amount without executing the transaction. Performs a dry run that takes into account current pool conditions, oracle prices, and fees. Use this for accurate estimates before executing.
async simulateSwap(params: SwapParams): Promise<SimulateSwapResult>
Usage example
// Simulate swapping 1 ARCH for USDC
const simulation = await client.simulateSwap({
  assetIn: "aarch",
  amountIn: "1000000000000000000", // 1 ARCH (18 decimals)
  assetOut: "ibc/43897B9739BD63E3A08A88191999C632E052724AB96BD4C74AE31375C991F48D" // USDC
});

console.log(`Pool: ${simulation.poolAddress}`);
console.log(`Expected output: ${simulation.amountOut} ${simulation.assetOut}`);
console.log(`Protocol fee: ${simulation.protocolFee}`);
console.log(`LP fee: ${simulation.lpFee}`);
if (simulation.dynamicFeePercentage) {
  console.log(`Dynamic fee: ${simulation.dynamicFeePercentage}`);
}
console.log(`Total fees: ${simulation.totalFees}`);

Execute a swap

swap() executes a token swap transaction on the Bolt execution layer through the router contract. It performs a “swap exact in” operation where you specify exactly how much of the input asset to swap, and receive a variable amount of the output asset based on current pool conditions.
async swap(params: SwapParams, signer: Signer): Promise<SwapResult<ExecuteResult>>

type SwapParams = {
  assetIn: string;           // Denomination of the asset being sold
  amountIn: string;          // Exact amount of input asset to swap (in minimal units)
  assetOut: string;          // Denomination of the asset being bought
  minimumAmountOut?: string; // Optional minimum acceptable amount of output asset
  receiver?: Address;        // Optional recipient address for swapped assets
};
Usage examples
import { BoltCosmWasmClient } from '@bolt-liquidity-hq/cosmwasm-client';
import { OfflineSigner } from '@cosmjs/proto-signing';

const client = new BoltCosmWasmClient();

const signer: OfflineSigner = // ... obtain signer from wallet or mnemonic

try {
  const result = await client.swap({
    assetIn: "aarch",
    amountIn: "1000000000000000000", // 1 ARCH (18 decimals)
    assetOut: "ibc/43897B9739BD63E3A08A88191999C632E052724AB96BD4C74AE31375C991F48D", // USDC IBC denom
    minimumAmountOut: '1900000', // Minimum 1.9 USDC (6 decimals)
    receiver: "archway1..." // Optional custom receiver
  }, signer);

  console.log('Swap successful!');
  console.log(`Transaction hash: ${result.txHash}`);
  console.log(`Received: ${result.amountOut} ${result.assetOut}`);
} catch (error) {
  console.error('Swap failed:', error.message);
}
import { BoltCosmWasmClient } from '@bolt-liquidity-hq/cosmwasm-client';
import { OfflineSigner } from '@cosmjs/proto-signing';

const client = new BoltCosmWasmClient();

const signer: OfflineSigner = // ... obtain signer from wallet or mnemonic

const minimumAmountOut = "1900000"; // Minimum 1.9 USDC (6 decimals)

try {
  const result = await client.swap({
    assetIn: "aarch",
    amountIn: "1000000000000000000", // 1 ARCH (18 decimals)
    assetOut: "ibc/43897B9739BD63E3A08A88191999C632E052724AB96BD4C74AE31375C991F48D", // USDC IBC denom
    minimumAmountOut
  }, signer);

  console.log(`Minimum: ${minimumAmountOut} USDC`);
  console.log(`Received: ${result.amountOut} USDC`);
} catch (error) {
  console.error('Swap failed:', error.message);
}
import { BoltCosmWasmClient } from '@bolt-liquidity-hq/cosmwasm-client';
import { OfflineSigner } from '@cosmjs/proto-signing';

const client = new BoltCosmWasmClient();

const signer: OfflineSigner = // ... obtain signer from wallet or mnemonic

const receiver = "archway1e1f1..."; // Custom recipient

try {
  const result = await client.swap({
    assetIn: "aarch",
    amountIn: "1000000000000000000", // 1 ARCH (18 decimals)
    assetOut: "ibc/43897B9739BD63E3A08A88191999C632E052724AB96BD4C74AE31375C991F48D", // USDC IBC denom
    receiver
  }, signer);

  console.log('Swap successful!');
  console.log(`Transaction hash: ${result.txHash}`);
  console.log(`Sent: ${result.amountOut} ${result.assetOut} to ${receiver}`);
} catch (error) {
  console.error('Swap failed:', error.message);
}

Best practices

// Always set minimumAmountOut to protect against price variations
const calculateMinimumOutput = (
  amountIn: string,
  price: string,
  priceSlippageTolerance: number = 0.01
) => {
  const expectedOutput = BigNumber(amountIn).multipliedBy(price);

  const slippageMultiplier = new BigNumber(1).minus(priceSlippageTolerance);

  return expectedOutput
    .multipliedBy(slippageMultiplier)
    .toFixed(0, BigNumber.ROUND_DOWN); // return as integer string
};

const price = await client.getPrice(assetIn, assetOut);
const minimumAmountOut = calculateMinimumOutput(amountIn, price.price, 0.01); // 1% slippage

const result = await client.swap({
  assetIn,
  amountIn,
  assetOut,
  minimumAmountOut
}, signer);
async function robustSwap(signer: Signer, params: SwapParams, maxRetries: number = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      console.log(`Swap attempt ${attempt}/${maxRetries}`);

      const result = await client.swap(params, signer);

      console.log(`Swap successful on attempt ${attempt}`);
      return result;

    } catch (error) {
      console.error(`Attempt ${attempt} failed:`, error.message);

      if (attempt === maxRetries) {
        throw new Error(`Swap failed after ${maxRetries} attempts: ${error.message}`);
      }

      // Wait before retry (exponential backoff)
      const delay = Math.pow(2, attempt) * 1000;
      console.log(`Waiting ${delay}ms before retry...`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}
import { OfflineSigner } from '@cosmjs/proto-signing';

const signer: OfflineSigner = // ... obtain signer from wallet or mnemonic
const result = await client.estimateSwapGasFees(
  {
    assetIn: "aarch",
    amountIn: "1000000000000000000", // 1 ARCH (18 decimals)
    assetOut: "ibc/43897B9739BD63E3A08A88191999C632E052724AB96BD4C74AE31375C991F48D", // USDC IBC denom
  },
  signer
);

if (result.amount > maxGasBudget) {
  throw new Error('Gas cost exceeds budget');
}

Error handling

Error

Cause

Fix

NotFoundErrorAsset, pool, or oracle not foundVerify denoms and pool addresses
InvalidParameterErrorBad address or negative amountValidate inputs before calling
InvalidObjectErrorObject does not exist on-chainConfirm addresses and network
InvalidTypeErrorType mismatch in paramsEnsure denoms and amounts are strings
MissingParameterErrorRequired field omittedProvide all non-optional fields
TransactionFailedErrorOn-chain execution failedCheck gas, inventory, oracle freshness
ParseErrorFailed to deserialise dataRetry or switch RPC endpoint

Archway Outpost Addresses

Contract addresses for mainnet and testnet.

Contract Math

Understand the pricing formula behind simulateSwap().

Pool State Indexing

Index pool state for real-time integration.

Contact The Team

Questions about routing, integration, or execution strategy? Reach out.