@fizzyflow/endless-vector 0.0.3 → 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,7 +104,7 @@ 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;
107
108
 
108
109
  let normalizedPackageId = packageId;
109
110
  if (normalizedPackageId == 'mainnet' || (!normalizedPackageId && suiClient?.network == 'mainnet')) {
@@ -129,10 +130,13 @@ export default class EndlessVector {
129
130
  tx.setGasPayment([gasCoin]);
130
131
  }
131
132
 
132
- if (items && Array.isArray(items) && items.length) {
133
+ if (array && array.length) {
134
+ const vectorInput = await EndlessVector.getCreateTransactionAndReturnVectorInput({
135
+ packageId: normalizedPackageId,
136
+ }, array, tx);
133
137
  tx.moveCall({
134
- target: `${normalizedPackageId}::endless_vector::empty_entry_and_push`,
135
- arguments: [tx.pure(bcs.vector(bcs.vector(bcs.u8())).serialize(items))],
138
+ target: `${normalizedPackageId}::endless_vector::transfer_to_sender`,
139
+ arguments: [vectorInput],
136
140
  });
137
141
  } else {
138
142
  tx.moveCall({
@@ -180,33 +184,92 @@ export default class EndlessVector {
180
184
  });
181
185
  }
182
186
 
183
- get isWritable() {
184
- return !!(this._packageId && this._signAndExecuteTransaction);
185
- }
186
-
187
187
  /**
188
- * Creates a transaction to push new byte arrays to the EndlessVector.
189
- * 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
+ }
190
222
 
191
- * @param {Uint8Array} arr - Array of byte arrays to push
192
- * @returns {Transaction} The transaction object to be signed and executed
193
- */
194
- getPushTransaction(arr, txToAppendTo = null) {
195
- if (!this._packageId) {
196
- throw new Error('packageId is required to compose push transaction');
223
+ if (!normalizedPackageId) {
224
+ throw new Error('packageId is required');
197
225
  }
226
+ // Create transaction to call empty_entry
198
227
 
199
228
  let tx = txToAppendTo;
200
229
  if (!tx) {
201
230
  tx = new Transaction();
202
231
  }
203
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) {
204
267
  const maxArgLength = 12 * 1024;
205
268
  if (arr.length < maxArgLength) {
206
269
  tx.moveCall({
207
- target: `${this._packageId}::endless_vector::push_back`,
270
+ target: `${packageId}::endless_vector::push_back`,
208
271
  arguments: [
209
- tx.object(this.id),
272
+ vectorInput,
210
273
  tx.pure(bcs.vector(bcs.u8()).serialize(arr)),
211
274
  ],
212
275
  });
@@ -223,17 +286,41 @@ export default class EndlessVector {
223
286
  chunks.push(new Uint8Array()); // empty chunk
224
287
  }
225
288
  }
226
- const args = [tx.object(this.id)];
289
+ const args = [vectorInput];
227
290
  for (let i = 0; i < N; i++) {
228
291
  args.push(tx.pure(bcs.vector(bcs.u8()).serialize(chunks[i])));
229
292
  }
230
293
  tx.moveCall({
231
- target: `${this._packageId}::endless_vector::compose_and_push_back`,
294
+ target: `${packageId}::endless_vector::compose_and_push_back`,
232
295
  arguments: args,
233
296
  });
234
297
  } else {
235
298
  throw new Error('Array too large, max '+(10*maxArgLength)+' bytes supported per single tx');
236
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
+
237
324
  return tx;
238
325
  }
239
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.3",
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