diff --git a/csharp/src/StationAPI/Endpoints/OrderEndpoints.cs b/csharp/src/StationAPI/Endpoints/OrderEndpoints.cs index d4d73d33..bc5a92ba 100644 --- a/csharp/src/StationAPI/Endpoints/OrderEndpoints.cs +++ b/csharp/src/StationAPI/Endpoints/OrderEndpoints.cs @@ -1,4 +1,5 @@ using System.Text.Json; +using System.Text.Json.Nodes; using Train.Solver.StationAPI.Configuration; using Train.Solver.StationAPI.Helpers; using Train.Solver.StationAPI.Models; @@ -59,6 +60,8 @@ private static async Task GetOrderAsync( }); } + var enrichedOrder = EnrichTransactionNetworks(orderJson.Value); + var response = new OrderStatusResponse { Solver = new SolverProfileResponse @@ -68,7 +71,7 @@ private static async Task GetOrderAsync( Description = solver.Description, LogoUrl = solver.LogoUrl }, - Order = orderJson + Order = enrichedOrder }; return Results.Ok(new ApiResponse { Data = response }); @@ -149,7 +152,8 @@ await SseWriter.WriteEventAsync(context.Response, "error", var orderJson = await solverClient.GetOrderAsync(solverId, hashlock, ct); if (orderJson is not null) { - var orderResponse = new OrderStatusResponse { Solver = profile, Order = orderJson }; + var enrichedOrder = EnrichTransactionNetworks(orderJson.Value); + var orderResponse = new OrderStatusResponse { Solver = profile, Order = enrichedOrder }; var jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; var json = JsonSerializer.Serialize(orderResponse, jsonOptions); await SseWriter.WriteEventRawAsync(context.Response, "order", json, ct); @@ -227,4 +231,48 @@ private static bool IsTerminalEventType(string eventType, JsonElement data) return statusProp.GetString(); return null; } -} + + /// + /// Replaces transaction network slugs with caip2Id values derived from + /// the order's source/destination network fields (which are already caip2Id). + /// + private static JsonElement? EnrichTransactionNetworks(JsonElement orderElement) + { + var node = JsonNode.Parse(orderElement.GetRawText()); + if (node is not JsonObject order) return orderElement; + + var sourceCaip2Id = order["source"]?["network"]?.GetValue(); + var destCaip2Id = order["destination"]?["network"]?.GetValue(); + + if (order["transactions"] is not JsonArray transactions) + return orderElement; + + // Build slug → caip2Id map using unambiguous transaction types + var slugToCaip2Id = new Dictionary(); + foreach (var tx in transactions) + { + if (tx is not JsonObject txObj) continue; + var type = txObj["type"]?.GetValue(); + var slug = txObj["network"]?.GetValue(); + if (slug is null) continue; + + if (type is "UserHTLCLock" or "UserHTLCRefund" && sourceCaip2Id is not null) + slugToCaip2Id.TryAdd(slug, sourceCaip2Id); + else if (type is "HTLCLock" or "HTLCRefund" or "CloseSolverLock" && destCaip2Id is not null) + slugToCaip2Id.TryAdd(slug, destCaip2Id); + } + + // Replace network slugs with caip2Id + foreach (var tx in transactions) + { + if (tx is not JsonObject txObj) continue; + var slug = txObj["network"]?.GetValue(); + if (slug is not null && slugToCaip2Id.TryGetValue(slug, out var caip2Id)) + { + txObj["network"] = caip2Id; + } + } + + return JsonSerializer.Deserialize(node.ToJsonString()); + } +} \ No newline at end of file diff --git a/csharp/src/StationAPI/station-config.json b/csharp/src/StationAPI/station-config.json index 441a38c8..2653c451 100644 --- a/csharp/src/StationAPI/station-config.json +++ b/csharp/src/StationAPI/station-config.json @@ -62,6 +62,57 @@ "logoUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/info/logo.png" } ] + }, + { + "caip2Id": "starknet:SN_SEPOLIA", + "displayName": "Starknet Sepolia", + "chainId": "SN_SEPOLIA", + "networkType": "starknet", + "logoUrl": "https://raw.githubusercontent.com/TrainProtocol/icons/main/networks/starknet.png", + "nativeTokenAddress": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", + "tokens": [ + { + "symbol": "ETH", + "contract": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", + "decimals": 18, + "priceSymbol": "ETHUSDT", + "logoUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/info/logo.png" + } + ] + }, + { + "caip2Id": "aztec:devnet", + "displayName": "Aztec Devnet", + "chainId": "devnet", + "networkType": "aztec", + "logoUrl": "https://raw.githubusercontent.com/TrainProtocol/icons/main/networks/aztec.png", + "nativeTokenAddress": "0x02c31306cad429e0a00d3a4ee8ba251853099f835101ee2c637e9b3b9351a056", + "tokens": [ + { + "symbol": "ETH", + "contract": "0x02c31306cad429e0a00d3a4ee8ba251853099f835101ee2c637e9b3b9351a056", + "decimals": 18, + "priceSymbol": "ETHUSDT", + "logoUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/info/logo.png" + } + ] + }, + { + "caip2Id": "solana:devnet", + "displayName": "Solana Devnet", + "chainId": "devnet", + "networkType": "solana", + "logoUrl": "https://raw.githubusercontent.com/TrainProtocol/icons/main/networks/solana.png", + "nativeTokenAddress": "11111111111111111111111111111111", + "tokens": [ + { + "symbol": "SOL", + "contract": "11111111111111111111111111111111", + "decimals": 9, + "priceSymbol": "SOLUSDT", + "logoUrl": "https://raw.githubusercontent.com/TrainProtocol/icons/main/networks/solana.png" + } + ] } ], "solvers": [