@@ -469,18 +469,28 @@ export class SDKClient {
469469 const requestIdPrefix = formatRequestIdMessage ( requestId ) ;
470470 const currentDateNow = Date . now ( ) ;
471471 try {
472- const shouldLimit = this . hbarLimiter . shouldLimit ( currentDateNow , SDKClient . transactionMode , callerName ) ;
473- if ( shouldLimit ) {
472+ // check hbar limit before executing transaction
473+ if ( this . hbarLimiter . shouldLimit ( currentDateNow , SDKClient . recordMode , callerName ) ) {
474474 throw predefined . HBAR_RATE_LIMIT_EXCEEDED ;
475475 }
476476
477+ // execute transaction
477478 this . logger . info ( `${ requestIdPrefix } Execute ${ transactionType } transaction` ) ;
478- const resp = await transaction . execute ( this . clientMain ) ;
479+ const transactionResponse = await transaction . execute ( this . clientMain ) ;
480+
481+ // retrieve and capture transaction fee in metrics and rate limiter class
482+ await this . executeGetTransactionRecord (
483+ transactionResponse ,
484+ callerName ,
485+ interactingEntity ,
486+ transaction . constructor . name ,
487+ requestId ,
488+ ) ;
479489
480490 this . logger . info (
481- `${ requestIdPrefix } ${ resp . transactionId } ${ callerName } ${ transactionType } status: ${ Status . Success } (${ Status . Success . _code } )` ,
491+ `${ requestIdPrefix } ${ transactionResponse . transactionId } ${ callerName } ${ transactionType } status: ${ Status . Success } (${ Status . Success . _code } )` ,
482492 ) ;
483- return resp ;
493+ return transactionResponse ;
484494 } catch ( e : any ) {
485495 const sdkClientError = new SDKClientError ( e , e . message ) ;
486496 let transactionFee : number | Hbar = 0 ;
@@ -526,87 +536,88 @@ export class SDKClient {
526536 } ;
527537
528538 async executeGetTransactionRecord (
529- resp : TransactionResponse ,
530- transactionName : string ,
539+ transactionResponse : TransactionResponse ,
531540 callerName : string ,
532541 interactingEntity : string ,
542+ txConstructorName : string ,
533543 requestId ?: string ,
534- ) : Promise < TransactionRecord > {
544+ ) {
535545 const requestIdPrefix = formatRequestIdMessage ( requestId ) ;
536546 const currentDateNow = Date . now ( ) ;
547+ let gasUsed : any = 0 ;
548+ let transactionFee : number = 0 ;
549+ const transactionId : string = transactionResponse . transactionId . toString ( ) ;
550+
537551 try {
538- if ( ! resp . getRecord ) {
552+ if ( ! transactionResponse . getRecord ) {
539553 throw new SDKClientError (
540554 { } ,
541- `${ requestIdPrefix } Invalid response format, expected record availability: ${ JSON . stringify ( resp ) } ` ,
555+ `${ requestIdPrefix } Invalid response format, expected record availability: ${ JSON . stringify (
556+ transactionResponse ,
557+ ) } `,
542558 ) ;
543559 }
544- const shouldLimit = this . hbarLimiter . shouldLimit ( currentDateNow , SDKClient . recordMode , transactionName ) ;
545- if ( shouldLimit ) {
560+ if ( this . hbarLimiter . shouldLimit ( currentDateNow , SDKClient . recordMode , callerName ) ) {
546561 throw predefined . HBAR_RATE_LIMIT_EXCEEDED ;
547562 }
548563
549- const transactionRecord : TransactionRecord = await resp . getRecord ( this . clientMain ) ;
550- const cost = transactionRecord . transactionFee . toTinybars ( ) . toNumber ( ) ;
551- this . hbarLimiter . addExpense ( cost , currentDateNow ) ;
552- this . logger . info (
553- `${ requestIdPrefix } ${ resp . transactionId } ${ callerName } ${ transactionName } record status: ${ Status . Success } (${ Status . Success . _code } ), cost: ${ transactionRecord . transactionFee } ` ,
554- ) ;
555- this . captureMetrics (
556- SDKClient . transactionMode ,
557- transactionName ,
558- transactionRecord . receipt . status ,
559- cost ,
560- transactionRecord ?. contractFunctionResult ?. gasUsed ,
561- callerName ,
562- interactingEntity ,
563- ) ;
564-
565- this . hbarLimiter . addExpense ( cost , currentDateNow ) ;
566-
567- return transactionRecord ;
564+ // get transactionRecord
565+ const transactionRecord : TransactionRecord = await transactionResponse . getRecord ( this . clientMain ) ;
566+
567+ // get transactionFee and gasUsed for metrics
568+ /**
569+ * @todo : Determine how to separate the fee charged exclusively by the operator because
570+ * the transactionFee below includes the entire charges of the transaction,
571+ * with some portions paid by tx.from, not the operator.
572+ */
573+ transactionFee = transactionRecord . transactionFee . toTinybars ( ) . toNumber ( ) ;
574+ gasUsed = transactionRecord ?. contractFunctionResult ?. gasUsed . toNumber ( ) ;
568575 } catch ( e : any ) {
569- // capture sdk record retrieval errors and shorten familiar stack trace
570- const sdkClientError = new SDKClientError ( e , e . message ) ;
571- let transactionFee : number | Hbar = 0 ;
572- if ( sdkClientError . isValidNetworkError ( ) ) {
573- try {
574- // pull transaction record for fee
575- const transactionRecord = await new TransactionRecordQuery ( )
576- . setTransactionId ( resp . transactionId ! )
577- . setNodeAccountIds ( [ resp . nodeId ] )
578- . setValidateReceiptStatus ( false )
579- . execute ( this . clientMain ) ;
580- transactionFee = transactionRecord . transactionFee ;
581-
582- this . captureMetrics (
583- SDKClient . transactionMode ,
584- transactionName ,
585- sdkClientError . status ,
586- transactionFee . toTinybars ( ) . toNumber ( ) ,
587- transactionRecord ?. contractFunctionResult ?. gasUsed ,
588- callerName ,
589- interactingEntity ,
590- ) ;
591-
592- this . hbarLimiter . addExpense ( transactionFee . toTinybars ( ) . toNumber ( ) , currentDateNow ) ;
593- } catch ( err : any ) {
594- const recordQueryError = new SDKClientError ( err , err . message ) ;
595- this . logger . error (
596- recordQueryError ,
597- `${ requestIdPrefix } Error raised during TransactionRecordQuery for ${ resp . transactionId } ` ,
598- ) ;
599- }
576+ try {
577+ // get transactionFee and gasUsed for metrics
578+ // Only utilize SDK query when .getRecord throws an error. This can limit the number of calls to the SDK.
579+ const transactionRecord = await new TransactionRecordQuery ( )
580+ . setTransactionId ( transactionId )
581+ . setNodeAccountIds ( [ transactionResponse . nodeId ] )
582+ . setValidateReceiptStatus ( false )
583+ . execute ( this . clientMain ) ;
584+ transactionFee = transactionRecord . transactionFee . toTinybars ( ) . toNumber ( ) ;
585+ gasUsed = transactionRecord ?. contractFunctionResult ?. gasUsed . toNumber ( ) ;
586+ } catch ( err : any ) {
587+ const recordQueryError = new SDKClientError ( err , err . message ) ;
588+ this . logger . error (
589+ recordQueryError ,
590+ `${ requestIdPrefix } Error raised during TransactionRecordQuery for ${ transactionId } ` ,
591+ ) ;
600592 }
601593
594+ // log error from getRecord
595+ const sdkClientError = new SDKClientError ( e , e . message ) ;
602596 this . logger . debug (
603- `${ requestIdPrefix } ${ resp . transactionId } ${ callerName } ${ transactionName } record status: ${ sdkClientError . status } (${ sdkClientError . status . _code } ), cost: ${ transactionFee } ` ,
597+ `${ requestIdPrefix } ${ transactionId } ${ callerName } record status: ${ sdkClientError . status } (${ sdkClientError . status . _code } ), cost: ${ transactionFee } ` ,
604598 ) ;
605599
606- if ( e instanceof JsonRpcError ) {
607- throw predefined . HBAR_RATE_LIMIT_EXCEEDED ;
608- }
609- throw sdkClientError ;
600+ // Throw WRONG_NONCE error as more error handling logic for WRONG_NONCE is awaited in eth.sendRawTransactionErrorHandler(). Otherwise, move on and return transactionResponse eventually.
601+ if ( e . status && e . status . toString ( ) === constants . TRANSACTION_RESULT_STATUS . WRONG_NONCE ) throw sdkClientError ;
602+ } finally {
603+ /**
604+ * @note Retrieving and capturing the charged transaction fees at the end of the flow
605+ * ensures these fees are eventually captured in the metrics and rate limiter class,
606+ * even if SDK transactions fail at any point.
607+ */
608+ this . logger . trace (
609+ `${ requestId } Capturing HBAR charged transaction fee: transactionId=${ transactionId } , txConstructorName=${ txConstructorName } , callerName=${ callerName } , txChargedFee=${ transactionFee } tinybars` ,
610+ ) ;
611+ this . hbarLimiter . addExpense ( transactionFee , currentDateNow ) ;
612+ this . captureMetrics (
613+ SDKClient . transactionMode ,
614+ txConstructorName ,
615+ Status . Success ,
616+ transactionFee ,
617+ gasUsed ,
618+ callerName ,
619+ interactingEntity ,
620+ ) ;
610621 }
611622 }
612623
@@ -786,6 +797,7 @@ export class SDKClient {
786797 */
787798 public deleteFile = async ( fileId : FileId , requestId ?: string , callerName ?: string , interactingEntity ?: string ) => {
788799 // format request ID msg
800+ const currentDateNow = Date . now ( ) ;
789801 const requestIdPrefix = formatRequestIdMessage ( requestId ) ;
790802
791803 try {
@@ -801,7 +813,8 @@ export class SDKClient {
801813 // get fileDeleteTx's record
802814 const deleteFileRecord = await fileDeleteTxResponse . getRecord ( this . clientMain ) ;
803815
804- // capture metrics
816+ // capture transactionFee in metrics and HBAR limiter class
817+ this . hbarLimiter . addExpense ( deleteFileRecord . transactionFee . toTinybars ( ) . toNumber ( ) , currentDateNow ) ;
805818 this . captureMetrics (
806819 SDKClient . transactionMode ,
807820 fileDeleteTx . constructor . name ,
0 commit comments