@twin.org/dlt-iota 0.0.3-next.5 → 0.0.3-next.7

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.
Files changed (32) hide show
  1. package/README.md +4 -6
  2. package/dist/es/index.js +2 -0
  3. package/dist/es/index.js.map +1 -1
  4. package/dist/es/iotaIdentityUtils.js +84 -0
  5. package/dist/es/iotaIdentityUtils.js.map +1 -0
  6. package/dist/es/models/IIotaControllerCapInfo.js +4 -0
  7. package/dist/es/models/IIotaControllerCapInfo.js.map +1 -0
  8. package/dist/types/index.d.ts +2 -0
  9. package/dist/types/iotaIdentityUtils.d.ts +23 -0
  10. package/dist/types/models/IIotaControllerCapInfo.d.ts +16 -0
  11. package/docs/changelog.md +15 -1
  12. package/docs/examples.md +420 -1
  13. package/docs/reference/classes/Iota.md +26 -26
  14. package/docs/reference/classes/IotaIdentityUtils.md +65 -0
  15. package/docs/reference/classes/IotaSmartContractUtils.md +7 -7
  16. package/docs/reference/index.md +2 -0
  17. package/docs/reference/interfaces/IAdminCapFields.md +1 -1
  18. package/docs/reference/interfaces/IContractData.md +6 -6
  19. package/docs/reference/interfaces/IGasReservationResult.md +3 -3
  20. package/docs/reference/interfaces/IGasStationConfig.md +2 -2
  21. package/docs/reference/interfaces/IGasStationExecuteResponse.md +2 -2
  22. package/docs/reference/interfaces/IGasStationReserveGasResponse.md +2 -2
  23. package/docs/reference/interfaces/IGasStationReserveGasResult.md +3 -3
  24. package/docs/reference/interfaces/IIotaConfig.md +10 -52
  25. package/docs/reference/interfaces/IIotaControllerCapInfo.md +22 -0
  26. package/docs/reference/interfaces/IIotaDryRun.md +5 -5
  27. package/docs/reference/interfaces/IIotaResponseOptions.md +2 -8
  28. package/docs/reference/interfaces/IMigrationStateFields.md +2 -2
  29. package/docs/reference/interfaces/ISmartContractObject.md +2 -2
  30. package/docs/reference/variables/NetworkTypes.md +3 -3
  31. package/locales/en.json +6 -0
  32. package/package.json +3 -2
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # TWIN DLT IOTA
2
2
 
3
- DLT helpers for use with IOTA.
3
+ This package provides utilities for integrating applications with IOTA distributed ledger capabilities, including client construction, transaction submission, sponsored transaction support, and smart contract migration helpers. It is intended for services that need a reliable and repeatable approach to ledger operations without rebuilding common primitives.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,14 +8,12 @@ DLT helpers for use with IOTA.
8
8
  npm install @twin.org/dlt-iota
9
9
  ```
10
10
 
11
- ## Testing
11
+ ## Docker
12
12
 
13
- The tests developed are functional tests and need an instance of the IOTA Gas Station and Redis up and running.
14
-
15
- The simplest way to set up the testing environment using our unified container:
13
+ To perform testing of this component it may be necessary to launch a local instance to communicate with.
16
14
 
17
15
  ```shell
