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