@fizzyflow/endless-vector 0.0.2 → 0.0.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/EndlessVector.js CHANGED
@@ -8,6 +8,7 @@ import ids from './ids.js';
8
8
  * @typedef {import('@mysten/sui/client').SuiClient} SuiClient
9
9
  * @typedef {import('@mysten/sui/client').GetObjectParams} GetObjectParams
10
10
  * @typedef {import('@mysten/sui/client').GetDynamicFieldsParams} GetDynamicFieldsParams
11
+ * @typedef {import('@mysten/sui/transactions').TransactionResult} TransactionResult
11
12
  */
12
13
 
13
14
  /**
@@ -94,7 +95,7 @@ export default class EndlessVector {
94
95
  * @param {SuiClient} params.suiClient - Sui client instance for blockchain interactions
95
96
  * @param {string} params.packageId - ID of the Move package containing the EndlessVector module
96
97
  * @param {CustomSignAndExecuteTransactionFunction} params.signAndExecuteTransaction - Function to sign and execute transactions
97
- * @param {?Array<Uint8Array>} [params.items] - Optional array of Uint8Array items to initialize the vector with. If provided, uses empty_entry_and_push, otherwise uses empty_entry
98
+ * @param {?Uint8Array} [params.array] - Optional Uint8Array to initialize the vector with as the first item to get with .at(0)
98
99
  * @param {?Object} [params.gasCoin] - Optional gas coin object reference {objectId: string, digest: string, version: string} to use for transaction payment
99
100
  * @param {?Object} [params.options] - Optional transaction parameters
100
101
  * @param {?Number} [params.options.timeout] - Transaction confirmation timeout in ms, default 30000
@@ -103,12 +104,19 @@ export default class EndlessVector {
103
104
  * @throws {Error} If the transaction fails or no EndlessVector object is created
104
105
  */
