@movebridge/core 0.0.1 → 0.2.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.
package/dist/index.js CHANGED
@@ -270,14 +270,32 @@ function wrapError(error, code, context) {
270
270
  var import_eventemitter3 = __toESM(require("eventemitter3"));
271
271
  var import_wallet_standard = require("@aptos-labs/wallet-standard");
272
272
  var SUPPORTED_WALLETS = {
273
- "petra": "petra",
274
- "petra wallet": "petra",
275
- "pontem": "pontem",
276
- "pontem wallet": "pontem",
273
+ "razor": "razor",
274
+ "razor wallet": "razor",
275
+ "razorwallet": "razor",
277
276
  "nightly": "nightly",
278
- "nightly wallet": "nightly"
277
+ "nightly wallet": "nightly",
278
+ "okx": "okx",
279
+ "okx wallet": "okx",
280
+ "okxwallet": "okx"
279
281
  };
280
282
  var STORAGE_KEY = "movebridge:lastWallet";
283
+ function normalizeHash(data) {
284
+ if (typeof data === "string") {
285
+ return data.startsWith("0x") ? data : `0x${data}`;
286
+ }
287
+ if (data instanceof Uint8Array) {
288
+ return "0x" + Array.from(data).map((b) => b.toString(16).padStart(2, "0")).join("");
289
+ }
290
+ if (data && typeof data === "object" && "hash" in data) {
291
+ return normalizeHash(data.hash);
292
+ }
293
+ if (data && typeof data.toString === "function") {
294
+ const str = data.toString();
295
+ return str.startsWith("0x") ? str : `0x${str}`;
296
+ }
297
+ return String(data);
298
+ }
281
299
  function toHexString(data) {
282
300
  if (typeof data === "string") return data;
283
301
  if (data instanceof Uint8Array) {
@@ -288,56 +306,88 @@ function toHexString(data) {
288
306
  }
289
307
  return String(data);
290
308
  }
309
+ function extractUserResponse(response) {
310
+ if (!response) {
311
+ throw new Error("Empty response from wallet");
312
+ }
313
+ const resp = response;
314
+ if (resp.status === "rejected") {
315
+ throw new Error("User rejected the request");
316
+ }
317
+ if (resp.args !== void 0) {
318
+ return resp.args;
319
+ }
320
+ return response;
321
+ }
291
322
  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"];
323
+ const features = wallet.features || {};
324
+ const connectFeature = features["aptos:connect"];
325
+ const disconnectFeature = features["aptos:disconnect"];
326
+ const signTxFeature = features["aptos:signAndSubmitTransaction"];
327
+ const signOnlyFeature = features["aptos:signTransaction"];
328
+ const accountChangeFeature = features["aptos:onAccountChange"];
329
+ const networkChangeFeature = features["aptos:onNetworkChange"];
298
330
  return {
299
331
  name: wallet.name,
300
332
  icon: wallet.icon || "",
301
333
  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");
334
+ if (!connectFeature) {
335
+ throw new Error("Wallet does not support connect");
307
336
  }
337
+ const response = await connectFeature.connect();
338
+ const result = extractUserResponse(response);
308
339
  return {
309
340
  address: toHexString(result.address),
310
341
  publicKey: toHexString(result.publicKey)
311
342
  };
312
343
  },
313
344
  async disconnect() {
314
- if (disconnectFeature) await disconnectFeature.disconnect();
345
+ if (disconnectFeature) {
346
+ await disconnectFeature.disconnect();
347
+ }
315
348
  },
316
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
317
349
  async signAndSubmitTransaction(payload) {
318
- if (!signTxFeature) throw new Error("Wallet does not support signAndSubmitTransaction");
350
+ if (!signTxFeature) {
351
+ throw new Error("Wallet does not support signAndSubmitTransaction");
352
+ }
319
353
  const response = await signTxFeature.signAndSubmitTransaction(payload);
320
- if (response?.status === "rejected") {
321
- throw new Error("User rejected the transaction");
354
+ const result = extractUserResponse(response);
355
+ let hash;
356
+ if (result && typeof result === "object" && "hash" in result) {
357
+ hash = normalizeHash(result.hash);
358
+ } else {
359
+ hash = normalizeHash(result);
322
360
  }
323
- const result = response?.args ?? response;
324
- return { hash: result.hash || toHexString(result) };
361
+ return { hash };
325
362
  },
326
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
327
363
  async signTransaction(payload) {
328
- if (!signOnlyFeature) throw new Error("Wallet does not support signTransaction");
364
+ if (!signOnlyFeature) {
365
+ throw new Error("Wallet does not support signTransaction");
366
+ }
329
367
  const response = await signOnlyFeature.signTransaction(payload);
330
- if (response?.status === "rejected") {
331
- throw new Error("User rejected the transaction");
368
+ const result = extractUserResponse(response);
369
+ if (result instanceof Uint8Array) {
370
+ return result;
371
+ }
372
+ if (result && typeof result === "object") {
373
+ if ("authenticator" in result && result.authenticator) {
374
+ return result.authenticator;
375
+ }
376
+ if ("signature" in result && result.signature) {
377
+ return result.signature;
378
+ }
332
379
  }
333
- const result = response?.args ?? response;
334
- return result.authenticator || result.signature || new Uint8Array();
380
+ return new Uint8Array();
335
381
  },
336
382
  onAccountChange(cb) {
337
383
  if (accountChangeFeature) {
338
384
  accountChangeFeature.onAccountChange((account) => {
339
- if (account) {
340
- cb({ address: toHexString(account.address), publicKey: toHexString(account.publicKey) });
385
+ if (account && typeof account === "object") {
386
+ const acc = account;
387
+ cb({
388
+ address: toHexString(acc.address),
389
+ publicKey: toHexString(acc.publicKey)
390
+ });
341
391
  } else {
342
392
  cb(null);
343
393
  }
@@ -347,7 +397,11 @@ function createStandardAdapter(wallet) {
347
397
  onNetworkChange(cb) {
348
398
  if (networkChangeFeature) {
349
399
  networkChangeFeature.onNetworkChange((network) => {
350
- cb(network?.name || String(network));
400
+ if (network && typeof network === "object" && "name" in network) {
401
+ cb(network.name);
402
+ } else {
403
+ cb(String(network));
404
+ }
351
405
  });
352
406
  }
353
407
  }
@@ -357,17 +411,22 @@ var WalletManager = class extends import_eventemitter3.default {
357
411
  state = { connected: false, address: null, publicKey: null };
358
412
  currentWallet = null;
359
413
  adapter = null;
360
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
361
414
  standardWallets = /* @__PURE__ */ new Map();
362
415
  detectedWallets = [];
363
416
  unsubscribe = null;
417
+ /**
418
+ * Detects available wallets using AIP-62 standard
419
+ * @returns Array of detected wallet types
420
+ */
364
421
  detectWallets() {
365
422
  if (typeof window === "undefined") return [];
366
423
  const available = /* @__PURE__ */ new Set();
367
424
  this.standardWallets.clear();
368
425
  try {
369
426
  const { aptosWallets, on } = (0, import_wallet_standard.getAptosWallets)();
370
- if (this.unsubscribe) this.unsubscribe();
427
+ if (this.unsubscribe) {
428
+ this.unsubscribe();
429
+ }
371
430
  this.unsubscribe = on("register", () => this.detectWallets());
372
431
  for (const wallet of aptosWallets) {
373
432
  const normalizedName = wallet.name.toLowerCase();
@@ -383,21 +442,42 @@ var WalletManager = class extends import_eventemitter3.default {
383
442
  this.detectedWallets = Array.from(available);
384
443
  return this.detectedWallets;
385
444
  }
445
+ /**
446
+ * Gets detailed wallet information for UI display
447
+ * @returns Array of wallet info objects
448
+ */
386
449
  getWalletInfo() {
387
450
  return this.detectedWallets.map((type) => {
388
451
  const wallet = this.standardWallets.get(type);
389
- return { type, name: wallet?.name || type, icon: wallet?.icon || "" };
452
+ return {
453
+ type,
454
+ name: wallet?.name || type,
455
+ icon: wallet?.icon || ""
456
+ };
390
457
  });
391
458
  }
459
+ /**
460
+ * Connects to a wallet
461
+ * @param wallet - Wallet type to connect to
462
+ * @throws MovementError if wallet not found or connection fails
463
+ */
392
464
  async connect(wallet) {
393
465
  const available = this.detectWallets();
394
- if (!available.includes(wallet)) throw Errors.walletNotFound(wallet, available);
466
+ if (!available.includes(wallet)) {
467
+ throw Errors.walletNotFound(wallet, available);
468
+ }
395
469
  try {
396
470
  const standardWallet = this.standardWallets.get(wallet);
397
- if (!standardWallet) throw new Error(`Wallet ${wallet} not found`);
471
+ if (!standardWallet) {
472
+ throw new Error(`Wallet ${wallet} not found`);
473
+ }
398
474
  this.adapter = createStandardAdapter(standardWallet);
399
475
  const result = await this.adapter.connect();
400
- this.state = { connected: true, address: result.address, publicKey: result.publicKey };
476
+ this.state = {
477
+ connected: true,
478
+ address: result.address,
479
+ publicKey: result.publicKey
480
+ };
401
481
  this.currentWallet = wallet;
402
482
  this.saveLastWallet(wallet);
403
483
  this.setupEventListeners();
@@ -407,6 +487,9 @@ var WalletManager = class extends import_eventemitter3.default {
407
487
  throw Errors.walletConnectionFailed(wallet, error);
408
488
  }
409
489
  }
490
+ /**
491
+ * Disconnects from the current wallet
492
+ */
410
493
  async disconnect() {
411
494
  if (this.adapter) {
412
495
  try {
@@ -420,15 +503,28 @@ var WalletManager = class extends import_eventemitter3.default {
420
503
  this.clearLastWallet();
421
504
  this.emit("disconnect");
422
505
  }
506
+ /**
507
+ * Gets the current wallet state
508
+ */
423
509
  getState() {
424
510
  return { ...this.state };
425
511
  }
512
+ /**
513
+ * Gets the currently connected wallet type
514
+ */
426
515
  getWallet() {
427
516
  return this.currentWallet;
428
517
  }
518
+ /**
519
+ * Gets the wallet adapter for direct access
520
+ * @internal
521
+ */
429
522
  getAdapter() {
430
523
  return this.adapter;
431
524
  }
525
+ /**
526
+ * Attempts to auto-connect to the last used wallet
527
+ */
432
528
  async autoConnect() {
433
529
  const lastWallet = this.getLastWallet();
434
530
  if (!lastWallet) return;
@@ -441,6 +537,9 @@ var WalletManager = class extends import_eventemitter3.default {
441
537
  }
442
538
  }
443
539
  }
540
+ /**
541
+ * Cleans up resources
542
+ */
444
543
  destroy() {
445
544
  if (this.unsubscribe) {
446
545
  this.unsubscribe();
@@ -451,18 +550,26 @@ var WalletManager = class extends import_eventemitter3.default {
451
550
  if (!this.adapter) return;
452
551
  this.adapter.onAccountChange((account) => {
453
552
  if (account) {
454
- this.state = { connected: true, address: account.address, publicKey: account.publicKey };
553
+ this.state = {
554
+ connected: true,
555
+ address: account.address,
556
+ publicKey: account.publicKey
557
+ };
455
558
  this.emit("accountChanged", account.address);
456
559
  } else {
457
560
  this.state = { connected: false, address: null, publicKey: null };
458
561
  this.emit("disconnect");
459
562
  }
460
563
  });
461
- this.adapter.onNetworkChange((network) => this.emit("networkChanged", network));
564
+ this.adapter.onNetworkChange((network) => {
565
+ this.emit("networkChanged", network);
566
+ });
462
567
  }
463
568
  saveLastWallet(wallet) {
464
569
  try {
465
- if (typeof localStorage !== "undefined") localStorage.setItem(STORAGE_KEY, wallet);
570
+ if (typeof localStorage !== "undefined") {
571
+ localStorage.setItem(STORAGE_KEY, wallet);
572
+ }
466
573
  } catch {
467
574
  }
468
575
  }
@@ -480,7 +587,9 @@ var WalletManager = class extends import_eventemitter3.default {
480
587
  }
481
588
  clearLastWallet() {
482
589
  try {
483
- if (typeof localStorage !== "undefined") localStorage.removeItem(STORAGE_KEY);
590
+ if (typeof localStorage !== "undefined") {
591
+ localStorage.removeItem(STORAGE_KEY);
592
+ }
484
593
  } catch {
485
594
  }
486
595
  }
@@ -494,98 +603,50 @@ var TransactionBuilder = class {
494
603
  }
495
604
  /**
496
605
  * Builds a transfer transaction payload
606
+ * Uses 0x1::aptos_account::transfer which handles account creation
497
607
  * @param options - Transfer options
498
- * @returns Transaction payload
608
+ * @returns Transaction payload ready for signing
499
609
  */
500
610
  async transfer(options) {
501
- const coinType = options.coinType ?? DEFAULT_COIN_TYPE;
502
611
  return {
503
- type: "entry_function_payload",
504
- function: "0x1::coin::transfer",
505
- typeArguments: [coinType],
506
- arguments: [options.to, options.amount]
612
+ function: "0x1::aptos_account::transfer",
613
+ typeArguments: [],
614
+ functionArguments: [options.to, options.amount]
507
615
  };
508
616
  }
509
617
  /**
510
618
  * Builds a generic transaction payload
511
- * @param options - Build options
512
- * @returns Transaction payload
619
+ * @param options - Build options with function, typeArguments, and arguments
620
+ * @returns Transaction payload ready for signing
513
621
  */
514
622
  async build(options) {
515
623
  return {
516
- type: "entry_function_payload",
517
624
  function: options.function,
518
625
  typeArguments: options.typeArguments,
519
- arguments: options.arguments
626
+ functionArguments: options.arguments
520
627
  };
521
628
  }
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
629
  /**
574
630
  * Signs and submits a transaction in one step
631
+ * This is the recommended method for most use cases
575
632
  * @param payload - Transaction payload
576
633
  * @returns Transaction hash
634
+ * @throws MovementError with code WALLET_NOT_CONNECTED if no wallet connected
635
+ * @throws MovementError with code TRANSACTION_FAILED if submission fails
577
636
  */
578
637
  async signAndSubmit(payload) {
579
638
  const adapter = this.walletManager.getAdapter();
580
- if (!adapter) {
639
+ const state = this.walletManager.getState();
640
+ if (!adapter || !state.connected) {
581
641
  throw Errors.walletNotConnected();
582
642
  }
583
643
  try {
584
644
  const result = await adapter.signAndSubmitTransaction({
585
- type: payload.type,
586
- function: payload.function,
587
- type_arguments: payload.typeArguments,
588
- arguments: payload.arguments
645
+ payload: {
646
+ function: payload.function,
647
+ typeArguments: payload.typeArguments,
648
+ functionArguments: payload.functionArguments
649
+ }
589
650
  });
590
651
  return result.hash;
591
652
  } catch (error) {
@@ -594,8 +655,9 @@ var TransactionBuilder = class {
594
655
  }
595
656
  /**
596
657
  * Simulates a transaction without submitting
658
+ * Useful for gas estimation and checking if transaction will succeed
597
659
  * @param payload - Transaction payload
598
- * @returns Simulation result with gas estimate
660
+ * @returns Simulation result with success status and gas estimate
599
661
  */
600
662
  async simulate(payload) {
601
663
  const state = this.walletManager.getState();
@@ -603,16 +665,17 @@ var TransactionBuilder = class {
603
665
  throw Errors.walletNotConnected();
604
666
  }
605
667
  try {
606
- const client = this.aptosClient;
607
- const result = await client.transaction.simulate.simple({
668
+ const transaction = await this.aptosClient.transaction.build.simple({
608
669
  sender: state.address,
609
670
  data: {
610
671
  function: payload.function,
611
672
  typeArguments: payload.typeArguments,
612
- functionArguments: payload.arguments
673
+ functionArguments: payload.functionArguments
613
674
  }
614
675
  });
615
- const simResult = result[0];
676
+ const [simResult] = await this.aptosClient.transaction.simulate.simple({
677
+ transaction
678
+ });
616
679
  return {
617
680
  success: simResult?.success ?? false,
618
681
  gasUsed: String(simResult?.gas_used ?? "0"),
@@ -622,6 +685,15 @@ var TransactionBuilder = class {
622
685
  throw wrapError(error, "TRANSACTION_FAILED", "Failed to simulate transaction");
623
686
  }
624
687
  }
688
+ /**
689
+ * Convenience method: Transfer and wait for confirmation
690
+ * @param options - Transfer options
691
+ * @returns Transaction hash
692
+ */
693
+ async transferAndSubmit(options) {
694
+ const payload = await this.transfer(options);
695
+ return this.signAndSubmit(payload);
696
+ }
625
697
  };
626
698
 
627
699
  // src/contract.ts
@@ -638,17 +710,27 @@ var ContractInterface = class {
638
710
  module;
639
711
  /**
640
712
  * Calls a view function (read-only)
713
+ * View functions don't require a wallet connection
714
+ *
641
715
  * @param functionName - Name of the view function
642
716
  * @param args - Function arguments
643
- * @param typeArgs - Type arguments (optional)
717
+ * @param typeArgs - Type arguments for generic functions
644
718
  * @returns Function result
645
719
  * @throws MovementError with code VIEW_FUNCTION_FAILED if call fails
720
+ *
721
+ * @example
722
+ * ```typescript
723
+ * // Get coin balance
724
+ * const balance = await contract.view('balance', [address], ['0x1::aptos_coin::AptosCoin']);
725
+ *
726
+ * // Check if account exists
727
+ * const exists = await contract.view('exists_at', [address]);
728
+ * ```
646
729
  */
647
- async view(functionName, args, typeArgs = []) {
730
+ async view(functionName, args = [], typeArgs = []) {
648
731
  const fullFunctionName = `${this.address}::${this.module}::${functionName}`;
649
732
  try {
650
- const client = this.aptosClient;
651
- const result = await client.view({
733
+ const result = await this.aptosClient.view({
652
734
  payload: {
653
735
  function: fullFunctionName,
654
736
  typeArguments: typeArgs,
@@ -662,14 +744,25 @@ var ContractInterface = class {
662
744
  }
663
745
  /**
664
746
  * Calls an entry function (write operation)
747
+ * Requires a connected wallet
748
+ *
665
749
  * @param functionName - Name of the entry function
666
750
  * @param args - Function arguments
667
- * @param typeArgs - Type arguments (optional)
751
+ * @param typeArgs - Type arguments for generic functions
668
752
  * @returns Transaction hash
669
- * @throws MovementError with code WALLET_NOT_CONNECTED if no wallet is connected
753
+ * @throws MovementError with code WALLET_NOT_CONNECTED if no wallet connected
670
754
  * @throws MovementError with code TRANSACTION_FAILED if transaction fails
755
+ *
756
+ * @example
757
+ * ```typescript
758
+ * // Transfer coins
759
+ * const txHash = await contract.call('transfer', [recipient, amount], ['0x1::aptos_coin::AptosCoin']);
760
+ *
761
+ * // Call a custom function
762
+ * const txHash = await contract.call('increment', []);
763
+ * ```
671
764
  */
672
- async call(functionName, args, typeArgs = []) {
765
+ async call(functionName, args = [], typeArgs = []) {
673
766
  const adapter = this.walletManager.getAdapter();
674
767
  const state = this.walletManager.getState();
675
768
  if (!adapter || !state.connected) {
@@ -678,10 +771,11 @@ var ContractInterface = class {
678
771
  const fullFunctionName = `${this.address}::${this.module}::${functionName}`;
679
772
  try {
680
773
  const result = await adapter.signAndSubmitTransaction({
681
- type: "entry_function_payload",
682
- function: fullFunctionName,
683
- type_arguments: typeArgs,
684
- arguments: args
774
+ payload: {
775
+ function: fullFunctionName,
776
+ typeArguments: typeArgs,
777
+ functionArguments: args
778
+ }
685
779
  });
686
780
  return result.hash;
687
781
  } catch (error) {
@@ -690,13 +784,17 @@ var ContractInterface = class {
690
784
  }
691
785
  /**
692
786
  * Checks if a resource exists at the contract address
693
- * @param resourceType - Full resource type (e.g., '0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>')
787
+ * @param resourceType - Full resource type
694
788
  * @returns true if resource exists
789
+ *
790
+ * @example
791
+ * ```typescript
792
+ * const hasCoin = await contract.hasResource('0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>');
793
+ * ```
695
794
  */
696
795
  async hasResource(resourceType) {
697
796
  try {
698
- const client = this.aptosClient;
699
- await client.getAccountResource({
797
+ await this.aptosClient.getAccountResource({
700
798
  accountAddress: this.address,
701
799
  resourceType
702
800
  });
@@ -709,11 +807,17 @@ var ContractInterface = class {
709
807
  * Gets a resource from the contract address
710
808
  * @param resourceType - Full resource type
711
809
  * @returns Resource data or null if not found
810
+ *
811
+ * @example
812
+ * ```typescript
813
+ * const coinStore = await contract.getResource<{ coin: { value: string } }>(
814
+ * '0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>'
815
+ * );
816
+ * ```
712
817
  */
713
818
  async getResource(resourceType) {
714
819
  try {
715
- const client = this.aptosClient;
716
- const resource = await client.getAccountResource({
820
+ const resource = await this.aptosClient.getAccountResource({
717
821
  accountAddress: this.address,
718
822
  resourceType
719
823
  });
@@ -742,29 +846,60 @@ var EventListener = class {
742
846
  subscriptionCounter = 0;
743
847
  pollIntervalMs;
744
848
  /**
745
- * Subscribes to contract events
849
+ * Subscribes to blockchain events
850
+ * Supports both new format (accountAddress + eventType) and legacy format (eventHandle)
851
+ *
746
852
  * @param subscription - Subscription configuration
747
- * @returns Subscription ID
748
- * @throws MovementError with code INVALID_EVENT_HANDLE if event handle is invalid
853
+ * @returns Subscription ID for unsubscribing
854
+ *
855
+ * @example
856
+ * ```typescript
857
+ * // New format (recommended)
858
+ * const subId = events.subscribe({
859
+ * accountAddress: '0x1',
860
+ * eventType: '0x1::coin::DepositEvent',
861
+ * callback: (event) => console.log(event),
862
+ * });
863
+ *
864
+ * // Legacy format (backward compatible)
865
+ * const subId = events.subscribe({
866
+ * eventHandle: '0x1::coin::DepositEvent',
867
+ * callback: (event) => console.log(event),
868
+ * });
869
+ * ```
749
870
  */
750
871
  subscribe(subscription) {
751
- if (!isValidEventHandle(subscription.eventHandle)) {
752
- throw Errors.invalidEventHandle(subscription.eventHandle);
753
- }
754
872
  const subscriptionId = `sub_${++this.subscriptionCounter}`;
873
+ let accountAddress;
874
+ let eventType;
875
+ if ("accountAddress" in subscription) {
876
+ accountAddress = subscription.accountAddress;
877
+ eventType = subscription.eventType;
878
+ } else {
879
+ const parts = subscription.eventHandle.split("::");
880
+ if (parts.length >= 3 && parts[0]) {
881
+ accountAddress = parts[0];
882
+ eventType = subscription.eventHandle;
883
+ } else {
884
+ accountAddress = subscription.eventHandle;
885
+ eventType = subscription.eventHandle;
886
+ }
887
+ }
755
888
  const internalSub = {
756
- eventHandle: subscription.eventHandle,
889
+ accountAddress,
890
+ eventType,
757
891
  callback: subscription.callback,
758
- lastSequenceNumber: "0",
892
+ lastSequenceNumber: BigInt(-1),
893
+ // Start at -1 to catch all events
759
894
  intervalId: null
760
895
  };
896
+ this.subscriptions.set(subscriptionId, internalSub);
761
897
  internalSub.intervalId = setInterval(() => {
762
898
  this.pollEvents(subscriptionId).catch(() => {
763
899
  });
764
900
  }, this.pollIntervalMs);
765
901
  this.pollEvents(subscriptionId).catch(() => {
766
902
  });
767
- this.subscriptions.set(subscriptionId, internalSub);
768
903
  return subscriptionId;
769
904
  }
770
905
  /**
@@ -776,6 +911,7 @@ var EventListener = class {
776
911
  if (subscription) {
777
912
  if (subscription.intervalId) {
778
913
  clearInterval(subscription.intervalId);
914
+ subscription.intervalId = null;
779
915
  }
780
916
  this.subscriptions.delete(subscriptionId);
781
917
  }
@@ -804,8 +940,8 @@ var EventListener = class {
804
940
  return this.subscriptions.has(subscriptionId);
805
941
  }
806
942
  /**
807
- * Polls for new events
808
- * @param subscriptionId - Subscription ID
943
+ * Polls for new events for a subscription
944
+ * @internal
809
945
  */
810
946
  async pollEvents(subscriptionId) {
811
947
  const subscription = this.subscriptions.get(subscriptionId);
@@ -813,29 +949,31 @@ var EventListener = class {
813
949
  return;
814
950
  }
815
951
  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,
952
+ const events = await this.aptosClient.getAccountEventsByEventType({
953
+ accountAddress: subscription.accountAddress,
954
+ eventType: subscription.eventType,
826
955
  options: {
827
- limit: 25
956
+ limit: 25,
957
+ orderBy: [{ sequence_number: "desc" }]
828
958
  }
829
959
  });
830
- for (const event of events) {
831
- const sequenceNumber = event.sequence_number?.toString() ?? "0";
832
- if (BigInt(sequenceNumber) > BigInt(subscription.lastSequenceNumber)) {
960
+ const sortedEvents = [...events].sort((a, b) => {
961
+ const seqA = BigInt(a.sequence_number);
962
+ const seqB = BigInt(b.sequence_number);
963
+ return seqA < seqB ? -1 : seqA > seqB ? 1 : 0;
964
+ });
965
+ for (const event of sortedEvents) {
966
+ const sequenceNumber = BigInt(event.sequence_number);
967
+ if (sequenceNumber > subscription.lastSequenceNumber) {
833
968
  const contractEvent = {
834
969
  type: event.type,
835
- sequenceNumber,
970
+ sequenceNumber: String(event.sequence_number),
836
971
  data: event.data
837
972
  };
838
- subscription.callback(contractEvent);
973
+ try {
974
+ subscription.callback(contractEvent);
975
+ } catch {
976
+ }
839
977
  subscription.lastSequenceNumber = sequenceNumber;
840
978
  }
841
979
  }
@@ -951,10 +1089,9 @@ var Movement = class {
951
1089
  sender: userTx.sender,
952
1090
  sequenceNumber: userTx.sequence_number,
953
1091
  payload: {
954
- type: "entry_function_payload",
955
1092
  function: userTx.payload?.function ?? "",
956
1093
  typeArguments: userTx.payload?.type_arguments ?? [],
957
- arguments: userTx.payload?.arguments ?? []
1094
+ functionArguments: userTx.payload?.arguments ?? []
958
1095
  },
959
1096
  timestamp: userTx.timestamp
960
1097
  };