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

@avail-project/nexus-core

v3.1.0

Published

Nexus headless SDK for cross-chain transactions

Readme

@avail-project/nexus-core

Headless TypeScript SDK for cross-chain swaps, same-asset bridging, wallet balances, and EVM contract execution. Bring your own wallet connection and user interface.

Install and connect

npm install @avail-project/nexus-core
import { createNexusClient, type EthereumProvider } from '@avail-project/nexus-core';
import { formatUnits, parseUnits } from '@avail-project/nexus-core/utils';

const client = createNexusClient({ clientId: 'your-app-name', network: 'mainnet' });
await client.initialize();

// Use the EIP-1193 provider supplied by your wallet connector.
declare const provider: EthereumProvider;
await client.setEVMProvider(provider);

clientId is a required, non-empty, stable application identifier. Catalog queries and exact-input quote previews need initialization but no wallet. Execution and wallet balances need a connected provider. Recreate the client after account/provider changes and call client.destroy() on cleanup.

mainnet and canary use mainnet chains. testnet supports standalone execution only; intent operations and async intent catalog helpers reject with ENVIRONMENT_NOT_SUPPORTED.

Choose an operation

| Need | Method | | --- | --- | | Receive a specified destination amount | swapWithExactOut | | Spend specified source amounts | swapWithExactIn | | Bridge the same asset across chains | Either swap method, with that asset's address on each chain | | Fund a destination contract call, then execute it | swapAndExecute | | Execute using funds already on the destination | execute | | Estimate a standalone contract call | simulateExecute |

Amounts are raw bigint values. Resolve tokens by chain ID and contract address, then use their own decimals with parseUnits / formatUnits. Native-token addresses come from catalog metadata. Routes, providers, and fees are determined by the quote.

Discover assets and balances

const chains = client.getSupportedChains();
const swapChains = chains.filter((chain) => chain.capabilities.intent);
const executeChains = chains.filter((chain) => chain.capabilities.execute);

const page = await client.getTokensByChain(8453, { symbol: 'USDC', limit: 25 });
const baseUsdc = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
const destination = { chainId: 8453, tokenAddress: baseUsdc } as const;
const token = await client.getToken(destination);
const amountRaw = parseUnits('10', token.decimals);

const balances = await client.getBalances();
for (const balance of balances) {
  console.log(balance.symbol, formatUnits(balance.usableBalanceRaw, balance.decimals), balance.usable);
}

Balances expose actualBalanceRaw (full wallet balance) and usableBalanceRaw (available for routing after gas reserves) as bigint. balanceRaw is a deprecated alias of usableBalanceRaw. valueUsd values the usable balance and is null when no price is available.

| Catalog method | Returns | | --- | --- | | getSupportedChains() | Cached chain metadata, without token lists | | getTokens(query?) | Token page across chains | | getTokensByChain(chainId, query?) | Token page on one chain | | getToken({ chainId, tokenAddress }) | Exact token metadata | | getAvailableSourceTokens(destination, selectedSources?, query?) | Page of provider groups containing chains and tokens | | getAvailableDestinationTokens(sources, query?) | Page of chains containing destination tokens | | confirmRouteExists(sources, destination) | Catalog compatibility as a boolean | | getSupportedChainsForRoute(constraints) | Chain metadata matching route constraints |

Token queries accept chainId, providers, name, symbol, contract, includeUnverified, offset, and limit. Filters combine with AND. Pages default to offset 0 and limit 50; the maximum limit is 1000. Token pages default to verified assets; opt in with includeUnverified: true. Exact lookup can resolve unverified tokens. Balances include both; respect verified and usable.

Advance pagination by page.offset + page.limit while that is less than page.total. Compatibility pages count candidates before filtering, so an empty page does not mean there are no later matches. Reset the offset when filters or selections change. Keep selections outside the displayed page; returned tokens use address, while selections use tokenAddress. Deduplicate provider groups by chain ID plus address.

For exact-input pickers, pass existing sources to getAvailableSourceTokens so all selections share one provider. Exact-output sources are alternatives: use getAvailableSourceTokens(destination, [], query). confirmRouteExists requires a provider shared by all sources and does not guarantee a quote. Lookup failures reject separately from empty pages.

Route constraints accept sources, destinations, providers, and valueUsd. Legs accept chainId?, tokenAddress?, and amountRaw?; amounts require both identity fields. Use the same fields on each leg of one side, and at most one sizing mode: source amounts, destination amounts, or USD value. Amounts must be non-negative. Token queries do not inherit route constraints.

Swap

Exact output specifies what to receive. Omit sources (or pass []) for automatic wallet funding; explicit sources restrict eligible chains and tokens. toNativeAmountRaw optionally requests destination native funds for gas.

const result = await client.swapWithExactOut({
  toChainId: destination.chainId,
  toTokenAddress: destination.tokenAddress,
  toAmountRaw: amountRaw,
  sources: [{ chainId: 1, tokenAddress: '0xA0b86991c6218b36c1d19d4a2e9eb0cE3606eB48' }],
});
console.log(result.intentId, result.intentExplorerUrl);