105
106
  static async create(params) {
106
- const { suiClient, packageId, signAndExecuteTransaction, items, gasCoin, options = {} } = params;
107
+ const { suiClient, packageId, signAndExecuteTransaction, array, gasCoin, options = {} } = params;
108
+
109
+ let normalizedPackageId = packageId;
110
+ if (normalizedPackageId == 'mainnet' || (!normalizedPackageId && suiClient?.network == 'mainnet')) {
111
+ normalizedPackageId = ids['mainnet'].packageId;
112
+ } else if (normalizedPackageId == 'testnet' || (!normalizedPackageId && suiClient?.network == 'testnet')) {
113
+ normalizedPackageId = ids['testnet'].packageId;
114
+ }
107
115
 
108
116
  if (!suiClient) {
109
117
  throw new Error('suiClient is required');
110
118
  }
111
- if (!packageId) {
119
+ if (!normalizedPackageId) {
112
120
  throw new Error('packageId is required');
113
121
  }
114
122
  if (!signAndExecuteTransaction) {
@@ -122,14 +130,17 @@ export default class EndlessVector {
122
130
  tx.setGasPayment([gasCoin]);
123
131
  }
124
132
 
125
- if (items && Array.isArray(items) && items.length) {
133
+ if (array && array.length) {
134
+ const vectorInput = await EndlessVector.getCreateTransactionAndReturnVectorInput({
135
+ packageId: normalizedPackageId,
136
+ }, array, tx);
126
137
  tx.moveCall({
127
- target: `${packageId}::endless_vector::empty_entry_and_push`,
128
- arguments: [tx.pure(bcs.vector(bcs.vector(bcs.u8())).serialize(items))],
138
+ target: `${normalizedPackageId}::endless_vector::transfer_to_sender`,
139
+ arguments: [vectorInput],
129
140
  });
130
141
  } else {
131
142
  tx.moveCall({
132
- target: `${packageId}::endless_vector::empty_entry`,
143
+ target: `${normalizedPackageId}::endless_vector::empty_entry`,
133
144
  arguments: [],
134
145
  });
135
146
  }
@@ -168,38 +179,97 @@ export default class EndlessVector {
168
179
  return new EndlessVector({
169
180
  suiClient,
170
181
  id: createdVector.objectId,
171
- packageId,
182
+ packageId: normalizedPackageId,
172
183
  signAndExecuteTransaction,
173
184
  });
174
185
  }
175
186
 
176
- get isWritable() {
177
- return !!(this._packageId && this._signAndExecuteTransaction);
178
- }
179
-
180
187
  /**
181
- * Creates a transaction to push new byte arrays to the EndlessVector.
182
- * Note: this method only creates the transaction, it does not sign or execute it.
188
+ * Creates an empty EndlessVector and returns the vector input reference.
189
+ * Appends vector creation to existing transaction or makes new one.
190
+ *
191
+ * Returns the vector input reference for use in subsequent move calls as argument or to be transferred.
192
+ *
193
+ * @param {Object} params - Configuration parameters
194
+ * @param {string} params.packageId - The package ID ('mainnet', 'testnet', or explicit package ID)
195
+ * @param {Uint8Array|null} [arr=null] - Optional Uint8Array to push back to the new vector as the first item
196
+ * @param {Transaction|null} [txToAppendTo=null] - Optional existing transaction to append the move calls to
197
+ * @returns {Promise<TransactionResult>} Vector input reference for use in subsequent move calls
198
+ * @throws {Error} Throws if packageId is not provided or invalid
199
+ *
200
+ * @example
201
+ * // Create an empty vector
202
+ * const vectorInput = await EndlessVector.getCreateTransactionAndReturnVectorInput({
203
+ * packageId: 'mainnet'
204
+ * });
205
+ *
206
+ * @example
207
+ * // Create and populate a vector within an existing transaction
208
+ * const data = new Uint8Array([1, 2, 3, 4]);
209
+ * const tx = new Transaction();
210
+ * const vectorInput = await EndlessVector.getCreateTransactionAndReturnVectorInput({
211
+ * packageId: contract.id
212
+ * }, data, tx);
213
+ */
214
+ static async getCreateTransactionAndReturnVectorInput(params, arr = null, txToAppendTo = null) {
215
+ const { packageId } = params;
216
+ let normalizedPackageId = packageId;
217
+ if (normalizedPackageId == 'mainnet') {
218
+ normalizedPackageId = ids['mainnet'].packageId;
219
+ } else if (normalizedPackageId == 'testnet') {
220
+ normalizedPackageId = ids['testnet'].packageId;
221
+ }
183
222
 
184
- * @param {Uint8Array} arr - Array of byte arrays to push
185
- * @returns {Transaction} The transaction object to be signed and executed
186
- */
187
- getPushTransaction(arr, txToAppendTo = null) {
188
- if (!this._packageId) {
189
- throw new Error('packageId is required to compose push transaction');
223
+ if (!normalizedPackageId) {
224
+ throw new Error('packageId is required');
190
225
  }
226
+ // Create transaction to call empty_entry
191
227
 
192
228
  let tx = txToAppendTo;
193
229
  if (!tx) {
194
230
  tx = new Transaction();
195
231
  }
196
232
 
233
+ const vectorInput = tx.moveCall({
234
+ target: `${normalizedPackageId}::endless_vector::empty`,
235
+ arguments: [],
236
+ });
237
+
238
+ if (arr && arr) {
239
+ EndlessVector.composePushTransaction(normalizedPackageId, vectorInput, arr, tx);
240
+ }
241
+
242
+ return vectorInput;
243
+ }
244
+
245
+ get isWritable() {
246
+ return !!(this._packageId && this._signAndExecuteTransaction);
247
+ }
248
+
249
+ /**
250
+ * Attach move calls to transaction, to push item into endlessvector, handling large arrays by chunking them.
251
+ * This static method can be used to compose transactions for any existing EndlessVector instance:
252
+ * tx.object(vector.id)
253
+ * or newly created one, accepting TransactionResult as vectorInput, see: getCreateTransactionAndReturnVectorInput
254
+ *
255
+ * For arrays smaller than 12KB, it uses a single push_back call.
256
+ * For arrays between 12KB and 120KB, it splits the data into 10 chunks and uses compose_and_push_back.
257
+ *
258
+ * @static
259
+ * @param {string} packageId - The package ID of the Move module containing the endless_vector functions
260
+ * @param {TransactionObjectArgument} vectorInput - The transaction object argument representing the EndlessVector
261
+ * @param {Uint8Array} arr - The byte array to push to the vector
262
+ * @param {Transaction} tx - The transaction object to append the move calls to
263
+ * @returns {Transaction} The transaction object with the push operations added
264
+ * @throws {Error} If the array is larger than 120KB (10 * 12KB)
265
+ */
266
+ static composePushTransaction(packageId, vectorInput, arr, tx) {
197
267
  const maxArgLength = 12 * 1024;
198
268
  if (arr.length < maxArgLength) {
199
269
  tx.moveCall({
200
- target: `${this._packageId}::endless_vector::push_back`,
270
+ target: `${packageId}::endless_vector::push_back`,
201
271
  arguments: [
202
- tx.object(this.id),
272
+ vectorInput,
203
273
  tx.pure(bcs.vector(bcs.u8()).serialize(arr)),
204
274
  ],
205
275
  });
@@ -216,17 +286,41 @@ export default class EndlessVector {
216
286
  chunks.push(new Uint8Array()); // empty chunk
217
287
  }
218
288
  }
219
- const args = [tx.object(this.id)];
289
+ const args = [vectorInput];
220
290
  for (let i = 0; i < N; i++) {
221
291
  args.push(tx.pure(bcs.vector(bcs.u8()).serialize(chunks[i])));
222
292
  }
223
293
  tx.moveCall({
224
- target: `${this._packageId}::endless_vector::compose_and_push_back`,
294
+ target: `${packageId}::endless_vector::compose_and_push_back`,
225
295
  arguments: args,
226
296
  });
227
297
  } else {
228
298
  throw new Error('Array too large, max '+(10*maxArgLength)+' bytes supported per single tx');
229
299
  }
300
+
301
+ return tx;
302
+ }
303
+
304
+
305
+ /**
306
+ * Creates a transaction to push new byte arrays to the EndlessVector.
307
+ * Note: this method only creates the transaction, it does not sign or execute it.
308
+
309
+ * @param {Uint8Array} arr - Uint8Array to push
310
+ * @returns {Transaction} The transaction object to be signed and executed
311
+ */
312
+ getPushTransaction(arr, txToAppendTo = null) {
313
+ if (!this._packageId) {
314
+ throw new Error('packageId is required to compose push transaction');
315
+ }
316
+
317
+ let tx = txToAppendTo;
318
+ if (!tx) {
319
+ tx = new Transaction();
320
+ }
321
+
322
+ EndlessVector.composePushTransaction(this._packageId, tx.object(this.id), arr, tx);
323
+
230
324
  return tx;
231
325
  }
232
326
 
package/README.md CHANGED
@@ -32,10 +32,7 @@ const vector = await EndlessVector.create({
32
32
  const vectorWithData = await EndlessVector.create({
33
33
  suiClient: client,
34
34
  packageId: 'testnet', // or 'mainnet' or '0xYOUR_PACKAGE_ID'
35
- items: [
36
- new Uint8Array([1, 2, 3]),
37
- new Uint8Array([4, 5, 6])
38
- ],
35
+ array: new Uint8Array([1, 2, 3]), // [0] to append to EndlessVector
39
36
  signAndExecuteTransaction: async (tx) => {
40
37
  const result = await wallet.signAndExecuteTransaction({ transaction: tx });
41
38
  return result.digest;
@@ -57,7 +54,7 @@ console.log('Total items:', vector.length);
57
54
  console.log('Total size:', vector.binaryLength, 'bytes');
58
55
 
59
56
  // Read items
60
- const firstItem = await vector.at(0);
57
+ const firstItem = await vector.at(0); // Uint8Array
61
58
  ```
62
59
 
63
60
  ## API Reference
@@ -72,7 +69,7 @@ Creates a new EndlessVector on the blockchain.
72
69
  - `suiClient` (SuiClient) - Sui client instance for blockchain interactions
73
70
  - `packageId` (string) - 'testnet', 'mainnet', or ID of the Move package containing the EndlessVector module
74
71
  - `signAndExecuteTransaction` (function) - Function to sign and execute transactions
75
- - `items` (Array<Uint8Array>, optional) - Initial items to push to the vector
72
+ - `array` (Uint8Array, optional) - Optional first vector<u8> to push back to the new vector
76
73
  - `gasCoin` (Object, optional) - Gas coin object reference `{objectId: string, digest: string, version: string}` for transaction payment
77
74
  - `options` (Object, optional) - Additional options:
78
75
  - `timeout` (number) - Transaction confirmation timeout in ms (default: 30000)
@@ -85,7 +82,7 @@ Creates a new EndlessVector on the blockchain.
85
82
  const vector = await EndlessVector.create({
86
83
  suiClient: client,
87
84
  packageId: 'testnet', // or 'mainnet' or '0xPACKAGE_ID'
88
- items: [new Uint8Array([1, 2, 3])],
85
+ array: new Uint8Array([1, 2, 3]),
89
86
  gasCoin: {
90
87
  objectId: '0xGAS_COIN_ID',
91
88
  digest: 'DIGEST',
@@ -276,7 +273,7 @@ const vectors = await Promise.all(
276
273
  EndlessVector.create({
277
274
  suiClient: client,
278
275
  packageId: 'testnet', // or 'mainnet' or '0xPACKAGE_ID'
279
- items: items,
276
+ array: items,
280
277
  gasCoin: gasCoinRefs[i],
281
278
  signAndExecuteTransaction: async (tx) => {
282
279
  const result = await wallet.signAndExecuteTransaction({ transaction: tx });
package/ids.js CHANGED
@@ -1,11 +1,11 @@
1
1
 
2
2
  export default {
3
3
  'testnet': {
4
- packageId: '0x5e35ef9e8ea315a4db40047b9133c028603168e7b41a27511e9f7eea38313685', // should be the same as in Move.lock
5
- originalPackageId: '0x5e35ef9e8ea315a4db40047b9133c028603168e7b41a27511e9f7eea38313685',
4
+ packageId: '0x30bf8e91f40774ed08234f767360b4154562232903928b5f2f8fe5b2d51655bc', // should be the same as in Move.lock
5
+ originalPackageId: '0x30bf8e91f40774ed08234f767360b4154562232903928b5f2f8fe5b2d51655bc',
6
6
  },
7
7
  'mainnet': {
8
- packageId: '0xa81971816c632022658736d464d35bd47a7d6b5ad60e0903697e4adda0aa1331', // should be the same as in Move.lock
9
- originalPackageId: '0xa81971816c632022658736d464d35bd47a7d6b5ad60e0903697e4adda0aa1331',
8
+ packageId: '0x94cf19d164f014cfe73dda1121f27b54b69f0dbfa89d8b6ed48ae8a9144f5d65', // should be the same as in Move.lock
9
+ originalPackageId: '0x94cf19d164f014cfe73dda1121f27b54b69f0dbfa89d8b6ed48ae8a9144f5d65',
10
10
  },
11
11
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fizzyflow/endless-vector",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -24,7 +24,7 @@
24
24
  "author": "suidouble (https://github.com/suidouble)",
25
25
  "license": "Apache-2.0",
26
26
  "scripts": {
27
- "test": "tap -j1 -t240 ./test/*.test.js"
27
+ "test": "tap -j1 -t320 ./test/*.test.js"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "@mysten/sui": "^1.44.0"
package/test/base.test.js CHANGED
@@ -71,6 +71,50 @@ test('attach a local package', async t => {
71
71
  });
72
72
 
73
73
 
74
+ test('test raw create transaction', async t => {
75
+ const arr = randomBytesOfLength(120 * 1024); // 100KB
76
+
77
+ const tx = new suiMaster.Transaction();
78
+ const vectorTxInput = await EndlessVector.getCreateTransactionAndReturnVectorInput({
79
+ packageId: contract.id,
80
+ }, arr, tx);
81
+ tx.transferObjects([vectorTxInput], tx.pure.address(suiMaster.address));
82
+
83
+ t.ok(tx);
84
+ let digest = null;
85
+ try {
86
+ digest = await signAndExecuteTransaction(tx);
87
+ t.ok(digest);
88
+ } catch (e) {
89
+ console.error('Error preparing transaction:', e);
90
+ }
91
+
92
+ const transactionBlockResponse = await suiMaster.client.waitForTransaction({
93
+ digest: digest,
94
+ options: { showObjectChanges: true, },
95
+ });
96
+
97
+ // Find the created EndlessVector object
98
+ const objectChanges = transactionBlockResponse.objectChanges || [];
99
+ const createdVector = objectChanges.find(
100
+ change => change.type === 'created' &&
101
+ change.objectType &&
102
+ change.objectType.includes('endless_vector::EndlessVector')
103
+ );
104
+
105
+ const createdVectorId = createdVector ? createdVector.objectId : null;
106
+ t.ok(createdVectorId, 'Created EndlessVector object should have an ID');
107
+
108
+ const loadBack = new EndlessVector({
109
+ suiClient: suiMaster.client,
110
+ id: createdVectorId,
111
+ });
112
+ await loadBack.initialize();
113
+
114
+ t.ok(loadBack.length === 1, `Loaded vector should have length 1, got ${loadBack.length}`);
115
+ const getBack = await loadBack.at(0);
116
+ t.ok(equalUint8Arrays(getBack, arr), 'Data retrieved from loaded vector should match original data');
117
+ });
74
118
 
75
119
 
76
120
  test('make a test EndlessVector and push single Uint8Array to it', async t => {
@@ -358,7 +402,7 @@ test('test parallel creation, push, and append of 6 vectors', async t => {
358
402
  // Prepare test data for each vector
359
403
  const testData = [];
360
404
  for (let i = 0; i < vectorCount; i++) {
361
- testData.push([randomBytesOfLength(1024), new Uint8Array([0, 1, i])]);
405
+ testData.push(randomBytesOfLength(1024));
362
406
  }
363
407
 
364
408
  // Create N EndlessVectors in parallel using static create method
@@ -368,7 +412,7 @@ test('test parallel creation, push, and append of 6 vectors', async t => {
368
412
  EndlessVector.create({
369
413
  suiClient: suiMaster.client,
370
414
  packageId: contract.id,
371
- items: testData[i] ? testData[i] : [],
415
+ array: testData[i] ? testData[i] : null,
372
416
  gasCoin: gasCoinInputs[i],
373
417
  signAndExecuteTransaction: async (tx) => {
374
418
  console.log(`Creating vector ${i}...`);
@@ -407,24 +451,20 @@ test('test parallel creation, push, and append of 6 vectors', async t => {
407
451
  // Verify the data after concatenation
408
452
  await mainVector.initialize();
409
453
 
410
- // Each vector has 2 items, so total should be vectorCount * 2
411
- const expectedLength = vectorCount * 2;
454
+ const expectedLength = vectorCount;
412
455
  t.ok(mainVector.length === expectedLength, `mainVector should have ${expectedLength} items, got ${mainVector.length}`);
413
456
 
414
457
  console.log(`Verifying ${expectedLength} items in concatenated vector...`);
415
458
 
416
459
  // Verify each item matches the original testData
417
460
  for (let vectorIdx = 0; vectorIdx < vectorCount; vectorIdx++) {
418
- for (let itemIdx = 0; itemIdx < testData[vectorIdx].length; itemIdx++) {
419
- const globalIdx = vectorIdx * testData[vectorIdx].length + itemIdx;
420
- const retrieved = await mainVector.at(globalIdx);
421
- const expected = testData[vectorIdx][itemIdx];
422
-
423
- t.ok(
424
- equalUint8Arrays(retrieved, expected),
425
- `Item ${globalIdx} (vector ${vectorIdx}, item ${itemIdx}) should match`
426
- );
427
- }
461
+ const expected = testData[vectorIdx];
462
+ const retrieved = await mainVector.at(vectorIdx);
463
+
464
+ t.ok(
465
+ equalUint8Arrays(retrieved, expected),
466
+ `Item ${vectorIdx} (vector ${vectorIdx}, item ${vectorIdx}) should match`
467
+ );
428
468
  }
429
469
  });
430
470