UNPKG

@stellar/stellar-sdk

Version:

A library for working with the Stellar network, including communication with the Horizon and Soroban RPC servers.

360 lines (354 loc) 14.4 kB
'use strict'; require('../node_modules/.pnpm/@stellar_js-xdr@4.0.0/node_modules/@stellar/js-xdr/src/int.js'); require('../node_modules/.pnpm/@stellar_js-xdr@4.0.0/node_modules/@stellar/js-xdr/src/hyper.js'); require('../node_modules/.pnpm/@stellar_js-xdr@4.0.0/node_modules/@stellar/js-xdr/src/unsigned-int.js'); require('../node_modules/.pnpm/@stellar_js-xdr@4.0.0/node_modules/@stellar/js-xdr/src/unsigned-hyper.js'); require('../node_modules/.pnpm/@stellar_js-xdr@4.0.0/node_modules/@stellar/js-xdr/src/xdr-type.js'); require('buffer'); var curr_generated = require('../base/generated/curr_generated.js'); require('@noble/hashes/sha2.js'); require('../base/signing.js'); require('../base/keypair.js'); require('base32.js'); require('../base/util/continued_fraction.js'); require('../base/util/bignumber.js'); require('../base/transaction_builder.js'); require('../base/muxed_account.js'); require('../base/scval.js'); require('../base/numbers/uint128.js'); require('../base/numbers/uint256.js'); require('../base/numbers/int128.js'); require('../base/numbers/int256.js'); var utils = require('./utils.js'); class TypeGenerator { spec; // event index (in event-entry declaration order) -> resolved (possibly // disambiguated) interface name. Keyed by index rather than raw name // because a contract may declare several events with the same name. // Lazily computed on first use; see resolveEventInterfaceNames(). eventInterfaceNames = null; constructor(spec) { this.spec = spec; } /** * Generate all TypeScript type definitions */ generate() { let eventIndex = 0; const types = this.spec.entries.map( (entry) => entry.switch() === curr_generated.default.ScSpecEntryKind.scSpecEntryEventV0() ? this.generateEvent(entry.eventV0(), eventIndex++) : this.generateEntry(entry) ).filter((t) => t).join("\n\n"); const imports = this.generateImports(); const eventUnion = this.generateContractEventUnion(); return `${imports} ${types} ${eventUnion} `; } /** * Generate TypeScript for a single spec entry */ generateEntry(entry) { switch (entry.switch()) { case curr_generated.default.ScSpecEntryKind.scSpecEntryUdtStructV0(): if (utils.isTupleStruct(entry.udtStructV0())) { return this.generateTupleStruct(entry.udtStructV0()); } return this.generateStruct(entry.udtStructV0()); case curr_generated.default.ScSpecEntryKind.scSpecEntryUdtUnionV0(): return this.generateUnion(entry.udtUnionV0()); case curr_generated.default.ScSpecEntryKind.scSpecEntryUdtEnumV0(): return this.generateEnum(entry.udtEnumV0()); case curr_generated.default.ScSpecEntryKind.scSpecEntryUdtErrorEnumV0(): return this.generateErrorEnum(entry.udtErrorEnumV0()); // Events are handled directly in generate(), which numbers them in // declaration order. default: return null; } } generateImports() { const imports = utils.generateTypeImports( this.spec.entries.flatMap((entry) => { switch (entry.switch()) { case curr_generated.default.ScSpecEntryKind.scSpecEntryUdtStructV0(): return entry.udtStructV0().fields().map((field) => field.type()); case curr_generated.default.ScSpecEntryKind.scSpecEntryUdtUnionV0(): return entry.udtUnionV0().cases().flatMap((unionCase) => { if (unionCase.switch() === curr_generated.default.ScSpecUdtUnionCaseV0Kind.scSpecUdtUnionCaseTupleV0()) { return unionCase.tupleCase().type(); } return []; }); case curr_generated.default.ScSpecEntryKind.scSpecEntryUdtEnumV0(): return []; case curr_generated.default.ScSpecEntryKind.scSpecEntryUdtErrorEnumV0(): return []; case curr_generated.default.ScSpecEntryKind.scSpecEntryEventV0(): return entry.eventV0().params().map((param) => param.type()); default: return []; } }) ); return utils.formatImports(imports, { includeTypeFileImports: false // Types file doesn't import from itself }); } /** * Generate TypeScript interface for a struct */ generateStruct(struct) { const name = utils.sanitizeIdentifier(struct.name().toString()); const doc = utils.formatJSDocComment( struct.doc().toString() || `Struct: ${name}`, 0 ); const fields = struct.fields().map((field) => { const fieldName = utils.sanitizeIdentifier(field.name().toString()); const fieldType = utils.parseTypeFromTypeDef(field.type()); const fieldDoc = utils.formatJSDocComment(field.doc().toString(), 2); return `${fieldDoc} ${fieldName}: ${fieldType};`; }).join("\n"); return `${doc}export interface ${name} { ${fields} }`; } /** * Generate TypeScript union type */ generateUnion(union) { const name = utils.sanitizeIdentifier(union.name().toString()); const doc = utils.formatJSDocComment( union.doc().toString() || `Union: ${name}`, 0 ); const cases = union.cases().map((unionCase) => this.generateUnionCase(unionCase)); const caseTypes = cases.map((c) => { if (c.types.length > 0) { return `${utils.formatJSDocComment(c.doc, 2)} { tag: "${utils.escapeStringLiteral(c.name)}"; values: readonly [${c.types.join(", ")}] }`; } return `${utils.formatJSDocComment(c.doc, 2)} { tag: "${utils.escapeStringLiteral(c.name)}"; values: void }`; }).join(" |\n"); return `${doc} export type ${name} = ${caseTypes};`; } /** * Generate TypeScript enum */ generateEnum(enumEntry) { const name = utils.sanitizeIdentifier(enumEntry.name().toString()); const doc = utils.formatJSDocComment( enumEntry.doc().toString() || `Enum: ${name}`, 0 ); const members = enumEntry.cases().map((enumCase) => { const caseName = utils.sanitizeIdentifier(enumCase.name().toString()); const caseValue = enumCase.value(); const caseDoc = enumCase.doc().toString() || `Enum Case: ${caseName}`; return `${utils.formatJSDocComment(caseDoc, 2)} ${caseName} = ${caseValue}`; }).join(",\n"); return `${doc}export enum ${name} { ${members} }`; } /** * Generate TypeScript error enum */ generateErrorEnum(errorEnum) { const name = utils.sanitizeIdentifier(errorEnum.name().toString()); const doc = utils.formatJSDocComment( errorEnum.doc().toString() || `Error Enum: ${name}`, 0 ); const cases = errorEnum.cases().map((enumCase) => this.generateEnumCase(enumCase)); const members = cases.map((c) => { return `${utils.formatJSDocComment(c.doc, 2)} ${c.value} : { message: "${utils.escapeStringLiteral(c.name)}" }`; }).join(",\n"); return `${doc}export const ${name} = { ${members} }`; } /** * Generate union case */ generateUnionCase(unionCase) { switch (unionCase.switch()) { case curr_generated.default.ScSpecUdtUnionCaseV0Kind.scSpecUdtUnionCaseVoidV0(): { const voidCase = unionCase.voidCase(); return { doc: voidCase.doc().toString(), name: voidCase.name().toString(), types: [] }; } case curr_generated.default.ScSpecUdtUnionCaseV0Kind.scSpecUdtUnionCaseTupleV0(): { const tupleCase = unionCase.tupleCase(); return { doc: tupleCase.doc().toString(), name: tupleCase.name().toString(), types: tupleCase.type().map((t) => utils.parseTypeFromTypeDef(t)) }; } default: throw new Error(`Unknown union case kind: ${unionCase.switch()}`); } } /** * Generate enum case */ generateEnumCase(enumCase) { return { doc: enumCase.doc().toString(), name: enumCase.name().toString(), value: enumCase.value() }; } /** * Compute the exported TS interface name for an event, e.g. "transfer" * becomes "TransferEvent". Resolved (and disambiguated if necessary) via * {@link resolveEventInterfaceNames}, so every call site agrees. */ eventInterfaceName(event, eventIndex) { const resolved = this.resolveEventInterfaceNames().get(eventIndex); if (resolved === void 0) { return `${utils.toPascalCase(utils.sanitizeIdentifier(event.name().toString()))}Event`; } return resolved; } /** * The resolved (possibly disambiguated) interface name of every event in * the spec, in declaration order. Exposed so callers (e.g. the bindings * generator's diagnostics) can report renames and duplicates. */ eventInterfaceNamesInOrder() { return this.spec.events().map((event, eventIndex) => this.eventInterfaceName(event, eventIndex)); } /** * True if the given event's resolved interface name differs from its * preferred (unsuffixed) form, i.e. it was disambiguated away from a * collision. */ eventInterfaceNameWasRenamed(event, eventIndex) { const preferred = `${utils.toPascalCase(utils.sanitizeIdentifier(event.name().toString()))}Event`; return this.eventInterfaceName(event, eventIndex) !== preferred; } /** * The name-normalization used for event interface names (and UDT type * names) is not injective — e.g. events "FooBar" and "foo_bar" both * produce the interface name "FooBarEvent", a contract may declare * several events with the very same name (composed modules each emitting * their own "transfer"), and an event can just as easily collide with a * UDT (struct/union/enum) of the same generated name. Since UDT/function * names are load-bearing (referenced directly in signatures) and * event-derived names are already synthetic, UDT names always win: they * are reserved first, in spec-entry order. Events are then resolved in * spec-entry order, appending the smallest integer 2 or greater needed to * make the name unique (and reserving whatever name results, so later * events see it too). This is deterministic for a given spec. */ resolveEventInterfaceNames() { if (this.eventInterfaceNames !== null) { return this.eventInterfaceNames; } const reserved = /* @__PURE__ */ new Set(["ContractEvent"]); for (const entry of this.spec.entries) { switch (entry.switch()) { case curr_generated.default.ScSpecEntryKind.scSpecEntryUdtStructV0(): reserved.add( utils.sanitizeIdentifier(entry.udtStructV0().name().toString()) ); break; case curr_generated.default.ScSpecEntryKind.scSpecEntryUdtUnionV0(): reserved.add( utils.sanitizeIdentifier(entry.udtUnionV0().name().toString()) ); break; case curr_generated.default.ScSpecEntryKind.scSpecEntryUdtEnumV0(): reserved.add(utils.sanitizeIdentifier(entry.udtEnumV0().name().toString())); break; case curr_generated.default.ScSpecEntryKind.scSpecEntryUdtErrorEnumV0(): reserved.add( utils.sanitizeIdentifier(entry.udtErrorEnumV0().name().toString()) ); break; } } const resolved = /* @__PURE__ */ new Map(); let eventIndex = 0; for (const entry of this.spec.entries) { if (entry.switch() !== curr_generated.default.ScSpecEntryKind.scSpecEntryEventV0()) { continue; } const event = entry.eventV0(); const preferred = `${utils.toPascalCase(utils.sanitizeIdentifier(event.name().toString()))}Event`; let candidate = preferred; let suffix = 2; while (reserved.has(candidate)) { candidate = `${preferred}${suffix}`; suffix += 1; } reserved.add(candidate); resolved.set(eventIndex, candidate); eventIndex += 1; } this.eventInterfaceNames = resolved; return resolved; } /** * Generate TypeScript interface for a Soroban contract event */ generateEvent(event, eventIndex) { const rawName = event.name().toString(); const name = this.eventInterfaceName(event, eventIndex); const preferred = `${utils.toPascalCase(utils.sanitizeIdentifier(rawName))}Event`; const renameNote = this.eventInterfaceNameWasRenamed(event, eventIndex) ? ` Note: renamed from "${preferred}" to avoid a collision with another generated name.` : ""; const doc = utils.formatJSDocComment( (event.doc().toString() || `Event: ${rawName}`) + renameNote, 0 ); const fieldKey = (rawParamName) => /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(rawParamName) ? rawParamName : `"${utils.escapeStringLiteral(rawParamName)}"`; const dataIsMapFormat = event.dataFormat().value === curr_generated.default.ScSpecEventDataFormat.scSpecEventDataFormatMap().value; const dataFields = event.params().map((param) => { const fieldName = fieldKey(param.name().toString()); const fieldType = utils.parseTypeFromTypeDef(param.type()); const fieldDoc = utils.formatJSDocComment(param.doc().toString(), 4); const optional = dataIsMapFormat && param.location().value === curr_generated.default.ScSpecEventParamLocationV0.scSpecEventParamLocationData().value; return `${fieldDoc} ${fieldName}${optional ? "?" : ""}: ${fieldType};`; }).join("\n"); return `${doc}export interface ${name} { name: "${utils.escapeStringLiteral(rawName)}"; data: { ${dataFields} }; }`; } /** * Generate the discriminated union of all contract events, if the spec defines any. */ generateContractEventUnion() { const eventEntries = this.spec.entries.filter( (entry) => entry.switch() === curr_generated.default.ScSpecEntryKind.scSpecEntryEventV0() ); if (eventEntries.length === 0) { return ""; } const names = eventEntries.map( (entry, eventIndex) => this.eventInterfaceName(entry.eventV0(), eventIndex) ); return `export type ContractEvent = ${names.join(" | ")};`; } generateTupleStruct(udtStruct) { const name = utils.sanitizeIdentifier(udtStruct.name().toString()); const doc = utils.formatJSDocComment( udtStruct.doc().toString() || `Tuple Struct: ${name}`, 0 ); const types = udtStruct.fields().map((field) => utils.parseTypeFromTypeDef(field.type())).join(", "); return `${doc}export type ${name} = readonly [${types}];`; } } exports.TypeGenerator = TypeGenerator; //# sourceMappingURL=types.js.map