@zebec-network/zebec-vault-sdk 5.0.3 → 5.1.0

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/README.md CHANGED
@@ -1,577 +1,607 @@
1
- # Zebec Vault SDK
2
-
3
- A TypeScript SDK for interacting with Zebec Vault on Solana, enabling secure multi operations, token streaming, staking, and virtual card management.
4
-
5
- ## Features
6
-
7
- - **Vault Management**: Create and manage secure vaults with proposal-based execution
8
- - **Token Operations**: Deposit and withdraw SOL and SPL tokens
9
- - **Streaming Payments**: Create, manage, and cancel payment streams
10
- - **Staking**: Stake tokens with customizable lock periods
11
- - **Virtual Cards**: Create and load Zebec cards with automatic token swapping
12
- - **Multi-Signature Support**: Execute operations through proposals with multiple signers
13
- - **Jupiter Integration**: Built-in token swapping for card operations
14
-
15
- ## Installation
16
-
17
- ```bash
18
- npm install @zebec-network/zebec-vault-sdk
19
- ```
20
-
21
- ```bash
22
- yarn add @zebec-network/zebec-vault-sdk
23
- ```
24
-
25
- ## Quick Start
26
-
27
- ### Setup
28
-
29
- ```typescript
30
- import { Connection, Keypair } from '@solana/web3.js';
31
- import { createAnchorProvider, ZebecVaultService } from '@zebec-network/zebec-vault-sdk';
32
-
33
- // Create connection
34
- const connection = new Connection('https://api.mainnet-beta.solana.com'); // use private dedicatd rpc for production
35
-
36
- // Create wallet adapter
37
- // for frontend application you can use wallet provided by the wallet provider
38
- const wallet = useAnchorWallet();
39
- // for server side app you can use Wallet class provided by @coral-xyz/anchor
40
- const wallet = new Wallet(keypair) // create keypair from secret key
41
-
42
- // Create provider
43
- const provider = createAnchorProvider(connection, wallet);
44
-
45
- // Initialize service
46
- const vaultService = ZebecVaultService.create(provider, 'mainnet-beta');
47
- ```
48
-
49
- ## Core Features
50
-
51
- ### 1. Vault Operations
52
-
53
- #### Create a Vault
54
-
55
- ```typescript
56
- const payload = await vaultService.createVault({
57
- payer: wallet.publicKey // optional if using AnchorProvider
58
- });
59
-
60
- const signature = await payload.execute();
61
- console.log('Vault created:', signature);
62
- ```
63
-
64
- #### Deposit SOL
65
-
66
- ```typescript
67
- const payload = await vaultService.depositSol({
68
- depositor: wallet.publicKey,
69
- amount: 1.5 // SOL amount
70
- });
71
-
72
- await payload.execute();
73
- ```
74
-
75
- #### Deposit SPL Tokens
76
-
77
- ```typescript
78
- const payload = await vaultService.deposit({
79
- depositor: wallet.publicKey,
80
- tokenMint: 'TOKEN_MINT_ADDRESS',
81
- amount: 100 // token amount
82
- });
83
-
84
- await payload.execute();
85
-
86
- // Note: if WSOL address is given in tokenMint, it will wrap SOL to WSOL and deposit.
87
- ```
88
-
89
- #### Withdraw SOL
90
-
91
- ```typescript
92
- const payload = await vaultService.withdrawSol({
93
- withdrawer: wallet.publicKey,
94
- amount: 0.5
95
- });
96
-
97
- await payload.execute();
98
- ```
99
-
100
- #### Withdraw SPL Tokens
101
-
102
- ```typescript
103
- const payload = await vaultService.withdraw({
104
- withdrawer: wallet.publicKey,
105
- tokenMint: 'TOKEN_MINT_ADDRESS',
106
- amount: 50
107
- });
108
-
109
- await payload.execute();
110
-
111
- // Note: if WSOL is address in given in tokenMint, it will with withdraw WSOL and unwrap into withdrawer wallet.
112
- ```
113
-
114
- ### 2. Proposal System
115
-
116
- #### Create a Proposal
117
-
118
- ```typescript
119
- import { SystemProgram } from '@solana/web3.js';
120
-
121
- // Create custom instructions
122
- const instruction = SystemProgram.transfer({
123
- fromPubkey: vaultSigner,
124
- toPubkey: recipient,
125
- lamports: 1000000
126
- });
127
-
128
- const payload = await vaultService.createProposal({
129
- proposer: wallet.publicKey,
130
- name: 'Transfer SOL',
131
- actions: [instruction]
132
- });
133
-
134
- await payload.execute();
135
- ```
136
-
137
- #### Execute a Proposal
138
-
139
- ```typescript
140
- const payload = await vaultService.executeProposal({
141
- caller: wallet.publicKey,
142
- proposal: proposalAddress,
143
- addressLookupTables: [lookupTableAddress] // optional
144
- });
145
-
146
- await payload.execute();
147
- ```
148
-
149
- #### Execute Proposal Directly (Single Transaction)
150
-
151
- ```typescript
152
- const payload = await vaultService.executeProposalDirect({
153
- proposer: wallet.publicKey,
154
- actions: [instruction1, instruction2],
155
- addressLookupTables: [lookupTableAddress]
156
- });
157
-
158
- await payload.execute();
159
- ```
160
-
161
- ### 3. Payment Streaming
162
-
163
- #### Create a Stream
164
-
165
- ```typescript
166
- const payload = await vaultService.createStreamFromVault({
167
- vaultOwner: wallet.publicKey,
168
- receiver: recipientAddress,
169
- streamToken: tokenMintAddress,
170
- amount: 1000, // total amount
171
- duration: 2592000, // 30 days in seconds
172
- startNow: true,
173
- startTime: Math.floor(Date.now() / 1000),
174
- automaticWithdrawal: false,
175
- cancelableByRecipient: true,
176
- cancelableBySender: true,
177
- isPausable: true,
178
- transferableByRecipient: false,
179
- transferableBySender: false,
180
- canTopup: true,
181
- rateUpdatable: false,
182
- cliffPercentage: 0, // 0-100
183
- autoWithdrawFrequency: 86400, // 1 day
184
- streamName: 'Monthly Payment'
185
- });
186
-
187
- await payload.execute();
188
- ```
189
-
190
- #### Create Multiple Streams
191
-
192
- ```typescript
193
- const payload = await vaultService.createMultipleStreamFromVault({
194
- vaultOwner: wallet.publicKey,
195
- streamInfo: [
196
- {
197
- receiver: recipient1,
198
- streamToken: tokenMint,
199
- amount: 500,
200
- duration: 2592000,
201
- // ... other stream parameters
202
- },
203
- {
204
- receiver: recipient2,
205
- streamToken: tokenMint,
206
- amount: 1000,
207
- duration: 2592000,
208
- // ... other stream parameters
209
- }
210
- ]
211
- });
212
-
213
- // Execute all streams in multiple transactions
214
- await payload.executeAll();
215
- ```
216
-
217
- #### Pause/Resume a Stream
218
-
219
- ```typescript
220
- const payload = await vaultService.pauseResumeStream({
221
- vaultOwner: wallet.publicKey,
222
- streamMetadata: streamAddress
223
- });
224
-
225
- await payload.execute();
226
- ```
227
-
228
- #### Cancel a Stream
229
-
230
- ```typescript
231
- const payload = await vaultService.cancelStream({
232
- vaultOwner: wallet.publicKey,
233
- streamMetadata: streamAddress
234
- });
235
-
236
- await payload.execute();
237
- ```
238
-
239
- #### Withdraw from Stream
240
-
241
- ```typescript
242
- const payload = await vaultService.withdrawStream({
243
- vaultOwner: wallet.publicKey,
244
- streamMetadata: streamAddress
245
- });
246
-
247
- await payload.execute();
248
- ```
249
-
250
- #### Change Stream Receiver
251
-
252
- ```typescript
253
- const payload = await vaultService.changeStreamReceiver({
254
- vaultOwner: wallet.publicKey,
255
- streamMetadata: streamAddress,
256
- newRecipient: newRecipientAddress
257
- });
258
-
259
- await payload.execute();
260
- ```
261
-
262
- #### Get Stream Information
263
-
264
- ```typescript
265
- const streamInfo = await vaultService.getStreamMetadataInfo(streamAddress);
266
-
267
- console.log('Stream details:', {
268
- sender: streamInfo.parties.sender,
269
- receiver: streamInfo.parties.receiver,
270
- token: streamInfo.financials.streamToken,
271
- deposited: streamInfo.financials.depositedAmount,
272
- withdrawn: streamInfo.financials.withdrawnAmount,
273
- startTime: new Date(streamInfo.schedule.startTime * 1000),
274
- endTime: new Date(streamInfo.schedule.endTime * 1000),
275
- isPaused: streamInfo.schedule.pausedTimestamp > 0
276
- });
277
- ```
278
-
279
- ### 4. Staking
280
-
281
- #### Stake Tokens
282
-
283
- ```typescript
284
- const payload = await vaultService.stake({
285
- lockupName: 'main-lockup',
286
- vaultOwner: wallet.publicKey,
287
- amount: 1000,
288
- lockPeriod: 7776000, // 90 days in seconds
289
- nonce: 0n
290
- });
291
-
292
- await payload.execute();
293
- ```
294
-
295
- #### Unstake Tokens
296
-
297
- ```typescript
298
- const payload = await vaultService.unstake({
299
- lockupName: 'main-lockup',
300
- vaultOwner: wallet.publicKey,
301
- nonce: 0n
302
- });
303
-
304
- await payload.execute();
305
- ```
306
-
307
- #### Get Stake Nonce Information
308
-
309
- ```typescript
310
- const nonceInfo = await vaultService.getStakeUserNonceInfo(
311
- 'main-lockup',
312
- wallet.publicKey
313
- );
314
-
315
- console.log('Current nonce:', nonceInfo?.nonce);
316
- ```
317
-
318
- ### 5. Virtual Cards
319
-
320
- #### Create a Silver Card
321
-
322
- ```typescript
323
- const nextIndex = await vaultService.getNextCardIndex();
324
-
325
- const payload = await vaultService.createSilverCard({
326
- vaultOwnerAddress: wallet.publicKey,
327
- nextCardIndex: nextIndex,
328
- amount: 100, // USDC amount
329
- usdcAddress: USDC_MINT_ADDRESS,
330
- emailHash: Buffer.from('your-32-byte-hash'),
331
- currency: 'USD'
332
- });
333
-
334
- await payload.execute();
335
- ```
336
-
337
- #### Load a Carbon Card
338
-
339
- ```typescript
340
- const nextIndex = await vaultService.getNextCardIndex();
341
-
342
- const payload = await vaultService.loadCarbonCard({
343
- vaultOwnerAddress: wallet.publicKey,
344
- nextCardIndex: nextIndex,
345
- amount: 50,
346
- usdcAddress: USDC_MINT_ADDRESS,
347
- emailHash: Buffer.from('your-32-byte-hash'),
348
- currency: 'USD',
349
- reloadCardId: 'CARD_ID'
350
- });
351
-
352
- await payload.execute();
353
- ```
354
-
355
- #### Swap and Create Silver Card
356
-
357
- ```typescript
358
- // First, get a Jupiter quote
359
- const quoteResponse = await fetch(
360
- `https://quote-api.jup.ag/v6/quote?inputMint=${inputMint}&outputMint=${USDC}&amount=${amount}&slippageBps=50`
361
- );
362
- const quoteInfo = await quoteResponse.json();
363
-
364
- const payload = await vaultService.swapAndCreateSilverCard({
365
- vaultOwnerAddress: wallet.publicKey,
366
- quoteInfo: quoteInfo,
367
- nextCardCounter: nextIndex,
368
- emailHash: Buffer.from('your-32-byte-hash'),
369
- currency: 'USD',
370
- wrapAndUnwrapSol: true // if swapping SOL
371
- });
372
-
373
- await payload.execute();
374
- ```
375
-
376
- #### Swap and Load Carbon Card
377
-
378
- ```typescript
379
- const payload = await vaultService.swapAndLoadCarbonCard({
380
- vaultOwnerAddress: wallet.publicKey,
381
- quoteInfo: quoteInfo,
382
- nextCardCounter: nextIndex,
383
- emailHash: Buffer.from('your-32-byte-hash'),
384
- currency: 'USD',
385
- reloadCardId: 'CARD_ID',
386
- wrapAndUnwrapSol: true
387
- });
388
-
389
- await payload.execute();
390
- ```
391
-
392
- #### Get Card Custom Token Fees
393
-
394
- ```typescript
395
- const tokenFees = await vaultService.getCardCustomTokenFees();
396
-
397
- tokenFees.forEach(fee => {
398
- console.log(`Token: ${fee.tokenAddress}, Fee: ${fee.fee}%`);
399
- });
400
- ```
401
-
402
- ## Query Functions
403
-
404
- ### Get Vault Information
405
-
406
- ```typescript
407
- // Get specific user's vault
408
- const vaultInfo = await vaultService.getVaultInfoOfUser(userAddress);
409
-
410
- if (vaultInfo) {
411
- console.log('Vault:', vaultInfo.vault.toString());
412
- console.log('Owner:', vaultInfo.owner.toString());
413
- console.log('Created:', new Date(vaultInfo.createdDate * 1000));
414
- }
415
-
416
- // Get all vaults
417
- const allVaults = await vaultService.getAllVaultsInfo();
418
- console.log(`Total vaults: ${allVaults.length}`);
419
- ```
420
-
421
- ### Get Proposals
422
-
423
- ```typescript
424
- const proposals = await vaultService.getProposalsInfoOfVault(vaultAddress);
425
-
426
- proposals.forEach(proposal => {
427
- console.log('Proposal:', proposal.name);
428
- console.log('Status:', proposal.proposalStage);
429
- console.log('Actions:', proposal.actions.length);
430
- console.log('Executed:', proposal.isExecuted);
431
- });
432
- ```
433
-
434
- ## Readonly Provider
435
-
436
- For read-only operations without a wallet:
437
-
438
- ```typescript
439
- import { createReadonlyProvider } from '@zebec-network/zebec-vault-sdk';
440
-
441
- const readonlyProvider = createReadonlyProvider(
442
- connection,
443
- optionalWalletAddress
444
- );
445
-
446
- const service = ZebecVaultService.create(readonlyProvider, 'mainnet-beta');
447
-
448
- // Can only call query methods
449
- const vaultInfo = await service.getVaultInfoOfUser(someAddress);
450
- ```
451
-
452
- ## Error Handling
453
-
454
- The SDK provides custom error types:
455
-
456
- ```typescript
457
- import {
458
- AmountOutOfRangeError,
459
- DailyCardLimitReachedError,
460
- NotEnoughBalanceError,
461
- AssociatedTokenAccountDoesNotExistsError
462
- } from '@zebec-network/zebec-vault-sdk';
463
-
464
- try {
465
- await payload.execute();
466
- } catch (error) {
467
- if (error instanceof AmountOutOfRangeError) {
468
- console.error('Amount must be between', error.minRange, 'and', error.maxRange);
469
- } else if (error instanceof DailyCardLimitReachedError) {
470
- console.error('Daily limit:', error.dailyCardLimit);
471
- } else if (error instanceof NotEnoughBalanceError) {
472
- console.error('Insufficient balance');
473
- }
474
- }
475
- ```
476
-
477
- ## Advanced Features
478
-
479
- ### Program Derived Addresses (PDAs)
480
-
481
- ```typescript
482
- import {
483
- deriveUserVault,
484
- deriveVaultSigner,
485
- deriveStreamConfigPda,
486
- deriveCardConfigPda,
487
- deriveLockupAddress
488
- } from '@zebec-network/zebec-vault-sdk';
489
-
490
- const [vaultAddress, vaultBump] = deriveUserVault(
491
- userAddress,
492
- vaultProgramId
493
- );
494
-
495
- const [signerAddress, signerBump] = deriveVaultSigner(
496
- vaultAddress,
497
- vaultProgramId
498
- );
499
- ```
500
-
501
- ### Transaction Utilities
502
-
503
- ```typescript
504
- import {
505
- calculateProposalSize,
506
- transactionInstructionToProposalAction
507
- } from '@zebec-network/zebec-vault-sdk';
508
-
509
- // Calculate proposal size
510
- const size = calculateProposalSize('proposal-name', actions);
511
-
512
- // Convert instruction to proposal action
513
- const action = transactionInstructionToProposalAction(instruction);
514
- ```
515
-
516
- ## Network Configuration
517
-
518
- ```typescript
519
- // Mainnet
520
- const mainnetService = ZebecVaultService.create(provider, 'mainnet-beta');
521
-
522
- // Devnet
523
- const devnetService = ZebecVaultService.create(provider, 'devnet');
524
- ```
525
-
526
- ## Constants
527
-
528
- ```typescript
529
- import {
530
- ZEBEC_VAULT_PROGRAM_ID,
531
- JUPITER_AGGREGATOR_PROGRAM_ID,
532
- CARD_LOOKUP_TABLE_ADDRESS,
533
- STAKE_LOOKUP_TABLE_ADDRESS
534
- } from '@zebec-network/zebec-vault-sdk';
535
-
536
- console.log('Vault Program:', ZEBEC_VAULT_PROGRAM_ID['mainnet-beta']);
537
- ```
538
-
539
- ## Types
540
-
541
- The SDK exports comprehensive TypeScript types:
542
-
543
- ```typescript
544
- import type {
545
- VaultInfo,
546
- ProposalInfo,
547
- ProposalAction,
548
- CreateStreamFromVaultParams,
549
- StreamMetadataInfo,
550
- TokenFeeRecord,
551
- QuoteInfo,
552
- StakeUserNonceInfo
553
- } from '@zebec-network/zebec-vault-sdk';
554
- ```
555
-
556
- ## Best Practices
557
-
558
- 1. **Always check balances** before operations
559
- 2. **Use proposals** for critical operations requiring multiple approvals
560
- 3. **Handle errors** appropriately with the provided error types
561
- 4. **Verify stream parameters** before creation (duration, amounts, permissions)
562
- 5. **Test on devnet** before deploying to mainnet
563
- 6. **Use address lookup tables** for complex transactions to reduce size
564
-
565
- ## Support
566
-
567
- - **Documentation**: [Zebec Network Docs](https://docs.zebec.io)
568
- - **GitHub**: [zebec-network](https://github.com/zebec-network)
569
- - **Discord**: [Join our community](https://discord.gg/zebec)
570
-
571
- ## License
572
-
573
- MIT
574
-
575
- ## Contributing
576
-
577
- Contributions are welcome! Please read our contributing guidelines before submitting pull requests.
1
+ # Introduction
2
+
3
+ A Typescript SDK for interacting with Zebec Vault on Solana, enabling secure multi operations, token streaming, staking, and virtual card management.
4
+
5
+ # **Features**
6
+
7
+ - **Vault Management**: Create and manage secure vaults with proposal-based execution
8
+ - **Token Operations**: Deposit and withdraw SOL and SPL tokens
9
+ - **Streaming Payments**: Create, manage, and cancel payment streams
10
+ - **Staking**: Stake tokens with customizable lock periods
11
+ - **Virtual Cards**: Create and load Zebec cards with automatic token swapping
12
+ - **Jupiter Integration**: Built-in token swapping for card operations
13
+
14
+ # **Installation**
15
+
16
+ ```jsx
17
+ npm install @zebec-network/zebec-vault-sdk
18
+ ```
19
+
20
+ ```jsx
21
+ yarn add @zebec-network/zebec-vault-sdk
22
+ ```
23
+
24
+ # **Quick Start**
25
+
26
+ ## **Setup**
27
+
28
+ ```tsx
29
+ import { Connection, Keypair } from "@solana/web3.js";
30
+ import { createAnchorProvider, ZebecVaultService } from "@zebec-network/zebec-vault-sdk";
31
+
32
+ // Create connection
33
+ const connection = new Connection("https://api.mainnet-beta.solana.com"); // use private dedicatd rpc for production
34
+
35
+ // Create wallet adapter
36
+ // for frontend application you can use wallet provided by the wallet provider
37
+ // for example:
38
+ const wallet = useAnchorWallet();
39
+
40
+ // for server side app you can use Wallet class provided by @coral-xyz/anchor
41
+ const wallet = new Wallet(keypair); // create keypair from secret key
42
+
43
+ // Create provider
44
+ const provider = createAnchorProvider(connection, wallet);
45
+
46
+ // Initialize service
47
+ const vaultService = ZebecVaultService.create(provider, "mainnet-beta");
48
+ ```
49
+
50
+ ## Core Features
51
+
52
+ ### **Vault Operations**
53
+
54
+ Each user has a vault that is derived from the seed containing user’s wallet. Then a vault signer is derived from the vault taking it as a seed. The vault signer is responsible for holding SPL Tokens and Sol balances and signing the transaction during CPI executions.
55
+
56
+ **Create a vault**
57
+
58
+ ```jsx
59
+ const payload = await vaultService.createVault({
60
+ payer: wallet.publicKey, // optional if using AnchorProvider
61
+ });
62
+
63
+ const signature = await payload.execute();
64
+ console.log("Vault created:", signature);
65
+ ```
66
+
67
+ **Get Vault Information**
68
+
69
+ ```tsx
70
+ // Get specific user's vault
71
+ const vaultInfo = await vaultService.getVaultInfoOfUser(userAddress);
72
+
73
+ if (vaultInfo) {
74
+ console.log("Vault:", vaultInfo.vault.toString());
75
+ console.log("Owner:", vaultInfo.owner.toString());
76
+ console.log("Created:", new Date(vaultInfo.createdDate * 1000));
77
+ }
78
+
79
+ // Get all vaults
80
+ const allVaults = await vaultService.getAllVaultsInfo();
81
+ console.log(`Total vaults: ${allVaults.length}`);
82
+ ```
83
+
84
+ **Deposit SOL**
85
+
86
+ ```tsx
87
+ const payload = await vaultService.depositSol({
88
+ depositor: wallet.publicKey,
89
+ amount: 1.5, // SOL amount
90
+ });
91
+ ```
92
+
93
+ **Deposit SPL Tokens**
94
+
95
+ ```tsx
96
+ const payload = await vaultService.deposit({
97
+ depositor: wallet.publicKey,
98
+ tokenMint: "TOKEN_MINT_ADDRESS",
99
+ amount: 100, // token amount
100
+ });
101
+
102
+ const signature = await payload.execute();
103
+ ```
104
+
105
+ > ***Note: If WSOL address is given in tokenMint, it will wrap SOL to WSOL and deposit as spl token.***
106
+ >
107
+
108
+ **Withdraw SOL**
109
+
110
+ ```tsx
111
+ const payload = await vaultService.withdrawSol({
112
+ withdrawer: wallet.publicKey,
113
+ amount: 0.5,
114
+ });
115
+
116
+ const signature = await payload.execute();
117
+ ```
118
+
119
+ **Withdraw SPL Tokens**
120
+
121
+ ```tsx
122
+ const payload = await vaultService.withdraw({
123
+ withdrawer: wallet.publicKey,
124
+ tokenMint: "TOKEN_MINT_ADDRESS",
125
+ amount: 50,
126
+ });
127
+
128
+ const signature = await payload.execute();
129
+ ```
130
+
131
+ > ***Note: If WSOL is address in given in tokenMint, it will with withdraw WSOL and unwrap into withdrawer wallet.***
132
+ >
133
+
134
+ ### Proposal System
135
+
136
+ Proposal System includes methods like creating proposal, appending actions and executing proposal. A proposal is like a transaction in Solana but with extra fields like proposal stage, created date, expiry dates, execution status, name, etc. Each Proposal contains one or more Proposal Action which is similar to Transaction Instruction in Solana. While executing the proposal, the Proposal Actions get executed in order in which its resides inside a Proposal. You can delete the Proposal and claim SOLs used in creating the Proposal. Each Proposal belongs to a vault and only signer of that vault can execute the Proposal, delete or append any Proposal Actions in the Proposal.
137
+
138
+ **Create a Proposal**
139
+
140
+ ```tsx
141
+ import { SystemProgram, Keypair } from "@solana/web3.js";
142
+ import { parseSol } from "@zebec-network/solana-common";
143
+
144
+ const [vault] = deriveUserVault(wallet.publicKey, service.vaultV1ProgramId);
145
+ const [vaultSigner] = deriveVaultSigner(vault, service.vaultV1ProgramId);
146
+ const recipient = "<Recipient PublicKey>";
147
+
148
+ // Create custom instructions. For example:
149
+ const instruction = SystemProgram.transfer({
150
+ fromPubkey: vaultSigner,
151
+ toPubkey: recipient,
152
+ lamports: Number(parseSol(1)),
153
+ });
154
+
155
+ const proposalKeypair = Keypair.generate()
156
+ console.log("Proposal:", proposalKeypair.publicKey.toString());
157
+
158
+ const payload = await vaultService.createProposal({
159
+ proposer: wallet.publicKey,
160
+ name: "Transfer SOL",
161
+ actions: [instruction],
162
+ });
163
+
164
+ const signature = await payload.execute();
165
+ ```
166
+
167
+ ### **Get Proposals**
168
+
169
+ ```tsx
170
+ const proposals = await vaultService.getProposalsInfoOfVault(vaultAddress);
171
+
172
+ proposals.forEach((proposal) => {
173
+ console.log("Proposal:", proposal.name);
174
+ console.log("Status:", proposal.proposalStage);
175
+ console.log("Actions:", proposal.actions.length);
176
+ console.log("Executed:", proposal.isExecuted);
177
+ });
178
+ ```
179
+
180
+ **Append Actions**
181
+
182
+ ```tsx
183
+ import { SystemProgram } from "@solana/web3.js";
184
+ import { parseSol } from "@zebec-network/solana-common";
185
+
186
+ const proposal = "<Proposal PublicKey>";
187
+
188
+ const instruction = SystemProgram.transfer({
189
+ fromPubkey: vaultSigner,
190
+ toPubkey: wallet.publicKey,
191
+ lamports: Number(parseSol(0.01)),
192
+ });
193
+
194
+ const payload = await service.appendActions({
195
+ actions: actionsB,
196
+ proposal,
197
+ });
198
+
199
+ const signature = await payload.execute()
200
+ ```
201
+
202
+ **Delete Proposal**
203
+
204
+ ```tsx
205
+ const proposal = "<Proposal PublicKey>";
206
+ const payload = await service.deleteProposal({ proposal });
207
+ const deleteProposalSignature = await payload.execute();
208
+ ```
209
+
210
+ **Execute a Proposal**
211
+
212
+ ```tsx
213
+ const proposal = "<Proposal PublicKey>";
214
+ const lookupTableAddress = "<Lookup Table Address PublicKey>";
215
+
216
+ const payload = await vaultService.executeProposal({
217
+ caller: wallet.publicKey,
218
+ proposal,
219
+ addressLookupTables: [lookupTableAddress], // optional
220
+ });
221
+
222
+ const signature = await payload.execute();
223
+ ```
224
+
225
+ **Execute Proposal Directly (Single Transaction)**
226
+ Proposal Action can be directly executed without creating Proposal. However, it may cause transaction size limit while some size limit can be mitigated using address lookup table.
227
+
228
+ ```tsx
229
+ const [vault] = deriveUserVault(wallet.publicKey, service.vaultV1ProgramId);
230
+ const [vaultSigner] = deriveVaultSigner(vault, service.vaultV1ProgramId);
231
+ const recipient = "<Recipient PublicKey>";
232
+
233
+ const instruction1 = SystemProgram.transfer({
234
+ fromPubkey: vaultSigner,
235
+ toPubkey: recipient,
236
+ lamports: Number(parseSol(1)),
237
+ });
238
+
239
+ const instruction2 = new TransactionInstruction({
240
+ keys: [],
241
+ programId: MEMO_PROGRAM_ID,
242
+ data: Buffer.from("Transfer 1 sol from vault signer to recipient", "utf8"),
243
+ });
244
+
245
+ const payload = await vaultService.executeProposalDirect({
246
+ proposer: wallet.publicKey,
247
+ actions: [instruction1, instruction2],
248
+ addressLookupTables: [lookupTableAddress],
249
+ });
250
+
251
+ const signature = await payload.execute();
252
+ ```
253
+
254
+ ### **Payment Streaming**
255
+
256
+ **Create a Stream**
257
+
258
+ ```tsx
259
+ const payload = await vaultService.createStreamFromVault({
260
+ vaultOwner: wallet.publicKey,
261
+ receiver: recipientAddress,
262
+ streamToken: tokenMintAddress,
263
+ amount: 1000, // total amount
264
+ duration: 2592000, // 30 days in seconds
265
+ startNow: true,
266
+ startTime: Math.floor(Date.now() / 1000),
267
+ automaticWithdrawal: false,
268
+ cancelableByRecipient: true,
269
+ cancelableBySender: true,
270
+ isPausable: true,
271
+ transferableByRecipient: false,
272
+ transferableBySender: false,
273
+ canTopup: true,
274
+ rateUpdatable: false,
275
+ cliffPercentage: 0, // 0-100
276
+ autoWithdrawFrequency: 86400, // 1 day
277
+ streamName: "Monthly Payment",
278
+ });
279
+
280
+ const await payload.execute();
281
+ ```
282
+
283
+ **Create Multiple Streams**
284
+
285
+ There is a difference in payload that is return for creating multiple stream. While other payload is an instance of `TransactionPayload` class, the payload returned for creating multiple stream is an instance of `MultiTransactionPayload`. After invoking execute method in payload, it returns `MultiTransactionPayloadExecuteReturn` type which is an Array of union of `PromiseSettledResult<*string*>` and an object consisting of transactions data and transactions. The `PromiseSettledResult<*string*>` contains signature string as a value if the execution is success otherwise it it contains reason for the failure.
286
+
287
+ ```tsx
288
+ export type MultiTransactionPayloadExecuteReturn = (PromiseSettledResult<string> & {
289
+ transactionData: {
290
+ readonly instructions: web3.TransactionInstruction[];
291
+ readonly feePayer: web3.PublicKey;
292
+ readonly signers?: web3.Signer[];
293
+ readonly addressLookupTableAccounts?: web3.AddressLookupTableAccount[];
294
+ };
295
+ transaction: web3.VersionedTransaction;
296
+ })[];
297
+ ```
298
+
299
+ ```tsx
300
+ const payload = await vaultService.createMultipleStreamFromVault({
301
+ vaultOwner: wallet.publicKey,
302
+ streamInfo: [
303
+ {
304
+ receiver: recipient1,
305
+ streamToken: tokenMint,
306
+ amount: 500,
307
+ duration: 2592000,
308
+ // ... other stream parameters
309
+ },
310
+ {
311
+ receiver: recipient2,
312
+ streamToken: tokenMint,
313
+ amount: 1000,
314
+ duration: 2592000,
315
+ // ... other stream parameters
316
+ },
317
+ ],
318
+ });
319
+
320
+ // Execute all streams in multiple transactions
321
+ const result = await payload.execute();
322
+ ```
323
+
324
+ ### **Pause/Resume a Stream**
325
+
326
+ ```tsx
327
+ const payload = await vaultService.pauseResumeStream({
328
+ vaultOwner: wallet.publicKey,
329
+ streamMetadata: streamAddress,
330
+ });
331
+
332
+ const signature = await payload.execute();
333
+ ```
334
+
335
+ ### **Cancel a Stream**
336
+
337
+ ```tsx
338
+ const payload = await vaultService.cancelStream({
339
+ vaultOwner: wallet.publicKey,
340
+ streamMetadata: streamAddress,
341
+ });
342
+
343
+ await payload.execute();
344
+
345
+ ```
346
+
347
+ ### **Withdraw from Stream**
348
+
349
+ ```tsx
350
+ const payload = await vaultService.withdrawStream({
351
+ vaultOwner: wallet.publicKey,
352
+ streamMetadata: streamAddress,
353
+ });
354
+
355
+ await payload.execute();
356
+ ```
357
+
358
+ ### **Change Stream Receiver**
359
+
360
+ ```tsx
361
+ const payload = await vaultService.changeStreamReceiver({
362
+ vaultOwner: wallet.publicKey,
363
+ streamMetadata: streamAddress,
364
+ newRecipient: newRecipientAddress,
365
+ });
366
+
367
+ await payload.execute();
368
+
369
+ ```
370
+
371
+ ### **Get Stream Information**
372
+
373
+ ```tsx
374
+ const streamInfo = await vaultService.getStreamMetadataInfo(streamAddress);
375
+
376
+ console.log("Stream details:", {
377
+ sender: streamInfo.parties.sender,
378
+ receiver: streamInfo.parties.receiver,
379
+ token: streamInfo.financials.streamToken,
380
+ deposited: streamInfo.financials.depositedAmount,
381
+ withdrawn: streamInfo.financials.withdrawnAmount,
382
+ startTime: new Date(streamInfo.schedule.startTime * 1000),
383
+ endTime: new Date(streamInfo.schedule.endTime * 1000),
384
+ isPaused: streamInfo.schedule.pausedTimestamp > 0,
385
+ });
386
+ ```
387
+
388
+ ### Staking
389
+
390
+ **Stake Tokens**
391
+
392
+ ```tsx
393
+ const payload = await vaultService.stake({
394
+ lockupName: "main-lockup",
395
+ vaultOwner: wallet.publicKey,
396
+ amount: 1000,
397
+ lockPeriod: 7776000, // 90 days in seconds
398
+ nonce: 0n,
399
+ });
400
+
401
+ await payload.execute();
402
+
403
+ ```
404
+
405
+ **Unstake Tokens**
406
+
407
+ ```tsx
408
+ const payload = await vaultService.unstake({
409
+ lockupName: "main-lockup",
410
+ vaultOwner: wallet.publicKey,
411
+ nonce: 0n,
412
+ });
413
+
414
+ await payload.execute();
415
+
416
+ ```
417
+
418
+ **Get Stake Nonce Information**
419
+
420
+ ```tsx
421
+ const nonceInfo = await vaultService.getStakeUserNonceInfo("main-lockup", wallet.publicKey);
422
+
423
+ console.log("Current nonce:", nonceInfo ? nonceInfo.nonce : 0n);
424
+ ```
425
+
426
+ ### Zebec Cards
427
+
428
+ **Create a Silver Card**
429
+
430
+ ```tsx
431
+ const nextIndex = await vaultService.getNextCardIndex();
432
+
433
+ const payload = await vaultService.createSilverCard({
434
+ vaultOwnerAddress: wallet.publicKey,
435
+ nextCardIndex: nextIndex,
436
+ amount: 100, // USDC amount
437
+ usdcAddress: USDC_MINT_ADDRESS,
438
+ emailHash: Buffer.from("your-32-byte-hash"),
439
+ currency: "USD",
440
+ });
441
+
442
+ await payload.execute();
443
+
444
+ ```
445
+
446
+ **Load a Carbon Card**
447
+
448
+ ```tsx
449
+ const nextIndex = await vaultService.getNextCardIndex();
450
+
451
+ const payload = await vaultService.loadCarbonCard({
452
+ vaultOwnerAddress: wallet.publicKey,
453
+ nextCardIndex: nextIndex,
454
+ amount: 50,
455
+ usdcAddress: USDC_MINT_ADDRESS,
456
+ emailHash: Buffer.from("your-32-byte-hash"),
457
+ currency: "USD",
458
+ reloadCardId: "CARD_ID",
459
+ });
460
+
461
+ await payload.execute();
462
+
463
+ ```
464
+
465
+ **Swap and Create Silver Card**
466
+
467
+ ```tsx
468
+ // First, get a Jupiter quote
469
+ const quoteResponse = await fetch(
470
+ `https://quote-api.jup.ag/v6/quote?inputMint=${inputMint}&outputMint=${USDC}&amount=${amount}&slippageBps=50`,
471
+ );
472
+ const quoteInfo = await quoteResponse.json();
473
+
474
+ const payload = await vaultService.swapAndCreateSilverCard({
475
+ vaultOwnerAddress: wallet.publicKey,
476
+ quoteInfo: quoteInfo,
477
+ nextCardCounter: nextIndex,
478
+ emailHash: Buffer.from("your-32-byte-hash"),
479
+ currency: "USD",
480
+ wrapAndUnwrapSol: true, // if swapping SOL
481
+ });
482
+
483
+ await payload.execute();
484
+
485
+ ```
486
+
487
+ **Swap and Load Carbon Card**
488
+
489
+ ```tsx
490
+ const payload = await vaultService.swapAndLoadCarbonCard({
491
+ vaultOwnerAddress: wallet.publicKey,
492
+ quoteInfo: quoteInfo,
493
+ nextCardCounter: nextIndex,
494
+ emailHash: Buffer.from("your-32-byte-hash"),
495
+ currency: "USD",
496
+ reloadCardId: "CARD_ID",
497
+ wrapAndUnwrapSol: true,
498
+ });
499
+
500
+ await payload.execute();
501
+ ```
502
+
503
+ **Get Card Custom Token Fees**
504
+
505
+ ```tsx
506
+ const tokenFees = await vaultService.getCardCustomTokenFees();
507
+
508
+ tokenFees.forEach((fee) => {
509
+ console.log(`Token: ${fee.tokenAddress}, Fee: ${fee.fee}%`);
510
+ });
511
+ ```
512
+
513
+ ### **Read-only Provider**
514
+
515
+ For read-only operations without a wallet:
516
+
517
+ ```tsx
518
+ import { createReadonlyProvider } from "@zebec-network/zebec-vault-sdk";
519
+
520
+ const readonlyProvider = createReadonlyProvider(connection, optionalWalletAddress);
521
+
522
+ const service = ZebecVaultService.create(readonlyProvider, "mainnet-beta");
523
+
524
+ // Can only call query methods
525
+ const vaultInfo = await service.getVaultInfoOfUser(someAddress);
526
+ ```
527
+
528
+ ### **Advanced Features**
529
+
530
+ **Program Derived Addresses (PDAs)**
531
+
532
+ ```tsx
533
+ import {
534
+ deriveUserVault,
535
+ deriveVaultSigner,
536
+ deriveStreamConfigPda,
537
+ deriveCardConfigPda,
538
+ deriveLockupAddress,
539
+ } from "@zebec-network/zebec-vault-sdk";
540
+
541
+ const [vaultAddress, vaultBump] = deriveUserVault(userAddress, vaultProgramId);
542
+
543
+ const [signerAddress, signerBump] = deriveVaultSigner(vaultAddress, vaultProgramId);
544
+ ```
545
+
546
+ **Transaction Utilities**
547
+
548
+ ```tsx
549
+ import { calculateProposalSize, transactionInstructionToProposalAction } from "@zebec-network/zebec-vault-sdk";
550
+
551
+ // Calculate proposal size
552
+ const size = calculateProposalSize("proposal-name", actions);
553
+
554
+ // Convert instruction to proposal action
555
+ const action = transactionInstructionToProposalAction(instruction);
556
+
557
+ ```
558
+
559
+ **Network Configuration**
560
+
561
+ ```tsx
562
+ // Mainnet
563
+ const mainnetService = ZebecVaultService.create(provider, "mainnet-beta");
564
+
565
+ // Devnet
566
+ const devnetService = ZebecVaultService.create(provider, "devnet");
567
+ ```
568
+
569
+ ### **Constants**
570
+
571
+ ```tsx
572
+ import {
573
+ ZEBEC_VAULT_PROGRAM_ID,
574
+ JUPITER_AGGREGATOR_PROGRAM_ID,
575
+ CARD_LOOKUP_TABLE_ADDRESS,
576
+ STAKE_LOOKUP_TABLE_ADDRESS,
577
+ } from "@zebec-network/zebec-vault-sdk";
578
+
579
+ console.log("Vault Program:", ZEBEC_VAULT_PROGRAM_ID["mainnet-beta"]);
580
+
581
+ ```
582
+
583
+ ### **Types**
584
+
585
+ The SDK exports comprehensive TypeScript types:
586
+
587
+ ```tsx
588
+ import type {
589
+ VaultInfo,
590
+ ProposalInfo,
591
+ ProposalAction,
592
+ CreateStreamFromVaultParams,
593
+ StreamMetadataInfo,
594
+ TokenFeeRecord,
595
+ QuoteInfo,
596
+ StakeUserNonceInfo,
597
+ } from "@zebec-network/zebec-vault-sdk";
598
+ ```
599
+
600
+ ## **Best Practices**
601
+
602
+ 1. **Always check balances** before operations
603
+ 2. **Use proposals** for critical operations that require multiple instructions and cannot be executed in a single transaction.
604
+ 3. **Handle errors** appropriately with the provided error types
605
+ 4. **Verify stream parameters** before creation (duration, amounts, permissions)
606
+ 5. **Test on `devnet`** before deploying to `mainnet`
607
+ 6. **Use address lookup tables** for complex transactions to reduce size
package/dist/service.js CHANGED
@@ -1006,17 +1006,21 @@ class ZebecVaultService {
1006
1006
  const STREAM_NAME_BUFFER_SIZE = 128;
1007
1007
  const streamNameArray = new Uint8Array(STREAM_NAME_BUFFER_SIZE);
1008
1008
  streamNameArray.set(anchor_1.utils.bytes.utf8.encode(params.streamName));
1009
+ const autoWithdrawFrequency = params.autoWithdrawFrequency ?? 0;
1010
+ if (params.automaticWithdrawal && autoWithdrawFrequency <= 0) {
1011
+ throw new Error("autoWithdrawFrequency must be greater than 0 when automaticWithdrawal is enabled");
1012
+ }
1009
1013
  const ix = await this.getCreateStreamFromVaultInstruction(vaultOwner, vault, vaultSigner, vaultSignerAta, receiverVaultSigner, receiverVaultSignerAta, streamToken, streamMetadata, streamConfig, withdrawAccount, streamVault, streamVaultAta, this.streamProgramId, {
1010
1014
  amount,
1011
1015
  automaticWithdrawal: params.automaticWithdrawal,
1012
- autoWithdrawFrequency: new anchor_1.BN(params.autoWithdrawFrequency),
1016
+ autoWithdrawFrequency: new anchor_1.BN(autoWithdrawFrequency),
1013
1017
  cancelableByRecipient: params.cancelableByRecipient,
1014
1018
  cancelableBySender: params.cancelableBySender,
1015
1019
  canTopup: params.canTopup,
1016
1020
  cliffPercentage,
1017
1021
  duration: new anchor_1.BN(params.duration),
1018
1022
  isPausable: params.isPausable,
1019
- numberOfWithdrawls: new anchor_1.BN(Math.floor(params.duration / params.autoWithdrawFrequency)),
1023
+ numberOfWithdrawls: autoWithdrawFrequency > 0 ? new anchor_1.BN(Math.floor(params.duration / autoWithdrawFrequency)) : new anchor_1.BN(0),
1020
1024
  rateUpdatable: params.rateUpdatable,
1021
1025
  startNow: params.startNow,
1022
1026
  startTime: new anchor_1.BN(params.startTime),
@@ -1050,17 +1054,21 @@ class ZebecVaultService {
1050
1054
  const STREAM_NAME_BUFFER_SIZE = 128;
1051
1055
  const streamNameArray = new Uint8Array(STREAM_NAME_BUFFER_SIZE);
1052
1056
  streamNameArray.set(anchor_1.utils.bytes.utf8.encode(info.streamName));
1057
+ const autoWithdrawFrequency = info.autoWithdrawFrequency ?? 0;
1058
+ if (info.automaticWithdrawal && autoWithdrawFrequency <= 0) {
1059
+ throw new Error("autoWithdrawFrequency must be greater than 0 when automaticWithdrawal is enabled");
1060
+ }
1053
1061
  const ix = await this.getCreateStreamFromVaultInstruction(vaultOwner, vault, vaultSigner, vaultSignerAta, receiverVaultSigner, receiverVaultSignerAta, streamToken, streamMetadata, streamConfig, withdrawAccount, streamVault, streamVaultAta, this.streamProgramId, {
1054
1062
  amount,
1055
1063
  automaticWithdrawal: info.automaticWithdrawal,
1056
- autoWithdrawFrequency: new anchor_1.BN(info.autoWithdrawFrequency),
1064
+ autoWithdrawFrequency: new anchor_1.BN(autoWithdrawFrequency),
1057
1065
  cancelableByRecipient: info.cancelableByRecipient,
1058
1066
  cancelableBySender: info.cancelableBySender,
1059
1067
  canTopup: info.canTopup,
1060
1068
  cliffPercentage,
1061
1069
  duration: new anchor_1.BN(info.duration),
1062
1070
  isPausable: info.isPausable,
1063
- numberOfWithdrawls: new anchor_1.BN(Math.floor(info.duration / info.autoWithdrawFrequency)),
1071
+ numberOfWithdrawls: autoWithdrawFrequency > 0 ? new anchor_1.BN(Math.floor(info.duration / autoWithdrawFrequency)) : new anchor_1.BN(0),
1064
1072
  rateUpdatable: info.rateUpdatable,
1065
1073
  startNow: info.startNow,
1066
1074
  startTime: new anchor_1.BN(info.startTime),
package/dist/types.d.ts CHANGED
@@ -172,7 +172,7 @@ export type CreateStreamFromVaultParams = {
172
172
  isPausable: boolean;
173
173
  startNow: boolean;
174
174
  startTime: number;
175
- autoWithdrawFrequency: number;
175
+ autoWithdrawFrequency?: number;
176
176
  streamName: string;
177
177
  transferableByRecipient: boolean;
178
178
  transferableBySender: boolean;
@@ -194,7 +194,7 @@ export type createMultipleStreamFromVaultParams = {
194
194
  isPausable: boolean;
195
195
  startNow: boolean;
196
196
  startTime: number;
197
- autoWithdrawFrequency: number;
197
+ autoWithdrawFrequency?: number;
198
198
  streamName: string;
199
199
  transferableByRecipient: boolean;
200
200
  transferableBySender: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zebec-network/zebec-vault-sdk",
3
- "version": "5.0.3",
3
+ "version": "5.1.0",
4
4
  "description": "An SDK for zebec vault solana program",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -41,7 +41,7 @@
41
41
  "@solana/web3.js": "^1.98.2",
42
42
  "@types/bn.js": "^5.2.0",
43
43
  "@zebec-network/core-utils": "^1.1.1",
44
- "@zebec-network/solana-common": "^2.3.0",
44
+ "@zebec-network/solana-common": "^2.3.1",
45
45
  "bignumber.js": "^9.3.1",
46
46
  "buffer": "^6.0.3"
47
47
  }