"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.BindingGenerator = void 0;
var _contract = require("../contract");
var _config = require("./config");
var _types = require("./types");
var _client = require("./client");
var _wasm_spec_parser = require("../contract/wasm_spec_parser");
var _wasm_fetcher = require("./wasm_fetcher");
var _sacSpec = require("./sac-spec");
/**
* Options for generating TypeScript bindings.
*
* @property contractName - The name used for the generated package and client class.
* Should be in kebab-case (e.g., "my-contract").
*/
/**
* The output of the binding generation process.
*
* Contains all generated TypeScript source files and configuration files
* needed to create a standalone npm package for interacting with a Stellar contract.
*
* @property index - The index.ts barrel export file that re-exports Client and types
* @property types - The types.ts file containing TypeScript interfaces for contract structs, enums, and unions
* @property client - The client.ts file containing the generated Client class with typed methods
* @property packageJson - The package.json for the generated npm package
* @property tsConfig - The tsconfig.json for TypeScript compilation
* @property readme - The README.md with usage documentation
* @property gitignore - The .gitignore file for the generated package
*/
/**
* Generates TypeScript bindings for Stellar smart contracts.
*
* This class creates fully-typed TypeScript client code from a contract's specification,
* allowing developers to interact with Stellar smart contracts with full IDE support
* and compile-time type checking.
*
* @example
* // Create from a local WASM file
* const wasmBuffer = fs.readFileSync("./my_contract.wasm");
* const generator = await BindingGenerator.fromWasm(wasmBuffer);
* const bindings = generator.generate({ contractName: "my-contract" });
*
* @example
* // Create from a contract deployed on the network
* const generator = await BindingGenerator.fromContractId(
* "CABC...XYZ",
* "https://soroban-testnet.stellar.org",
* Networks.TESTNET
* );
* const bindings = generator.generate({ contractName: "my-contract" });
*
* @example
* // Create from a Spec object directly
* const spec = new Spec(specEntries);
* const generator = BindingGenerator.fromSpec(spec);
* const bindings = generator.generate({ contractName: "my-contract" });
*/
class BindingGenerator {
/**
* Private constructor - use static factory methods instead.
*
* @param spec - The parsed contract specification
*/
constructor(spec) {
this.spec = spec;
}
/**
* Creates a BindingGenerator from an existing Spec object.
*
* Use this when you already have a parsed contract specification,
* such as from manually constructed spec entries or from another source.
*
* @param spec - The contract specification containing function and type definitions
* @returns A new BindingGenerator instance
*
* @example
* const spec = new Spec(specEntries);
* const generator = BindingGenerator.fromSpec(spec);
*/
static fromSpec(spec) {
return new BindingGenerator(spec);
}
/**
* Creates a BindingGenerator from a WASM binary buffer.
*
* Parses the contract specification directly from the WASM file's custom section.
* This is the most common method when working with locally compiled contracts.
*
* @param wasmBuffer - The raw WASM binary as a Buffer
* @returns A Promise resolving to a new BindingGenerator instance
* @throws {Error} If the WASM file doesn't contain a valid contract spec
*
* @example
* const wasmBuffer = fs.readFileSync("./target/wasm32-unknown-unknown/release/my_contract.wasm");
* const generator = await BindingGenerator.fromWasm(wasmBuffer);
*/
static fromWasm(wasmBuffer) {
const spec = new _contract.Spec((0, _wasm_spec_parser.specFromWasm)(wasmBuffer));
return new BindingGenerator(spec);
}
/**
* Creates a BindingGenerator by fetching WASM from the network using its hash.
*
* Retrieves the WASM bytecode from Stellar RPC using the WASM hash,
* then parses the contract specification from it. Useful when you know
* the hash of an installed WASM but don't have the binary locally.
*
* @param wasmHash - The hex-encoded hash of the installed WASM blob
* @param rpcServer - The Stellar RPC server instance
* @returns A Promise resolving to a new BindingGenerator instance
* @throws {Error} If the WASM cannot be fetched or doesn't contain a valid spec
*
* @example
* const generator = await BindingGenerator.fromWasmHash(
* "a1b2c3...xyz",
* "https://soroban-testnet.stellar.org",
* Networks.TESTNET
* );
*/
static async fromWasmHash(wasmHash, rpcServer) {
const wasm = await (0, _wasm_fetcher.fetchFromWasmHash)(wasmHash, rpcServer);
if (wasm.type !== "wasm") {
throw new Error("Fetched contract is not of type 'wasm'");
}
return BindingGenerator.fromWasm(wasm.wasmBytes);
}
/**
* Creates a BindingGenerator by fetching contract info from a deployed contract ID.
*
* Retrieves the contract's WASM from the network using the contract ID,
* then parses the specification. If the contract is a Stellar Asset Contract (SAC),
* returns a generator with the standard SAC specification.
*
* @param contractId - The contract ID (C... address) of the deployed contract
* @param rpcServer - The Stellar RPC server instance
* @returns A Promise resolving to a new BindingGenerator instance
* @throws {Error} If the contract cannot be found or fetched
*
* @example
* const generator = await BindingGenerator.fromContractId(
* "CABC123...XYZ",
* rpcServer
* );
*/
static async fromContractId(contractId, rpcServer) {
const result = await (0, _wasm_fetcher.fetchFromContractId)(contractId, rpcServer);
if (result.type === "wasm") {
return BindingGenerator.fromWasm(result.wasmBytes);
}
// Stellar Asset Contract
const spec = new _contract.Spec(_sacSpec.SAC_SPEC);
return BindingGenerator.fromSpec(spec);
}
/**
* Generates TypeScript bindings for the contract.
*
* Produces all the files needed for a standalone npm package:
* - `client.ts`: A typed Client class with methods for each contract function
* - `types.ts`: TypeScript interfaces for all contract types (structs, enums, unions)
* - `index.ts`: Barrel export file
* - `package.json`, `tsconfig.json`, `README.md`, `.gitignore`: Package configuration
*
* The generated code does not write to disk - use the returned strings
* to write files as needed.
*
* @param options - Configuration options for generation
* @param options.contractName - Required. The name for the generated package (kebab-case recommended)
* @returns An object containing all generated file contents as strings
* @throws {Error} If contractName is missing or empty
*
* @example
* const bindings = generator.generate({
* contractName: "my-token",
* contractAddress: "CABC...XYZ",
* rpcUrl: "https://soroban-testnet.stellar.org",
* networkPassphrase: Networks.TESTNET
* });
*
* // Write files to disk
* fs.writeFileSync("./src/client.ts", bindings.client);
* fs.writeFileSync("./src/types.ts", bindings.types);
*/
generate(options) {
this.validateOptions(options);
const typeGenerator = new _types.TypeGenerator(this.spec);
const clientGenerator = new _client.ClientGenerator(this.spec);
const types = typeGenerator.generate();
const client = clientGenerator.generate();
let index = `export { Client } from "./client.js";`;
if (types.trim() !== "") {
index = index.concat(`\nexport * from "./types.js";`);
}
// Generate config files
const configGenerator = new _config.ConfigGenerator();
const {
packageJson,
tsConfig,
readme,
gitignore
} = configGenerator.generate(options);
return {
index,
types,
client,
packageJson,
tsConfig,
readme,
gitignore
};
}
/**
* Validates that required generation options are provided.
*
* @param options - The options to validate
* @throws {Error} If contractName is missing or empty
*/
validateOptions(options) {
if (!options.contractName || options.contractName.trim() === "") {
throw new Error("contractName is required and cannot be empty");
}
}
}
exports.BindingGenerator = BindingGenerator;
Source