@unifiedflow/unified-flow-sdk 1.0.0 → 1.0.1

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 ADDED
@@ -0,0 +1,292 @@
1
+ # @unifiedflow/unified-flow-sdk
2
+
3
+ TypeScript SDK for interacting with the **Unified Flow** on-chain token vesting and streaming program on Solana.
4
+
5
+ ---
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @unifiedflow/unified-flow-sdk
11
+ # or
12
+ yarn add @unifiedflow/unified-flow-sdk
13
+ ```
14
+
15
+ ### Peer Dependencies
16
+
17
+ ```bash
18
+ npm install @coral-xyz/anchor @solana/web3.js @solana/spl-token
19
+ ```
20
+
21
+ > **Version note:** Make sure your project uses the same `@coral-xyz/anchor` version as the SDK to avoid type conflicts. Add to `package.json` if needed:
22
+ > ```json
23
+ > { "overrides": { "@coral-xyz/anchor": "<sdk-anchor-version>" } }
24
+ > ```
25
+
26
+ ---
27
+
28
+ ## Quick Start
29
+
30
+ ### 1. Initialize the Client
31
+
32
+ ```typescript
33
+ import { useAnchorWallet, useConnection } from "@solana/wallet-adapter-react";
34
+ import { AnchorProvider, Program } from "@coral-xyz/anchor";
35
+ import { IDL, UnifiedFlow, UnifiedFlowClient } from "@unifiedflow/unified-flow-sdk";
36
+ import { useMemo } from "react";
37
+
38
+ export function useUnifiedFlowClient() {
39
+ const { connection } = useConnection();
40
+ const wallet = useAnchorWallet();
41
+
42
+ return useMemo(() => {
43
+ if (!wallet) return null;
44
+
45
+ const provider = new AnchorProvider(connection, wallet, {
46
+ commitment: "confirmed",
47
+ });
48
+
49
+ const program = new Program(IDL, provider) as any;
50
+ return new UnifiedFlowClient(program);
51
+ }, [wallet, connection]);
52
+ }
53
+ ```
54
+
55
+ ### 2. Use in a Component
56
+
57
+ ```typescript
58
+ const client = useUnifiedFlowClient();
59
+
60
+ if (!client) return <p>Connect your wallet</p>;
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Vesting Types
66
+
67
+ | Value | Type | Description |
68
+ |-------|-------------|--------------------------------------------------|
69
+ | `0` | `LINEAR` | Tokens vest continuously over time |
70
+ | `1` | `CLIFF` | All tokens unlock at a single cliff timestamp |
71
+ | `2` | `MILESTONE` | Tokens unlock per milestone, approved by creator |
72
+
73
+ ---
74
+
75
+ ## API Reference
76
+
77
+ ### `createStream`
78
+
79
+ Create a new vesting stream.
80
+
81
+ ```typescript
82
+ const builder = await client.createStream(
83
+ creator, // PublicKey — stream creator / funder
84
+ recipient, // PublicKey — token recipient
85
+ mint, // PublicKey — SPL token mint
86
+ new BN(1_000_000),// amount — total tokens (in smallest unit)
87
+ new BN(startTs), // start timestamp (Unix seconds)
88
+ new BN(cliffTs), // cliff timestamp (set equal to startTs if no cliff)
89
+ new BN(endTs), // end timestamp
90
+ 0, // vestingType: 0 = linear, 1 = cliff, 2 = milestone
91
+ [], // milestones: MilestoneInput[] (empty for non-milestone)
92
+ new BN(nonce) // unique nonce per creator+recipient pair
93
+ );
94
+
95
+ const txSig = await builder.rpc();
96
+ ```
97
+
98
+ **Milestone stream example:**
99
+
100
+ ```typescript
101
+ const milestones: MilestoneInput[] = [
102
+ { amount: new BN(250_000) },
103
+ { amount: new BN(250_000) },
104
+ { amount: new BN(500_000) },
105
+ ];
106
+
107
+ const builder = await client.createStream(
108
+ creator, recipient, mint,
109
+ new BN(1_000_000),
110
+ new BN(startTs), new BN(startTs), new BN(endTs),
111
+ 2, // MILESTONE
112
+ milestones,
113
+ new BN(nonce)
114
+ );
115
+ ```
116
+
117
+ ---
118
+
119
+ ### `withdraw`
120
+
121
+ Withdraw vested/unlocked tokens from a stream. Callable by the recipient.
122
+
123
+ ```typescript
124
+ const builder = await client.withdraw(
125
+ streamPDA, // PublicKey — stream account address
126
+ recipient, // PublicKey
127
+ mint // PublicKey
128
+ );
129
+
130
+ const txSig = await builder.rpc();
131
+ ```
132
+
133
+ ---
134
+
135
+ ### `cancel`
136
+
137
+ Cancel an active stream. Returns unvested tokens to the creator. Callable by the creator.
138
+
139
+ ```typescript
140
+ const builder = await client.cancel(
141
+ streamPDA, // PublicKey — stream account address
142
+ creator, // PublicKey
143
+ recipient, // PublicKey
144
+ mint // PublicKey
145
+ );
146
+
147
+ const txSig = await builder.rpc();
148
+ ```
149
+
150
+ ---
151
+
152
+ ### `unlockMilestone`
153
+
154
+ Unlock the next milestone in a milestone vesting stream. Callable by the creator.
155
+
156
+ ```typescript
157
+ const builder = await client.unlockMilestone(
158
+ streamPDA, // PublicKey
159
+ creator, // PublicKey
160
+ milestoneIndex // number — 0-indexed
161
+ );
162
+
163
+ const txSig = await builder.rpc();
164
+ ```
165
+
166
+ ---
167
+
168
+ ### `editMilestone`
169
+
170
+ Change the token amount for a specific milestone (before it is unlocked).
171
+
172
+ ```typescript
173
+ const builder = await client.editMilestone(
174
+ streamPDA,
175
+ creator,
176
+ mint,
177
+ milestoneIndex, // number
178
+ new BN(newAmount) // BN
179
+ );
180
+
181
+ const txSig = await builder.rpc();
182
+ ```
183
+
184
+ ---
185
+
186
+ ### `editCliff`
187
+
188
+ Update the cliff timestamp on a cliff vesting stream.
189
+
190
+ ```typescript
191
+ const builder = await client.editCliff(
192
+ streamPDA,
193
+ creator,
194
+ new BN(newCliffTs)
195
+ );
196
+
197
+ const txSig = await builder.rpc();
198
+ ```
199
+
200
+ ---
201
+
202
+ ### `editLinear`
203
+
204
+ Extend the end timestamp and/or top up tokens on a linear vesting stream. Both operations happen in a single transaction.
205
+
206
+ ```typescript
207
+ const builder = await client.editLinear(
208
+ streamPDA,
209
+ creator,
210
+ mint,
211
+ new BN(newEndTs), // new end timestamp
212
+ new BN(topupAmount) // additional tokens to deposit (0 if no top-up)
213
+ );
214
+
215
+ const txSig = await builder.rpc();
216
+ ```
217
+
218
+ ---
219
+
220
+ ## PDA Helpers
221
+
222
+ The SDK exports PDA derivation utilities if you need raw account addresses:
223
+
224
+ ```typescript
225
+ import {
226
+ getConfigPDA,
227
+ getStreamPDA,
228
+ getMilestonePDA,
229
+ getFeeVaultPDA,
230
+ getVaultATA,
231
+ } from "@unifiedflow/unified-flow-sdk";
232
+
233
+ const [streamPDA] = getStreamPDA(creator, recipient, nonce, programId);
234
+ const [milestonePDA] = getMilestonePDA(streamPDA, milestoneIndex, programId);
235
+ ```
236
+
237
+ ---
238
+
239
+ ## Chainlink Oracle
240
+
241
+ Withdrawal fees are denominated in USD and calculated on-chain via the Chainlink SOL/USD price feed. The SDK wires this up automatically — no extra configuration needed.
242
+
243
+ | Constant | Value |
244
+ |-----------------------|----------------------------------------------|
245
+ | `CHAINLINK_PROGRAM_ID`| `HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny` |
246
+ | `SOL_USD_FEED` | `99B2bTijsU6f1GCT73HmdR7HCFFjGMBcPZY6jZ96ynrR` |
247
+
248
+ ---
249
+
250
+ ## Builder Pattern
251
+
252
+ All client methods return an **Anchor instruction builder**. You have three options:
253
+
254
+ ```typescript
255
+ const builder = await client.withdraw(streamPDA, recipient, mint);
256
+
257
+ // Execute and return transaction signature
258
+ const txSig = await builder.rpc();
259
+
260
+ // Build a Transaction object (e.g. for simulation or custom signing)
261
+ const tx = await builder.transaction();
262
+
263
+ // Simulate without sending
264
+ const result = await builder.simulate();
265
+ ```
266
+
267
+ ---
268
+
269
+ ## Error Reference
270
+
271
+ | Error | Likely Cause |
272
+ |-------------------------------|-----------------------------------------------------------|
273
+ | `AccountDiscriminatorMismatch`| Wrong cluster (e.g. calling devnet PDA on mainnet RPC) |
274
+ | `AccountNotInitialized` | Stream or milestone PDA does not exist yet |
275
+ | `InvalidMilestoneIndex` | `milestoneIndex` out of range |
276
+ | `StreamNotActive` | Stream already cancelled or fully vested |
277
+
278
+ ---
279
+
280
+ ## Network Support
281
+
282
+ | Network | Status |
283
+ |---------|--------|
284
+ | Devnet | ✅ Supported |
285
+ | Mainnet | ✅ Supported |
286
+ | Localnet| ✅ Supported (bankrun / solana-test-validator) |
287
+
288
+ ---
289
+
290
+ ## License
291
+
292
+ MIT
package/package.json CHANGED
@@ -1,14 +1,18 @@
1
1
  {
2
2
  "name": "@unifiedflow/unified-flow-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "SDK for the Unified Flow program",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "build": "tsc"
9
9
  },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
