Generate TypeScript contract types and optional client implementations for NEO N3 from a contract manifest.
npm i -D @smartargs/n3-typegen- 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-jsto build params, but you can replace it with your own encoder.
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
contractHashparameter - --impl: Also emit a concrete client class using an
N3Invokerinterface
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-hashOutputs:
<Name>.d.ts:<Name>APIinterface with method signatures<Name>Client.ts(with--impl): client class that calls anN3Invokern3-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 --implThe .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: truemethods are considered read-only and will useinvokeReadin the 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 usesinvokeRead, elseinvoke.
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,
},
],
});import { generate } from "@smartargs/n3-typegen";
await generate({
manifest: "path/to/manifest.json",
out: "src/contracts",
name: "ExampleContract",
hash: "0x1234",
impl: true,
});