18
- docker run -d --name twin-gas-station-test -p 9527:9527 -p 6379:6379 -p 9184:9184 twinfoundation/twin-gas-station-test:latest
16
+ docker run -d --name twin-dlt-iota -p 9527:9527 -p 6379:6379 -p 9184:9184 twinfoundation/twin-gas-station-test:latest
19
17
  ```
20
18
 
21
19
  ## Examples
package/dist/es/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // Copyright 2024 IOTA Stiftung.
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
3
  export * from "./iota.js";
4
+ export * from "./iotaIdentityUtils.js";
4
5
  export * from "./iotaSmartContractUtils.js";
5
6
  export * from "./models/IAdminCapFields.js";
6
7
  export * from "./models/IContractData.js";
@@ -10,6 +11,7 @@ export * from "./models/IGasStationExecuteResponse.js";
10
11
  export * from "./models/IGasStationReserveGasResponse.js";
11
12
  export * from "./models/IGasStationReserveGasResult.js";
12
13
  export * from "./models/IIotaClient.js";
14
+ export * from "./models/IIotaControllerCapInfo.js";
13
15
  export * from "./models/IIotaTransaction.js";
14
16
  export * from "./models/IIotaTransactionBlockResponse.js";
15
17
  export * from "./models/IIotaConfig.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,WAAW,CAAC;AAC1B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC;AAClD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wCAAwC,CAAC;AACvD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,yCAAyC,CAAC;AACxD,cAAc,yBAAyB,CAAC;AACxC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,2CAA2C,CAAC;AAC1D,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,cAAc,uCAAuC,CAAC;AACtD,cAAc,kCAAkC,CAAC;AACjD,cAAc,0BAA0B,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./iota.js\";\nexport * from \"./iotaSmartContractUtils.js\";\nexport * from \"./models/IAdminCapFields.js\";\nexport * from \"./models/IContractData.js\";\nexport * from \"./models/IGasReservationResult.js\";\nexport * from \"./models/IGasStationConfig.js\";\nexport * from \"./models/IGasStationExecuteResponse.js\";\nexport * from \"./models/IGasStationReserveGasResponse.js\";\nexport * from \"./models/IGasStationReserveGasResult.js\";\nexport * from \"./models/IIotaClient.js\";\nexport * from \"./models/IIotaTransaction.js\";\nexport * from \"./models/IIotaTransactionBlockResponse.js\";\nexport * from \"./models/IIotaConfig.js\";\nexport * from \"./models/IIotaDryRun.js\";\nexport * from \"./models/IIotaResponseOptions.js\";\nexport * from \"./models/IMigrationStateFields.js\";\nexport * from \"./models/ISmartContractDeployments.js\";\nexport * from \"./models/ISmartContractObject.js\";\nexport * from \"./models/networkTypes.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,WAAW,CAAC;AAC1B,cAAc,wBAAwB,CAAC;AACvC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC;AAClD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wCAAwC,CAAC;AACvD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,yCAAyC,CAAC;AACxD,cAAc,yBAAyB,CAAC;AACxC,cAAc,oCAAoC,CAAC;AACnD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,2CAA2C,CAAC;AAC1D,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,cAAc,uCAAuC,CAAC;AACtD,cAAc,kCAAkC,CAAC;AACjD,cAAc,0BAA0B,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./iota.js\";\nexport * from \"./iotaIdentityUtils.js\";\nexport * from \"./iotaSmartContractUtils.js\";\nexport * from \"./models/IAdminCapFields.js\";\nexport * from \"./models/IContractData.js\";\nexport * from \"./models/IGasReservationResult.js\";\nexport * from \"./models/IGasStationConfig.js\";\nexport * from \"./models/IGasStationExecuteResponse.js\";\nexport * from \"./models/IGasStationReserveGasResponse.js\";\nexport * from \"./models/IGasStationReserveGasResult.js\";\nexport * from \"./models/IIotaClient.js\";\nexport * from \"./models/IIotaControllerCapInfo.js\";\nexport * from \"./models/IIotaTransaction.js\";\nexport * from \"./models/IIotaTransactionBlockResponse.js\";\nexport * from \"./models/IIotaConfig.js\";\nexport * from \"./models/IIotaDryRun.js\";\nexport * from \"./models/IIotaResponseOptions.js\";\nexport * from \"./models/IMigrationStateFields.js\";\nexport * from \"./models/ISmartContractDeployments.js\";\nexport * from \"./models/ISmartContractObject.js\";\nexport * from \"./models/networkTypes.js\";\n"]}
@@ -0,0 +1,84 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { IdentityClient, IdentityClientReadOnly, Jwk, JwkMemStore, KeyIdMemStore, OnChainIdentity, Storage, StorageSigner } from "@iota/identity-wasm/node/index.js";
4
+ import { decodeIotaPrivateKey } from "@iota/iota-sdk/cryptography";
5
+ import { Ed25519Keypair } from "@iota/iota-sdk/keypairs/ed25519";
6
+ import { Base64Url, GeneralError, Guards, Is, NotFoundError } from "@twin.org/core";
7
+ import { Iota } from "./iota.js";
8
+ /**
9
+ * Utility class for resolving IOTA Identity on-chain objects required by
10
+ * the NFT mint_with_identity() Move contract function.
11
+ */
12
+ export class IotaIdentityUtils {
13
+ /**
14
+ * Runtime name for the class.
15
+ */
16
+ static CLASS_NAME = "IotaIdentityUtils";
17
+ /**
18
+ * Resolve the on-chain object IDs for an identity and its controller token.
19
+ * Returns the IDs needed to call mint_with_identity() on the NFT Move contract.
20
+ * @param identityId The DID of the identity (e.g. "did:iota:testnet:0x...").
21
+ * @param controllerAddress The on-chain address of the controller wallet.
22
+ * @param client The IOTA client instance.
23
+ * @returns The identity object ID and controller token object ID.
24
+ * @throws NotFoundError if the identity does not exist on-chain.
25
+ * @throws GeneralError if the identity has been deleted or the controller token is not found.
26
+ */
27
+ static async getControllerCapInfo(identityId, controllerAddress, client) {
28
+ Guards.stringValue(IotaIdentityUtils.CLASS_NAME, "identityId", identityId);
29
+ Guards.stringValue(IotaIdentityUtils.CLASS_NAME, "controllerAddress", controllerAddress);
30
+ Guards.object(IotaIdentityUtils.CLASS_NAME, "client", client);
31
+ // Extract the Object ID from the DID — last colon-delimited segment.
32
+ // On-chain DIDs include the 0x prefix in the segment; the check avoids double-prefixing.
33
+ const idParts = identityId.split(":");
34
+ const lastSegment = idParts[idParts.length - 1];
35
+ const identityObjectId = lastSegment.startsWith("0x") ? lastSegment : `0x${lastSegment}`;
36
+ let onChain;
37
+ let controllerToken;
38
+ try {
39
+ // IIotaClient and the IotaClient expected by identity-wasm resolve from different
40
+ // module entry points (dist/esm vs dist/cjs). The protected `transport` field causes
41
+ // a structural incompatibility even though the runtime types are identical.
42
+ const identityClientReadOnly = await IdentityClientReadOnly.create(client);
43
+ // getControllerTokenForAddress requires IdentityClient even though the operation
44
+ // is read-only — the signer is never called, only the embedded read-only client.
45
+ // StorageSigner is used (rather than a plain object satisfying TransactionSigner
46
+ // structurally) because IdentityClient.create() validates iotaPublicKeyBytes()
47
+ // through an internal WASM code path that only accepts bytes from the library's
48
+ // own signer implementations. Plain JS objects fail with "Unsupported curve"
49
+ // even for valid Ed25519 keys because they take a different callback path.
50
+ const noOpKeypair = new Ed25519Keypair();
51
+ const rawPublic = noOpKeypair.getPublicKey().toRawBytes();
52
+ const { secretKey: rawPrivate } = decodeIotaPrivateKey(noOpKeypair.getSecretKey());
53
+ const noOpSigner = new StorageSigner(new Storage(new JwkMemStore(), new KeyIdMemStore()), "", new Jwk({
54
+ kty: "OKP" /* JwkType.Okp */,
55
+ crv: "Ed25519",
56
+ alg: "EdDSA" /* JwsAlgorithm.EdDSA */,
57
+ x: Base64Url.encode(rawPublic),
58
+ d: Base64Url.encode(rawPrivate)
59
+ }));
60
+ const identityClient = await IdentityClient.create(identityClientReadOnly, noOpSigner);
61
+ onChain = await OnChainIdentity.getById(identityObjectId, identityClient);
62
+ if (!Is.undefined(onChain) && !onChain.hasDeletedDid()) {
63
+ controllerToken = await onChain.getControllerTokenForAddress(controllerAddress, identityClient);
64
+ }
65
+ }
66
+ catch (error) {
67
+ throw new GeneralError(IotaIdentityUtils.CLASS_NAME, "getControllerCapInfoFailed", undefined, Iota.extractPayloadError(error));
68
+ }
69
+ if (Is.undefined(onChain)) {
70
+ throw new NotFoundError(IotaIdentityUtils.CLASS_NAME, "identityNotFound", identityId);
71
+ }
72
+ if (onChain.hasDeletedDid()) {
73
+ throw new GeneralError(IotaIdentityUtils.CLASS_NAME, "identityDeleted", { identityId });
74
+ }
75
+ if (Is.undefined(controllerToken)) {
76
+ throw new NotFoundError(IotaIdentityUtils.CLASS_NAME, "controllerTokenNotFound", identityId);
77
+ }
78
+ return {
79
+ identityObjectId,
80
+ controllerCapObjectId: controllerToken.id()
81
+ };
82
+ }
83
+ }
84
+ //# sourceMappingURL=iotaIdentityUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iotaIdentityUtils.js","sourceRoot":"","sources":["../../src/iotaIdentityUtils.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\tIdentityClient,\n\tIdentityClientReadOnly,\n\tJwk,\n\tJwkMemStore,\n\tJwkType,\n\tJwsAlgorithm,\n\tKeyIdMemStore,\n\tOnChainIdentity,\n\tStorage,\n\tStorageSigner\n} from \"@iota/identity-wasm/node/index.js\";\nimport { decodeIotaPrivateKey } from \"@iota/iota-sdk/cryptography\";\nimport { Ed25519Keypair } from \"@iota/iota-sdk/keypairs/ed25519\";\nimport { Base64Url, GeneralError, Guards, Is, NotFoundError } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { Iota } from \"./iota.js\";\nimport type { IIotaClient } from \"./models/IIotaClient.js\";\nimport type { IIotaControllerCapInfo } from \"./models/IIotaControllerCapInfo.js\";\n\n/**\n * Utility class for resolving IOTA Identity on-chain objects required by\n * the NFT mint_with_identity() Move contract function.\n */\nexport class IotaIdentityUtils {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<IotaIdentityUtils>();\n\n\t/**\n\t * Resolve the on-chain object IDs for an identity and its controller token.\n\t * Returns the IDs needed to call mint_with_identity() on the NFT Move contract.\n\t * @param identityId The DID of the identity (e.g. \"did:iota:testnet:0x...\").\n\t * @param controllerAddress The on-chain address of the controller wallet.\n\t * @param client The IOTA client instance.\n\t * @returns The identity object ID and controller token object ID.\n\t * @throws NotFoundError if the identity does not exist on-chain.\n\t * @throws GeneralError if the identity has been deleted or the controller token is not found.\n\t */\n\tpublic static async getControllerCapInfo(\n\t\tidentityId: string,\n\t\tcontrollerAddress: string,\n\t\tclient: IIotaClient\n\t): Promise<IIotaControllerCapInfo> {\n\t\tGuards.stringValue(IotaIdentityUtils.CLASS_NAME, nameof(identityId), identityId);\n\t\tGuards.stringValue(IotaIdentityUtils.CLASS_NAME, nameof(controllerAddress), controllerAddress);\n\t\tGuards.object(IotaIdentityUtils.CLASS_NAME, nameof(client), client);\n\n\t\t// Extract the Object ID from the DID — last colon-delimited segment.\n\t\t// On-chain DIDs include the 0x prefix in the segment; the check avoids double-prefixing.\n\t\tconst idParts = identityId.split(\":\");\n\t\tconst lastSegment = idParts[idParts.length - 1];\n\t\tconst identityObjectId = lastSegment.startsWith(\"0x\") ? lastSegment : `0x${lastSegment}`;\n\n\t\tlet onChain: OnChainIdentity | undefined;\n\t\tlet controllerToken: Awaited<ReturnType<OnChainIdentity[\"getControllerTokenForAddress\"]>>;\n\n\t\ttry {\n\t\t\t// IIotaClient and the IotaClient expected by identity-wasm resolve from different\n\t\t\t// module entry points (dist/esm vs dist/cjs). The protected `transport` field causes\n\t\t\t// a structural incompatibility even though the runtime types are identical.\n\t\t\tconst identityClientReadOnly = await IdentityClientReadOnly.create(\n\t\t\t\tclient as unknown as Parameters<(typeof IdentityClientReadOnly)[\"create\"]>[0]\n\t\t\t);\n\n\t\t\t// getControllerTokenForAddress requires IdentityClient even though the operation\n\t\t\t// is read-only — the signer is never called, only the embedded read-only client.\n\t\t\t// StorageSigner is used (rather than a plain object satisfying TransactionSigner\n\t\t\t// structurally) because IdentityClient.create() validates iotaPublicKeyBytes()\n\t\t\t// through an internal WASM code path that only accepts bytes from the library's\n\t\t\t// own signer implementations. Plain JS objects fail with \"Unsupported curve\"\n\t\t\t// even for valid Ed25519 keys because they take a different callback path.\n\t\t\tconst noOpKeypair = new Ed25519Keypair();\n\t\t\tconst rawPublic = noOpKeypair.getPublicKey().toRawBytes();\n\t\t\tconst { secretKey: rawPrivate } = decodeIotaPrivateKey(noOpKeypair.getSecretKey());\n\t\t\tconst noOpSigner = new StorageSigner(\n\t\t\t\tnew Storage(new JwkMemStore(), new KeyIdMemStore()),\n\t\t\t\t\"\",\n\t\t\t\tnew Jwk({\n\t\t\t\t\tkty: JwkType.Okp,\n\t\t\t\t\tcrv: \"Ed25519\",\n\t\t\t\t\talg: JwsAlgorithm.EdDSA,\n\t\t\t\t\tx: Base64Url.encode(rawPublic),\n\t\t\t\t\td: Base64Url.encode(rawPrivate)\n\t\t\t\t})\n\t\t\t);\n\t\t\tconst identityClient = await IdentityClient.create(identityClientReadOnly, noOpSigner);\n\n\t\t\tonChain = await OnChainIdentity.getById(identityObjectId, identityClient);\n\t\t\tif (!Is.undefined(onChain) && !onChain.hasDeletedDid()) {\n\t\t\t\tcontrollerToken = await onChain.getControllerTokenForAddress(\n\t\t\t\t\tcontrollerAddress,\n\t\t\t\t\tidentityClient\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tIotaIdentityUtils.CLASS_NAME,\n\t\t\t\t\"getControllerCapInfoFailed\",\n\t\t\t\tundefined,\n\t\t\t\tIota.extractPayloadError(error)\n\t\t\t);\n\t\t}\n\n\t\tif (Is.undefined(onChain)) {\n\t\t\tthrow new NotFoundError(IotaIdentityUtils.CLASS_NAME, \"identityNotFound\", identityId);\n\t\t}\n\n\t\tif (onChain.hasDeletedDid()) {\n\t\t\tthrow new GeneralError(IotaIdentityUtils.CLASS_NAME, \"identityDeleted\", { identityId });\n\t\t}\n\n\t\tif (Is.undefined(controllerToken)) {\n\t\t\tthrow new NotFoundError(IotaIdentityUtils.CLASS_NAME, \"controllerTokenNotFound\", identityId);\n\t\t}\n\n\t\treturn {\n\t\t\tidentityObjectId,\n\t\t\tcontrollerCapObjectId: controllerToken.id()\n\t\t};\n\t}\n}\n"]}
@@ -0,0 +1,4 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ export {};
4
+ //# sourceMappingURL=IIotaControllerCapInfo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IIotaControllerCapInfo.js","sourceRoot":"","sources":["../../../src/models/IIotaControllerCapInfo.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * On-chain object IDs needed to call mint_with_identity() on the NFT Move contract.\n */\nexport interface IIotaControllerCapInfo {\n\t/**\n\t * The on-chain Object ID of the Identity Move object (hex string, e.g. \"0x...\").\n\t * Used as the Identity argument in mint_with_identity().\n\t */\n\tidentityObjectId: string;\n\n\t/**\n\t * The on-chain Object ID of the ControllerToken Move object (hex string, e.g. \"0x...\").\n\t * Proves that the controller address controls identityObjectId.\n\t * Used as the ControllerCap argument in mint_with_identity().\n\t */\n\tcontrollerCapObjectId: string;\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  export * from "./iota.js";
2
+ export * from "./iotaIdentityUtils.js";
2
3
  export * from "./iotaSmartContractUtils.js";
3
4
  export * from "./models/IAdminCapFields.js";
4
5
  export * from "./models/IContractData.js";
@@ -8,6 +9,7 @@ export * from "./models/IGasStationExecuteResponse.js";
8
9
  export * from "./models/IGasStationReserveGasResponse.js";
9
10
  export * from "./models/IGasStationReserveGasResult.js";
10
11
  export * from "./models/IIotaClient.js";
12
+ export * from "./models/IIotaControllerCapInfo.js";
11
13
  export * from "./models/IIotaTransaction.js";
12
14
  export * from "./models/IIotaTransactionBlockResponse.js";
13
15
  export * from "./models/IIotaConfig.js";
@@ -0,0 +1,23 @@
1
+ import type { IIotaClient } from "./models/IIotaClient.js";
2
+ import type { IIotaControllerCapInfo } from "./models/IIotaControllerCapInfo.js";
3
+ /**
4
+ * Utility class for resolving IOTA Identity on-chain objects required by
5
+ * the NFT mint_with_identity() Move contract function.
6
+ */
7
+ export declare class IotaIdentityUtils {
8
+ /**
9
+ * Runtime name for the class.
10
+ */
11
+ static readonly CLASS_NAME: string;
12
+ /**
13
+ * Resolve the on-chain object IDs for an identity and its controller token.
14
+ * Returns the IDs needed to call mint_with_identity() on the NFT Move contract.
15
+ * @param identityId The DID of the identity (e.g. "did:iota:testnet:0x...").
16
+ * @param controllerAddress The on-chain address of the controller wallet.
17
+ * @param client The IOTA client instance.
18
+ * @returns The identity object ID and controller token object ID.
19
+ * @throws NotFoundError if the identity does not exist on-chain.
20
+ * @throws GeneralError if the identity has been deleted or the controller token is not found.
21
+ */
22
+ static getControllerCapInfo(identityId: string, controllerAddress: string, client: IIotaClient): Promise<IIotaControllerCapInfo>;
23
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * On-chain object IDs needed to call mint_with_identity() on the NFT Move contract.
3
+ */
4
+ export interface IIotaControllerCapInfo {
5
+ /**
6
+ * The on-chain Object ID of the Identity Move object (hex string, e.g. "0x...").
7
+ * Used as the Identity argument in mint_with_identity().
8
+ */
9
+ identityObjectId: string;
10
+ /**
11
+ * The on-chain Object ID of the ControllerToken Move object (hex string, e.g. "0x...").
12
+ * Proves that the controller address controls identityObjectId.
13
+ * Used as the ControllerCap argument in mint_with_identity().
14
+ */
15
+ controllerCapObjectId: string;
16
+ }
package/docs/changelog.md CHANGED
@@ -1,4 +1,18 @@
1
- # @twin.org/dlt-iota - Changelog
1
+ # Changelog
2
+
3
+ ## [0.0.3-next.7](https://github.com/twinfoundation/dlt/compare/dlt-iota-v0.0.3-next.6...dlt-iota-v0.0.3-next.7) (2026-03-13)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * buffer usage required for identity ([#60](https://github.com/twinfoundation/dlt/issues/60)) ([0ba7d16](https://github.com/twinfoundation/dlt/commit/0ba7d165662b0083aa2b4c1325dd8c2e65defa2e))
9
+
10
+ ## [0.0.3-next.6](https://github.com/twinfoundation/dlt/compare/dlt-iota-v0.0.3-next.5...dlt-iota-v0.0.3-next.6) (2026-03-12)
11
+
12
+
13
+ ### Features
14
+
15
+ * add IotaIdentityUtils to resolve IOTA identity controller cap info ([#57](https://github.com/twinfoundation/dlt/issues/57)) ([f15281a](https://github.com/twinfoundation/dlt/commit/f15281af3b2812bde5130a1813f9c0143f1462bf))
2
16
 
3
17
  ## [0.0.3-next.5](https://github.com/twinfoundation/dlt/compare/dlt-iota-v0.0.3-next.4...dlt-iota-v0.0.3-next.5) (2026-03-03)
4
18
 
package/docs/examples.md CHANGED
@@ -1 +1,420 @@
1
- # @twin.org/dlt-iota - Examples
1
+ # DLT IOTA Examples
2
+
3
+ These snippets show practical ways to configure clients, derive addresses, post transactions, and manage contract migration flows in production-style integrations.
4
+
5
+ ## Iota
6
+
7
+ ```typescript
8
+ import { Iota, NetworkTypes, type IIotaConfig } from '@twin.org/dlt-iota';
9
+
10
+ const config: IIotaConfig = {
11
+ network: NetworkTypes.Testnet,
12
+ clientOptions: {
13
+ url: 'https://api.testnet.iota.cafe'
14
+ },
15
+ inclusionTimeoutSeconds: 45
16
+ };
17
+
18
+ Iota.populateConfig(config);
19
+ const client = Iota.createClient(config);
20
+
21
+ console.log(config.coinType); // 4218
22
+ console.log(config.vaultMnemonicId); // mnemonic
23
+ console.log(client !== undefined); // true
24
+ ```
25
+
26
+ ```typescript
27
+ import { Bip39 } from '@twin.org/crypto';
28
+ import { Iota } from '@twin.org/dlt-iota';
29
+
30
+ const mnemonic =
31
+ 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
32
+ const seed = Bip39.mnemonicToSeed(mnemonic);
33
+
34
+ const addresses = Iota.getAddresses(seed, Iota.DEFAULT_COIN_TYPE, 0, 0, 3);
35
+ const keyPair = Iota.getKeyPair(seed, Iota.DEFAULT_COIN_TYPE, 0, 0);
36
+ const found = Iota.findAddress(50, Iota.DEFAULT_COIN_TYPE, seed, addresses[0]);
37
+
38
+ console.log(addresses.length); // 3
39
+ console.log(found.address === addresses[0]); // true
40
+ console.log(keyPair.publicKey.length > 0); // true
41
+ ```
42
+
43
+ ```typescript
44
+ import { Converter } from '@twin.org/core';
45
+ import { Iota, type IIotaConfig } from '@twin.org/dlt-iota';
46
+ import type { IVaultConnector } from '@twin.org/vault-models';
47
+
48
+ const config: IIotaConfig = {
49
+ clientOptions: {
50
+ url: 'https://api.testnet.iota.cafe'
51
+ },
52
+ vaultMnemonicId: 'deployment-mnemonic',
53
+ vaultSeedId: 'deployment-seed'
54
+ };
55
+
56
+ const vaultConnector: IVaultConnector = {
57
+ async getSecret<T>(key: string): Promise<T> {
58
+ if (key.endsWith('/deployment-seed')) {
59
+ return Converter.bytesToBase64(new Uint8Array(32).fill(7)) as T;
60
+ }
61
+ if (key.endsWith('/deployment-mnemonic')) {
62
+ const value =
63
+ 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
64
+ return value as T;
65
+ }
66
+ throw new Error('Unknown key');
67
+ }
68
+ } as IVaultConnector;
69
+
70
+ const mnemonicKey = Iota.buildMnemonicKey('deployer', config.vaultMnemonicId);
71
+ const seedKey = Iota.buildSeedKey('deployer', config.vaultSeedId);
72
+ const seed = await Iota.getSeed(config, vaultConnector, 'deployer');
73
+
74
+ console.log(mnemonicKey); // deployer/deployment-mnemonic
75
+ console.log(seedKey); // deployer/deployment-seed
76
+ console.log(seed.length); // 32
77
+ ```
78
+
79
+ ```typescript
80
+ import {
81
+ Iota,
82
+ type IIotaClient,
83
+ type IIotaConfig,
84
+ type IIotaTransaction,
85
+ type IIotaTransactionBlockResponse
86
+ } from '@twin.org/dlt-iota';
87
+ import type { IVaultConnector } from '@twin.org/vault-models';
88
+
89
+ const config: IIotaConfig = {
90
+ clientOptions: {
91
+ url: 'https://api.testnet.iota.cafe'
92
+ },
93
+ waitForConfirmation: true
94
+ };
95
+
96
+ const tx = Iota.createTransaction();
97
+ tx.setSender('0xabc123');
98
+
99
+ const responseOptions = {
100
+ waitForConfirmation: true,
101
+ dryRunLabel: 'transfer_iota'
102
+ };
103
+
104
+ const client = {} as IIotaClient;
105
+ const vaultConnector = {} as IVaultConnector;
106
+
107
+ const result: IIotaTransactionBlockResponse = await Iota.prepareAndPostTransaction(
108
+ config,
109
+ vaultConnector,
110
+ undefined,
111
+ 'deployer',
112
+ client,
113
+ '0xabc123',
114
+ tx as IIotaTransaction,
115
+ responseOptions
116
+ );
117
+
118
+ await Iota.waitForTransactionConfirmation(client, result.digest, config, {
119
+ showEffects: true,
120
+ showEvents: true,
121
+ showObjectChanges: true
122
+ });
123
+
124
+ console.log(typeof result.digest === 'string'); // true
125
+ ```
126
+
127
+ ```typescript
128
+ import {
129
+ Iota,
130
+ type IIotaClient,
131
+ type IIotaConfig,
132
+ type IIotaTransaction,
133
+ type IIotaTransactionBlockResponse
134
+ } from '@twin.org/dlt-iota';
135
+ import type { IVaultConnector } from '@twin.org/vault-models';
136
+
137
+ const config: IIotaConfig = {
138
+ clientOptions: {
139
+ url: 'https://api.testnet.iota.cafe'
140
+ },
141
+ gasBudget: 80_000_000,
142
+ gasStation: {
143
+ gasStationUrl: 'https://gasstation.testnet.example',
144
+ gasStationAuthToken: 'replace-with-token'
145
+ }
146
+ };
147
+
148
+ const client = {} as IIotaClient;
149
+ const vaultConnector = {} as IVaultConnector;
150
+ const tx = Iota.createTransaction();
151
+
152
+ const gasReservation = await Iota.reserveGas(config, config.gasBudget ?? 50_000_000);
153
+ const txBytes = new Uint8Array([1, 2, 3, 4]);
154
+ const userSignature = 'base64-signature';
155
+
156
+ const sponsoredResult = await Iota.executeGasStationTransaction(
157
+ config,
158
+ gasReservation.reservationId,
159
+ txBytes,
160
+ userSignature
161
+ );
162
+
163
+ const finalResult: IIotaTransactionBlockResponse =
164
+ await Iota.executeAndConfirmGasStationTransaction(
165
+ config,
166
+ client,
167
+ gasReservation.reservationId,
168
+ txBytes,
169
+ userSignature,
170
+ {
171
+ waitForConfirmation: true
172
+ }
173
+ );
174
+
175
+ const postedResult = await Iota.prepareAndPostGasStationTransaction(
176
+ config,
177
+ vaultConnector,
178
+ 'deployer',
179
+ client,
180
+ '0xabc123',
181
+ tx as IIotaTransaction
182
+ );
183
+
184
+ console.log(gasReservation.reservationId > 0); // true
185
+ console.log(sponsoredResult.confirmedLocalExecution); // true
186
+ console.log(typeof finalResult.digest === 'string'); // true
187
+ console.log(typeof postedResult.digest === 'string'); // true
188
+ ```
189
+
190
+ ```typescript
191
+ import { Iota, type IIotaClient, type IIotaConfig } from '@twin.org/dlt-iota';
192
+ import type { ILoggingComponent } from '@twin.org/logging-models';
193
+
194
+ const config: IIotaConfig = {
195
+ clientOptions: {
196
+ url: 'https://api.testnet.iota.cafe'
197
+ },
198
+ inclusionTimeoutSeconds: 30
199
+ };
200
+
201
+ const client = {} as IIotaClient;
202
+ const tx = Iota.createTransaction();
203
+ const logging = {} as ILoggingComponent;
204
+
205
+ const packageExists = await Iota.packageExistsOnNetwork(client, '0x2');
206
+ const dryRun = await Iota.dryRunTransaction(client, logging, tx, '0xabc123', 'mint_token');
207
+ const isAbort = Iota.isAbortError(new Error('MoveAbort: abort code: 17'), 17);
208
+ const parsedError = Iota.extractPayloadError({ code: 'InsufficientGas' });
209
+
210
+ console.log(packageExists); // true
211
+ console.log(dryRun.status); // success
212
+ console.log(isAbort); // true
213
+ console.log(parsedError.name); // General
214
+ ```
215
+
216
+ ```typescript
217
+ import {
218
+ Iota,
219
+ type IIotaClient,
220
+ type IIotaConfig,
221
+ type IIotaTransactionBlockResponse
222
+ } from '@twin.org/dlt-iota';
223
+ import type { IVaultConnector } from '@twin.org/vault-models';
224
+
225
+ const config: IIotaConfig = {
226
+ clientOptions: {
227
+ url: 'https://api.testnet.iota.cafe'
228
+ },
229
+ gasStation: {
230
+ gasStationUrl: 'https://gasstation.testnet.example',
231
+ gasStationAuthToken: 'replace-with-token'
232
+ }
233
+ };
234
+
235
+ const client = {} as IIotaClient;
236
+ const vaultConnector = {} as IVaultConnector;
237
+
238
+ const transferResult: IIotaTransactionBlockResponse = await Iota.prepareAndPostValueTransaction(
239
+ config,
240
+ vaultConnector,
241
+ undefined,
242
+ 'deployer',
243
+ client,
244
+ '0xabc123',
245
+ 5_000_000n,
246
+ '0xdef456'
247
+ );
248
+
249
+ console.log(typeof transferResult.digest === 'string'); // true
250
+ ```
251
+
252
+ ## IotaSmartContractUtils
253
+
254
+ ```typescript
255
+ import {
256
+ IotaSmartContractUtils,
257
+ NetworkTypes,
258
+ type IIotaConfig,
259
+ type ISmartContractDeployments
260
+ } from '@twin.org/dlt-iota';
261
+ import type { IotaClient } from '@iota/iota-sdk/client';
262
+ import type { ILoggingComponent } from '@twin.org/logging-models';
263
+ import type { IVaultConnector } from '@twin.org/vault-models';
264
+ import type { IWalletConnector } from '@twin.org/wallet-models';
265
+
266
+ const config: IIotaConfig = {
267
+ network: NetworkTypes.Testnet,
268
+ clientOptions: {
269
+ url: 'https://api.testnet.iota.cafe'
270
+ },
271
+ enableCostLogging: true
272
+ };
273
+
274
+ const deploymentConfig: ISmartContractDeployments = {
275
+ testnet: {
276
+ packageId: '0x111',
277
+ deployedPackageId: '0x111',
278
+ migrationStateId: '0x222',
279
+ packageBytecode: 'base64'
280
+ }
281
+ };
282
+
283
+ const client = {} as IotaClient;
284
+ const vaultConnector = {} as IVaultConnector;
285
+ const walletConnector = {} as IWalletConnector;
286
+ const logging = {} as ILoggingComponent;
287
+
288
+ await IotaSmartContractUtils.enableMigration(
289
+ config,
290
+ client,
291
+ vaultConnector,
292
+ walletConnector,
293
+ logging,
294
+ 80_000_000,
295
+ 'controller',
296
+ 'nft',
297
+ '0x111',
298
+ deploymentConfig
299
+ );
300
+
301
+ await IotaSmartContractUtils.disableMigration(
302
+ config,
303
+ client,
304
+ vaultConnector,
305
+ walletConnector,
306
+ logging,
307
+ 80_000_000,
308
+ 'controller',
309
+ 'nft',
310
+ '0x111',
311
+ deploymentConfig
312
+ );
313
+
314
+ console.log(IotaSmartContractUtils.CLASS_NAME); // IotaSmartContractUtils
315
+ ```
316
+
317
+ ```typescript
318
+ import {
319
+ IotaSmartContractUtils,
320
+ NetworkTypes,
321
+ type IIotaConfig,
322
+ type ISmartContractDeployments
323
+ } from '@twin.org/dlt-iota';
324
+ import type { IotaClient } from '@iota/iota-sdk/client';
325
+ import type { ILoggingComponent } from '@twin.org/logging-models';
326
+ import type { IVaultConnector } from '@twin.org/vault-models';
327
+ import type { IWalletConnector } from '@twin.org/wallet-models';
328
+
329
+ const config: IIotaConfig = {
330
+ network: NetworkTypes.Testnet,
331
+ clientOptions: {
332
+ url: 'https://api.testnet.iota.cafe'
333
+ }
334
+ };
335
+
336
+ const deploymentConfig: ISmartContractDeployments = {
337
+ testnet: {
338
+ packageId: '0x111',
339
+ deployedPackageId: '0x111',
340
+ migrationStateId: '0x222',
341
+ packageBytecode: 'base64'
342
+ }
343
+ };
344
+
345
+ const client = {} as IotaClient;
346
+ const vaultConnector = {} as IVaultConnector;
347
+ const walletConnector = {} as IWalletConnector;
348
+ const logging = {} as ILoggingComponent;
349
+
350
+ await IotaSmartContractUtils.migrateSmartContract(
351
+ config,
352
+ client,
353
+ vaultConnector,
354
+ walletConnector,
355
+ logging,
356
+ 90_000_000,
357
+ 'controller',
358
+ '0x999',
359
+ 'nft',
360
+ '0x111',
361
+ deploymentConfig
362
+ );
363
+
364
+ const migrationActive = await IotaSmartContractUtils.isMigrationActive(
365
+ config,
366
+ client,
367
+ 'nft',
368
+ '0x111',
369
+ deploymentConfig,
370
+ 'controller',
371
+ walletConnector
372
+ );
373
+
374
+ console.log(migrationActive); // true
375
+ ```
376
+
377
+ ```typescript
378
+ import { IotaSmartContractUtils, NetworkTypes, type IIotaConfig } from '@twin.org/dlt-iota';
379
+ import type { IotaClient } from '@iota/iota-sdk/client';
380
+ import type { IWalletConnector } from '@twin.org/wallet-models';
381
+
382
+ type ContractObjectFields = {
383
+ fields: {
384
+ version: string;
385
+ };
386
+ };
387
+
388
+ const config: IIotaConfig = {
389
+ network: NetworkTypes.Testnet,
390
+ clientOptions: {
391
+ url: 'https://api.testnet.iota.cafe'
392
+ }
393
+ };
394
+
395
+ const client = {} as IotaClient;
396
+ const walletConnector = {} as IWalletConnector;
397
+
398
+ const currentVersion = await IotaSmartContractUtils.getCurrentContractVersion(
399
+ config,
400
+ client,
401
+ 'nft',
402
+ '0x111',
403
+ 'controller',
404
+ walletConnector
405
+ );
406
+
407
+ const isCompatible = await IotaSmartContractUtils.validateObjectVersion<ContractObjectFields>(
408
+ config,
409
+ client,
410
+ 'nft',
411
+ '0x111',
412
+ 'controller',
413
+ '0x777',
414
+ walletConnector,
415
+ content => Number(content.fields.version)
416
+ );
417
+
418
+ console.log(currentVersion >= 1); // true
419
+ console.log(isCompatible); // true
420
+ ```