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