@@ -248,7 +248,7 @@ export class Udt extends ssri.Trait {
248248 *
249249 * // Manually find cells using the same filter
250250 * for await (const cell of signer.findCells(udt.filter)) {
251- * console.log(`Found UDT cell with balance: ${ccc.udtBalanceFrom (cell.outputData)}`);
251+ * console.log(`Found UDT cell with balance: ${udt.balanceFrom (cell.outputData, cell.cellOutput )}`);
252252 * }
253253 * ```
254254 */
@@ -473,6 +473,179 @@ export class Udt extends ssri.Trait {
473473 return ssri . ExecutorResponse . new ( undefined ) ;
474474 }
475475
476+ static balanceFrom ( outputData : ccc . HexLike ) : ccc . Num {
477+ const data = ccc . bytesFrom ( outputData ) . slice ( 0 , 16 ) ;
478+ return data . length === 0 ? ccc . Zero : ccc . numFromBytes ( data ) ;
479+ }
480+
481+ balanceFrom ( outputData : ccc . HexLike , _output ?: ccc . CellOutputLike ) : ccc . Num {
482+ return Udt . balanceFrom ( outputData ) ;
483+ }
484+
485+ /**
486+ * Calculates comprehensive information about all UDT cells controlled by the signer.
487+ * This method scans through every UDT cell that the signer controls and aggregates
488+ * their balance, capacity, and count information.
489+ *
490+ * ⚠️ **Performance Warning**: This is an expensive operation that scales with the number
491+ * of UDT cells. For addresses with many UDT cells (hundreds or thousands), this method
492+ * can take significant time and resources. Use sparingly and consider caching results.
493+ *
494+ * @param signer - The signer whose UDT cells to scan and analyze
495+ * @param options - Optional configuration for the calculation
496+ * @param options.source - Data source to use: "chain" (default) for on-chain data, "local" for local indexer cache
497+ * @returns A promise resolving to an object containing:
498+ * - balance: Total UDT balance across all cells
499+ * - capacity: Total CKB capacity occupied by all UDT cells
500+ * - count: Number of UDT cells found
501+ *
502+ * @example
503+ * ```typescript
504+ * const udt = new Udt(codeOutPoint, scriptConfig);
505+ *
506+ * // Calculate comprehensive UDT information from chain (default)
507+ * const info = await udt.calculateInfo(signer);
508+ * console.log(`Total UDT balance: ${info.balance}`);
509+ * console.log(`Total capacity used: ${info.capacity} CKB`);
510+ * console.log(`Number of UDT cells: ${info.count}`);
511+ *
512+ * // Use local cache for faster response (may be less up-to-date)
513+ * const localInfo = await udt.calculateInfo(signer, { source: "local" });
514+ * console.log(`Local cached balance: ${localInfo.balance}`);
515+ *
516+ * // Use for wallet balance display
517+ * const balanceInTokens = ccc.fixedPointToString(info.balance, 8); // Assuming 8 decimals
518+ * console.log(`Balance: ${balanceInTokens} tokens in ${info.count} cells`);
519+ * ```
520+ *
521+ * @remarks
522+ * **Performance Considerations:**
523+ * - Execution time is O(n) where n is the number of UDT cells
524+ * - Network requests are made for each cell discovery when using "chain" source
525+ * - "local" source is faster but may not reflect the most recent state
526+ * - Consider implementing client-side caching for frequently accessed data
527+ * - For transaction-specific calculations, use `getInputsInfo()` or `getOutputsInfo()` instead
528+ *
529+ * **Data Source Options:**
530+ * - `"chain"` (default): Queries the blockchain directly for the most up-to-date information
531+ * - `"local"`: Uses local indexer cache, faster but potentially stale data
532+ *
533+ * **Use Cases:**
534+ * - Wallet balance display and portfolio overview
535+ * - UDT cell consolidation planning
536+ * - Comprehensive account analysis
537+ * - Debugging and development tools
538+ *
539+ * **Alternative Methods:**
540+ * - Use `calculateBalance()` if you only need the total balance
541+ * - Use `completeInputsAll()` if you need to collect all cells for a transaction
542+ * - Use transaction-specific methods for partial calculations
543+ */
544+ async calculateInfo (
545+ signer : ccc . Signer ,
546+ options ?: { source ?: "chain" | "local" | null } ,
547+ ) : Promise < {
548+ balance : ccc . Num ;
549+ capacity : ccc . Num ;
550+ count : number ;
551+ } > {
552+ let balance = ccc . Zero ;
553+ let capacity = ccc . Zero ;
554+ let count = 0 ;
555+ const isFromLocal = ( options ?. source ?? "chain" ) === "local" ;
556+
557+ for await ( const cell of isFromLocal
558+ ? signer . findCells ( this . filter )
559+ : signer . findCellsOnChain ( this . filter ) ) {
560+ balance += this . balanceFrom ( cell . outputData , cell . cellOutput ) ;
561+ capacity += cell . cellOutput . capacity ;
562+ count += 1 ;
563+ }
564+
565+ return {
566+ balance,
567+ capacity,
568+ count,
569+ } ;
570+ }
571+
572+ /**
573+ * Calculates the total UDT balance across all cells controlled by the signer.
574+ * This method provides a convenient way to get the complete UDT balance without
575+ * needing the additional capacity and count information.
576+ *
577+ * ⚠️ **Performance Warning**: This is an expensive operation that scans all UDT cells.
578+ * For addresses with many UDT cells, this method can be slow and resource-intensive.
579+ * Consider caching results and using sparingly in production applications.
580+ *
581+ * @param signer - The signer whose total UDT balance to calculate
582+ * @param options - Optional configuration for the calculation
583+ * @param options.source - Data source to use: "chain" (default) for on-chain data, "local" for local indexer cache
584+ * @returns A promise resolving to the total UDT balance across all cells
585+ *
586+ * @example
587+ * ```typescript
588+ * const udt = new Udt(codeOutPoint, scriptConfig);
589+ *
590+ * // Get total balance for wallet display (from chain)
591+ * const totalBalance = await udt.calculateBalance(signer);
592+ * console.log(`Total UDT balance: ${totalBalance}`);
593+ *
594+ * // Get balance from local cache for faster response
595+ * const cachedBalance = await udt.calculateBalance(signer, { source: "local" });
596+ * console.log(`Cached UDT balance: ${cachedBalance}`);
597+ *
598+ * // Convert to human-readable format (assuming 8 decimals)
599+ * const decimals = await udt.decimals();
600+ * if (decimals.res !== undefined) {
601+ * const humanReadable = ccc.fixedPointToString(totalBalance, Number(decimals.res));
602+ * console.log(`Balance: ${humanReadable} tokens`);
603+ * }
604+ *
605+ * // Check if user has sufficient balance for a transfer
606+ * const requiredAmount = ccc.fixedPointFrom(100);
607+ * if (totalBalance >= requiredAmount) {
608+ * console.log("Sufficient balance for transfer");
609+ * } else {
610+ * console.log(`Insufficient balance. Need ${requiredAmount - totalBalance} more`);
611+ * }
612+ * ```
613+ *
614+ * @remarks
615+ * **Performance Considerations:**
616+ * - This method internally calls `calculateInfo()` and extracts only the balance
617+ * - Execution time scales linearly with the number of UDT cells
618+ * - Network overhead increases with cell count when using "chain" source
619+ * - "local" source is faster but may not reflect the most recent state
620+ * - Results should be cached when used multiple times
621+ *
622+ * **Data Source Options:**
623+ * - `"chain"` (default): Queries the blockchain directly for the most up-to-date balance
624+ * - `"local"`: Uses local indexer cache, faster but potentially stale data
625+ *
626+ * **When to Use:**
627+ * - Wallet balance display
628+ * - Transfer amount validation
629+ * - Portfolio calculations
630+ * - Simple balance checks
631+ *
632+ * **When NOT to Use:**
633+ * - In transaction loops or frequent operations
634+ * - When you also need capacity or count information (use `calculateInfo()` instead)
635+ * - For transaction input/output analysis (use transaction-specific methods)
636+ *
637+ * **Alternative Methods:**
638+ * - Use `calculateInfo()` if you need additional information beyond balance
639+ * - Use `getInputsBalance()` for transaction input analysis
640+ * - Use `getOutputsBalance()` for transaction output analysis
641+ */
642+ async calculateBalance (
643+ signer : ccc . Signer ,
644+ options ?: { source ?: "chain" | "local" | null } ,
645+ ) : Promise < ccc . Num > {
646+ return ( await this . calculateInfo ( signer , options ) ) . balance ;
647+ }
648+
476649 /**
477650 * Adds the UDT script code as a cell dependency to the transaction.
478651 * This method ensures that the transaction includes the necessary cell dependency
@@ -772,7 +945,7 @@ export class Udt extends ssri.Trait {
772945 }
773946
774947 return [
775- acc [ 0 ] + ccc . udtBalanceFrom ( outputData ) ,
948+ acc [ 0 ] + this . balanceFrom ( outputData , cellOutput ) ,
776949 acc [ 1 ] + cellOutput . capacity ,
777950 acc [ 2 ] + 1 ,
778951 ] ;
@@ -873,7 +1046,7 @@ export class Udt extends ssri.Trait {
8731046 }
8741047
8751048 return [
876- acc [ 0 ] + ccc . udtBalanceFrom ( tx . outputsData [ i ] ) ,
1049+ acc [ 0 ] + this . balanceFrom ( tx . outputsData [ i ] , output ) ,
8771050 acc [ 1 ] + output . capacity ,
8781051 acc [ 2 ] + 1 ,
8791052 ] ;
@@ -994,7 +1167,7 @@ export class Udt extends ssri.Trait {
9941167 * tx,
9951168 * signer,
9961169 * ([balanceAcc, capacityAcc], cell) => {
997- * const balance = ccc.udtBalanceFrom (cell.outputData);
1170+ * const balance = udt.balanceFrom (cell.outputData, cell.cellOutput );
9981171 * const newBalance = balanceAcc + balance;
9991172 * const newCapacity = capacityAcc + cell.cellOutput.capacity;
10001173 *
@@ -1129,10 +1302,10 @@ export class Udt extends ssri.Trait {
11291302 } = await this . completeInputs (
11301303 tx ,
11311304 from ,
1132- ( [ balanceAcc , capacityAcc ] , { cellOutput : { capacity } , outputData } ) => {
1133- const balance = ccc . udtBalanceFrom ( outputData ) ;
1305+ ( [ balanceAcc , capacityAcc ] , { cellOutput, outputData } ) => {
1306+ const balance = this . balanceFrom ( outputData , cellOutput ) ;
11341307 const balanceBurned = balanceAcc + balance ;
1135- const capacityBurned = capacityAcc + capacity ;
1308+ const capacityBurned = capacityAcc + cellOutput . capacity ;
11361309
11371310 // Try to provide enough capacity with UDT cells to avoid extra occupation
11381311 return balanceBurned >= ccc . Zero && capacityBurned >= ccc . Zero
@@ -1354,6 +1527,7 @@ export class Udt extends ssri.Trait {
13541527 ) {
13551528 const tx = ccc . Transaction . from ( txLike ) ;
13561529 const index = Number ( ccc . numFrom ( indexLike ) ) ;
1530+ const output = tx . outputs [ index ] ;
13571531 const outputData = ccc . bytesFrom ( tx . outputsData [ index ] ) ;
13581532
13591533 if ( ! this . isUdt ( { cellOutput : tx . outputs [ index ] , outputData } ) ) {
@@ -1366,7 +1540,7 @@ export class Udt extends ssri.Trait {
13661540 ( tx , balance , shouldModify ) => {
13671541 if ( shouldModify ) {
13681542 const balanceData = ccc . numLeToBytes (
1369- ccc . udtBalanceFrom ( outputData ) + balance ,
1543+ this . balanceFrom ( outputData , output ) + balance ,
13701544 16 ,
13711545 ) ;
13721546
0 commit comments