@movebridge/core 0.0.1

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.
package/dist/index.js ADDED
@@ -0,0 +1,1025 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ContractInterface: () => ContractInterface,
34
+ DEFAULT_COIN_TYPE: () => DEFAULT_COIN_TYPE,
35
+ Errors: () => Errors,
36
+ EventListener: () => EventListener,
37
+ Movement: () => Movement,
38
+ MovementError: () => MovementError,
39
+ NETWORK_CONFIG: () => NETWORK_CONFIG,
40
+ TransactionBuilder: () => TransactionBuilder,
41
+ WalletManager: () => WalletManager,
42
+ getExplorerAccountUrl: () => getExplorerAccountUrl,
43
+ getExplorerTxUrl: () => getExplorerTxUrl,
44
+ isMovementError: () => isMovementError,
45
+ isValidAddress: () => isValidAddress,
46
+ isValidEventHandle: () => isValidEventHandle,
47
+ resolveConfig: () => resolveConfig,
48
+ wrapError: () => wrapError
49
+ });
50
+ module.exports = __toCommonJS(index_exports);
51
+
52
+ // src/client.ts
53
+ var import_ts_sdk = require("@aptos-labs/ts-sdk");
54
+
55
+ // src/config.ts
56
+ var NETWORK_CONFIG = {
57
+ mainnet: {
58
+ chainId: 126,
59
+ rpcUrl: "https://full.mainnet.movementinfra.xyz/v1",
60
+ indexerUrl: "https://indexer.mainnet.movementnetwork.xyz/v1/graphql",
61
+ explorerUrl: "https://explorer.movementnetwork.xyz/?network=mainnet"
62
+ },
63
+ testnet: {
64
+ chainId: 250,
65
+ rpcUrl: "https://testnet.movementnetwork.xyz/v1",
66
+ indexerUrl: null,
67
+ explorerUrl: "https://explorer.movementnetwork.xyz/?network=bardock+testnet",
68
+ faucetUrl: "https://faucet.testnet.movementnetwork.xyz/"
69
+ }
70
+ };
71
+ var DEFAULT_COIN_TYPE = "0x1::aptos_coin::AptosCoin";
72
+ function resolveConfig(config) {
73
+ const networkConfig = NETWORK_CONFIG[config.network];
74
+ return {
75
+ network: config.network,
76
+ chainId: networkConfig.chainId,
77
+ rpcUrl: config.rpcUrl ?? networkConfig.rpcUrl,
78
+ indexerUrl: config.indexerUrl ?? networkConfig.indexerUrl,
79
+ explorerUrl: networkConfig.explorerUrl,
80
+ autoConnect: config.autoConnect ?? false
81
+ };
82
+ }
83
+ function isValidAddress(address) {
84
+ const addressRegex = /^0x[a-fA-F0-9]{1,64}$/;
85
+ return addressRegex.test(address);
86
+ }
87
+ function isValidEventHandle(eventHandle) {
88
+ const eventHandleRegex = /^0x[a-fA-F0-9]{1,64}::[a-zA-Z_][a-zA-Z0-9_]*::[a-zA-Z_][a-zA-Z0-9_]*$/;
89
+ return eventHandleRegex.test(eventHandle);
90
+ }
91
+ function getExplorerTxUrl(network, txHash) {
92
+ const config = NETWORK_CONFIG[network];
93
+ return `${config.explorerUrl}&txn=${txHash}`;
94
+ }
95
+ function getExplorerAccountUrl(network, address) {
96
+ const config = NETWORK_CONFIG[network];
97
+ return `${config.explorerUrl}&account=${address}`;
98
+ }
99
+
100
+ // src/errors.ts
101
+ var MovementError = class _MovementError extends Error {
102
+ constructor(message, code, details) {
103
+ super(message);
104
+ this.code = code;
105
+ this.details = details;
106
+ if (Error.captureStackTrace) {
107
+ Error.captureStackTrace(this, _MovementError);
108
+ }
109
+ Object.setPrototypeOf(this, _MovementError.prototype);
110
+ }
111
+ name = "MovementError";
112
+ /**
113
+ * Serializes the error to a JSON-compatible object
114
+ */
115
+ toJSON() {
116
+ return {
117
+ name: this.name,
118
+ message: this.message,
119
+ code: this.code,
120
+ details: this.details
121
+ };
122
+ }
123
+ /**
124
+ * Creates a string representation of the error
125
+ */
126
+ toString() {
127
+ return `${this.name} [${this.code}]: ${this.message}`;
128
+ }
129
+ };
130
+ var Errors = {
131
+ /**
132
+ * Creates an invalid address error
133
+ */
134
+ invalidAddress(address, reason) {
135
+ return new MovementError(
136
+ `Invalid address: ${address}${reason ? ` - ${reason}` : ""}`,
137
+ "INVALID_ADDRESS",
138
+ { address, reason }
139
+ );
140
+ },
141
+ /**
142
+ * Creates a wallet not found error
143
+ */
144
+ walletNotFound(wallet, available) {
145
+ return new MovementError(
146
+ `Wallet "${wallet}" not found. Available wallets: ${available.join(", ") || "none"}`,
147
+ "WALLET_NOT_FOUND",
148
+ { wallet, available }
149
+ );
150
+ },
151
+ /**
152
+ * Creates a wallet connection failed error
153
+ */
154
+ walletConnectionFailed(wallet, originalError) {
155
+ const errorMessage = originalError instanceof Error ? originalError.message : String(originalError);
156
+ return new MovementError(
157
+ `Failed to connect to wallet "${wallet}": ${errorMessage}`,
158
+ "WALLET_CONNECTION_FAILED",
159
+ { wallet, originalError }
160
+ );
161
+ },
162
+ /**
163
+ * Creates a wallet not connected error
164
+ */
165
+ walletNotConnected() {
166
+ return new MovementError(
167
+ "No wallet connected. Please connect a wallet first.",
168
+ "WALLET_NOT_CONNECTED"
169
+ );
170
+ },
171
+ /**
172
+ * Creates a transaction failed error
173
+ */
174
+ transactionFailed(hash, vmStatus, gasUsed) {
175
+ return new MovementError(
176
+ `Transaction ${hash} failed with status: ${vmStatus}`,
177
+ "TRANSACTION_FAILED",
178
+ { hash, vmStatus, gasUsed }
179
+ );
180
+ },
181
+ /**
182
+ * Creates a transaction timeout error
183
+ */
184
+ transactionTimeout(hash) {
185
+ return new MovementError(
186
+ `Transaction ${hash} timed out waiting for confirmation`,
187
+ "TRANSACTION_TIMEOUT",
188
+ { hash }
189
+ );
190
+ },
191
+ /**
192
+ * Creates a view function failed error
193
+ */
194
+ viewFunctionFailed(functionName, args, originalError) {
195
+ const errorMessage = originalError instanceof Error ? originalError.message : String(originalError);
196
+ return new MovementError(
197
+ `View function "${functionName}" failed: ${errorMessage}`,
198
+ "VIEW_FUNCTION_FAILED",
199
+ { function: functionName, args, originalError }
200
+ );
201
+ },
202
+ /**
203
+ * Creates an invalid event handle error
204
+ */
205
+ invalidEventHandle(eventHandle) {
206
+ return new MovementError(
207
+ `Invalid event handle format: ${eventHandle}`,
208
+ "INVALID_EVENT_HANDLE",
209
+ { eventHandle, expectedFormat: "0xADDRESS::module::EventType" }
210
+ );
211
+ },
212
+ /**
213
+ * Creates a network error
214
+ */
215
+ networkError(url, httpStatus, responseBody) {
216
+ return new MovementError(
217
+ `Network request to ${url} failed${httpStatus ? ` with status ${httpStatus}` : ""}`,
218
+ "NETWORK_ERROR",
219
+ { url, httpStatus, responseBody }
220
+ );
221
+ },
222
+ /**
223
+ * Creates an ABI fetch failed error
224
+ */
225
+ abiFetchFailed(address, network, originalError) {
226
+ const errorMessage = originalError instanceof Error ? originalError.message : String(originalError);
227
+ return new MovementError(
228
+ `Failed to fetch ABI for ${address} on ${network}: ${errorMessage}`,
229
+ "ABI_FETCH_FAILED",
230
+ { address, network, originalError }
231
+ );
232
+ },
233
+ /**
234
+ * Creates a codegen failed error
235
+ */
236
+ codegenFailed(reason, abi) {
237
+ return new MovementError(
238
+ `Code generation failed: ${reason}`,
239
+ "CODEGEN_FAILED",
240
+ { reason, abi }
241
+ );
242
+ },
243
+ /**
244
+ * Creates an invalid argument error
245
+ */
246
+ invalidArgument(argument, reason) {
247
+ return new MovementError(
248
+ `Invalid argument "${argument}": ${reason}`,
249
+ "INVALID_ARGUMENT",
250
+ { argument, reason }
251
+ );
252
+ }
253
+ };
254
+ function isMovementError(error) {
255
+ return error instanceof MovementError;
256
+ }
257
+ function wrapError(error, code, context) {
258
+ if (isMovementError(error)) {
259
+ return error;
260
+ }
261
+ const message = error instanceof Error ? error.message : String(error);
262
+ return new MovementError(
263
+ context ? `${context}: ${message}` : message,
264
+ code,
265
+ { originalError: error }
266
+ );
267
+ }
268
+
269
+ // src/wallet.ts
270
+ var import_eventemitter3 = __toESM(require("eventemitter3"));
271
+ var import_wallet_standard = require("@aptos-labs/wallet-standard");
272
+ var SUPPORTED_WALLETS = {
273
+ "petra": "petra",
274
+ "petra wallet": "petra",
275
+ "pontem": "pontem",
276
+ "pontem wallet": "pontem",
277
+ "nightly": "nightly",
278
+ "nightly wallet": "nightly"
279
+ };
280
+ var STORAGE_KEY = "movebridge:lastWallet";
281
+ function toHexString(data) {
282
+ if (typeof data === "string") return data;
283
+ if (data instanceof Uint8Array) {
284
+ return "0x" + Array.from(data).map((b) => b.toString(16).padStart(2, "0")).join("");
285
+ }
286
+ if (data && typeof data.toString === "function") {
287
+ return data.toString();
288
+ }
289
+ return String(data);
290
+ }
291
+ function createStandardAdapter(wallet) {
292
+ const connectFeature = wallet.features?.["aptos:connect"];
293
+ const disconnectFeature = wallet.features?.["aptos:disconnect"];
294
+ const signTxFeature = wallet.features?.["aptos:signAndSubmitTransaction"];
295
+ const signOnlyFeature = wallet.features?.["aptos:signTransaction"];
296
+ const accountChangeFeature = wallet.features?.["aptos:onAccountChange"];
297
+ const networkChangeFeature = wallet.features?.["aptos:onNetworkChange"];
298
+ return {
299
+ name: wallet.name,
300
+ icon: wallet.icon || "",
301
+ async connect() {
302
+ if (!connectFeature) throw new Error("Wallet does not support connect");
303
+ const response = await connectFeature.connect();
304
+ const result = response?.args ?? response;
305
+ if (response?.status === "rejected") {
306
+ throw new Error("User rejected the connection");
307
+ }
308
+ return {
309
+ address: toHexString(result.address),
310
+ publicKey: toHexString(result.publicKey)
311
+ };
312
+ },
313
+ async disconnect() {
314
+ if (disconnectFeature) await disconnectFeature.disconnect();
315
+ },
316
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
317
+ async signAndSubmitTransaction(payload) {
318
+ if (!signTxFeature) throw new Error("Wallet does not support signAndSubmitTransaction");
319
+ const response = await signTxFeature.signAndSubmitTransaction(payload);
320
+ if (response?.status === "rejected") {
321
+ throw new Error("User rejected the transaction");
322
+ }
323
+ const result = response?.args ?? response;
324
+ return { hash: result.hash || toHexString(result) };
325
+ },
326
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
327
+ async signTransaction(payload) {
328
+ if (!signOnlyFeature) throw new Error("Wallet does not support signTransaction");
329
+ const response = await signOnlyFeature.signTransaction(payload);
330
+ if (response?.status === "rejected") {
331
+ throw new Error("User rejected the transaction");
332
+ }
333
+ const result = response?.args ?? response;
334
+ return result.authenticator || result.signature || new Uint8Array();
335
+ },
336
+ onAccountChange(cb) {
337
+ if (accountChangeFeature) {
338
+ accountChangeFeature.onAccountChange((account) => {
339
+ if (account) {
340
+ cb({ address: toHexString(account.address), publicKey: toHexString(account.publicKey) });
341
+ } else {
342
+ cb(null);
343
+ }
344
+ });
345
+ }
346
+ },
347
+ onNetworkChange(cb) {
348
+ if (networkChangeFeature) {
349
+ networkChangeFeature.onNetworkChange((network) => {
350
+ cb(network?.name || String(network));
351
+ });
352
+ }
353
+ }
354
+ };
355
+ }
356
+ var WalletManager = class extends import_eventemitter3.default {
357
+ state = { connected: false, address: null, publicKey: null };
358
+ currentWallet = null;
359
+ adapter = null;
360
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
361
+ standardWallets = /* @__PURE__ */ new Map();
362
+ detectedWallets = [];
363
+ unsubscribe = null;
364
+ detectWallets() {
365
+ if (typeof window === "undefined") return [];
366
+ const available = /* @__PURE__ */ new Set();
367
+ this.standardWallets.clear();
368
+ try {
369
+ const { aptosWallets, on } = (0, import_wallet_standard.getAptosWallets)();
370
+ if (this.unsubscribe) this.unsubscribe();
371
+ this.unsubscribe = on("register", () => this.detectWallets());
372
+ for (const wallet of aptosWallets) {
373
+ const normalizedName = wallet.name.toLowerCase();
374
+ const walletType = SUPPORTED_WALLETS[normalizedName];
375
+ if (walletType) {
376
+ available.add(walletType);
377
+ this.standardWallets.set(walletType, wallet);
378
+ }
379
+ }
380
+ } catch (error) {
381
+ console.warn("Failed to detect wallets via AIP-62 standard:", error);
382
+ }
383
+ this.detectedWallets = Array.from(available);
384
+ return this.detectedWallets;
385
+ }
386
+ getWalletInfo() {
387
+ return this.detectedWallets.map((type) => {
388
+ const wallet = this.standardWallets.get(type);
389
+ return { type, name: wallet?.name || type, icon: wallet?.icon || "" };
390
+ });
391
+ }
392
+ async connect(wallet) {
393
+ const available = this.detectWallets();
394
+ if (!available.includes(wallet)) throw Errors.walletNotFound(wallet, available);
395
+ try {
396
+ const standardWallet = this.standardWallets.get(wallet);
397
+ if (!standardWallet) throw new Error(`Wallet ${wallet} not found`);
398
+ this.adapter = createStandardAdapter(standardWallet);
399
+ const result = await this.adapter.connect();
400
+ this.state = { connected: true, address: result.address, publicKey: result.publicKey };
401
+ this.currentWallet = wallet;
402
+ this.saveLastWallet(wallet);
403
+ this.setupEventListeners();
404
+ this.emit("connect", result.address);
405
+ } catch (error) {
406
+ this.adapter = null;
407
+ throw Errors.walletConnectionFailed(wallet, error);
408
+ }
409
+ }
410
+ async disconnect() {
411
+ if (this.adapter) {
412
+ try {
413
+ await this.adapter.disconnect();
414
+ } catch {
415
+ }
416
+ }
417
+ this.state = { connected: false, address: null, publicKey: null };
418
+ this.currentWallet = null;
419
+ this.adapter = null;
420
+ this.clearLastWallet();
421
+ this.emit("disconnect");
422
+ }
423
+ getState() {
424
+ return { ...this.state };
425
+ }
426
+ getWallet() {
427
+ return this.currentWallet;
428
+ }
429
+ getAdapter() {
430
+ return this.adapter;
431
+ }
432
+ async autoConnect() {
433
+ const lastWallet = this.getLastWallet();
434
+ if (!lastWallet) return;
435
+ const available = this.detectWallets();
436
+ if (available.includes(lastWallet)) {
437
+ try {
438
+ await this.connect(lastWallet);
439
+ } catch {
440
+ this.clearLastWallet();
441
+ }
442
+ }
443
+ }
444
+ destroy() {
445
+ if (this.unsubscribe) {
446
+ this.unsubscribe();
447
+ this.unsubscribe = null;
448
+ }
449
+ }
450
+ setupEventListeners() {
451
+ if (!this.adapter) return;
452
+ this.adapter.onAccountChange((account) => {
453
+ if (account) {
454
+ this.state = { connected: true, address: account.address, publicKey: account.publicKey };
455
+ this.emit("accountChanged", account.address);
456
+ } else {
457
+ this.state = { connected: false, address: null, publicKey: null };
458
+ this.emit("disconnect");
459
+ }
460
+ });
461
+ this.adapter.onNetworkChange((network) => this.emit("networkChanged", network));
462
+ }
463
+ saveLastWallet(wallet) {
464
+ try {
465
+ if (typeof localStorage !== "undefined") localStorage.setItem(STORAGE_KEY, wallet);
466
+ } catch {
467
+ }
468
+ }
469
+ getLastWallet() {
470
+ try {
471
+ if (typeof localStorage !== "undefined") {
472
+ const wallet = localStorage.getItem(STORAGE_KEY);
473
+ if (wallet && Object.values(SUPPORTED_WALLETS).includes(wallet)) {
474
+ return wallet;
475
+ }
476
+ }
477
+ } catch {
478
+ }
479
+ return null;
480
+ }
481
+ clearLastWallet() {
482
+ try {
483
+ if (typeof localStorage !== "undefined") localStorage.removeItem(STORAGE_KEY);
484
+ } catch {
485
+ }
486
+ }
487
+ };
488
+
489
+ // src/transaction.ts
490
+ var TransactionBuilder = class {
491
+ constructor(aptosClient, walletManager) {
492
+ this.aptosClient = aptosClient;
493
+ this.walletManager = walletManager;
494
+ }
495
+ /**
496
+ * Builds a transfer transaction payload
497
+ * @param options - Transfer options
498
+ * @returns Transaction payload
499
+ */
500
+ async transfer(options) {
501
+ const coinType = options.coinType ?? DEFAULT_COIN_TYPE;
502
+ return {
503
+ type: "entry_function_payload",
504
+ function: "0x1::coin::transfer",
505
+ typeArguments: [coinType],
506
+ arguments: [options.to, options.amount]
507
+ };
508
+ }
509
+ /**
510
+ * Builds a generic transaction payload
511
+ * @param options - Build options
512
+ * @returns Transaction payload
513
+ */
514
+ async build(options) {
515
+ return {
516
+ type: "entry_function_payload",
517
+ function: options.function,
518
+ typeArguments: options.typeArguments,
519
+ arguments: options.arguments
520
+ };
521
+ }
522
+ /**
523
+ * Signs a transaction payload
524
+ * @param payload - Transaction payload to sign
525
+ * @returns Signed transaction
526
+ * @throws MovementError with code WALLET_NOT_CONNECTED if no wallet is connected
527
+ */
528
+ async sign(payload) {
529
+ const adapter = this.walletManager.getAdapter();
530
+ const state = this.walletManager.getState();
531
+ if (!adapter || !state.connected || !state.address) {
532
+ throw Errors.walletNotConnected();
533
+ }
534
+ try {
535
+ const signatureBytes = await adapter.signTransaction({
536
+ type: payload.type,
537
+ function: payload.function,
538
+ type_arguments: payload.typeArguments,
539
+ arguments: payload.arguments
540
+ });
541
+ const signature = Array.from(signatureBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
542
+ return {
543
+ payload,
544
+ signature: `0x${signature}`,
545
+ sender: state.address
546
+ };
547
+ } catch (error) {
548
+ throw wrapError(error, "TRANSACTION_FAILED", "Failed to sign transaction");
549
+ }
550
+ }
551
+ /**
552
+ * Submits a signed transaction to the network
553
+ * @param signed - Signed transaction
554
+ * @returns Transaction hash
555
+ */
556
+ async submit(signed) {
557
+ const adapter = this.walletManager.getAdapter();
558
+ if (!adapter) {
559
+ throw Errors.walletNotConnected();
560
+ }
561
+ try {
562
+ const result = await adapter.signAndSubmitTransaction({
563
+ type: signed.payload.type,
564
+ function: signed.payload.function,
565
+ type_arguments: signed.payload.typeArguments,
566
+ arguments: signed.payload.arguments
567
+ });
568
+ return result.hash;
569
+ } catch (error) {
570
+ throw wrapError(error, "TRANSACTION_FAILED", "Failed to submit transaction");
571
+ }
572
+ }
573
+ /**
574
+ * Signs and submits a transaction in one step
575
+ * @param payload - Transaction payload
576
+ * @returns Transaction hash
577
+ */
578
+ async signAndSubmit(payload) {
579
+ const adapter = this.walletManager.getAdapter();
580
+ if (!adapter) {
581
+ throw Errors.walletNotConnected();
582
+ }
583
+ try {
584
+ const result = await adapter.signAndSubmitTransaction({
585
+ type: payload.type,
586
+ function: payload.function,
587
+ type_arguments: payload.typeArguments,
588
+ arguments: payload.arguments
589
+ });
590
+ return result.hash;
591
+ } catch (error) {
592
+ throw wrapError(error, "TRANSACTION_FAILED", "Failed to sign and submit transaction");
593
+ }
594
+ }
595
+ /**
596
+ * Simulates a transaction without submitting
597
+ * @param payload - Transaction payload
598
+ * @returns Simulation result with gas estimate
599
+ */
600
+ async simulate(payload) {
601
+ const state = this.walletManager.getState();
602
+ if (!state.connected || !state.address) {
603
+ throw Errors.walletNotConnected();
604
+ }
605
+ try {
606
+ const client = this.aptosClient;
607
+ const result = await client.transaction.simulate.simple({
608
+ sender: state.address,
609
+ data: {
610
+ function: payload.function,
611
+ typeArguments: payload.typeArguments,
612
+ functionArguments: payload.arguments
613
+ }
614
+ });
615
+ const simResult = result[0];
616
+ return {
617
+ success: simResult?.success ?? false,
618
+ gasUsed: String(simResult?.gas_used ?? "0"),
619
+ vmStatus: String(simResult?.vm_status ?? "unknown")
620
+ };
621
+ } catch (error) {
622
+ throw wrapError(error, "TRANSACTION_FAILED", "Failed to simulate transaction");
623
+ }
624
+ }
625
+ };
626
+
627
+ // src/contract.ts
628
+ var ContractInterface = class {
629
+ constructor(aptosClient, walletManager, options) {
630
+ this.aptosClient = aptosClient;
631
+ this.walletManager = walletManager;
632
+ this.address = options.address;
633
+ this.module = options.module;
634
+ }
635
+ /** Contract address */
636
+ address;
637
+ /** Module name */
638
+ module;
639
+ /**
640
+ * Calls a view function (read-only)
641
+ * @param functionName - Name of the view function
642
+ * @param args - Function arguments
643
+ * @param typeArgs - Type arguments (optional)
644
+ * @returns Function result
645
+ * @throws MovementError with code VIEW_FUNCTION_FAILED if call fails
646
+ */
647
+ async view(functionName, args, typeArgs = []) {
648
+ const fullFunctionName = `${this.address}::${this.module}::${functionName}`;
649
+ try {
650
+ const client = this.aptosClient;
651
+ const result = await client.view({
652
+ payload: {
653
+ function: fullFunctionName,
654
+ typeArguments: typeArgs,
655
+ functionArguments: args
656
+ }
657
+ });
658
+ return result.length === 1 ? result[0] : result;
659
+ } catch (error) {
660
+ throw Errors.viewFunctionFailed(fullFunctionName, args, error);
661
+ }
662
+ }
663
+ /**
664
+ * Calls an entry function (write operation)
665
+ * @param functionName - Name of the entry function
666
+ * @param args - Function arguments
667
+ * @param typeArgs - Type arguments (optional)
668
+ * @returns Transaction hash
669
+ * @throws MovementError with code WALLET_NOT_CONNECTED if no wallet is connected
670
+ * @throws MovementError with code TRANSACTION_FAILED if transaction fails
671
+ */
672
+ async call(functionName, args, typeArgs = []) {
673
+ const adapter = this.walletManager.getAdapter();
674
+ const state = this.walletManager.getState();
675
+ if (!adapter || !state.connected) {
676
+ throw Errors.walletNotConnected();
677
+ }
678
+ const fullFunctionName = `${this.address}::${this.module}::${functionName}`;
679
+ try {
680
+ const result = await adapter.signAndSubmitTransaction({
681
+ type: "entry_function_payload",
682
+ function: fullFunctionName,
683
+ type_arguments: typeArgs,
684
+ arguments: args
685
+ });
686
+ return result.hash;
687
+ } catch (error) {
688
+ throw wrapError(error, "TRANSACTION_FAILED", `Failed to call ${fullFunctionName}`);
689
+ }
690
+ }
691
+ /**
692
+ * Checks if a resource exists at the contract address
693
+ * @param resourceType - Full resource type (e.g., '0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>')
694
+ * @returns true if resource exists
695
+ */
696
+ async hasResource(resourceType) {
697
+ try {
698
+ const client = this.aptosClient;
699
+ await client.getAccountResource({
700
+ accountAddress: this.address,
701
+ resourceType
702
+ });
703
+ return true;
704
+ } catch {
705
+ return false;
706
+ }
707
+ }
708
+ /**
709
+ * Gets a resource from the contract address
710
+ * @param resourceType - Full resource type
711
+ * @returns Resource data or null if not found
712
+ */
713
+ async getResource(resourceType) {
714
+ try {
715
+ const client = this.aptosClient;
716
+ const resource = await client.getAccountResource({
717
+ accountAddress: this.address,
718
+ resourceType
719
+ });
720
+ return resource;
721
+ } catch {
722
+ return null;
723
+ }
724
+ }
725
+ /**
726
+ * Gets the full function name for a function in this module
727
+ * @param functionName - Function name
728
+ * @returns Full function name (address::module::function)
729
+ */
730
+ getFullFunctionName(functionName) {
731
+ return `${this.address}::${this.module}::${functionName}`;
732
+ }
733
+ };
734
+
735
+ // src/events.ts
736
+ var EventListener = class {
737
+ constructor(aptosClient, options) {
738
+ this.aptosClient = aptosClient;
739
+ this.pollIntervalMs = options?.pollIntervalMs ?? 3e3;
740
+ }
741
+ subscriptions = /* @__PURE__ */ new Map();
742
+ subscriptionCounter = 0;
743
+ pollIntervalMs;
744
+ /**
745
+ * Subscribes to contract events
746
+ * @param subscription - Subscription configuration
747
+ * @returns Subscription ID
748
+ * @throws MovementError with code INVALID_EVENT_HANDLE if event handle is invalid
749
+ */
750
+ subscribe(subscription) {
751
+ if (!isValidEventHandle(subscription.eventHandle)) {
752
+ throw Errors.invalidEventHandle(subscription.eventHandle);
753
+ }
754
+ const subscriptionId = `sub_${++this.subscriptionCounter}`;
755
+ const internalSub = {
756
+ eventHandle: subscription.eventHandle,
757
+ callback: subscription.callback,
758
+ lastSequenceNumber: "0",
759
+ intervalId: null
760
+ };
761
+ internalSub.intervalId = setInterval(() => {
762
+ this.pollEvents(subscriptionId).catch(() => {
763
+ });
764
+ }, this.pollIntervalMs);
765
+ this.pollEvents(subscriptionId).catch(() => {
766
+ });
767
+ this.subscriptions.set(subscriptionId, internalSub);
768
+ return subscriptionId;
769
+ }
770
+ /**
771
+ * Unsubscribes from events
772
+ * @param subscriptionId - Subscription ID to remove
773
+ */
774
+ unsubscribe(subscriptionId) {
775
+ const subscription = this.subscriptions.get(subscriptionId);
776
+ if (subscription) {
777
+ if (subscription.intervalId) {
778
+ clearInterval(subscription.intervalId);
779
+ }
780
+ this.subscriptions.delete(subscriptionId);
781
+ }
782
+ }
783
+ /**
784
+ * Unsubscribes from all events
785
+ */
786
+ unsubscribeAll() {
787
+ for (const [id] of this.subscriptions) {
788
+ this.unsubscribe(id);
789
+ }
790
+ }
791
+ /**
792
+ * Gets active subscription count
793
+ * @returns Number of active subscriptions
794
+ */
795
+ getSubscriptionCount() {
796
+ return this.subscriptions.size;
797
+ }
798
+ /**
799
+ * Checks if a subscription exists
800
+ * @param subscriptionId - Subscription ID
801
+ * @returns true if subscription exists
802
+ */
803
+ hasSubscription(subscriptionId) {
804
+ return this.subscriptions.has(subscriptionId);
805
+ }
806
+ /**
807
+ * Polls for new events
808
+ * @param subscriptionId - Subscription ID
809
+ */
810
+ async pollEvents(subscriptionId) {
811
+ const subscription = this.subscriptions.get(subscriptionId);
812
+ if (!subscription) {
813
+ return;
814
+ }
815
+ try {
816
+ const parts = subscription.eventHandle.split("::");
817
+ if (parts.length !== 3) {
818
+ return;
819
+ }
820
+ const [address, module2, eventType] = parts;
821
+ const eventHandleStruct = `${address}::${module2}::${eventType}`;
822
+ const client = this.aptosClient;
823
+ const events = await client.getAccountEventsByEventType({
824
+ accountAddress: address,
825
+ eventType: eventHandleStruct,
826
+ options: {
827
+ limit: 25
828
+ }
829
+ });
830
+ for (const event of events) {
831
+ const sequenceNumber = event.sequence_number?.toString() ?? "0";
832
+ if (BigInt(sequenceNumber) > BigInt(subscription.lastSequenceNumber)) {
833
+ const contractEvent = {
834
+ type: event.type,
835
+ sequenceNumber,
836
+ data: event.data
837
+ };
838
+ subscription.callback(contractEvent);
839
+ subscription.lastSequenceNumber = sequenceNumber;
840
+ }
841
+ }
842
+ } catch {
843
+ }
844
+ }
845
+ };
846
+
847
+ // src/client.ts
848
+ var Movement = class {
849
+ /** Resolved configuration */
850
+ config;
851
+ /** Aptos SDK client instance */
852
+ aptosClient;
853
+ /** Wallet manager instance */
854
+ wallet;
855
+ /** Transaction builder instance */
856
+ transaction;
857
+ /** Event listener instance */
858
+ events;
859
+ /**
860
+ * Creates a new Movement client
861
+ * @param config - Configuration options
862
+ */
863
+ constructor(config) {
864
+ this.config = resolveConfig(config);
865
+ const aptosConfigOptions = {
866
+ network: import_ts_sdk.Network.CUSTOM,
867
+ fullnode: this.config.rpcUrl
868
+ };
869
+ if (this.config.indexerUrl) {
870
+ aptosConfigOptions.indexer = this.config.indexerUrl;
871
+ }
872
+ const aptosConfig = new import_ts_sdk.AptosConfig(aptosConfigOptions);
873
+ this.aptosClient = new import_ts_sdk.Aptos(aptosConfig);
874
+ this.wallet = new WalletManager();
875
+ this.transaction = new TransactionBuilder(this.aptosClient, this.wallet);
876
+ this.events = new EventListener(this.aptosClient);
877
+ if (this.config.autoConnect) {
878
+ this.wallet.autoConnect().catch(() => {
879
+ });
880
+ }
881
+ }
882
+ /**
883
+ * Gets the underlying Aptos client
884
+ * @returns Aptos SDK client instance
885
+ */
886
+ getAptosClient() {
887
+ return this.aptosClient;
888
+ }
889
+ /**
890
+ * Gets the account balance for an address
891
+ * @param address - Account address
892
+ * @returns Balance as string in smallest unit (octas)
893
+ * @throws MovementError with code INVALID_ADDRESS if address is invalid
894
+ */
895
+ async getAccountBalance(address) {
896
+ if (!isValidAddress(address)) {
897
+ throw Errors.invalidAddress(address, "Invalid address format");
898
+ }
899
+ try {
900
+ const resources = await this.aptosClient.getAccountResources({
901
+ accountAddress: address
902
+ });
903
+ const coinResource = resources.find(
904
+ (r) => r.type === "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>"
905
+ );
906
+ if (!coinResource) {
907
+ return "0";
908
+ }
909
+ const data = coinResource.data;
910
+ return data.coin.value;
911
+ } catch (error) {
912
+ throw wrapError(error, "NETWORK_ERROR", `Failed to get balance for ${address}`);
913
+ }
914
+ }
915
+ /**
916
+ * Gets all resources for an account
917
+ * @param address - Account address
918
+ * @returns Array of resources
919
+ * @throws MovementError with code INVALID_ADDRESS if address is invalid
920
+ */
921
+ async getAccountResources(address) {
922
+ if (!isValidAddress(address)) {
923
+ throw Errors.invalidAddress(address, "Invalid address format");
924
+ }
925
+ try {
926
+ const resources = await this.aptosClient.getAccountResources({
927
+ accountAddress: address
928
+ });
929
+ return resources.map((r) => ({
930
+ type: r.type,
931
+ data: r.data
932
+ }));
933
+ } catch (error) {
934
+ if (error instanceof Error && error.message.includes("Account not found")) {
935
+ return [];
936
+ }
937
+ throw wrapError(error, "NETWORK_ERROR", `Failed to get resources for ${address}`);
938
+ }
939
+ }
940
+ /**
941
+ * Gets a transaction by hash
942
+ * @param hash - Transaction hash
943
+ * @returns Transaction data
944
+ */
945
+ async getTransaction(hash) {
946
+ try {
947
+ const tx = await this.aptosClient.getTransactionByHash({ transactionHash: hash });
948
+ const userTx = tx;
949
+ return {
950
+ hash: userTx.hash,
951
+ sender: userTx.sender,
952
+ sequenceNumber: userTx.sequence_number,
953
+ payload: {
954
+ type: "entry_function_payload",
955
+ function: userTx.payload?.function ?? "",
956
+ typeArguments: userTx.payload?.type_arguments ?? [],
957
+ arguments: userTx.payload?.arguments ?? []
958
+ },
959
+ timestamp: userTx.timestamp
960
+ };
961
+ } catch (error) {
962
+ throw wrapError(error, "NETWORK_ERROR", `Failed to get transaction ${hash}`);
963
+ }
964
+ }
965
+ /**
966
+ * Waits for a transaction to be confirmed
967
+ * @param hash - Transaction hash
968
+ * @param options - Wait options
969
+ * @returns Transaction response
970
+ */
971
+ async waitForTransaction(hash, options) {
972
+ const timeoutMs = options?.timeoutMs ?? 3e4;
973
+ const checkIntervalMs = options?.checkIntervalMs ?? 1e3;
974
+ const startTime = Date.now();
975
+ while (Date.now() - startTime < timeoutMs) {
976
+ try {
977
+ const tx = await this.aptosClient.getTransactionByHash({ transactionHash: hash });
978
+ if ("success" in tx) {
979
+ const committedTx = tx;
980
+ return {
981
+ hash: committedTx.hash,
982
+ success: committedTx.success,
983
+ vmStatus: committedTx.vm_status,
984
+ gasUsed: committedTx.gas_used,
985
+ events: committedTx.events.map((e) => ({
986
+ type: e.type,
987
+ sequenceNumber: e.sequence_number,
988
+ data: e.data
989
+ }))
990
+ };
991
+ }
992
+ } catch {
993
+ }
994
+ await new Promise((resolve) => setTimeout(resolve, checkIntervalMs));
995
+ }
996
+ throw Errors.transactionTimeout(hash);
997
+ }
998
+ /**
999
+ * Creates a contract interface for interacting with a Move module
1000
+ * @param options - Contract options
1001
+ * @returns Contract interface
1002
+ */
1003
+ contract(options) {
1004
+ return new ContractInterface(this.aptosClient, this.wallet, options);
1005
+ }
1006
+ };
1007
+ // Annotate the CommonJS export names for ESM import in node:
1008
+ 0 && (module.exports = {
1009
+ ContractInterface,
1010
+ DEFAULT_COIN_TYPE,
1011
+ Errors,
1012
+ EventListener,
1013
+ Movement,
1014
+ MovementError,
1015
+ NETWORK_CONFIG,
1016
+ TransactionBuilder,
1017
+ WalletManager,
1018
+ getExplorerAccountUrl,
1019
+ getExplorerTxUrl,
1020
+ isMovementError,
1021
+ isValidAddress,
1022
+ isValidEventHandle,
1023
+ resolveConfig,
1024
+ wrapError
1025
+ });