@qubic.ts/contracts 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/package.json +17 -0
  2. package/scripts/generate-artifacts.ts +178 -0
  3. package/src/codec/entry-input.test.ts +198 -0
  4. package/src/codec/entry-input.ts +959 -0
  5. package/src/codec/index.ts +8 -0
  6. package/src/generated/contracts/CCF.registry.json +741 -0
  7. package/src/generated/contracts/CCF.types.ts +307 -0
  8. package/src/generated/contracts/GQMPROP.registry.json +518 -0
  9. package/src/generated/contracts/GQMPROP.types.ts +238 -0
  10. package/src/generated/contracts/MLM.registry.json +8 -0
  11. package/src/generated/contracts/MLM.types.ts +42 -0
  12. package/src/generated/contracts/MSVAULT.registry.json +1162 -0
  13. package/src/generated/contracts/MSVAULT.types.ts +598 -0
  14. package/src/generated/contracts/NOST.registry.json +1131 -0
  15. package/src/generated/contracts/NOST.types.ts +515 -0
  16. package/src/generated/contracts/QBAY.registry.json +1492 -0
  17. package/src/generated/contracts/QBAY.types.ts +681 -0
  18. package/src/generated/contracts/QBOND.registry.json +734 -0
  19. package/src/generated/contracts/QBOND.types.ts +397 -0
  20. package/src/generated/contracts/QDRAW.registry.json +112 -0
  21. package/src/generated/contracts/QDRAW.types.ts +110 -0
  22. package/src/generated/contracts/QDUEL.registry.json +466 -0
  23. package/src/generated/contracts/QDUEL.types.ts +265 -0
  24. package/src/generated/contracts/QEARN.registry.json +458 -0
  25. package/src/generated/contracts/QEARN.types.ts +265 -0
  26. package/src/generated/contracts/QIP.registry.json +483 -0
  27. package/src/generated/contracts/QIP.types.ts +194 -0
  28. package/src/generated/contracts/QRAFFLE.registry.json +916 -0
  29. package/src/generated/contracts/QRAFFLE.types.ts +446 -0
  30. package/src/generated/contracts/QRP.registry.json +139 -0
  31. package/src/generated/contracts/QRP.types.ts +144 -0
  32. package/src/generated/contracts/QRWA.registry.json +765 -0
  33. package/src/generated/contracts/QRWA.types.ts +402 -0
  34. package/src/generated/contracts/QSWAP.registry.json +941 -0
  35. package/src/generated/contracts/QSWAP.types.ts +479 -0
  36. package/src/generated/contracts/QTF.registry.json +480 -0
  37. package/src/generated/contracts/QTF.types.ts +346 -0
  38. package/src/generated/contracts/QUOTTERY.registry.json +530 -0
  39. package/src/generated/contracts/QUOTTERY.types.ts +262 -0
  40. package/src/generated/contracts/QUTIL.registry.json +1378 -0
  41. package/src/generated/contracts/QUTIL.types.ts +612 -0
  42. package/src/generated/contracts/QVAULT.registry.json +527 -0
  43. package/src/generated/contracts/QVAULT.types.ts +309 -0
  44. package/src/generated/contracts/QX.registry.json +610 -0
  45. package/src/generated/contracts/QX.types.ts +323 -0
  46. package/src/generated/contracts/RANDOM.registry.json +51 -0
  47. package/src/generated/contracts/RANDOM.types.ts +65 -0
  48. package/src/generated/contracts/RL.registry.json +490 -0
  49. package/src/generated/contracts/RL.types.ts +304 -0
  50. package/src/generated/contracts/SWATCH.registry.json +8 -0
  51. package/src/generated/contracts/SWATCH.types.ts +42 -0
  52. package/src/generated/core-registry.codecs.ts +6622 -0
  53. package/src/generated/core-registry.source.json +14342 -0
  54. package/src/generated/core-registry.ts +14349 -0
  55. package/src/generated/core-registry.types.ts +100 -0
  56. package/src/generator/contract-codecs.fixture.test.ts +17 -0
  57. package/src/generator/contract-codecs.test.ts +115 -0
  58. package/src/generator/contract-codecs.ts +416 -0
  59. package/src/generator/index.ts +14 -0
  60. package/src/generator/per-contract-files.test.ts +70 -0
  61. package/src/generator/per-contract-files.ts +122 -0
  62. package/src/generator/registry-runtime.fixture.test.ts +17 -0
  63. package/src/generator/registry-runtime.test.ts +55 -0
  64. package/src/generator/registry-runtime.ts +28 -0
  65. package/src/generator/registry-types.fixture.test.ts +17 -0
  66. package/src/generator/registry-types.test.ts +55 -0
  67. package/src/generator/registry-types.ts +75 -0
  68. package/src/index.test.ts +29 -0
  69. package/src/index.ts +49 -0
  70. package/src/registry/index.ts +17 -0
  71. package/src/registry/io-layout.fixture.test.ts +24 -0
  72. package/src/registry/io-layout.test.ts +93 -0
  73. package/src/registry/io-layout.ts +57 -0
  74. package/src/registry/normalize.ts +61 -0
  75. package/src/registry/schema.fixture.test.ts +21 -0
  76. package/src/registry/schema.ts +97 -0
  77. package/src/registry/types.ts +98 -0
  78. package/test/fixtures/io-layout.contracts.json +32 -0
  79. package/test/fixtures/io-layout.layouts.json +14 -0
  80. package/test/fixtures/registry.sample.codecs.ts +100 -0
  81. package/test/fixtures/registry.sample.json +27 -0
  82. package/test/fixtures/registry.sample.runtime.ts +54 -0
  83. package/test/fixtures/registry.sample.types.ts +16 -0
