@@ -121,6 +121,7 @@ import type {
121121 BatchCancelResult ,
122122 BatchWithdrawResult ,
123123 BatchWithdrawPartialResult ,
124+ BatchProgress ,
124125 BulkCreateOptions ,
125126 BulkCreateResult ,
126127 CancelStreamParams ,
@@ -2473,6 +2474,9 @@ export class SoroStreamClient<TEventData = Record<string, unknown>> {
24732474 * @param streamIds - Stream IDs to withdraw from.
24742475 * @param batchSize - Maximum operations per transaction (default 8). Chunks
24752476 * are still attempted together, but a chunk failure is recorded per-stream.
2477+ * @param onProgress - Optional callback fired after each chunk completes
2478+ * (success or failure), reporting cumulative `{ completed, total,
2479+ * processedIds }` so callers can render incremental progress.
24762480 * @returns `{ successes, failures }` — IDs that were successfully withdrawn
24772481 * and the IDs+errors that were not.
24782482 *
@@ -2483,17 +2487,27 @@ export class SoroStreamClient<TEventData = Record<string, unknown>> {
24832487 *
24842488 * @example
24852489 * ```ts
2486- * const { successes, failures } = await client.batchWithdraw(["1", "2", "3"]);
2490+ * const { successes, failures } = await client.batchWithdraw(
2491+ * ["1", "2", "3"],
2492+ * 2,
2493+ * ({ completed, total }) => console.log(`Withdrawn ${completed}/${total}`),
2494+ * );
24872495 * if (failures.length) {
24882496 * console.warn("Some withdrawals failed:", failures);
24892497 * }
24902498 * console.log("Withdrawn:", successes);
24912499 * ```
24922500 */
2493- async batchWithdraw ( streamIds : string [ ] , batchSize = 8 ) : Promise < BatchWithdrawPartialResult > {
2501+ async batchWithdraw (
2502+ streamIds : string [ ] ,
2503+ batchSize = 8 ,
2504+ onProgress ?: ( progress : BatchProgress ) => void ,
2505+ ) : Promise < BatchWithdrawPartialResult > {
24942506 const successes : string [ ] = [ ] ;
24952507 const failures : { id : string ; error : Error } [ ] = [ ] ;
24962508 const recipient = await this . requireWalletAdapter ( ) . getPublicKey ( ) ;
2509+ const total = streamIds . length ;
2510+ let completed = 0 ;
24972511
24982512 for ( let i = 0 ; i < streamIds . length ; i += batchSize ) {
24992513 const chunk = streamIds . slice ( i , i + batchSize ) ;
@@ -2522,6 +2536,9 @@ export class SoroStreamClient<TEventData = Record<string, unknown>> {
25222536 failures . push ( { id, error : err instanceof Error ? err : new Error ( String ( err ) ) } ) ;
25232537 }
25242538 }
2539+
2540+ completed += chunk . length ;
2541+ onProgress ?.( { completed, total, processedIds : chunk } ) ;
25252542 }
25262543
25272544 return { successes, failures } ;
@@ -4478,14 +4495,22 @@ export class SoroStreamClient<TEventData = Record<string, unknown>> {
44784495 * @param options.token - Default SAC token contract address for rows that omit `token`.
44794496 * @param options.autoRenew - Whether created streams auto-renew (default false).
44804497 * @param options.batchSize - Maximum operations per transaction (default 8).
4498+ * @param options.onProgress - Optional callback fired after each chunk (or
4499+ * row, for mixed-token chunks) is submitted, reporting cumulative
4500+ * `{ completed, total, processedIds }` so callers can render incremental
4501+ * progress. Fires on failures too — a failed row still counts as processed.
44814502 * @returns `{ batches }` — one entry per submitted transaction, each with its `txHash` and the resulting `streamIds`.
44824503 * @throws {BulkCreatePartialError } If one or more rows fail; carries `successfulBatches` and `failedSlots`.
44834504 * @throws {TransactionFailedError } If a submitted transaction is rejected (wrapped into `failedSlots` rather than thrown directly).
44844505 *
44854506 * @example
44864507 * ```ts
44874508 * try {
4488- * const { batches } = await client.bulkCreateStreams(rows, { token: usdc });
4509+ * const { batches } = await client.bulkCreateStreams(rows, {
4510+ * token: usdc,
4511+ * onProgress: ({ completed, total }) =>
4512+ * console.log(`Created ${completed}/${total} streams`),
4513+ * });
44894514 * } catch (err) {
44904515 * if (err instanceof BulkCreatePartialError) {
44914516 * console.error(`${err.failedSlots.length} stream(s) failed:`, err.failedSlots);
@@ -4502,6 +4527,9 @@ export class SoroStreamClient<TEventData = Record<string, unknown>> {
45024527 const defaultToken = options . token ;
45034528 const autoRenew = options . autoRenew ?? false ;
45044529 const batchSize = options . batchSize ?? 8 ;
4530+ const onProgress = options . onProgress ;
4531+ const total = rows . length ;
4532+ let completed = 0 ;
45054533
45064534 // Validate cliff for all rows before submitting anything
45074535 for ( const row of rows ) {
@@ -4543,6 +4571,9 @@ export class SoroStreamClient<TEventData = Record<string, unknown>> {
45434571 } catch ( error ) {
45444572 failedSlots . push ( { index : i + j , row, error } ) ;
45454573 }
4574+
4575+ completed += 1 ;
4576+ onProgress ?.( { completed, total, processedIds : [ row . recipient ] } ) ;
45464577 }
45474578 } else {
45484579 try {
@@ -4570,6 +4601,9 @@ export class SoroStreamClient<TEventData = Record<string, unknown>> {
45704601 failedSlots . push ( { index : i + j , row, error } ) ;
45714602 } ) ;
45724603 }
4604+
4605+ completed += chunk . length ;
4606+ onProgress ?.( { completed, total, processedIds : chunk . map ( ( r ) => r . recipient ) } ) ;
45734607 }
45744608 }
45754609
0 commit comments