Neon-js Adds dAPI Support for Neo N3

A shared provider layer for Neo N3 gives wallets and dApps a cleaner path to connect, request signatures, prepare transactions, and handle errors without rebuilding the same integration work each time.

14 July 2026

COZ has added dAPI support to neon-js with a new package: @cityofzion/neon-dapi.

This gives Neo wallets and dApps a shared, typed implementation of the NEP-21 dAPI standard for Neo N3. It brings account access, network state, signing, invokes, transaction flow, provider events, authentication payloads, and provider errors into a clearer developer surface built on familiar neon-js primitives.

The practical result is simple: Neo developers now have a stronger foundation for wallet and dApp integration.

The wallet integration problem

Wallet integration is one of the first real tests of a blockchain developer experience. A dApp eventually has to ask a wallet to do something: identify the connected account, confirm the network, request a signature, prepare an invoke, relay a transaction, or explain why a request failed.

If every wallet and dApp handles those flows differently, developers spend more time on integration edge cases and less time shipping useful products. @cityofzion/neon-dapi helps reduce that friction for Neo N3.

The package does not replace ecosystem-level connection solutions such as WalletConnect, AppKit, or other cross-chain connection layers. Those tools solve transport, session, and multi-ecosystem connection problems. The role of neon-dapi is different: it defines and implements the Neo-specific provider surface that wallets and dApps can share once a Neo interaction needs to happen.

What is included

The package gives wallets and dApps a shared set of building blocks:

Provider interface
DapiProvider defines the wallet provider interface for the NEP-21 dAPI surface.
Provider operations
DapiOperations supports common blockchain and cryptographic operations behind provider methods.
Typed request and response models
Account, network, signer, transaction, block, invoke, signing, and provider-event models give dApps and wallets a consistent contract.
Authentication payloads
NEP-20 challenge and response payload types support wallet-mediated authentication flows.
Consistent error handling
DapiError and DapiErrorCode give dApps a predictable way to handle provider errors.
Canonical Neo N3 networks
DapiNetwork includes MainNet and TestNet network magic values.
Conformance testing
Browser tests help validate injected NEP-21 provider behavior.
1npm i @cityofzion/neon-dapi @cityofzion/neon-core

@cityofzion/neon-core is a peer dependency because the dAPI package builds on the same core primitives used across Neo transaction and script workflows.

A clearer provider surface for dApps

At the center of the package is DapiProvider, the typed surface a wallet exposes and a dApp can build against. It covers the wallet-facing actions developers need most often: provider information, supported networks, connected accounts, balances, read calls, invokes, transaction flow, message signing, authentication, and provider events.

1import type { DapiProvider } from "@cityofzion/neon-dapi";
2
3async function printProviderState(provider: DapiProvider) {
4  const accounts = await provider.getAccounts();
5
6  console.log({
7    name: provider.name,
8    dapiVersion: provider.dapiVersion,
9    network: provider.network,
10    connected: provider.connected,
11    accounts: accounts.map((account) => account.address),
12  });
13}

The value is not just that these methods exist. The value is that they are typed, standard-aligned, and part of the neon-js developer stack.

Read calls and user-approved invokes

A dApp can use the provider surface for read-only contract calls and separate user-approved transaction flows. The distinction matters: reads should be easy to inspect, while writes should remain under wallet/user approval.

