@movebridge/core 0.1.0 → 0.2.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.d.mts +275 -67
- package/dist/index.d.ts +275 -67
- package/dist/index.js +610 -160
- package/dist/index.mjs +610 -160
- package/package.json +52 -52
- package/LICENSE +0 -0
package/dist/index.js
CHANGED
|
@@ -63,7 +63,7 @@ var NETWORK_CONFIG = {
|
|
|
63
63
|
testnet: {
|
|
64
64
|
chainId: 250,
|
|
65
65
|
rpcUrl: "https://testnet.movementnetwork.xyz/v1",
|
|
66
|
-
indexerUrl:
|
|
66
|
+
indexerUrl: "https://hasura.testnet.movementnetwork.xyz/v1/graphql",
|
|
67
67
|
explorerUrl: "https://explorer.movementnetwork.xyz/?network=bardock+testnet",
|
|
68
68
|
faucetUrl: "https://faucet.testnet.movementnetwork.xyz/"
|
|
69
69
|
}
|
|
@@ -280,6 +280,56 @@ var SUPPORTED_WALLETS = {
|
|
|
280
280
|
"okxwallet": "okx"
|
|
281
281
|
};
|
|
282
282
|
var STORAGE_KEY = "movebridge:lastWallet";
|
|
283
|
+
function normalizeHash(data) {
|
|
284
|
+
if (data === null || data === void 0) {
|
|
285
|
+
throw new Error("Invalid hash: received null or undefined");
|
|
286
|
+
}
|
|
287
|
+
if (typeof data === "string") {
|
|
288
|
+
const trimmed = data.trim();
|
|
289
|
+
if (!trimmed) {
|
|
290
|
+
throw new Error("Invalid hash: received empty string");
|
|
291
|
+
}
|
|
292
|
+
return trimmed.startsWith("0x") ? trimmed : `0x${trimmed}`;
|
|
293
|
+
}
|
|
294
|
+
if (data instanceof Uint8Array) {
|
|
295
|
+
if (data.length === 0) {
|
|
296
|
+
throw new Error("Invalid hash: received empty Uint8Array");
|
|
297
|
+
}
|
|
298
|
+
return "0x" + Array.from(data).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
299
|
+
}
|
|
300
|
+
if (data instanceof ArrayBuffer) {
|
|
301
|
+
const arr = new Uint8Array(data);
|
|
302
|
+
if (arr.length === 0) {
|
|
303
|
+
throw new Error("Invalid hash: received empty ArrayBuffer");
|
|
304
|
+
}
|
|
305
|
+
return "0x" + Array.from(arr).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
306
|
+
}
|
|
307
|
+
if (data && typeof data === "object") {
|
|
308
|
+
if ("hash" in data) {
|
|
309
|
+
return normalizeHash(data.hash);
|
|
310
|
+
}
|
|
311
|
+
if ("txnHash" in data) {
|
|
312
|
+
return normalizeHash(data.txnHash);
|
|
313
|
+
}
|
|
314
|
+
if ("transactionHash" in data) {
|
|
315
|
+
return normalizeHash(data.transactionHash);
|
|
316
|
+
}
|
|
317
|
+
if ("output" in data) {
|
|
318
|
+
return normalizeHash(data.output);
|
|
319
|
+
}
|
|
320
|
+
if (typeof data.toString === "function") {
|
|
321
|
+
const str = data.toString();
|
|
322
|
+
if (str !== "[object Object]") {
|
|
323
|
+
return str.startsWith("0x") ? str : `0x${str}`;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
const strValue = String(data);
|
|
328
|
+
if (strValue === "[object Object]" || !strValue) {
|
|
329
|
+
throw new Error("Invalid hash: could not extract hash from response");
|
|
330
|
+
}
|
|
331
|
+
return strValue.startsWith("0x") ? strValue : `0x${strValue}`;
|
|
332
|
+
}
|
|
283
333
|
function toHexString(data) {
|
|
284
334
|
if (typeof data === "string") return data;
|
|
285
335
|
if (data instanceof Uint8Array) {
|
|
@@ -290,56 +340,159 @@ function toHexString(data) {
|
|
|
290
340
|
}
|
|
291
341
|
return String(data);
|
|
292
342
|
}
|
|
343
|
+
function extractUserResponse(response) {
|
|
344
|
+
if (!response) {
|
|
345
|
+
throw new Error("Empty response from wallet");
|
|
346
|
+
}
|
|
347
|
+
if (typeof response === "string" || typeof response === "number") {
|
|
348
|
+
return response;
|
|
349
|
+
}
|
|
350
|
+
if (response instanceof Uint8Array) {
|
|
351
|
+
return response;
|
|
352
|
+
}
|
|
353
|
+
const resp = response;
|
|
354
|
+
if (resp.status === "rejected" || resp.status === "Rejected") {
|
|
355
|
+
throw new Error("User rejected the request");
|
|
356
|
+
}
|
|
357
|
+
if (resp.status === "error" || resp.error) {
|
|
358
|
+
const errorMsg = resp.error || resp.message || "Transaction failed";
|
|
359
|
+
throw new Error(String(errorMsg));
|
|
360
|
+
}
|
|
361
|
+
if (resp.args !== void 0) {
|
|
362
|
+
return resp.args;
|
|
363
|
+
}
|
|
364
|
+
if (resp.status === "approved" && resp.output !== void 0) {
|
|
365
|
+
return resp.output;
|
|
366
|
+
}
|
|
367
|
+
if (resp.result !== void 0) {
|
|
368
|
+
return resp.result;
|
|
369
|
+
}
|
|
370
|
+
if (resp.data !== void 0) {
|
|
371
|
+
return resp.data;
|
|
372
|
+
}
|
|
373
|
+
return response;
|
|
374
|
+
}
|
|
293
375
|
function createStandardAdapter(wallet) {
|
|
294
|
-
const
|
|
295
|
-
const
|
|
296
|
-
const
|
|
297
|
-
const
|
|
298
|
-
const
|
|
299
|
-
const
|
|
376
|
+
const features = wallet.features || {};
|
|
377
|
+
const connectFeature = features["aptos:connect"];
|
|
378
|
+
const disconnectFeature = features["aptos:disconnect"];
|
|
379
|
+
const signTxFeature = features["aptos:signAndSubmitTransaction"];
|
|
380
|
+
const signOnlyFeature = features["aptos:signTransaction"];
|
|
381
|
+
const accountChangeFeature = features["aptos:onAccountChange"];
|
|
382
|
+
const networkChangeFeature = features["aptos:onNetworkChange"];
|
|
300
383
|
return {
|
|
301
384
|
name: wallet.name,
|
|
302
385
|
icon: wallet.icon || "",
|
|
303
386
|
async connect() {
|
|
304
|
-
if (!connectFeature)
|
|
305
|
-
|
|
306
|
-
const result = response?.args ?? response;
|
|
307
|
-
if (response?.status === "rejected") {
|
|
308
|
-
throw new Error("User rejected the connection");
|
|
387
|
+
if (!connectFeature) {
|
|
388
|
+
throw new Error("Wallet does not support connect");
|
|
309
389
|
}
|
|
390
|
+
const response = await connectFeature.connect();
|
|
391
|
+
const result = extractUserResponse(response);
|
|
310
392
|
return {
|
|
311
393
|
address: toHexString(result.address),
|
|
312
394
|
publicKey: toHexString(result.publicKey)
|
|
313
395
|
};
|
|
314
396
|
},
|
|
315
397
|
async disconnect() {
|
|
316
|
-
if (disconnectFeature)
|
|
398
|
+
if (disconnectFeature) {
|
|
399
|
+
await disconnectFeature.disconnect();
|
|
400
|
+
}
|
|
317
401
|
},
|
|
318
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
319
402
|
async signAndSubmitTransaction(payload) {
|
|
320
|
-
if (!signTxFeature)
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
403
|
+
if (!signTxFeature) {
|
|
404
|
+
throw new Error("Wallet does not support signAndSubmitTransaction");
|
|
405
|
+
}
|
|
406
|
+
const walletName = wallet.name.toLowerCase();
|
|
407
|
+
const isOKX = walletName.includes("okx");
|
|
408
|
+
let txPayload;
|
|
409
|
+
if (isOKX) {
|
|
410
|
+
txPayload = {
|
|
411
|
+
type: "entry_function_payload",
|
|
412
|
+
function: payload.payload.function,
|
|
413
|
+
type_arguments: payload.payload.typeArguments,
|
|
414
|
+
arguments: payload.payload.functionArguments
|
|
415
|
+
};
|
|
416
|
+
} else {
|
|
417
|
+
txPayload = payload;
|
|
418
|
+
}
|
|
419
|
+
let response;
|
|
420
|
+
try {
|
|
421
|
+
response = await signTxFeature.signAndSubmitTransaction(txPayload);
|
|
422
|
+
} catch (firstError) {
|
|
423
|
+
if (isOKX) {
|
|
424
|
+
try {
|
|
425
|
+
response = await signTxFeature.signAndSubmitTransaction(payload);
|
|
426
|
+
} catch {
|
|
427
|
+
throw firstError;
|
|
428
|
+
}
|
|
429
|
+
} else {
|
|
430
|
+
try {
|
|
431
|
+
const legacyPayload = {
|
|
432
|
+
type: "entry_function_payload",
|
|
433
|
+
function: payload.payload.function,
|
|
434
|
+
type_arguments: payload.payload.typeArguments,
|
|
435
|
+
arguments: payload.payload.functionArguments
|
|
436
|
+
};
|
|
437
|
+
response = await signTxFeature.signAndSubmitTransaction(legacyPayload);
|
|
438
|
+
} catch {
|
|
439
|
+
throw firstError;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
const result = extractUserResponse(response);
|
|
444
|
+
try {
|
|
445
|
+
if (typeof result === "string") {
|
|
446
|
+
return { hash: normalizeHash(result) };
|
|
447
|
+
}
|
|
448
|
+
if (result && typeof result === "object") {
|
|
449
|
+
const hashObj = result;
|
|
450
|
+
if (hashObj.hash !== void 0) {
|
|
451
|
+
return { hash: normalizeHash(hashObj.hash) };
|
|
452
|
+
}
|
|
453
|
+
if (hashObj.txnHash !== void 0) {
|
|
454
|
+
return { hash: normalizeHash(hashObj.txnHash) };
|
|
455
|
+
}
|
|
456
|
+
if (hashObj.transactionHash !== void 0) {
|
|
457
|
+
return { hash: normalizeHash(hashObj.transactionHash) };
|
|
458
|
+
}
|
|
459
|
+
return { hash: normalizeHash(result) };
|
|
460
|
+
}
|
|
461
|
+
return { hash: normalizeHash(result) };
|
|
462
|
+
} catch (error) {
|
|
463
|
+
throw new Error(
|
|
464
|
+
`Failed to extract transaction hash from wallet response: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
465
|
+
);
|
|
324
466
|
}
|
|
325
|
-
const result = response?.args ?? response;
|
|
326
|
-
return { hash: result.hash || toHexString(result) };
|
|
327
467
|
},
|
|
328
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
329
468
|
async signTransaction(payload) {
|
|
330
|
-
if (!signOnlyFeature)
|
|
469
|
+
if (!signOnlyFeature) {
|
|
470
|
+
throw new Error("Wallet does not support signTransaction");
|
|
471
|
+
}
|
|
331
472
|
const response = await signOnlyFeature.signTransaction(payload);
|
|
332
|
-
|
|
333
|
-
|
|
473
|
+
const result = extractUserResponse(response);
|
|
474
|
+
if (result instanceof Uint8Array) {
|
|
475
|
+
return result;
|
|
334
476
|
}
|
|
335
|
-
|
|
336
|
-
|
|
477
|
+
if (result && typeof result === "object") {
|
|
478
|
+
if ("authenticator" in result && result.authenticator) {
|
|
479
|
+
return result.authenticator;
|
|
480
|
+
}
|
|
481
|
+
if ("signature" in result && result.signature) {
|
|
482
|
+
return result.signature;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return new Uint8Array();
|
|
337
486
|
},
|
|
338
487
|
onAccountChange(cb) {
|
|
339
488
|
if (accountChangeFeature) {
|
|
340
489
|
accountChangeFeature.onAccountChange((account) => {
|
|
341
|
-
if (account) {
|
|
342
|
-
|
|
490
|
+
if (account && typeof account === "object") {
|
|
491
|
+
const acc = account;
|
|
492
|
+
cb({
|
|
493
|
+
address: toHexString(acc.address),
|
|
494
|
+
publicKey: toHexString(acc.publicKey)
|
|
495
|
+
});
|
|
343
496
|
} else {
|
|
344
497
|
cb(null);
|
|
345
498
|
}
|
|
@@ -349,7 +502,11 @@ function createStandardAdapter(wallet) {
|
|
|
349
502
|
onNetworkChange(cb) {
|
|
350
503
|
if (networkChangeFeature) {
|
|
351
504
|
networkChangeFeature.onNetworkChange((network) => {
|
|
352
|
-
|
|
505
|
+
if (network && typeof network === "object" && "name" in network) {
|
|
506
|
+
cb(network.name);
|
|
507
|
+
} else {
|
|
508
|
+
cb(String(network));
|
|
509
|
+
}
|
|
353
510
|
});
|
|
354
511
|
}
|
|
355
512
|
}
|
|
@@ -359,17 +516,22 @@ var WalletManager = class extends import_eventemitter3.default {
|
|
|
359
516
|
state = { connected: false, address: null, publicKey: null };
|
|
360
517
|
currentWallet = null;
|
|
361
518
|
adapter = null;
|
|
362
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
363
519
|
standardWallets = /* @__PURE__ */ new Map();
|
|
364
520
|
detectedWallets = [];
|
|
365
521
|
unsubscribe = null;
|
|
522
|
+
/**
|
|
523
|
+
* Detects available wallets using AIP-62 standard
|
|
524
|
+
* @returns Array of detected wallet types
|
|
525
|
+
*/
|
|
366
526
|
detectWallets() {
|
|
367
527
|
if (typeof window === "undefined") return [];
|
|
368
528
|
const available = /* @__PURE__ */ new Set();
|
|
369
529
|
this.standardWallets.clear();
|
|
370
530
|
try {
|
|
371
531
|
const { aptosWallets, on } = (0, import_wallet_standard.getAptosWallets)();
|
|
372
|
-
if (this.unsubscribe)
|
|
532
|
+
if (this.unsubscribe) {
|
|
533
|
+
this.unsubscribe();
|
|
534
|
+
}
|
|
373
535
|
this.unsubscribe = on("register", () => this.detectWallets());
|
|
374
536
|
for (const wallet of aptosWallets) {
|
|
375
537
|
const normalizedName = wallet.name.toLowerCase();
|
|
@@ -385,21 +547,42 @@ var WalletManager = class extends import_eventemitter3.default {
|
|
|
385
547
|
this.detectedWallets = Array.from(available);
|
|
386
548
|
return this.detectedWallets;
|
|
387
549
|
}
|
|
550
|
+
/**
|
|
551
|
+
* Gets detailed wallet information for UI display
|
|
552
|
+
* @returns Array of wallet info objects
|
|
553
|
+
*/
|
|
388
554
|
getWalletInfo() {
|
|
389
555
|
return this.detectedWallets.map((type) => {
|
|
390
556
|
const wallet = this.standardWallets.get(type);
|
|
391
|
-
return {
|
|
557
|
+
return {
|
|
558
|
+
type,
|
|
559
|
+
name: wallet?.name || type,
|
|
560
|
+
icon: wallet?.icon || ""
|
|
561
|
+
};
|
|
392
562
|
});
|
|
393
563
|
}
|
|
564
|
+
/**
|
|
565
|
+
* Connects to a wallet
|
|
566
|
+
* @param wallet - Wallet type to connect to
|
|
567
|
+
* @throws MovementError if wallet not found or connection fails
|
|
568
|
+
*/
|
|
394
569
|
async connect(wallet) {
|
|
395
570
|
const available = this.detectWallets();
|
|
396
|
-
if (!available.includes(wallet))
|
|
571
|
+
if (!available.includes(wallet)) {
|
|
572
|
+
throw Errors.walletNotFound(wallet, available);
|
|
573
|
+
}
|
|
397
574
|
try {
|
|
398
575
|
const standardWallet = this.standardWallets.get(wallet);
|
|
399
|
-
if (!standardWallet)
|
|
576
|
+
if (!standardWallet) {
|
|
577
|
+
throw new Error(`Wallet ${wallet} not found`);
|
|
578
|
+
}
|
|
400
579
|
this.adapter = createStandardAdapter(standardWallet);
|
|
401
580
|
const result = await this.adapter.connect();
|
|
402
|
-
this.state = {
|
|
581
|
+
this.state = {
|
|
582
|
+
connected: true,
|
|
583
|
+
address: result.address,
|
|
584
|
+
publicKey: result.publicKey
|
|
585
|
+
};
|
|
403
586
|
this.currentWallet = wallet;
|
|
404
587
|
this.saveLastWallet(wallet);
|
|
405
588
|
this.setupEventListeners();
|
|
@@ -409,6 +592,9 @@ var WalletManager = class extends import_eventemitter3.default {
|
|
|
409
592
|
throw Errors.walletConnectionFailed(wallet, error);
|
|
410
593
|
}
|
|
411
594
|
}
|
|
595
|
+
/**
|
|
596
|
+
* Disconnects from the current wallet
|
|
597
|
+
*/
|
|
412
598
|
async disconnect() {
|
|
413
599
|
if (this.adapter) {
|
|
414
600
|
try {
|
|
@@ -422,15 +608,28 @@ var WalletManager = class extends import_eventemitter3.default {
|
|
|
422
608
|
this.clearLastWallet();
|
|
423
609
|
this.emit("disconnect");
|
|
424
610
|
}
|
|
611
|
+
/**
|
|
612
|
+
* Gets the current wallet state
|
|
613
|
+
*/
|
|
425
614
|
getState() {
|
|
426
615
|
return { ...this.state };
|
|
427
616
|
}
|
|
617
|
+
/**
|
|
618
|
+
* Gets the currently connected wallet type
|
|
619
|
+
*/
|
|
428
620
|
getWallet() {
|
|
429
621
|
return this.currentWallet;
|
|
430
622
|
}
|
|
623
|
+
/**
|
|
624
|
+
* Gets the wallet adapter for direct access
|
|
625
|
+
* @internal
|
|
626
|
+
*/
|
|
431
627
|
getAdapter() {
|
|
432
628
|
return this.adapter;
|
|
433
629
|
}
|
|
630
|
+
/**
|
|
631
|
+
* Attempts to auto-connect to the last used wallet
|
|
632
|
+
*/
|
|
434
633
|
async autoConnect() {
|
|
435
634
|
const lastWallet = this.getLastWallet();
|
|
436
635
|
if (!lastWallet) return;
|
|
@@ -443,6 +642,9 @@ var WalletManager = class extends import_eventemitter3.default {
|
|
|
443
642
|
}
|
|
444
643
|
}
|
|
445
644
|
}
|
|
645
|
+
/**
|
|
646
|
+
* Cleans up resources
|
|
647
|
+
*/
|
|
446
648
|
destroy() {
|
|
447
649
|
if (this.unsubscribe) {
|
|
448
650
|
this.unsubscribe();
|
|
@@ -453,18 +655,26 @@ var WalletManager = class extends import_eventemitter3.default {
|
|
|
453
655
|
if (!this.adapter) return;
|
|
454
656
|
this.adapter.onAccountChange((account) => {
|
|
455
657
|
if (account) {
|
|
456
|
-
this.state = {
|
|
658
|
+
this.state = {
|
|
659
|
+
connected: true,
|
|
660
|
+
address: account.address,
|
|
661
|
+
publicKey: account.publicKey
|
|
662
|
+
};
|
|
457
663
|
this.emit("accountChanged", account.address);
|
|
458
664
|
} else {
|
|
459
665
|
this.state = { connected: false, address: null, publicKey: null };
|
|
460
666
|
this.emit("disconnect");
|
|
461
667
|
}
|
|
462
668
|
});
|
|
463
|
-
this.adapter.onNetworkChange((network) =>
|
|
669
|
+
this.adapter.onNetworkChange((network) => {
|
|
670
|
+
this.emit("networkChanged", network);
|
|
671
|
+
});
|
|
464
672
|
}
|
|
465
673
|
saveLastWallet(wallet) {
|
|
466
674
|
try {
|
|
467
|
-
if (typeof localStorage !== "undefined")
|
|
675
|
+
if (typeof localStorage !== "undefined") {
|
|
676
|
+
localStorage.setItem(STORAGE_KEY, wallet);
|
|
677
|
+
}
|
|
468
678
|
} catch {
|
|
469
679
|
}
|
|
470
680
|
}
|
|
@@ -482,7 +692,9 @@ var WalletManager = class extends import_eventemitter3.default {
|
|
|
482
692
|
}
|
|
483
693
|
clearLastWallet() {
|
|
484
694
|
try {
|
|
485
|
-
if (typeof localStorage !== "undefined")
|
|
695
|
+
if (typeof localStorage !== "undefined") {
|
|
696
|
+
localStorage.removeItem(STORAGE_KEY);
|
|
697
|
+
}
|
|
486
698
|
} catch {
|
|
487
699
|
}
|
|
488
700
|
}
|
|
@@ -496,108 +708,70 @@ var TransactionBuilder = class {
|
|
|
496
708
|
}
|
|
497
709
|
/**
|
|
498
710
|
* Builds a transfer transaction payload
|
|
711
|
+
* Uses 0x1::aptos_account::transfer which handles account creation
|
|
499
712
|
* @param options - Transfer options
|
|
500
|
-
* @returns Transaction payload
|
|
713
|
+
* @returns Transaction payload ready for signing
|
|
501
714
|
*/
|
|
502
715
|
async transfer(options) {
|
|
503
|
-
const coinType = options.coinType ?? DEFAULT_COIN_TYPE;
|
|
504
716
|
return {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
arguments: [options.to, options.amount]
|
|
717
|
+
function: "0x1::aptos_account::transfer",
|
|
718
|
+
typeArguments: [],
|
|
719
|
+
functionArguments: [options.to, options.amount]
|
|
509
720
|
};
|
|
510
721
|
}
|
|
511
722
|
/**
|
|
512
723
|
* Builds a generic transaction payload
|
|
513
|
-
* @param options - Build options
|
|
514
|
-
* @returns Transaction payload
|
|
724
|
+
* @param options - Build options with function, typeArguments, and arguments
|
|
725
|
+
* @returns Transaction payload ready for signing
|
|
515
726
|
*/
|
|
516
727
|
async build(options) {
|
|
517
728
|
return {
|
|
518
|
-
type: "entry_function_payload",
|
|
519
729
|
function: options.function,
|
|
520
730
|
typeArguments: options.typeArguments,
|
|
521
|
-
|
|
731
|
+
functionArguments: options.arguments
|
|
522
732
|
};
|
|
523
733
|
}
|
|
524
|
-
/**
|
|
525
|
-
* Signs a transaction payload
|
|
526
|
-
* @param payload - Transaction payload to sign
|
|
527
|
-
* @returns Signed transaction
|
|
528
|
-
* @throws MovementError with code WALLET_NOT_CONNECTED if no wallet is connected
|
|
529
|
-
*/
|
|
530
|
-
async sign(payload) {
|
|
531
|
-
const adapter = this.walletManager.getAdapter();
|
|
532
|
-
const state = this.walletManager.getState();
|
|
533
|
-
if (!adapter || !state.connected || !state.address) {
|
|
534
|
-
throw Errors.walletNotConnected();
|
|
535
|
-
}
|
|
536
|
-
try {
|
|
537
|
-
const signatureBytes = await adapter.signTransaction({
|
|
538
|
-
type: payload.type,
|
|
539
|
-
function: payload.function,
|
|
540
|
-
type_arguments: payload.typeArguments,
|
|
541
|
-
arguments: payload.arguments
|
|
542
|
-
});
|
|
543
|
-
const signature = Array.from(signatureBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
544
|
-
return {
|
|
545
|
-
payload,
|
|
546
|
-
signature: `0x${signature}`,
|
|
547
|
-
sender: state.address
|
|
548
|
-
};
|
|
549
|
-
} catch (error) {
|
|
550
|
-
throw wrapError(error, "TRANSACTION_FAILED", "Failed to sign transaction");
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
/**
|
|
554
|
-
* Submits a signed transaction to the network
|
|
555
|
-
* @param signed - Signed transaction
|
|
556
|
-
* @returns Transaction hash
|
|
557
|
-
*/
|
|
558
|
-
async submit(signed) {
|
|
559
|
-
const adapter = this.walletManager.getAdapter();
|
|
560
|
-
if (!adapter) {
|
|
561
|
-
throw Errors.walletNotConnected();
|
|
562
|
-
}
|
|
563
|
-
try {
|
|
564
|
-
const result = await adapter.signAndSubmitTransaction({
|
|
565
|
-
type: signed.payload.type,
|
|
566
|
-
function: signed.payload.function,
|
|
567
|
-
type_arguments: signed.payload.typeArguments,
|
|
568
|
-
arguments: signed.payload.arguments
|
|
569
|
-
});
|
|
570
|
-
return result.hash;
|
|
571
|
-
} catch (error) {
|
|
572
|
-
throw wrapError(error, "TRANSACTION_FAILED", "Failed to submit transaction");
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
734
|
/**
|
|
576
735
|
* Signs and submits a transaction in one step
|
|
736
|
+
* This is the recommended method for most use cases
|
|
577
737
|
* @param payload - Transaction payload
|
|
578
738
|
* @returns Transaction hash
|
|
739
|
+
* @throws MovementError with code WALLET_NOT_CONNECTED if no wallet connected
|
|
740
|
+
* @throws MovementError with code TRANSACTION_FAILED if submission fails
|
|
741
|
+
*
|
|
742
|
+
* Note: Transaction failures do NOT affect wallet connection state.
|
|
743
|
+
* The wallet remains connected even if a transaction fails.
|
|
579
744
|
*/
|
|
580
745
|
async signAndSubmit(payload) {
|
|
581
746
|
const adapter = this.walletManager.getAdapter();
|
|
582
|
-
|
|
747
|
+
const stateBefore = this.walletManager.getState();
|
|
748
|
+
if (!adapter || !stateBefore.connected) {
|
|
583
749
|
throw Errors.walletNotConnected();
|
|
584
750
|
}
|
|
585
751
|
try {
|
|
586
752
|
const result = await adapter.signAndSubmitTransaction({
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
753
|
+
payload: {
|
|
754
|
+
function: payload.function,
|
|
755
|
+
typeArguments: payload.typeArguments,
|
|
756
|
+
functionArguments: payload.functionArguments
|
|
757
|
+
}
|
|
591
758
|
});
|
|
592
759
|
return result.hash;
|
|
593
760
|
} catch (error) {
|
|
594
|
-
|
|
761
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
762
|
+
const isUserRejection = errorMessage.toLowerCase().includes("rejected") || errorMessage.toLowerCase().includes("cancelled") || errorMessage.toLowerCase().includes("canceled") || errorMessage.toLowerCase().includes("denied");
|
|
763
|
+
throw wrapError(
|
|
764
|
+
error,
|
|
765
|
+
"TRANSACTION_FAILED",
|
|
766
|
+
isUserRejection ? "Transaction was rejected by user" : "Failed to sign and submit transaction"
|
|
767
|
+
);
|
|
595
768
|
}
|
|
596
769
|
}
|
|
597
770
|
/**
|
|
598
771
|
* Simulates a transaction without submitting
|
|
772
|
+
* Useful for gas estimation and checking if transaction will succeed
|
|
599
773
|
* @param payload - Transaction payload
|
|
600
|
-
* @returns Simulation result with gas estimate
|
|
774
|
+
* @returns Simulation result with success status and gas estimate
|
|
601
775
|
*/
|
|
602
776
|
async simulate(payload) {
|
|
603
777
|
const state = this.walletManager.getState();
|
|
@@ -605,16 +779,17 @@ var TransactionBuilder = class {
|
|
|
605
779
|
throw Errors.walletNotConnected();
|
|
606
780
|
}
|
|
607
781
|
try {
|
|
608
|
-
const
|
|
609
|
-
const result = await client.transaction.simulate.simple({
|
|
782
|
+
const transaction = await this.aptosClient.transaction.build.simple({
|
|
610
783
|
sender: state.address,
|
|
611
784
|
data: {
|
|
612
785
|
function: payload.function,
|
|
613
786
|
typeArguments: payload.typeArguments,
|
|
614
|
-
functionArguments: payload.
|
|
787
|
+
functionArguments: payload.functionArguments
|
|
615
788
|
}
|
|
616
789
|
});
|
|
617
|
-
const simResult =
|
|
790
|
+
const [simResult] = await this.aptosClient.transaction.simulate.simple({
|
|
791
|
+
transaction
|
|
792
|
+
});
|
|
618
793
|
return {
|
|
619
794
|
success: simResult?.success ?? false,
|
|
620
795
|
gasUsed: String(simResult?.gas_used ?? "0"),
|
|
@@ -624,6 +799,15 @@ var TransactionBuilder = class {
|
|
|
624
799
|
throw wrapError(error, "TRANSACTION_FAILED", "Failed to simulate transaction");
|
|
625
800
|
}
|
|
626
801
|
}
|
|
802
|
+
/**
|
|
803
|
+
* Convenience method: Transfer and wait for confirmation
|
|
804
|
+
* @param options - Transfer options
|
|
805
|
+
* @returns Transaction hash
|
|
806
|
+
*/
|
|
807
|
+
async transferAndSubmit(options) {
|
|
808
|
+
const payload = await this.transfer(options);
|
|
809
|
+
return this.signAndSubmit(payload);
|
|
810
|
+
}
|
|
627
811
|
};
|
|
628
812
|
|
|
629
813
|
// src/contract.ts
|
|
@@ -640,17 +824,27 @@ var ContractInterface = class {
|
|
|
640
824
|
module;
|
|
641
825
|
/**
|
|
642
826
|
* Calls a view function (read-only)
|
|
827
|
+
* View functions don't require a wallet connection
|
|
828
|
+
*
|
|
643
829
|
* @param functionName - Name of the view function
|
|
644
830
|
* @param args - Function arguments
|
|
645
|
-
* @param typeArgs - Type arguments
|
|
831
|
+
* @param typeArgs - Type arguments for generic functions
|
|
646
832
|
* @returns Function result
|
|
647
833
|
* @throws MovementError with code VIEW_FUNCTION_FAILED if call fails
|
|
834
|
+
*
|
|
835
|
+
* @example
|
|
836
|
+
* ```typescript
|
|
837
|
+
* // Get coin balance
|
|
838
|
+
* const balance = await contract.view('balance', [address], ['0x1::aptos_coin::AptosCoin']);
|
|
839
|
+
*
|
|
840
|
+
* // Check if account exists
|
|
841
|
+
* const exists = await contract.view('exists_at', [address]);
|
|
842
|
+
* ```
|
|
648
843
|
*/
|
|
649
|
-
async view(functionName, args, typeArgs = []) {
|
|
844
|
+
async view(functionName, args = [], typeArgs = []) {
|
|
650
845
|
const fullFunctionName = `${this.address}::${this.module}::${functionName}`;
|
|
651
846
|
try {
|
|
652
|
-
const
|
|
653
|
-
const result = await client.view({
|
|
847
|
+
const result = await this.aptosClient.view({
|
|
654
848
|
payload: {
|
|
655
849
|
function: fullFunctionName,
|
|
656
850
|
typeArguments: typeArgs,
|
|
@@ -664,14 +858,25 @@ var ContractInterface = class {
|
|
|
664
858
|
}
|
|
665
859
|
/**
|
|
666
860
|
* Calls an entry function (write operation)
|
|
861
|
+
* Requires a connected wallet
|
|
862
|
+
*
|
|
667
863
|
* @param functionName - Name of the entry function
|
|
668
864
|
* @param args - Function arguments
|
|
669
|
-
* @param typeArgs - Type arguments
|
|
865
|
+
* @param typeArgs - Type arguments for generic functions
|
|
670
866
|
* @returns Transaction hash
|
|
671
|
-
* @throws MovementError with code WALLET_NOT_CONNECTED if no wallet
|
|
867
|
+
* @throws MovementError with code WALLET_NOT_CONNECTED if no wallet connected
|
|
672
868
|
* @throws MovementError with code TRANSACTION_FAILED if transaction fails
|
|
869
|
+
*
|
|
870
|
+
* @example
|
|
871
|
+
* ```typescript
|
|
872
|
+
* // Transfer coins
|
|
873
|
+
* const txHash = await contract.call('transfer', [recipient, amount], ['0x1::aptos_coin::AptosCoin']);
|
|
874
|
+
*
|
|
875
|
+
* // Call a custom function
|
|
876
|
+
* const txHash = await contract.call('increment', []);
|
|
877
|
+
* ```
|
|
673
878
|
*/
|
|
674
|
-
async call(functionName, args, typeArgs = []) {
|
|
879
|
+
async call(functionName, args = [], typeArgs = []) {
|
|
675
880
|
const adapter = this.walletManager.getAdapter();
|
|
676
881
|
const state = this.walletManager.getState();
|
|
677
882
|
if (!adapter || !state.connected) {
|
|
@@ -680,10 +885,11 @@ var ContractInterface = class {
|
|
|
680
885
|
const fullFunctionName = `${this.address}::${this.module}::${functionName}`;
|
|
681
886
|
try {
|
|
682
887
|
const result = await adapter.signAndSubmitTransaction({
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
888
|
+
payload: {
|
|
889
|
+
function: fullFunctionName,
|
|
890
|
+
typeArguments: typeArgs,
|
|
891
|
+
functionArguments: args
|
|
892
|
+
}
|
|
687
893
|
});
|
|
688
894
|
return result.hash;
|
|
689
895
|
} catch (error) {
|
|
@@ -692,13 +898,17 @@ var ContractInterface = class {
|
|
|
692
898
|
}
|
|
693
899
|
/**
|
|
694
900
|
* Checks if a resource exists at the contract address
|
|
695
|
-
* @param resourceType - Full resource type
|
|
901
|
+
* @param resourceType - Full resource type
|
|
696
902
|
* @returns true if resource exists
|
|
903
|
+
*
|
|
904
|
+
* @example
|
|
905
|
+
* ```typescript
|
|
906
|
+
* const hasCoin = await contract.hasResource('0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>');
|
|
907
|
+
* ```
|
|
697
908
|
*/
|
|
698
909
|
async hasResource(resourceType) {
|
|
699
910
|
try {
|
|
700
|
-
|
|
701
|
-
await client.getAccountResource({
|
|
911
|
+
await this.aptosClient.getAccountResource({
|
|
702
912
|
accountAddress: this.address,
|
|
703
913
|
resourceType
|
|
704
914
|
});
|
|
@@ -711,11 +921,17 @@ var ContractInterface = class {
|
|
|
711
921
|
* Gets a resource from the contract address
|
|
712
922
|
* @param resourceType - Full resource type
|
|
713
923
|
* @returns Resource data or null if not found
|
|
924
|
+
*
|
|
925
|
+
* @example
|
|
926
|
+
* ```typescript
|
|
927
|
+
* const coinStore = await contract.getResource<{ coin: { value: string } }>(
|
|
928
|
+
* '0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>'
|
|
929
|
+
* );
|
|
930
|
+
* ```
|
|
714
931
|
*/
|
|
715
932
|
async getResource(resourceType) {
|
|
716
933
|
try {
|
|
717
|
-
const
|
|
718
|
-
const resource = await client.getAccountResource({
|
|
934
|
+
const resource = await this.aptosClient.getAccountResource({
|
|
719
935
|
accountAddress: this.address,
|
|
720
936
|
resourceType
|
|
721
937
|
});
|
|
@@ -744,31 +960,99 @@ var EventListener = class {
|
|
|
744
960
|
subscriptionCounter = 0;
|
|
745
961
|
pollIntervalMs;
|
|
746
962
|
/**
|
|
747
|
-
* Subscribes to
|
|
963
|
+
* Subscribes to blockchain events
|
|
964
|
+
* Supports both new format (accountAddress + eventType) and legacy format (eventHandle)
|
|
965
|
+
*
|
|
748
966
|
* @param subscription - Subscription configuration
|
|
749
|
-
* @returns Subscription ID
|
|
750
|
-
*
|
|
967
|
+
* @returns Subscription ID for unsubscribing
|
|
968
|
+
*
|
|
969
|
+
* @example
|
|
970
|
+
* ```typescript
|
|
971
|
+
* // New format (recommended)
|
|
972
|
+
* const subId = events.subscribe({
|
|
973
|
+
* accountAddress: '0x1',
|
|
974
|
+
* eventType: '0x1::coin::DepositEvent',
|
|
975
|
+
* callback: (event) => console.log(event),
|
|
976
|
+
* });
|
|
977
|
+
*
|
|
978
|
+
* // Legacy format (backward compatible)
|
|
979
|
+
* const subId = events.subscribe({
|
|
980
|
+
* eventHandle: '0x1::coin::DepositEvent',
|
|
981
|
+
* callback: (event) => console.log(event),
|
|
982
|
+
* });
|
|
983
|
+
* ```
|
|
751
984
|
*/
|
|
752
985
|
subscribe(subscription) {
|
|
753
|
-
if (!isValidEventHandle(subscription.eventHandle)) {
|
|
754
|
-
throw Errors.invalidEventHandle(subscription.eventHandle);
|
|
755
|
-
}
|
|
756
986
|
const subscriptionId = `sub_${++this.subscriptionCounter}`;
|
|
987
|
+
let accountAddress;
|
|
988
|
+
let eventType;
|
|
989
|
+
if ("accountAddress" in subscription) {
|
|
990
|
+
accountAddress = subscription.accountAddress;
|
|
991
|
+
eventType = subscription.eventType;
|
|
992
|
+
} else {
|
|
993
|
+
const parsed = this.parseEventHandle(subscription.eventHandle);
|
|
994
|
+
accountAddress = parsed.accountAddress;
|
|
995
|
+
eventType = parsed.eventType;
|
|
996
|
+
}
|
|
997
|
+
if (!this.isValidEventType(eventType)) {
|
|
998
|
+
if (process.env.NODE_ENV === "development") {
|
|
999
|
+
console.warn(
|
|
1000
|
+
`[EventListener] Event type "${eventType}" may not be in the expected format (address::module::EventType). Subscription will proceed but may not receive events.`
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
757
1004
|
const internalSub = {
|
|
758
|
-
|
|
1005
|
+
accountAddress,
|
|
1006
|
+
eventType,
|
|
759
1007
|
callback: subscription.callback,
|
|
760
|
-
lastSequenceNumber:
|
|
1008
|
+
lastSequenceNumber: BigInt(-1),
|
|
1009
|
+
// Start at -1 to catch all events
|
|
761
1010
|
intervalId: null
|
|
762
1011
|
};
|
|
1012
|
+
this.subscriptions.set(subscriptionId, internalSub);
|
|
763
1013
|
internalSub.intervalId = setInterval(() => {
|
|
764
1014
|
this.pollEvents(subscriptionId).catch(() => {
|
|
765
1015
|
});
|
|
766
1016
|
}, this.pollIntervalMs);
|
|
767
1017
|
this.pollEvents(subscriptionId).catch(() => {
|
|
768
1018
|
});
|
|
769
|
-
this.subscriptions.set(subscriptionId, internalSub);
|
|
770
1019
|
return subscriptionId;
|
|
771
1020
|
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Parses a legacy event handle into account address and event type
|
|
1023
|
+
* @internal
|
|
1024
|
+
*/
|
|
1025
|
+
parseEventHandle(eventHandle) {
|
|
1026
|
+
const parts = eventHandle.split("::");
|
|
1027
|
+
if (parts.length >= 3 && parts[0]) {
|
|
1028
|
+
return {
|
|
1029
|
+
accountAddress: parts[0],
|
|
1030
|
+
eventType: eventHandle
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
return {
|
|
1034
|
+
accountAddress: eventHandle,
|
|
1035
|
+
eventType: eventHandle
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Validates event type format
|
|
1040
|
+
* Expected format: address::module::EventType
|
|
1041
|
+
* @internal
|
|
1042
|
+
*/
|
|
1043
|
+
isValidEventType(eventType) {
|
|
1044
|
+
const parts = eventType.split("::");
|
|
1045
|
+
if (parts.length < 3) {
|
|
1046
|
+
return false;
|
|
1047
|
+
}
|
|
1048
|
+
const address = parts[0];
|
|
1049
|
+
if (!address || !address.startsWith("0x")) {
|
|
1050
|
+
return false;
|
|
1051
|
+
}
|
|
1052
|
+
const module2 = parts[1];
|
|
1053
|
+
const eventName = parts.slice(2).join("::");
|
|
1054
|
+
return Boolean(module2 && eventName);
|
|
1055
|
+
}
|
|
772
1056
|
/**
|
|
773
1057
|
* Unsubscribes from events
|
|
774
1058
|
* @param subscriptionId - Subscription ID to remove
|
|
@@ -778,6 +1062,7 @@ var EventListener = class {
|
|
|
778
1062
|
if (subscription) {
|
|
779
1063
|
if (subscription.intervalId) {
|
|
780
1064
|
clearInterval(subscription.intervalId);
|
|
1065
|
+
subscription.intervalId = null;
|
|
781
1066
|
}
|
|
782
1067
|
this.subscriptions.delete(subscriptionId);
|
|
783
1068
|
}
|
|
@@ -806,8 +1091,8 @@ var EventListener = class {
|
|
|
806
1091
|
return this.subscriptions.has(subscriptionId);
|
|
807
1092
|
}
|
|
808
1093
|
/**
|
|
809
|
-
* Polls for new events
|
|
810
|
-
* @
|
|
1094
|
+
* Polls for new events for a subscription
|
|
1095
|
+
* @internal
|
|
811
1096
|
*/
|
|
812
1097
|
async pollEvents(subscriptionId) {
|
|
813
1098
|
const subscription = this.subscriptions.get(subscriptionId);
|
|
@@ -815,33 +1100,199 @@ var EventListener = class {
|
|
|
815
1100
|
return;
|
|
816
1101
|
}
|
|
817
1102
|
try {
|
|
818
|
-
const
|
|
819
|
-
|
|
820
|
-
|
|
1103
|
+
const events = await this.fetchEvents(
|
|
1104
|
+
subscription.accountAddress,
|
|
1105
|
+
subscription.eventType
|
|
1106
|
+
);
|
|
1107
|
+
const sortedEvents = this.sortEventsBySequence(events);
|
|
1108
|
+
for (const event of sortedEvents) {
|
|
1109
|
+
const sequenceNumber = this.parseSequenceNumber(event.sequence_number);
|
|
1110
|
+
if (sequenceNumber > subscription.lastSequenceNumber) {
|
|
1111
|
+
const contractEvent = {
|
|
1112
|
+
type: event.type || subscription.eventType,
|
|
1113
|
+
sequenceNumber: String(event.sequence_number),
|
|
1114
|
+
data: this.normalizeEventData(event.data)
|
|
1115
|
+
};
|
|
1116
|
+
this.safeInvokeCallback(subscription.callback, contractEvent);
|
|
1117
|
+
subscription.lastSequenceNumber = sequenceNumber;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
} catch (error) {
|
|
1121
|
+
if (process.env.NODE_ENV === "development") {
|
|
1122
|
+
console.warn(`[EventListener] Polling error for ${subscriptionId}:`, error);
|
|
821
1123
|
}
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Fetches events from the blockchain
|
|
1128
|
+
* Handles different API response formats and fallback methods
|
|
1129
|
+
* @internal
|
|
1130
|
+
*/
|
|
1131
|
+
async fetchEvents(accountAddress, eventType) {
|
|
1132
|
+
try {
|
|
1133
|
+
const events = await this.aptosClient.getAccountEventsByEventType({
|
|
1134
|
+
accountAddress,
|
|
1135
|
+
eventType,
|
|
828
1136
|
options: {
|
|
829
|
-
limit: 25
|
|
1137
|
+
limit: 25,
|
|
1138
|
+
orderBy: [{ sequence_number: "desc" }]
|
|
830
1139
|
}
|
|
831
1140
|
});
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
1141
|
+
return events.map((e) => ({
|
|
1142
|
+
type: e.type,
|
|
1143
|
+
sequence_number: e.sequence_number,
|
|
1144
|
+
data: e.data
|
|
1145
|
+
}));
|
|
1146
|
+
} catch (indexerError) {
|
|
1147
|
+
try {
|
|
1148
|
+
const events = await this.fetchEventsViaEventHandle(accountAddress, eventType);
|
|
1149
|
+
return events;
|
|
1150
|
+
} catch (handleError) {
|
|
1151
|
+
try {
|
|
1152
|
+
const events = await this.fetchEventsViaResources(accountAddress, eventType);
|
|
1153
|
+
return events;
|
|
1154
|
+
} catch (resourceError) {
|
|
1155
|
+
if (process.env.NODE_ENV === "development") {
|
|
1156
|
+
console.warn(
|
|
1157
|
+
`[EventListener] Failed to fetch events for ${eventType}:`,
|
|
1158
|
+
{ indexerError, handleError, resourceError }
|
|
1159
|
+
);
|
|
1160
|
+
}
|
|
1161
|
+
return [];
|
|
842
1162
|
}
|
|
843
1163
|
}
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Fetches events using the event handle API
|
|
1168
|
+
* @internal
|
|
1169
|
+
*/
|
|
1170
|
+
async fetchEventsViaEventHandle(accountAddress, eventType) {
|
|
1171
|
+
const parts = eventType.split("::");
|
|
1172
|
+
if (parts.length < 3) {
|
|
1173
|
+
throw new Error("Invalid event type format");
|
|
1174
|
+
}
|
|
1175
|
+
const structName = parts.slice(2).join("::");
|
|
1176
|
+
const eventHandleMappings = {
|
|
1177
|
+
"DepositEvent": { resource: "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>", field: "deposit_events" },
|
|
1178
|
+
"WithdrawEvent": { resource: "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>", field: "withdraw_events" }
|
|
1179
|
+
};
|
|
1180
|
+
const mapping = eventHandleMappings[structName];
|
|
1181
|
+
if (!mapping) {
|
|
1182
|
+
throw new Error(`Unknown event type: ${structName}`);
|
|
1183
|
+
}
|
|
1184
|
+
const events = await this.aptosClient.getEventsByEventHandle({
|
|
1185
|
+
accountAddress,
|
|
1186
|
+
eventHandleStruct: mapping.resource,
|
|
1187
|
+
fieldName: mapping.field,
|
|
1188
|
+
options: { limit: 25 }
|
|
1189
|
+
});
|
|
1190
|
+
return events.map((e) => ({
|
|
1191
|
+
type: e.type || eventType,
|
|
1192
|
+
sequence_number: e.sequence_number,
|
|
1193
|
+
data: e.data
|
|
1194
|
+
}));
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* Fetches events by checking account resources
|
|
1198
|
+
* This is a fallback method when indexer is not available
|
|
1199
|
+
* @internal
|
|
1200
|
+
*/
|
|
1201
|
+
async fetchEventsViaResources(accountAddress, eventType) {
|
|
1202
|
+
const resources = await this.aptosClient.getAccountResources({
|
|
1203
|
+
accountAddress
|
|
1204
|
+
});
|
|
1205
|
+
const coinStore = resources.find(
|
|
1206
|
+
(r) => r.type === "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>"
|
|
1207
|
+
);
|
|
1208
|
+
if (!coinStore) {
|
|
1209
|
+
return [];
|
|
1210
|
+
}
|
|
1211
|
+
const data = coinStore.data;
|
|
1212
|
+
const isDeposit = eventType.includes("Deposit");
|
|
1213
|
+
const eventHandle = isDeposit ? data.deposit_events : data.withdraw_events;
|
|
1214
|
+
if (!eventHandle) {
|
|
1215
|
+
return [];
|
|
1216
|
+
}
|
|
1217
|
+
try {
|
|
1218
|
+
const creationNum = eventHandle.guid.id.creation_num;
|
|
1219
|
+
const addr = eventHandle.guid.id.addr;
|
|
1220
|
+
const response = await fetch(
|
|
1221
|
+
`${this.getFullnodeUrl()}/accounts/${accountAddress}/events/${addr}/${creationNum}?limit=25`
|
|
1222
|
+
);
|
|
1223
|
+
if (!response.ok) {
|
|
1224
|
+
throw new Error(`HTTP ${response.status}`);
|
|
1225
|
+
}
|
|
1226
|
+
const events = await response.json();
|
|
1227
|
+
return events.map((e) => ({
|
|
1228
|
+
type: e.type || eventType,
|
|
1229
|
+
sequence_number: e.sequence_number,
|
|
1230
|
+
data: e.data
|
|
1231
|
+
}));
|
|
1232
|
+
} catch {
|
|
1233
|
+
return [];
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Gets the fullnode URL from the Aptos client config
|
|
1238
|
+
* @internal
|
|
1239
|
+
*/
|
|
1240
|
+
getFullnodeUrl() {
|
|
1241
|
+
const config = this.aptosClient.config;
|
|
1242
|
+
return config?.fullnode || "https://testnet.movementnetwork.xyz/v1";
|
|
1243
|
+
}
|
|
1244
|
+
/**
|
|
1245
|
+
* Sorts events by sequence number in ascending order
|
|
1246
|
+
* @internal
|
|
1247
|
+
*/
|
|
1248
|
+
sortEventsBySequence(events) {
|
|
1249
|
+
return [...events].sort((a, b) => {
|
|
1250
|
+
const seqA = this.parseSequenceNumber(a.sequence_number);
|
|
1251
|
+
const seqB = this.parseSequenceNumber(b.sequence_number);
|
|
1252
|
+
return seqA < seqB ? -1 : seqA > seqB ? 1 : 0;
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Parses sequence number from various formats
|
|
1257
|
+
* @internal
|
|
1258
|
+
*/
|
|
1259
|
+
parseSequenceNumber(value) {
|
|
1260
|
+
if (typeof value === "bigint") {
|
|
1261
|
+
return value;
|
|
1262
|
+
}
|
|
1263
|
+
if (typeof value === "number") {
|
|
1264
|
+
return BigInt(value);
|
|
1265
|
+
}
|
|
1266
|
+
try {
|
|
1267
|
+
return BigInt(value);
|
|
844
1268
|
} catch {
|
|
1269
|
+
return BigInt(0);
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Normalizes event data to a consistent format
|
|
1274
|
+
* @internal
|
|
1275
|
+
*/
|
|
1276
|
+
normalizeEventData(data) {
|
|
1277
|
+
if (data === null || data === void 0) {
|
|
1278
|
+
return {};
|
|
1279
|
+
}
|
|
1280
|
+
if (typeof data === "object") {
|
|
1281
|
+
return data;
|
|
1282
|
+
}
|
|
1283
|
+
return { value: data };
|
|
1284
|
+
}
|
|
1285
|
+
/**
|
|
1286
|
+
* Safely invokes a callback without letting errors propagate
|
|
1287
|
+
* @internal
|
|
1288
|
+
*/
|
|
1289
|
+
safeInvokeCallback(callback, event) {
|
|
1290
|
+
try {
|
|
1291
|
+
callback(event);
|
|
1292
|
+
} catch (error) {
|
|
1293
|
+
if (process.env.NODE_ENV === "development") {
|
|
1294
|
+
console.warn("[EventListener] Callback error:", error);
|
|
1295
|
+
}
|
|
845
1296
|
}
|
|
846
1297
|
}
|
|
847
1298
|
};
|
|
@@ -953,10 +1404,9 @@ var Movement = class {
|
|
|
953
1404
|
sender: userTx.sender,
|
|
954
1405
|
sequenceNumber: userTx.sequence_number,
|
|
955
1406
|
payload: {
|
|
956
|
-
type: "entry_function_payload",
|
|
957
1407
|
function: userTx.payload?.function ?? "",
|
|
958
1408
|
typeArguments: userTx.payload?.type_arguments ?? [],
|
|
959
|
-
|
|
1409
|
+
functionArguments: userTx.payload?.arguments ?? []
|
|
960
1410
|
},
|
|
961
1411
|
timestamp: userTx.timestamp
|
|
962
1412
|
};
|