@twin.org/dlt-iota 0.0.2-next.2 → 0.0.2-next.4
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/cjs/index.cjs +439 -7
- package/dist/esm/index.mjs +439 -9
- package/dist/types/index.d.ts +7 -0
- package/dist/types/iota.d.ts +7 -7
- package/dist/types/iotaSmartContractUtils.d.ts +105 -0
- package/dist/types/models/IAdminCapFields.d.ts +14 -0
- package/dist/types/models/IContractData.d.ts +25 -0
- package/dist/types/models/IIotaConfig.d.ts +5 -0
- package/dist/types/models/IMigrationStateFields.d.ts +18 -0
- package/dist/types/models/ISmartContractDeployments.d.ts +8 -0
- package/dist/types/models/ISmartContractObject.d.ts +18 -0
- package/dist/types/models/networkTypes.d.ts +21 -0
- package/docs/changelog.md +14 -0
- package/docs/reference/classes/Iota.md +10 -10
- package/docs/reference/classes/IotaSmartContractUtils.md +473 -0
- package/docs/reference/index.md +14 -0
- package/docs/reference/interfaces/IAdminCapFields.md +17 -0
- package/docs/reference/interfaces/IContractData.md +43 -0
- package/docs/reference/interfaces/IIotaConfig.md +14 -0
- package/docs/reference/interfaces/IMigrationStateFields.md +25 -0
- package/docs/reference/interfaces/ISmartContractObject.md +25 -0
- package/docs/reference/type-aliases/ISmartContractDeployments.md +5 -0
- package/docs/reference/type-aliases/NetworkTypes.md +5 -0
- package/docs/reference/variables/NetworkTypes.md +25 -0
- package/locales/en.json +17 -0
- package/package.json +4 -3
package/dist/cjs/index.cjs
CHANGED
|
@@ -105,7 +105,7 @@ class Iota {
|
|
|
105
105
|
* Prepare and post a transaction.
|
|
106
106
|
* @param config The configuration.
|
|
107
107
|
* @param vaultConnector The vault connector.
|
|
108
|
-
* @param
|
|
108
|
+
* @param logging The logging component.
|
|
109
109
|
* @param identity The identity of the user to access the vault keys.
|
|
110
110
|
* @param client The client instance.
|
|
111
111
|
* @param source The source address.
|
|
@@ -114,7 +114,7 @@ class Iota {
|
|
|
114
114
|
* @param options The transaction options.
|
|
115
115
|
* @returns The transaction result.
|
|
116
116
|
*/
|
|
117
|
-
static async prepareAndPostValueTransaction(config, vaultConnector,
|
|
117
|
+
static async prepareAndPostValueTransaction(config, vaultConnector, logging, identity, client, source, amount, recipient, options) {
|
|
118
118
|
try {
|
|
119
119
|
const txb = new transactions.Transaction();
|
|
120
120
|
const [coin] = txb.splitCoins(txb.gas, [txb.pure.u64(amount)]);
|
|
@@ -123,7 +123,7 @@ class Iota {
|
|
|
123
123
|
if (core.Is.object(config.gasStation)) {
|
|
124
124
|
return await this.prepareAndPostGasStationTransaction(config, vaultConnector, identity, client, source, txb);
|
|
125
125
|
}
|
|
126
|
-
const result = await this.prepareAndPostTransaction(config, vaultConnector,
|
|
126
|
+
const result = await this.prepareAndPostTransaction(config, vaultConnector, logging, identity, client, source, txb, options);
|
|
127
127
|
return result;
|
|
128
128
|
}
|
|
129
129
|
catch (error) {
|
|
@@ -134,7 +134,7 @@ class Iota {
|
|
|
134
134
|
* Prepare and post a transaction.
|
|
135
135
|
* @param config The configuration.
|
|
136
136
|
* @param vaultConnector The vault connector.
|
|
137
|
-
* @param
|
|
137
|
+
* @param logging The logging component.
|
|
138
138
|
* @param identity The identity of the user to access the vault keys.
|
|
139
139
|
* @param client The client instance.
|
|
140
140
|
* @param owner The owner of the address.
|
|
@@ -142,7 +142,7 @@ class Iota {
|
|
|
142
142
|
* @param options The transaction options.
|
|
143
143
|
* @returns The transaction response.
|
|
144
144
|
*/
|
|
145
|
-
static async prepareAndPostTransaction(config, vaultConnector,
|
|
145
|
+
static async prepareAndPostTransaction(config, vaultConnector, logging, identity, client, owner, transaction, options) {
|
|
146
146
|
// Check if gas station configuration is present
|
|
147
147
|
if (core.Is.object(config.gasStation)) {
|
|
148
148
|
return this.prepareAndPostGasStationTransaction(config, vaultConnector, identity, client, owner, transaction, options);
|
|
@@ -150,7 +150,7 @@ class Iota {
|
|
|
150
150
|
// Traditional transaction flow
|
|
151
151
|
// Dry run the transaction if cost logging is enabled to get the gas and storage costs
|
|
152
152
|
if (core.Is.stringValue(options?.dryRunLabel)) {
|
|
153
|
-
await Iota.dryRunTransaction(client,
|
|
153
|
+
await Iota.dryRunTransaction(client, logging, transaction, owner, options.dryRunLabel);
|
|
154
154
|
}
|
|
155
155
|
const seed = await this.getSeed(config, vaultConnector, identity);
|
|
156
156
|
const addressKeyPair = Iota.findAddress(config.maxAddressScanRange ?? Iota.DEFAULT_SCAN_RANGE, config.coinType ?? Iota.DEFAULT_COIN_TYPE, seed, owner);
|
|
@@ -296,7 +296,7 @@ class Iota {
|
|
|
296
296
|
/**
|
|
297
297
|
* Dry run a transaction and log the results.
|
|
298
298
|
* @param client The IOTA client.
|
|
299
|
-
* @param logging The logging
|
|
299
|
+
* @param logging The logging component.
|
|
300
300
|
* @param txb The transaction to dry run.
|
|
301
301
|
* @param sender The sender address.
|
|
302
302
|
* @param operation The operation to log.
|
|
@@ -511,4 +511,436 @@ class Iota {
|
|
|
511
511
|
}
|
|
512
512
|
}
|
|
513
513
|
|
|
514
|
+
// Copyright 2024 IOTA Stiftung.
|
|
515
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
516
|
+
/**
|
|
517
|
+
* Utility class providing common smart contract operations for IOTA-based contracts.
|
|
518
|
+
* This class uses composition pattern to provide shared functionality without inheritance complexity.
|
|
519
|
+
*/
|
|
520
|
+
class IotaSmartContractUtils {
|
|
521
|
+
/**
|
|
522
|
+
* Runtime name for the class.
|
|
523
|
+
*/
|
|
524
|
+
static CLASS_NAME = "IotaSmartContractUtils";
|
|
525
|
+
/**
|
|
526
|
+
* Migrate a smart contract object to the current version using admin privileges.
|
|
527
|
+
* This is a generic migration method that works with any IOTA smart contract.
|
|
528
|
+
* @param config The IOTA configuration.
|
|
529
|
+
* @param client The IOTA client instance.
|
|
530
|
+
* @param vaultConnector The vault connector for key management.
|
|
531
|
+
* @param walletConnector The wallet connector for address generation.
|
|
532
|
+
* @param logging Optional logging component.
|
|
533
|
+
* @param gasBudget The gas budget for the transaction.
|
|
534
|
+
* @param identity The identity of the controller with admin privileges.
|
|
535
|
+
* @param objectId The ID of the object to migrate.
|
|
536
|
+
* @param namespace The contract namespace (e.g., "nft", "verifiable_storage").
|
|
537
|
+
* @param packageId The deployed package ID for the contract.
|
|
538
|
+
* @param deploymentConfig The deployment configuration containing object IDs.
|
|
539
|
+
* @param walletAddressIndex Optional wallet address index for the controller.
|
|
540
|
+
* @returns Promise that resolves when migration is complete.
|
|
541
|
+
*/
|
|
542
|
+
static async migrateSmartContract(config, client, vaultConnector, walletConnector, logging, gasBudget, identity, objectId, namespace, packageId, deploymentConfig, walletAddressIndex) {
|
|
543
|
+
try {
|
|
544
|
+
const txb = new transactions.Transaction();
|
|
545
|
+
txb.setGasBudget(gasBudget);
|
|
546
|
+
const moduleName = this.getModuleName(namespace);
|
|
547
|
+
// Get admin address for the transaction
|
|
548
|
+
const adminAddress = await this.getPackageControllerAddress(walletConnector, identity, walletAddressIndex);
|
|
549
|
+
// Get the required object IDs from deployment config
|
|
550
|
+
const { adminCapId, migrationStateId } = await this.getContractObjectIds(client, namespace, config.network, deploymentConfig, packageId, adminAddress);
|
|
551
|
+
txb.moveCall({
|
|
552
|
+
target: `${packageId}::${moduleName}::migrate_${moduleName}`,
|
|
553
|
+
arguments: [txb.object(adminCapId), txb.object(migrationStateId), txb.object(objectId)]
|
|
554
|
+
});
|
|
555
|
+
const result = await Iota.prepareAndPostTransaction(config, vaultConnector, logging, identity, client, adminAddress, txb, {
|
|
556
|
+
dryRunLabel: config.enableCostLogging ? "migrate_object" : undefined
|
|
557
|
+
});
|
|
558
|
+
if (result.effects?.status?.status !== "success") {
|
|
559
|
+
throw new core.GeneralError(IotaSmartContractUtils.CLASS_NAME, "migrationFailed", {
|
|
560
|
+
error: result.effects?.status?.error,
|
|
561
|
+
objectId
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
catch (error) {
|
|
566
|
+
throw new core.GeneralError(IotaSmartContractUtils.CLASS_NAME, "migrateSmartContractFailed", { objectId }, error);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Enable migration operations using admin privileges.
|
|
571
|
+
* @param config The IOTA configuration.
|
|
572
|
+
* @param client The IOTA client instance.
|
|
573
|
+
* @param vaultConnector The vault connector for key management.
|
|
574
|
+
* @param walletConnector The wallet connector for address generation.
|
|
575
|
+
* @param logging Optional logging component.
|
|
576
|
+
* @param gasBudget The gas budget for the transaction.
|
|
577
|
+
* @param identity The identity of the controller with admin privileges.
|
|
578
|
+
* @param namespace The contract namespace (e.g., "nft", "verifiable_storage").
|
|
579
|
+
* @param packageId The deployed package ID for the contract.
|
|
580
|
+
* @param deploymentConfig The deployment configuration containing object IDs.
|
|
581
|
+
* @param walletAddressIndex Optional wallet address index for the controller.
|
|
582
|
+
* @returns Promise that resolves when migration is enabled.
|
|
583
|
+
*/
|
|
584
|
+
static async enableMigration(config, client, vaultConnector, walletConnector, logging, gasBudget, identity, namespace, packageId, deploymentConfig, walletAddressIndex) {
|
|
585
|
+
try {
|
|
586
|
+
const txb = new transactions.Transaction();
|
|
587
|
+
txb.setGasBudget(gasBudget);
|
|
588
|
+
const moduleName = this.getModuleName(namespace);
|
|
589
|
+
// Get admin address for the transaction
|
|
590
|
+
const adminAddress = await this.getPackageControllerAddress(walletConnector, identity, walletAddressIndex);
|
|
591
|
+
// Get the required object IDs from deployment config
|
|
592
|
+
const { adminCapId, migrationStateId } = await this.getContractObjectIds(client, namespace, config.network, deploymentConfig, packageId, adminAddress);
|
|
593
|
+
txb.moveCall({
|
|
594
|
+
target: `${packageId}::${moduleName}::enable_migration`,
|
|
595
|
+
arguments: [txb.object(adminCapId), txb.object(migrationStateId)]
|
|
596
|
+
});
|
|
597
|
+
const result = await Iota.prepareAndPostTransaction(config, vaultConnector, logging, identity, client, adminAddress, txb, {
|
|
598
|
+
dryRunLabel: config.enableCostLogging ? "enable_migration" : undefined
|
|
599
|
+
});
|
|
600
|
+
if (result.effects?.status?.status !== "success") {
|
|
601
|
+
throw new core.GeneralError(IotaSmartContractUtils.CLASS_NAME, "enableMigrationFailed", {
|
|
602
|
+
error: result.effects?.status?.error
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
catch (error) {
|
|
607
|
+
throw new core.GeneralError(IotaSmartContractUtils.CLASS_NAME, "enableMigrationFailed", undefined, error);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Disable migration operations using admin privileges.
|
|
612
|
+
* @param config The IOTA configuration.
|
|
613
|
+
* @param client The IOTA client instance.
|
|
614
|
+
* @param vaultConnector The vault connector for key management.
|
|
615
|
+
* @param walletConnector The wallet connector for address generation.
|
|
616
|
+
* @param logging Optional logging component.
|
|
617
|
+
* @param gasBudget The gas budget for the transaction.
|
|
618
|
+
* @param identity The identity of the controller with admin privileges.
|
|
619
|
+
* @param namespace The contract namespace (e.g., "nft", "verifiable_storage").
|
|
620
|
+
* @param packageId The deployed package ID for the contract.
|
|
621
|
+
* @param deploymentConfig The deployment configuration containing object IDs.
|
|
622
|
+
* @param walletAddressIndex Optional wallet address index for the controller.
|
|
623
|
+
* @returns Promise that resolves when migration is disabled.
|
|
624
|
+
*/
|
|
625
|
+
static async disableMigration(config, client, vaultConnector, walletConnector, logging, gasBudget, identity, namespace, packageId, deploymentConfig, walletAddressIndex) {
|
|
626
|
+
try {
|
|
627
|
+
const txb = new transactions.Transaction();
|
|
628
|
+
txb.setGasBudget(gasBudget);
|
|
629
|
+
const moduleName = this.getModuleName(namespace);
|
|
630
|
+
// Get admin address for the transaction
|
|
631
|
+
const adminAddress = await this.getPackageControllerAddress(walletConnector, identity, walletAddressIndex);
|
|
632
|
+
// Get the required object IDs from deployment config
|
|
633
|
+
const { adminCapId, migrationStateId } = await this.getContractObjectIds(client, namespace, config.network, deploymentConfig, packageId, adminAddress);
|
|
634
|
+
txb.moveCall({
|
|
635
|
+
target: `${packageId}::${moduleName}::disable_migration`,
|
|
636
|
+
arguments: [txb.object(adminCapId), txb.object(migrationStateId)]
|
|
637
|
+
});
|
|
638
|
+
const result = await Iota.prepareAndPostTransaction(config, vaultConnector, logging, identity, client, adminAddress, txb, {
|
|
639
|
+
dryRunLabel: config.enableCostLogging ? "disable_migration" : undefined
|
|
640
|
+
});
|
|
641
|
+
if (result.effects?.status?.status !== "success") {
|
|
642
|
+
throw new core.GeneralError(IotaSmartContractUtils.CLASS_NAME, "disableMigrationFailed", {
|
|
643
|
+
error: result.effects?.status?.error
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
catch (error) {
|
|
648
|
+
throw new core.GeneralError(IotaSmartContractUtils.CLASS_NAME, "disableMigrationFailed", undefined, error);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Check if migration is currently active for a smart contract.
|
|
653
|
+
* @param config The IOTA configuration.
|
|
654
|
+
* @param client The IOTA client instance.
|
|
655
|
+
* @param namespace The contract namespace (e.g., "nft", "verifiable_storage").
|
|
656
|
+
* @param packageId The deployed package ID for the contract.
|
|
657
|
+
* @param deploymentConfig The deployment configuration containing object IDs.
|
|
658
|
+
* @param identity The identity for MigrationState discovery.
|
|
659
|
+
* @param walletConnector The wallet connector for address generation.
|
|
660
|
+
* @param walletAddressIndex Optional wallet address index.
|
|
661
|
+
* @returns True if migration is enabled, false otherwise.
|
|
662
|
+
*/
|
|
663
|
+
static async isMigrationActive(config, client, namespace, packageId, deploymentConfig, identity, walletConnector, walletAddressIndex) {
|
|
664
|
+
try {
|
|
665
|
+
// Get admin address for discovery
|
|
666
|
+
const adminAddress = await this.getPackageControllerAddress(walletConnector, identity, walletAddressIndex);
|
|
667
|
+
// Get the migration state ID
|
|
668
|
+
const { migrationStateId } = await this.getContractObjectIds(client, namespace, config.network, deploymentConfig, packageId, adminAddress);
|
|
669
|
+
const migrationStateResponse = await client.getObject({
|
|
670
|
+
id: migrationStateId,
|
|
671
|
+
options: {
|
|
672
|
+
showContent: true,
|
|
673
|
+
showType: true
|
|
674
|
+
}
|
|
675
|
+
});
|
|
676
|
+
if (!migrationStateResponse.data?.content) {
|
|
677
|
+
throw new core.GeneralError(IotaSmartContractUtils.CLASS_NAME, "migrationStateNotReadable", {
|
|
678
|
+
migrationStateId
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
const content = migrationStateResponse.data.content;
|
|
682
|
+
if (content.dataType === "moveObject" && core.Is.objectValue(content.fields)) {
|
|
683
|
+
const fields = content.fields;
|
|
684
|
+
return core.Is.boolean(fields.enabled) ? fields.enabled : false;
|
|
685
|
+
}
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
catch (error) {
|
|
689
|
+
throw new core.GeneralError(IotaSmartContractUtils.CLASS_NAME, "isMigrationActiveFailed", undefined, error);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Get the current contract version from the deployed smart contract.
|
|
694
|
+
* @param config The IOTA configuration.
|
|
695
|
+
* @param client The IOTA client instance.
|
|
696
|
+
* @param namespace The contract namespace (e.g., "nft", "verifiable_storage").
|
|
697
|
+
* @param packageId The deployed package ID for the contract.
|
|
698
|
+
* @param identity The identity for package controller address.
|
|
699
|
+
* @param walletConnector The wallet connector for address generation.
|
|
700
|
+
* @param walletAddressIndex Optional wallet address index.
|
|
701
|
+
* @returns The current version number of the contract.
|
|
702
|
+
*/
|
|
703
|
+
static async getCurrentContractVersion(config, client, namespace, packageId, identity, walletConnector, walletAddressIndex) {
|
|
704
|
+
try {
|
|
705
|
+
const tx = new transactions.Transaction();
|
|
706
|
+
const moduleName = this.getModuleName(namespace);
|
|
707
|
+
tx.moveCall({
|
|
708
|
+
target: `${packageId}::${moduleName}::get_current_version`,
|
|
709
|
+
arguments: []
|
|
710
|
+
});
|
|
711
|
+
const controllerAddress = await this.getPackageControllerAddress(walletConnector, identity, walletAddressIndex);
|
|
712
|
+
const result = await client.devInspectTransactionBlock({
|
|
713
|
+
sender: controllerAddress,
|
|
714
|
+
transactionBlock: tx
|
|
715
|
+
});
|
|
716
|
+
if (core.Is.arrayValue(result.results) &&
|
|
717
|
+
core.Is.object(result.results[0]) &&
|
|
718
|
+
core.Is.arrayValue(result.results[0].returnValues)) {
|
|
719
|
+
const versionBytes = result.results[0].returnValues[0];
|
|
720
|
+
// Convert to Uint8Array if it's a regular array
|
|
721
|
+
const byteData = versionBytes[0];
|
|
722
|
+
if (!core.Is.arrayValue(byteData) && !core.Is.uint8Array(byteData)) {
|
|
723
|
+
throw new core.GeneralError(this.CLASS_NAME, "invalidVersionData");
|
|
724
|
+
}
|
|
725
|
+
// Convert to Uint8Array for BCS parsing
|
|
726
|
+
const uint8Data = core.Is.uint8Array(byteData) ? byteData : new Uint8Array(byteData);
|
|
727
|
+
// The version is returned as a u64, decode it from bytes using BCS
|
|
728
|
+
// IOTA Move contracts return data in BCS format
|
|
729
|
+
const version = Number(bcs.bcs.u64().parse(uint8Data));
|
|
730
|
+
return version;
|
|
731
|
+
}
|
|
732
|
+
throw new core.GeneralError(IotaSmartContractUtils.CLASS_NAME, "getCurrentContractVersionNoData", {
|
|
733
|
+
resultExists: core.Is.arrayValue(result.results),
|
|
734
|
+
resultLength: core.Is.arrayValue(result.results) ? result.results.length : 0,
|
|
735
|
+
hasReturnValues: core.Is.arrayValue(result.results?.[0]?.returnValues)
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
catch (error) {
|
|
739
|
+
throw new core.GeneralError(IotaSmartContractUtils.CLASS_NAME, "getCurrentContractVersionFailed", undefined, error);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Validate that an object version is compatible with the current contract.
|
|
744
|
+
* @param config The IOTA configuration.
|
|
745
|
+
* @param client The IOTA client instance.
|
|
746
|
+
* @param namespace The contract namespace (e.g., "nft", "verifiable_storage").
|
|
747
|
+
* @param packageId The deployed package ID for the contract.
|
|
748
|
+
* @param identity The identity for version checking.
|
|
749
|
+
* @param objectId The object ID to validate.
|
|
750
|
+
* @param walletConnector The wallet connector for address generation.
|
|
751
|
+
* @param versionExtractor Function to extract version from object content.
|
|
752
|
+
* @param walletAddressIndex Optional wallet address index.
|
|
753
|
+
* @returns True if the object version is compatible, false otherwise.
|
|
754
|
+
*/
|
|
755
|
+
static async validateObjectVersion(config, client, namespace, packageId, identity, objectId, walletConnector, versionExtractor, walletAddressIndex) {
|
|
756
|
+
try {
|
|
757
|
+
// Get current contract version
|
|
758
|
+
const currentVersion = await this.getCurrentContractVersion(config, client, namespace, packageId, identity, walletConnector, walletAddressIndex);
|
|
759
|
+
// Get object version
|
|
760
|
+
const objectResponse = await client.getObject({
|
|
761
|
+
id: objectId,
|
|
762
|
+
options: {
|
|
763
|
+
showContent: true,
|
|
764
|
+
showType: true
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
if (!objectResponse.data?.content) {
|
|
768
|
+
throw new core.GeneralError(IotaSmartContractUtils.CLASS_NAME, "objectNotReadable", {
|
|
769
|
+
objectId
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
const content = objectResponse.data.content;
|
|
773
|
+
if (content.dataType === "moveObject" && core.Is.objectValue(content.fields)) {
|
|
774
|
+
const objectVersion = versionExtractor(content);
|
|
775
|
+
return objectVersion <= currentVersion;
|
|
776
|
+
}
|
|
777
|
+
throw new core.GeneralError(IotaSmartContractUtils.CLASS_NAME, "objectInvalidFormat", {
|
|
778
|
+
objectId,
|
|
779
|
+
content
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
catch (error) {
|
|
783
|
+
throw new core.GeneralError(IotaSmartContractUtils.CLASS_NAME, "validateObjectVersionFailed", { objectId }, error);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Get the module name for a given namespace.
|
|
788
|
+
* @param namespace The contract namespace.
|
|
789
|
+
* @returns The module name in snake_case format.
|
|
790
|
+
* @internal
|
|
791
|
+
*/
|
|
792
|
+
static getModuleName(namespace) {
|
|
793
|
+
// Convert namespace to snake_case for module name
|
|
794
|
+
return core.StringHelper.snakeCase(namespace);
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Get the package controller address for transactions.
|
|
798
|
+
* @param walletConnector The wallet connector for address generation.
|
|
799
|
+
* @param identity The identity to use.
|
|
800
|
+
* @param addressIndex Optional address index to use.
|
|
801
|
+
* @returns The controller address.
|
|
802
|
+
* @internal
|
|
803
|
+
*/
|
|
804
|
+
static async getPackageControllerAddress(walletConnector, identity, addressIndex = 0) {
|
|
805
|
+
const addresses = await walletConnector.getAddresses(identity, 0, addressIndex, 1);
|
|
806
|
+
return addresses[0];
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Get contract object IDs (AdminCap and MigrationState) from deployment config with fallback discovery.
|
|
810
|
+
* @param client The IOTA client instance.
|
|
811
|
+
* @param namespace The contract namespace.
|
|
812
|
+
* @param network The network name.
|
|
813
|
+
* @param deploymentConfig The deployment configuration.
|
|
814
|
+
* @param packageId The package ID.
|
|
815
|
+
* @param adminAddress The admin address for object discovery.
|
|
816
|
+
* @returns Object containing adminCapId and migrationStateId.
|
|
817
|
+
* @internal
|
|
818
|
+
*/
|
|
819
|
+
static async getContractObjectIds(client, namespace, network, deploymentConfig, packageId, adminAddress) {
|
|
820
|
+
try {
|
|
821
|
+
// First try to load from deployment JSON
|
|
822
|
+
const networkConfig = deploymentConfig[network];
|
|
823
|
+
if (core.Is.objectValue(networkConfig)) {
|
|
824
|
+
const migrationStateId = networkConfig.migrationStateId;
|
|
825
|
+
// AdminCap must be discovered from blockchain (not stored in JSON)
|
|
826
|
+
const adminCapId = await this.discoverAdminCap(client, packageId, namespace, adminAddress);
|
|
827
|
+
if (core.Is.stringValue(migrationStateId) && core.Is.stringValue(adminCapId)) {
|
|
828
|
+
return { adminCapId, migrationStateId };
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
// Fallback: discover both from blockchain
|
|
832
|
+
return await this.discoverContractObjectsFromBlockchain(client, packageId, namespace, adminAddress);
|
|
833
|
+
}
|
|
834
|
+
catch (error) {
|
|
835
|
+
throw new core.GeneralError(IotaSmartContractUtils.CLASS_NAME, "getContractObjectIdsFailed", { namespace, network, packageId }, error);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Discover AdminCap object from the blockchain.
|
|
840
|
+
* @param client The IOTA client instance.
|
|
841
|
+
* @param packageId The package ID.
|
|
842
|
+
* @param namespace The contract namespace.
|
|
843
|
+
* @param adminAddress The admin address.
|
|
844
|
+
* @returns The AdminCap object ID.
|
|
845
|
+
* @internal
|
|
846
|
+
*/
|
|
847
|
+
static async discoverAdminCap(client, packageId, namespace, adminAddress) {
|
|
848
|
+
const adminCapType = `${packageId}::${this.getModuleName(namespace)}::AdminCap`;
|
|
849
|
+
const adminCapObjects = await client.getOwnedObjects({
|
|
850
|
+
owner: adminAddress,
|
|
851
|
+
filter: {
|
|
852
|
+
StructType: adminCapType
|
|
853
|
+
},
|
|
854
|
+
options: {
|
|
855
|
+
showContent: true,
|
|
856
|
+
showType: true
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
if (core.Is.arrayValue(adminCapObjects.data) && adminCapObjects.data.length > 0) {
|
|
860
|
+
const adminCapObject = adminCapObjects.data[0];
|
|
861
|
+
if (core.Is.stringValue(adminCapObject.data?.objectId)) {
|
|
862
|
+
return adminCapObject.data.objectId;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
throw new core.GeneralError(IotaSmartContractUtils.CLASS_NAME, "adminCapNotFound", {
|
|
866
|
+
adminCapType,
|
|
867
|
+
adminAddress
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Discover contract objects from blockchain as fallback.
|
|
872
|
+
* @param client The IOTA client instance.
|
|
873
|
+
* @param packageId The package ID.
|
|
874
|
+
* @param namespace The contract namespace.
|
|
875
|
+
* @param adminAddress The admin address.
|
|
876
|
+
* @returns Object containing adminCapId and migrationStateId.
|
|
877
|
+
* @internal
|
|
878
|
+
*/
|
|
879
|
+
static async discoverContractObjectsFromBlockchain(client, packageId, namespace, adminAddress) {
|
|
880
|
+
// Discover AdminCap
|
|
881
|
+
const adminCapId = await this.discoverAdminCap(client, packageId, namespace, adminAddress);
|
|
882
|
+
// Discover MigrationState through transaction history
|
|
883
|
+
const migrationStateType = `${packageId}::${this.getModuleName(namespace)}::MigrationState`;
|
|
884
|
+
const transactions = await client.queryTransactionBlocks({
|
|
885
|
+
filter: {
|
|
886
|
+
FromAddress: adminAddress
|
|
887
|
+
},
|
|
888
|
+
options: {
|
|
889
|
+
showObjectChanges: true,
|
|
890
|
+
showEffects: true
|
|
891
|
+
},
|
|
892
|
+
limit: 20,
|
|
893
|
+
order: "descending"
|
|
894
|
+
});
|
|
895
|
+
// Look for MigrationState object creation in transaction history
|
|
896
|
+
let migrationStateId;
|
|
897
|
+
for (const tx of transactions.data) {
|
|
898
|
+
const objectChanges = tx.objectChanges;
|
|
899
|
+
if (core.Is.arrayValue(objectChanges)) {
|
|
900
|
+
for (const change of objectChanges) {
|
|
901
|
+
if ((change.type === "created" || change.type === "mutated") &&
|
|
902
|
+
"objectType" in change &&
|
|
903
|
+
change.objectType === migrationStateType) {
|
|
904
|
+
migrationStateId = change.objectId;
|
|
905
|
+
break;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
if (migrationStateId) {
|
|
909
|
+
break;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
if (!migrationStateId) {
|
|
914
|
+
throw new core.GeneralError(IotaSmartContractUtils.CLASS_NAME, "migrationStateNotFound", {
|
|
915
|
+
migrationStateType,
|
|
916
|
+
adminAddress
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
return { adminCapId, migrationStateId };
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// Copyright 2024 IOTA Stiftung.
|
|
924
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
925
|
+
/**
|
|
926
|
+
* Network types supported for deployment
|
|
927
|
+
*/
|
|
928
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
929
|
+
const NetworkTypes = {
|
|
930
|
+
/**
|
|
931
|
+
* Testnet.
|
|
932
|
+
*/
|
|
933
|
+
Testnet: "testnet",
|
|
934
|
+
/**
|
|
935
|
+
* Devnet.
|
|
936
|
+
*/
|
|
937
|
+
Devnet: "devnet",
|
|
938
|
+
/**
|
|
939
|
+
* Mainnet.
|
|
940
|
+
*/
|
|
941
|
+
Mainnet: "mainnet"
|
|
942
|
+
};
|
|
943
|
+
|
|
514
944
|
exports.Iota = Iota;
|
|
945
|
+
exports.IotaSmartContractUtils = IotaSmartContractUtils;
|
|
946
|
+
exports.NetworkTypes = NetworkTypes;
|