@twin.org/wallet-connector-entity-storage 0.0.1-next.10

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.
@@ -0,0 +1,303 @@
1
+ import { property, entity, LogicalOperator, ComparisonOperator, EntitySchemaFactory, EntitySchemaHelper } from '@twin.org/entity';
2
+ import { Guards, Is, Coerce, GeneralError } from '@twin.org/core';
3
+ import { EntityStorageConnectorFactory } from '@twin.org/entity-storage-models';
4
+ import { Bip39, Bip44, KeyType } from '@twin.org/crypto';
5
+ import { VaultConnectorFactory } from '@twin.org/vault-models';
6
+ import { FaucetConnectorFactory } from '@twin.org/wallet-models';
7
+
8
+ // Copyright 2024 IOTA Stiftung.
9
+ // SPDX-License-Identifier: Apache-2.0.
10
+ /**
11
+ * Class describing a wallet address.
12
+ */
13
+ let WalletAddress = class WalletAddress {
14
+ /**
15
+ * The address in the wallet.
16
+ */
17
+ address;
18
+ /**
19
+ * The identity of the owner.
20
+ */
21
+ identity;
22
+ /**
23
+ * The balance of the wallet as bigint.
24
+ */
25
+ balance;
26
+ };
27
+ __decorate([
28
+ property({ type: "string", isPrimary: true }),
29
+ __metadata("design:type", String)
30
+ ], WalletAddress.prototype, "address", undefined);
31
+ __decorate([
32
+ property({ type: "string" }),
33
+ __metadata("design:type", String)
34
+ ], WalletAddress.prototype, "identity", undefined);
35
+ __decorate([
36
+ property({ type: "string" }),
37
+ __metadata("design:type", String)
38
+ ], WalletAddress.prototype, "balance", undefined);
39
+ WalletAddress = __decorate([
40
+ entity()
41
+ ], WalletAddress);
42
+
43
+ // Copyright 2024 IOTA Stiftung.
44
+ // SPDX-License-Identifier: Apache-2.0.
45
+ /**
46
+ * Class for performing faucet operations using entity storage.
47
+ */
48
+ class EntityStorageFaucetConnector {
49
+ /**
50
+ * The namespace supported by the wallet connector.
51
+ */
52
+ static NAMESPACE = "entity-storage";
53
+ /**
54
+ * Runtime name for the class.
55
+ */
56
+ CLASS_NAME = "EntityStorageFaucetConnector";
57
+ /**
58
+ * The entity storage for wallets.
59
+ * @internal
60
+ */
61
+ _walletAddressEntityStorage;
62
+ /**
63
+ * Create a new instance of EntityStorageFaucetConnector.
64
+ * @param options The options for the wallet connector.
65
+ */
66
+ constructor(options) {
67
+ this._walletAddressEntityStorage = EntityStorageConnectorFactory.get(options?.walletAddressEntityStorageType ?? "wallet-address");
68
+ }
69
+ /**
70
+ * Fund the wallet from the faucet.
71
+ * @param identity The identity of the user to access the vault keys.
72
+ * @param address The bech32 encoded address of the address to fund.
73
+ * @param timeoutInSeconds The timeout in seconds to wait for the funding to complete.
74
+ * @returns The amount added to the address by the faucet.
75
+ */
76
+ async fundAddress(identity, address, timeoutInSeconds = 60) {
77
+ Guards.stringValue(this.CLASS_NAME, "identity", identity);
78
+ Guards.stringValue(this.CLASS_NAME, "address", address);
79
+ let walletAddress = await this._walletAddressEntityStorage.get(address);
80
+ if (Is.empty(walletAddress)) {
81
+ walletAddress = {
82
+ balance: "0",
83
+ identity,
84
+ address
85
+ };
86
+ }
87
+ const maxFundAmount = 1000000000n;
88
+ const balance = Coerce.bigint(walletAddress.balance) ?? 0n;
89
+ walletAddress.balance = (balance + maxFundAmount).toString();
90
+ await this._walletAddressEntityStorage.set(walletAddress);
91
+ return maxFundAmount;
92
+ }
93
+ }
94
+
95
+ // Copyright 2024 IOTA Stiftung.
96
+ // SPDX-License-Identifier: Apache-2.0.
97
+ /**
98
+ * Class for performing wallet operations using in-memory storage.
99
+ */
100
+ class EntityStorageWalletConnector {
101
+ /**
102
+ * The namespace supported by the wallet connector.
103
+ */
104
+ static NAMESPACE = "entity-storage";
105
+ /**
106
+ * Default name for the mnemonic secret.
107
+ * @internal
108
+ */
109
+ static _DEFAULT_MNEMONIC_SECRET_NAME = "mnemonic";
110
+ /**
111
+ * Default coin type.
112
+ * @internal
113
+ */
114
+ static _DEFAULT_COIN_TYPE = 9999;
115
+ /**
116
+ * Default bech32 hrp.
117
+ * @internal
118
+ */
119
+ static _DEFAULT_BECH32_HRP = "ent";
120
+ /**
121
+ * Runtime name for the class.
122
+ */
123
+ CLASS_NAME = "EntityStorageWalletConnector";
124
+ /**
125
+ * The vault for the mnemonic.
126
+ * @internal
127
+ */
128
+ _vaultConnector;
129
+ /**
130
+ * The faucet.
131
+ * @internal
132
+ */
133
+ _faucetConnector;
134
+ /**
135
+ * The entity storage for wallets.
136
+ * @internal
137
+ */
138
+ _walletAddressEntityStorage;
139
+ /**
140
+ * The configuration to use for tangle operations.
141
+ * @internal
142
+ */
143
+ _config;
144
+ /**
145
+ * Create a new instance of EntityStorageWalletConnector.
146
+ * @param options The options for the wallet connector.
147
+ */
148
+ constructor(options) {
149
+ this._vaultConnector = VaultConnectorFactory.get(options?.vaultConnectorType ?? "vault");
150
+ this._faucetConnector = FaucetConnectorFactory.getIfExists(options?.faucetConnectorType ?? "faucet");
151
+ this._walletAddressEntityStorage = EntityStorageConnectorFactory.get(options?.walletAddressEntityStorageType ?? "wallet-address");
152
+ this._config = options?.config ?? {};
153
+ this._config.coinType ??= EntityStorageWalletConnector._DEFAULT_COIN_TYPE;
154
+ this._config.bech32Hrp ??= EntityStorageWalletConnector._DEFAULT_BECH32_HRP;
155
+ }
156
+ /**
157
+ * Create a new wallet.
158
+ * @param identity The identity of the user to access the vault keys.
159
+ * @returns Nothing.
160
+ */
161
+ async create(identity) {
162
+ Guards.stringValue(this.CLASS_NAME, "identity", identity);
163
+ const mnemonic = Bip39.randomMnemonic();
164
+ await this._vaultConnector.setSecret(this.buildMnemonicKey(identity), mnemonic);
165
+ }
166
+ /**
167
+ * Get the addresses for the requested range.
168
+ * @param identity The identity of the user to access the vault keys.
169
+ * @param accountIndex The account index to get the addresses for.
170
+ * @param startAddressIndex The start index for the addresses.
171
+ * @param count The number of addresses to generate.
172
+ * @returns The list of addresses.
173
+ */
174
+ async getAddresses(identity, accountIndex, startAddressIndex, count) {
175
+ Guards.stringValue(this.CLASS_NAME, "identity", identity);
176
+ Guards.integer(this.CLASS_NAME, "startAddressIndex", startAddressIndex);
177
+ Guards.integer(this.CLASS_NAME, "count", count);
178
+ const mnemonic = await this._vaultConnector.getSecret(this.buildMnemonicKey(identity));
179
+ const seed = Bip39.mnemonicToSeed(mnemonic);
180
+ const keyPairs = [];
181
+ for (let i = startAddressIndex; i < startAddressIndex + count; i++) {
182
+ const addressKeyPair = Bip44.addressBech32(seed, KeyType.Ed25519, this._config.bech32Hrp ?? EntityStorageWalletConnector._DEFAULT_BECH32_HRP, this._config.coinType ?? EntityStorageWalletConnector._DEFAULT_COIN_TYPE, accountIndex, false, i);
183
+ keyPairs.push(addressKeyPair.address);
184
+ }
185
+ return keyPairs;
186
+ }
187
+ /**
188
+ * Get the balance for an address in a wallet.
189
+ * @param identity The identity of the user to access the vault keys.
190
+ * @param address The bech32 encoded address.
191
+ * @returns The balance of the wallet address.
192
+ */
193
+ async getBalance(identity, address) {
194
+ Guards.stringValue(this.CLASS_NAME, "address", address);
195
+ const walletAddress = await this._walletAddressEntityStorage.get(address);
196
+ return Coerce.bigint(walletAddress?.balance) ?? 0n;
197
+ }
198
+ /**
199
+ * Ensure the balance for an address in a wallet.
200
+ * @param identity The identity of the user to access the vault keys.
201
+ * @param address The bech32 encoded address.
202
+ * @param ensureBalance The balance to ensure on the address.
203
+ * @param timeoutInSeconds The timeout in seconds to wait for the funding to complete.
204
+ * @returns True if the balance has been ensured.
205
+ */
206
+ async ensureBalance(identity, address, ensureBalance, timeoutInSeconds) {
207
+ Guards.stringValue(this.CLASS_NAME, "identity", identity);
208
+ Guards.stringValue(this.CLASS_NAME, "address", address);
209
+ Guards.bigint(this.CLASS_NAME, "ensureBalance", ensureBalance);
210
+ if (this._faucetConnector) {
211
+ let retryCount = 10;
212
+ let currentBalance = await this.getBalance(identity, address);
213
+ while (currentBalance < ensureBalance && retryCount > 0) {
214
+ const addedBalance = await this._faucetConnector.fundAddress(identity, address, timeoutInSeconds);
215
+ if (addedBalance === 0n) {
216
+ // The balance has not increased, so return.
217
+ return false;
218
+ }
219
+ currentBalance += addedBalance;
220
+ if (currentBalance < ensureBalance) {
221
+ // The balance has increased but is still not enough, wait and try again.
222
+ await new Promise(resolve => setTimeout(resolve, 100));
223
+ retryCount--;
224
+ }
225
+ }
226
+ return currentBalance >= ensureBalance;
227
+ }
228
+ return false;
229
+ }
230
+ /**
231
+ * Transfer funds to an address.
232
+ * @param identity The identity of the user to access the vault keys.
233
+ * @param addressSource The bech32 encoded address to send the funds from.
234
+ * @param addressDest The bech32 encoded address to send the funds to.
235
+ * @param amount The amount to transfer.
236
+ * @returns An identifier for the transfer if there was one.
237
+ */
238
+ async transfer(identity, addressSource, addressDest, amount) {
239
+ Guards.stringValue(this.CLASS_NAME, "identity", identity);
240
+ Guards.stringValue(this.CLASS_NAME, "addressSource", addressSource);
241
+ Guards.stringValue(this.CLASS_NAME, "addressDest", addressDest);
242
+ Guards.bigint(this.CLASS_NAME, "amount", amount);
243
+ const walletAddresses = await this._walletAddressEntityStorage.query({
244
+ logicalOperator: LogicalOperator.And,
245
+ conditions: [
246
+ {
247
+ property: "identity",
248
+ comparison: ComparisonOperator.Equals,
249
+ value: identity
250
+ },
251
+ {
252
+ property: "address",
253
+ comparison: ComparisonOperator.Equals,
254
+ value: addressSource
255
+ }
256
+ ]
257
+ });
258
+ let walletAddress;
259
+ let balance = 0n;
260
+ if (walletAddresses.entities.length > 0) {
261
+ walletAddress = walletAddresses.entities[0];
262
+ balance = BigInt(walletAddress.balance);
263
+ walletAddress.balance = (BigInt(walletAddress.balance) - amount).toString();
264
+ }
265
+ if (balance < amount) {
266
+ throw new GeneralError(this.CLASS_NAME, "insufficientFunds");
267
+ }
268
+ if (!Is.empty(walletAddress)) {
269
+ await this._walletAddressEntityStorage.set(walletAddress);
270
+ let destWalletAddress = await this._walletAddressEntityStorage.get(addressDest);
271
+ if (Is.empty(destWalletAddress)) {
272
+ destWalletAddress = {
273
+ balance: "0",
274
+ identity: "",
275
+ address: addressDest
276
+ };
277
+ }
278
+ destWalletAddress.balance = (BigInt(destWalletAddress.balance) + amount).toString();
279
+ await this._walletAddressEntityStorage.set(destWalletAddress);
280
+ }
281
+ return undefined;
282
+ }
283
+ /**
284
+ * Build the key name to access the mnemonic in the vault.
285
+ * @param identity The identity of the user to access the vault keys.
286
+ * @returns The vault key.
287
+ * @internal
288
+ */
289
+ buildMnemonicKey(identity) {
290
+ return `${identity}/${this._config.vaultMnemonicId ?? EntityStorageWalletConnector._DEFAULT_MNEMONIC_SECRET_NAME}`;
291
+ }
292
+ }
293
+
294
+ // Copyright 2024 IOTA Stiftung.
295
+ // SPDX-License-Identifier: Apache-2.0.
296
+ /**
297
+ * Initialize the schema for the wallet entity storage connector.
298
+ */
299
+ function initSchema() {
300
+ EntitySchemaFactory.register("WalletAddress", () => EntitySchemaHelper.getSchema(WalletAddress));
301
+ }
302
+
303
+ export { EntityStorageFaucetConnector, EntityStorageWalletConnector, WalletAddress, initSchema };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Class describing a wallet address.
3
+ */
4
+ export declare class WalletAddress {
5
+ /**
6
+ * The address in the wallet.
7
+ */
8
+ address: string;
9
+ /**
10
+ * The identity of the owner.
11
+ */
12
+ identity: string;
13
+ /**
14
+ * The balance of the wallet as bigint.
15
+ */
16
+ balance: string;
17
+ }
@@ -0,0 +1,28 @@
1
+ import type { IFaucetConnector } from "@twin.org/wallet-models";
2
+ import type { IEntityStorageFaucetConnectorConstructorOptions } from "./models/IEntityStorageFaucetConnectorConstructorOptions";
3
+ /**
4
+ * Class for performing faucet operations using entity storage.
5
+ */
6
+ export declare class EntityStorageFaucetConnector implements IFaucetConnector {
7
+ /**
8
+ * The namespace supported by the wallet connector.
9
+ */
10
+ static readonly NAMESPACE: string;
11
+ /**
12
+ * Runtime name for the class.
13
+ */
14
+ readonly CLASS_NAME: string;
15
+ /**
16
+ * Create a new instance of EntityStorageFaucetConnector.
17
+ * @param options The options for the wallet connector.
18
+ */
19
+ constructor(options?: IEntityStorageFaucetConnectorConstructorOptions);
20
+ /**
21
+ * Fund the wallet from the faucet.
22
+ * @param identity The identity of the user to access the vault keys.
23
+ * @param address The bech32 encoded address of the address to fund.
24
+ * @param timeoutInSeconds The timeout in seconds to wait for the funding to complete.
25
+ * @returns The amount added to the address by the faucet.
26
+ */
27
+ fundAddress(identity: string, address: string, timeoutInSeconds?: number): Promise<bigint>;
28
+ }
@@ -0,0 +1,60 @@
1
+ import { type IWalletConnector } from "@twin.org/wallet-models";
2
+ import type { IEntityStorageWalletConnectorConstructorOptions } from "./models/IEntityStorageWalletConnectorConstructorOptions";
3
+ /**
4
+ * Class for performing wallet operations using in-memory storage.
5
+ */
6
+ export declare class EntityStorageWalletConnector implements IWalletConnector {
7
+ /**
8
+ * The namespace supported by the wallet connector.
9
+ */
10
+ static readonly NAMESPACE: string;
11
+ /**
12
+ * Runtime name for the class.
13
+ */
14
+ readonly CLASS_NAME: string;
15
+ /**
16
+ * Create a new instance of EntityStorageWalletConnector.
17
+ * @param options The options for the wallet connector.
18
+ */
19
+ constructor(options?: IEntityStorageWalletConnectorConstructorOptions);
20
+ /**
21
+ * Create a new wallet.
22
+ * @param identity The identity of the user to access the vault keys.
23
+ * @returns Nothing.
24
+ */
25
+ create(identity: string): Promise<void>;
26
+ /**
27
+ * Get the addresses for the requested range.
28
+ * @param identity The identity of the user to access the vault keys.
29
+ * @param accountIndex The account index to get the addresses for.
30
+ * @param startAddressIndex The start index for the addresses.
31
+ * @param count The number of addresses to generate.
32
+ * @returns The list of addresses.
33
+ */
34
+ getAddresses(identity: string, accountIndex: number, startAddressIndex: number, count: number): Promise<string[]>;
35
+ /**
36
+ * Get the balance for an address in a wallet.
37
+ * @param identity The identity of the user to access the vault keys.
38
+ * @param address The bech32 encoded address.
39
+ * @returns The balance of the wallet address.
40
+ */
41
+ getBalance(identity: string, address: string): Promise<bigint>;
42
+ /**
43
+ * Ensure the balance for an address in a wallet.
44
+ * @param identity The identity of the user to access the vault keys.
45
+ * @param address The bech32 encoded address.
46
+ * @param ensureBalance The balance to ensure on the address.
47
+ * @param timeoutInSeconds The timeout in seconds to wait for the funding to complete.
48
+ * @returns True if the balance has been ensured.
49
+ */
50
+ ensureBalance(identity: string, address: string, ensureBalance: bigint, timeoutInSeconds?: number): Promise<boolean>;
51
+ /**
52
+ * Transfer funds to an address.
53
+ * @param identity The identity of the user to access the vault keys.
54
+ * @param addressSource The bech32 encoded address to send the funds from.
55
+ * @param addressDest The bech32 encoded address to send the funds to.
56
+ * @param amount The amount to transfer.
57
+ * @returns An identifier for the transfer if there was one.
58
+ */
59
+ transfer(identity: string, addressSource: string, addressDest: string, amount: bigint): Promise<string | undefined>;
60
+ }
@@ -0,0 +1,7 @@
1
+ export * from "./entities/walletAddress";
2
+ export * from "./entityStorageFaucetConnector";
3
+ export * from "./entityStorageWalletConnector";
4
+ export * from "./models/IEntityStorageFaucetConnectorConstructorOptions";
5
+ export * from "./models/IEntityStorageWalletConnectorConfig";
6
+ export * from "./models/IEntityStorageWalletConnectorConstructorOptions";
7
+ export * from "./schema";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Options for the entity storage faucet connector.
3
+ */
4
+ export interface IEntityStorageFaucetConnectorConstructorOptions {
5
+ /**
6
+ * The entity storage type for wallet addresses.
7
+ * @default wallet-address
8
+ */
9
+ walletAddressEntityStorageType?: string;
10
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Configuration for the Entity Storage Wallet Connector.
3
+ */
4
+ export interface IEntityStorageWalletConnectorConfig {
5
+ /**
6
+ * The id of the entry in the vault containing the mnemonic.
7
+ * @default mnemonic
8
+ */
9
+ vaultMnemonicId?: string;
10
+ /**
11
+ * The coin type.
12
+ * @default 9999
13
+ */
14
+ coinType?: number;
15
+ /**
16
+ * The bech32 human readable part for the addresses.
17
+ * @default ent
18
+ */
19
+ bech32Hrp?: string;
20
+ }
@@ -0,0 +1,25 @@
1
+ import type { IEntityStorageWalletConnectorConfig } from "./IEntityStorageWalletConnectorConfig";
2
+ /**
3
+ * Options for the entity storage wallet connector.
4
+ */
5
+ export interface IEntityStorageWalletConnectorConstructorOptions {
6
+ /**
7
+ * Vault connector to use for wallet secrets.
8
+ * @default vault
9
+ */
10
+ vaultConnectorType?: string;
11
+ /**
12
+ * Optional faucet for requesting funds.
13
+ * @default faucet
14
+ */
15
+ faucetConnectorType?: string;
16
+ /**
17
+ * The entity storage for wallets.
18
+ * @default wallet-address
19
+ */
20
+ walletAddressEntityStorageType?: string;
21
+ /**
22
+ * The configuration for the wallet connector.
23
+ */
24
+ config?: IEntityStorageWalletConnectorConfig;
25
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Initialize the schema for the wallet entity storage connector.
3
+ */
4
+ export declare function initSchema(): void;
@@ -0,0 +1,5 @@
1
+ # @twin.org/wallet-connector-entity-storage - Changelog
2
+
3
+ ## v0.0.1-next.10
4
+
5
+ - Initial Release
@@ -0,0 +1 @@
1
+ # @twin.org/wallet-connector-entity-storage - Examples
@@ -0,0 +1,85 @@
1
+ # Class: EntityStorageFaucetConnector
2
+
3
+ Class for performing faucet operations using entity storage.
4
+
5
+ ## Implements
6
+
7
+ - `IFaucetConnector`
8
+
9
+ ## Constructors
10
+
11
+ ### new EntityStorageFaucetConnector()
12
+
13
+ > **new EntityStorageFaucetConnector**(`options`?): [`EntityStorageFaucetConnector`](EntityStorageFaucetConnector.md)
14
+
15
+ Create a new instance of EntityStorageFaucetConnector.
16
+
17
+ #### Parameters
18
+
19
+ ##### options?
20
+
21
+ [`IEntityStorageFaucetConnectorConstructorOptions`](../interfaces/IEntityStorageFaucetConnectorConstructorOptions.md)
22
+
23
+ The options for the wallet connector.
24
+
25
+ #### Returns
26
+
27
+ [`EntityStorageFaucetConnector`](EntityStorageFaucetConnector.md)
28
+
29
+ ## Properties
30
+
31
+ ### NAMESPACE
32
+
33
+ > `readonly` `static` **NAMESPACE**: `string` = `"entity-storage"`
34
+
35
+ The namespace supported by the wallet connector.
36
+
37
+ ***
38
+
39
+ ### CLASS\_NAME
40
+
41
+ > `readonly` **CLASS\_NAME**: `string`
42
+
43
+ Runtime name for the class.
44
+
45
+ #### Implementation of
46
+
47
+ `IFaucetConnector.CLASS_NAME`
48
+
49
+ ## Methods
50
+
51
+ ### fundAddress()
52
+
53
+ > **fundAddress**(`identity`, `address`, `timeoutInSeconds`): `Promise`\<`bigint`\>
54
+
55
+ Fund the wallet from the faucet.
56
+
57
+ #### Parameters
58
+
59
+ ##### identity
60
+
61
+ `string`
62
+
63
+ The identity of the user to access the vault keys.
64
+
65
+ ##### address
66
+
67
+ `string`
68
+
69
+ The bech32 encoded address of the address to fund.
70
+
71
+ ##### timeoutInSeconds
72
+
73
+ `number` = `60`
74
+
75
+ The timeout in seconds to wait for the funding to complete.
76
+
77
+ #### Returns
78
+
79
+ `Promise`\<`bigint`\>
80
+
81
+ The amount added to the address by the faucet.
82
+
83
+ #### Implementation of
84
+
85
+ `IFaucetConnector.fundAddress`