1import type { DapiProvider, UInt160 } from "@cityofzion/neon-dapi";
2
3const GAS_TOKEN_HASH: UInt160 = "0xd2a4cff31913016155e38e474a2c06d08be276cf";
4
5async function readGasSymbol(provider: DapiProvider) {
6  const result = await provider.call({
7    hash: GAS_TOKEN_HASH,
8    operation: "symbol",
9    args: [],
10  });
11
12  if (result.state !== "HALT") {
13    throw new Error(result.exception ?? "Contract call failed");
14  }
15
16  return result.stack[0];
17}
1import type { DapiProvider, Signer, UInt160 } from "@cityofzion/neon-dapi";
2
3async function requestTransfer(
4  provider: DapiProvider,
5  tokenHash: UInt160,
6  from: UInt160,
7  to: UInt160,
8  amount: string,
9) {
10  const signers: Signer[] = [{ account: from, scopes: "CalledByEntry" }];
11
12  return provider.invoke(
13    [
14      {
15        hash: tokenHash,
16        operation: "transfer",
17        args: [
18          { type: "Hash160", value: from },
19          { type: "Hash160", value: to },
20          { type: "Integer", value: amount },
21          { type: "Any" },
22        ],
23      },
24    ],
25    signers,
26  );
27}

The examples show the shape of the interface. A wallet still controls account selection, approval prompts, signing policy, and relay behavior.

Authentication payloads and standards alignment

This release is also part of a larger standards story for Neo. NEP-21 defines the Neo N3 dAPI provider surface. NEP-20 defines the authentication challenge and response model used by dAPI authentication flows. The new package includes NEP-20 authentication payload types so authentication behavior can be represented consistently.

1import {
2  DapiNetwork,
3  type AuthenticationChallengePayload,
4  type DapiProvider,
5} from "@cityofzion/neon-dapi";
6
7async function authenticate(provider: DapiProvider) {
8  const challenge: AuthenticationChallengePayload = {
9    action: "Authentication",
10    grant_type: "Signature",
11    allowed_algorithms: ["ECDSA-P256"],
12    domain: "example.dapp",
13    networks: [DapiNetwork.TESTNET],
14    nonce: crypto.randomUUID(),
15    timestamp: Math.floor(Date.now() / 1000),
16  };
17
18  return provider.authenticate(challenge);
19}

NEP-33 is related context, but it is not the same thing. The open NEP-33 proposal defines an application authentication URI scheme for app-to-app authentication flows. It should not be treated as a transaction-signing API, transfer API, smart-contract invocation API, or general session-authorization layer. @cityofzion/neon-dapi strengthens the NEP-21 and NEP-20 path inside the neon-js stack while preserving a clearer direction for broader authentication flows as the ecosystem evolves.

Provider errors that dApps can handle

Wallet requests fail for different reasons. A user can cancel. A request can be invalid. A contract can fail. A node can return an RPC error. Normalized dAPI errors help dApps respond with better guidance instead of treating every failure as an unknown exception.

1import { DapiError, DapiErrorCode } from "@cityofzion/neon-dapi";
2
3try {
4  const txid = await provider.invoke(invocations, signers);
5  console.log("submitted", txid);
6} catch (error) {
7  const dapiError = DapiError.parseError(error);
8
9  if (dapiError.code === DapiErrorCode.CANCELED) {
10    // User declined in the wallet UI.
11  } else if (dapiError.code === DapiErrorCode.INSUFFICIENT_FUNDS) {
12    // Show fee or funding guidance.
13  } else if (dapiError.code === DapiErrorCode.RPC_ERROR) {
14    // Node/RPC problem; retry or switch endpoint.
15  }
16}

Conformance testing for provider quality

The package also includes a browser conformance suite under packages/neon-dapi/conformance. Wallet teams can use it to test an injected NEP-21 provider against expected behavior in a real browser environment.

1npx serve packages/neon-dapi/conformance

Some tests require a connected account with funds on the target network, so teams should run the suite carefully and on the intended network. The important point is that provider compatibility becomes easier to check, discuss, and improve.

A stronger foundation for Neo applications

This release is not only about adding another package. It is about making Neo easier to build on.

A strong blockchain developer stack needs more than contracts and RPC methods. It needs reliable wallet interaction, consistent request semantics, shared error handling, authentication payloads, and practical tests that help wallet and dApp teams meet the same expectations.

Developers can review the merged neon-js PR #976 and the package README for implementation details.