@zebec-network/zebec-vault-sdk 5.2.1 → 5.2.3
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 +294 -215
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A Typescript SDK for interacting with Zebec Vault on Solana, enabling secure multi operations, token streaming, staking, and virtual card management.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## **Features**
|
|
6
6
|
|
|
7
7
|
- **Vault Management**: Create and manage secure vaults with proposal-based execution
|
|
8
8
|
- **Token Operations**: Deposit and withdraw SOL and SPL tokens
|
|
@@ -11,7 +11,7 @@ A Typescript SDK for interacting with Zebec Vault on Solana, enabling secure mul
|
|
|
11
11
|
- **Virtual Cards**: Create and load Zebec cards with automatic token swapping
|
|
12
12
|
- **Jupiter Integration**: Built-in token swapping for card operations
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
## **Installation**
|
|
15
15
|
|
|
16
16
|
```jsx
|
|
17
17
|
npm install @zebec-network/zebec-vault-sdk
|
|
@@ -21,23 +21,22 @@ npm install @zebec-network/zebec-vault-sdk
|
|
|
21
21
|
yarn add @zebec-network/zebec-vault-sdk
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
## **Quick Start**
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
### **Setup**
|
|
27
27
|
|
|
28
28
|
```tsx
|
|
29
29
|
import { Connection, Keypair } from "@solana/web3.js";
|
|
30
30
|
import { createAnchorProvider, ZebecVaultService } from "@zebec-network/zebec-vault-sdk";
|
|
31
31
|
|
|
32
32
|
// Create connection
|
|
33
|
-
const connection = new Connection("https://api.mainnet-beta.solana.com"); // use private
|
|
33
|
+
const connection = new Connection("https://api.mainnet-beta.solana.com"); // use a private dedicated RPC for production
|
|
34
34
|
|
|
35
35
|
// Create wallet adapter
|
|
36
|
-
//
|
|
37
|
-
// for example:
|
|
36
|
+
// For a frontend application, use the wallet provided by the wallet adapter:
|
|
38
37
|
const wallet = useAnchorWallet();
|
|
39
38
|
|
|
40
|
-
//
|
|
39
|
+
// For a server-side app, use the Wallet class from @coral-xyz/anchor:
|
|
41
40
|
const wallet = new Wallet(keypair); // create keypair from secret key
|
|
42
41
|
|
|
43
42
|
// Create provider
|
|
@@ -51,29 +50,29 @@ const vaultService = ZebecVaultService.create(provider, "mainnet-beta");
|
|
|
51
50
|
|
|
52
51
|
### **Vault Operations**
|
|
53
52
|
|
|
54
|
-
Each user has a vault that is derived from
|
|
53
|
+
Each user has a vault that is derived from a seed containing the user's wallet. A vault signer is then derived from the vault as a seed. The vault signer holds SPL token and SOL balances and signs transactions during CPI executions.
|
|
55
54
|
|
|
56
|
-
|
|
55
|
+
#### Create a vault
|
|
57
56
|
|
|
58
|
-
```
|
|
57
|
+
```tsx
|
|
59
58
|
const payload = await vaultService.createVault({
|
|
60
|
-
|
|
59
|
+
payer: wallet.publicKey, // optional if using AnchorProvider
|
|
61
60
|
});
|
|
62
61
|
|
|
63
62
|
const signature = await payload.execute();
|
|
64
63
|
console.log("Vault created:", signature);
|
|
65
64
|
```
|
|
66
65
|
|
|
67
|
-
|
|
66
|
+
#### Get Vault Information
|
|
68
67
|
|
|
69
68
|
```tsx
|
|
70
|
-
// Get specific user's vault
|
|
69
|
+
// Get a specific user's vault (param is optional when using AnchorProvider)
|
|
71
70
|
const vaultInfo = await vaultService.getVaultInfoOfUser(userAddress);
|
|
72
71
|
|
|
73
72
|
if (vaultInfo) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
console.log("Vault:", vaultInfo.vault.toString());
|
|
74
|
+
console.log("Owner:", vaultInfo.owner.toString());
|
|
75
|
+
console.log("Created:", new Date(vaultInfo.createdDate * 1000));
|
|
77
76
|
}
|
|
78
77
|
|
|
79
78
|
// Get all vaults
|
|
@@ -81,101 +80,102 @@ const allVaults = await vaultService.getAllVaultsInfo();
|
|
|
81
80
|
console.log(`Total vaults: ${allVaults.length}`);
|
|
82
81
|
```
|
|
83
82
|
|
|
84
|
-
|
|
83
|
+
#### Deposit SOL
|
|
85
84
|
|
|
86
85
|
```tsx
|
|
87
86
|
const payload = await vaultService.depositSol({
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
depositor: wallet.publicKey,
|
|
88
|
+
amount: 1.5, // SOL amount
|
|
90
89
|
});
|
|
90
|
+
|
|
91
|
+
const signature = await payload.execute();
|
|
91
92
|
```
|
|
92
93
|
|
|
93
|
-
|
|
94
|
+
#### Deposit SPL Tokens
|
|
94
95
|
|
|
95
96
|
```tsx
|
|
96
97
|
const payload = await vaultService.deposit({
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
depositor: wallet.publicKey,
|
|
99
|
+
tokenMint: "TOKEN_MINT_ADDRESS",
|
|
100
|
+
amount: 100, // token amount
|
|
100
101
|
});
|
|
101
102
|
|
|
102
103
|
const signature = await payload.execute();
|
|
103
104
|
```
|
|
104
105
|
|
|
105
|
-
> **_Note: If WSOL address is given
|
|
106
|
+
> **_Note: If the WSOL address is given as `tokenMint`, SOL will be wrapped to WSOL and deposited as an SPL token._**
|
|
106
107
|
|
|
107
|
-
|
|
108
|
+
#### Withdraw SOL
|
|
108
109
|
|
|
109
110
|
```tsx
|
|
110
111
|
const payload = await vaultService.withdrawSol({
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
withdrawer: wallet.publicKey,
|
|
113
|
+
amount: 0.5,
|
|
113
114
|
});
|
|
114
115
|
|
|
115
116
|
const signature = await payload.execute();
|
|
116
117
|
```
|
|
117
118
|
|
|
118
|
-
|
|
119
|
+
#### Withdraw SPL Tokens
|
|
119
120
|
|
|
120
121
|
```tsx
|
|
121
122
|
const payload = await vaultService.withdraw({
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
withdrawer: wallet.publicKey,
|
|
124
|
+
tokenMint: "TOKEN_MINT_ADDRESS",
|
|
125
|
+
amount: 50,
|
|
125
126
|
});
|
|
126
127
|
|
|
127
128
|
const signature = await payload.execute();
|
|
128
129
|
```
|
|
129
130
|
|
|
130
|
-
> **_Note: If WSOL
|
|
131
|
+
> **_Note: If the WSOL address is given as `tokenMint`, WSOL will be withdrawn and unwrapped into the withdrawer's wallet._**
|
|
131
132
|
|
|
132
133
|
### Proposal System
|
|
133
134
|
|
|
134
|
-
Proposal System includes methods
|
|
135
|
+
The Proposal System includes methods for creating proposals, appending actions, and executing proposals. A proposal is like a Solana transaction but with extra fields such as proposal stage, created date, expiry date, execution status, and name. Each proposal contains one or more `ProposalAction`, which is similar to a `TransactionInstruction` in Solana. During execution, proposal actions run in order. You can delete a proposal to reclaim the SOL rent used to create it. Each proposal belongs to a vault and only the vault signer can execute, delete, or append actions to it.
|
|
135
136
|
|
|
136
|
-
|
|
137
|
+
#### Create a Proposal
|
|
137
138
|
|
|
138
139
|
```tsx
|
|
139
|
-
import { SystemProgram
|
|
140
|
+
import { SystemProgram } from "@solana/web3.js";
|
|
140
141
|
import { parseSol } from "@zebec-network/solana-common";
|
|
142
|
+
import { deriveUserVault, deriveVaultSigner } from "@zebec-network/zebec-vault-sdk";
|
|
141
143
|
|
|
142
|
-
const [vault] = deriveUserVault(wallet.publicKey,
|
|
143
|
-
const [vaultSigner] = deriveVaultSigner(vault,
|
|
144
|
+
const [vault] = deriveUserVault(wallet.publicKey, vaultService.vaultV1ProgramId);
|
|
145
|
+
const [vaultSigner] = deriveVaultSigner(vault, vaultService.vaultV1ProgramId);
|
|
144
146
|
const recipient = "<Recipient PublicKey>";
|
|
145
147
|
|
|
146
148
|
// Create custom instructions. For example:
|
|
147
149
|
const instruction = SystemProgram.transfer({
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
fromPubkey: vaultSigner,
|
|
151
|
+
toPubkey: recipient,
|
|
152
|
+
lamports: Number(parseSol(1)),
|
|
151
153
|
});
|
|
152
154
|
|
|
153
|
-
const proposalKeypair = Keypair.generate();
|
|
154
|
-
console.log("Proposal:", proposalKeypair.publicKey.toString());
|
|
155
|
-
|
|
156
155
|
const payload = await vaultService.createProposal({
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
156
|
+
proposer: wallet.publicKey,
|
|
157
|
+
name: "Transfer SOL",
|
|
158
|
+
actions: [instruction],
|
|
159
|
+
// proposalKeypair: Keypair.generate(), // optional; auto-generated if omitted
|
|
160
160
|
});
|
|
161
161
|
|
|
162
162
|
const signature = await payload.execute();
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
#### Get Proposals
|
|
166
166
|
|
|
167
167
|
```tsx
|
|
168
168
|
const proposals = await vaultService.getProposalsInfoOfVault(vaultAddress);
|
|
169
169
|
|
|
170
170
|
proposals.forEach((proposal) => {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
171
|
+
console.log("Proposal:", proposal.name);
|
|
172
|
+
console.log("Status:", proposal.proposalStage);
|
|
173
|
+
console.log("Actions:", proposal.actions.length);
|
|
174
|
+
console.log("Executed:", proposal.isExecuted);
|
|
175
175
|
});
|
|
176
176
|
```
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
#### Append Actions
|
|
179
179
|
|
|
180
180
|
```tsx
|
|
181
181
|
import { SystemProgram } from "@solana/web3.js";
|
|
@@ -184,66 +184,71 @@ import { parseSol } from "@zebec-network/solana-common";
|
|
|
184
184
|
const proposal = "<Proposal PublicKey>";
|
|
185
185
|
|
|
186
186
|
const instruction = SystemProgram.transfer({
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
187
|
+
fromPubkey: vaultSigner,
|
|
188
|
+
toPubkey: wallet.publicKey,
|
|
189
|
+
lamports: Number(parseSol(0.01)),
|
|
190
190
|
});
|
|
191
191
|
|
|
192
|
-
const payload = await
|
|
193
|
-
|
|
194
|
-
|
|
192
|
+
const payload = await vaultService.appendActions({
|
|
193
|
+
proposal,
|
|
194
|
+
actions: [instruction],
|
|
195
195
|
});
|
|
196
196
|
|
|
197
197
|
const signature = await payload.execute();
|
|
198
198
|
```
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
#### Delete Proposal
|
|
201
201
|
|
|
202
202
|
```tsx
|
|
203
203
|
const proposal = "<Proposal PublicKey>";
|
|
204
|
-
const payload = await
|
|
204
|
+
const payload = await vaultService.deleteProposal({ proposal });
|
|
205
205
|
const deleteProposalSignature = await payload.execute();
|
|
206
206
|
```
|
|
207
207
|
|
|
208
|
-
|
|
208
|
+
#### Execute a Proposal
|
|
209
209
|
|
|
210
210
|
```tsx
|
|
211
211
|
const proposal = "<Proposal PublicKey>";
|
|
212
212
|
const lookupTableAddress = "<Lookup Table Address PublicKey>";
|
|
213
213
|
|
|
214
214
|
const payload = await vaultService.executeProposal({
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
215
|
+
caller: wallet.publicKey,
|
|
216
|
+
proposal,
|
|
217
|
+
addressLookupTables: [lookupTableAddress], // optional
|
|
218
218
|
});
|
|
219
219
|
|
|
220
220
|
const signature = await payload.execute();
|
|
221
221
|
```
|
|
222
222
|
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
#### Execute Proposal Directly (Single Transaction)
|
|
224
|
+
|
|
225
|
+
Proposal actions can be executed directly without creating a persistent proposal. However, this may hit the transaction size limit; some cases can be mitigated using address lookup tables.
|
|
225
226
|
|
|
226
227
|
```tsx
|
|
227
|
-
|
|
228
|
-
|
|
228
|
+
import { SystemProgram, TransactionInstruction } from "@solana/web3.js";
|
|
229
|
+
import { parseSol } from "@zebec-network/solana-common";
|
|
230
|
+
import { deriveUserVault, deriveVaultSigner } from "@zebec-network/zebec-vault-sdk";
|
|
231
|
+
|
|
232
|
+
const [vault] = deriveUserVault(wallet.publicKey, vaultService.vaultV1ProgramId);
|
|
233
|
+
const [vaultSigner] = deriveVaultSigner(vault, vaultService.vaultV1ProgramId);
|
|
229
234
|
const recipient = "<Recipient PublicKey>";
|
|
230
235
|
|
|
231
236
|
const instruction1 = SystemProgram.transfer({
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
237
|
+
fromPubkey: vaultSigner,
|
|
238
|
+
toPubkey: recipient,
|
|
239
|
+
lamports: Number(parseSol(1)),
|
|
235
240
|
});
|
|
236
241
|
|
|
237
242
|
const instruction2 = new TransactionInstruction({
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
243
|
+
keys: [],
|
|
244
|
+
programId: MEMO_PROGRAM_ID,
|
|
245
|
+
data: Buffer.from("Transfer 1 sol from vault signer to recipient", "utf8"),
|
|
241
246
|
});
|
|
242
247
|
|
|
243
248
|
const payload = await vaultService.executeProposalDirect({
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
249
|
+
proposer: wallet.publicKey,
|
|
250
|
+
actions: [instruction1, instruction2],
|
|
251
|
+
addressLookupTables: [lookupTableAddress],
|
|
247
252
|
});
|
|
248
253
|
|
|
249
254
|
const signature = await payload.execute();
|
|
@@ -251,80 +256,104 @@ const signature = await payload.execute();
|
|
|
251
256
|
|
|
252
257
|
### **Payment Streaming**
|
|
253
258
|
|
|
254
|
-
|
|
259
|
+
#### Create a Stream
|
|
255
260
|
|
|
256
261
|
```tsx
|
|
257
262
|
const payload = await vaultService.createStreamFromVault({
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
263
|
+
vaultOwner: wallet.publicKey,
|
|
264
|
+
receiver: recipientAddress,
|
|
265
|
+
streamToken: tokenMintAddress,
|
|
266
|
+
streamConfigName: "Config-001", // name of the stream configuration to use
|
|
267
|
+
amount: 1000, // total amount
|
|
268
|
+
duration: 2592000, // 30 days in seconds
|
|
269
|
+
startNow: true,
|
|
270
|
+
startTime: Math.floor(Date.now() / 1000),
|
|
271
|
+
automaticWithdrawal: false,
|
|
272
|
+
cancelableByRecipient: true,
|
|
273
|
+
cancelableBySender: true,
|
|
274
|
+
isPausable: true,
|
|
275
|
+
transferableByRecipient: false,
|
|
276
|
+
transferableBySender: false,
|
|
277
|
+
canTopup: true,
|
|
278
|
+
rateUpdatable: false,
|
|
279
|
+
cliffPercentage: 0, // 0-100
|
|
280
|
+
autoWithdrawFrequency: 86400, // 1 day in seconds; required when automaticWithdrawal is true
|
|
281
|
+
streamName: "Monthly Payment",
|
|
276
282
|
});
|
|
277
283
|
|
|
278
|
-
const await payload.execute();
|
|
284
|
+
const signature = await payload.execute();
|
|
279
285
|
```
|
|
280
286
|
|
|
281
|
-
|
|
287
|
+
#### Create Multiple Streams
|
|
282
288
|
|
|
283
|
-
There is a difference in payload
|
|
289
|
+
There is a difference in the payload returned for creating multiple streams. While other payloads are instances of `TransactionPayload`, the payload returned for multiple streams is an instance of `MultiTransactionPayload`. After calling `execute()`, it returns `MultiTransactionPayloadExecuteReturn`, which is an array of settled promise results — each containing either a transaction signature on success or a failure reason.
|
|
284
290
|
|
|
285
291
|
```tsx
|
|
286
292
|
export type MultiTransactionPayloadExecuteReturn = (PromiseSettledResult<string> & {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
293
|
+
transactionData: {
|
|
294
|
+
readonly instructions: web3.TransactionInstruction[];
|
|
295
|
+
readonly feePayer: web3.PublicKey;
|
|
296
|
+
readonly signers?: web3.Signer[];
|
|
297
|
+
readonly addressLookupTableAccounts?: web3.AddressLookupTableAccount[];
|
|
298
|
+
};
|
|
299
|
+
transaction: web3.VersionedTransaction;
|
|
294
300
|
})[];
|
|
295
301
|
```
|
|
296
302
|
|
|
297
303
|
```tsx
|
|
298
304
|
const payload = await vaultService.createMultipleStreamFromVault({
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
305
|
+
vaultOwner: wallet.publicKey,
|
|
306
|
+
streamConfigName: "Config-001", // applies to all streams
|
|
307
|
+
streamInfo: [
|
|
308
|
+
{
|
|
309
|
+
receiver: recipient1,
|
|
310
|
+
streamToken: tokenMint,
|
|
311
|
+
amount: 500,
|
|
312
|
+
duration: 2592000,
|
|
313
|
+
startNow: false,
|
|
314
|
+
startTime: Math.floor(Date.now() / 1000) + 30,
|
|
315
|
+
automaticWithdrawal: false,
|
|
316
|
+
cancelableByRecipient: false,
|
|
317
|
+
cancelableBySender: false,
|
|
318
|
+
isPausable: false,
|
|
319
|
+
transferableByRecipient: false,
|
|
320
|
+
transferableBySender: false,
|
|
321
|
+
canTopup: false,
|
|
322
|
+
rateUpdatable: false,
|
|
323
|
+
cliffPercentage: 0,
|
|
324
|
+
streamName: "Stream 1",
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
receiver: recipient2,
|
|
328
|
+
streamToken: tokenMint,
|
|
329
|
+
amount: 1000,
|
|
330
|
+
duration: 2592000,
|
|
331
|
+
startNow: false,
|
|
332
|
+
startTime: Math.floor(Date.now() / 1000) + 30,
|
|
333
|
+
automaticWithdrawal: false,
|
|
334
|
+
cancelableByRecipient: false,
|
|
335
|
+
cancelableBySender: false,
|
|
336
|
+
isPausable: false,
|
|
337
|
+
transferableByRecipient: false,
|
|
338
|
+
transferableBySender: false,
|
|
339
|
+
canTopup: false,
|
|
340
|
+
rateUpdatable: false,
|
|
341
|
+
cliffPercentage: 0,
|
|
342
|
+
streamName: "Stream 2",
|
|
343
|
+
},
|
|
344
|
+
],
|
|
316
345
|
});
|
|
317
346
|
|
|
318
|
-
// Execute all streams in
|
|
319
|
-
const
|
|
347
|
+
// Execute all streams in separate transactions
|
|
348
|
+
const results = await payload.execute();
|
|
320
349
|
```
|
|
321
350
|
|
|
322
351
|
### **Pause/Resume a Stream**
|
|
323
352
|
|
|
324
353
|
```tsx
|
|
325
354
|
const payload = await vaultService.pauseResumeStream({
|
|
326
|
-
|
|
327
|
-
|
|
355
|
+
vaultOwner: wallet.publicKey,
|
|
356
|
+
streamMetadata: streamAddress,
|
|
328
357
|
});
|
|
329
358
|
|
|
330
359
|
const signature = await payload.execute();
|
|
@@ -334,8 +363,8 @@ const signature = await payload.execute();
|
|
|
334
363
|
|
|
335
364
|
```tsx
|
|
336
365
|
const payload = await vaultService.cancelStream({
|
|
337
|
-
|
|
338
|
-
|
|
366
|
+
vaultOwner: wallet.publicKey,
|
|
367
|
+
streamMetadata: streamAddress,
|
|
339
368
|
});
|
|
340
369
|
|
|
341
370
|
await payload.execute();
|
|
@@ -345,8 +374,8 @@ await payload.execute();
|
|
|
345
374
|
|
|
346
375
|
```tsx
|
|
347
376
|
const payload = await vaultService.withdrawStream({
|
|
348
|
-
|
|
349
|
-
|
|
377
|
+
vaultOwner: wallet.publicKey,
|
|
378
|
+
streamMetadata: streamAddress,
|
|
350
379
|
});
|
|
351
380
|
|
|
352
381
|
await payload.execute();
|
|
@@ -356,9 +385,9 @@ await payload.execute();
|
|
|
356
385
|
|
|
357
386
|
```tsx
|
|
358
387
|
const payload = await vaultService.changeStreamReceiver({
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
388
|
+
vaultOwner: wallet.publicKey,
|
|
389
|
+
streamMetadata: streamAddress,
|
|
390
|
+
newRecipient: newRecipientAddress,
|
|
362
391
|
});
|
|
363
392
|
|
|
364
393
|
await payload.execute();
|
|
@@ -370,46 +399,46 @@ await payload.execute();
|
|
|
370
399
|
const streamInfo = await vaultService.getStreamMetadataInfo(streamAddress);
|
|
371
400
|
|
|
372
401
|
console.log("Stream details:", {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
402
|
+
sender: streamInfo.parties.sender,
|
|
403
|
+
receiver: streamInfo.parties.receiver,
|
|
404
|
+
token: streamInfo.financials.streamToken,
|
|
405
|
+
deposited: streamInfo.financials.depositedAmount,
|
|
406
|
+
withdrawn: streamInfo.financials.withdrawnAmount,
|
|
407
|
+
startTime: new Date(streamInfo.schedule.startTime * 1000),
|
|
408
|
+
endTime: new Date(streamInfo.schedule.endTime * 1000),
|
|
409
|
+
isPaused: streamInfo.schedule.pausedTimestamp > 0,
|
|
381
410
|
});
|
|
382
411
|
```
|
|
383
412
|
|
|
384
413
|
### Staking
|
|
385
414
|
|
|
386
|
-
|
|
415
|
+
#### Stake Tokens
|
|
387
416
|
|
|
388
417
|
```tsx
|
|
389
418
|
const payload = await vaultService.stake({
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
419
|
+
lockupName: "main-lockup",
|
|
420
|
+
vaultOwner: wallet.publicKey,
|
|
421
|
+
amount: 1000,
|
|
422
|
+
lockPeriod: 7776000, // 90 days in seconds; must be one of the lockup's valid periods
|
|
423
|
+
nonce: 0n,
|
|
395
424
|
});
|
|
396
425
|
|
|
397
426
|
await payload.execute();
|
|
398
427
|
```
|
|
399
428
|
|
|
400
|
-
|
|
429
|
+
#### Unstake Tokens
|
|
401
430
|
|
|
402
431
|
```tsx
|
|
403
432
|
const payload = await vaultService.unstake({
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
433
|
+
lockupName: "main-lockup",
|
|
434
|
+
vaultOwner: wallet.publicKey,
|
|
435
|
+
nonce: 0n,
|
|
407
436
|
});
|
|
408
437
|
|
|
409
438
|
await payload.execute();
|
|
410
439
|
```
|
|
411
440
|
|
|
412
|
-
|
|
441
|
+
#### Get Stake Nonce Information
|
|
413
442
|
|
|
414
443
|
```tsx
|
|
415
444
|
const nonceInfo = await vaultService.getStakeUserNonceInfo("main-lockup", wallet.publicKey);
|
|
@@ -419,85 +448,94 @@ console.log("Current nonce:", nonceInfo ? nonceInfo.nonce : 0n);
|
|
|
419
448
|
|
|
420
449
|
### Zebec Cards
|
|
421
450
|
|
|
422
|
-
|
|
451
|
+
#### Create a Silver Card
|
|
423
452
|
|
|
424
453
|
```tsx
|
|
425
454
|
const nextIndex = await vaultService.getNextCardIndex();
|
|
426
455
|
|
|
427
456
|
const payload = await vaultService.createSilverCard({
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
457
|
+
vaultOwnerAddress: wallet.publicKey,
|
|
458
|
+
nextCardIndex: nextIndex,
|
|
459
|
+
amount: 100, // USDC amount
|
|
460
|
+
usdcAddress: USDC_MINT_ADDRESS,
|
|
461
|
+
emailHash: Buffer.from("your-32-byte-hash"), // must be exactly 32 bytes
|
|
462
|
+
currency: "USD",
|
|
434
463
|
});
|
|
435
464
|
|
|
436
465
|
await payload.execute();
|
|
437
466
|
```
|
|
438
467
|
|
|
439
|
-
|
|
468
|
+
#### Load a Carbon Card
|
|
440
469
|
|
|
441
470
|
```tsx
|
|
442
471
|
const nextIndex = await vaultService.getNextCardIndex();
|
|
443
472
|
|
|
444
473
|
const payload = await vaultService.loadCarbonCard({
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
474
|
+
vaultOwnerAddress: wallet.publicKey,
|
|
475
|
+
nextCardIndex: nextIndex,
|
|
476
|
+
amount: 50,
|
|
477
|
+
usdcAddress: USDC_MINT_ADDRESS,
|
|
478
|
+
emailHash: Buffer.from("your-32-byte-hash"), // must be exactly 32 bytes
|
|
479
|
+
currency: "USD",
|
|
480
|
+
reloadCardId: "CARD_ID",
|
|
452
481
|
});
|
|
453
482
|
|
|
454
483
|
await payload.execute();
|
|
455
484
|
```
|
|
456
485
|
|
|
457
|
-
|
|
486
|
+
#### Swap and Create Silver Card
|
|
458
487
|
|
|
459
488
|
```tsx
|
|
460
489
|
// First, get a Jupiter quote
|
|
490
|
+
const quoteParams = new URLSearchParams({
|
|
491
|
+
inputMint: inputMint,
|
|
492
|
+
outputMint: USDC_MINT_ADDRESS,
|
|
493
|
+
amount: parsedAmount, // in smallest units
|
|
494
|
+
slippageBps: "50",
|
|
495
|
+
swapMode: "ExactIn",
|
|
496
|
+
});
|
|
461
497
|
const quoteResponse = await fetch(
|
|
462
|
-
|
|
498
|
+
`https://lite-api.jup.ag/swap/v1/quote?${quoteParams}`,
|
|
463
499
|
);
|
|
464
500
|
const quoteInfo = await quoteResponse.json();
|
|
465
501
|
|
|
502
|
+
const nextIndex = await vaultService.getNextCardIndex();
|
|
503
|
+
|
|
466
504
|
const payload = await vaultService.swapAndCreateSilverCard({
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
505
|
+
vaultOwnerAddress: wallet.publicKey,
|
|
506
|
+
quoteInfo: quoteInfo,
|
|
507
|
+
nextCardCounter: nextIndex,
|
|
508
|
+
emailHash: Buffer.from("your-32-byte-hash"), // must be exactly 32 bytes
|
|
509
|
+
currency: "USD",
|
|
510
|
+
wrapAndUnwrapSol: true, // set to true when swapping SOL
|
|
473
511
|
});
|
|
474
512
|
|
|
475
513
|
await payload.execute();
|
|
476
514
|
```
|
|
477
515
|
|
|
478
|
-
|
|
516
|
+
#### Swap and Load Carbon Card
|
|
479
517
|
|
|
480
518
|
```tsx
|
|
481
519
|
const payload = await vaultService.swapAndLoadCarbonCard({
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
520
|
+
vaultOwnerAddress: wallet.publicKey,
|
|
521
|
+
quoteInfo: quoteInfo,
|
|
522
|
+
nextCardCounter: nextIndex,
|
|
523
|
+
emailHash: Buffer.from("your-32-byte-hash"), // must be exactly 32 bytes
|
|
524
|
+
currency: "USD",
|
|
525
|
+
reloadCardId: "CARD_ID",
|
|
526
|
+
wrapAndUnwrapSol: true,
|
|
489
527
|
});
|
|
490
528
|
|
|
491
529
|
await payload.execute();
|
|
492
530
|
```
|
|
493
531
|
|
|
494
|
-
|
|
532
|
+
#### Get Card Custom Token Fees
|
|
495
533
|
|
|
496
534
|
```tsx
|
|
497
535
|
const tokenFees = await vaultService.getCardCustomTokenFees();
|
|
498
536
|
|
|
499
537
|
tokenFees.forEach((fee) => {
|
|
500
|
-
|
|
538
|
+
console.log(`Token: ${fee.tokenAddress}, Fee: ${fee.fee}%`);
|
|
501
539
|
});
|
|
502
540
|
```
|
|
503
541
|
|
|
@@ -518,15 +556,12 @@ const vaultInfo = await service.getVaultInfoOfUser(someAddress);
|
|
|
518
556
|
|
|
519
557
|
### **Advanced Features**
|
|
520
558
|
|
|
521
|
-
|
|
559
|
+
#### Program Derived Addresses (PDAs)
|
|
522
560
|
|
|
523
561
|
```tsx
|
|
524
562
|
import {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
deriveStreamConfigPda,
|
|
528
|
-
deriveCardConfigPda,
|
|
529
|
-
deriveLockupAddress,
|
|
563
|
+
deriveUserVault,
|
|
564
|
+
deriveVaultSigner,
|
|
530
565
|
} from "@zebec-network/zebec-vault-sdk";
|
|
531
566
|
|
|
532
567
|
const [vaultAddress, vaultBump] = deriveUserVault(userAddress, vaultProgramId);
|
|
@@ -534,19 +569,19 @@ const [vaultAddress, vaultBump] = deriveUserVault(userAddress, vaultProgramId);
|
|
|
534
569
|
const [signerAddress, signerBump] = deriveVaultSigner(vaultAddress, vaultProgramId);
|
|
535
570
|
```
|
|
536
571
|
|
|
537
|
-
|
|
572
|
+
#### Transaction Utilities
|
|
538
573
|
|
|
539
574
|
```tsx
|
|
540
575
|
import { calculateProposalSize, transactionInstructionToProposalAction } from "@zebec-network/zebec-vault-sdk";
|
|
541
576
|
|
|
542
|
-
// Calculate proposal size
|
|
577
|
+
// Calculate proposal size (in bytes)
|
|
543
578
|
const size = calculateProposalSize("proposal-name", actions);
|
|
544
579
|
|
|
545
|
-
// Convert
|
|
580
|
+
// Convert a TransactionInstruction to a ProposalAction
|
|
546
581
|
const action = transactionInstructionToProposalAction(instruction);
|
|
547
582
|
```
|
|
548
583
|
|
|
549
|
-
|
|
584
|
+
#### Network Configuration
|
|
550
585
|
|
|
551
586
|
```tsx
|
|
552
587
|
// Mainnet
|
|
@@ -560,37 +595,81 @@ const devnetService = ZebecVaultService.create(provider, "devnet");
|
|
|
560
595
|
|
|
561
596
|
```tsx
|
|
562
597
|
import {
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
598
|
+
ZEBEC_VAULT_PROGRAM_ID,
|
|
599
|
+
JUPITER_AGGREGATOR_PROGRAM_ID,
|
|
600
|
+
CARD_LOOKUP_TABLE_ADDRESS,
|
|
601
|
+
STAKE_LOOKUP_TABLE_ADDRESS,
|
|
567
602
|
} from "@zebec-network/zebec-vault-sdk";
|
|
568
603
|
|
|
569
604
|
console.log("Vault Program:", ZEBEC_VAULT_PROGRAM_ID["mainnet-beta"]);
|
|
570
605
|
```
|
|
571
606
|
|
|
607
|
+
### **Error Types**
|
|
608
|
+
|
|
609
|
+
The SDK throws typed errors you can catch and inspect:
|
|
610
|
+
|
|
611
|
+
```tsx
|
|
612
|
+
import {
|
|
613
|
+
AmountOutOfRangeError,
|
|
614
|
+
DailyCardLimitReachedError,
|
|
615
|
+
InvalidUsdcAddressError,
|
|
616
|
+
NotEnoughBalanceError,
|
|
617
|
+
QuoteResponseError,
|
|
618
|
+
AssociatedTokenAccountDoesNotExistsError,
|
|
619
|
+
} from "@zebec-network/zebec-vault-sdk";
|
|
620
|
+
|
|
621
|
+
try {
|
|
622
|
+
await payload.execute();
|
|
623
|
+
} catch (err) {
|
|
624
|
+
if (err instanceof AmountOutOfRangeError) {
|
|
625
|
+
console.error(`Amount must be between ${err.minRange} and ${err.maxRange}`);
|
|
626
|
+
} else if (err instanceof DailyCardLimitReachedError) {
|
|
627
|
+
console.error(`Daily limit: ${err.dailyCardLimit}, requested: ${err.requestedAmount}`);
|
|
628
|
+
} else if (err instanceof NotEnoughBalanceError) {
|
|
629
|
+
console.error("Insufficient balance");
|
|
630
|
+
} else if (err instanceof AssociatedTokenAccountDoesNotExistsError) {
|
|
631
|
+
console.error("Token account does not exist");
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
```
|
|
635
|
+
|
|
572
636
|
### **Types**
|
|
573
637
|
|
|
574
638
|
The SDK exports comprehensive TypeScript types:
|
|
575
639
|
|
|
576
640
|
```tsx
|
|
577
641
|
import type {
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
642
|
+
VaultInfo,
|
|
643
|
+
ProposalInfo,
|
|
644
|
+
ProposalAction,
|
|
645
|
+
ProposalStage,
|
|
646
|
+
CreateStreamFromVaultParams,
|
|
647
|
+
createMultipleStreamFromVaultParams,
|
|
648
|
+
StreamMetadataInfo,
|
|
649
|
+
TokenFeeRecord,
|
|
650
|
+
QuoteInfo,
|
|
651
|
+
RouteInfo,
|
|
652
|
+
StakeUserNonceInfo,
|
|
653
|
+
CreateSilverCardParams,
|
|
654
|
+
LoadCarbonCardParams,
|
|
655
|
+
SwapAndCreateSilverCardParams,
|
|
656
|
+
SwapAndLoadCarbonCardParams,
|
|
657
|
+
CancelStreamParams,
|
|
658
|
+
PauseResumeStreamParams,
|
|
659
|
+
ChangeStreamReceiverParams,
|
|
660
|
+
WithdrawStreamParams,
|
|
661
|
+
ParsedCardConfigInfo,
|
|
662
|
+
WhitelistInfo,
|
|
663
|
+
Numeric,
|
|
586
664
|
} from "@zebec-network/zebec-vault-sdk";
|
|
587
665
|
```
|
|
588
666
|
|
|
589
667
|
## **Best Practices**
|
|
590
668
|
|
|
591
|
-
1. **Always check balances**
|
|
592
|
-
2. **Use proposals**
|
|
593
|
-
3. **Handle errors**
|
|
594
|
-
4. **Verify stream parameters**
|
|
595
|
-
5. **Test on `devnet`**
|
|
596
|
-
6. **Use address lookup tables**
|
|
669
|
+
1. **Always check balances** before operations
|
|
670
|
+
2. **Use proposals** for critical operations that require multiple instructions and cannot be executed in a single transaction
|
|
671
|
+
3. **Handle errors** appropriately with the provided error types
|
|
672
|
+
4. **Verify stream parameters** before creation (duration, amounts, permissions)
|
|
673
|
+
5. **Test on `devnet`** before deploying to `mainnet`
|
|
674
|
+
6. **Use address lookup tables** for complex transactions to reduce transaction size
|
|
675
|
+
7. **Use a private dedicated RPC** for production workloads
|
package/package.json
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"@zebec-network/core-utils": "^1.1.1",
|
|
9
9
|
"@zebec-network/solana-common": "^2.3.1",
|
|
10
10
|
"@zebec-network/zebec-card-v2-sdk": "^2.6.0",
|
|
11
|
-
"@zebec-network/zebec-stake-sdk": "^1.3.
|
|
12
|
-
"@zebec-network/zebec-stream-sdk": "^3.
|
|
11
|
+
"@zebec-network/zebec-stake-sdk": "^1.3.1",
|
|
12
|
+
"@zebec-network/zebec-stream-sdk": "^3.2.0",
|
|
13
13
|
"bignumber.js": "^9.3.1",
|
|
14
14
|
"buffer": "^6.0.3"
|
|
15
15
|
},
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"test:single": "ts-mocha -p ./tsconfig.json -t 1000000000"
|
|
48
48
|
},
|
|
49
49
|
"types": "dist/index.d.ts",
|
|
50
|
-
"version": "5.2.
|
|
50
|
+
"version": "5.2.3"
|
|
51
51
|
}
|