From 910633d305ae7f7567011badc3b448fe794e6701 Mon Sep 17 00:00:00 2001 From: Simon McLoughlin Date: Tue, 26 Mar 2024 12:43:17 +0000 Subject: [PATCH] - update extractors to handle v3 and v4 of 3route - rename stub files to reflect which version it is - add new stubs - update tests --- .../Factories/OperationFactory.swift | 28 +- .../Factories/OperationFactoryTests.swift | 33 ++- .../Stubs/3route_v3_fa1-for-xtz.json | 121 +++++++++ .../Stubs/3route_v3_fa2-for-xtz.json | 250 ++++++++++++++++++ ...or-fa2.json => 3route_v4_fa1-for-fa2.json} | 0 ...es.json => 3route_v4_multiple-routes.json} | 0 ...route.json => 3route_v4_single-route.json} | 0 7 files changed, 422 insertions(+), 10 deletions(-) create mode 100644 Tests/KukaiCoreSwiftTests/Stubs/3route_v3_fa1-for-xtz.json create mode 100644 Tests/KukaiCoreSwiftTests/Stubs/3route_v3_fa2-for-xtz.json rename Tests/KukaiCoreSwiftTests/Stubs/{3route-fa1-for-fa2.json => 3route_v4_fa1-for-fa2.json} (100%) rename Tests/KukaiCoreSwiftTests/Stubs/{3route-multiple-routes.json => 3route_v4_multiple-routes.json} (100%) rename Tests/KukaiCoreSwiftTests/Stubs/{3route-single-route.json => 3route_v4_single-route.json} (100%) diff --git a/Sources/KukaiCoreSwift/Factories/OperationFactory.swift b/Sources/KukaiCoreSwift/Factories/OperationFactory.swift index d59d8605..6ebe574a 100644 --- a/Sources/KukaiCoreSwift/Factories/OperationFactory.swift +++ b/Sources/KukaiCoreSwift/Factories/OperationFactory.swift @@ -490,8 +490,24 @@ public class OperationFactory { /** Extract rpc amount (without decimal info) michelson `execute` value for a 3route call */ - public static func tokenAmountFromExecuteMichelson(michelson: Any) -> Decimal? { - if let michelsonDict = michelson as? [String: Any] { + public static func tokenAmountFromExecuteMichelson(michelson: Any, contract: String) -> Decimal? { + + if contract == "KT1R7WEtNNim3YgkxPt8wPMczjH3eyhbJMtz", let michelsonDict = michelson as? [String: Any] { + // v3 + + let routeArray = (michelsonDict.michelsonArgsArray()?.michelsonPair(atIndex: 1)?.michelsonArgsArray()?.michelsonPair(atIndex: 1)?.michelsonArgsArray()?.michelsonPair(atIndex: 1)?.michelsonArgsArray()?.michelsonPair(atIndex: 1)?.michelsonArgsUnknownArray()?.first as? [[String: Any]]) + + var total: Decimal = 0 + for michelsonDictRoute in routeArray ?? [] { + let value = michelsonDictRoute.michelsonArgsArray()?.michelsonPair(atIndex: 1)?.michelsonArgsArray()?.michelsonPair(atIndex: 0)?.michelsonArgsArray()?.michelsonInt(atIndex: 0) + total += Decimal(string: value ?? "0") ?? 0 + } + + return total + + } else if contract == "KT1V5XKmeypanMS9pR65REpqmVejWBZURuuT", let michelsonDict = michelson as? [String: Any] { + // v4 + let routeArray = (michelsonDict.michelsonArgsArray()?.michelsonPair(atIndex: 1)?.michelsonArgsArray()?.michelsonPair(atIndex: 1)?.michelsonArgsArray()?.michelsonPair(atIndex: 1)?.michelsonArgsArray()?.michelsonPair(atIndex: 1)?.michelsonArgsUnknownArray()?.first as? [[String: Any]]) var total: Decimal = 0 @@ -594,7 +610,7 @@ public class OperationFactory { - Approve operation - update_operator operation */ - public static func tokenIdAndAmountFromMichelson(michelson: Any) -> (rpcAmount: String, tokenId: Decimal?, destination: String?)? { + public static func tokenIdAndAmountFromMichelson(michelson: Any, contract: String) -> (rpcAmount: String, tokenId: Decimal?, destination: String?)? { if let michelsonDict = michelson as? [String: Any], let entrypoint = michelsonDict["entrypoint"] as? String { switch entrypoint { case OperationTransaction.StandardEntrypoint.approve.rawValue: @@ -615,7 +631,7 @@ public class OperationFactory { return tokenIdAndAmountFromTransferMichelson(michelson: michelsonDict["value"] ?? [:]) case OperationTransaction.StandardEntrypoint.execute.rawValue: // 3route - if let response = tokenAmountFromExecuteMichelson(michelson: michelsonDict["value"] ?? [:]) { + if let response = tokenAmountFromExecuteMichelson(michelson: michelsonDict["value"] ?? [:], contract: contract) { return (rpcAmount: response.description, tokenId: nil, destination: nil) // Can extract amount, but nothing else } else { @@ -665,7 +681,7 @@ public class OperationFactory { var lastTokenAddress: String? = nil for op in operations { - if let opTrans = op as? OperationTransaction, let details = tokenIdAndAmountFromMichelson(michelson: opTrans.parameters ?? [:]), let entrypoint = (opTrans.parameters?["entrypoint"] as? String) { + if let opTrans = op as? OperationTransaction, let details = tokenIdAndAmountFromMichelson(michelson: opTrans.parameters ?? [:], contract: opTrans.destination), let entrypoint = (opTrans.parameters?["entrypoint"] as? String) { if entrypoint == OperationTransaction.StandardEntrypoint.approve.rawValue || entrypoint == OperationTransaction.StandardEntrypoint.updateOperators.rawValue { @@ -676,7 +692,7 @@ public class OperationFactory { } else if let lastDetails = lastTokenIdAndAmountResults, let lastTokenAddress = lastTokenAddress, (entrypoint != OperationTransaction.StandardEntrypoint.approve.rawValue && entrypoint != OperationTransaction.StandardEntrypoint.updateOperators.rawValue), - let knownOpDetails = tokenIdAndAmountFromMichelson(michelson: opTrans.parameters ?? [:]) { + let knownOpDetails = tokenIdAndAmountFromMichelson(michelson: opTrans.parameters ?? [:], contract: opTrans.destination) { // If we have a previous set of details from an approve or update, check if we can extract something useful from this one to complete the info return (tokenContract: lastTokenAddress, rpcAmount: knownOpDetails.rpcAmount, tokenId: lastDetails.tokenId, destination: lastDetails.destination ?? "") diff --git a/Tests/KukaiCoreSwiftTests/Factories/OperationFactoryTests.swift b/Tests/KukaiCoreSwiftTests/Factories/OperationFactoryTests.swift index 915c4bd6..0789b694 100644 --- a/Tests/KukaiCoreSwiftTests/Factories/OperationFactoryTests.swift +++ b/Tests/KukaiCoreSwiftTests/Factories/OperationFactoryTests.swift @@ -627,10 +627,35 @@ class OperationFactoryTests: XCTestCase { XCTAssert(contractDetails3?.entrypoint == "tezToTokenPayment", contractDetails3?.entrypoint ?? "-") } - func testExtractors3Route() { + func testExtractors3RouteV3() { let decoder = JSONDecoder() - let singleRouteJsonData = MockConstants.jsonStub(fromFilename: "3route-single-route") + let fa1ForXTZData = MockConstants.jsonStub(fromFilename: "3route_v3_fa1-for-xtz") + let fa1ForXTZJson = (try? decoder.decode([OperationTransaction].self, from: fa1ForXTZData)) ?? [] + XCTAssert(fa1ForXTZJson.count > 0) + + let details1 = OperationFactory.Extractor.firstNonZeroTokenTransferAmount(operations: fa1ForXTZJson) + XCTAssert(details1?.tokenContract == "KT1JVjgXPMMSaa6FkzeJcgb8q9cUaLmwaJUX", details1?.tokenContract ?? "-") + XCTAssert(details1?.rpcAmount == "6605336839045864425", details1?.rpcAmount ?? "-") + XCTAssert(details1?.tokenId == nil, details1?.tokenId?.description ?? "-") + XCTAssert(details1?.destination == "KT1R7WEtNNim3YgkxPt8wPMczjH3eyhbJMtz", details1?.destination ?? "-") + + + let fa2ForXTZData = MockConstants.jsonStub(fromFilename: "3route_v3_fa2-for-xtz") + let fa2ForXTZJson = (try? decoder.decode([OperationTransaction].self, from: fa2ForXTZData)) ?? [] + XCTAssert(fa2ForXTZJson.count > 0) + + let details2 = OperationFactory.Extractor.firstNonZeroTokenTransferAmount(operations: fa2ForXTZJson) + XCTAssert(details2?.tokenContract == "KT1914CUZ7EegAFPbfgQMRkw8Uz5mYkEz2ui", details2?.tokenContract ?? "-") + XCTAssert(details2?.rpcAmount == "47557742041756", details2?.rpcAmount ?? "-") + XCTAssert(details2?.tokenId == 0, details2?.tokenId?.description ?? "-") + XCTAssert(details2?.destination == "KT1R7WEtNNim3YgkxPt8wPMczjH3eyhbJMtz", details2?.destination ?? "-") + } + + func testExtractors3RouteV4() { + let decoder = JSONDecoder() + + let singleRouteJsonData = MockConstants.jsonStub(fromFilename: "3route_v4_single-route") let singleRouteJson = (try? decoder.decode([OperationTransaction].self, from: singleRouteJsonData)) ?? [] XCTAssert(singleRouteJson.count > 0) @@ -641,7 +666,7 @@ class OperationFactoryTests: XCTestCase { XCTAssert(details1?.destination == "KT1V5XKmeypanMS9pR65REpqmVejWBZURuuT", details1?.destination ?? "-") - let multipleRouteJsonData = MockConstants.jsonStub(fromFilename: "3route-multiple-routes") + let multipleRouteJsonData = MockConstants.jsonStub(fromFilename: "3route_v4_multiple-routes") let multipleRouteJson = (try? decoder.decode([OperationTransaction].self, from: multipleRouteJsonData)) ?? [] XCTAssert(multipleRouteJson.count > 0) @@ -652,7 +677,7 @@ class OperationFactoryTests: XCTestCase { XCTAssert(details2?.destination == "KT1V5XKmeypanMS9pR65REpqmVejWBZURuuT", details2?.destination ?? "-") - let fa1ForFa2Data = MockConstants.jsonStub(fromFilename: "3route-fa1-for-fa2") + let fa1ForFa2Data = MockConstants.jsonStub(fromFilename: "3route_v4_fa1-for-fa2") let fa1ForFa2Json = (try? decoder.decode([OperationTransaction].self, from: fa1ForFa2Data)) ?? [] XCTAssert(fa1ForFa2Json.count > 0) diff --git a/Tests/KukaiCoreSwiftTests/Stubs/3route_v3_fa1-for-xtz.json b/Tests/KukaiCoreSwiftTests/Stubs/3route_v3_fa1-for-xtz.json new file mode 100644 index 00000000..95cea3c5 --- /dev/null +++ b/Tests/KukaiCoreSwiftTests/Stubs/3route_v3_fa1-for-xtz.json @@ -0,0 +1,121 @@ +[ + { + "amount": "0", + "counter": "24122766", + "destination": "KT1JVjgXPMMSaa6FkzeJcgb8q9cUaLmwaJUX", + "fee": "0", + "gas_limit": "1859", + "kind": "transaction", + "parameters": { + "entrypoint": "approve", + "value": { + "args": [ + { + "string": "KT1R7WEtNNim3YgkxPt8wPMczjH3eyhbJMtz" + }, + { + "int": "6605336839045864425" + } + ], + "prim": "Pair" + } + }, + "source": "tz1Nzj8BKu3F6KhyaJYZXEfXkyN51HBNJixi", + "storage_limit": "18" + }, + { + "amount": "0", + "counter": "24122767", + "destination": "KT1R7WEtNNim3YgkxPt8wPMczjH3eyhbJMtz", + "fee": "3585", + "gas_limit": "28887", + "kind": "transaction", + "parameters": { + "entrypoint": "execute", + "value": { + "args": [ + { + "int": "77" + }, + { + "args": [ + { + "int": "0" + }, + { + "args": [ + { + "int": "9299" + }, + { + "args": [ + { + "string": "tz1Nzj8BKu3F6KhyaJYZXEfXkyN51HBNJixi" + }, + { + "args": [ + [ + { + "args": [ + { + "int": "0" + }, + { + "args": [ + { + "args": [ + { + "int": "6605336839045864425" + } + ], + "prim": "Some" + }, + { + "args": [ + { + "int": "377" + }, + { + "args": [ + { + "int": "3" + }, + { + "bytes": "" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + ], + "prim": "Elt" + } + ], + { + "int": "3" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + }, + "source": "tz1Nzj8BKu3F6KhyaJYZXEfXkyN51HBNJixi", + "storage_limit": "0" + } +] \ No newline at end of file diff --git a/Tests/KukaiCoreSwiftTests/Stubs/3route_v3_fa2-for-xtz.json b/Tests/KukaiCoreSwiftTests/Stubs/3route_v3_fa2-for-xtz.json new file mode 100644 index 00000000..61b7fdce --- /dev/null +++ b/Tests/KukaiCoreSwiftTests/Stubs/3route_v3_fa2-for-xtz.json @@ -0,0 +1,250 @@ +[ + { + "amount": "0", + "counter": "24122766", + "destination": "KT1914CUZ7EegAFPbfgQMRkw8Uz5mYkEz2ui", + "fee": "0", + "gas_limit": "2742", + "kind": "transaction", + "parameters": { + "entrypoint": "update_operators", + "value": [ + { + "args": [ + { + "args": [ + { + "string": "tz1Nzj8BKu3F6KhyaJYZXEfXkyN51HBNJixi" + }, + { + "args": [ + { + "string": "KT1R7WEtNNim3YgkxPt8wPMczjH3eyhbJMtz" + }, + { + "int": "0" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + ], + "prim": "Left" + } + ] + }, + "source": "tz1Nzj8BKu3F6KhyaJYZXEfXkyN51HBNJixi", + "storage_limit": "0" + }, + { + "amount": "0", + "counter": "24122767", + "destination": "KT1R7WEtNNim3YgkxPt8wPMczjH3eyhbJMtz", + "fee": "0", + "gas_limit": "47925", + "kind": "transaction", + "parameters": { + "entrypoint": "execute", + "value": { + "args": [ + { + "int": "76" + }, + { + "args": [ + { + "int": "0" + }, + { + "args": [ + { + "int": "319989425" + }, + { + "args": [ + { + "string": "tz1Nzj8BKu3F6KhyaJYZXEfXkyN51HBNJixi" + }, + { + "args": [ + [ + { + "args": [ + { + "int": "0" + }, + { + "args": [ + { + "args": [ + { + "int": "46273683006631" + } + ], + "prim": "Some" + }, + { + "args": [ + { + "int": "370" + }, + { + "args": [ + { + "int": "1" + }, + { + "bytes": "" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + ], + "prim": "Elt" + }, + { + "args": [ + { + "int": "1" + }, + { + "args": [ + { + "args": [ + { + "int": "1284059035125" + } + ], + "prim": "Some" + }, + { + "args": [ + { + "int": "912" + }, + { + "args": [ + { + "int": "3" + }, + { + "bytes": "" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + ], + "prim": "Elt" + }, + { + "args": [ + { + "int": "2" + }, + { + "args": [ + { + "prim": "None" + }, + { + "args": [ + { + "int": "889" + }, + { + "args": [ + { + "int": "0" + }, + { + "bytes": "" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + ], + "prim": "Elt" + } + ], + { + "int": "3" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + }, + "source": "tz1Nzj8BKu3F6KhyaJYZXEfXkyN51HBNJixi", + "storage_limit": "0" + }, + { + "amount": "0", + "counter": "24122768", + "destination": "KT1914CUZ7EegAFPbfgQMRkw8Uz5mYkEz2ui", + "fee": "5916", + "gas_limit": "553", + "kind": "transaction", + "parameters": { + "entrypoint": "update_operators", + "value": [ + { + "args": [ + { + "args": [ + { + "string": "tz1Nzj8BKu3F6KhyaJYZXEfXkyN51HBNJixi" + }, + { + "args": [ + { + "string": "KT1R7WEtNNim3YgkxPt8wPMczjH3eyhbJMtz" + }, + { + "int": "0" + } + ], + "prim": "Pair" + } + ], + "prim": "Pair" + } + ], + "prim": "Right" + } + ] + }, + "source": "tz1Nzj8BKu3F6KhyaJYZXEfXkyN51HBNJixi", + "storage_limit": "0" + } +] \ No newline at end of file diff --git a/Tests/KukaiCoreSwiftTests/Stubs/3route-fa1-for-fa2.json b/Tests/KukaiCoreSwiftTests/Stubs/3route_v4_fa1-for-fa2.json similarity index 100% rename from Tests/KukaiCoreSwiftTests/Stubs/3route-fa1-for-fa2.json rename to Tests/KukaiCoreSwiftTests/Stubs/3route_v4_fa1-for-fa2.json diff --git a/Tests/KukaiCoreSwiftTests/Stubs/3route-multiple-routes.json b/Tests/KukaiCoreSwiftTests/Stubs/3route_v4_multiple-routes.json similarity index 100% rename from Tests/KukaiCoreSwiftTests/Stubs/3route-multiple-routes.json rename to Tests/KukaiCoreSwiftTests/Stubs/3route_v4_multiple-routes.json diff --git a/Tests/KukaiCoreSwiftTests/Stubs/3route-single-route.json b/Tests/KukaiCoreSwiftTests/Stubs/3route_v4_single-route.json similarity index 100% rename from Tests/KukaiCoreSwiftTests/Stubs/3route-single-route.json rename to Tests/KukaiCoreSwiftTests/Stubs/3route_v4_single-route.json