Skip to content

Commit

Permalink
fix(starknet): waitForAcceptance now chek on finality status (#441)
Browse files Browse the repository at this point in the history
  • Loading branch information
ptisserand committed Feb 28, 2025
1 parent 4841e40 commit 6b987fe
Showing 1 changed file with 101 additions and 33 deletions.
134 changes: 101 additions & 33 deletions packages/starknet/lib/src/util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool> waitForState({
class _Status {
final String execution;
final String finality;
_Status(this.execution, this.finality);
}

Future<bool> _waitForTransactionStatus({
required String transactionHash,
required Provider provider,
required List<String> 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) {
Expand All @@ -67,7 +74,7 @@ Future<bool> 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)',
);
Expand All @@ -77,24 +84,86 @@ Future<bool> 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<void>.delayed(interval);
}
await Future<void>.delayed(interval);
}
return succeed;
}

/// Returns `true` when [transactionHash] status is in `{PENDING, ACCEPTED_ON_L2, ACCEPTED_ON_L2}`
Future<bool> _waitForExecutionStatus({
required String transactionHash,
required Provider provider,
required List<String> 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<bool> _waitForFinalityStatus({
required String transactionHash,
required Provider provider,
required List<String> 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<bool> waitForState({
required String transactionHash,
required Provider provider,
required List<String> 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<bool> waitForTransaction({
required String transactionHash,
Expand All @@ -103,13 +172,10 @@ Future<bool> 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,
Expand All @@ -118,10 +184,12 @@ Future<bool> 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<bool> waitForAcceptance({
required String transactionHash,
Expand All @@ -130,10 +198,10 @@ Future<bool> 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,
Expand Down

0 comments on commit 6b987fe

Please sign in to comment.