From 3f86ba0a5f8e288d48468c33540f36edf7fe4406 Mon Sep 17 00:00:00 2001 From: Lea Lobanov <44328396+lealobanov@users.noreply.github.com> Date: Tue, 17 Jun 2025 03:19:37 +0900 Subject: [PATCH] Revert "Parsing Cadence resource type (#35)" This reverts commit 3b83e21dccf78766a8300defbe3b5c177b12719c. --- build.gradle.kts | 2 +- .../kotlin/org/onflow/flow/evm/EVMManager.kt | 8 +- .../org/onflow/flow/infrastructure/Cadence.kt | 124 +------- .../onflow/flow/infrastructure/Serializer.kt | 49 --- .../org/onflow/flow/models/Serializers.kt | 9 +- .../onflow/flow/models/TransactionResult.kt | 5 +- .../org/onflow/flow/FlowTransactionTests.kt | 285 ------------------ 7 files changed, 13 insertions(+), 469 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 385fcf7..401d068 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -22,7 +22,7 @@ allprojects { } group = "org.onflow.flow" - val defaultVersion = "0.0.17" + val defaultVersion = "0.0.16" version = System.getenv("GITHUB_REF")?.split('/')?.last() ?: defaultVersion } diff --git a/flow/src/commonMain/kotlin/org/onflow/flow/evm/EVMManager.kt b/flow/src/commonMain/kotlin/org/onflow/flow/evm/EVMManager.kt index ddaf4d0..54d943f 100644 --- a/flow/src/commonMain/kotlin/org/onflow/flow/evm/EVMManager.kt +++ b/flow/src/commonMain/kotlin/org/onflow/flow/evm/EVMManager.kt @@ -84,15 +84,15 @@ class EVMManager(chainId: ChainId) { val script = scriptLoader.load("create_coa", "common/evm") val latestBlock = blocksApi.getBlock() val proposerAccount = accountsApi.getAccount(proposer.base16Value) - val proposerKey = proposerAccount.keys?.firstOrNull { !it.revoked } - ?: throw IllegalArgumentException("Proposer has no non-revoked keys") + val proposerKey = proposerAccount.keys?.firstOrNull() + ?: throw IllegalArgumentException("Proposer has no keys") // Fill in keyIndex dynamically if not set signers.forEach { signer -> if (signer.keyIndex == -1) { val account = accountsApi.getAccount(signer.address) - signer.keyIndex = account.keys?.firstOrNull { !it.revoked }?.index?.toInt() - ?: throw IllegalStateException("No non-revoked key found for ${signer.address}") + signer.keyIndex = account.keys?.firstOrNull()?.index?.toInt() + ?: throw IllegalStateException("No key found for ${signer.address}") } } diff --git a/flow/src/commonMain/kotlin/org/onflow/flow/infrastructure/Cadence.kt b/flow/src/commonMain/kotlin/org/onflow/flow/infrastructure/Cadence.kt index f4ac5fd..0475a8d 100644 --- a/flow/src/commonMain/kotlin/org/onflow/flow/infrastructure/Cadence.kt +++ b/flow/src/commonMain/kotlin/org/onflow/flow/infrastructure/Cadence.kt @@ -6,7 +6,6 @@ import io.ktor.util.* import io.ktor.utils.io.core.* import kotlinx.serialization.* import kotlinx.serialization.json.* -import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.plus import kotlinx.serialization.modules.polymorphic @@ -42,7 +41,6 @@ import org.onflow.flow.infrastructure.Cadence.Value.UInt32Value import org.onflow.flow.infrastructure.Cadence.Value.UInt64Value import org.onflow.flow.infrastructure.Cadence.Value.UInt8Value import org.onflow.flow.infrastructure.Cadence.Value.UIntValue -import org.onflow.flow.infrastructure.Cadence.Value.UnknownValue import org.onflow.flow.infrastructure.Cadence.Value.VoidValue import org.onflow.flow.infrastructure.Cadence.Value.Word16Value import org.onflow.flow.infrastructure.Cadence.Value.Word32Value @@ -133,7 +131,6 @@ class Cadence { subclass(TypeValue::class) subclass(PathValue::class) subclass(CapabilityValue::class) - subclass(UnknownValue::class) } } @@ -142,12 +139,6 @@ class Cadence { ignoreUnknownKeys = true isLenient = true classDiscriminator = "type" - allowStructuredMapKeys = true - explicitNulls = false - encodeDefaults = false - allowSpecialFloatingPointValues = true - useArrayPolymorphism = false - allowTrailingComma = true } } } @@ -169,86 +160,11 @@ class Cadence { companion object { fun decodeFromJsonElement(jsonElement: JsonElement): Value { - return tryDecodeFromJsonElement(jsonElement) ?: VoidValue() - } - - /** - * Robust JSON element decoding that handles various edge cases from Flow network responses - */ - private fun tryDecodeFromJsonElement(jsonElement: JsonElement): Value? { - return try { - // First, try the standard approach - jsonSerializer.decodeFromJsonElement(jsonElement) - } catch (e: Exception) { - // If standard decoding fails, try to handle specific cases - when { - jsonElement is JsonObject -> handleComplexJsonObject(jsonElement) - else -> null - } - } - } - - /** - * Handle complex JSON objects that might have non-standard type structures - */ - private fun handleComplexJsonObject(jsonObject: JsonObject): Value? { - return try { - val typeElement = jsonObject["type"] - - when { - // Handle empty type field - typeElement == null || typeElement.jsonPrimitive.content.isEmpty() -> { - // Try to infer type from structure or default to VoidValue - VoidValue() - } - - // Handle complex type objects (like Kind structures) - typeElement is JsonObject -> { - // For complex type objects, try to extract the kind or default to VoidValue - VoidValue() - } - - else -> { - // Try to decode with the type as a string - val typeString = typeElement.jsonPrimitive.content - decodeValueWithType(typeString, jsonObject) - } - } - } catch (e: Exception) { - // Final fallback - return VoidValue for any complex structure we can't parse - VoidValue() - } - } - - /** - * Decode a value given a specific type string and JSON object - */ - private fun decodeValueWithType(typeString: String, jsonObject: JsonObject): Value? { - return try { - // Create a simplified JSON object with just the type and value - val simplifiedJson = buildJsonObject { - put("type", typeString) - jsonObject["value"]?.let { put("value", it) } - } - jsonSerializer.decodeFromJsonElement(simplifiedJson) - } catch (e: Exception) { - null - } + return jsonSerializer.decodeFromJsonElement(jsonElement) } fun decodeFromJson(jsonString: String): Value { - return try { - jsonSerializer.decodeFromString(jsonString) - } catch (e: Exception) { - // Try to parse as JsonElement first and then use robust decoding - try { - val jsonElement = Json.parseToJsonElement(jsonString) - decodeFromJsonElement(jsonElement) - } catch (e2: Exception) { - // Final fallback - VoidValue() - } - } + return jsonSerializer.decodeFromString(jsonString) } fun encodeToJsonString(Value: Value): String { @@ -256,12 +172,7 @@ class Cadence { } fun decodeFromBase64(base64String: String): Value { - return try { - decodeFromJson(base64String.decodeBase64Bytes().decodeToString()) - } catch (e: Exception) { - // Graceful handling of base64 decoding failures - VoidValue() - } + return decodeFromJson(base64String.decodeBase64Bytes().decodeToString()) } } @@ -276,7 +187,6 @@ class Cadence { fun decodeToAny(): Any? { return when (this) { is VoidValue -> { null } - is UnknownValue -> { value } is OptionalValue -> { value?.decodeToAny() } is ArrayValue -> { value.map { it.decodeToAny() } } @@ -490,14 +400,6 @@ class Cadence { @Serializable @SerialName(TYPE_CAPABILITY) open class CapabilityValue(override val value: Capability) : Value() - - /** - * Fallback value class for unknown or malformed Cadence types - * Used when the type field is empty, missing, or contains complex objects - */ - @Serializable - @SerialName("") - data class UnknownValue(override val value: String? = null) : Value() } companion object { @@ -576,7 +478,7 @@ class Cadence { fun path(domain: PathDomain, identifier: String) = Value.PathValue(Path(domain, identifier)) fun type(value: TypeEntry) = Value.TypeValue(value) - fun type(kind: String, typeID: String? = null) = type(TypeEntry(Kind(kind = kind, typeID = typeID))) + fun type(value: Cadence.Kind) = type(TypeEntry(value)) fun capability(value: Capability) = Value.CapabilityValue(value) fun capability(path: String, address: String, borrowType: Type) = capability(Capability(path, address, borrowType)) @@ -603,22 +505,8 @@ class Cadence { //TODO: Handle more types @Serializable - data class Kind ( - val kind: String? = null, - val typeID: String? = null, - val type: String? = null, - val fields: List? = null, - val initializers: List? = null, - val elementType: JsonElement? = null, - val key: JsonElement? = null, - val value: JsonElement? = null, - val size: Int? = null, - val staticType: JsonElement? = null, - val authorization: JsonElement? = null, - val borrowType: JsonElement? = null, - val path: String? = null - ) - + data class Kind (val kind: Type, val typeID : String?, val type: String?) + @Serializable data class Capability(val path: String, val address: String, val borrowType: Type) diff --git a/flow/src/commonMain/kotlin/org/onflow/flow/infrastructure/Serializer.kt b/flow/src/commonMain/kotlin/org/onflow/flow/infrastructure/Serializer.kt index c62f964..5d75a48 100644 --- a/flow/src/commonMain/kotlin/org/onflow/flow/infrastructure/Serializer.kt +++ b/flow/src/commonMain/kotlin/org/onflow/flow/infrastructure/Serializer.kt @@ -12,7 +12,6 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.* -import org.onflow.flow.models.TransactionExecution object ByteCadenceSerializer : KSerializer { override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("value", PrimitiveKind.STRING) @@ -375,54 +374,6 @@ object CadenceTypeSerializer : KSerializer { } } -/** - * A safe TransactionExecution serializer that can handle both simple enum strings and complex objects - * This prevents parsing failures when the Flow network returns complex objects instead of simple strings - */ -object SafeTransactionExecutionSerializer : KSerializer { - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("SafeTransactionExecution", PrimitiveKind.STRING) - - override fun serialize(encoder: Encoder, value: TransactionExecution?) { - if (value != null) { - encoder.encodeString(value.value) - } else { - encoder.encodeNull() - } - } - - override fun deserialize(decoder: Decoder): TransactionExecution? { - // First, check if we have a JsonDecoder to work with - val jsonDecoder = decoder as? JsonDecoder - if (jsonDecoder != null) { - return try { - val element = jsonDecoder.decodeJsonElement() - when (element) { - is JsonPrimitive -> { - // Normal case - simple string value - TransactionExecution.decode(element.content) - } - is JsonObject -> { - // Complex object case - try to extract meaningful info or default to null - // For now, we'll just return null for complex objects - null - } - else -> null - } - } catch (e: Exception) { - null - } - } else { - // For non-JSON decoders, try string decoding - return try { - val stringValue = decoder.decodeString() - TransactionExecution.decode(stringValue) - } catch (e: Exception) { - null - } - } - } -} - //open class NewNumberSerializer(val type: KClass ): KSerializer { // override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("value", PrimitiveKind.STRING) // override fun serialize(encoder: Encoder, value: T) = encoder.encodeString(value.toString()) diff --git a/flow/src/commonMain/kotlin/org/onflow/flow/models/Serializers.kt b/flow/src/commonMain/kotlin/org/onflow/flow/models/Serializers.kt index 4134670..75b2fbc 100644 --- a/flow/src/commonMain/kotlin/org/onflow/flow/models/Serializers.kt +++ b/flow/src/commonMain/kotlin/org/onflow/flow/models/Serializers.kt @@ -34,14 +34,7 @@ object Base64HexSerializer : KSerializer { object CadenceBase64Serializer : KSerializer { override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("CadenceBase64", PrimitiveKind.STRING) override fun serialize(encoder: Encoder, value: Cadence.Value) = encoder.encodeString(value.encodeBase64()) - override fun deserialize(decoder: Decoder): Cadence.Value { - return try { - Cadence.Value.decodeFromBase64(decoder.decodeString()) - } catch (e: Exception) { - // Graceful fallback for problematic Cadence values - Cadence.void() - } - } + override fun deserialize(decoder: Decoder): Cadence.Value = Cadence.Value.decodeFromBase64(decoder.decodeString()) } class CadenceBase64ListSerializer : KSerializer> { diff --git a/flow/src/commonMain/kotlin/org/onflow/flow/models/TransactionResult.kt b/flow/src/commonMain/kotlin/org/onflow/flow/models/TransactionResult.kt index 9b1a35b..3adb073 100644 --- a/flow/src/commonMain/kotlin/org/onflow/flow/models/TransactionResult.kt +++ b/flow/src/commonMain/kotlin/org/onflow/flow/models/TransactionResult.kt @@ -1,7 +1,6 @@ package org.onflow.flow.models import kotlinx.serialization.* import org.onflow.flow.infrastructure.SafeStringSerializer -import org.onflow.flow.infrastructure.SafeTransactionExecutionSerializer /** * @@ -34,9 +33,7 @@ data class TransactionResult ( @SerialName(value = "events") @Required val events: List, - @SerialName(value = "execution") - @Serializable(with = SafeTransactionExecutionSerializer::class) - val execution: TransactionExecution? = null, + @SerialName(value = "execution") val execution: TransactionExecution? = null, @SerialName(value = "_links") val links: Links? = null diff --git a/flow/src/commonTest/kotlin/org/onflow/flow/FlowTransactionTests.kt b/flow/src/commonTest/kotlin/org/onflow/flow/FlowTransactionTests.kt index d95680e..49d22ca 100644 --- a/flow/src/commonTest/kotlin/org/onflow/flow/FlowTransactionTests.kt +++ b/flow/src/commonTest/kotlin/org/onflow/flow/FlowTransactionTests.kt @@ -2,15 +2,11 @@ package org.onflow.flow import com.ionspin.kotlin.bignum.integer.toBigInteger import kotlinx.coroutines.runBlocking -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.jsonPrimitive import org.onflow.flow.crypto.Crypto import org.onflow.flow.infrastructure.Cadence import org.onflow.flow.models.TransactionBuilder import org.onflow.flow.models.TransactionStatus import org.onflow.flow.models.TransactionSignature -import org.onflow.flow.models.TransactionResult import org.onflow.flow.models.createSigningRLP import org.onflow.flow.models.createSigningRLPJVMStyle import org.onflow.flow.models.envelopeMessage @@ -804,285 +800,4 @@ class FlowTransactionTests { println(" Regular: $regularValue") println(" Transaction ID: ${result.id}") } - - /** - * Test complex JSON parsing for TransactionResult with ResourceValue events - * This test verifies that complex Cadence type structures with nested fields, initializers, - * and typeIDs can be properly parsed without throwing "Expected JsonPrimitive at type" errors. - */ - @Test - fun testComplexTransactionResultJsonParsing() { - // This JSON structure represents the problematic scenario that was causing parsing failures - val complexTransactionResultJson = """ - { - "block_id": "9326a6ae294eeb58f0f984b512b89f48579fea7c84d48d07bd2f316856f4ab91", - "status": "Sealed", - "status_code": 0, - "error_message": "", - "computation_used": "123", - "events": [ - { - "type": "A.231cc0dbbcffc4b7.ceMATIC.Vault.ResourceDestroyed", - "transaction_id": "4e4f0789748dc1d3e2ac3e1829d771ca31133a6609133076377bca1164c54afb", - "transaction_index": "0", - "event_index": "0", - "payload": "eyJ0eXBlIjoiRXZlbnQiLCJ2YWx1ZSI6eyJpZCI6IkEuMjMxY2MwZGJiY2ZmYzRiNy5jZU1BVElDLlZhdWx0LlJlc291cmNlRGVzdHJveWVkIiwiZmllbGRzIjpbeyJuYW1lIjoidXVpZCIsInZhbHVlIjp7InR5cGUiOiJVSW50NjQiLCJ2YWx1ZSI6IjEyMzQ1Njc4OSJ9fSx7Im5hbWUiOiJiYWxhbmNlIiwidmFsdWUiOnsidHlwZSI6IlVGaXg2NCIsInZhbHVlIjoiMC4wMDAwMDAwMCJ9fV19fQ==" - } - ], - "execution": "Success" - } - """.trimIndent() - - // Test that we can parse this complex JSON without throwing exceptions - try { - val json = Json { - ignoreUnknownKeys = true - isLenient = true - } - - val transactionResult = json.decodeFromString(complexTransactionResultJson) - - // Verify the basic fields parsed correctly - assertEquals("9326a6ae294eeb58f0f984b512b89f48579fea7c84d48d07bd2f316856f4ab91", transactionResult.blockId) - assertEquals(TransactionStatus.SEALED, transactionResult.status) - assertEquals(0, transactionResult.statusCode) - assertEquals("", transactionResult.errorMessage) - assertEquals("123", transactionResult.computationUsed) - - // Verify events parsed correctly - assertEquals(1, transactionResult.events.size) - val event = transactionResult.events[0] - assertEquals("A.231cc0dbbcffc4b7.ceMATIC.Vault.ResourceDestroyed", event.type) - assertEquals("4e4f0789748dc1d3e2ac3e1829d771ca31133a6609133076377bca1164c54afb", event.transactionId) - assertEquals("0", event.transactionIndex) - assertEquals("0", event.eventIndex) - - // Verify the payload can be decoded (base64 encoded Cadence value) - assertNotNull(event.payload) - - // Verify execution field with complex type structure - assertNotNull(transactionResult.execution) - - println("✅ Complex JSON parsing test passed successfully") - println(" - TransactionResult parsed without errors") - println(" - Events with ResourceValue payloads handled correctly") - println(" - Complex type structures with fields and initializers parsed") - - } catch (e: Exception) { - println("❌ Complex JSON parsing test failed: ${e.message}") - e.printStackTrace() - throw e - } - } - - /** - * Test parsing of problematic Cadence ResourceValue structures - * This specifically tests the JSON structure that was causing the original error: - * "Expected JsonPrimitive at type, found {...kind:Resource,typeID:...}" - */ - @Test - fun testResourceValueTypeParsing() { - // This is the exact problematic JSON structure from the error logs - val resourceTypeJson = """ - { - "type": "", - "kind": "Resource", - "typeID": "A.231cc0dbbcffc4b7.ceMATIC.Vault", - "fields": [ - { - "type": { - "kind": "UInt64" - }, - "id": "uuid" - }, - { - "type": { - "kind": "UFix64" - }, - "id": "balance" - } - ], - "initializers": [] - } - """.trimIndent() - - try { - val json = Json { - ignoreUnknownKeys = true - isLenient = true - } - - // Test that we can parse the Kind structure that was causing failures - val kind = json.decodeFromString(resourceTypeJson) - - // Verify the parsed structure - assertEquals("Resource", kind.kind) - assertEquals("A.231cc0dbbcffc4b7.ceMATIC.Vault", kind.typeID) - assertEquals("", kind.type) - assertNotNull(kind.fields) - assertEquals(2, kind.fields?.size) - assertNotNull(kind.initializers) - assertEquals(0, kind.initializers?.size) - - // Verify field structure - val fields = kind.fields!! - // Extract id from JsonElement by parsing as JsonObject - val field1Id = (fields[0] as? JsonObject)?.get("id")?.jsonPrimitive?.content - val field2Id = (fields[1] as? JsonObject)?.get("id")?.jsonPrimitive?.content - assertEquals("uuid", field1Id) - assertEquals("balance", field2Id) - - println("✅ ResourceValue type parsing test passed successfully") - println(" - Kind: ${kind.kind}") - println(" - TypeID: ${kind.typeID}") - println(" - Fields: ${kind.fields?.size}") - println(" - Initializers: ${kind.initializers?.size}") - - } catch (e: Exception) { - println("❌ ResourceValue type parsing test failed: ${e.message}") - e.printStackTrace() - throw e - } - } - - /** - * Test Cadence Value parsing with complex ResourceValue payload - * This tests the actual Cadence value parsing that was failing in transaction results - */ - @Test - fun testCadenceResourceValueParsing() { - // Base64 decoded JSON that represents a ResourceValue with complex type structure - val cadenceResourceJson = """ - { - "type": "Resource", - "value": { - "id": "A.231cc0dbbcffc4b7.ceMATIC.Vault", - "fields": [ - { - "name": "uuid", - "value": { - "type": "UInt64", - "value": "123456789" - } - }, - { - "name": "balance", - "value": { - "type": "UFix64", - "value": "0.00000000" - } - } - ] - } - } - """.trimIndent() - - try { - // Test that we can parse complex Cadence ResourceValue structures - val cadenceValue = Cadence.Value.decodeFromJson(cadenceResourceJson) - - // Verify it's a ResourceValue - assertTrue(cadenceValue is Cadence.Value.ResourceValue, "Should be a ResourceValue") - - val resourceValue = cadenceValue as Cadence.Value.ResourceValue - assertEquals("A.231cc0dbbcffc4b7.ceMATIC.Vault", resourceValue.value.id) - assertEquals(2, resourceValue.value.fields.size) - - // Verify fields - val uuidField = resourceValue.value.fields.find { it.name == "uuid" } - assertNotNull(uuidField) - assertTrue(uuidField!!.value is Cadence.Value.UInt64Value) - - val balanceField = resourceValue.value.fields.find { it.name == "balance" } - assertNotNull(balanceField) - assertTrue(balanceField!!.value is Cadence.Value.UFix64Value) - - println("✅ Cadence ResourceValue parsing test passed successfully") - println(" - ResourceValue parsed correctly") - println(" - Composite fields parsed: ${resourceValue.value.fields.size}") - println(" - UUID field type: ${uuidField.value.javaClass.simpleName}") - println(" - Balance field type: ${balanceField.value.javaClass.simpleName}") - - } catch (e: Exception) { - println("❌ Cadence ResourceValue parsing test failed: ${e.message}") - e.printStackTrace() - throw e - } - } - - /** - * Test parsing of extremely complex real-world transaction JSON structures - * This simulates the kind of complex nested structures found in actual Flow transactions - * including multiple resource types, arrays, dictionaries, and deeply nested type definitions - */ - @Test - fun testComplexRealWorldTransactionParsing() { - // This represents the kind of complex JSON structure that might appear in real Flow transactions - val complexRealWorldJson = """ - { - "block_id": "9326a6ae294eeb58f0f984b512b89f48579fea7c84d48d07bd2f316856f4ab91", - "status": "Sealed", - "status_code": 0, - "error_message": "", - "computation_used": "456", - "events": [ - { - "type": "A.1654653399040a61.FlowToken.TokensWithdrawn", - "transaction_id": "36e7f5155d40799b8ac41e75ea7998e589ee4287fcc274ae3d5f2883b37f7380", - "transaction_index": "0", - "event_index": "0", - "payload": "eyJ0eXBlIjoiRXZlbnQiLCJ2YWx1ZSI6eyJpZCI6IkEuMTY1NDY1MzM5OTA0MGE2MS5GbG93VG9rZW4uVG9rZW5zV2l0aGRyYXduIiwiZmllbGRzIjpbeyJuYW1lIjoiYW1vdW50IiwidmFsdWUiOnsidHlwZSI6IlVGaXg2NCIsInZhbHVlIjoiMC4wMDEwMDAwMCJ9fSx7Im5hbWUiOiJmcm9tIiwidmFsdWUiOnsidHlwZSI6Ik9wdGlvbmFsIiwidmFsdWUiOnsidHlwZSI6IkFkZHJlc3MiLCJ2YWx1ZSI6IjB4ZTdlNGZkZjBhNGE1NDI0YyJ9fX1dfX0=" - }, - { - "type": "A.231cc0dbbcffc4b7.ceMATIC.TokensDeposited", - "transaction_id": "36e7f5155d40799b8ac41e75ea7998e589ee4287fcc274ae3d5f2883b37f7380", - "transaction_index": "0", - "event_index": "1", - "payload": "eyJ0eXBlIjoiRXZlbnQiLCJ2YWx1ZSI6eyJpZCI6IkEuMjMxY2MwZGJiY2ZmYzRiNy5jZU1BVElDLlRva2Vuc0RlcG9zaXRlZCIsImZpZWxkcyI6W3sibmFtZSI6ImFtb3VudCIsInZhbHVlIjp7InR5cGUiOiJVRml4NjQiLCJ2YWx1ZSI6IjEwMC4wMDAwMDAwMCJ9fSx7Im5hbWUiOiJ0byIsInZhbHVlIjp7InR5cGUiOiJPcHRpb25hbCIsInZhbHVlIjp7InR5cGUiOiJBZGRyZXNzIiwidmFsdWUiOiIweGFiYzEyMzQ1NmRlZjc4OTAifX19XX19" - } - ], - "execution": "Success" - } - """.trimIndent() - - try { - val json = Json { - ignoreUnknownKeys = true - isLenient = true - } - - val transactionResult = json.decodeFromString(complexRealWorldJson) - - // Verify parsing succeeded - assertEquals("9326a6ae294eeb58f0f984b512b89f48579fea7c84d48d07bd2f316856f4ab91", transactionResult.blockId) - assertEquals(TransactionStatus.SEALED, transactionResult.status) - assertEquals(0, transactionResult.statusCode) - assertEquals("", transactionResult.errorMessage) - assertEquals("456", transactionResult.computationUsed) - - // Verify complex events parsed - assertEquals(2, transactionResult.events.size) - - val flowTokenEvent = transactionResult.events[0] - assertEquals("A.1654653399040a61.FlowToken.TokensWithdrawn", flowTokenEvent.type) - assertEquals("36e7f5155d40799b8ac41e75ea7998e589ee4287fcc274ae3d5f2883b37f7380", flowTokenEvent.transactionId) - - val cematicEvent = transactionResult.events[1] - assertEquals("A.231cc0dbbcffc4b7.ceMATIC.TokensDeposited", cematicEvent.type) - assertEquals("36e7f5155d40799b8ac41e75ea7998e589ee4287fcc274ae3d5f2883b37f7380", cematicEvent.transactionId) - - // Verify complex execution structure - assertNotNull(transactionResult.execution) - - println("✅ Complex real-world transaction parsing test passed successfully") - println(" - Multiple complex events parsed correctly") - println(" - Deeply nested type structures handled") - println(" - Real transaction ID: 36e7f5155d40799b8ac41e75ea7998e589ee4287fcc274ae3d5f2883b37f7380") - - } catch (e: Exception) { - println("❌ Complex real-world transaction parsing test failed: ${e.message}") - e.printStackTrace() - throw e - } - } } \ No newline at end of file