@fizzyflow/endless-vector 0.0.7 → 0.0.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.
- package/EndlessVector.js +401 -142
- package/EndlessVectorArchive.js +31 -36
- package/EndlessVectorHistory.js +17 -13
- package/EndlessVectorItem.js +160 -0
- package/EndlessVectorSeal.js +192 -0
- package/EndlessVectorWalrus.js +467 -0
- package/README.md +253 -198
- package/ids.js +4 -4
- package/index.d.ts +181 -157
- package/index.js +5 -1
- package/package.json +16 -6
- package/test/base.test.js +388 -427
- package/test/base.test.txt +499 -0
- package/test/fixture.js +93 -0
- package/test/grpc_json_browser.test.js +22 -0
- package/test/helpers.js +2 -2
- package/test/helpers.txt +32 -0
- package/test/seal.test.js +319 -0
- package/test/walrus_blobs.test.js +178 -0
- package/test/walrus_blobs_extend.test.js +115 -0
- package/test/walrus_blobs_history.test.js +301 -0
- package/test/walrus_blobs_sdk.test.js +148 -0
- package/tsconfig.json +13 -0
- package/vitest.config.js +16 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Endless Vector - JavaScript SDK
|
|
2
2
|
|
|
3
|
-
JavaScript/TypeScript SDK for
|
|
3
|
+
JavaScript/TypeScript SDK for the Endless Vector smart contract on [Sui](https://sui.io). Endless Vector is a scalable, append-only on-chain `vector<vector<u8>>` that grows beyond Sui object size limits by automatically splitting data into history segments. Items larger than ~120 KB are transparently stored as [Walrus](https://walrus.xyz) blobs. Optional [Seal](https://github.com/nicola/seal) encryption protects all stored data with AES-256-GCM.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,276 +10,346 @@ npm install @fizzyflow/endless-vector
|
|
|
10
10
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
|
-
### Creating a New Vector
|
|
14
|
-
|
|
15
13
|
```javascript
|
|
16
|
-
import { SuiClient } from '@mysten/sui/client';
|
|
17
14
|
import { EndlessVector } from '@fizzyflow/endless-vector';
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// Create an empty vector
|
|
22
|
-
const vector = await EndlessVector.create({
|
|
16
|
+
// Create
|
|
17
|
+
const ev = await EndlessVector.create({
|
|
23
18
|
suiClient: client,
|
|
24
|
-
packageId: 'testnet',
|
|
19
|
+
packageId: 'testnet', // or 'mainnet', or an explicit 0x... package ID
|
|
25
20
|
signAndExecuteTransaction: async (tx) => {
|
|
26
21
|
const result = await wallet.signAndExecuteTransaction({ transaction: tx });
|
|
27
22
|
return result.digest;
|
|
28
|
-
}
|
|
23
|
+
},
|
|
29
24
|
});
|
|
30
25
|
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
//array: [new Uint8Array([1, 2, 3]), new Uint8Array([5, 6, 7])], // or [0] and [1] to append to EndlessVector
|
|
37
|
-
signAndExecuteTransaction: async (tx) => {
|
|
38
|
-
const result = await wallet.signAndExecuteTransaction({ transaction: tx });
|
|
39
|
-
return result.digest;
|
|
40
|
-
}
|
|
41
|
-
});
|
|
26
|
+
// Write
|
|
27
|
+
await ev.push(new Uint8Array([1, 2, 3]));
|
|
28
|
+
|
|
29
|
+
// Read
|
|
30
|
+
const item = await ev.at(0); // Uint8Array
|
|
42
31
|
```
|
|
43
32
|
|
|
44
|
-
|
|
33
|
+
## Constructor
|
|
45
34
|
|
|
46
35
|
```javascript
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
id: '0xYOUR_VECTOR_OBJECT_ID'
|
|
50
|
-
});
|
|
36
|
+
const ev = new EndlessVector(params);
|
|
37
|
+
```
|
|
51
38
|
|
|
52
|
-
|
|
39
|
+
| Parameter | Type | Required | Description |
|
|
40
|
+
|-----------|------|----------|-------------|
|
|
41
|
+
| `suiClient` | SuiGrpcClient | yes | Sui gRPC client instance |
|
|
42
|
+
| `id` | string | yes | On-chain object ID of the vector |
|
|
43
|
+
| `packageId` | string | no | Move package ID, or `'testnet'`/`'mainnet'`. Enables writes |
|
|
44
|
+
| `signAndExecuteTransaction` | function | no | Signs and submits a Transaction, returns its digest. Enables writes |
|
|
45
|
+
| `walrusClient` | WalrusClient | no | `@mysten/walrus` client for blob read/write |
|
|
46
|
+
| `publisherUrl` | string | no | Walrus publisher HTTP URL (fallback when no `walrusClient`) |
|
|
47
|
+
| `aggregatorUrl` | string | no | Walrus aggregator HTTP URL (fallback when no `walrusClient`) |
|
|
48
|
+
| `senderAddress` | string | no | Sender Sui address, required for Walrus blob writes |
|
|
49
|
+
| `sealClient` | SealClient | no | `@mysten/seal` client for encryption/decryption |
|
|
50
|
+
| `sessionKey` | SessionKey | no | Pre-built Seal SessionKey. Alternative to `signer` |
|
|
51
|
+
| `signer` | Signer | no | Keypair or wallet signer used to auto-create a SessionKey when needed |
|
|
52
|
+
| `sealTtlMin` | number | no | SessionKey TTL in minutes (default: 5) |
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
console.log('Total size:', vector.binaryLength, 'bytes');
|
|
54
|
+
Providing only `suiClient` + `id` gives a **read-only** instance. Add `packageId` + `signAndExecuteTransaction` for **writes**. Add Walrus params for **large items** (>120 KB). Add Seal params for **encryption**.
|
|
56
55
|
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
## EndlessVector.create(params)
|
|
57
|
+
|
|
58
|
+
Creates a new on-chain vector.
|
|
59
|
+
|
|
60
|
+
Accepts all constructor params above, plus:
|
|
61
|
+
|
|
62
|
+
| Parameter | Type | Required | Description |
|
|
63
|
+
|-----------|------|----------|-------------|
|
|
64
|
+
| `array` | Uint8Array \| Uint8Array[] | no | Initial item(s) to push |
|
|
65
|
+
| `gasCoin` | `{objectId, digest, version}` | no | Explicit gas coin for parallel creation |
|
|
66
|
+
| `options.timeout` | number | no | Tx confirmation timeout, ms (default: 30000) |
|
|
67
|
+
| `options.pollIntervalMs` | number | no | Tx poll interval, ms (default: 200) |
|
|
68
|
+
|
|
69
|
+
When `sealClient` is provided, the vector is created with Seal encryption enabled. A random AES-256-GCM key is generated, Seal-wrapped scoped to the new vector's object ID, and stored on-chain. Any initial `array` items are encrypted before storage.
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
const ev = await EndlessVector.create({
|
|
73
|
+
suiClient: client,
|
|
74
|
+
packageId: 'testnet',
|
|
75
|
+
sealClient,
|
|
76
|
+
signer: keypair,
|
|
77
|
+
signAndExecuteTransaction: sign,
|
|
78
|
+
});
|
|
79
|
+
// All push() / at() calls now encrypt/decrypt transparently
|
|
59
80
|
```
|
|
60
81
|
|
|
61
|
-
##
|
|
82
|
+
## Properties
|
|
83
|
+
|
|
84
|
+
| Property | Type | Description |
|
|
85
|
+
|----------|------|-------------|
|
|
86
|
+
| `id` | string | Object ID |
|
|
87
|
+
| `isWritable` | boolean | Whether writes are enabled |
|
|
88
|
+
| `length` | number | Total item count (append-only, never decreases) |
|
|
89
|
+
| `binaryLength` | number | Total size of all items in bytes |
|
|
90
|
+
| `sealEncryptedKey` | Uint8Array \| null | Seal-wrapped AES key, or null if unencrypted |
|
|
91
|
+
| `seal` | EndlessVectorSeal | Seal companion (always present; active only when `sealClient` was provided) |
|
|
92
|
+
| `walrus` | EndlessVectorWalrus | Walrus companion (always present; active only when Walrus params were provided) |
|
|
93
|
+
| `historyItemsCount` | number | History segments in the current object |
|
|
94
|
+
| `archiveItemsCount` | number | Total archive entries ever created |
|
|
95
|
+
| `archivedAtLength` | number | `length` at the time of the last archive |
|
|
96
|
+
| `archivedFromLength` | number | Items before this index have been burned |
|
|
97
|
+
| `burnedArchiveCount` | number | Burned archive count |
|
|
98
|
+
| `firstNotHistoryIndex` | number | First index stored in the current object |
|
|
62
99
|
|
|
63
|
-
|
|
100
|
+
## Methods
|
|
64
101
|
|
|
65
|
-
|
|
102
|
+
### initialize()
|
|
66
103
|
|
|
67
|
-
|
|
104
|
+
Loads metadata from chain. Most read methods call this internally.
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
await ev.initialize();
|
|
108
|
+
```
|
|
68
109
|
|
|
69
|
-
|
|
70
|
-
- `suiClient` (SuiClient) - Sui client instance for blockchain interactions
|
|
71
|
-
- `packageId` (string) - 'testnet', 'mainnet', or ID of the Move package containing the EndlessVector module
|
|
72
|
-
- `signAndExecuteTransaction` (function) - Function to sign and execute transactions
|
|
73
|
-
- `array` (Uint8Array or Uint8Array[], optional) - Optional first vector<u8>(s) to push back to the new vector
|
|
74
|
-
- `gasCoin` (Object, optional) - Gas coin object reference `{objectId: string, digest: string, version: string}` for transaction payment
|
|
75
|
-
- `options` (Object, optional) - Additional options:
|
|
76
|
-
- `timeout` (number) - Transaction confirmation timeout in ms (default: 30000)
|
|
77
|
-
- `pollIntervalMs` (number) - Poll interval in ms (default: 1000)
|
|
110
|
+
### reInitialize()
|
|
78
111
|
|
|
79
|
-
|
|
112
|
+
Marks the instance stale so the next operation re-fetches from chain.
|
|
80
113
|
|
|
81
|
-
**Example:**
|
|
82
114
|
```javascript
|
|
83
|
-
|
|
84
|
-
suiClient: client,
|
|
85
|
-
packageId: 'testnet', // or 'mainnet' or '0xPACKAGE_ID'
|
|
86
|
-
array: new Uint8Array([1, 2, 3]),
|
|
87
|
-
gasCoin: {
|
|
88
|
-
objectId: '0xGAS_COIN_ID',
|
|
89
|
-
digest: 'DIGEST',
|
|
90
|
-
version: 'VERSION'
|
|
91
|
-
},
|
|
92
|
-
signAndExecuteTransaction: async (tx) => {
|
|
93
|
-
const result = await wallet.signAndExecuteTransaction({ transaction: tx });
|
|
94
|
-
return result.digest;
|
|
95
|
-
}
|
|
96
|
-
});
|
|
115
|
+
ev.reInitialize();
|
|
97
116
|
```
|
|
98
117
|
|
|
99
|
-
###
|
|
118
|
+
### isEncrypted()
|
|
119
|
+
|
|
120
|
+
Async. Returns `true` if the vector has a Seal encryption key on-chain. Calls `initialize()` internally.
|
|
100
121
|
|
|
101
122
|
```javascript
|
|
102
|
-
|
|
103
|
-
suiClient, // SuiClient instance (required for reading)
|
|
104
|
-
id, // Object ID of the EndlessVector (required)
|
|
105
|
-
packageId, // 'testnet', 'mainnet', or Package ID for write operations (optional)
|
|
106
|
-
signAndExecuteTransaction // Function to sign/execute transactions (optional)
|
|
107
|
-
});
|
|
123
|
+
if (await ev.isEncrypted()) { /* ... */ }
|
|
108
124
|
```
|
|
109
125
|
|
|
110
|
-
|
|
111
|
-
- **Read-only mode**: Provide only `suiClient` and `id`
|
|
112
|
-
- **Writable mode**: Provide all parameters including `packageId` and `signAndExecuteTransaction`
|
|
126
|
+
### push(arr, params?)
|
|
113
127
|
|
|
114
|
-
|
|
128
|
+
Appends one or more `Uint8Array` items. Requires writable mode.
|
|
115
129
|
|
|
116
|
-
-
|
|
117
|
-
-
|
|
118
|
-
-
|
|
119
|
-
- `binaryLength` (number) - Total binary size of all items in bytes
|
|
120
|
-
- `historyItemsCount` (number) - Number of history segments
|
|
121
|
-
- `archiveItemsCount` (number) - Number of archive segments
|
|
122
|
-
- `archivedFromLength` (number) - Starting index after burned archives
|
|
123
|
-
- `burnedArchiveCount` (number) - Number of archives that have been burned
|
|
124
|
-
- `firstNotHistoryIndex` (number) - First index stored in current object (not in history)
|
|
130
|
+
- Items up to ~120 KB are stored on-chain as `vector<u8>`.
|
|
131
|
+
- Larger items are stored as Walrus blobs (requires Walrus params).
|
|
132
|
+
- On encrypted vectors, every item is AES-256-GCM encrypted before storage (28 bytes overhead per item).
|
|
125
133
|
|
|
126
|
-
|
|
134
|
+
```javascript
|
|
135
|
+
await ev.push(new Uint8Array([1, 2, 3]));
|
|
136
|
+
await ev.push([chunk1, chunk2, chunk3]); // multiple items
|
|
137
|
+
```
|
|
127
138
|
|
|
128
|
-
|
|
139
|
+
### getPushTransaction(arr, tx?)
|
|
129
140
|
|
|
130
|
-
|
|
141
|
+
Returns a `Transaction` without executing it. Useful for batching multiple pushes.
|
|
131
142
|
|
|
132
143
|
```javascript
|
|
133
|
-
|
|
144
|
+
const tx = new Transaction();
|
|
145
|
+
ev.getPushTransaction(data1, tx);
|
|
146
|
+
ev.getPushTransaction(data2, tx);
|
|
147
|
+
await signAndExecuteTransaction(tx);
|
|
134
148
|
```
|
|
135
149
|
|
|
136
|
-
|
|
150
|
+
### at(index)
|
|
137
151
|
|
|
138
|
-
|
|
152
|
+
Reads the item at a zero-based index. On encrypted vectors, decrypts transparently.
|
|
139
153
|
|
|
140
154
|
```javascript
|
|
141
|
-
await
|
|
155
|
+
const data = await ev.at(0); // Uint8Array
|
|
142
156
|
```
|
|
143
157
|
|
|
144
|
-
|
|
158
|
+
### concat(other, params?)
|
|
145
159
|
|
|
146
|
-
|
|
160
|
+
Appends all items from another vector (or array of vectors) into this one. Sources are consumed (destroyed).
|
|
147
161
|
|
|
148
162
|
```javascript
|
|
149
|
-
|
|
150
|
-
await
|
|
163
|
+
await ev.concat(otherVector);
|
|
164
|
+
await ev.concat([v2, v3]);
|
|
165
|
+
await ev.concat('0xOTHER_VECTOR_ID');
|
|
151
166
|
```
|
|
152
167
|
|
|
153
|
-
**
|
|
154
|
-
- `arr` (Uint8Array or Uint8Array[]) - Data to push
|
|
155
|
-
- `params` (Object, optional) - Additional parameters
|
|
168
|
+
**Restrictions:** cannot concat vectors that have archived items or that are Seal-encrypted.
|
|
156
169
|
|
|
157
|
-
|
|
170
|
+
### getConcatTransaction(other, tx?)
|
|
158
171
|
|
|
159
|
-
|
|
172
|
+
Returns a concat `Transaction` without executing.
|
|
160
173
|
|
|
161
|
-
|
|
162
|
-
// Single push transaction
|
|
163
|
-
const tx = vector.getPushTransaction(new Uint8Array([1, 2, 3]));
|
|
164
|
-
await signAndExecuteTransaction(tx);
|
|
174
|
+
### archive(params?)
|
|
165
175
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
await signAndExecuteTransaction(tx);
|
|
176
|
+
Sweeps current history segments into a new archive entry, freeing capacity for future pushes.
|
|
177
|
+
|
|
178
|
+
```javascript
|
|
179
|
+
await ev.archive();
|
|
171
180
|
```
|
|
172
181
|
|
|
173
|
-
|
|
174
|
-
- `arr` (Uint8Array or Uint8Array[]) - Data to push
|
|
175
|
-
- `tx` (Transaction, optional) - Existing transaction to append to
|
|
182
|
+
### getArchiveTransaction(tx?)
|
|
176
183
|
|
|
177
|
-
|
|
184
|
+
Returns an archive `Transaction` without executing.
|
|
178
185
|
|
|
179
|
-
|
|
186
|
+
### burnArchive(params?)
|
|
180
187
|
|
|
181
|
-
|
|
188
|
+
Permanently deletes the oldest archive entry. Items in the burned range become unreadable.
|
|
182
189
|
|
|
183
190
|
```javascript
|
|
184
|
-
|
|
191
|
+
await ev.burnArchive();
|
|
192
|
+
// ev.archivedFromLength now advanced; at() throws for burned indices
|
|
185
193
|
```
|
|
186
194
|
|
|
187
|
-
|
|
188
|
-
- `index` (number) - Zero-based index
|
|
195
|
+
### getBurnArchiveTransaction(tx?)
|
|
189
196
|
|
|
190
|
-
|
|
197
|
+
Returns a burn-archive `Transaction` without executing.
|
|
191
198
|
|
|
192
|
-
|
|
199
|
+
## Walrus Blob Storage
|
|
193
200
|
|
|
194
|
-
|
|
201
|
+
When Walrus params are configured, items larger than ~120 KB are automatically stored as Walrus blobs instead of on-chain `vector<u8>`. The SDK handles upload, certification, and read-back. On encrypted vectors, blobs contain ciphertext only.
|
|
195
202
|
|
|
196
203
|
```javascript
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
204
|
+
const ev = new EndlessVector({
|
|
205
|
+
suiClient: client,
|
|
206
|
+
id: '0x...',
|
|
207
|
+
packageId: 'testnet',
|
|
208
|
+
walrusClient, // or aggregatorUrl + publisherUrl
|
|
209
|
+
senderAddress: wallet.address,
|
|
210
|
+
signAndExecuteTransaction: sign,
|
|
211
|
+
});
|
|
202
212
|
|
|
203
|
-
//
|
|
204
|
-
await
|
|
205
|
-
await vector1.concat(['0xVECTOR2_ID', '0xVECTOR3_ID']);
|
|
213
|
+
await ev.push(largeFile); // >120 KB → stored as Walrus blob
|
|
214
|
+
const data = await ev.at(0); // fetched from Walrus transparently
|
|
206
215
|
```
|
|
207
216
|
|
|
208
|
-
|
|
209
|
-
- `other` (string | EndlessVector | Array<string | EndlessVector>) - Vector(s) to concatenate
|
|
217
|
+
### Blob lifetime: inspect & extend
|
|
210
218
|
|
|
211
|
-
|
|
219
|
+
Walrus blobs are backed by storage that expires at an `end_epoch`. The Walrus companion (`ev.walrus`) can report when a vector's blobs expire and renew them all in a single transaction — covering blobs in current items, history segments, and non-burned archive segments. These methods require `walrusClient` (to resolve the Walrus `System` object and current storage price).
|
|
212
220
|
|
|
213
|
-
|
|
221
|
+
#### minBlobEndEpoch()
|
|
214
222
|
|
|
215
|
-
|
|
223
|
+
Async. Reads the soonest-expiring blob's `end_epoch` across the whole vector, on-chain via simulation (no gas, works read-only). Returns `null` if the vector holds no blobs.
|
|
216
224
|
|
|
217
|
-
|
|
225
|
+
```javascript
|
|
226
|
+
const minEpoch = await ev.walrus.minBlobEndEpoch(); // number | null
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### extendBlobsCostToEpoch(targetEndEpoch)
|
|
230
|
+
|
|
231
|
+
Async. Returns the exact WAL cost (in FROST, as a `bigint`) to bring every blob up to `targetEndEpoch`. Computed on-chain, so it accounts for every blob across all tiers — even ones not loaded in the SDK. `0n` when nothing needs extending.
|
|
218
232
|
|
|
219
233
|
```javascript
|
|
220
|
-
const
|
|
221
|
-
|
|
234
|
+
const cost = await ev.walrus.extendBlobsCostToEpoch(minEpoch + 10); // bigint (FROST)
|
|
235
|
+
```
|
|
222
236
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
237
|
+
#### extendBlobsToEpoch(targetEndEpoch, params?)
|
|
238
|
+
|
|
239
|
+
Async. Extends every blob whose storage ends before `targetEndEpoch` up to that epoch, in one transaction. Blobs already valid through the target (and expired blobs, which Walrus cannot extend) are skipped on-chain. Resolves to the new minimum blob end epoch. Requires writable mode and `senderAddress`.
|
|
240
|
+
|
|
241
|
+
The WAL payment is sourced automatically from the sender's balance for exactly the on-chain cost, and any leftover is returned to the sender. Pass `walCoin` to pay from a specific coin, or `cost` to skip the cost read.
|
|
242
|
+
|
|
243
|
+
```javascript
|
|
244
|
+
const minEpoch = await ev.walrus.minBlobEndEpoch();
|
|
245
|
+
const newMin = await ev.walrus.extendBlobsToEpoch(minEpoch + 10);
|
|
246
|
+
// newMin === minEpoch + 10
|
|
227
247
|
```
|
|
228
248
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
249
|
+
| Parameter | Type | Required | Description |
|
|
250
|
+
|-----------|------|----------|-------------|
|
|
251
|
+
| `cost` | bigint | no | Precomputed cost in FROST; skips the on-chain cost read |
|
|
252
|
+
| `walCoin` | TransactionObjectArgument | no | WAL coin to pay from; if omitted, one is sourced from the sender's balance |
|
|
253
|
+
| `timeout` | number | no | Tx confirmation timeout, ms |
|
|
254
|
+
| `pollIntervalMs` | number | no | Tx poll interval, ms |
|
|
255
|
+
|
|
256
|
+
#### getExtendBlobsToEpochTransaction(targetEndEpoch, params?)
|
|
257
|
+
|
|
258
|
+
Async. Returns the extend `Transaction` without executing, for batching. Accepts `cost`, `walCoin`, and `txToAppendTo`.
|
|
232
259
|
|
|
233
|
-
|
|
260
|
+
## Seal Encryption
|
|
234
261
|
|
|
235
|
-
|
|
262
|
+
Seal provides end-to-end encryption for vector items. The access policy (`seal_approve_endless_vector_owner`) ensures only the vector owner can decrypt.
|
|
263
|
+
|
|
264
|
+
### Creating an encrypted vector
|
|
236
265
|
|
|
237
266
|
```javascript
|
|
238
|
-
const
|
|
267
|
+
const ev = await EndlessVector.create({
|
|
239
268
|
suiClient: client,
|
|
240
|
-
|
|
269
|
+
packageId: 'testnet',
|
|
270
|
+
sealClient,
|
|
271
|
+
signer: keypair,
|
|
272
|
+
signAndExecuteTransaction: sign,
|
|
241
273
|
});
|
|
274
|
+
// AES key generated → Seal-wrapped → stored on-chain
|
|
275
|
+
// All push()/at() calls encrypt/decrypt transparently
|
|
276
|
+
```
|
|
242
277
|
|
|
243
|
-
|
|
278
|
+
### Reading an encrypted vector
|
|
244
279
|
|
|
245
|
-
|
|
246
|
-
console.log('Items:', vector.length);
|
|
247
|
-
console.log('Size:', vector.binaryLength, 'bytes');
|
|
248
|
-
console.log('History segments:', vector.historyItemsCount);
|
|
249
|
-
console.log('Archive segments:', vector.archiveItemsCount);
|
|
280
|
+
You can provide a `signer` and the SDK auto-creates a SessionKey:
|
|
250
281
|
|
|
251
|
-
|
|
252
|
-
const
|
|
282
|
+
```javascript
|
|
283
|
+
const ev = new EndlessVector({
|
|
284
|
+
suiClient: client,
|
|
285
|
+
id: '0x...',
|
|
286
|
+
sealClient,
|
|
287
|
+
signer: keypair, // SDK creates a 5-minute SessionKey automatically
|
|
288
|
+
});
|
|
289
|
+
const data = await ev.at(0); // decrypted
|
|
253
290
|
```
|
|
254
291
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
To execute transactions in parallel, speeding up data upload process, you would probably need separate gas coin for each tx:
|
|
292
|
+
Or provide a pre-built `sessionKey` (useful in browser wallets where signing is interactive):
|
|
258
293
|
|
|
259
294
|
```javascript
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
295
|
+
const ev = new EndlessVector({
|
|
296
|
+
suiClient: client,
|
|
297
|
+
id: '0x...',
|
|
298
|
+
sealClient,
|
|
299
|
+
sessionKey: mySessionKey, // created externally, e.g. via wallet adapter
|
|
264
300
|
});
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
301
|
+
const data = await ev.at(0); // decrypted using the provided SessionKey
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
You can also set the session key after construction:
|
|
305
|
+
|
|
306
|
+
```javascript
|
|
307
|
+
ev.seal._sessionKey = mySessionKey;
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### How it works
|
|
311
|
+
|
|
312
|
+
1. `create()` generates a random AES-256-GCM key
|
|
313
|
+
2. The key is Seal-wrapped scoped to the vector's object ID and stored on-chain as `seal_encrypted_key`
|
|
314
|
+
3. `push()` encrypts each item before storage (adds 28 bytes: 12B nonce + 16B GCM tag)
|
|
315
|
+
4. `at()` unwraps the AES key via Seal (requires a valid SessionKey), then decrypts the item
|
|
316
|
+
5. The unwrapped AES key is cached in memory for subsequent reads
|
|
317
|
+
|
|
318
|
+
Passing `sealClient` to an unencrypted vector is safe — `push()` and `at()` check for the on-chain `sealEncryptedKey` before attempting any encryption/decryption.
|
|
319
|
+
|
|
320
|
+
## Examples
|
|
321
|
+
|
|
322
|
+
### Archive and burn lifecycle
|
|
270
323
|
|
|
271
|
-
|
|
324
|
+
```javascript
|
|
325
|
+
await ev.push(largeData);
|
|
326
|
+
await ev.archive();
|
|
327
|
+
await ev.initialize();
|
|
328
|
+
|
|
329
|
+
console.log(ev.archiveItemsCount); // 1
|
|
330
|
+
console.log(ev.archivedAtLength); // e.g. 5
|
|
331
|
+
|
|
332
|
+
const item = await ev.at(0); // still readable
|
|
333
|
+
|
|
334
|
+
await ev.burnArchive();
|
|
335
|
+
await ev.initialize();
|
|
336
|
+
|
|
337
|
+
console.log(ev.burnedArchiveCount); // 1
|
|
338
|
+
console.log(ev.archivedFromLength); // 5 — items 0..4 are gone
|
|
339
|
+
await ev.at(0); // throws
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Parallel vector creation with gas coins
|
|
343
|
+
|
|
344
|
+
```javascript
|
|
272
345
|
const vectors = await Promise.all(
|
|
273
|
-
|
|
346
|
+
dataChunks.map((chunk, i) =>
|
|
274
347
|
EndlessVector.create({
|
|
275
348
|
suiClient: client,
|
|
276
|
-
packageId: 'testnet',
|
|
277
|
-
array:
|
|
349
|
+
packageId: 'testnet',
|
|
350
|
+
array: chunk,
|
|
278
351
|
gasCoin: gasCoinRefs[i],
|
|
279
|
-
signAndExecuteTransaction:
|
|
280
|
-
const result = await wallet.signAndExecuteTransaction({ transaction: tx });
|
|
281
|
-
return result.digest;
|
|
282
|
-
}
|
|
352
|
+
signAndExecuteTransaction: sign,
|
|
283
353
|
})
|
|
284
354
|
)
|
|
285
355
|
);
|
|
@@ -287,38 +357,23 @@ const vectors = await Promise.all(
|
|
|
287
357
|
|
|
288
358
|
## Testing
|
|
289
359
|
|
|
290
|
-
Run the test suite:
|
|
291
|
-
|
|
292
360
|
```bash
|
|
293
|
-
|
|
361
|
+
pnpm test:base # core tests
|
|
362
|
+
pnpm test:seal # seal encryption tests
|
|
363
|
+
pnpm test:walrus-blobs # walrus blob storage
|
|
364
|
+
pnpm test:walrus-blobs-sdk # walrus blob storage (SDK client)
|
|
365
|
+
pnpm test:walrus-blobs-history # blob storage across history segments
|
|
366
|
+
pnpm test:walrus-blobs-extend # blob lifetime: min end epoch, cost, extend
|
|
294
367
|
```
|
|
295
368
|
|
|
296
|
-
Tests use
|
|
297
|
-
|
|
298
|
-
### Performance Considerations
|
|
299
|
-
|
|
300
|
-
- **Caching**: Loaded history and archive segments are cached
|
|
301
|
-
- **Lazy Loading**: History/archive data is only loaded when accessed
|
|
302
|
-
- **Batch Writes**: Use `getPushTransaction()` to combine multiple pushes in one transaction
|
|
303
|
-
- **Re-initialization**: After `push()` or `concat()`, metadata is refreshed on next read
|
|
304
|
-
|
|
305
|
-
### Concatenation
|
|
306
|
-
|
|
307
|
-
The `concat()` method efficiently merges vectors by transferring ownership of internal data structures rather than copying items one by one. This makes it very efficient for combining large datasets.
|
|
308
|
-
|
|
309
|
-
**Restrictions:**
|
|
310
|
-
- Cannot concatenate vectors that have archived items
|
|
311
|
-
- The concatenated vector is consumed (destroyed) in the process
|
|
312
|
-
- All items from concatenated vector(s) are appended in order
|
|
369
|
+
Tests use [vitest](https://vitest.dev/) and require a local Sui validator with Walrus and Seal localnet services.
|
|
313
370
|
|
|
314
371
|
## License
|
|
315
372
|
|
|
316
373
|
Apache-2.0
|
|
317
374
|
|
|
318
|
-
##
|
|
319
|
-
|
|
320
|
-
https://github.com/fizzyFlow/endless_vector
|
|
321
|
-
|
|
322
|
-
## Author
|
|
375
|
+
## Links
|
|
323
376
|
|
|
324
|
-
[
|
|
377
|
+
- [Repository](https://github.com/fizzyFlow/endless_vector)
|
|
378
|
+
- [npm](https://www.npmjs.com/package/@fizzyflow/endless-vector)
|
|
379
|
+
- [Author](https://github.com/suidouble)
|
package/ids.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
|
|
2
2
|
export default {
|
|
3
3
|
'testnet': {
|
|
4
|
-
packageId: '
|
|
5
|
-
originalPackageId: '
|
|
4
|
+
packageId: '0xaa7b048bc68a31204955da199b18020d68bb9823ae021b4c5d3f7245330ce825', // should be the same as in Move.lock
|
|
5
|
+
originalPackageId: '0xaa7b048bc68a31204955da199b18020d68bb9823ae021b4c5d3f7245330ce825',
|
|
6
6
|
},
|
|
7
7
|
'mainnet': {
|
|
8
|
-
packageId: '
|
|
9
|
-
originalPackageId: '
|
|
8
|
+
packageId: '0xec49cff1adbf19e0fdfa7933047162a749005fdfb64a1cebfb1ea5a8727b16f3', // should be the same as in Move.lock
|
|
9
|
+
originalPackageId: '0xec49cff1adbf19e0fdfa7933047162a749005fdfb64a1cebfb1ea5a8727b16f3',
|
|
10
10
|
},
|
|
11
11
|
};
|