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