@totemsdk/core 1.2.6 → 1.2.7

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 (95) hide show
  1. package/dist/canonical.d.ts +3 -0
  2. package/dist/canonical.js +22 -0
  3. package/dist/cjs/MiniNumber.d.ts +47 -0
  4. package/dist/cjs/MiniNumber.js +302 -0
  5. package/dist/cjs/Streamable.d.ts +268 -0
  6. package/dist/cjs/Streamable.js +439 -0
  7. package/dist/cjs/adapters/index.d.ts +190 -0
  8. package/dist/cjs/adapters/index.js +118 -0
  9. package/dist/cjs/adapters/types.d.ts +150 -0
  10. package/dist/cjs/adapters/types.js +81 -0
  11. package/dist/cjs/binio.d.ts +9 -0
  12. package/dist/cjs/binio.js +25 -0
  13. package/dist/cjs/bip39.d.ts +91 -0
  14. package/dist/cjs/bip39.js +434 -0
  15. package/dist/cjs/canonical.d.ts +3 -0
  16. package/dist/cjs/canonical.js +27 -0
  17. package/dist/cjs/derive.d.ts +2 -0
  18. package/dist/cjs/derive.js +13 -0
  19. package/dist/cjs/index.d.ts +49 -0
  20. package/dist/cjs/index.js +231 -0
  21. package/dist/cjs/javaStreamables.d.ts +254 -0
  22. package/dist/cjs/javaStreamables.js +391 -0
  23. package/dist/cjs/lease/LeaseMonitor.d.ts +44 -0
  24. package/dist/cjs/lease/LeaseMonitor.js +115 -0
  25. package/dist/cjs/lease/LeaseStore.d.ts +58 -0
  26. package/dist/cjs/lease/LeaseStore.js +151 -0
  27. package/dist/cjs/lease/WatermarkStore.d.ts +61 -0
  28. package/dist/cjs/lease/WatermarkStore.js +209 -0
  29. package/dist/cjs/lease/index.d.ts +14 -0
  30. package/dist/cjs/lease/index.js +22 -0
  31. package/dist/cjs/lease-client.d.ts +31 -0
  32. package/dist/cjs/lease-client.js +44 -0
  33. package/dist/cjs/legacy.d.ts +23 -0
  34. package/dist/cjs/legacy.js +163 -0
  35. package/dist/cjs/minima32.d.ts +18 -0
  36. package/dist/cjs/minima32.js +121 -0
  37. package/dist/cjs/minimaWireSerializer.d.ts +60 -0
  38. package/dist/cjs/minimaWireSerializer.js +77 -0
  39. package/dist/cjs/mmr.d.ts +157 -0
  40. package/dist/cjs/mmr.js +352 -0
  41. package/dist/cjs/mx.d.ts +1 -0
  42. package/dist/cjs/mx.js +10 -0
  43. package/dist/cjs/package.json +1 -0
  44. package/dist/cjs/params.d.ts +29 -0
  45. package/dist/cjs/params.js +20 -0
  46. package/dist/cjs/script.d.ts +7 -0
  47. package/dist/cjs/script.js +27 -0
  48. package/dist/cjs/scripts/contract-helpers.d.ts +252 -0
  49. package/dist/cjs/scripts/contract-helpers.js +407 -0
  50. package/dist/cjs/scripts/dapp-types.d.ts +119 -0
  51. package/dist/cjs/scripts/dapp-types.js +77 -0
  52. package/dist/cjs/scripts/index.d.ts +4 -0
  53. package/dist/cjs/scripts/index.js +38 -0
  54. package/dist/cjs/scripts/types.d.ts +81 -0
  55. package/dist/cjs/scripts/types.js +159 -0
  56. package/dist/cjs/scripts/witness-serializer.d.ts +29 -0
  57. package/dist/cjs/scripts/witness-serializer.js +306 -0
  58. package/dist/cjs/transaction.d.ts +106 -0
  59. package/dist/cjs/transaction.js +293 -0
  60. package/dist/cjs/treekey.d.ts +399 -0
  61. package/dist/cjs/treekey.js +827 -0
  62. package/dist/cjs/tx/TransactionLifecycle.d.ts +55 -0
  63. package/dist/cjs/tx/TransactionLifecycle.js +205 -0
  64. package/dist/cjs/tx/TransactionReceiptStore.d.ts +36 -0
  65. package/dist/cjs/tx/TransactionReceiptStore.js +93 -0
  66. package/dist/cjs/tx/TransactionService.d.ts +50 -0
  67. package/dist/cjs/tx/TransactionService.js +186 -0
  68. package/dist/cjs/tx/index.d.ts +13 -0
  69. package/dist/cjs/tx/index.js +20 -0
  70. package/dist/cjs/tx/types.d.ts +115 -0
  71. package/dist/cjs/tx/types.js +6 -0
  72. package/dist/cjs/util.d.ts +1 -0
  73. package/dist/cjs/util.js +5 -0
  74. package/dist/cjs/utils.d.ts +8 -0
  75. package/dist/cjs/utils.js +42 -0
  76. package/dist/cjs/verify.d.ts +18 -0
  77. package/dist/cjs/verify.js +174 -0
  78. package/dist/cjs/version.d.ts +12 -0
  79. package/dist/cjs/version.js +15 -0
  80. package/dist/cjs/wasm-sync.d.ts +71 -0
  81. package/dist/cjs/wasm-sync.js +97 -0
  82. package/dist/cjs/wots.d.ts +232 -0
  83. package/dist/cjs/wots.js +589 -0
  84. package/dist/index.d.ts +9 -3
  85. package/dist/index.js +25 -2
  86. package/dist/minimaWireSerializer.js +2 -2
  87. package/dist/mmr.js +1 -1
  88. package/dist/transaction.js +10 -10
  89. package/dist/treekey.js +14 -0
  90. package/dist/wasm-sync.d.ts +2 -2
  91. package/dist/wasm-sync.js +3 -1
  92. package/dist/wots.d.ts +1 -1
  93. package/package.json +84 -45
  94. package/dist/wasm-bridge.d.ts +0 -54
  95. package/dist/wasm-bridge.js +0 -303
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @module TransactionLifecycle
3
+ * Orchestrates the complete transaction flow with lease and watermark management
4
+ *
5
+ * Integrates:
6
+ * - TransactionService for RPC calls
7
+ * - LeaseStore for lease persistence
8
+ * - WatermarkStore for index tracking
9
+ * - TransactionReceiptStore for history
10
+ */
11
+ import type { LoggerAdapter, MetricsAdapter } from '../adapters/index.js';
12
+ import type { LeaseStore, WatermarkStore } from '../lease/index.js';
13
+ import type { TransactionService } from './TransactionService.js';
14
+ import type { TransactionReceiptStore } from './TransactionReceiptStore.js';
15
+ import type { PrepareRequest, PrepareResponse, FinalizeResponse, TransactionMetadata, SignResult } from './types.js';
16
+ import type { WotsSigningDependencies } from './TransactionService.js';
17
+ export declare class TransactionLifecycleError extends Error {
18
+ code: number;
19
+ userMessage: string;
20
+ constructor(message: string, code: number, userMessage: string);
21
+ }
22
+ export declare class WatermarkExhaustedError extends Error {
23
+ constructor();
24
+ }
25
+ export interface TransactionLifecycleConfig {
26
+ validateWatermarkBeforePrepare?: boolean;
27
+ syncWatermarkBeforePrepare?: boolean;
28
+ }
29
+ export interface WatermarkSyncFunction {
30
+ (rootPublicKey: string): Promise<{
31
+ updated: boolean;
32
+ multiDeviceConflict: boolean;
33
+ }>;
34
+ }
35
+ export interface PrepareResult extends PrepareResponse {
36
+ metadata: TransactionMetadata;
37
+ }
38
+ export declare class TransactionLifecycle {
39
+ private readonly txService;
40
+ private readonly leaseStore;
41
+ private readonly watermarkStore;
42
+ private readonly receiptStore;
43
+ private readonly logger;
44
+ private readonly metrics;
45
+ private readonly config;
46
+ private syncWatermark?;
47
+ constructor(txService: TransactionService, leaseStore: LeaseStore, watermarkStore: WatermarkStore, receiptStore: TransactionReceiptStore, logger?: LoggerAdapter, metrics?: MetricsAdapter, config?: TransactionLifecycleConfig);
48
+ setSyncWatermarkFunction(fn: WatermarkSyncFunction): void;
49
+ prepare(params: PrepareRequest, rootPublicKey: string): Promise<PrepareResult>;
50
+ sign(prepareResult: PrepareResult, seed: Uint8Array, deps: WotsSigningDependencies, paramSetName?: string): Promise<SignResult>;
51
+ finalize(leaseToken: string, signedHex: string, metadata: TransactionMetadata): Promise<FinalizeResponse>;
52
+ cancelLease(leaseToken: string): Promise<void>;
53
+ private validateWatermark;
54
+ private parseAxiaError;
55
+ }
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ /**
3
+ * @module TransactionLifecycle
4
+ * Orchestrates the complete transaction flow with lease and watermark management
5
+ *
6
+ * Integrates:
7
+ * - TransactionService for RPC calls
8
+ * - LeaseStore for lease persistence
9
+ * - WatermarkStore for index tracking
10
+ * - TransactionReceiptStore for history
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.TransactionLifecycle = exports.WatermarkExhaustedError = exports.TransactionLifecycleError = void 0;
14
+ const index_js_1 = require("../adapters/index.js");
15
+ class TransactionLifecycleError extends Error {
16
+ code;
17
+ userMessage;
18
+ constructor(message, code, userMessage) {
19
+ super(message);
20
+ this.code = code;
21
+ this.userMessage = userMessage;
22
+ this.name = 'TransactionLifecycleError';
23
+ }
24
+ }
25
+ exports.TransactionLifecycleError = TransactionLifecycleError;
26
+ class WatermarkExhaustedError extends Error {
27
+ constructor() {
28
+ super('All WOTS signatures have been used. Please create a new wallet.');
29
+ this.name = 'WatermarkExhaustedError';
30
+ }
31
+ }
32
+ exports.WatermarkExhaustedError = WatermarkExhaustedError;
33
+ class TransactionLifecycle {
34
+ txService;
35
+ leaseStore;
36
+ watermarkStore;
37
+ receiptStore;
38
+ logger;
39
+ metrics;
40
+ config;
41
+ syncWatermark;
42
+ constructor(txService, leaseStore, watermarkStore, receiptStore, logger = new index_js_1.NoopLogger(), metrics = new index_js_1.NoopMetrics(), config = {}) {
43
+ this.txService = txService;
44
+ this.leaseStore = leaseStore;
45
+ this.watermarkStore = watermarkStore;
46
+ this.receiptStore = receiptStore;
47
+ this.logger = logger;
48
+ this.metrics = metrics;
49
+ this.config = {
50
+ validateWatermarkBeforePrepare: config.validateWatermarkBeforePrepare ?? true,
51
+ syncWatermarkBeforePrepare: config.syncWatermarkBeforePrepare ?? true,
52
+ };
53
+ }
54
+ setSyncWatermarkFunction(fn) {
55
+ this.syncWatermark = fn;
56
+ }
57
+ async prepare(params, rootPublicKey) {
58
+ if (this.config.validateWatermarkBeforePrepare) {
59
+ await this.validateWatermark(rootPublicKey);
60
+ }
61
+ try {
62
+ const result = await this.txService.prepare(params, rootPublicKey);
63
+ await this.leaseStore.save({
64
+ leaseId: result.leaseId,
65
+ leaseToken: result.leaseToken,
66
+ indices: { addressIndex: result.addressIndex, l1: result.l1, l2: result.l2 },
67
+ expiresAt: Date.now() + result.leaseTTL,
68
+ status: 'active',
69
+ createdAt: Date.now(),
70
+ txId: result.txId,
71
+ leaseTTL: result.leaseTTL,
72
+ });
73
+ this.logger.debug(`Lease persisted: ${result.leaseId}`);
74
+ return {
75
+ ...result,
76
+ metadata: {
77
+ to: params.to,
78
+ amount: params.amount,
79
+ tokenId: params.tokenId || '0x00',
80
+ },
81
+ };
82
+ }
83
+ catch (error) {
84
+ const parsed = this.parseAxiaError(error);
85
+ throw new TransactionLifecycleError(error.message, parsed.code, parsed.userMessage);
86
+ }
87
+ }
88
+ async sign(prepareResult, seed, deps, paramSetName) {
89
+ const request = {
90
+ addressIndex: prepareResult.addressIndex,
91
+ l1: prepareResult.l1,
92
+ l2: prepareResult.l2,
93
+ digestTx: prepareResult.digestTx,
94
+ };
95
+ return this.txService.sign(request, seed, deps, paramSetName);
96
+ }
97
+ async finalize(leaseToken, signedHex, metadata) {
98
+ const lease = this.leaseStore.getByToken(leaseToken);
99
+ if (!lease) {
100
+ throw new TransactionLifecycleError('Lease not found', 404, 'Transaction lease not found. Please try again.');
101
+ }
102
+ try {
103
+ const result = await this.txService.finalize({ leaseToken, signedHex });
104
+ await this.watermarkStore.markUsed(lease.indices);
105
+ await this.watermarkStore.advanceWatermark(lease.indices);
106
+ await this.receiptStore.initialize();
107
+ await this.receiptStore.add({
108
+ txpowid: result.txpowid,
109
+ timestamp: Date.now(),
110
+ to: metadata.to,
111
+ amount: metadata.amount,
112
+ tokenId: metadata.tokenId,
113
+ indices: lease.indices,
114
+ status: 'confirmed',
115
+ txId: lease.txId,
116
+ leaseId: lease.leaseId,
117
+ });
118
+ await this.leaseStore.updateStatus(lease.leaseId, 'finalized');
119
+ await this.leaseStore.delete(lease.leaseId);
120
+ this.logger.debug(`Transaction finalized: ${result.txpowid}`);
121
+ this.logger.debug('Watermark advanced and receipt stored');
122
+ return result;
123
+ }
124
+ catch (error) {
125
+ const parsed = this.parseAxiaError(error);
126
+ throw new TransactionLifecycleError(error.message, parsed.code, parsed.userMessage);
127
+ }
128
+ }
129
+ async cancelLease(leaseToken) {
130
+ const lease = this.leaseStore.getByToken(leaseToken);
131
+ if (lease) {
132
+ await this.leaseStore.updateStatus(lease.leaseId, 'cancelled');
133
+ await this.leaseStore.delete(lease.leaseId);
134
+ this.logger.debug(`Lease cancelled: ${lease.leaseId}`);
135
+ }
136
+ }
137
+ async validateWatermark(rootPublicKey) {
138
+ if (!this.watermarkStore.isInitialized()) {
139
+ await this.watermarkStore.initialize();
140
+ }
141
+ if (this.watermarkStore.isExhausted()) {
142
+ throw new WatermarkExhaustedError();
143
+ }
144
+ if (this.config.syncWatermarkBeforePrepare && this.syncWatermark) {
145
+ try {
146
+ this.logger.debug('Syncing watermark before transaction prepare...');
147
+ const syncResult = await this.syncWatermark(rootPublicKey);
148
+ if (syncResult.updated) {
149
+ this.logger.debug('Watermark synced before prepare');
150
+ }
151
+ if (syncResult.multiDeviceConflict) {
152
+ this.logger.warn('Multi-device conflict detected before prepare');
153
+ }
154
+ if (this.watermarkStore.isExhausted()) {
155
+ throw new WatermarkExhaustedError();
156
+ }
157
+ }
158
+ catch (error) {
159
+ if (error instanceof WatermarkExhaustedError) {
160
+ throw error;
161
+ }
162
+ this.logger.warn('Watermark sync failed before prepare:', error);
163
+ }
164
+ }
165
+ }
166
+ parseAxiaError(error) {
167
+ const message = error.message || error.toString();
168
+ if (error?.code === 429) {
169
+ const limit = error.limit ?? 10;
170
+ return {
171
+ code: 429,
172
+ userMessage: `Daily signing limit reached (${limit}/day). Resets at midnight UTC.`,
173
+ };
174
+ }
175
+ if (message.includes('403') || error.code === 403) {
176
+ return {
177
+ code: 403,
178
+ userMessage: 'Access denied. Your project may not have permission for this operation.',
179
+ };
180
+ }
181
+ if (message.includes('409') || error.code === 409) {
182
+ return {
183
+ code: 409,
184
+ userMessage: 'WOTS indices exhausted. Please create a new wallet.',
185
+ };
186
+ }
187
+ if (message.includes('410') || error.code === 410) {
188
+ return {
189
+ code: 410,
190
+ userMessage: 'Transaction lease expired. Please try again.',
191
+ };
192
+ }
193
+ if (message.includes('502') || error.code === 502) {
194
+ return {
195
+ code: 502,
196
+ userMessage: 'Failed to post transaction to network. Please try again.',
197
+ };
198
+ }
199
+ return {
200
+ code: 500,
201
+ userMessage: 'An error occurred. Please try again.',
202
+ };
203
+ }
204
+ }
205
+ exports.TransactionLifecycle = TransactionLifecycle;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @module TransactionReceiptStore
3
+ * Persistent storage for transaction receipts
4
+ *
5
+ * Uses in-memory cache backed by persistent storage.
6
+ * Pattern:
7
+ * - initialize() loads from storage into memory cache
8
+ * - Sync getters read from memory cache (fast, no I/O)
9
+ * - Mutators update memory cache AND persist to storage
10
+ */
11
+ import type { StorageAdapter, LoggerAdapter } from '../adapters/index.js';
12
+ import type { TransactionReceipt } from './types.js';
13
+ export interface TransactionReceiptStoreConfig {
14
+ storageKey?: string;
15
+ maxReceipts?: number;
16
+ }
17
+ export declare class TransactionReceiptStore {
18
+ private receipts;
19
+ private _initialized;
20
+ private readonly storageKey;
21
+ private readonly maxReceipts;
22
+ private readonly storage;
23
+ private readonly logger;
24
+ constructor(storage: StorageAdapter, logger?: LoggerAdapter, config?: TransactionReceiptStoreConfig);
25
+ initialize(): Promise<void>;
26
+ isInitialized(): boolean;
27
+ private load;
28
+ private persist;
29
+ add(receipt: TransactionReceipt): Promise<void>;
30
+ getAll(): TransactionReceipt[];
31
+ getByTxpowid(txpowid: string): TransactionReceipt | undefined;
32
+ getRecent(count?: number): TransactionReceipt[];
33
+ updateStatus(txpowid: string, status: TransactionReceipt['status']): Promise<void>;
34
+ clear(): Promise<void>;
35
+ count(): number;
36
+ }
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ /**
3
+ * @module TransactionReceiptStore
4
+ * Persistent storage for transaction receipts
5
+ *
6
+ * Uses in-memory cache backed by persistent storage.
7
+ * Pattern:
8
+ * - initialize() loads from storage into memory cache
9
+ * - Sync getters read from memory cache (fast, no I/O)
10
+ * - Mutators update memory cache AND persist to storage
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.TransactionReceiptStore = void 0;
14
+ const index_js_1 = require("../adapters/index.js");
15
+ const DEFAULT_STORAGE_KEY = 'totem_transaction_receipts';
16
+ const DEFAULT_MAX_RECEIPTS = 1000;
17
+ class TransactionReceiptStore {
18
+ receipts = [];
19
+ _initialized = false;
20
+ storageKey;
21
+ maxReceipts;
22
+ storage;
23
+ logger;
24
+ constructor(storage, logger = new index_js_1.NoopLogger(), config = {}) {
25
+ this.storage = storage;
26
+ this.logger = logger;
27
+ this.storageKey = config.storageKey ?? DEFAULT_STORAGE_KEY;
28
+ this.maxReceipts = config.maxReceipts ?? DEFAULT_MAX_RECEIPTS;
29
+ }
30
+ async initialize() {
31
+ if (this._initialized)
32
+ return;
33
+ await this.load();
34
+ this._initialized = true;
35
+ }
36
+ isInitialized() {
37
+ return this._initialized;
38
+ }
39
+ async load() {
40
+ try {
41
+ const receipts = await this.storage.get(this.storageKey);
42
+ if (receipts) {
43
+ this.receipts = receipts;
44
+ this.logger.debug(`Loaded ${this.receipts.length} transaction receipts`);
45
+ }
46
+ }
47
+ catch (error) {
48
+ this.logger.error('Failed to load receipts:', error);
49
+ }
50
+ }
51
+ async persist() {
52
+ try {
53
+ const trimmedReceipts = this.receipts.slice(-this.maxReceipts);
54
+ await this.storage.set(this.storageKey, trimmedReceipts);
55
+ this.receipts = trimmedReceipts;
56
+ }
57
+ catch (error) {
58
+ this.logger.error('Failed to persist receipts:', error);
59
+ throw error;
60
+ }
61
+ }
62
+ async add(receipt) {
63
+ this.receipts.push(receipt);
64
+ this.logger.debug(`Added receipt: ${receipt.txpowid}`);
65
+ await this.persist();
66
+ }
67
+ getAll() {
68
+ return [...this.receipts].reverse();
69
+ }
70
+ getByTxpowid(txpowid) {
71
+ return this.receipts.find(r => r.txpowid === txpowid);
72
+ }
73
+ getRecent(count = 50) {
74
+ return this.getAll().slice(0, count);
75
+ }
76
+ async updateStatus(txpowid, status) {
77
+ const receipt = this.receipts.find(r => r.txpowid === txpowid);
78
+ if (receipt) {
79
+ receipt.status = status;
80
+ this.logger.debug(`Updated receipt status: ${txpowid} → ${status}`);
81
+ await this.persist();
82
+ }
83
+ }
84
+ async clear() {
85
+ this.receipts = [];
86
+ await this.storage.remove(this.storageKey);
87
+ this.logger.debug('Cleared all receipts');
88
+ }
89
+ count() {
90
+ return this.receipts.length;
91
+ }
92
+ }
93
+ exports.TransactionReceiptStore = TransactionReceiptStore;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @module TransactionService
3
+ * Handles WOTS signing flow: prepare → sign → finalize
4
+ *
5
+ * Platform-agnostic implementation using HttpClient adapter.
6
+ *
7
+ * Sign architecture (2026-02): uses per-address TreeKey (depth=3, keysPerLevel=64)
8
+ * matching Minima Wallet.java exactly. Each address has its own TreeKey derived
9
+ * from the wallet seed. Signing produces 3 proofs (Root→L1→L2→DATA).
10
+ */
11
+ import type { HttpClient, LoggerAdapter, MetricsAdapter } from '../adapters/index.js';
12
+ import type { PrepareRequest, PrepareResponse, SignRequest, SignResult, FinalizeRequest, FinalizeResponse } from './types.js';
13
+ export interface TransactionServiceConfig {
14
+ baseUrl: string;
15
+ apiKey: string;
16
+ paramSet?: string;
17
+ }
18
+ /**
19
+ * @deprecated WotsSigningDependencies is no longer used by TransactionService.sign().
20
+ * The service now derives everything from the seed and indices directly using
21
+ * the built-in TreeKey implementation. This interface is kept for backward compatibility
22
+ * only and will be removed in a future version.
23
+ */
24
+ export interface WotsSigningDependencies {
25
+ wotsSign?: (seed: Uint8Array, index: number, message: Uint8Array, paramSet: any) => Uint8Array;
26
+ fromHex?: (hex: string) => Uint8Array;
27
+ getParamSet?: (name: string) => any;
28
+ defaultParamSet?: any;
29
+ }
30
+ export declare class TransactionService {
31
+ private readonly http;
32
+ private readonly logger;
33
+ private readonly metrics;
34
+ private readonly config;
35
+ constructor(http: HttpClient, config: TransactionServiceConfig, logger?: LoggerAdapter, metrics?: MetricsAdapter);
36
+ prepare(params: PrepareRequest, rootPublicKey: string): Promise<PrepareResponse>;
37
+ /**
38
+ * Sign a transaction using per-address TreeKey architecture.
39
+ *
40
+ * Produces 3 proofs (Root→L1→L2→DATA) matching Minima's TreeKey.sign() exactly.
41
+ *
42
+ * @param request - Indices and digestTx from the /prepare response
43
+ * @param seed - 32-byte wallet base seed (from mnemonic)
44
+ * @param _deps - Deprecated, unused. Pass null or omit.
45
+ * @param _paramSet - Deprecated, unused. TreeKey uses its own param set.
46
+ */
47
+ sign(request: SignRequest, seed: Uint8Array, _deps?: WotsSigningDependencies | null, _paramSet?: string): Promise<SignResult>;
48
+ finalize(params: FinalizeRequest): Promise<FinalizeResponse>;
49
+ private bytesToHex;
50
+ }
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ /**
3
+ * @module TransactionService
4
+ * Handles WOTS signing flow: prepare → sign → finalize
5
+ *
6
+ * Platform-agnostic implementation using HttpClient adapter.
7
+ *
8
+ * Sign architecture (2026-02): uses per-address TreeKey (depth=3, keysPerLevel=64)
9
+ * matching Minima Wallet.java exactly. Each address has its own TreeKey derived
10
+ * from the wallet seed. Signing produces 3 proofs (Root→L1→L2→DATA).
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.TransactionService = void 0;
14
+ const index_js_1 = require("../adapters/index.js");
15
+ const treekey_js_1 = require("../treekey.js");
16
+ const mmr_js_1 = require("../mmr.js");
17
+ const wots_js_1 = require("../wots.js");
18
+ class TransactionService {
19
+ http;
20
+ logger;
21
+ metrics;
22
+ config;
23
+ constructor(http, config, logger = new index_js_1.NoopLogger(), metrics = new index_js_1.NoopMetrics()) {
24
+ this.http = http;
25
+ this.config = config;
26
+ this.logger = logger;
27
+ this.metrics = metrics;
28
+ }
29
+ async prepare(params, rootPublicKey) {
30
+ const startTime = Date.now();
31
+ const url = `${this.config.baseUrl.replace(/\/$/, '')}/v1/wots-hardened/prepare`;
32
+ const txId = params.txId || `tx-${Date.now()}-${Math.random().toString(36).slice(2)}`;
33
+ this.logger.debug('Requesting lease for transaction...');
34
+ this.logger.debug(` txId: ${txId}, to: ${params.to}, amount: ${params.amount}`);
35
+ if (typeof params.addressIndex !== 'number' || params.addressIndex < 0 || params.addressIndex >= 64) {
36
+ throw new Error('params.addressIndex is required and must be 0-63');
37
+ }
38
+ try {
39
+ const response = await this.http.post(url, {
40
+ txId,
41
+ rootPublicKey,
42
+ to: params.to,
43
+ amount: params.amount,
44
+ tokenId: params.tokenId || '0x00',
45
+ burn: params.burn || null,
46
+ paramSet: this.config.paramSet || 'v2-spec',
47
+ addressIndex: params.addressIndex,
48
+ }, {
49
+ headers: {
50
+ 'Content-Type': 'application/json',
51
+ 'x-api-key': this.config.apiKey,
52
+ },
53
+ });
54
+ if (response.status === 429) {
55
+ const body = response.data;
56
+ const err = new Error(body?.error || 'Daily signing limit reached');
57
+ err.code = 429;
58
+ err.limit = body?.limit;
59
+ err.used = body?.used;
60
+ err.retryAfter = body?.retryAfter;
61
+ throw err;
62
+ }
63
+ if (!response.ok) {
64
+ const body = response.data;
65
+ const msg = body?.error || body?.message || `HTTP ${response.status}`;
66
+ const err = new Error(msg);
67
+ err.code = response.status;
68
+ throw err;
69
+ }
70
+ const result = response.data;
71
+ const elapsed = Date.now() - startTime;
72
+ this.metrics.timing('tx_prepare_duration_ms', elapsed);
73
+ this.metrics.increment('tx_prepare_success');
74
+ this.logger.debug(`Lease acquired (${elapsed}ms)`);
75
+ this.logger.debug(` leaseId: ${result.leaseId}`);
76
+ this.logger.debug(` indices: (addressIndex=${result.addressIndex}, l1=${result.l1}, l2=${result.l2})`);
77
+ this.logger.debug(` TTL: ${result.leaseTTL}ms`);
78
+ return result;
79
+ }
80
+ catch (error) {
81
+ if (error.code === 429 || error.code === 400 || error.code === 403 || error.code === 409) {
82
+ throw error;
83
+ }
84
+ this.metrics.increment('tx_prepare_error');
85
+ this.logger.error('Lease acquisition failed:', error);
86
+ throw error;
87
+ }
88
+ }
89
+ /**
90
+ * Sign a transaction using per-address TreeKey architecture.
91
+ *
92
+ * Produces 3 proofs (Root→L1→L2→DATA) matching Minima's TreeKey.sign() exactly.
93
+ *
94
+ * @param request - Indices and digestTx from the /prepare response
95
+ * @param seed - 32-byte wallet base seed (from mnemonic)
96
+ * @param _deps - Deprecated, unused. Pass null or omit.
97
+ * @param _paramSet - Deprecated, unused. TreeKey uses its own param set.
98
+ */
99
+ async sign(request, seed, _deps, _paramSet) {
100
+ const startTime = Date.now();
101
+ const { addressIndex, l1, l2, digestTx } = request;
102
+ this.logger.debug('Starting per-address TreeKey signing...');
103
+ this.logger.debug(` indices: (addressIndex=${addressIndex}, l1=${l1}, l2=${l2})`);
104
+ if (addressIndex < 0 || addressIndex >= 64) {
105
+ throw new Error(`Invalid addressIndex: ${addressIndex}. Must be 0-63.`);
106
+ }
107
+ const digestBytes = (0, wots_js_1.fromHex)(digestTx);
108
+ if (digestBytes.length !== 32) {
109
+ throw new Error(`Invalid digest length: ${digestBytes.length}, expected 32 bytes`);
110
+ }
111
+ // Derive unified child TreeKey for this address index
112
+ this.logger.debug(` Deriving unified child TreeKey for index ${addressIndex}...`);
113
+ const treeKey = (0, treekey_js_1.createUnifiedChildTreeKey)(seed, addressIndex);
114
+ // Convert (l1, l2) to Java-compatible 'uses' counter
115
+ // Java's TreeKey.sign() uses baseConversion(uses) to compute path
116
+ const KEYS_PER_LEVEL = 64;
117
+ const uses = l1 * KEYS_PER_LEVEL + l2;
118
+ this.logger.debug(` Converting indices to uses: (l1=${l1}, l2=${l2}) → uses=${uses}`);
119
+ // setUses() + sign() matches Java pattern: tk.setUses(uses) → tk.sign(zData)
120
+ treeKey.setUses(uses);
121
+ const treeSignature = treeKey.sign(digestBytes);
122
+ const newUses = treeKey.getUses();
123
+ this.logger.debug(` TreeKey signing complete, proofs: ${treeSignature.proofs.length}, newUses: ${newUses}`);
124
+ // Build HierarchicalWitnessBundle with unified naming
125
+ const treeKeyPubkey = treeKey.getPublicKey();
126
+ const witnessBundle = {
127
+ addressIndex,
128
+ l1,
129
+ l2,
130
+ rootPublicKey: `0x${this.bytesToHex(treeKeyPubkey)}`,
131
+ proofs: treeSignature.proofs.map((proof) => ({
132
+ leafPubkey: `0x${this.bytesToHex(proof.leafPubkey)}`,
133
+ signature: `0x${this.bytesToHex(proof.signature)}`,
134
+ mmrProof: `0x${this.bytesToHex((0, mmr_js_1.serializeMMRProof)(proof.mmrProof))}`,
135
+ })),
136
+ };
137
+ // Serialize to byte-exact Java-compatible wire format
138
+ const serializedBytes = (0, treekey_js_1.serializeTreeSignature)(treeSignature);
139
+ const signedHex = `0x${this.bytesToHex(serializedBytes)}`;
140
+ const elapsed = Date.now() - startTime;
141
+ this.metrics.timing('tx_sign_duration_ms', elapsed);
142
+ this.logger.debug(`Signature created (${elapsed}ms), serialized: ${serializedBytes.length} bytes`);
143
+ return { witnessBundle, signedHex };
144
+ }
145
+ async finalize(params) {
146
+ const startTime = Date.now();
147
+ const url = `${this.config.baseUrl.replace(/\/$/, '')}/v1/wots-hardened/finalize`;
148
+ this.logger.debug('Submitting signed transaction to network...');
149
+ this.logger.debug(` leaseToken: ${params.leaseToken.slice(0, 16)}...`);
150
+ try {
151
+ const response = await this.http.post(url, params, {
152
+ headers: {
153
+ 'Content-Type': 'application/json',
154
+ 'x-api-key': this.config.apiKey,
155
+ },
156
+ });
157
+ if (!response.ok) {
158
+ const body = response.data;
159
+ const msg = body?.error || body?.message || `HTTP ${response.status}`;
160
+ const err = new Error(msg);
161
+ err.code = response.status;
162
+ throw err;
163
+ }
164
+ const result = response.data;
165
+ const elapsed = Date.now() - startTime;
166
+ this.metrics.timing('tx_finalize_duration_ms', elapsed);
167
+ this.metrics.increment('tx_finalize_success');
168
+ this.logger.debug(`Transaction finalized (${elapsed}ms)`);
169
+ this.logger.debug(` txpowid: ${result.txpowid}, leaseId: ${result.leaseId}`);
170
+ return result;
171
+ }
172
+ catch (error) {
173
+ if (error.code !== undefined)
174
+ throw error;
175
+ this.metrics.increment('tx_finalize_error');
176
+ this.logger.error('Finalization failed:', error);
177
+ throw error;
178
+ }
179
+ }
180
+ bytesToHex(bytes) {
181
+ return Array.from(bytes)
182
+ .map(b => b.toString(16).padStart(2, '0'))
183
+ .join('');
184
+ }
185
+ }
186
+ exports.TransactionService = TransactionService;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @module @totemsdk/core/tx
3
+ * Transaction service and lifecycle management
4
+ *
5
+ * This module provides:
6
+ * - TransactionService: WOTS signing flow (prepare → sign → finalize)
7
+ * - TransactionLifecycle: Orchestrates flow with lease/watermark stores
8
+ * - TransactionReceiptStore: Persistent transaction history
9
+ */
10
+ export { TransactionService, type TransactionServiceConfig, type WotsSigningDependencies, } from './TransactionService.js';
11
+ export { TransactionLifecycle, TransactionLifecycleError, WatermarkExhaustedError, type TransactionLifecycleConfig, type WatermarkSyncFunction, type PrepareResult, } from './TransactionLifecycle.js';
12
+ export { TransactionReceiptStore, type TransactionReceiptStoreConfig, } from './TransactionReceiptStore.js';
13
+ export type { WotsIndices, PrepareRequest, PrepareResponse, SignRequest, SignResult, HierarchicalWitnessBundle, WitnessBundle, FinalizeRequest, FinalizeResponse, TransactionMetadata, TransactionReceipt, TransactionError, } from './types.js';
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ /**
3
+ * @module @totemsdk/core/tx
4
+ * Transaction service and lifecycle management
5
+ *
6
+ * This module provides:
7
+ * - TransactionService: WOTS signing flow (prepare → sign → finalize)
8
+ * - TransactionLifecycle: Orchestrates flow with lease/watermark stores
9
+ * - TransactionReceiptStore: Persistent transaction history
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.TransactionReceiptStore = exports.WatermarkExhaustedError = exports.TransactionLifecycleError = exports.TransactionLifecycle = exports.TransactionService = void 0;
13
+ var TransactionService_js_1 = require("./TransactionService.js");
14
+ Object.defineProperty(exports, "TransactionService", { enumerable: true, get: function () { return TransactionService_js_1.TransactionService; } });
15
+ var TransactionLifecycle_js_1 = require("./TransactionLifecycle.js");
16
+ Object.defineProperty(exports, "TransactionLifecycle", { enumerable: true, get: function () { return TransactionLifecycle_js_1.TransactionLifecycle; } });
17
+ Object.defineProperty(exports, "TransactionLifecycleError", { enumerable: true, get: function () { return TransactionLifecycle_js_1.TransactionLifecycleError; } });
18
+ Object.defineProperty(exports, "WatermarkExhaustedError", { enumerable: true, get: function () { return TransactionLifecycle_js_1.WatermarkExhaustedError; } });
19
+ var TransactionReceiptStore_js_1 = require("./TransactionReceiptStore.js");
20
+ Object.defineProperty(exports, "TransactionReceiptStore", { enumerable: true, get: function () { return TransactionReceiptStore_js_1.TransactionReceiptStore; } });