@@ -0,0 +1,100 @@
1
+ /* eslint-disable */
2
+ /* This file is generated. Do not edit manually. */
3
+
4
+ export type ContractName = "CCF" | "GQMPROP" | "MLM" | "MSVAULT" | "NOST" | "QBAY" | "QBOND" | "QDRAW" | "QDUEL" | "QEARN" | "QIP" | "QRAFFLE" | "QRP" | "QRWA" | "QSWAP" | "QTF" | "QUOTTERY" | "QUTIL" | "QVAULT" | "QX" | "RANDOM" | "RL" | "SWATCH";
5
+
6
+ export type FunctionName<C extends ContractName> = C extends "QX"
7
+ ? "AssetAskOrders" | "AssetBidOrders" | "EntityAskOrders" | "EntityBidOrders" | "Fees"
8
+ : C extends "QUOTTERY"
9
+ ? "basicInfo" | "getActiveBet" | "getBetByCreator" | "getBetInfo" | "getBetOptionDetail"
10
+ : C extends "RANDOM"
11
+ ? never
12
+ : C extends "QUTIL"
13
+ ? "GetCurrentPollId" | "GetCurrentResult" | "GetFees" | "GetPollInfo" | "GetPollsByCreator" | "GetSendToManyV1Fee" | "GetShareholderProposal" | "GetShareholderProposalFees" | "GetShareholderProposalIndices" | "GetShareholderVotes" | "GetShareholderVotingResults" | "GetTotalNumberOfAssetShares" | "QueryFeeReserve"
14
+ : C extends "MLM"
15
+ ? never
16
+ : C extends "GQMPROP"
17
+ ? "GetProposal" | "GetProposalIndices" | "GetRevenueDonation" | "GetVote" | "GetVotingResults"
18
+ : C extends "SWATCH"
19
+ ? never
20
+ : C extends "CCF"
21
+ ? "GetLatestTransfers" | "GetProposal" | "GetProposalFee" | "GetProposalIndices" | "GetRegularPayments" | "GetVote" | "GetVotingResults"
22
+ : C extends "QEARN"
23
+ ? "getBurnedAndBoostedStats" | "getBurnedAndBoostedStatsPerEpoch" | "getEndedStatus" | "getLockInfoPerEpoch" | "getStateOfRound" | "getStatsPerEpoch" | "getUserLockedInfo" | "getUserLockStatus"
24
+ : C extends "QVAULT"
25
+ ? "getData"
26
+ : C extends "MSVAULT"
27
+ ? "getAssetReleaseStatus" | "getBalanceOf" | "getFees" | "getFeeVotes" | "getFeeVotesOwner" | "getFeeVotesScore" | "getManagedAssetBalance" | "getReleaseStatus" | "getRevenueInfo" | "getUniqueFeeVotes" | "getUniqueFeeVotesRanking" | "getVaultAssetBalances" | "getVaultName" | "getVaultOwners" | "getVaults" | "isShareHolder"
28
+ : C extends "QBAY"
29
+ ? "getIncomingAuctions" | "getInfoOfCollectionByCreator" | "getInfoOfCollectionById" | "getInfoOfMarketplace" | "getInfoOfNFTById" | "getInfoOfNFTUserPossessed" | "getNumberOfNFTForUser" | "getUserCreatedCollection" | "getUserCreatedNFT"
30
+ : C extends "QSWAP"
31
+ ? "Fees" | "GetLiquidityOf" | "GetPoolBasicState" | "InvestRewardsInfo" | "QuoteExactAssetInput" | "QuoteExactAssetOutput" | "QuoteExactQuInput" | "QuoteExactQuOutput"
32
+ : C extends "NOST"
33
+ ? "checkTokenCreatability" | "getFundarasingByIndex" | "getInfoUserInvested" | "getMaxClaimAmount" | "getNumberOfInvestedProjects" | "getProjectByIndex" | "getProjectIndexListByCreator" | "getStats" | "getTierLevelByUser" | "getUserVoteStatus"
34
+ : C extends "QDRAW"
35
+ ? "getInfo" | "getParticipants"
36
+ : C extends "RL"
37
+ ? "GetBalance" | "GetDrawHour" | "GetFees" | "GetMaxNumberOfPlayers" | "GetNextEpochData" | "GetPlayers" | "GetSchedule" | "GetState" | "GetTicketPrice" | "GetWinners"
38
+ : C extends "QBOND"
39
+ ? "GetCFA" | "GetEarnedFees" | "GetFees" | "GetInfoPerEpoch" | "GetMBondsTable" | "GetOrders" | "GetUserMBonds" | "GetUserOrders"
40
+ : C extends "QIP"
41
+ ? "getICOInfo"
42
+ : C extends "QRAFFLE"
43
+ ? "getActiveProposal" | "getActiveTokenRaffle" | "getAnalytics" | "getEndedQuRaffle" | "getEndedTokenRaffle" | "getEpochRaffleIndexes" | "getQuRaffleEntryAmountPerUser" | "getQuRaffleEntryAverageAmount" | "getRegisters"
44
+ : C extends "QRWA"
45
+ ? "GetActiveAssetReleasePollIds" | "GetActiveGovPollIds" | "GetAssetReleasePoll" | "GetDividendBalances" | "GetGeneralAssetBalance" | "GetGeneralAssets" | "GetGovParams" | "GetGovPoll" | "GetTotalDistributed" | "GetTreasuryBalance"
46
+ : C extends "QRP"
47
+ ? "GetAllowedSC" | "GetAvailableReserve"
48
+ : C extends "QTF"
49
+ ? "EstimatePrizePayouts" | "GetDrawHour" | "GetFees" | "GetNextEpochData" | "GetPools" | "GetSchedule" | "GetState" | "GetTicketPrice" | "GetWinnerData"
50
+ : C extends "QDUEL"
51
+ ? "GetPercentFees" | "GetRooms" | "GetTTLHours" | "GetUserProfile"
52
+ : never;
53
+
54
+ export type ProcedureName<C extends ContractName> = C extends "QX"
55
+ ? "AddToAskOrder" | "AddToBidOrder" | "IssueAsset" | "RemoveFromAskOrder" | "RemoveFromBidOrder" | "TransferShareManagementRights" | "TransferShareOwnershipAndPossession"
56
+ : C extends "QUOTTERY"
57
+ ? "cancelBet" | "issueBet" | "joinBet" | "publishResult"
58
+ : C extends "RANDOM"
59
+ ? "RevealAndCommit"
60
+ : C extends "QUTIL"
61
+ ? "BurnQubic" | "BurnQubicForContract" | "CancelPoll" | "CreatePoll" | "DistributeQuToShareholders" | "SendToManyBenchmark" | "SendToManyV1" | "SetShareholderProposal" | "SetShareholderVotes" | "Vote"
62
+ : C extends "MLM"
63
+ ? never
64
+ : C extends "GQMPROP"
65
+ ? "SetProposal" | "Vote"
66
+ : C extends "SWATCH"
67
+ ? never
68
+ : C extends "CCF"
69
+ ? "SetProposal" | "Vote"
70
+ : C extends "QEARN"
71
+ ? "lock" | "unlock"
72
+ : C extends "QVAULT"
73
+ ? "changeAdminAddress" | "changeAuthAddress" | "changeDistributionPermille" | "changeReinvestingAddress" | "saveBannedAddress" | "submitAdminAddress" | "submitAuthAddress" | "submitBannedAddress" | "submitDistributionPermille" | "submitReinvestingAddress" | "submitUnbannedAddress" | "unblockBannedAddress"
74
+ : C extends "MSVAULT"
75
+ ? "deposit" | "depositAsset" | "registerVault" | "releaseAssetTo" | "releaseTo" | "resetAssetRelease" | "resetRelease" | "revokeAssetManagementRights" | "voteFeeChange"
76
+ : C extends "QBAY"
77
+ ? "acceptOffer" | "bidOnTraditionalAuction" | "buy" | "cancelExchange" | "cancelOffer" | "cancelSale" | "changeStatusOfMarketPlace" | "createCollection" | "createTraditionalAuction" | "listInExchange" | "listInMarket" | "makeOffer" | "mint" | "mintOfDrop" | "settingCFBAndQubicPrice" | "transfer" | "TransferShareManagementRights"
78
+ : C extends "QSWAP"
79
+ ? "AddLiquidity" | "CreatePool" | "IssueAsset" | "RemoveLiquidity" | "SetInvestRewardsInfo" | "SwapAssetForExactQu" | "SwapExactAssetForQu" | "SwapExactQuForAsset" | "SwapQuForExactAsset" | "TransferShareManagementRights" | "TransferShareOwnershipAndPossession"
80
+ : C extends "NOST"
81
+ ? "claimToken" | "createFundraising" | "createProject" | "investInProject" | "logoutFromTier" | "registerInTier" | "TransferShareManagementRights" | "upgradeTier" | "voteInProject"
82
+ : C extends "QDRAW"
83
+ ? "buyTicket"
84
+ : C extends "RL"
85
+ ? "BuyTicket" | "SetPrice" | "SetSchedule"
86
+ : C extends "QBOND"
87
+ ? "AddAskOrder" | "AddBidOrder" | "BurnQU" | "RemoveAskOrder" | "RemoveBidOrder" | "Stake" | "TransferMBondOwnershipAndPossession" | "UpdateCFA"
88
+ : C extends "QIP"
89
+ ? "buyToken" | "createICO" | "TransferShareManagementRights"
90
+ : C extends "QRAFFLE"
91
+ ? "depositInQuRaffle" | "depositInTokenRaffle" | "logoutInSystem" | "registerInSystem" | "submitEntryAmount" | "submitProposal" | "TransferShareManagementRights" | "voteInProposal"
92
+ : C extends "QRWA"
93
+ ? "CreateAssetReleasePoll" | "DepositGeneralAsset" | "DonateToTreasury" | "RevokeAssetManagementRights" | "VoteAssetRelease" | "VoteGovParams"
94
+ : C extends "QRP"
95
+ ? "AddAllowedSC" | "RemoveAllowedSC" | "WithdrawReserve"
96
+ : C extends "QTF"
97
+ ? "BuyTicket" | "SetDrawHour" | "SetPrice" | "SetSchedule" | "SetTargetJackpot"
98
+ : C extends "QDUEL"
99
+ ? "ConnectToRoom" | "CreateRoom" | "Deposit" | "SetPercentFees" | "SetTTLHours" | "Withdraw"
100
+ : never;
@@ -0,0 +1,17 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { validateContractsRegistry } from "../registry/index.js";
3
+ import { generateContractCodecs } from "./contract-codecs.js";
4
+
5
+ describe("generateContractCodecs fixture", () => {
6
+ it("matches the expected generated file", async () => {
7
+ const fixturePath = new URL("../../test/fixtures/registry.sample.json", import.meta.url);
8
+ const expectedPath = new URL("../../test/fixtures/registry.sample.codecs.ts", import.meta.url);
9
+
10
+ const fixtureRaw = (await Bun.file(fixturePath).json()) as unknown;
11
+ const expected = await Bun.file(expectedPath).text();
12
+ const registry = validateContractsRegistry(fixtureRaw);
13
+ const generated = generateContractCodecs(registry);
14
+
15
+ expect(generated).toBe(expected);
16
+ });
17
+ });
@@ -0,0 +1,115 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import type { ContractsRegistry } from "../registry/types.js";
3
+ import { generateContractCodecs } from "./contract-codecs.js";
4
+
5
+ describe("generateContractCodecs", () => {
6
+ it("generates typed function/procedure wiring from io types", () => {
7
+ const output = generateContractCodecs(makeFixture(), {
8
+ banner: "// generated",
9
+ });
10
+
11
+ expect(output).toContain("export type QUTIL_GetFees_input = Record<string, never>;");
12
+ expect(output).toContain("export type QUTIL_GetFees_output = {");
13
+ expect(output).toContain("readonly fee: bigint;");
14
+ expect(output).toContain("readonly indices: ReadonlyArray<number>;");
15
+ expect(output).toContain("export interface ContractTypeMap {");
16
+ expect(output).toContain("readonly input: QUTIL_GetFees_input;");
17
+ expect(output).toContain("readonly output: QUTIL_GetFees_output;");
18
+ expect(output).toContain("readonly orders: ReadonlyArray<QUTIL_Order>;");
19
+ expect(output).toContain("readonly revealedBits: ReadonlyArray<boolean>;");
20
+ expect(output).toContain("export const contractCodecRegistry = {");
21
+ expect(output).toContain("GetFees: {");
22
+ expect(output).toContain("// generated");
23
+ });
24
+
25
+ it("is deterministic", () => {
26
+ const first = generateContractCodecs(makeFixture());
27
+ const second = generateContractCodecs(makeFixture());
28
+ expect(first).toBe(second);
29
+ });
30
+ });
31
+
32
+ function makeFixture(): ContractsRegistry {
33
+ return {
34
+ version: 1,
35
+ metadata: {
36
+ generatedAt: "2026-02-18T00:00:00.000Z",
37
+ sources: [{ name: "qubic/core" }],
38
+ },
39
+ contracts: [
40
+ {
41
+ name: "QUTIL",
42
+ contractIndex: 4,
43
+ address: "EAAAA",
44
+ ioTypes: [
45
+ { kind: "alias", name: "GetFees_input", target: "NoData" },
46
+ {
47
+ kind: "struct",
48
+ name: "GetFees_output",
49
+ fields: [
50
+ { name: "fee", type: "sint64" },
51
+ { name: "indices", type: "Array<uint16, 64>" },
52
+ ],
53
+ },
54
+ {
55
+ kind: "struct",
56
+ name: "GetOrders_input",
57
+ fields: [],
58
+ },
59
+ {
60
+ kind: "struct",
61
+ name: "GetOrders_output",
62
+ fields: [{ name: "orders", type: "Array<QUTIL::GetOrders_output::Order, 2>" }],
63
+ },
64
+ {
65
+ kind: "struct",
66
+ name: "Order",
67
+ fields: [{ name: "value", type: "uint16" }],
68
+ },
69
+ {
70
+ kind: "struct",
71
+ name: "RevealAndCommit_input",
72
+ fields: [{ name: "revealedBits", type: "BitArray<64>" }],
73
+ },
74
+ {
75
+ kind: "struct",
76
+ name: "RevealAndCommit_output",
77
+ fields: [],
78
+ },
79
+ { kind: "alias", name: "BurnQubic_input", target: "UnknownAliasType" },
80
+ { kind: "alias", name: "BurnQubic_output", target: "sint32" },
81
+ ],
82
+ entries: [
83
+ {
84
+ kind: "function",
85
+ name: "GetFees",
86
+ inputType: 7,
87
+ inputTypeName: "GetFees_input",
88
+ outputTypeName: "GetFees_output",
89
+ },
90
+ {
91
+ kind: "function",
92
+ name: "GetOrders",
93
+ inputType: 8,
94
+ inputTypeName: "GetOrders_input",
95
+ outputTypeName: "GetOrders_output",
96
+ },
97
+ {
98
+ kind: "procedure",
99
+ name: "RevealAndCommit",
100
+ inputType: 9,
101
+ inputTypeName: "RevealAndCommit_input",
102
+ outputTypeName: "RevealAndCommit_output",
103
+ },
104
+ {
105
+ kind: "procedure",
106
+ name: "BurnQubic",
107
+ inputType: 2,
108
+ inputTypeName: "BurnQubic_input",
109
+ outputTypeName: "BurnQubic_output",
110
+ },
111
+ ],
112
+ },
113
+ ],
114
+ };
115
+ }
@@ -0,0 +1,416 @@
1
+ import { normalizeContractsRegistry } from "../registry/normalize.js";
2
+ import type {
3
+ ContractDefinition,
4
+ ContractEntry,
5
+ ContractIoTypeDefinition,
6
+ ContractsRegistry,
7
+ } from "../registry/types.js";
8
+
9
+ export type GenerateContractCodecsOptions = Readonly<{
10
+ banner?: string;
11
+ runtimeExportName?: string;
12
+ }>;
13
+
14
+ export function generateContractCodecs(
15
+ registry: ContractsRegistry,
16
+ options: GenerateContractCodecsOptions = {},
17
+ ): string {
18
+ const normalized = normalizeContractsRegistry(registry);
19
+ const contracts = normalized.contracts;
20
+ const runtimeExportName = options.runtimeExportName ?? "contractCodecRegistry";
21
+ const contexts = contracts.map(createContractContext);
22
+
23
+ const banner =
24
+ options.banner ?? "/* eslint-disable */\n/* This file is generated. Do not edit manually. */";
25
+
26
+ const ioTypeBlocks = contexts.flatMap((context) => renderContractIoTypeAliases(context));
27
+
28
+ return [
29
+ banner,
30
+ "",
31
+ ...ioTypeBlocks,
32
+ ...(ioTypeBlocks.length > 0 ? [""] : []),
33
+ "export interface ContractTypeMap {",
34
+ ...contexts.map((context) => renderContractTypeMapEntry(context)),
35
+ "}",
36
+ "",
37
+ "export type ContractName = keyof ContractTypeMap & string;",
38
+ 'export type FunctionName<C extends ContractName> = keyof ContractTypeMap[C]["functions"] & string;',
39
+ 'export type ProcedureName<C extends ContractName> = keyof ContractTypeMap[C]["procedures"] & string;',
40
+ "",
41
+ "export type FunctionInput<",
42
+ " C extends ContractName,",
43
+ " F extends FunctionName<C>,",
44
+ '> = ContractTypeMap[C]["functions"][F]["input"];',
45
+ "export type FunctionOutput<",
46
+ " C extends ContractName,",
47
+ " F extends FunctionName<C>,",
48
+ '> = ContractTypeMap[C]["functions"][F]["output"];',
49
+ "",
50
+ "export type ProcedureInput<",
51
+ " C extends ContractName,",
52
+ " P extends ProcedureName<C>,",
53
+ '> = ContractTypeMap[C]["procedures"][P]["input"];',
54
+ "export type ProcedureOutput<",
55
+ " C extends ContractName,",
56
+ " P extends ProcedureName<C>,",
57
+ '> = ContractTypeMap[C]["procedures"][P]["output"];',
58
+ "",
59
+ `export const ${runtimeExportName} = {`,
60
+ ...contexts.map((context) => renderRuntimeContractEntry(context)),
61
+ `} as const satisfies Readonly<Record<ContractName, unknown>>;`,
62
+ "",
63
+ `export type RuntimeContractCodecRegistry = typeof ${runtimeExportName};`,
64
+ "",
65
+ ].join("\n");
66
+ }
67
+
68
+ type ContractRenderContext = Readonly<{
69
+ contract: ContractDefinition;
70
+ ioTypeByName: ReadonlyMap<string, ContractIoTypeDefinition>;
71
+ typeAliasByIoName: ReadonlyMap<string, string>;
72
+ }>;
73
+
74
+ function createContractContext(contract: ContractDefinition): ContractRenderContext {
75
+ const ioTypeByName = new Map((contract.ioTypes ?? []).map((typeDef) => [typeDef.name, typeDef]));
76
+ const typeAliasByIoName = new Map<string, string>();
77
+
78
+ for (const typeDef of contract.ioTypes ?? []) {
79
+ typeAliasByIoName.set(typeDef.name, makeIoAliasName(contract.name, typeDef.name));
80
+ }
81
+
82
+ return {
83
+ contract,
84
+ ioTypeByName,
85
+ typeAliasByIoName,
86
+ };
87
+ }
88
+
89
+ function renderContractIoTypeAliases(context: ContractRenderContext): readonly string[] {
90
+ const blocks: string[] = [];
91
+ const typeDefs = context.contract.ioTypes ?? [];
92
+
93
+ for (const typeDef of typeDefs) {
94
+ const aliasName = context.typeAliasByIoName.get(typeDef.name);
95
+ if (!aliasName) continue;
96
+ const rendered = renderIoTypeDefinition(typeDef, context);
97
+ blocks.push(`export type ${aliasName} = ${rendered};`);
98
+ }
99
+
100
+ return blocks;
101
+ }
102
+
103
+ function renderIoTypeDefinition(
104
+ typeDef: ContractIoTypeDefinition,
105
+ context: ContractRenderContext,
106
+ ): string {
107
+ if (typeDef.kind === "alias") {
108
+ return renderCoreTypeExpression(typeDef.target, context);
109
+ }
110
+
111
+ if (typeDef.fields.length === 0) {
112
+ return "Record<string, never>";
113
+ }
114
+
115
+ const lines = ["{"];
116
+ for (const field of typeDef.fields) {
117
+ lines.push(
118
+ ` readonly ${quoteProperty(field.name)}: ${renderCoreTypeExpression(field.type, context)};`,
119
+ );
120
+ }
121
+ lines.push("}");
122
+ return lines.join("\n");
123
+ }
124
+
125
+ function renderCoreTypeExpression(typeExpression: string, context: ContractRenderContext): string {
126
+ const normalized = typeExpression.trim();
127
+ if (!normalized) return "unknown";
128
+
129
+ const noQualifiers = normalizeTypeExpression(stripTypeQualifiers(normalized));
130
+
131
+ if (isArrayType(noQualifiers)) {
132
+ const args = splitTopLevelGenericArgs(extractGenericArgs(noQualifiers) ?? "");
133
+ const elementType = renderCoreTypeExpression(args[0] ?? "unknown", context);
134
+ return `ReadonlyArray<${elementType}>`;
135
+ }
136
+
137
+ if (isFixedArrayNotation(noQualifiers)) {
138
+ const base = noQualifiers.replace(/\[[^\]]*\]\s*$/, "").trim();
139
+ return `ReadonlyArray<${renderCoreTypeExpression(base, context)}>`;
140
+ }
141
+
142
+ const simple = collapseWhitespace(noQualifiers);
143
+ if (isBitArrayType(simple)) {
144
+ return "ReadonlyArray<boolean>";
145
+ }
146
+
147
+ const primitive = CORE_PRIMITIVE_TO_TS[simple];
148
+ if (primitive) return primitive;
149
+
150
+ const ioAlias = resolveIoAliasName(simple, context.typeAliasByIoName);
151
+ if (ioAlias) return ioAlias;
152
+
153
+ return "unknown";
154
+ }
155
+
156
+ const CORE_PRIMITIVE_TO_TS: Readonly<Record<string, string>> = {
157
+ NoData: "Record<string, never>",
158
+ bit: "boolean",
159
+ bool: "boolean",
160
+ sint8: "number",
161
+ uint8: "number",
162
+ int8_t: "number",
163
+ uint8_t: "number",
164
+ sint16: "number",
165
+ uint16: "number",
166
+ int16_t: "number",
167
+ uint16_t: "number",
168
+ sint32: "number",
169
+ uint32: "number",
170
+ int32_t: "number",
171
+ uint32_t: "number",
172
+ sint64: "bigint",
173
+ uint64: "bigint",
174
+ int64_t: "bigint",
175
+ uint64_t: "bigint",
176
+ uint128: "bigint",
177
+ id: "string",
178
+ m256i: "string",
179
+ };
180
+
181
+ function renderContractTypeMapEntry(context: ContractRenderContext): string {
182
+ const { contract } = context;
183
+ const functionEntries = contract.entries.filter((entry) => entry.kind === "function");
184
+ const procedureEntries = contract.entries.filter((entry) => entry.kind === "procedure");
185
+
186
+ return [
187
+ ` ${quoteProperty(contract.name)}: {`,
188
+ ` readonly contractIndex: ${contract.contractIndex};`,
189
+ ...renderEntryTypeContainer("functions", functionEntries, context),
190
+ ...renderEntryTypeContainer("procedures", procedureEntries, context),
191
+ " };",
192
+ ].join("\n");
193
+ }
194
+
195
+ function renderEntryTypeContainer(
196
+ name: "functions" | "procedures",
197
+ entries: readonly ContractEntry[],
198
+ context: ContractRenderContext,
199
+ ): readonly string[] {
200
+ if (entries.length === 0) {
201
+ return [` readonly ${name}: Record<never, never>;`];
202
+ }
203
+
204
+ return [` readonly ${name}: {`, ...renderEntryTypeMap(entries, context), " };"];
205
+ }
206
+
207
+ function renderEntryTypeMap(
208
+ entries: readonly ContractEntry[],
209
+ context: ContractRenderContext,
210
+ ): readonly string[] {
211
+ return entries.map((entry) =>
212
+ [
213
+ ` ${quoteProperty(entry.name)}: {`,
214
+ ` readonly kind: ${quote(entry.kind)};`,
215
+ ` readonly inputType: ${entry.inputType};`,
216
+ ` readonly inputTypeName: ${quote(entry.inputTypeName ?? `${entry.name}_input`)};`,
217
+ ` readonly outputTypeName: ${quote(entry.outputTypeName ?? `${entry.name}_output`)};`,
218
+ ` readonly input: ${resolveEntryInputType(entry, context)};`,
219
+ ` readonly output: ${resolveEntryOutputType(entry, context)};`,
220
+ " };",
221
+ ].join("\n"),
222
+ );
223
+ }
224
+
225
+ function resolveEntryInputType(entry: ContractEntry, context: ContractRenderContext): string {
226
+ if (!entry.inputTypeName) return "unknown";
227
+ const aliased = context.typeAliasByIoName.get(entry.inputTypeName);
228
+ if (aliased) return aliased;
229
+
230
+ const ioType = context.ioTypeByName.get(entry.inputTypeName);
231
+ if (!ioType) return "unknown";
232
+ return renderIoTypeDefinition(ioType, context);
233
+ }
234
+
235
+ function resolveEntryOutputType(entry: ContractEntry, context: ContractRenderContext): string {
236
+ if (!entry.outputTypeName) return "unknown";
237
+ const aliased = context.typeAliasByIoName.get(entry.outputTypeName);
238
+ if (aliased) return aliased;
239
+
240
+ const ioType = context.ioTypeByName.get(entry.outputTypeName);
241
+ if (!ioType) return "unknown";
242
+ return renderIoTypeDefinition(ioType, context);
243
+ }
244
+
245
+ function renderRuntimeContractEntry(context: ContractRenderContext): string {
246
+ const { contract } = context;
247
+ const functionEntries = contract.entries.filter((entry) => entry.kind === "function");
248
+ const procedureEntries = contract.entries.filter((entry) => entry.kind === "procedure");
249
+
250
+ return [
251
+ ` ${quoteProperty(contract.name)}: {`,
252
+ ` contractIndex: ${contract.contractIndex},`,
253
+ ...renderRuntimeEntryContainer("functions", functionEntries),
254
+ ...renderRuntimeEntryContainer("procedures", procedureEntries),
255
+ " },",
256
+ ].join("\n");
257
+ }
258
+
259
+ function renderRuntimeEntryContainer(
260
+ name: "functions" | "procedures",
261
+ entries: readonly ContractEntry[],
262
+ ): readonly string[] {
263
+ if (entries.length === 0) {
264
+ return [` ${name}: {},`];
265
+ }
266
+
267
+ return [` ${name}: {`, ...renderRuntimeEntries(entries), " },"];
268
+ }
269
+
270
+ function renderRuntimeEntries(entries: readonly ContractEntry[]): readonly string[] {
271
+ return entries.map((entry) =>
272
+ [
273
+ ` ${quoteProperty(entry.name)}: {`,
274
+ ` kind: ${quote(entry.kind)},`,
275
+ ` inputType: ${entry.inputType},`,
276
+ ` inputTypeName: ${quote(entry.inputTypeName ?? `${entry.name}_input`)},`,
277
+ ` outputTypeName: ${quote(entry.outputTypeName ?? `${entry.name}_output`)},`,
278
+ " },",
279
+ ].join("\n"),
280
+ );
281
+ }
282
+
283
+ function makeIoAliasName(contractName: string, ioTypeName: string): string {
284
+ return `${toTypeIdentifier(contractName)}_${toTypeIdentifier(ioTypeName)}`;
285
+ }
286
+
287
+ function toTypeIdentifier(value: string): string {
288
+ const sanitized = value.replace(/[^A-Za-z0-9_]/g, "_");
289
+ const startsWithDigit = /^[0-9]/.test(sanitized);
290
+ return startsWithDigit ? `T_${sanitized}` : sanitized;
291
+ }
292
+
293
+ function quote(value: string): string {
294
+ return JSON.stringify(value);
295
+ }
296
+
297
+ function quoteProperty(value: string): string {
298
+ return isValidIdentifier(value) ? value : JSON.stringify(value);
299
+ }
300
+
301
+ function isValidIdentifier(value: string): boolean {
302
+ return /^[$A-Z_a-z][$\w]*$/.test(value);
303
+ }
304
+
305
+ function stripTypeQualifiers(value: string): string {
306
+ return collapseWhitespace(
307
+ value
308
+ .replace(/\bQPI::/g, "")
309
+ .replace(/\bconst\b/g, "")
310
+ .replace(/\bvolatile\b/g, "")
311
+ .replace(/\bstruct\b/g, "")
312
+ .replace(/\bclass\b/g, "")
313
+ .replace(/\s*&+/g, "")
314
+ .replace(/\s*\*+/g, ""),
315
+ );
316
+ }
317
+
318
+ function collapseWhitespace(value: string): string {
319
+ return value.trim().replace(/\s+/g, " ");
320
+ }
321
+
322
+ function normalizeTypeExpression(value: string): string {
323
+ return collapseWhitespace(value)
324
+ .replace(/^::+/, "")
325
+ .replace(/\s*<\s*/g, "<")
326
+ .replace(/\s*>\s*/g, ">")
327
+ .replace(/\s*,\s*/g, ", ");
328
+ }
329
+
330
+ function resolveIoAliasName(
331
+ typeExpression: string,
332
+ aliasByIoName: ReadonlyMap<string, string>,
333
+ ): string | undefined {
334
+ for (const candidate of getTypeLookupCandidates(typeExpression)) {
335
+ const resolved = aliasByIoName.get(candidate);
336
+ if (resolved) return resolved;
337
+ }
338
+ return undefined;
339
+ }
340
+
341
+ function getTypeLookupCandidates(value: string): readonly string[] {
342
+ const candidates: string[] = [];
343
+ const seen = new Set<string>();
344
+
345
+ const push = (candidate: string | undefined): void => {
346
+ const normalized = normalizeTypeExpression(candidate ?? "");
347
+ if (!normalized || seen.has(normalized)) return;
348
+ seen.add(normalized);
349
+ candidates.push(normalized);
350
+ };
351
+
352
+ const normalized = normalizeTypeExpression(value);
353
+ push(normalized);
354
+ push(extractUnqualifiedTypeName(normalized));
355
+
356
+ const templateBase = extractTemplateBaseName(normalized);
357
+ if (templateBase) {
358
+ push(templateBase);
359
+ push(extractUnqualifiedTypeName(templateBase));
360
+ }
361
+
362
+ return candidates;
363
+ }
364
+
365
+ function extractTemplateBaseName(value: string): string | undefined {
366
+ const index = value.indexOf("<");
367
+ if (index <= 0) return undefined;
368
+ return value.slice(0, index).trim();
369
+ }
370
+
371
+ function extractUnqualifiedTypeName(value: string): string {
372
+ const trimmed = value.trim();
373
+ const segments = trimmed.split("::").filter((segment) => segment.length > 0);
374
+ return segments[segments.length - 1] ?? trimmed;
375
+ }
376
+
377
+ function isArrayType(value: string): boolean {
378
+ return /^Array\s*</.test(value);
379
+ }
380
+
381
+ function extractGenericArgs(value: string): string | null {
382
+ const start = value.indexOf("<");
383
+ const end = value.lastIndexOf(">");
384
+ if (start < 0 || end < 0 || end <= start) return null;
385
+ return value.slice(start + 1, end);
386
+ }
387
+
388
+ function splitTopLevelGenericArgs(value: string): readonly string[] {
389
+ const out: string[] = [];
390
+ let depth = 0;
391
+ let current = "";
392
+
393
+ for (const char of value) {
394
+ if (char === "<") depth += 1;
395
+ if (char === ">") depth = Math.max(0, depth - 1);
396
+
397
+ if (char === "," && depth === 0) {
398
+ out.push(current.trim());
399
+ current = "";
400
+ continue;
401
+ }
402
+
403
+ current += char;
404
+ }
405
+
406
+ if (current.trim().length > 0) out.push(current.trim());
407
+ return out;
408
+ }
409
+
410
+ function isFixedArrayNotation(value: string): boolean {
411
+ return /\[[^\]]*\]\s*$/.test(value);
412
+ }
413
+
414
+ function isBitArrayType(value: string): boolean {
415
+ return /^BitArray<\d+>$/.test(value);
416
+ }
@@ -0,0 +1,14 @@
1
+ export {
2
+ type GenerateContractCodecsOptions,
3
+ generateContractCodecs,
4
+ } from "./contract-codecs.js";
5
+ export {
6
+ type GeneratedPerContractFile,
7
+ type GeneratePerContractFilesOptions,
8
+ generatePerContractFiles,
9
+ } from "./per-contract-files.js";
10
+ export {
11
+ type GenerateRegistryRuntimeOptions,
12
+ generateRegistryRuntime,
13
+ } from "./registry-runtime.js";
14
+ export { type GenerateRegistryTypesOptions, generateRegistryTypes } from "./registry-types.js";