10
14
  "dependencies": {
11
- "@coral-xyz/anchor": "^0.30.0",
15
+ "@coral-xyz/anchor": "^0.32.1",
12
16
  "@solana/spl-token": "^0.4.0",
13
17
  "@solana/web3.js": "^1.90.0"
14
18
  },
package/src/client.ts DELETED
@@ -1,196 +0,0 @@
1
- import { Program, BN, Idl } from "@coral-xyz/anchor";
2
- import { PublicKey, SystemProgram } from "@solana/web3.js";
3
- import { TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync } from "@solana/spl-token";
4
- import { UnifiedFlow } from "./types";
5
- import { getConfigPDA, getFeeVaultPDA, getMilestonePDA, getStreamPDA, getVaultATA } from "./pda";
6
-
7
- export const CHAINLINK_PROGRAM_ID = new PublicKey("HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny");
8
- export const SOL_USD_FEED = new PublicKey("99B2bTijsU6f1GCT73HmdR7HCFFjGMBcPZY6jZ96ynrR");
9
-
10
- export interface MilestoneInput {
11
- amount: BN;
12
- }
13
-
14
- export class UnifiedFlowClient {
15
- constructor(public readonly program: Program<UnifiedFlow>) {}
16
-
17
- /**
18
- * Helper to derive the config PDA
19
- */
20
- public getConfigPDA(): PublicKey {
21
- return getConfigPDA(this.program.programId)[0];
22
- }
23
-
24
- /**
25
- * Create a new stream (Linear, Cliff, or Milestone)
26
- */
27
- public async createStream(
28
- creator: PublicKey,
29
- recipient: PublicKey,
30
- mint: PublicKey,
31
- amount: BN,
32
- startTs: BN,
33
- cliffTs: BN,
34
- endTs: BN,
35
- vestingType: number,
36
- milestones: MilestoneInput[],
37
- nonce: BN
38
- ) {
39
- const config = this.getConfigPDA();
40
- const stream = getStreamPDA(creator, recipient, nonce, this.program.programId)[0];
41
- const vault = getVaultATA(mint, stream);
42
- const creatorTokenAccount = getAssociatedTokenAddressSync(mint, creator, true);
43
-
44
- const builder = this.program.methods
45
- .createStream(amount, startTs, cliffTs, endTs, vestingType, milestones, nonce)
46
- .accounts({
47
- creator,
48
- recipient,
49
- mint,
50
- creatorTokenAccount,
51
- tokenProgram: TOKEN_PROGRAM_ID,
52
- });
53
-
54
- if (vestingType === 2 && milestones.length > 0) { // VESTING_TYPE_MILESTONE = 2
55
- const remainingAccounts = milestones.map((_, i) => ({
56
- pubkey: getMilestonePDA(stream, i, this.program.programId)[0],
57
- isWritable: true,
58
- isSigner: false,
59
- }));
60
- builder.remainingAccounts(remainingAccounts);
61
- }
62
-
63
- return builder;
64
- }
65
-
66
- /**
67
- * Withdraw unlocked/vested tokens from a stream
68
- */
69
- public async withdraw(
70
- streamPDA: PublicKey,
71
- recipient: PublicKey,
72
- mint: PublicKey
73
- ) {
74
- const config = this.getConfigPDA();
75
- const vault = getVaultATA(mint, streamPDA);
76
- const recipientAta = getAssociatedTokenAddressSync(mint, recipient, true);
77
- const feeVault = getFeeVaultPDA(this.program.programId)[0];
78
-
79
- return this.program.methods
80
- .withdraw()
81
- .accounts({
82
- recipient,
83
- stream: streamPDA,
84
- recipientAta,
85
- chainlinkFeed: SOL_USD_FEED,
86
- tokenProgram: TOKEN_PROGRAM_ID,
87
- } as any);
88
- }
89
-
90
- /**
91
- * Cancel an active stream and return remaining tokens to the creator
92
- */
93
- public async cancel(
94
- streamPDA: PublicKey,
95
- creator: PublicKey,
96
- recipient: PublicKey,
97
- mint: PublicKey
98
- ) {
99
- const config = this.getConfigPDA();
100
- const vault = getVaultATA(mint, streamPDA);
101
- const creatorTokenAccount = getAssociatedTokenAddressSync(mint, creator, true);
102
- const recipientTokenAccount = getAssociatedTokenAddressSync(mint, recipient, true);
103
-
104
- return this.program.methods
105
- .cancel()
106
- .accounts({
107
- creator,
108
- stream: streamPDA,
109
- creatorTokenAccount,
110
- recipientTokenAccount,
111
- tokenProgram: TOKEN_PROGRAM_ID,
112
- } as any);
113
- }
114
-
115
- /**
116
- * Unlock a specific milestone
117
- */
118
- public async unlockMilestone(
119
- streamPDA: PublicKey,
120
- creator: PublicKey,
121
- milestoneIndex: number
122
- ) {
123
- const milestonePDA = getMilestonePDA(streamPDA, milestoneIndex, this.program.programId)[0];
124
-
125
- return this.program.methods
126
- .unlockMilestone()
127
- .accounts({
128
- stream: streamPDA,
129
- milestone: milestonePDA,
130
- } as any);
131
- }
132
-
133
- /**
134
- * Edit a milestone's amount (increase or decrease)
135
- */
136
- public async editMilestone(
137
- streamPDA: PublicKey,
138
- creator: PublicKey,
139
- mint: PublicKey,
140
- milestoneIndex: number,
141
- newAmount: BN
142
- ) {
143
- const milestonePDA = getMilestonePDA(streamPDA, milestoneIndex, this.program.programId)[0];
144
- const vault = getVaultATA(mint, streamPDA);
145
- const creatorTokenAccount = getAssociatedTokenAddressSync(mint, creator, true);
146
-
147
- return this.program.methods
148
- .editMilestone(newAmount)
149
- .accounts({
150
- stream: streamPDA,
151
- milestone: milestonePDA,
152
- creatorTokenAccount,
153
- tokenProgram: TOKEN_PROGRAM_ID,
154
- } as any);
155
- }
156
-
157
- /**
158
- * Edit the cliff timestamp of a cliff vesting stream
159
- */
160
- public async editCliff(
161
- streamPDA: PublicKey,
162
- creator: PublicKey,
163
- newCliffTs: BN
164
- ) {
165
- const config = this.getConfigPDA();
166
-
167
- return this.program.methods
168
- .editCliff(newCliffTs)
169
- .accounts({
170
- stream: streamPDA,
171
- } as any);
172
- }
173
-
174
- /**
175
- * Edit a linear stream's end timestamp and/or top up tokens
176
- */
177
- public async editLinear(
178
- streamPDA: PublicKey,
179
- creator: PublicKey,
180
- mint: PublicKey,
181
- newEndTs: BN,
182
- topupAmount: BN
183
- ) {
184
- const config = this.getConfigPDA();
185
- const vault = getVaultATA(mint, streamPDA);
186
- const creatorTokenAccount = getAssociatedTokenAddressSync(mint, creator, true);
187
-
188
- return this.program.methods
189
- .editLinear(newEndTs, topupAmount)
190
- .accounts({
191
- stream: streamPDA,
192
- creatorTokenAccount,
193
- tokenProgram: TOKEN_PROGRAM_ID,
194
- } as any);
195
- }
196
- }
package/src/index.ts DELETED
@@ -1,6 +0,0 @@
1
- export * from "./types";
2
- export * from "./pda";
3
- export * from "./client";
4
-
5
- import IDL from "./unified_flow.json";
6
- export { IDL };
package/src/pda.ts DELETED
@@ -1,53 +0,0 @@
1
- import { PublicKey } from "@solana/web3.js";
2
- import { getAssociatedTokenAddressSync } from "@solana/spl-token";
3
- import { BN } from "@coral-xyz/anchor";
4
-
5
- export const UNIFIED_FLOW_PROGRAM_ID = new PublicKey(
6
- "8M5yieUh7pxwUi1YBByDF82nqoorZwaKi8dBoMVpurFa"
7
- );
8
-
9
- export function getConfigPDA(programId: PublicKey = UNIFIED_FLOW_PROGRAM_ID): [PublicKey, number] {
10
- return PublicKey.findProgramAddressSync([Buffer.from("config")], programId);
11
- }
12
-
13
- export function getFeeVaultPDA(programId: PublicKey = UNIFIED_FLOW_PROGRAM_ID): [PublicKey, number] {
14
- return PublicKey.findProgramAddressSync([Buffer.from("fee_vault")], programId);
15
- }
16
-
17
- export function getStreamPDA(
18
- creator: PublicKey,
19
- recipient: PublicKey,
20
- nonce: BN,
21
- programId: PublicKey = UNIFIED_FLOW_PROGRAM_ID
22
- ): [PublicKey, number] {
23
- const nonceBuffer = Buffer.alloc(8);
24
- nonceBuffer.writeBigUInt64LE(BigInt(nonce.toString()));
25
-
26
- return PublicKey.findProgramAddressSync(
27
- [
28
- Buffer.from("stream"),
29
- creator.toBuffer(),
30
- recipient.toBuffer(),
31
- nonceBuffer,
32
- ],
33
- programId
34
- );
35
- }
36
-
37
- export function getMilestonePDA(
38
- stream: PublicKey,
39
- index: number,
40
- programId: PublicKey = UNIFIED_FLOW_PROGRAM_ID
41
- ): [PublicKey, number] {
42
- return PublicKey.findProgramAddressSync(
43
- [Buffer.from("milestone"), stream.toBuffer(), Buffer.from([index])],
44
- programId
45
- );
46
- }
47
-
48
- export function getVaultATA(
49
- mint: PublicKey,
50
- stream: PublicKey
51
- ): PublicKey {
52
- return getAssociatedTokenAddressSync(mint, stream, true);
53
- }