Skip to content

smartargs/n3-typegen

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

n3-typegen

Generate TypeScript contract types and optional client implementations for NEO N3 from a contract manifest.

npm version license node

Install

npm i -D @smartargs/n3-typegen

Requirements

  • Node.js >= 18
  • To use generated clients at runtime, you need an invoker implementation (e.g., a wallet SDK like NeoLine or an RPC client). The examples below use @cityofzion/neon-js to build params, but you can replace it with your own encoder.

CLI

npx @smartargs/n3-typegen --manifest <path> [--out <dir>] [--name <contract>] [--hash <scriptHash>] [--embed-hash] [--impl]
  • --manifest : Path to N3 contract manifest JSON (required)
  • --out : Output directory (default: src/contracts)
  • --name : Override contract name (affects file/interface/class)
  • --hash : Contract scripthash (required with --node); included in headers
  • --embed-hash: Embed contract hash into the client class (static). If omitted, the client constructor requires a contractHash parameter
  • --impl: Also emit a concrete client class using an N3Invoker interface

Fetch manifest from a node instead of a local file:

npx @smartargs/n3-typegen --node https://mainnet1.neo.coz.io:443 --hash 0x<hash> --out src/contracts --name FromChain --impl
# To keep constructor requiring the hash (not embedded), omit --embed-hash
npx @smartargs/n3-typegen --node https://mainnet1.neo.coz.io:443 --hash 0x<hash> --out src/contracts --name FromChain --impl --embed-hash

Outputs:

  • <Name>.d.ts: <Name>API interface with method signatures
  • <Name>Client.ts (with --impl): client class that calls an N3Invoker
  • n3-typegen-types.ts (with --impl): shared types used by clients (N3TypegenSigner, N3TypegenWitnessScope)

Example:

# Interface only
npx @smartargs/n3-typegen --manifest sample/manifest.json --out sample/out --name ExampleContract --hash 0x1234

# Interface + implementation client
npx @smartargs/n3-typegen --manifest sample/manifest.json --out sample/out --name ExampleContract --hash 0x1234 --impl

Generated Types

The .d.ts file exports an ExampleContractAPI interface with one method per ABI function. Duplicate ABI method names (overloads) are emitted as multiple function signatures.

Special handling:

  • ABI safe: true methods are considered read-only and will use invokeRead in the client

Generated Client

With --impl, a ExampleContractClient class is emitted. The class requires an N3Invoker implementation:

export interface N3Invoker {
  invoke<T>(
    contractHash: string,
    method: string,
    args: unknown[],
    signers?: N3TypegenSigner[]
  ): Promise<T>;
  invokeRead<T>(
    contractHash: string,
    method: string,
    args: unknown[]
  ): Promise<T>;
}
  • Methods are generated with overload signatures and a single implementation using rest parameters.
  • If all overloads are safe, the implementation uses invokeRead, else invoke.

Implementing an N3Invoker (NeoLine, Neon-JS, or custom)

Implement N3Invoker using your preferred stack (Angular, React, Node.js, etc.). The invoker simply needs to expose invoke for state-changing calls (optionally with signers) and invokeRead for read-only calls.

// wallet-invoker.ts
import { sc } from "@cityofzion/neon-js"; // optional; use your own encoder if preferred
import type { N3Invoker } from "./contracts/ExampleContractClient";
import type {
  N3TypegenSigner,
  N3TypegenWitnessScope,
} from "./contracts/n3-typegen-types";

export class WalletInvoker implements N3Invoker {
  constructor(
    private readonly provider: { invoke: Function; invokeRead: Function }
  ) {}

  private toParams(args: unknown[]) {
    return args.map((v) => sc.ContractParam.any(v));
  }

  async invokeRead<T>(
    contractHash: string,
    method: string,
    args: unknown[]
  ): Promise<T> {
    const res = await this.provider.invokeRead({
      scriptHash: contractHash,
      operation: method,
      args: this.toParams(args),
    });
    return (res?.stack?.[0]?.value ?? res) as T;
  }

  async invoke<T>(
    contractHash: string,
    method: string,
    args: unknown[],
    signers?: N3TypegenSigner[]
  ): Promise<T> {
    const res = await this.provider.invoke({
      scriptHash: contractHash,
      operation: method,
      args: this.toParams(args),
      signers, // if your provider supports it
    });
    return res as T;
  }
}

Usage:

import { ExampleContractClient } from "./contracts/ExampleContractClient";
import { WalletInvoker } from "./wallet-invoker";

const invoker = new WalletInvoker(neoline.n3); // or any provider exposing invoke/invokeRead
const client = new ExampleContractClient(invoker); // or pass contract hash if not using --hash

// Read (no signers):
const total = await client.totalSupply();

// Write with explicit signers:
await client.transfer(from, to, amount, {
  signers: [
    {
      account: from,
      scopes: N3TypegenWitnessScope.CalledByEntry,
    },
  ],
});

Programmatic API

import { generate } from "@smartargs/n3-typegen";

await generate({
  manifest: "path/to/manifest.json",
  out: "src/contracts",
  name: "ExampleContract",
  hash: "0x1234",
  impl: true,
});

About

Type-safe NEO N3 contract binding generator for TypeScript with optional client implementation.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published