diff --git a/PlatformSampleGameServer.csproj b/PlatformSampleGameServer.csproj
index 4b78c4c..39db74f 100644
--- a/PlatformSampleGameServer.csproj
+++ b/PlatformSampleGameServer.csproj
@@ -22,6 +22,6 @@
-
+
diff --git a/Services/EnjinService.cs b/Services/EnjinService.cs
index ca0b9b3..6749f3e 100644
--- a/Services/EnjinService.cs
+++ b/Services/EnjinService.cs
@@ -20,7 +20,9 @@ namespace PlatformSampleGameServer.Services;
//
// - There are no event subscriptions in v3. After submitting a transaction
// we poll GetTransaction until State is terminal
-// (Finalized | Failed | Abandoned | Timeout).
+// (Finalized | Failed | Abandoned | Timeout). Finalized alone does not mean
+// success: a batch extrinsic still finalizes on-chain when wrapped items
+// fail to dispatch, so we also require Error to be null.
//
// - The daemon wallet (configured DaemonWalletAddress) owns the collection
// and mints tokens to player wallets. Per-player melts and transfers are
@@ -625,7 +627,11 @@ CancellationToken ct
ct.ThrowIfCancellationRequested();
var query = new QueryQueryBuilder().WithGetTransaction(
- new TransactionQueryBuilder().WithUuid().WithState(),
+ new TransactionQueryBuilder()
+ .WithUuid()
+ .WithState()
+ .WithError()
+ .WithFailedItemIndexes(),
_network,
_chain,
uuid: uuid
@@ -642,6 +648,17 @@ CancellationToken ct
switch (txn.State)
{
case TransactionStateEnum.Finalized:
+ // A non-null Error under Finalized means the extrinsic made it
+ // on-chain but one or more wrapped batch items did not dispatch,
+ // so the requested work did not fully happen.
+ if (!string.IsNullOrEmpty(txn.Error))
+ {
+ throw new InvalidOperationException(
+ $"Transaction {uuid} ({description}) finalized but reported an error: "
+ + $"{txn.Error}{DescribeFailedItems(txn.FailedItemIndexes)}"
+ );
+ }
+
_log.LogInformation(
"Transaction {Uuid} ({Desc}) finalized after {Elapsed:F0}s.",
uuid,
@@ -655,6 +672,12 @@ CancellationToken ct
case TransactionStateEnum.Timeout:
throw new InvalidOperationException(
$"Transaction {uuid} ({description}) ended in terminal state {txn.State}."
+ + (
+ string.IsNullOrEmpty(txn.Error)
+ ? string.Empty
+ : $" Error: {txn.Error}"
+ )
+ + DescribeFailedItems(txn.FailedItemIndexes)
);
default:
@@ -700,6 +723,13 @@ await Task.Delay(
// Helpers
// ------------------------------------------------------------------
+ // Zero-based positions of the batch items that failed to dispatch, when the
+ // platform reports them (CONTINUE_ON_ERROR batches only).
+ private static string DescribeFailedItems(ICollection? failedItemIndexes) =>
+ failedItemIndexes is { Count: > 0 }
+ ? $" Failed batch items: {string.Join(", ", failedItemIndexes)}."
+ : string.Empty;
+
private BigInteger RequireCollectionId() =>
_state.CollectionId
?? throw new InvalidOperationException(