Exact input specifies every source's positive raw amount. All sources and the destination must share one provider.

const source = {
  chainId: 1,
  tokenAddress: '0xA0b86991c6218b36c1d19d4a2e9eb0cE3606eB48',
} as const;
const sourceToken = await client.getToken(source);
await client.swapWithExactIn({
  sources: [{ ...source, amountRaw: parseUnits('5', sourceToken.decimals) }],
  toChainId: destination.chainId,
  toTokenAddress: destination.tokenAddress,
});

Review quotes and preview rates

Pass options.hooks.onIntent to display a confirmation UI. The operation waits for allow() or deny(); returning from the callback alone does not accept it. Without a hook, quotes are accepted automatically. ERC-20 approvals use the minimum required amount; wallets may also prompt for approval signatures and source transactions.

import type { IntentHookData, SwapOperationOptions } from '@avail-project/nexus-core';

declare function showReview(hook: IntentHookData): void;
const options: SwapOperationOptions = {
  hooks: { onIntent: showReview },
  slippageBps: 50,
  fillTimeoutMinutes: 2,
  pollingIntervalMs: 2000,
};

Pass options as the second argument to either swap method. These are the default option values. Slippage accepts an integer from 0 to 10000 or 'auto'. The review UI uses hook.quote, hook.execution, hook.allow(), hook.deny(), and await hook.refresh(sources?). Refresh returns a complete replacement quote; render that return value and read hook.execution again. Await refresh before allowing. Exact-input replacement sources must still carry token addresses and raw amounts. The quote is fixed after acceptance.

Quotes expose input, output, output.minAmountRaw, fees, allowances, plan, sourceVerdicts, and expiresAt (Unix seconds). Amounts ending in Raw are bigint; USD quote amounts are decimal strings. quote.isExecutable is a source-balance check; quote.executionWarnings describes any shortfalls.

swapWithExactIn can preview rates before wallet connection. Its hook exposes:

type QuoteExecution =
  | { possible: true }
  | { possible: false; cause: 'not-connected' | 'insufficient-balance' };

A preview remains disconnected throughout that operation. After connecting, start a new swap call and review its fresh quote. allow() on a disconnected preview rejects with WALLET_NOT_CONNECTED; deny() rejects as a user denial. There is no successful preview result. Without a hook, the quote event is emitted and the call rejects. Preview quotes can also fail if source approval gas is missing. Connected quotes with insufficient balances cannot execute; fund the wallet and refresh. Exact-output and composite operations require a wallet.

Progress and results

options.onEvent receives IntentEvent:

  • quote: the current quote and plan.
  • step: step, state (started, completed, or failed), committed, and optional errorDetails.
  • status: intentId, status (created, deposited, fulfilled, or expired), substatus, and per-source legs with transaction links and errors.
import type { IntentEvent } from '@avail-project/nexus-core';

const onEvent = (event: IntentEvent): void => {
  switch (event.type) {
    case 'quote':
      console.log('Quote updated', event.quote.output, event.quote.fees, event.quote.plan.steps);
      break;
    case 'step':
      console.log('Step progress', event.step.type, event.state);
      if (event.state === 'failed') {
        console.error(event.errorDetails?.message, event.errorDetails?.code);
        // A committed attempt may still settle; check its status before retrying.
        console.log('Intent committed', event.committed);
      }
      break;
    case 'status':
      console.log('Intent status', event.intentId, event.status, event.substatus);
      for (const leg of event.legs) {
        console.log('Source progress', leg.sourceIndex, leg.status, leg.txExplorerUrl, leg.error);
      }
      break;
  }
};

const swapOptions: SwapOperationOptions = { ...options, onEvent };

Pass swapOptions as the second argument to either swap method. Replace the logging with your application's progress UI; the error example below uses these same options.

Plan step types are erc20_approval, source_approval_signature, intent_signature, native_transaction, intent_submission, and intent_fulfillment. Event callback failures do not interrupt the operation. Use errorDetails instead of the deprecated step error string.

Swap promises resolve after fulfillment with IntentResult: intentId, intentExplorerUrl, quote, status, approvals, nativeTransactions, and optional attemptId. Track attemptId alongside intentId for support. A rejected promise may follow already-submitted transactions.

Execute a contract call

import type { ExecuteParams } from '@avail-project/nexus-core';

declare const contract: ExecuteParams['to'];
declare const calldata: ExecuteParams['data'];
const executeParams: ExecuteParams = {
  toChainId: destination.chainId,
  to: contract,
  data: calldata,
  value: 0n,
  tokenApproval: {
    toTokenAddress: destination.tokenAddress,
    amount: amountRaw,
    spender: contract,
  },
};
const estimate = await client.simulateExecute(executeParams);
const executed = await client.execute(executeParams);
console.log(estimate.estimatedGasUnits, executed.execute.txExplorerUrl);

Omit tokenApproval when none is required; native-token transfers use value. Simulation requires a wallet and estimates approval gas when needed. Execution returns execute, optional approval, and chainId. By default it waits for a receipt; waitForReceipt: false returns after broadcast. Receipt controls are receiptTimeout (milliseconds, default 300000) and requiredConfirmations (default 1). A submitted transaction can still fail after a broadcast-only result.

