diff --git a/csharp/TrainSolver.slnx b/csharp/TrainSolver.slnx
index 06f65438..2a765544 100644
--- a/csharp/TrainSolver.slnx
+++ b/csharp/TrainSolver.slnx
@@ -25,16 +25,16 @@
-
-
-
-
+
+
+
+
diff --git a/csharp/src/API/Endpoints/SolverV1Endpoints.cs b/csharp/src/API/Endpoints/SolverV1Endpoints.cs
index e3f85cc4..27fc09a2 100644
--- a/csharp/src/API/Endpoints/SolverV1Endpoints.cs
+++ b/csharp/src/API/Endpoints/SolverV1Endpoints.cs
@@ -158,7 +158,7 @@ private static async Task BuildTransactionAsync(
{
var prepareTransactionResponse = await temporalClient
.ExecuteWorkflowAsync(
- "TransactionBuilderWorkflow",
+ "TransactionBuilderWorkflow",
args: [request],
new(id: Guid.CreateVersion7().ToString(), taskQueue: "Core"));
diff --git a/csharp/src/AdminAPI/Endpoints/NetworkTypeEndpoints.cs b/csharp/src/AdminAPI/Endpoints/NetworkTypeEndpoints.cs
new file mode 100644
index 00000000..8ab273b3
--- /dev/null
+++ b/csharp/src/AdminAPI/Endpoints/NetworkTypeEndpoints.cs
@@ -0,0 +1,24 @@
+using Microsoft.AspNetCore.Mvc;
+using Train.Solver.Common.Enums;
+using Train.Solver.Data.Abstractions.Entities;
+using Train.Solver.Data.Abstractions.Repositories;
+using Train.Solver.Infrastructure.Abstractions.Models;
+using Train.Solver.Infrastructure.Extensions;
+
+namespace Train.Solver.AdminAPI.Endpoints;
+
+public static class NetworkTypeEndpoints
+{
+ public static RouteGroupBuilder MapNetworkTypeEndpoints(this RouteGroupBuilder group)
+ {
+ group.MapGet("/network-types", GetAllAsync)
+ .Produces>();
+
+ return group;
+ }
+
+ private static async Task GetAllAsync()
+ {
+ return Results.Ok(typeof(NetworkType).GetEnumNames());
+ }
+}
\ No newline at end of file
diff --git a/csharp/src/AdminAPI/Endpoints/RebalanceEndpoints.cs b/csharp/src/AdminAPI/Endpoints/RebalanceEndpoints.cs
index 3a5cbf39..617ac22a 100644
--- a/csharp/src/AdminAPI/Endpoints/RebalanceEndpoints.cs
+++ b/csharp/src/AdminAPI/Endpoints/RebalanceEndpoints.cs
@@ -1,9 +1,11 @@
using Microsoft.AspNetCore.Mvc;
using Temporalio.Api.Enums.V1;
using Temporalio.Client;
+using Temporalio.Converters;
using Train.Solver.AdminAPI.Models;
using Train.Solver.Common.Enums;
using Train.Solver.Common.Extensions;
+using Train.Solver.Common.Helpers;
using Train.Solver.Data.Abstractions.Repositories;
using Train.Solver.Infrastructure.Abstractions.Models;
using Train.Solver.Infrastructure.Extensions;
@@ -16,12 +18,35 @@ public static class RebalanceEndpoints
{
public static RouteGroupBuilder MapRebalanceEndpoints(this RouteGroupBuilder group)
{
+ group.MapGet("/rebalance", GetAllAsync)
+ .Produces(StatusCodes.Status200OK);
+
group.MapPost("/rebalance", RebalanceAsync)
.Produces(StatusCodes.Status200OK);
return group;
}
+ private static async Task GetAllAsync(
+ ITemporalClient temporalClient)
+ {
+ var query = $"`WorkflowId` STARTS_WITH \"Rebalance\" AND `ExecutionStatus`=\"Running\"";
+ var results = new List();
+
+ await foreach (var wf in temporalClient.ListWorkflowsAsync(query))
+ {
+ var whHandle = temporalClient.GetWorkflowHandle(wf.Id);
+ var describedWorkflow = await whHandle.DescribeAsync();
+
+ if (describedWorkflow.Memo.TryGetValue("Summary", out var summary) && summary != null)
+ {
+ results.Add(summary.ToString()!);
+ }
+ }
+
+ return Results.Ok(results);
+ }
+
private static async Task RebalanceAsync(
INetworkRepository repository,
IWalletRepository walletRepository,
@@ -74,12 +99,16 @@ private static async Task RebalanceAsync(
SignerAgentUrl = wallet.SignerAgent.Url,
},
new TransactionExecutionContext()],
- new(id: TemporalHelper.BuildProcessorId(network.Name, TransactionType.Transfer, Guid.NewGuid()),
+ new(id: TemporalHelper.BuildRebalanceProcessorId(network.Name, Guid.NewGuid()),
taskQueue: network.Type.ToString())
{
IdReusePolicy = WorkflowIdReusePolicy.TerminateIfRunning,
+ Memo = new Dictionary
+ {
+ { "Summary", $"Rebalancing { TokenUnitHelper.FromBaseUnits(request.Amount, token.Decimals) } {token.Asset} in {network.DisplayName} from {wallet.Address} to {trustedWallet.Address}" },
+ }
});
- return Results.Ok();
+ return Results.Ok(workflowId);
}
}
diff --git a/csharp/src/AdminAPI/Models/CreateRouteRequest.cs b/csharp/src/AdminAPI/Models/CreateRouteRequest.cs
index ed029d86..10167817 100644
--- a/csharp/src/AdminAPI/Models/CreateRouteRequest.cs
+++ b/csharp/src/AdminAPI/Models/CreateRouteRequest.cs
@@ -21,5 +21,5 @@ public class CreateRouteRequest
public bool IgnoreExpenseFee { get; set; }
- public string? ServiceFee { get; set; }
+ public string ServiceFee { get; set; } = default!;
}
diff --git a/csharp/src/AdminAPI/Models/UpdateRouteRequest.cs b/csharp/src/AdminAPI/Models/UpdateRouteRequest.cs
index 358a1a16..e939abc8 100644
--- a/csharp/src/AdminAPI/Models/UpdateRouteRequest.cs
+++ b/csharp/src/AdminAPI/Models/UpdateRouteRequest.cs
@@ -8,6 +8,6 @@ public class UpdateRouteRequest
public string RateProvider { get; set; } = default!;
public BigInteger MinAmount { get; set; }
public BigInteger MaxAmount { get; set; }
- public string? ServiceFee { get; set; }
+ public string ServiceFee { get; set; } = null!;
public RouteStatus Status { get; set; }
}
\ No newline at end of file
diff --git a/csharp/src/AdminAPI/Program.cs b/csharp/src/AdminAPI/Program.cs
index 87ba945e..34d88200 100644
--- a/csharp/src/AdminAPI/Program.cs
+++ b/csharp/src/AdminAPI/Program.cs
@@ -125,6 +125,11 @@
.RequireRateLimiting("Fixed")
.WithTags("Rate Provider");
+app.MapGroup("/api")
+ .MapNetworkTypeEndpoints()
+ .RequireRateLimiting("Fixed")
+ .WithTags("Network Type");
+
app.MapGroup("/api")
.MapTokenPriceEndpoints()
.RequireRateLimiting("Fixed")
diff --git a/csharp/src/Client/Client.csproj b/csharp/src/Client/Client.csproj
index 00a25102..ce68a18b 100644
--- a/csharp/src/Client/Client.csproj
+++ b/csharp/src/Client/Client.csproj
@@ -15,7 +15,11 @@
-
+
+
+
+
+
diff --git a/csharp/src/Common/Enums/NetworkType.cs b/csharp/src/Common/Enums/NetworkType.cs
index 96b30280..1906da0a 100644
--- a/csharp/src/Common/Enums/NetworkType.cs
+++ b/csharp/src/Common/Enums/NetworkType.cs
@@ -6,4 +6,5 @@ public enum NetworkType
Solana,
Starknet,
Fuel,
+ Aztec,
}
diff --git a/csharp/src/Data.Abstractions/Entities/Route.cs b/csharp/src/Data.Abstractions/Entities/Route.cs
index e154dee7..54e72c78 100644
--- a/csharp/src/Data.Abstractions/Entities/Route.cs
+++ b/csharp/src/Data.Abstractions/Entities/Route.cs
@@ -19,7 +19,7 @@ public class Route : EntityBase
public int RateProviderId { get; set; }
- public int? ServiceFeeId { get; set; }
+ public int ServiceFeeId { get; set; }
public RouteStatus Status { get; set; }
@@ -35,5 +35,5 @@ public class Route : EntityBase
public Wallet DestinationWallet { get; set; } = null!;
- public ServiceFee? ServiceFee { get; set; }
+ public ServiceFee ServiceFee { get; set; } = null!;
}
\ No newline at end of file
diff --git a/csharp/src/Data.Abstractions/Repositories/IRouteRepository.cs b/csharp/src/Data.Abstractions/Repositories/IRouteRepository.cs
index 5c59e38a..ea75525c 100644
--- a/csharp/src/Data.Abstractions/Repositories/IRouteRepository.cs
+++ b/csharp/src/Data.Abstractions/Repositories/IRouteRepository.cs
@@ -28,7 +28,7 @@ public interface IRouteRepository
BigInteger minAmount,
BigInteger maxAmount,
bool ignoreExpenseFee,
- string? serviceFee);
+ string serviceFee);
Task UpdateRoutesStatusAsync(int[] ids, RouteStatus status);
@@ -43,5 +43,5 @@ public interface IRouteRepository
BigInteger minAmount,
BigInteger maxAmount,
RouteStatus status,
- string? serviceFeeName);
+ string serviceFeeName);
}
diff --git a/csharp/src/Data.Npgsql/EFNetworkRepository.cs b/csharp/src/Data.Npgsql/EFNetworkRepository.cs
index 06cc1273..3d123257 100644
--- a/csharp/src/Data.Npgsql/EFNetworkRepository.cs
+++ b/csharp/src/Data.Npgsql/EFNetworkRepository.cs
@@ -193,6 +193,7 @@ await dbContext.Nodes
NetworkId = network.Id,
};
+ dbContext.Tokens.Add(token);
await dbContext.SaveChangesAsync();
return token;
diff --git a/csharp/src/Data.Npgsql/EFRouteRepository.cs b/csharp/src/Data.Npgsql/EFRouteRepository.cs
index ff0f5402..9624e3b7 100644
--- a/csharp/src/Data.Npgsql/EFRouteRepository.cs
+++ b/csharp/src/Data.Npgsql/EFRouteRepository.cs
@@ -26,7 +26,7 @@ public class EFRouteRepository(
BigInteger minAmount,
BigInteger maxAmount,
bool ignoreExpenseFee,
- string? serviceFeeName)
+ string serviceFeeName)
{
var sourceToken = await networkRepository.GetTokenAsync(sourceNetworkName, sourceTokenSymbol);
@@ -63,6 +63,13 @@ public class EFRouteRepository(
{
throw new ArgumentException($"Rate provider {rateProviderName} not found");
}
+
+ var serviceFee = await feeRepository.GetServiceFeeAsync(serviceFeeName);
+
+ if (serviceFee == null)
+ {
+ throw new ArgumentException($"Service fee {serviceFeeName} not found");
+ }
var route = new Route
{
@@ -75,16 +82,9 @@ public class EFRouteRepository(
MaxAmountInSource = maxAmount.ToString(),
Status = RouteStatus.Active,
IgnoreExpenseFee = ignoreExpenseFee,
+ ServiceFee = serviceFee,
};
-
- var serviceFee = string.IsNullOrEmpty(serviceFeeName)
- ? null
- : await feeRepository.GetServiceFeeAsync(serviceFeeName);
-
- if (serviceFee != null)
- {
- route.ServiceFeeId = serviceFee.Id;
- }
+
dbContext.Routes.Add(route);
await dbContext.SaveChangesAsync();
@@ -101,7 +101,7 @@ public class EFRouteRepository(
BigInteger minAmount,
BigInteger maxAmount,
RouteStatus status,
- string? serviceFeeName)
+ string serviceFeeName)
{
var route = await GetAsync(
sourceNetworkName,
@@ -123,17 +123,17 @@ public class EFRouteRepository(
route.RateProviderId = rateProvider.Id;
}
- var serviceFee = string.IsNullOrEmpty(serviceFeeName)
- ? null
- : await feeRepository.GetServiceFeeAsync(serviceFeeName);
+ var serviceFee = await feeRepository.GetServiceFeeAsync(serviceFeeName);
- if (serviceFee != null)
+ if (serviceFee == null)
{
- route.ServiceFeeId = serviceFee.Id;
+ throw new ArgumentException($"Service fee {serviceFeeName} not found");
}
+
route.MinAmountInSource = minAmount.ToString();
route.MaxAmountInSource = maxAmount.ToString();
+ route.ServiceFee = serviceFee;
route.Status = status;
await dbContext.SaveChangesAsync();
diff --git a/csharp/src/Data.Npgsql/Migrations/20250812103659_AddSeedData.Designer.cs b/csharp/src/Data.Npgsql/Migrations/20250812103659_AddSeedData.Designer.cs
new file mode 100644
index 00000000..a0a1a807
--- /dev/null
+++ b/csharp/src/Data.Npgsql/Migrations/20250812103659_AddSeedData.Designer.cs
@@ -0,0 +1,1067 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using Train.Solver.Data.Npgsql;
+
+#nullable disable
+
+namespace Train.Solver.Data.Npgsql.Migrations
+{
+ [DbContext(typeof(SolverDbContext))]
+ [Migration("20250812103659_AddSeedData")]
+ partial class AddSeedData
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "9.0.0")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Expense", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("FeeTokenId")
+ .HasColumnType("integer");
+
+ b.PrimitiveCollection("LastFeeValues")
+ .IsRequired()
+ .HasColumnType("text[]");
+
+ b.Property("TokenId")
+ .HasColumnType("integer");
+
+ b.Property("TransactionType")
+ .HasColumnType("integer")
+ .HasComment("Transfer=0,Approve=1,HTLCCommit=2,HTLCLock=3,HTLCRedeem=4,HTLCRefund=5,HTLCAddLockSig=6");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("FeeTokenId");
+
+ b.HasIndex("TokenId", "FeeTokenId", "TransactionType")
+ .IsUnique();
+
+ b.ToTable("Expenses");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Network", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ChainId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("DisplayName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("FeePercentageIncrease")
+ .HasColumnType("integer");
+
+ b.Property("FeeType")
+ .HasColumnType("integer");
+
+ b.Property("HTLCNativeContractAddress")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("HTLCTokenContractAddress")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("NativeTokenId")
+ .HasColumnType("integer");
+
+ b.Property("Type")
+ .HasColumnType("integer")
+ .HasComment("EVM=0,Solana=1,Starknet=2,Fuel=3");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.HasIndex("NativeTokenId");
+
+ b.HasIndex("ChainId", "Type")
+ .IsUnique();
+
+ b.ToTable("Networks");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Node", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("NetworkId")
+ .HasColumnType("integer");
+
+ b.Property("ProviderName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Url")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NetworkId");
+
+ b.HasIndex("ProviderName", "NetworkId")
+ .IsUnique();
+
+ b.ToTable("Nodes");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.RateProvider", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("RateProviders");
+
+ b.HasData(
+ new
+ {
+ Id = 100000,
+ CreatedDate = new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ Name = "SameAsset",
+ Version = 0u
+ },
+ new
+ {
+ Id = 100001,
+ CreatedDate = new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ Name = "Binance",
+ Version = 0u
+ });
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Route", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("DestinationTokenId")
+ .HasColumnType("integer");
+
+ b.Property("DestinationWalletId")
+ .HasColumnType("integer");
+
+ b.Property("IgnoreExpenseFee")
+ .HasColumnType("boolean");
+
+ b.Property("MaxAmountInSource")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("MinAmountInSource")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("RateProviderId")
+ .HasColumnType("integer");
+
+ b.Property("ServiceFeeId")
+ .HasColumnType("integer");
+
+ b.Property("SourceTokenId")
+ .HasColumnType("integer");
+
+ b.Property("SourceWalletId")
+ .HasColumnType("integer");
+
+ b.Property("Status")
+ .HasColumnType("integer")
+ .HasComment("Active=0,Inactive=1,Archived=2");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DestinationTokenId");
+
+ b.HasIndex("DestinationWalletId");
+
+ b.HasIndex("RateProviderId");
+
+ b.HasIndex("ServiceFeeId");
+
+ b.HasIndex("SourceWalletId");
+
+ b.HasIndex("SourceTokenId", "DestinationTokenId")
+ .IsUnique();
+
+ b.ToTable("Routes");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.ServiceFee", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("FeeInUsd")
+ .HasColumnType("numeric");
+
+ b.Property("FeePercentage")
+ .HasColumnType("numeric");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("ServiceFees");
+
+ b.HasData(
+ new
+ {
+ Id = 100000,
+ CreatedDate = new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ FeeInUsd = 0m,
+ FeePercentage = 0m,
+ Name = "Free",
+ Version = 0u
+ },
+ new
+ {
+ Id = 100001,
+ CreatedDate = new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ FeeInUsd = 0m,
+ FeePercentage = 0m,
+ Name = "Default",
+ Version = 0u
+ });
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.SignerAgent", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.PrimitiveCollection("SupportedTypes")
+ .IsRequired()
+ .HasColumnType("integer[]");
+
+ b.Property("Url")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("SignerAgents");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Swap", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CommitId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("DestinationAddress")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DestinationAmount")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("FeeAmount")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Hashlock")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("MetricId")
+ .HasColumnType("integer");
+
+ b.Property("RouteId")
+ .HasColumnType("integer");
+
+ b.Property("SourceAddress")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("SourceAmount")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CommitId")
+ .IsUnique();
+
+ b.HasIndex("CreatedDate");
+
+ b.HasIndex("DestinationAddress");
+
+ b.HasIndex("RouteId");
+
+ b.HasIndex("SourceAddress");
+
+ b.ToTable("Swaps");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.SwapMetric", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("DestinationNetwork")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DestinationToken")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ProfitInUsd")
+ .HasColumnType("numeric");
+
+ b.Property("SourceNetwork")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("SourceToken")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("SwapId")
+ .HasColumnType("integer");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.Property("VolumeInUsd")
+ .HasColumnType("numeric");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedDate");
+
+ b.HasIndex("DestinationNetwork");
+
+ b.HasIndex("SourceNetwork");
+
+ b.HasIndex("SwapId")
+ .IsUnique();
+
+ b.ToTable("SwapMetrics");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Token", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Asset")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("Decimals")
+ .HasColumnType("integer");
+
+ b.Property("NetworkId")
+ .HasColumnType("integer");
+
+ b.Property("TokenContract")
+ .HasColumnType("text");
+
+ b.Property("TokenPriceId")
+ .HasColumnType("integer");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Asset");
+
+ b.HasIndex("TokenPriceId");
+
+ b.HasIndex("NetworkId", "Asset")
+ .IsUnique();
+
+ b.ToTable("Tokens");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.TokenPrice", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("ExternalId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("LastUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("PriceInUsd")
+ .HasColumnType("numeric");
+
+ b.Property("Symbol")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Symbol")
+ .IsUnique();
+
+ b.ToTable("TokenPrices");
+
+ b.HasData(
+ new
+ {
+ Id = 10002,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "bitcoin",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "BTC",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10008,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "fuel-network",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "FUEL",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10014,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "avalanche-2",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "AVAX",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10018,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "optimism",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "OP",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10026,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "ethereum",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "ETH",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10035,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "solana",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "SOL",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10043,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "dai",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "DAI",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10046,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "usd-coin",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "USDC",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10050,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "immutable-x",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "IMX",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10054,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "binancecoin",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "BNB",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10055,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "tether",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "USDT",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10056,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "matic-network",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "MATIC",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10063,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "polygon-ecosystem-token",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "POL",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10069,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "ronin",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "RON",
+ Version = 0u
+ });
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Transaction", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("FeeAmount")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("NetworkId")
+ .HasColumnType("integer");
+
+ b.Property("Status")
+ .HasColumnType("integer")
+ .HasComment("Completed=0,Initiated=1,Failed=2");
+
+ b.Property("SwapId")
+ .HasColumnType("integer");
+
+ b.Property("Timestamp")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("TransactionHash")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Type")
+ .HasColumnType("integer")
+ .HasComment("Transfer=0,Approve=1,HTLCCommit=2,HTLCLock=3,HTLCRedeem=4,HTLCRefund=5,HTLCAddLockSig=6");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NetworkId");
+
+ b.HasIndex("Status");
+
+ b.HasIndex("SwapId");
+
+ b.HasIndex("TransactionHash");
+
+ b.HasIndex("Type");
+
+ b.HasIndex("TransactionHash", "NetworkId")
+ .IsUnique();
+
+ b.ToTable("Transactions");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.TrustedWallet", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Address")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("NetworkType")
+ .HasColumnType("integer");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Address", "NetworkType");
+
+ b.HasIndex("Name", "NetworkType")
+ .IsUnique();
+
+ b.ToTable("TrustedWallets");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Wallet", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Address")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("NetworkType")
+ .HasColumnType("integer");
+
+ b.Property("SignerAgentId")
+ .HasColumnType("integer");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SignerAgentId");
+
+ b.HasIndex("Address", "NetworkType");
+
+ b.HasIndex("Name", "NetworkType")
+ .IsUnique();
+
+ b.ToTable("Wallets");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Expense", b =>
+ {
+ b.HasOne("Train.Solver.Data.Abstractions.Entities.Token", "FeeToken")
+ .WithMany()
+ .HasForeignKey("FeeTokenId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Train.Solver.Data.Abstractions.Entities.Token", "Token")
+ .WithMany()
+ .HasForeignKey("TokenId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("FeeToken");
+
+ b.Navigation("Token");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Network", b =>
+ {
+ b.HasOne("Train.Solver.Data.Abstractions.Entities.Token", "NativeToken")
+ .WithMany()
+ .HasForeignKey("NativeTokenId");
+
+ b.Navigation("NativeToken");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Node", b =>
+ {
+ b.HasOne("Train.Solver.Data.Abstractions.Entities.Network", "Network")
+ .WithMany("Nodes")
+ .HasForeignKey("NetworkId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Network");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Route", b =>
+ {
+ b.HasOne("Train.Solver.Data.Abstractions.Entities.Token", "DestinationToken")
+ .WithMany()
+ .HasForeignKey("DestinationTokenId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Train.Solver.Data.Abstractions.Entities.Wallet", "DestinationWallet")
+ .WithMany()
+ .HasForeignKey("DestinationWalletId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Train.Solver.Data.Abstractions.Entities.RateProvider", "RateProvider")
+ .WithMany()
+ .HasForeignKey("RateProviderId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Train.Solver.Data.Abstractions.Entities.ServiceFee", "ServiceFee")
+ .WithMany()
+ .HasForeignKey("ServiceFeeId");
+
+ b.HasOne("Train.Solver.Data.Abstractions.Entities.Token", "SourceToken")
+ .WithMany()
+ .HasForeignKey("SourceTokenId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Train.Solver.Data.Abstractions.Entities.Wallet", "SourceWallet")
+ .WithMany()
+ .HasForeignKey("SourceWalletId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("DestinationToken");
+
+ b.Navigation("DestinationWallet");
+
+ b.Navigation("RateProvider");
+
+ b.Navigation("ServiceFee");
+
+ b.Navigation("SourceToken");
+
+ b.Navigation("SourceWallet");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Swap", b =>
+ {
+ b.HasOne("Train.Solver.Data.Abstractions.Entities.Route", "Route")
+ .WithMany()
+ .HasForeignKey("RouteId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Route");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.SwapMetric", b =>
+ {
+ b.HasOne("Train.Solver.Data.Abstractions.Entities.Swap", "Swap")
+ .WithOne("Metric")
+ .HasForeignKey("Train.Solver.Data.Abstractions.Entities.SwapMetric", "SwapId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Swap");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Token", b =>
+ {
+ b.HasOne("Train.Solver.Data.Abstractions.Entities.Network", "Network")
+ .WithMany("Tokens")
+ .HasForeignKey("NetworkId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Train.Solver.Data.Abstractions.Entities.TokenPrice", "TokenPrice")
+ .WithMany()
+ .HasForeignKey("TokenPriceId")
+ .OnDelete(DeleteBehavior.NoAction)
+ .IsRequired();
+
+ b.Navigation("Network");
+
+ b.Navigation("TokenPrice");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Transaction", b =>
+ {
+ b.HasOne("Train.Solver.Data.Abstractions.Entities.Network", "Network")
+ .WithMany()
+ .HasForeignKey("NetworkId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Train.Solver.Data.Abstractions.Entities.Swap", "Swap")
+ .WithMany("Transactions")
+ .HasForeignKey("SwapId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.Navigation("Network");
+
+ b.Navigation("Swap");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Wallet", b =>
+ {
+ b.HasOne("Train.Solver.Data.Abstractions.Entities.SignerAgent", "SignerAgent")
+ .WithMany()
+ .HasForeignKey("SignerAgentId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("SignerAgent");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Network", b =>
+ {
+ b.Navigation("Nodes");
+
+ b.Navigation("Tokens");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Swap", b =>
+ {
+ b.Navigation("Metric");
+
+ b.Navigation("Transactions");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/csharp/src/Data.Npgsql/Migrations/20250812103659_AddSeedData.cs b/csharp/src/Data.Npgsql/Migrations/20250812103659_AddSeedData.cs
new file mode 100644
index 00000000..5e8a02a9
--- /dev/null
+++ b/csharp/src/Data.Npgsql/Migrations/20250812103659_AddSeedData.cs
@@ -0,0 +1,150 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
+
+namespace Train.Solver.Data.Npgsql.Migrations
+{
+ ///
+ public partial class AddSeedData : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.InsertData(
+ table: "RateProviders",
+ columns: new[] { "Id", "Name" },
+ values: new object[,]
+ {
+ { 100000, "SameAsset" },
+ { 100001, "Binance" }
+ });
+
+ migrationBuilder.InsertData(
+ table: "ServiceFees",
+ columns: new[] { "Id", "FeeInUsd", "FeePercentage", "Name" },
+ values: new object[,]
+ {
+ { 100000, 0m, 0m, "Free" },
+ { 100001, 0m, 0m, "Default" }
+ });
+
+ migrationBuilder.InsertData(
+ table: "TokenPrices",
+ columns: new[] { "Id", "CreatedDate", "ExternalId", "LastUpdated", "PriceInUsd", "Symbol" },
+ values: new object[,]
+ {
+ { 10002, new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), "bitcoin", new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), 0m, "BTC" },
+ { 10008, new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), "fuel-network", new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), 0m, "FUEL" },
+ { 10014, new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), "avalanche-2", new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), 0m, "AVAX" },
+ { 10018, new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), "optimism", new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), 0m, "OP" },
+ { 10026, new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), "ethereum", new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), 0m, "ETH" },
+ { 10035, new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), "solana", new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), 0m, "SOL" },
+ { 10043, new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), "dai", new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), 0m, "DAI" },
+ { 10046, new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), "usd-coin", new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), 0m, "USDC" },
+ { 10050, new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), "immutable-x", new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), 0m, "IMX" },
+ { 10054, new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), "binancecoin", new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), 0m, "BNB" },
+ { 10055, new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), "tether", new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), 0m, "USDT" },
+ { 10056, new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), "matic-network", new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), 0m, "MATIC" },
+ { 10063, new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), "polygon-ecosystem-token", new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), 0m, "POL" },
+ { 10069, new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), "ronin", new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), 0m, "RON" }
+ });
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DeleteData(
+ table: "RateProviders",
+ keyColumn: "Id",
+ keyValue: 100000);
+
+ migrationBuilder.DeleteData(
+ table: "RateProviders",
+ keyColumn: "Id",
+ keyValue: 100001);
+
+ migrationBuilder.DeleteData(
+ table: "ServiceFees",
+ keyColumn: "Id",
+ keyValue: 100000);
+
+ migrationBuilder.DeleteData(
+ table: "ServiceFees",
+ keyColumn: "Id",
+ keyValue: 100001);
+
+ migrationBuilder.DeleteData(
+ table: "TokenPrices",
+ keyColumn: "Id",
+ keyValue: 10002);
+
+ migrationBuilder.DeleteData(
+ table: "TokenPrices",
+ keyColumn: "Id",
+ keyValue: 10008);
+
+ migrationBuilder.DeleteData(
+ table: "TokenPrices",
+ keyColumn: "Id",
+ keyValue: 10014);
+
+ migrationBuilder.DeleteData(
+ table: "TokenPrices",
+ keyColumn: "Id",
+ keyValue: 10018);
+
+ migrationBuilder.DeleteData(
+ table: "TokenPrices",
+ keyColumn: "Id",
+ keyValue: 10026);
+
+ migrationBuilder.DeleteData(
+ table: "TokenPrices",
+ keyColumn: "Id",
+ keyValue: 10035);
+
+ migrationBuilder.DeleteData(
+ table: "TokenPrices",
+ keyColumn: "Id",
+ keyValue: 10043);
+
+ migrationBuilder.DeleteData(
+ table: "TokenPrices",
+ keyColumn: "Id",
+ keyValue: 10046);
+
+ migrationBuilder.DeleteData(
+ table: "TokenPrices",
+ keyColumn: "Id",
+ keyValue: 10050);
+
+ migrationBuilder.DeleteData(
+ table: "TokenPrices",
+ keyColumn: "Id",
+ keyValue: 10054);
+
+ migrationBuilder.DeleteData(
+ table: "TokenPrices",
+ keyColumn: "Id",
+ keyValue: 10055);
+
+ migrationBuilder.DeleteData(
+ table: "TokenPrices",
+ keyColumn: "Id",
+ keyValue: 10056);
+
+ migrationBuilder.DeleteData(
+ table: "TokenPrices",
+ keyColumn: "Id",
+ keyValue: 10063);
+
+ migrationBuilder.DeleteData(
+ table: "TokenPrices",
+ keyColumn: "Id",
+ keyValue: 10069);
+ }
+ }
+}
diff --git a/csharp/src/Data.Npgsql/Migrations/20250812105030_MakeServiceFeeRequired.Designer.cs b/csharp/src/Data.Npgsql/Migrations/20250812105030_MakeServiceFeeRequired.Designer.cs
new file mode 100644
index 00000000..1c8bff33
--- /dev/null
+++ b/csharp/src/Data.Npgsql/Migrations/20250812105030_MakeServiceFeeRequired.Designer.cs
@@ -0,0 +1,1069 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using Train.Solver.Data.Npgsql;
+
+#nullable disable
+
+namespace Train.Solver.Data.Npgsql.Migrations
+{
+ [DbContext(typeof(SolverDbContext))]
+ [Migration("20250812105030_MakeServiceFeeRequired")]
+ partial class MakeServiceFeeRequired
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "9.0.0")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Expense", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("FeeTokenId")
+ .HasColumnType("integer");
+
+ b.PrimitiveCollection("LastFeeValues")
+ .IsRequired()
+ .HasColumnType("text[]");
+
+ b.Property("TokenId")
+ .HasColumnType("integer");
+
+ b.Property("TransactionType")
+ .HasColumnType("integer")
+ .HasComment("Transfer=0,Approve=1,HTLCCommit=2,HTLCLock=3,HTLCRedeem=4,HTLCRefund=5,HTLCAddLockSig=6");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("FeeTokenId");
+
+ b.HasIndex("TokenId", "FeeTokenId", "TransactionType")
+ .IsUnique();
+
+ b.ToTable("Expenses");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Network", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ChainId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("DisplayName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("FeePercentageIncrease")
+ .HasColumnType("integer");
+
+ b.Property("FeeType")
+ .HasColumnType("integer");
+
+ b.Property("HTLCNativeContractAddress")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("HTLCTokenContractAddress")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("NativeTokenId")
+ .HasColumnType("integer");
+
+ b.Property("Type")
+ .HasColumnType("integer")
+ .HasComment("EVM=0,Solana=1,Starknet=2,Fuel=3");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.HasIndex("NativeTokenId");
+
+ b.HasIndex("ChainId", "Type")
+ .IsUnique();
+
+ b.ToTable("Networks");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Node", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("NetworkId")
+ .HasColumnType("integer");
+
+ b.Property("ProviderName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Url")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NetworkId");
+
+ b.HasIndex("ProviderName", "NetworkId")
+ .IsUnique();
+
+ b.ToTable("Nodes");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.RateProvider", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("RateProviders");
+
+ b.HasData(
+ new
+ {
+ Id = 100000,
+ CreatedDate = new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ Name = "SameAsset",
+ Version = 0u
+ },
+ new
+ {
+ Id = 100001,
+ CreatedDate = new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ Name = "Binance",
+ Version = 0u
+ });
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Route", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("DestinationTokenId")
+ .HasColumnType("integer");
+
+ b.Property("DestinationWalletId")
+ .HasColumnType("integer");
+
+ b.Property("IgnoreExpenseFee")
+ .HasColumnType("boolean");
+
+ b.Property("MaxAmountInSource")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("MinAmountInSource")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("RateProviderId")
+ .HasColumnType("integer");
+
+ b.Property("ServiceFeeId")
+ .HasColumnType("integer");
+
+ b.Property("SourceTokenId")
+ .HasColumnType("integer");
+
+ b.Property("SourceWalletId")
+ .HasColumnType("integer");
+
+ b.Property("Status")
+ .HasColumnType("integer")
+ .HasComment("Active=0,Inactive=1,Archived=2");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DestinationTokenId");
+
+ b.HasIndex("DestinationWalletId");
+
+ b.HasIndex("RateProviderId");
+
+ b.HasIndex("ServiceFeeId");
+
+ b.HasIndex("SourceWalletId");
+
+ b.HasIndex("SourceTokenId", "DestinationTokenId")
+ .IsUnique();
+
+ b.ToTable("Routes");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.ServiceFee", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("FeeInUsd")
+ .HasColumnType("numeric");
+
+ b.Property("FeePercentage")
+ .HasColumnType("numeric");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("ServiceFees");
+
+ b.HasData(
+ new
+ {
+ Id = 100000,
+ CreatedDate = new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ FeeInUsd = 0m,
+ FeePercentage = 0m,
+ Name = "Free",
+ Version = 0u
+ },
+ new
+ {
+ Id = 100001,
+ CreatedDate = new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ FeeInUsd = 0m,
+ FeePercentage = 0m,
+ Name = "Default",
+ Version = 0u
+ });
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.SignerAgent", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.PrimitiveCollection("SupportedTypes")
+ .IsRequired()
+ .HasColumnType("integer[]");
+
+ b.Property("Url")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("SignerAgents");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Swap", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CommitId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("DestinationAddress")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DestinationAmount")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("FeeAmount")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Hashlock")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("MetricId")
+ .HasColumnType("integer");
+
+ b.Property("RouteId")
+ .HasColumnType("integer");
+
+ b.Property("SourceAddress")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("SourceAmount")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CommitId")
+ .IsUnique();
+
+ b.HasIndex("CreatedDate");
+
+ b.HasIndex("DestinationAddress");
+
+ b.HasIndex("RouteId");
+
+ b.HasIndex("SourceAddress");
+
+ b.ToTable("Swaps");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.SwapMetric", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("DestinationNetwork")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DestinationToken")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ProfitInUsd")
+ .HasColumnType("numeric");
+
+ b.Property("SourceNetwork")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("SourceToken")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("SwapId")
+ .HasColumnType("integer");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.Property("VolumeInUsd")
+ .HasColumnType("numeric");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedDate");
+
+ b.HasIndex("DestinationNetwork");
+
+ b.HasIndex("SourceNetwork");
+
+ b.HasIndex("SwapId")
+ .IsUnique();
+
+ b.ToTable("SwapMetrics");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Token", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Asset")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("Decimals")
+ .HasColumnType("integer");
+
+ b.Property("NetworkId")
+ .HasColumnType("integer");
+
+ b.Property("TokenContract")
+ .HasColumnType("text");
+
+ b.Property("TokenPriceId")
+ .HasColumnType("integer");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Asset");
+
+ b.HasIndex("TokenPriceId");
+
+ b.HasIndex("NetworkId", "Asset")
+ .IsUnique();
+
+ b.ToTable("Tokens");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.TokenPrice", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("ExternalId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("LastUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("PriceInUsd")
+ .HasColumnType("numeric");
+
+ b.Property("Symbol")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Symbol")
+ .IsUnique();
+
+ b.ToTable("TokenPrices");
+
+ b.HasData(
+ new
+ {
+ Id = 10002,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "bitcoin",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "BTC",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10008,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "fuel-network",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "FUEL",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10014,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "avalanche-2",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "AVAX",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10018,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "optimism",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "OP",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10026,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "ethereum",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "ETH",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10035,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "solana",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "SOL",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10043,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "dai",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "DAI",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10046,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "usd-coin",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "USDC",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10050,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "immutable-x",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "IMX",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10054,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "binancecoin",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "BNB",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10055,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "tether",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "USDT",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10056,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "matic-network",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "MATIC",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10063,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "polygon-ecosystem-token",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "POL",
+ Version = 0u
+ },
+ new
+ {
+ Id = 10069,
+ CreatedDate = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ ExternalId = "ronin",
+ LastUpdated = new DateTimeOffset(new DateTime(2025, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
+ PriceInUsd = 0m,
+ Symbol = "RON",
+ Version = 0u
+ });
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.Transaction", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedDate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("now()");
+
+ b.Property("FeeAmount")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("NetworkId")
+ .HasColumnType("integer");
+
+ b.Property("Status")
+ .HasColumnType("integer")
+ .HasComment("Completed=0,Initiated=1,Failed=2");
+
+ b.Property("SwapId")
+ .HasColumnType("integer");
+
+ b.Property("Timestamp")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("TransactionHash")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Type")
+ .HasColumnType("integer")
+ .HasComment("Transfer=0,Approve=1,HTLCCommit=2,HTLCLock=3,HTLCRedeem=4,HTLCRefund=5,HTLCAddLockSig=6");
+
+ b.Property("Version")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NetworkId");
+
+ b.HasIndex("Status");
+
+ b.HasIndex("SwapId");
+
+ b.HasIndex("TransactionHash");
+
+ b.HasIndex("Type");
+
+ b.HasIndex("TransactionHash", "NetworkId")
+ .IsUnique();
+
+ b.ToTable("Transactions");
+ });
+
+ modelBuilder.Entity("Train.Solver.Data.Abstractions.Entities.TrustedWallet", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Address")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property