diff --git a/packages/starknet/lib/src/util.dart b/packages/starknet/lib/src/util.dart index 817f1b1d..5f257c78 100644 --- a/packages/starknet/lib/src/util.dart +++ b/packages/starknet/lib/src/util.dart @@ -17,38 +17,45 @@ const _defaultInterval = Duration(seconds: 5); const _defaultMaxRetries = 60; const _errorStates = ['REVERTED']; -/// Returns `true` when [transactionHash] status is in [states] -/// -/// The [provider] will be query with a period of [interval] -/// This function will try [maxRetries] query before setting transaction status to `NOT_RECEIVED` -/// An optional [debugLog] function could be use to display internal debug log -/// Return `false` in case of error -Future waitForState({ +class _Status { + final String execution; + final String finality; + _Status(this.execution, this.finality); +} + +Future _waitForTransactionStatus({ required String transactionHash, required Provider provider, - required List states, - Duration interval = _defaultInterval, - int maxRetries = _defaultMaxRetries, + required bool Function(_Status status) checkStatus, + required Duration interval, + required int maxRetries, void Function(dynamic message)? debugLog, }) async { var count = 0; var done = false; var succeed = false; - var status = 'UNKNOWN'; + var _status = _Status('UNKNOWN', 'UNKNOWN'); + final txHash = Felt.fromHexString(transactionHash); while (!done) { final receipt = await provider.getTransactionReceipt(txHash); receipt.when( result: (result) { result.map( - declareTxnReceipt: (receipt) => status = receipt.execution_status, - deployTxnReceipt: (receipt) => status = receipt.execution_status, - deployAccountTxnReceipt: (receipt) => - status = receipt.execution_status, - l1HandlerTxnReceipt: (receipt) => status = receipt.execution_status, - pendingDeployTxnReceipt: (receipt) => status = 'PENDING', - pendingCommonReceiptProperties: (receipt) => status = 'PENDING', - invokeTxnReceipt: (receipt) => status = receipt.execution_status, + invokeTxnReceipt: (receipt) => _status = + _Status(receipt.execution_status, receipt.finality_status), + declareTxnReceipt: (receipt) => _status = + _Status(receipt.execution_status, receipt.finality_status), + deployTxnReceipt: (receipt) => _status = + _Status(receipt.execution_status, receipt.finality_status), + deployAccountTxnReceipt: (receipt) => _status = + _Status(receipt.execution_status, receipt.finality_status), + l1HandlerTxnReceipt: (receipt) => _status = + _Status(receipt.execution_status, receipt.finality_status), + pendingDeployTxnReceipt: (receipt) => + _status = _Status('PENDING', 'PENDING'), + pendingCommonReceiptProperties: (receipt) => + _status = _Status('PENDING', 'PENDING'), ); }, error: (error) { @@ -67,7 +74,7 @@ Future waitForState({ JsonRpcApiErrorCode.TXN_HASH_NOT_FOUND_PRE_0_4_0) && (count < maxRetries)) { count += 1; - status = 'UNKNOWN'; + _status = _Status('UNKNOWN', 'UNKNOWN'); debugLog?.call( 'Waiting for status of $transactionHash ($count / $maxRetries)', ); @@ -77,24 +84,86 @@ Future waitForState({ } }, ); - if (_errorStates.contains(status)) { + if (_errorStates.contains(_status.execution) || + _errorStates.contains(_status.finality)) { succeed = false; break; } - if (states.contains(status)) { + if (checkStatus(_status)) { succeed = true; break; - } else { - await Future.delayed(interval); } + await Future.delayed(interval); } return succeed; } -/// Returns `true` when [transactionHash] status is in `{PENDING, ACCEPTED_ON_L2, ACCEPTED_ON_L2}` +Future _waitForExecutionStatus({ + required String transactionHash, + required Provider provider, + required List statuses, + required Duration interval, + required int maxRetries, + void Function(dynamic message)? debugLog, +}) async { + return _waitForTransactionStatus( + transactionHash: transactionHash, + provider: provider, + checkStatus: (status) => statuses.contains(status.execution), + interval: interval, + maxRetries: maxRetries, + debugLog: debugLog, + ); +} + +Future _waitForFinalityStatus({ + required String transactionHash, + required Provider provider, + required List statuses, + required Duration interval, + required int maxRetries, + void Function(dynamic message)? debugLog, +}) async { + return _waitForTransactionStatus( + transactionHash: transactionHash, + provider: provider, + checkStatus: (status) => statuses.contains(status.finality), + interval: interval, + maxRetries: maxRetries, + debugLog: debugLog, + ); +} + +/// Returns `true` when [transactionHash] execution status is in [states] +/// +/// The [provider] will be query with a period of [interval] +/// This function will try [maxRetries] query before setting transaction status to `NOT_RECEIVED` +/// An optional [debugLog] function could be use to display internal debug log +/// Return `false` in case of error +Future waitForState({ + required String transactionHash, + required Provider provider, + required List states, + Duration interval = _defaultInterval, + int maxRetries = _defaultMaxRetries, + void Function(dynamic message)? debugLog, +}) async { + return _waitForExecutionStatus( + transactionHash: transactionHash, + provider: provider, + statuses: states, + interval: interval, + maxRetries: maxRetries, + debugLog: debugLog, + ); +} + +/// Returns `true` when [transactionHash] execution status is in `{PENDING, SUCEEDED}` /// /// /// The [provider] will be query with a period of [interval] +/// This function will try [maxRetries] query before setting transaction status to `NOT_RECEIVED` +/// An optional [debugLog] function could be use to display internal debug log /// Return `false` in case of error Future waitForTransaction({ required String transactionHash, @@ -103,13 +172,10 @@ Future waitForTransaction({ int maxRetries = _defaultMaxRetries, void Function(dynamic message)? debugLog, }) async { - return waitForState( + return _waitForExecutionStatus( transactionHash: transactionHash, provider: provider, - states: [ - 'PENDING', - 'SUCCEEDED', - ], + statuses: ['PENDING', 'SUCCEEDED'], interval: interval, maxRetries: maxRetries, debugLog: debugLog, @@ -118,10 +184,12 @@ Future waitForTransaction({ /// Returns `true` if [transactionHash] is accepted /// -/// A transaction is **accepted** if its state is +/// A transaction is **accepted** if its finality status is /// `ACCEPTED_ON_L2` or `ACCEPTED_ON_L1` /// /// The [provider] will be query with a period of [interval] +/// This function will try [maxRetries] query before setting transaction status to `NOT_RECEIVED` +/// An optional [debugLog] function could be use to display internal debug log /// Return `false` in case of error Future waitForAcceptance({ required String transactionHash, @@ -130,10 +198,10 @@ Future waitForAcceptance({ int maxRetries = _defaultMaxRetries, void Function(dynamic message)? debugLog, }) async { - return waitForState( + return _waitForFinalityStatus( transactionHash: transactionHash, provider: provider, - states: ['SUCCEEDED'], + statuses: ['ACCEPTED_ON_L1', 'ACCEPTED_ON_L2'], interval: interval, maxRetries: maxRetries, debugLog: debugLog,