Fund and execute

const composed = await client.swapAndExecute(
  {
    toChainId: destination.chainId,
    toTokenAddress: destination.tokenAddress,
    toAmountRaw: amountRaw,
    execute: {
      to: contract,
      data: calldata,
      gas: 350_000n,
      tokenApproval: executeParams.tokenApproval,
    },
  },
  {
    hooks: {
      onIntent({ intent, allow }) {
        console.log(intent.executeRequirement, intent.available, intent.shortfall);
        allow(); // In an app, call after the user confirms.
      },
    },
  },
);
console.log(composed.swapSkipped, composed.execute.txExplorerUrl);
if (!composed.swapSkipped) console.log(composed.swapResult.intentExplorerUrl);

Supply a positive execute.gas estimate appropriate to your contract call. The SDK accounts for execution gas and value, funds the destination shortfall, then executes after fulfillment. The composite hook receives intent, not quote; it runs even if funding is already sufficient. intent.quote exists only when intent.swapRequired is true.

Composite refresh(sources?) returns an updated intent with current fee estimates and a replacement funding quote when needed. It reuses the original balance snapshot; start a new operation to reread balances. Omitted sources retain the selection; [] clears it. Top-level beforeExecute can return { data?, value?, gas? } after funding, but overrides do not recalculate funding. Intent events cover funding; the composite promise covers the subsequent contract call too.

History and errors

const history = await client.listIntents({ page: 1, status: 'fulfilled' });
console.log(history.intents, history.total);

All SDK errors extend NexusError and carry category, code, context, and optional details. Use UserActionError for denials and ERROR_CODES for specific recovery actions. getIntentQuoteFailure(error) extracts structured routing, amount, balance, approval-gas, and price diagnostics. Handle codes rather than parsing message text. See the error reference.

| Quote error constant (ERROR_CODES.*) | Recovery | | --- | --- | | BACKEND_VALUE_ABOVE_CEILING | Reduce the value; getIntentQuoteFailure(error)?.details.maxValueUsd is the USD limit | | BACKEND_INPUT_BELOW_DEPOSIT_FEE | Increase the source amount to cover the deposit fee | | BACKEND_NO_ROUTE_TO_DESTINATION | Choose a destination supported by the allowed providers | | BACKEND_NO_ROUTABLE_SOURCE | Review source verdicts and adjust the sources or mixed failure reasons |

The middleware reports a specific amount or destination failure when all unroutable sources agree. These failures have retryable: false; change the request before retrying. Source verdicts can include DESTINATION_NOT_SERVED. A destination unsupported by every provider still follows token-not-supported handling.

import {
  ERROR_CODES,
  NexusError,
  UserActionError,
  getIntentQuoteFailure,
} from '@avail-project/nexus-core';

try {
  const swap = await client.swapWithExactOut(
    {
      toChainId: destination.chainId,
      toTokenAddress: destination.tokenAddress,
      toAmountRaw: amountRaw,
    },
    swapOptions,
  );
  console.log('Fulfilled', swap.intentExplorerUrl);
} catch (error) {
  if (error instanceof UserActionError) {
    console.info('Action declined', error.code);
  } else if (error instanceof NexusError) {
    switch (error.code) {
      case ERROR_CODES.WALLET_NOT_CONNECTED:
        console.info('Connect a wallet, then request a fresh quote.');
        break;
      case ERROR_CODES.BACKEND_INSUFFICIENT_BALANCE:
        console.info('Fund the wallet or choose different source assets.');
        break;
      case ERROR_CODES.BACKEND_INSUFFICIENT_APPROVAL_GAS:
        console.info('Add native gas funds on the source chain or choose another source.');
        break;
      default:
        console.error(error.message, error.code, error.context);
    }

    const failure = getIntentQuoteFailure(error);
    if (failure) console.log('Quote diagnostics', failure.subcode, failure.sourceVerdicts);
    if (error.details?.approvals) console.log('Broadcast approvals', error.details.approvals);
  } else {
    throw error;
  }
}

Use the messages to update your UI and retain the error code/context for support. Event handlers show progress; the operation's promise must still be caught to handle the final failure.

After a timeout or connection failure following commitment, check history, the explorer, and known transaction hashes before retrying. The intent may still fulfill. Approval failures can expose error.details.approvals with confirmed, reverted, or unconfirmed states. An unconfirmed transaction may still confirm; use a fresh quote when starting another operation.

Product analytics can be disabled with analytics: { enabled: false } in the client config. Diagnostic logging is configured separately; see the telemetry reference.

Migrating an existing integration

Use address-based swaps for removed bridge / bridgeAndTransfer methods and swapAndExecute for bridgeAndExecute. There is no recipient override or standalone swap simulation method; use quote review through onIntent. getBalancesForSwap is a deprecated alias of getBalances.

Remove local max/routing helpers, Safe and ephemeral-key configuration, onAllowance, and old bridge/swap plan events. Use options.hooks, IntentQuote, IntentEvent, and IntentResult. Utility functions are available from @avail-project/nexus-core/utils and client.utils.