From 49e29b6e009467864c09b2a980c4678fd78a879e Mon Sep 17 00:00:00 2001 From: Davit Mandzikyan Date: Tue, 18 Nov 2025 20:19:44 +0400 Subject: [PATCH 1/4] Create htlc lock for solana native contract. --- .../Workflow.Solana/Helpers/EventDecoder.cs | 2 +- .../Helpers/SolanaTransactionBuilder.cs | 83 ++++++++++++------- .../Programs/HTLCProgram/HTLCProgram.cs | 68 ++++++++++++--- .../HTLCProgram/HtlcInstructionDataBuilder.cs | 22 ++--- .../HTLCProgram/HtlcInstructionKeyProvider.cs | 43 ++++++++-- .../HTLCProgram/Models/HTLCLockRequest.cs | 33 -------- .../HTLCProgram/Models/HTLCPdaResponse.cs | 12 --- .../HTLCProgram/Models/HTLCSolLockRequest.cs | 31 +++++++ .../HTLCProgram/Models/HTLCSolPdaResponse.cs | 8 ++ .../HTLCProgram/Models/HTLCSplLockRequest.cs | 8 ++ .../HTLCProgram/Models/HTLCSplPdaResponse.cs | 10 +++ 11 files changed, 216 insertions(+), 104 deletions(-) delete mode 100644 csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCLockRequest.cs delete mode 100644 csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCPdaResponse.cs create mode 100644 csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSolLockRequest.cs create mode 100644 csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSolPdaResponse.cs create mode 100644 csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSplLockRequest.cs create mode 100644 csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSplPdaResponse.cs diff --git a/csharp/src/Workflow.Solana/Helpers/EventDecoder.cs b/csharp/src/Workflow.Solana/Helpers/EventDecoder.cs index a6fdf5b5..c6f5fbdc 100644 --- a/csharp/src/Workflow.Solana/Helpers/EventDecoder.cs +++ b/csharp/src/Workflow.Solana/Helpers/EventDecoder.cs @@ -5,9 +5,9 @@ using System.Numerics; using Train.Solver.Blockchain.Solana.Extensions; using Train.Solver.Blockchain.Solana.Models; -using Train.Solver.Blockchain.Solana.Programs.HTLCProgram; using Train.Solver.Infrastructure.Abstractions.Models; using Train.Solver.Workflow.Abstractions.Models; +using Train.Solver.Workflow.Solana.Programs.HTLCProgram; namespace Train.Solver.Blockchain.Solana.Helpers; diff --git a/csharp/src/Workflow.Solana/Helpers/SolanaTransactionBuilder.cs b/csharp/src/Workflow.Solana/Helpers/SolanaTransactionBuilder.cs index 050fee74..2a639d4d 100644 --- a/csharp/src/Workflow.Solana/Helpers/SolanaTransactionBuilder.cs +++ b/csharp/src/Workflow.Solana/Helpers/SolanaTransactionBuilder.cs @@ -8,12 +8,12 @@ using Solnet.Rpc.Builders; using Solnet.Wallet; using System.Numerics; -using Train.Solver.Blockchain.Solana.Programs.HTLCProgram; using Train.Solver.Blockchain.Solana.Programs.HTLCProgram.Models; using Train.Solver.Common.Extensions; -using Train.Solver.Data.Abstractions.Entities; using Train.Solver.Infrastructure.Abstractions.Models; using Train.Solver.Workflow.Abstractions.Models; +using Train.Solver.Workflow.Solana.Programs.HTLCProgram; +using Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; namespace Train.Solver.Workflow.Solana.Helpers; @@ -66,30 +66,53 @@ await GetOrCreateAssociatedTokenAccount( new PublicKey(solverAccount), new PublicKey(solverAccount)); - builder.SetLockTransactionInstruction( - new PublicKey(htlcContractAddress), - new HTLCLockRequest - { - Hashlock = request.Hashlock.HexToByteArray(), - Id = request.CommitId.HexToByteArray(), - SignerPublicKey = new PublicKey(solverAccount), - ReceiverPublicKey = new PublicKey(request.Receiver), - Amount = request.Amount, - Timelock = new BigInteger(request.Timelock), - SourceAsset = currency.Symbol, - DestinationNetwork = request.DestinationNetwork, - SourceAddress = request.DestinationAddress, - DestinationAsset = request.DestinationAsset, - SourceTokenPublicKey = new PublicKey(currency.Contract), - Reward = request.Reward, - RewardTimelock = new BigInteger(request.RewardTimelock), - }); + if (isNative) + { + builder.SetSolLockTransactionInstruction( + new PublicKey(htlcContractAddress), + new HTLCSplLockRequest + { + Id = request.CommitId.HexToByteArray(), + Hashlock = request.Hashlock.HexToByteArray(), + Timelock = new BigInteger(request.Timelock), + Amount = request.Amount, + DestinationNetwork = request.DestinationNetwork, + DestinationAsset = request.DestinationAsset, + SourceAsset = currency.Symbol, + SignerPublicKey = new PublicKey(solverAccount), + ReceiverPublicKey = new PublicKey(request.Receiver), + DestinationAddress = request.DestinationAddress, + Reward = request.Reward, + RewardTimelock = new BigInteger(request.RewardTimelock), + }); + } + else + { + builder.SetSplLockTransactionInstruction( + new PublicKey(htlcContractAddress), + new HTLCSplLockRequest + { + Id = request.CommitId.HexToByteArray(), + Hashlock = request.Hashlock.HexToByteArray(), + Timelock = new BigInteger(request.Timelock), + Amount = request.Amount, + DestinationNetwork = request.DestinationNetwork, + DestinationAsset = request.DestinationAsset, + SourceAsset = currency.Symbol, + SignerPublicKey = new PublicKey(solverAccount), + ReceiverPublicKey = new PublicKey(request.Receiver), + DestinationAddress = request.DestinationAddress, + SourceTokenPublicKey = new PublicKey(currency.Contract), + Reward = request.Reward, + RewardTimelock = new BigInteger(request.RewardTimelock), + }); + } var latestBlockResult = await rpcClient.GetLatestBlockHashAsync(); if (!latestBlockResult.WasSuccessful) { - throw new ($"Failed to get last valid block"); + throw new($"Failed to get last valid block"); } builder.SetRecentBlockHash(latestBlockResult.Result.Value.Blockhash); @@ -133,7 +156,7 @@ public static async Task BuildHTLCRedeemTransactionAsync( { throw new ArgumentNullException(nameof(request.SenderAddress), "Sender address is required"); } - + var currency = network.Tokens.SingleOrDefault(x => x.Symbol.ToUpper() == request.Asset.ToUpper()); if (currency is null) @@ -160,12 +183,12 @@ public static async Task BuildHTLCRedeemTransactionAsync( var builder = new TransactionBuilder() .SetFeePayer(new PublicKey(solverAccount)); - await GetOrCreateAssociatedTokenAccount( - rpcClient, - builder, - currency, - new PublicKey(solverAccount), - new PublicKey(solverAccount)); + await GetOrCreateAssociatedTokenAccount( + rpcClient, + builder, + currency, + new PublicKey(solverAccount), + new PublicKey(solverAccount)); builder.SetRedeemTransactionInstruction( new PublicKey(htlcContractAddress), @@ -177,7 +200,7 @@ await GetOrCreateAssociatedTokenAccount( SignerPublicKey = new PublicKey(solverAccount), ReceiverPublicKey = new PublicKey(request.DestinationAddress), SenderPublicKey = new PublicKey(request.SenderAddress), - RewardPublicKey = request.DestinationAddress == solverAccount? + RewardPublicKey = request.DestinationAddress == solverAccount ? new PublicKey(request.DestinationAddress) : new PublicKey(request.SenderAddress), }); @@ -535,5 +558,5 @@ private static async Task GetOrCreateAssociatedTokenAccount( { throw new Exception("Failed to load token wallet", ex); } - } + } } diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/HTLCProgram.cs b/csharp/src/Workflow.Solana/Programs/HTLCProgram/HTLCProgram.cs index 42eca084..58dafed8 100644 --- a/csharp/src/Workflow.Solana/Programs/HTLCProgram/HTLCProgram.cs +++ b/csharp/src/Workflow.Solana/Programs/HTLCProgram/HTLCProgram.cs @@ -1,18 +1,21 @@ using System.Text; using Solnet.Rpc.Builders; using Solnet.Wallet; +using Train.Solver.Blockchain.Solana.Programs; +using Train.Solver.Blockchain.Solana.Programs.HTLCProgram; using Train.Solver.Blockchain.Solana.Programs.HTLCProgram.Models; +using Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; -namespace Train.Solver.Blockchain.Solana.Programs.HTLCProgram; +namespace Train.Solver.Workflow.Solana.Programs.HTLCProgram; public static class HTLCProgram { - public static TransactionBuilder SetLockTransactionInstruction( + public static TransactionBuilder SetSplLockTransactionInstruction( this TransactionBuilder builder, PublicKey htlcProgramIdKey, - HTLCLockRequest htlcLockRequest) + HTLCSplLockRequest htlcLockRequest) { - var htlcPdaParams = GetHtlcPdaParams(htlcLockRequest.Id, htlcProgramIdKey); + var htlcPdaParams = GetSPLHtlcPdaParams(htlcLockRequest.Id, htlcProgramIdKey); var lockData = new HtlcInstructionDataBuilder().CreateLockData(htlcLockRequest); var lockRewardData = new HtlcInstructionDataBuilder().CreateLockRewardData(htlcLockRequest); @@ -21,7 +24,7 @@ public static TransactionBuilder SetLockTransactionInstruction( builder.AddInstruction(new() { ProgramId = htlcProgramIdKey, - Keys = HtlcInstructionKeyProvider.CreateLockAccountKeys(htlcLockRequest, htlcPdaParams), + Keys = HtlcInstructionKeyProvider.CreateSplLockAccountKeys(htlcLockRequest, htlcPdaParams), Data = lockData }); @@ -36,12 +39,38 @@ public static TransactionBuilder SetLockTransactionInstruction( return builder; } + public static TransactionBuilder SetSolLockTransactionInstruction( + this TransactionBuilder builder, + PublicKey htlcProgramIdKey, + HTLCSolLockRequest htlcLockRequest) + { + var pdaParams = GetSolHtlcParams(htlcLockRequest.Id, htlcProgramIdKey); + + var lockData = new HtlcInstructionDataBuilder().CreateLockData(htlcLockRequest); + var lockRewardData = new HtlcInstructionDataBuilder().CreateLockRewardData(htlcLockRequest); + + builder.AddInstruction(new() + { + ProgramId = htlcProgramIdKey, + Keys = HtlcInstructionKeyProvider.CreateSolLockAccountKeys(htlcLockRequest, pdaParams), + Data = lockData + }); + + builder.AddInstruction(new() + { + ProgramId = htlcProgramIdKey, + Keys = HtlcInstructionKeyProvider.CreateSolLockRewardAccountKeys(htlcLockRequest, pdaParams), + Data = lockRewardData + }); + return builder; + } + public static TransactionBuilder SetRedeemTransactionInstruction( this TransactionBuilder builder, PublicKey htlcProgramIdKey, HTLCRedeemRequest redeemRequest) { - var pdaParams = GetHtlcPdaParams(redeemRequest.Id, htlcProgramIdKey); + var pdaParams = GetSPLHtlcPdaParams(redeemRequest.Id, htlcProgramIdKey); var redeemData = new HtlcInstructionDataBuilder().CreateRedeemData(redeemRequest, pdaParams); @@ -60,7 +89,7 @@ public static TransactionBuilder SetRefundTransactionInstruction( PublicKey htlcProgramIdKey, HTLCRefundRequest htlcRefundRequest) { - var pdaParams = GetHtlcPdaParams(htlcRefundRequest.Id, htlcProgramIdKey); + var pdaParams = GetSPLHtlcPdaParams(htlcRefundRequest.Id, htlcProgramIdKey); var refundData = new HtlcInstructionDataBuilder().CreateRefundData(htlcRefundRequest, pdaParams); @@ -79,7 +108,7 @@ public static TransactionBuilder SetAddLockSigInstruction( PublicKey htlcProgramIdKey, HTLCAddlocksigRequest htlcAddlocksigRequest) { - var pdaParams = GetHtlcPdaParams(htlcAddlocksigRequest.AddLockSigMessageRequest.Id, htlcProgramIdKey); + var pdaParams = GetSPLHtlcPdaParams(htlcAddlocksigRequest.AddLockSigMessageRequest.Id, htlcProgramIdKey); var message = Ed25519Program.CreateAddLockSigMessage(htlcAddlocksigRequest.AddLockSigMessageRequest); var addLockSigData = new HtlcInstructionDataBuilder().CreateAddLockSigData(htlcAddlocksigRequest, pdaParams); @@ -103,7 +132,7 @@ public static TransactionBuilder SetGetDetailsInstruction( PublicKey htlcProgramIdKey, byte[] id) { - var pdaParams = GetHtlcPdaParams(id, htlcProgramIdKey); + var pdaParams = GetSPLHtlcPdaParams(id, htlcProgramIdKey); var getDetailsData = new HtlcInstructionDataBuilder().CreateGetDetailsData(pdaParams, id); @@ -117,7 +146,7 @@ public static TransactionBuilder SetGetDetailsInstruction( return builder; } - private static HTLCPdaResponse GetHtlcPdaParams( + private static HTLCSplPdaResponse GetSPLHtlcPdaParams( byte[] Id, PublicKey htlcProgramIdKey) { @@ -147,4 +176,23 @@ private static HTLCPdaResponse GetHtlcPdaParams( HtlcBump = htlcBump }; } + + private static HTLCSolPdaResponse GetSolHtlcParams( + byte[] Id, + PublicKey htlcProgramIdKey) + { + var htlc = PublicKey.TryFindProgramAddress( + new List() + { + Id + }, + htlcProgramIdKey, + out PublicKey htlcPubKey, + out _); + + return new() + { + HtlcPublicKey = htlcPubKey + }; + } } diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/HtlcInstructionDataBuilder.cs b/csharp/src/Workflow.Solana/Programs/HTLCProgram/HtlcInstructionDataBuilder.cs index bcc61755..4e93a791 100644 --- a/csharp/src/Workflow.Solana/Programs/HTLCProgram/HtlcInstructionDataBuilder.cs +++ b/csharp/src/Workflow.Solana/Programs/HTLCProgram/HtlcInstructionDataBuilder.cs @@ -4,6 +4,7 @@ using System.Text; using Train.Solver.Blockchain.Solana.Helpers; using Train.Solver.Blockchain.Solana.Programs.HTLCProgram.Models; +using Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; namespace Train.Solver.Blockchain.Solana.Programs.HTLCProgram; @@ -19,13 +20,13 @@ private void SetFieldData(string property, int span, Action Span = span, EncoderFunc = encoderFunc }); - } + } public byte[] CreateLockData( - HTLCLockRequest lockRequest) + HTLCSolLockRequest lockRequest) { var destinationAssetData = Encoding.UTF8.GetBytes(lockRequest.DestinationAsset); - var sourceAddressData = Encoding.UTF8.GetBytes(lockRequest.SourceAddress); + var destionationAddressData = Encoding.UTF8.GetBytes(lockRequest.DestinationAddress); var destinationNetworkData = Encoding.UTF8.GetBytes(lockRequest.DestinationNetwork); var sourceAssetData = Encoding.UTF8.GetBytes(lockRequest.SourceAsset); @@ -33,7 +34,7 @@ public byte[] CreateLockData( SetFieldData("hashlock", lockRequest.Hashlock.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); SetFieldData("timelock", 8, (v, buf, off) => buf.WriteBigInt((BigInteger)v, off, 8, isUnsigned: true, isBigEndian: false)); SetFieldData("destinationNetwork", destinationNetworkData.Length + 4, (v, buf, off) => FieldEncoder.EncodeByteArrayWithLength((byte[])v, buf, ref off)); - SetFieldData("sourceAddress", sourceAddressData.Length + 4, (v, buf, off) => FieldEncoder.EncodeByteArrayWithLength((byte[])v, buf, ref off)); + SetFieldData("destinationAddress", destionationAddressData.Length + 4, (v, buf, off) => FieldEncoder.EncodeByteArrayWithLength((byte[])v, buf, ref off)); SetFieldData("destinationAsset", destinationAssetData.Length + 4, (v, buf, off) => FieldEncoder.EncodeByteArrayWithLength((byte[])v, buf, ref off)); SetFieldData("sourceAsset", sourceAssetData.Length + 4, (v, buf, off) => FieldEncoder.EncodeByteArrayWithLength((byte[])v, buf, ref off)); SetFieldData("receiver", 32, (v, buf, off) => buf.WritePubKey((PublicKey)v, off)); @@ -45,7 +46,7 @@ public byte[] CreateLockData( { "hashlock", lockRequest.Hashlock}, { "timelock", lockRequest.Timelock}, { "destinationNetwork", destinationNetworkData }, - { "sourceAddress", sourceAddressData }, + { "destinationAddress", destionationAddressData }, { "destinationAsset", destinationAssetData }, { "sourceAsset", sourceAssetData }, { "receiver", lockRequest.ReceiverPublicKey }, @@ -58,7 +59,7 @@ public byte[] CreateLockData( } public byte[] CreateLockRewardData( - HTLCLockRequest lockRequest) + HTLCSolLockRequest lockRequest) { SetFieldData("id", lockRequest.Id.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); SetFieldData("rewardTimelock", 8, (v, buf, off) => buf.WriteBigInt((BigInteger)v, off, 8, isUnsigned: true, isBigEndian: false)); @@ -78,7 +79,7 @@ public byte[] CreateLockRewardData( public byte[] CreateRedeemData( HTLCRedeemRequest redeemRequest, - HTLCPdaResponse htlcPdaResponse) + HTLCSplPdaResponse htlcPdaResponse) { SetFieldData("id", redeemRequest.Id.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); SetFieldData("secret", redeemRequest.Secret.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); @@ -99,7 +100,7 @@ public byte[] CreateRedeemData( public byte[] CreateRefundData( HTLCRefundRequest refundRequest, - HTLCPdaResponse htlcPdaResponse) + HTLCSplPdaResponse htlcPdaResponse) { SetFieldData("id", refundRequest.Id.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); SetFieldData("htlcBump", 1, (v, buf, off) => buf.WriteU8((byte)v, off)); @@ -116,7 +117,7 @@ public byte[] CreateRefundData( } public byte[] CreateGetDetailsData( - HTLCPdaResponse hTLCPdaResponse, + HTLCSplPdaResponse hTLCPdaResponse, byte[] id) { SetFieldData("id", id.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); @@ -135,7 +136,7 @@ public byte[] CreateGetDetailsData( public byte[] CreateAddLockSigData( HTLCAddlocksigRequest addLockSigRequest, - HTLCPdaResponse htlcPdaResponse) + HTLCSplPdaResponse htlcPdaResponse) { SetFieldData("id", addLockSigRequest.AddLockSigMessageRequest.Id.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); SetFieldData("hashlock", addLockSigRequest.AddLockSigMessageRequest.Hashlock.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); @@ -155,7 +156,6 @@ public byte[] CreateAddLockSigData( FieldEncoder.Sighash(SolanaConstants.AddLockSigSighash)); } - private byte[] BuildInstructionData(Dictionary instructionExecutionOrder, byte[] descriminator) { return FieldEncoder.Encode(Fields, instructionExecutionOrder, descriminator); diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/HtlcInstructionKeyProvider.cs b/csharp/src/Workflow.Solana/Programs/HTLCProgram/HtlcInstructionKeyProvider.cs index a6865014..87c1fa31 100644 --- a/csharp/src/Workflow.Solana/Programs/HTLCProgram/HtlcInstructionKeyProvider.cs +++ b/csharp/src/Workflow.Solana/Programs/HTLCProgram/HtlcInstructionKeyProvider.cs @@ -3,14 +3,15 @@ using Solnet.Wallet; using Train.Solver.Blockchain.Solana.Helpers; using Train.Solver.Blockchain.Solana.Programs.HTLCProgram.Models; +using Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; namespace Train.Solver.Blockchain.Solana.Programs.HTLCProgram; public static class HtlcInstructionKeyProvider { - public static List CreateLockAccountKeys( - HTLCLockRequest htlcLockRequest, - HTLCPdaResponse htlcPdaResponse) + public static List CreateSplLockAccountKeys( + HTLCSplLockRequest htlcLockRequest, + HTLCSplPdaResponse htlcPdaResponse) { var keys = new List() { @@ -27,7 +28,22 @@ public static List CreateLockAccountKeys( return keys; } - public static IList CreateLockRewardAccountKeys(HTLCLockRequest htlcLockRequest, HTLCPdaResponse htlcPdaResponse) + public static List CreateSolLockAccountKeys( + HTLCSolLockRequest htlcLockRequest, + HTLCSolPdaResponse htlcPdaResponse) + { + var keys = new List() + { + AccountMeta.Writable(publicKey: htlcLockRequest.SignerPublicKey, isSigner: true), + AccountMeta.Writable(publicKey: htlcPdaResponse.HtlcPublicKey, isSigner: false), + AccountMeta.ReadOnly(publicKey: SystemProgram.ProgramIdKey, isSigner: false), + AccountMeta.ReadOnly(publicKey: SysVars.RentKey, isSigner: false) + }; + + return keys; + } + + public static IList CreateLockRewardAccountKeys(HTLCSplLockRequest htlcLockRequest, HTLCSplPdaResponse htlcPdaResponse) { var keys = new List() { @@ -44,9 +60,22 @@ public static IList CreateLockRewardAccountKeys(HTLCLockRequest htl return keys; } + public static IList CreateSolLockRewardAccountKeys(HTLCSolLockRequest htlcLockRequest, HTLCSolPdaResponse htlcPdaResponse) + { + var keys = new List() + { + AccountMeta.Writable(publicKey: htlcLockRequest.SignerPublicKey, isSigner: true), + AccountMeta.Writable(publicKey: htlcPdaResponse.HtlcPublicKey, isSigner: false), + AccountMeta.ReadOnly(publicKey: SystemProgram.ProgramIdKey, isSigner: false), + AccountMeta.ReadOnly(publicKey: SysVars.RentKey, isSigner: false) + }; + + return keys; + } + public static List SetRefundAccountKeys( HTLCRefundRequest refundRequest, - HTLCPdaResponse htlcPdaResponse) + HTLCSplPdaResponse htlcPdaResponse) { var keys = new List() { @@ -66,7 +95,7 @@ public static List SetRefundAccountKeys( public static List CreateRedeemAccountKeys( HTLCRedeemRequest htlcRedeemRequest, - HTLCPdaResponse htlcPdaResponse) + HTLCSplPdaResponse htlcPdaResponse) { var keys = new List() { @@ -100,7 +129,7 @@ public static List CreateGetDetailsAccountKeys(PublicKey htlcPubKey public static List CreateAddLockSigAccountKeys( HTLCAddlocksigRequest hTLCAddlocksigRequest, - HTLCPdaResponse htlcPdaResponse) + HTLCSplPdaResponse htlcPdaResponse) { var keys = new List { diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCLockRequest.cs b/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCLockRequest.cs deleted file mode 100644 index abc1235c..00000000 --- a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCLockRequest.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Numerics; -using Solnet.Wallet; - -namespace Train.Solver.Blockchain.Solana.Programs.HTLCProgram.Models; - -public class HTLCLockRequest -{ - public byte[] Hashlock { get; set; } = null!; - - public BigInteger Timelock { get; set; } - - public string DestinationNetwork { get; set; } = null!; - - public string SourceAddress { get; set; } = null!; - - public string DestinationAsset { get; set; } = null!; - - public string SourceAsset { get; set; } = null!; - - public byte[] Id { get; set; } = null!; - - public PublicKey SignerPublicKey { get; set; } = null!; - - public PublicKey ReceiverPublicKey { get; set; } = null!; - - public PublicKey SourceTokenPublicKey { get; set; } = null!; - - public BigInteger Amount { get; set; } - - public BigInteger Reward { get; set; } - - public BigInteger RewardTimelock { get; set; } -} diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCPdaResponse.cs b/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCPdaResponse.cs deleted file mode 100644 index 3e52827e..00000000 --- a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCPdaResponse.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Solnet.Wallet; - -namespace Train.Solver.Blockchain.Solana.Programs.HTLCProgram.Models; - -public class HTLCPdaResponse -{ - public PublicKey HtlcPublicKey { get; set; } = null!; - - public PublicKey HtlcTokenAccount { get; set; } = null!; - - public byte HtlcBump { get; set; } -} diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSolLockRequest.cs b/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSolLockRequest.cs new file mode 100644 index 00000000..3173ec29 --- /dev/null +++ b/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSolLockRequest.cs @@ -0,0 +1,31 @@ +using Solnet.Wallet; +using System.Numerics; + +namespace Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; + +public class HTLCSolLockRequest +{ + public required byte[] Id { get; set; } = null!; + + public required byte[] Hashlock { get; set; } = null!; + + public required BigInteger Timelock { get; set; } + + public required BigInteger Amount { get; set; } + + public required string DestinationNetwork { get; set; } = null!; + + public required string DestinationAddress { get; set; } = null!; + + public required string DestinationAsset { get; set; } = null!; + + public required string SourceAsset { get; set; } = null!; + + public required PublicKey SignerPublicKey { get; set; } = null!; + + public required PublicKey ReceiverPublicKey { get; set; } = null!; + + public required BigInteger Reward { get; set; } + + public required BigInteger RewardTimelock { get; set; } +} diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSolPdaResponse.cs b/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSolPdaResponse.cs new file mode 100644 index 00000000..2a698235 --- /dev/null +++ b/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSolPdaResponse.cs @@ -0,0 +1,8 @@ +using Solnet.Wallet; + +namespace Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; + +public class HTLCSolPdaResponse +{ + public required PublicKey HtlcPublicKey { get; set; } = null!; +} diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSplLockRequest.cs b/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSplLockRequest.cs new file mode 100644 index 00000000..9927a698 --- /dev/null +++ b/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSplLockRequest.cs @@ -0,0 +1,8 @@ +using Solnet.Wallet; + +namespace Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; + +public class HTLCSplLockRequest : HTLCSolLockRequest +{ + public PublicKey SourceTokenPublicKey { get; set; } = null!; +} diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSplPdaResponse.cs b/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSplPdaResponse.cs new file mode 100644 index 00000000..86d0c75e --- /dev/null +++ b/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSplPdaResponse.cs @@ -0,0 +1,10 @@ +using Solnet.Wallet; + +namespace Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; + +public class HTLCSplPdaResponse : HTLCSolPdaResponse +{ + public PublicKey HtlcTokenAccount { get; set; } = null!; + + public byte HtlcBump { get; set; } +} From cabdf2d382f5875ee372cbbc2f676e41af936e44 Mon Sep 17 00:00:00 2001 From: Davit Mandzikyan Date: Wed, 19 Nov 2025 02:51:50 +0400 Subject: [PATCH 2/4] Create Solana native htlc program. --- .../Workflow.Solana/Helpers/EventDecoder.cs | 23 ++- .../Helpers/SolanaTransactionBuilder.cs | 121 ++++++++++------ .../HTLCProgram/Models/HTLCSolPdaResponse.cs | 8 -- .../Programs/HtlcSolProgram/HTLCSolProgram.cs | 91 ++++++++++++ .../HtlcSolInstructionDataBuilder.cs | 133 ++++++++++++++++++ .../HtlcSolInstructionKeyProvider.cs | 102 ++++++++++++++ .../Models/HtlcSolLockRequest.cs} | 2 +- .../Models/HtlcSolPdaResponse.cs | 8 ++ .../Models/HtlcSolRedeemRequest.cs} | 8 +- .../Models/HtlcSolRefundRequest.cs} | 6 +- .../HTLCSplProgram.cs} | 80 ++++------- .../HtlcSplInstructionDataBuilder.cs} | 50 ++++++- .../HtlcSplInstructionKeyProvider.cs} | 16 ++- .../Models/HtlcAddlocksigRequest.cs} | 4 +- .../Models/HtlcSplLockRequest.cs} | 1 + .../Models/HtlcSplPdaResponse.cs} | 3 +- .../Models/HtlcSplRedeemRequest.cs | 13 ++ .../Models/HtlcSplRefundRequest.cs | 9 ++ 18 files changed, 540 insertions(+), 138 deletions(-) delete mode 100644 csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSolPdaResponse.cs create mode 100644 csharp/src/Workflow.Solana/Programs/HtlcSolProgram/HTLCSolProgram.cs create mode 100644 csharp/src/Workflow.Solana/Programs/HtlcSolProgram/HtlcSolInstructionDataBuilder.cs create mode 100644 csharp/src/Workflow.Solana/Programs/HtlcSolProgram/HtlcSolInstructionKeyProvider.cs rename csharp/src/Workflow.Solana/Programs/{HTLCProgram/Models/HTLCSolLockRequest.cs => HtlcSolProgram/Models/HtlcSolLockRequest.cs} (92%) create mode 100644 csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolPdaResponse.cs rename csharp/src/Workflow.Solana/Programs/{HTLCProgram/Models/HTLCRedeemRequest.cs => HtlcSolProgram/Models/HtlcSolRedeemRequest.cs} (57%) rename csharp/src/Workflow.Solana/Programs/{HTLCProgram/Models/HTLCRefundRequest.cs => HtlcSolProgram/Models/HtlcSolRefundRequest.cs} (54%) rename csharp/src/Workflow.Solana/Programs/{HTLCProgram/HTLCProgram.cs => HtlcSplProgram/HTLCSplProgram.cs} (56%) rename csharp/src/Workflow.Solana/Programs/{HTLCProgram/HtlcInstructionDataBuilder.cs => HtlcSplProgram/HtlcSplInstructionDataBuilder.cs} (72%) rename csharp/src/Workflow.Solana/Programs/{HTLCProgram/HtlcInstructionKeyProvider.cs => HtlcSplProgram/HtlcSplInstructionKeyProvider.cs} (93%) rename csharp/src/Workflow.Solana/Programs/{HTLCProgram/Models/HTLCAddlocksigRequest.cs => HtlcSplProgram/Models/HtlcAddlocksigRequest.cs} (72%) rename csharp/src/Workflow.Solana/Programs/{HTLCProgram/Models/HTLCSplLockRequest.cs => HtlcSplProgram/Models/HtlcSplLockRequest.cs} (76%) rename csharp/src/Workflow.Solana/Programs/{HTLCProgram/Models/HTLCSplPdaResponse.cs => HtlcSplProgram/Models/HtlcSplPdaResponse.cs} (62%) create mode 100644 csharp/src/Workflow.Solana/Programs/HtlcSplProgram/Models/HtlcSplRedeemRequest.cs create mode 100644 csharp/src/Workflow.Solana/Programs/HtlcSplProgram/Models/HtlcSplRefundRequest.cs diff --git a/csharp/src/Workflow.Solana/Helpers/EventDecoder.cs b/csharp/src/Workflow.Solana/Helpers/EventDecoder.cs index c6f5fbdc..a3a5bdfc 100644 --- a/csharp/src/Workflow.Solana/Helpers/EventDecoder.cs +++ b/csharp/src/Workflow.Solana/Helpers/EventDecoder.cs @@ -8,6 +8,7 @@ using Train.Solver.Infrastructure.Abstractions.Models; using Train.Solver.Workflow.Abstractions.Models; using Train.Solver.Workflow.Solana.Programs.HTLCProgram; +using Train.Solver.Workflow.Solana.Programs.HtlcSolProgram; namespace Train.Solver.Blockchain.Solana.Helpers; @@ -34,9 +35,11 @@ public static async Task GetBlockEventsAsync( { var htlcTokenContractAddress = network.HTLCTokenContractAddress; + var htlcNativeContractAddress = network.HTLCNativeContractAddress; + var trackedBlockEvents = blockResponseResult.Result.Transactions .Where(transaction => transaction.Transaction.Message.Instructions - .Any(instruction => instruction.ProgramId == htlcTokenContractAddress)) + .Any(instruction => instruction.ProgramId == htlcTokenContractAddress || instruction.ProgramId == htlcNativeContractAddress)) .ToList(); foreach (var transaction in trackedBlockEvents) @@ -47,11 +50,15 @@ public static async Task GetBlockEventsAsync( var isLockEvent = transaction.Meta.LogMessages .Any(x => x.Contains(SolanaConstants.HtlcConstants.addLockEventPrefixPattern)); + var contractAddress = transaction.Transaction.Message.Instructions.Any(x => x.ProgramId == htlcTokenContractAddress) ? + htlcTokenContractAddress : + htlcNativeContractAddress; + var accountForSimulation = solverAccounts.First(); if (isCommitEvent) { - var prefixPattern = "Program return: " + htlcTokenContractAddress + " "; + var prefixPattern = "Program return: " + contractAddress + " "; var logResult = transaction.Meta.LogMessages.Where(s => s.StartsWith(prefixPattern)) .Select(s => s.Substring(prefixPattern.Length)) @@ -63,6 +70,7 @@ public static async Task GetBlockEventsAsync( var commitEvent = await DeserializeCommitEventDataAsync( rpcClient, network, + contractAddress, id, accountForSimulation); @@ -106,7 +114,7 @@ public static async Task GetBlockEventsAsync( if (isLockEvent) { - var prefixPattern = "Program return: " + htlcTokenContractAddress + " "; + var prefixPattern = "Program return: " + contractAddress + " "; var logResult = transaction.Meta.LogMessages.Where(s => s.StartsWith(prefixPattern)) .Select(s => s.Substring(prefixPattern.Length)) @@ -118,6 +126,7 @@ public static async Task GetBlockEventsAsync( var addLockMessageResult = await DeserializeAddLockEventDataAsync( rpcClient, network, + contractAddress, id, accountForSimulation); @@ -139,6 +148,7 @@ public static async Task GetBlockEventsAsync( private static async Task DeserializeCommitEventDataAsync( IRpcClient rpcClient, DetailedNetworkDto network, + string contractAddress, string commitId, string solverAccount) { @@ -152,7 +162,7 @@ private static async Task DeserializeCommitEventData .SetFeePayer(new PublicKey(solverAccount)); builder.SetGetDetailsInstruction( - new PublicKey(network.HTLCTokenContractAddress), + new PublicKey(contractAddress), commitId.HexToByteArray()); var latestBlockHashResponse = await rpcClient.GetLatestBlockHashAsync(); @@ -191,6 +201,7 @@ private static async Task DeserializeCommitEventData private static async Task DeserializeAddLockEventDataAsync( IRpcClient rpcClient, DetailedNetworkDto network, + string contractAddress, string commitId, string solverAccount) { @@ -205,10 +216,8 @@ private static async Task DeserializeCommitEventData var builder = new TransactionBuilder() .SetFeePayer(new PublicKey(solverAccount)); - var htlcContractAddress = network.HTLCTokenContractAddress; - builder.SetGetDetailsInstruction( - new PublicKey(htlcContractAddress), + new PublicKey(contractAddress), commitId.HexToByteArray()); var latestBlockHashResponse = await rpcClient.GetLatestBlockHashAsync(); diff --git a/csharp/src/Workflow.Solana/Helpers/SolanaTransactionBuilder.cs b/csharp/src/Workflow.Solana/Helpers/SolanaTransactionBuilder.cs index 2a639d4d..d79c3459 100644 --- a/csharp/src/Workflow.Solana/Helpers/SolanaTransactionBuilder.cs +++ b/csharp/src/Workflow.Solana/Helpers/SolanaTransactionBuilder.cs @@ -14,6 +14,9 @@ using Train.Solver.Workflow.Abstractions.Models; using Train.Solver.Workflow.Solana.Programs.HTLCProgram; using Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; +using Train.Solver.Workflow.Solana.Programs.HtlcSolProgram; +using Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; +using Train.Solver.Workflow.Solana.Programs.HtlcSplProgram.Models; namespace Train.Solver.Workflow.Solana.Helpers; @@ -59,18 +62,11 @@ public static async Task BuildHTLCLockTransactionAsync( var builder = new TransactionBuilder() .SetFeePayer(new PublicKey(solverAccount)); - await GetOrCreateAssociatedTokenAccount( - rpcClient, - builder, - currency, - new PublicKey(solverAccount), - new PublicKey(solverAccount)); - if (isNative) { builder.SetSolLockTransactionInstruction( new PublicKey(htlcContractAddress), - new HTLCSplLockRequest + new HTLCSolLockRequest { Id = request.CommitId.HexToByteArray(), Hashlock = request.Hashlock.HexToByteArray(), @@ -88,6 +84,13 @@ await GetOrCreateAssociatedTokenAccount( } else { + await GetOrCreateAssociatedTokenAccount( + rpcClient, + builder, + currency, + new PublicKey(solverAccount), + new PublicKey(solverAccount)); + builder.SetSplLockTransactionInstruction( new PublicKey(htlcContractAddress), new HTLCSplLockRequest @@ -183,27 +186,43 @@ public static async Task BuildHTLCRedeemTransactionAsync( var builder = new TransactionBuilder() .SetFeePayer(new PublicKey(solverAccount)); - await GetOrCreateAssociatedTokenAccount( - rpcClient, - builder, - currency, - new PublicKey(solverAccount), - new PublicKey(solverAccount)); + if (isNative) + { + builder.SetSolRedeemTransactionInstruction( + new PublicKey(htlcContractAddress), + new HTLCSolRedeemRequest + { + Id = request.CommitId.HexToByteArray(), + Secret = BigInteger.Parse(request.Secret).ToHexBigInteger().HexValue.HexToByteArray(), + SignerPublicKey = new PublicKey(solverAccount), + ReceiverPublicKey = new PublicKey(request.DestinationAddress), + SenderPublicKey = new PublicKey(request.SenderAddress), + }); + } + else + { + await GetOrCreateAssociatedTokenAccount( + rpcClient, + builder, + currency, + new PublicKey(solverAccount), + new PublicKey(solverAccount)); - builder.SetRedeemTransactionInstruction( - new PublicKey(htlcContractAddress), - new HTLCRedeemRequest - { - Id = request.CommitId.HexToByteArray(), - Secret = BigInteger.Parse(request.Secret).ToHexBigInteger().HexValue.HexToByteArray(), - SourceTokenPublicKey = new PublicKey(currency.Contract), - SignerPublicKey = new PublicKey(solverAccount), - ReceiverPublicKey = new PublicKey(request.DestinationAddress), - SenderPublicKey = new PublicKey(request.SenderAddress), - RewardPublicKey = request.DestinationAddress == solverAccount ? - new PublicKey(request.DestinationAddress) : - new PublicKey(request.SenderAddress), - }); + builder.SetSplRedeemTransactionInstruction( + new PublicKey(htlcContractAddress), + new HTLCSplRedeemRequest + { + Id = request.CommitId.HexToByteArray(), + Secret = BigInteger.Parse(request.Secret).ToHexBigInteger().HexValue.HexToByteArray(), + SourceTokenPublicKey = new PublicKey(currency.Contract), + SignerPublicKey = new PublicKey(solverAccount), + ReceiverPublicKey = new PublicKey(request.DestinationAddress), + SenderPublicKey = new PublicKey(request.SenderAddress), + RewardPublicKey = request.DestinationAddress == solverAccount ? + new PublicKey(request.DestinationAddress) : + new PublicKey(request.SenderAddress), + }); + } var latestBlockResult = await rpcClient.GetLatestBlockHashAsync(); @@ -268,22 +287,36 @@ public static async Task BuildHTLCRefundTransactionAsync( var builder = new TransactionBuilder() .SetFeePayer(new PublicKey(solverAccount)); - await GetOrCreateAssociatedTokenAccount( - rpcClient, - builder, - currency, - new PublicKey(request.DestinationAddress), - new PublicKey(solverAccount)); + if (isNative) + { + builder.SetSolRefundTransactionInstruction( + new PublicKey(htlcContractAddress), + new HtlcSolRefundRequest + { + Id = request.CommitId.HexToByteArray(), + SignerPublicKey = new PublicKey(solverAccount), + ReceiverPublicKey = new PublicKey(request.DestinationAddress) + }); + } + else + { + await GetOrCreateAssociatedTokenAccount( + rpcClient, + builder, + currency, + new PublicKey(request.DestinationAddress), + new PublicKey(solverAccount)); - builder.SetRefundTransactionInstruction( - new PublicKey(htlcContractAddress), - new HTLCRefundRequest - { - Id = request.CommitId.HexToByteArray(), - SourceTokenPublicKey = new PublicKey(currency.Contract), - SignerPublicKey = new PublicKey(solverAccount), - ReceiverPublicKey = new PublicKey(request.DestinationAddress) - }); + builder.SetSplRefundTransactionInstruction( + new PublicKey(htlcContractAddress), + new HtlcSplRefundRequest + { + Id = request.CommitId.HexToByteArray(), + SourceTokenPublicKey = new PublicKey(currency.Contract), + SignerPublicKey = new PublicKey(solverAccount), + ReceiverPublicKey = new PublicKey(request.DestinationAddress) + }); + } var latestBlockHashResponse = await rpcClient.GetLatestBlockHashAsync(); @@ -422,7 +455,7 @@ public static async Task BuildHTLCAddlockSigTransactionAs builder.SetAddLockSigInstruction( new PublicKey(htlcContractAddress), - new HTLCAddlocksigRequest + new HtlcAddlocksigRequest { AddLockSigMessageRequest = new() { diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSolPdaResponse.cs b/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSolPdaResponse.cs deleted file mode 100644 index 2a698235..00000000 --- a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSolPdaResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Solnet.Wallet; - -namespace Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; - -public class HTLCSolPdaResponse -{ - public required PublicKey HtlcPublicKey { get; set; } = null!; -} diff --git a/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/HTLCSolProgram.cs b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/HTLCSolProgram.cs new file mode 100644 index 00000000..986c4a42 --- /dev/null +++ b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/HTLCSolProgram.cs @@ -0,0 +1,91 @@ +using Solnet.Rpc.Builders; +using Solnet.Wallet; +using Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; + +namespace Train.Solver.Workflow.Solana.Programs.HtlcSolProgram; + +public static class HtlcSolProgram +{ + public static TransactionBuilder SetSolLockTransactionInstruction( + this TransactionBuilder builder, + PublicKey htlcProgramIdKey, + HTLCSolLockRequest htlcLockRequest) + { + var pdaParams = GetSolHtlcPdaParams(htlcLockRequest.Id, htlcProgramIdKey); + + var lockData = new HtlcSolInstructionDataBuilder().CreateSolLockData(htlcLockRequest); + var lockRewardData = new HtlcSolInstructionDataBuilder().CreateLockRewardData(htlcLockRequest); + + builder.AddInstruction(new() + { + ProgramId = htlcProgramIdKey, + Keys = HtlcSolInstructionKeyProvider.CreateSolLockAccountKeys(htlcLockRequest, pdaParams), + Data = lockData + }); + + builder.AddInstruction(new() + { + ProgramId = htlcProgramIdKey, + Keys = HtlcSolInstructionKeyProvider.CreateSolLockRewardAccountKeys(htlcLockRequest, pdaParams), + Data = lockRewardData + }); + return builder; + } + + public static TransactionBuilder SetSolRedeemTransactionInstruction( + this TransactionBuilder builder, + PublicKey htlcProgramIdKey, + HTLCSolRedeemRequest redeemRequest) + { + var pdaParams = GetSolHtlcPdaParams(redeemRequest.Id, htlcProgramIdKey); + + var redeemData = new HtlcSolInstructionDataBuilder().CreateSolRedeemData(redeemRequest); + + builder.AddInstruction(new() + { + ProgramId = htlcProgramIdKey, + Keys = HtlcSolInstructionKeyProvider.CreateSolRedeemAccountKeys(redeemRequest, pdaParams), + Data = redeemData + }); + + return builder; + } + + public static TransactionBuilder SetSolRefundTransactionInstruction( + this TransactionBuilder builder, + PublicKey htlcProgramIdKey, + HtlcSolRefundRequest htlcRefundRequest) + { + var pdaParams = GetSolHtlcPdaParams(htlcRefundRequest.Id, htlcProgramIdKey); + + var refundData = new HtlcSolInstructionDataBuilder().CreateSolRefundData(htlcRefundRequest); + + builder.AddInstruction(new() + { + ProgramId = htlcProgramIdKey, + Keys = HtlcSolInstructionKeyProvider.SetSolRefundAccountKeys(htlcRefundRequest, pdaParams), + Data = refundData + }); + + return builder; + } + + private static HtlcSolPdaResponse GetSolHtlcPdaParams( + byte[] Id, + PublicKey htlcProgramIdKey) + { + var htlc = PublicKey.TryFindProgramAddress( + new List() + { + Id + }, + htlcProgramIdKey, + out PublicKey htlcPubKey, + out _); + + return new() + { + HtlcPublicKey = htlcPubKey + }; + } +} diff --git a/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/HtlcSolInstructionDataBuilder.cs b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/HtlcSolInstructionDataBuilder.cs new file mode 100644 index 00000000..6001ea15 --- /dev/null +++ b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/HtlcSolInstructionDataBuilder.cs @@ -0,0 +1,133 @@ +using Solnet.Programs.Utilities; +using Solnet.Wallet; +using System.Numerics; +using System.Text; +using Train.Solver.Blockchain.Solana.Helpers; +using Train.Solver.Blockchain.Solana.Programs.HTLCProgram.Models; +using Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; +using Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; + +namespace Train.Solver.Workflow.Solana.Programs.HtlcSolProgram; + +public class HtlcSolInstructionDataBuilder +{ + private List Fields { get; set; } = new(); + + private void SetFieldData(string property, int span, Action encoderFunc) + { + Fields.Add(new FieldEncoder.Field + { + Property = property, + Span = span, + EncoderFunc = encoderFunc + }); + } + + public byte[] CreateSolLockData( + HTLCSolLockRequest lockRequest) + { + var destinationAssetData = Encoding.UTF8.GetBytes(lockRequest.DestinationAsset); + var destionationAddressData = Encoding.UTF8.GetBytes(lockRequest.DestinationAddress); + var destinationNetworkData = Encoding.UTF8.GetBytes(lockRequest.DestinationNetwork); + var sourceAssetData = Encoding.UTF8.GetBytes(lockRequest.SourceAsset); + + SetFieldData("id", lockRequest.Id.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); + SetFieldData("hashlock", lockRequest.Hashlock.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); + SetFieldData("timelock", 8, (v, buf, off) => buf.WriteBigInt((BigInteger)v, off, 8, isUnsigned: true, isBigEndian: false)); + SetFieldData("amount", 8, (v, buf, off) => buf.WriteBigInt((BigInteger)v, off, 8, isUnsigned: true, isBigEndian: false)); + SetFieldData("destinationNetwork", destinationNetworkData.Length + 4, (v, buf, off) => FieldEncoder.EncodeByteArrayWithLength((byte[])v, buf, ref off)); + SetFieldData("destinationAddress", destionationAddressData.Length + 4, (v, buf, off) => FieldEncoder.EncodeByteArrayWithLength((byte[])v, buf, ref off)); + SetFieldData("destinationAsset", destinationAssetData.Length + 4, (v, buf, off) => FieldEncoder.EncodeByteArrayWithLength((byte[])v, buf, ref off)); + SetFieldData("sourceAsset", sourceAssetData.Length + 4, (v, buf, off) => FieldEncoder.EncodeByteArrayWithLength((byte[])v, buf, ref off)); + SetFieldData("receiver", 32, (v, buf, off) => buf.WritePubKey((PublicKey)v, off)); + + var instructionExecutionOrder = new Dictionary + { + { "id", lockRequest.Id}, + { "hashlock", lockRequest.Hashlock}, + { "timelock", lockRequest.Timelock}, + { "amount", lockRequest.Amount}, + { "destinationNetwork", destinationNetworkData }, + { "destinationAddress", destionationAddressData }, + { "destinationAsset", destinationAssetData }, + { "sourceAsset", sourceAssetData }, + { "receiver", lockRequest.ReceiverPublicKey }, + }; + + return BuildInstructionData( + instructionExecutionOrder, + FieldEncoder.Sighash(SolanaConstants.LockSighash)); + } + + public byte[] CreateLockRewardData( + HTLCSolLockRequest lockRequest) + { + SetFieldData("id", lockRequest.Id.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); + SetFieldData("rewardTimelock", 8, (v, buf, off) => buf.WriteBigInt((BigInteger)v, off, 8, isUnsigned: true, isBigEndian: false)); + SetFieldData("reward", 8, (v, buf, off) => buf.WriteBigInt((BigInteger)v, off, 8, isUnsigned: true, isBigEndian: false)); + + var instructionExecutionOrder = new Dictionary + { + { "id", lockRequest.Id}, + { "rewardTimelock", lockRequest.RewardTimelock}, + { "reward", lockRequest.Reward}, + }; + + return BuildInstructionData( + instructionExecutionOrder, + FieldEncoder.Sighash(SolanaConstants.LockRewardSighash)); + } + + public byte[] CreateSolRedeemData(HTLCSolRedeemRequest redeemRequest) + { + SetFieldData("id", redeemRequest.Id.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); + SetFieldData("secret", redeemRequest.Secret.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); + + var instructionExecutionOrder = new Dictionary + { + { "id", redeemRequest.Id}, + { "secret", redeemRequest.Secret}, + }; + + return BuildInstructionData( + instructionExecutionOrder, + FieldEncoder.Sighash(SolanaConstants.RedeemSighash)); + } + + public byte[] CreateRefundData( + HtlcSplRefundRequest refundRequest, + HTLCSplPdaResponse htlcPdaResponse) + { + SetFieldData("id", refundRequest.Id.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); + SetFieldData("htlcBump", 1, (v, buf, off) => buf.WriteU8((byte)v, off)); + + var instructionExecutionOrder = new Dictionary + { + { "id", refundRequest.Id }, + { "htlcBump", htlcPdaResponse.HtlcBump } + }; + + return BuildInstructionData( + instructionExecutionOrder, + FieldEncoder.Sighash(SolanaConstants.RefundSighash)); + } + + public byte[] CreateSolRefundData(HtlcSolRefundRequest refundRequest) + { + SetFieldData("id", refundRequest.Id.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); + + var instructionExecutionOrder = new Dictionary + { + { "id", refundRequest.Id }, + }; + + return BuildInstructionData( + instructionExecutionOrder, + FieldEncoder.Sighash(SolanaConstants.RefundSighash)); + } + + private byte[] BuildInstructionData(Dictionary instructionExecutionOrder, byte[] descriminator) + { + return FieldEncoder.Encode(Fields, instructionExecutionOrder, descriminator); + } +} diff --git a/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/HtlcSolInstructionKeyProvider.cs b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/HtlcSolInstructionKeyProvider.cs new file mode 100644 index 00000000..4526cf96 --- /dev/null +++ b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/HtlcSolInstructionKeyProvider.cs @@ -0,0 +1,102 @@ +using Solnet.Programs; +using Solnet.Rpc.Models; +using Solnet.Wallet; +using Train.Solver.Blockchain.Solana.Programs.HTLCProgram.Models; +using Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; +using Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; + +namespace Train.Solver.Workflow.Solana.Programs.HtlcSolProgram; + +public static class HtlcSolInstructionKeyProvider +{ + public static List CreateSolLockAccountKeys( + HTLCSolLockRequest htlcLockRequest, + HtlcSolPdaResponse htlcPdaResponse) + { + var keys = new List() + { + AccountMeta.Writable(publicKey: htlcLockRequest.SignerPublicKey, isSigner: true), + AccountMeta.Writable(publicKey: htlcPdaResponse.HtlcPublicKey, isSigner: false), + AccountMeta.ReadOnly(publicKey: SystemProgram.ProgramIdKey, isSigner: false), + AccountMeta.ReadOnly(publicKey: SysVars.RentKey, isSigner: false) + }; + + return keys; + } + + public static IList CreateSolLockRewardAccountKeys(HTLCSolLockRequest htlcLockRequest, HtlcSolPdaResponse htlcPdaResponse) + { + var keys = new List() + { + AccountMeta.Writable(publicKey: htlcLockRequest.SignerPublicKey, isSigner: true), + AccountMeta.Writable(publicKey: htlcPdaResponse.HtlcPublicKey, isSigner: false), + AccountMeta.ReadOnly(publicKey: SystemProgram.ProgramIdKey, isSigner: false), + AccountMeta.ReadOnly(publicKey: SysVars.RentKey, isSigner: false) + }; + + return keys; + } + + public static List SetSolRefundAccountKeys( + HtlcSplRefundRequest refundRequest, + HTLCSplPdaResponse htlcPdaResponse) + { + var keys = new List() + { + AccountMeta.Writable(publicKey: refundRequest.SignerPublicKey, isSigner: true), + AccountMeta.Writable(publicKey: htlcPdaResponse.HtlcPublicKey, isSigner: false), + AccountMeta.Writable(publicKey: htlcPdaResponse.HtlcTokenAccount, isSigner: false), + AccountMeta.Writable(publicKey: refundRequest.ReceiverPublicKey, isSigner: false), + AccountMeta.ReadOnly(publicKey: refundRequest.SourceTokenPublicKey, isSigner: false), + AccountMeta.Writable(publicKey: AssociatedTokenAccountProgram.DeriveAssociatedTokenAccount(refundRequest.ReceiverPublicKey, refundRequest.SourceTokenPublicKey), isSigner: false), + AccountMeta.ReadOnly(publicKey: SystemProgram.ProgramIdKey, isSigner: false), + AccountMeta.ReadOnly(publicKey: TokenProgram.ProgramIdKey, isSigner: false), + AccountMeta.ReadOnly(publicKey: SysVars.RentKey, isSigner: false) + }; + + return keys; + } + + public static List CreateSolRedeemAccountKeys( + HTLCSolRedeemRequest htlcRedeemRequest, + HtlcSolPdaResponse htlcPdaResponse) + { + var keys = new List() + { + AccountMeta.Writable(publicKey: htlcRedeemRequest.SignerPublicKey, isSigner: true), + AccountMeta.Writable(publicKey: htlcRedeemRequest.SenderPublicKey, isSigner: false), + AccountMeta.Writable(publicKey: htlcRedeemRequest.ReceiverPublicKey, isSigner: false), + AccountMeta.Writable(publicKey: htlcPdaResponse.HtlcPublicKey, isSigner: false), + AccountMeta.ReadOnly(publicKey: SystemProgram.ProgramIdKey, isSigner: false), + AccountMeta.ReadOnly(publicKey: SysVars.RentKey, isSigner: false) + }; + + return keys; + } + + public static List SetSolRefundAccountKeys( + HtlcSolRefundRequest refundRequest, + HtlcSolPdaResponse htlcPdaResponse) + { + var keys = new List() + { + AccountMeta.Writable(publicKey: refundRequest.SignerPublicKey, isSigner: true), + AccountMeta.Writable(publicKey: htlcPdaResponse.HtlcPublicKey, isSigner: false), + AccountMeta.Writable(publicKey: refundRequest.ReceiverPublicKey, isSigner: false), + AccountMeta.ReadOnly(publicKey: SystemProgram.ProgramIdKey, isSigner: false), + AccountMeta.ReadOnly(publicKey: SysVars.RentKey, isSigner: false) + }; + + return keys; + } + + public static List CreateGetDetailsAccountKeys(PublicKey htlcPubKey) + { + var keys = new List + { + AccountMeta.ReadOnly(publicKey: htlcPubKey, isSigner: false) + }; + + return keys; + } +} diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSolLockRequest.cs b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolLockRequest.cs similarity index 92% rename from csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSolLockRequest.cs rename to csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolLockRequest.cs index 3173ec29..e06e2a78 100644 --- a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSolLockRequest.cs +++ b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolLockRequest.cs @@ -1,7 +1,7 @@ using Solnet.Wallet; using System.Numerics; -namespace Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; +namespace Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; public class HTLCSolLockRequest { diff --git a/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolPdaResponse.cs b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolPdaResponse.cs new file mode 100644 index 00000000..2b4e98f8 --- /dev/null +++ b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolPdaResponse.cs @@ -0,0 +1,8 @@ +using Solnet.Wallet; + +namespace Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; + +public class HtlcSolPdaResponse +{ + public required PublicKey HtlcPublicKey { get; set; } = null!; +} diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCRedeemRequest.cs b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolRedeemRequest.cs similarity index 57% rename from csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCRedeemRequest.cs rename to csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolRedeemRequest.cs index e3b5707c..82292e44 100644 --- a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCRedeemRequest.cs +++ b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolRedeemRequest.cs @@ -1,20 +1,16 @@ using Solnet.Wallet; -namespace Train.Solver.Blockchain.Solana.Programs.HTLCProgram.Models; +namespace Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; -public class HTLCRedeemRequest +public class HTLCSolRedeemRequest { public byte[] Id { get; set; } = null!; public byte[] Secret { get; set; } = null!; - public PublicKey SourceTokenPublicKey { get; set; } = null!; - public PublicKey ReceiverPublicKey { get; set; } = null!; public PublicKey SignerPublicKey { get; set; } = null!; public PublicKey SenderPublicKey { get; set; } = null!; - - public PublicKey RewardPublicKey { get; set; } = null!; } diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCRefundRequest.cs b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolRefundRequest.cs similarity index 54% rename from csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCRefundRequest.cs rename to csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolRefundRequest.cs index dd650dbf..d7cb1279 100644 --- a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCRefundRequest.cs +++ b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolRefundRequest.cs @@ -1,14 +1,12 @@ using Solnet.Wallet; -namespace Train.Solver.Blockchain.Solana.Programs.HTLCProgram.Models; +namespace Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; -public class HTLCRefundRequest +public class HtlcSolRefundRequest { public byte[] Id { get; set; } = null!; public PublicKey SignerPublicKey { get; set; } = null!; public PublicKey ReceiverPublicKey { get; set; } = null!; - - public PublicKey SourceTokenPublicKey { get; set; } = null!; } diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/HTLCProgram.cs b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/HTLCSplProgram.cs similarity index 56% rename from csharp/src/Workflow.Solana/Programs/HTLCProgram/HTLCProgram.cs rename to csharp/src/Workflow.Solana/Programs/HtlcSplProgram/HTLCSplProgram.cs index 58dafed8..7a5e6726 100644 --- a/csharp/src/Workflow.Solana/Programs/HTLCProgram/HTLCProgram.cs +++ b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/HTLCSplProgram.cs @@ -5,26 +5,28 @@ using Train.Solver.Blockchain.Solana.Programs.HTLCProgram; using Train.Solver.Blockchain.Solana.Programs.HTLCProgram.Models; using Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; +using Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; +using Train.Solver.Workflow.Solana.Programs.HtlcSplProgram.Models; namespace Train.Solver.Workflow.Solana.Programs.HTLCProgram; -public static class HTLCProgram +public static class HTLCSplProgram { public static TransactionBuilder SetSplLockTransactionInstruction( this TransactionBuilder builder, PublicKey htlcProgramIdKey, HTLCSplLockRequest htlcLockRequest) { - var htlcPdaParams = GetSPLHtlcPdaParams(htlcLockRequest.Id, htlcProgramIdKey); + var htlcPdaParams = GetSplHtlcPdaParams(htlcLockRequest.Id, htlcProgramIdKey); - var lockData = new HtlcInstructionDataBuilder().CreateLockData(htlcLockRequest); - var lockRewardData = new HtlcInstructionDataBuilder().CreateLockRewardData(htlcLockRequest); + var lockData = new HtlcSplInstructionDataBuilder().CreateSplLockData(htlcLockRequest); + var lockRewardData = new HtlcSplInstructionDataBuilder().CreateLockRewardData(htlcLockRequest); // Add Lock instruction builder.AddInstruction(new() { ProgramId = htlcProgramIdKey, - Keys = HtlcInstructionKeyProvider.CreateSplLockAccountKeys(htlcLockRequest, htlcPdaParams), + Keys = HtlcSplInstructionKeyProvider.CreateSplLockAccountKeys(htlcLockRequest, htlcPdaParams), Data = lockData }); @@ -32,71 +34,45 @@ public static TransactionBuilder SetSplLockTransactionInstruction( builder.AddInstruction(new() { ProgramId = htlcProgramIdKey, - Keys = HtlcInstructionKeyProvider.CreateLockRewardAccountKeys(htlcLockRequest, htlcPdaParams), + Keys = HtlcSplInstructionKeyProvider.CreateLockRewardAccountKeys(htlcLockRequest, htlcPdaParams), Data = lockRewardData }); return builder; } - public static TransactionBuilder SetSolLockTransactionInstruction( + public static TransactionBuilder SetSplRedeemTransactionInstruction( this TransactionBuilder builder, PublicKey htlcProgramIdKey, - HTLCSolLockRequest htlcLockRequest) + HTLCSplRedeemRequest redeemRequest) { - var pdaParams = GetSolHtlcParams(htlcLockRequest.Id, htlcProgramIdKey); + var pdaParams = GetSplHtlcPdaParams(redeemRequest.Id, htlcProgramIdKey); - var lockData = new HtlcInstructionDataBuilder().CreateLockData(htlcLockRequest); - var lockRewardData = new HtlcInstructionDataBuilder().CreateLockRewardData(htlcLockRequest); + var redeemData = new HtlcSplInstructionDataBuilder().CreateRedeemData(redeemRequest, pdaParams); builder.AddInstruction(new() { ProgramId = htlcProgramIdKey, - Keys = HtlcInstructionKeyProvider.CreateSolLockAccountKeys(htlcLockRequest, pdaParams), - Data = lockData - }); - - builder.AddInstruction(new() - { - ProgramId = htlcProgramIdKey, - Keys = HtlcInstructionKeyProvider.CreateSolLockRewardAccountKeys(htlcLockRequest, pdaParams), - Data = lockRewardData - }); - return builder; - } - - public static TransactionBuilder SetRedeemTransactionInstruction( - this TransactionBuilder builder, - PublicKey htlcProgramIdKey, - HTLCRedeemRequest redeemRequest) - { - var pdaParams = GetSPLHtlcPdaParams(redeemRequest.Id, htlcProgramIdKey); - - var redeemData = new HtlcInstructionDataBuilder().CreateRedeemData(redeemRequest, pdaParams); - - builder.AddInstruction(new() - { - ProgramId = htlcProgramIdKey, - Keys = HtlcInstructionKeyProvider.CreateRedeemAccountKeys(redeemRequest, pdaParams), + Keys = HtlcSplInstructionKeyProvider.CreateRedeemAccountKeys(redeemRequest, pdaParams), Data = redeemData }); return builder; } - - public static TransactionBuilder SetRefundTransactionInstruction( + + public static TransactionBuilder SetSplRefundTransactionInstruction( this TransactionBuilder builder, PublicKey htlcProgramIdKey, - HTLCRefundRequest htlcRefundRequest) + HtlcSplRefundRequest htlcRefundRequest) { - var pdaParams = GetSPLHtlcPdaParams(htlcRefundRequest.Id, htlcProgramIdKey); + var pdaParams = GetSplHtlcPdaParams(htlcRefundRequest.Id, htlcProgramIdKey); - var refundData = new HtlcInstructionDataBuilder().CreateRefundData(htlcRefundRequest, pdaParams); + var refundData = new HtlcSplInstructionDataBuilder().CreateSplRefundData(htlcRefundRequest, pdaParams); builder.AddInstruction(new() { ProgramId = htlcProgramIdKey, - Keys = HtlcInstructionKeyProvider.SetRefundAccountKeys(htlcRefundRequest, pdaParams), + Keys = HtlcSplInstructionKeyProvider.SetSplRefundAccountKeys(htlcRefundRequest, pdaParams), Data = refundData }); @@ -106,11 +82,11 @@ public static TransactionBuilder SetRefundTransactionInstruction( public static TransactionBuilder SetAddLockSigInstruction( this TransactionBuilder builder, PublicKey htlcProgramIdKey, - HTLCAddlocksigRequest htlcAddlocksigRequest) + HtlcAddlocksigRequest htlcAddlocksigRequest) { - var pdaParams = GetSPLHtlcPdaParams(htlcAddlocksigRequest.AddLockSigMessageRequest.Id, htlcProgramIdKey); + var pdaParams = GetSplHtlcPdaParams(htlcAddlocksigRequest.AddLockSigMessageRequest.Id, htlcProgramIdKey); var message = Ed25519Program.CreateAddLockSigMessage(htlcAddlocksigRequest.AddLockSigMessageRequest); - var addLockSigData = new HtlcInstructionDataBuilder().CreateAddLockSigData(htlcAddlocksigRequest, pdaParams); + var addLockSigData = new HtlcSplInstructionDataBuilder().CreateAddLockSigData(htlcAddlocksigRequest, pdaParams); builder.CreateEd25519Instruction( htlcAddlocksigRequest.AddLockSigMessageRequest.SignerPublicKey, @@ -120,7 +96,7 @@ public static TransactionBuilder SetAddLockSigInstruction( builder.AddInstruction(new() { ProgramId = htlcProgramIdKey, - Keys = HtlcInstructionKeyProvider.CreateAddLockSigAccountKeys(htlcAddlocksigRequest, pdaParams), + Keys = HtlcSplInstructionKeyProvider.CreateAddLockSigAccountKeys(htlcAddlocksigRequest, pdaParams), Data = addLockSigData }); @@ -132,21 +108,21 @@ public static TransactionBuilder SetGetDetailsInstruction( PublicKey htlcProgramIdKey, byte[] id) { - var pdaParams = GetSPLHtlcPdaParams(id, htlcProgramIdKey); + var pdaParams = GetSplHtlcPdaParams(id, htlcProgramIdKey); - var getDetailsData = new HtlcInstructionDataBuilder().CreateGetDetailsData(pdaParams, id); + var getDetailsData = new HtlcSplInstructionDataBuilder().CreateGetDetailsData(pdaParams, id); builder.AddInstruction(new() { ProgramId = htlcProgramIdKey, - Keys = HtlcInstructionKeyProvider.CreateGetDetailsAccountKeys(pdaParams.HtlcPublicKey), + Keys = HtlcSplInstructionKeyProvider.CreateGetDetailsAccountKeys(pdaParams.HtlcPublicKey), Data = getDetailsData }); return builder; } - private static HTLCSplPdaResponse GetSPLHtlcPdaParams( + private static HTLCSplPdaResponse GetSplHtlcPdaParams( byte[] Id, PublicKey htlcProgramIdKey) { @@ -177,7 +153,7 @@ private static HTLCSplPdaResponse GetSPLHtlcPdaParams( }; } - private static HTLCSolPdaResponse GetSolHtlcParams( + private static HtlcSolPdaResponse GetSolHtlcParams( byte[] Id, PublicKey htlcProgramIdKey) { diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/HtlcInstructionDataBuilder.cs b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/HtlcSplInstructionDataBuilder.cs similarity index 72% rename from csharp/src/Workflow.Solana/Programs/HTLCProgram/HtlcInstructionDataBuilder.cs rename to csharp/src/Workflow.Solana/Programs/HtlcSplProgram/HtlcSplInstructionDataBuilder.cs index 4e93a791..a8618d4e 100644 --- a/csharp/src/Workflow.Solana/Programs/HTLCProgram/HtlcInstructionDataBuilder.cs +++ b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/HtlcSplInstructionDataBuilder.cs @@ -5,10 +5,12 @@ using Train.Solver.Blockchain.Solana.Helpers; using Train.Solver.Blockchain.Solana.Programs.HTLCProgram.Models; using Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; +using Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; +using Train.Solver.Workflow.Solana.Programs.HtlcSplProgram.Models; namespace Train.Solver.Blockchain.Solana.Programs.HTLCProgram; -public class HtlcInstructionDataBuilder +public class HtlcSplInstructionDataBuilder { private List Fields { get; set; } = new(); @@ -22,7 +24,7 @@ private void SetFieldData(string property, int span, Action }); } - public byte[] CreateLockData( + public byte[] CreateSplLockData( HTLCSolLockRequest lockRequest) { var destinationAssetData = Encoding.UTF8.GetBytes(lockRequest.DestinationAsset); @@ -58,6 +60,42 @@ public byte[] CreateLockData( FieldEncoder.Sighash(SolanaConstants.LockSighash)); } + public byte[] CreateSolLockData( + HTLCSolLockRequest lockRequest) + { + var destinationAssetData = Encoding.UTF8.GetBytes(lockRequest.DestinationAsset); + var destionationAddressData = Encoding.UTF8.GetBytes(lockRequest.DestinationAddress); + var destinationNetworkData = Encoding.UTF8.GetBytes(lockRequest.DestinationNetwork); + var sourceAssetData = Encoding.UTF8.GetBytes(lockRequest.SourceAsset); + + SetFieldData("id", lockRequest.Id.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); + SetFieldData("hashlock", lockRequest.Hashlock.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); + SetFieldData("timelock", 8, (v, buf, off) => buf.WriteBigInt((BigInteger)v, off, 8, isUnsigned: true, isBigEndian: false)); + SetFieldData("amount", 8, (v, buf, off) => buf.WriteBigInt((BigInteger)v, off, 8, isUnsigned: true, isBigEndian: false)); + SetFieldData("destinationNetwork", destinationNetworkData.Length + 4, (v, buf, off) => FieldEncoder.EncodeByteArrayWithLength((byte[])v, buf, ref off)); + SetFieldData("destinationAddress", destionationAddressData.Length + 4, (v, buf, off) => FieldEncoder.EncodeByteArrayWithLength((byte[])v, buf, ref off)); + SetFieldData("destinationAsset", destinationAssetData.Length + 4, (v, buf, off) => FieldEncoder.EncodeByteArrayWithLength((byte[])v, buf, ref off)); + SetFieldData("sourceAsset", sourceAssetData.Length + 4, (v, buf, off) => FieldEncoder.EncodeByteArrayWithLength((byte[])v, buf, ref off)); + SetFieldData("receiver", 32, (v, buf, off) => buf.WritePubKey((PublicKey)v, off)); + + var instructionExecutionOrder = new Dictionary + { + { "id", lockRequest.Id}, + { "hashlock", lockRequest.Hashlock}, + { "timelock", lockRequest.Timelock}, + { "amount", lockRequest.Amount}, + { "destinationNetwork", destinationNetworkData }, + { "destinationAddress", destionationAddressData }, + { "destinationAsset", destinationAssetData }, + { "sourceAsset", sourceAssetData }, + { "receiver", lockRequest.ReceiverPublicKey }, + }; + + return BuildInstructionData( + instructionExecutionOrder, + FieldEncoder.Sighash(SolanaConstants.LockSighash)); + } + public byte[] CreateLockRewardData( HTLCSolLockRequest lockRequest) { @@ -78,7 +116,7 @@ public byte[] CreateLockRewardData( } public byte[] CreateRedeemData( - HTLCRedeemRequest redeemRequest, + HTLCSplRedeemRequest redeemRequest, HTLCSplPdaResponse htlcPdaResponse) { SetFieldData("id", redeemRequest.Id.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); @@ -98,8 +136,8 @@ public byte[] CreateRedeemData( FieldEncoder.Sighash(SolanaConstants.RedeemSighash)); } - public byte[] CreateRefundData( - HTLCRefundRequest refundRequest, + public byte[] CreateSplRefundData( + HtlcSplRefundRequest refundRequest, HTLCSplPdaResponse htlcPdaResponse) { SetFieldData("id", refundRequest.Id.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); @@ -135,7 +173,7 @@ public byte[] CreateGetDetailsData( } public byte[] CreateAddLockSigData( - HTLCAddlocksigRequest addLockSigRequest, + HtlcAddlocksigRequest addLockSigRequest, HTLCSplPdaResponse htlcPdaResponse) { SetFieldData("id", addLockSigRequest.AddLockSigMessageRequest.Id.Length, (v, buf, off) => FieldEncoder.EncodeByteArray((byte[])v, buf, ref off)); diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/HtlcInstructionKeyProvider.cs b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/HtlcSplInstructionKeyProvider.cs similarity index 93% rename from csharp/src/Workflow.Solana/Programs/HTLCProgram/HtlcInstructionKeyProvider.cs rename to csharp/src/Workflow.Solana/Programs/HtlcSplProgram/HtlcSplInstructionKeyProvider.cs index 87c1fa31..52aecf4a 100644 --- a/csharp/src/Workflow.Solana/Programs/HTLCProgram/HtlcInstructionKeyProvider.cs +++ b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/HtlcSplInstructionKeyProvider.cs @@ -4,10 +4,12 @@ using Train.Solver.Blockchain.Solana.Helpers; using Train.Solver.Blockchain.Solana.Programs.HTLCProgram.Models; using Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; +using Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; +using Train.Solver.Workflow.Solana.Programs.HtlcSplProgram.Models; namespace Train.Solver.Blockchain.Solana.Programs.HTLCProgram; -public static class HtlcInstructionKeyProvider +public static class HtlcSplInstructionKeyProvider { public static List CreateSplLockAccountKeys( HTLCSplLockRequest htlcLockRequest, @@ -30,7 +32,7 @@ public static List CreateSplLockAccountKeys( public static List CreateSolLockAccountKeys( HTLCSolLockRequest htlcLockRequest, - HTLCSolPdaResponse htlcPdaResponse) + HtlcSolPdaResponse htlcPdaResponse) { var keys = new List() { @@ -60,7 +62,7 @@ public static IList CreateLockRewardAccountKeys(HTLCSplLockRequest return keys; } - public static IList CreateSolLockRewardAccountKeys(HTLCSolLockRequest htlcLockRequest, HTLCSolPdaResponse htlcPdaResponse) + public static IList CreateSolLockRewardAccountKeys(HTLCSolLockRequest htlcLockRequest, HtlcSolPdaResponse htlcPdaResponse) { var keys = new List() { @@ -73,8 +75,8 @@ public static IList CreateSolLockRewardAccountKeys(HTLCSolLockReque return keys; } - public static List SetRefundAccountKeys( - HTLCRefundRequest refundRequest, + public static List SetSplRefundAccountKeys( + HtlcSplRefundRequest refundRequest, HTLCSplPdaResponse htlcPdaResponse) { var keys = new List() @@ -94,7 +96,7 @@ public static List SetRefundAccountKeys( } public static List CreateRedeemAccountKeys( - HTLCRedeemRequest htlcRedeemRequest, + HTLCSplRedeemRequest htlcRedeemRequest, HTLCSplPdaResponse htlcPdaResponse) { var keys = new List() @@ -128,7 +130,7 @@ public static List CreateGetDetailsAccountKeys(PublicKey htlcPubKey } public static List CreateAddLockSigAccountKeys( - HTLCAddlocksigRequest hTLCAddlocksigRequest, + HtlcAddlocksigRequest hTLCAddlocksigRequest, HTLCSplPdaResponse htlcPdaResponse) { var keys = new List diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCAddlocksigRequest.cs b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/Models/HtlcAddlocksigRequest.cs similarity index 72% rename from csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCAddlocksigRequest.cs rename to csharp/src/Workflow.Solana/Programs/HtlcSplProgram/Models/HtlcAddlocksigRequest.cs index e654bb97..7b93bde6 100644 --- a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCAddlocksigRequest.cs +++ b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/Models/HtlcAddlocksigRequest.cs @@ -1,9 +1,9 @@ using Solnet.Wallet; using Train.Solver.Blockchain.Solana.Models; -namespace Train.Solver.Blockchain.Solana.Programs.HTLCProgram.Models; +namespace Train.Solver.Workflow.Solana.Programs.HtlcSplProgram.Models; -public class HTLCAddlocksigRequest +public class HtlcAddlocksigRequest { public SolanaAddLockSigMessageRequest AddLockSigMessageRequest { get; set; } = null!; diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSplLockRequest.cs b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/Models/HtlcSplLockRequest.cs similarity index 76% rename from csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSplLockRequest.cs rename to csharp/src/Workflow.Solana/Programs/HtlcSplProgram/Models/HtlcSplLockRequest.cs index 9927a698..3a56022d 100644 --- a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSplLockRequest.cs +++ b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/Models/HtlcSplLockRequest.cs @@ -1,4 +1,5 @@ using Solnet.Wallet; +using Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; namespace Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; diff --git a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSplPdaResponse.cs b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/Models/HtlcSplPdaResponse.cs similarity index 62% rename from csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSplPdaResponse.cs rename to csharp/src/Workflow.Solana/Programs/HtlcSplProgram/Models/HtlcSplPdaResponse.cs index 86d0c75e..ac608c5a 100644 --- a/csharp/src/Workflow.Solana/Programs/HTLCProgram/Models/HTLCSplPdaResponse.cs +++ b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/Models/HtlcSplPdaResponse.cs @@ -1,8 +1,9 @@ using Solnet.Wallet; +using Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; namespace Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; -public class HTLCSplPdaResponse : HTLCSolPdaResponse +public class HTLCSplPdaResponse : HtlcSolPdaResponse { public PublicKey HtlcTokenAccount { get; set; } = null!; diff --git a/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/Models/HtlcSplRedeemRequest.cs b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/Models/HtlcSplRedeemRequest.cs new file mode 100644 index 00000000..a7ab5b32 --- /dev/null +++ b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/Models/HtlcSplRedeemRequest.cs @@ -0,0 +1,13 @@ +using Solnet.Wallet; +using Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; + +namespace Train.Solver.Workflow.Solana.Programs.HtlcSplProgram.Models; + +public class HTLCSplRedeemRequest : HTLCSolRedeemRequest +{ + public PublicKey SourceTokenPublicKey { get; set; } = null!; + + public PublicKey RewardPublicKey { get; set; } = null!; + + public PublicKey ReceiverPublicKey { get; set; } = null!; +} diff --git a/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/Models/HtlcSplRefundRequest.cs b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/Models/HtlcSplRefundRequest.cs new file mode 100644 index 00000000..d1354eb4 --- /dev/null +++ b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/Models/HtlcSplRefundRequest.cs @@ -0,0 +1,9 @@ +using Solnet.Wallet; +using Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; + +namespace Train.Solver.Blockchain.Solana.Programs.HTLCProgram.Models; + +public class HtlcSplRefundRequest : HtlcSolRefundRequest +{ + public PublicKey SourceTokenPublicKey { get; set; } = null!; +} From 19f08f2016f150a9eeddba89ba3c9bdd67eef712 Mon Sep 17 00:00:00 2001 From: Davit Mandzikyan Date: Wed, 19 Nov 2025 02:55:16 +0400 Subject: [PATCH 3/4] Merge with dev. --- .../Models/HtlcSolLockRequest.cs | 31 ------------------- .../Models/HtlcSolPdaResponse.cs | 8 ----- 2 files changed, 39 deletions(-) delete mode 100644 csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolLockRequest.cs delete mode 100644 csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolPdaResponse.cs diff --git a/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolLockRequest.cs b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolLockRequest.cs deleted file mode 100644 index 3173ec29..00000000 --- a/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolLockRequest.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Solnet.Wallet; -using System.Numerics; - -namespace Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; - -public class HTLCSolLockRequest -{ - public required byte[] Id { get; set; } = null!; - - public required byte[] Hashlock { get; set; } = null!; - - public required BigInteger Timelock { get; set; } - - public required BigInteger Amount { get; set; } - - public required string DestinationNetwork { get; set; } = null!; - - public required string DestinationAddress { get; set; } = null!; - - public required string DestinationAsset { get; set; } = null!; - - public required string SourceAsset { get; set; } = null!; - - public required PublicKey SignerPublicKey { get; set; } = null!; - - public required PublicKey ReceiverPublicKey { get; set; } = null!; - - public required BigInteger Reward { get; set; } - - public required BigInteger RewardTimelock { get; set; } -} diff --git a/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolPdaResponse.cs b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolPdaResponse.cs deleted file mode 100644 index 2a698235..00000000 --- a/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolPdaResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Solnet.Wallet; - -namespace Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; - -public class HTLCSolPdaResponse -{ - public required PublicKey HtlcPublicKey { get; set; } = null!; -} From fc23c573eda43054fa3af6f56bf7694da116b318 Mon Sep 17 00:00:00 2001 From: Davit Mandzikyan Date: Wed, 19 Nov 2025 02:58:55 +0400 Subject: [PATCH 4/4] Add de;eted files. --- .../Models/HTLCSolLockRequest.cs | 31 +++++++++++++++++++ .../Models/HtlcSolPdaResponse.cs | 8 +++++ .../Programs/HtlcSplProgram/HTLCSplProgram.cs | 20 ------------ 3 files changed, 39 insertions(+), 20 deletions(-) create mode 100644 csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HTLCSolLockRequest.cs create mode 100644 csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolPdaResponse.cs diff --git a/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HTLCSolLockRequest.cs b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HTLCSolLockRequest.cs new file mode 100644 index 00000000..e793ad46 --- /dev/null +++ b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HTLCSolLockRequest.cs @@ -0,0 +1,31 @@ +using Solnet.Wallet; +using System.Numerics; + +namespace Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; + +public class HTLCSolLockRequest +{ + public required byte[] Id { get; set; } = null!; + + public required byte[] Hashlock { get; set; } = null!; + + public required BigInteger Timelock { get; set; } + + public required BigInteger Amount { get; set; } + + public required string DestinationNetwork { get; set; } = null!; + + public required string DestinationAddress { get; set; } = null!; + + public required string DestinationAsset { get; set; } = null!; + + public required string SourceAsset { get; set; } = null!; + + public required PublicKey SignerPublicKey { get; set; } = null!; + + public required PublicKey ReceiverPublicKey { get; set; } = null!; + + public required BigInteger Reward { get; set; } + + public required BigInteger RewardTimelock { get; set; } +} \ No newline at end of file diff --git a/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolPdaResponse.cs b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolPdaResponse.cs new file mode 100644 index 00000000..d60cd922 --- /dev/null +++ b/csharp/src/Workflow.Solana/Programs/HtlcSolProgram/Models/HtlcSolPdaResponse.cs @@ -0,0 +1,8 @@ +using Solnet.Wallet; + +namespace Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; + +public class HtlcSolPdaResponse +{ + public required PublicKey HtlcPublicKey { get; set; } = null!; +} \ No newline at end of file diff --git a/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/HTLCSplProgram.cs b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/HTLCSplProgram.cs index 7a5e6726..0813cdc0 100644 --- a/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/HTLCSplProgram.cs +++ b/csharp/src/Workflow.Solana/Programs/HtlcSplProgram/HTLCSplProgram.cs @@ -5,7 +5,6 @@ using Train.Solver.Blockchain.Solana.Programs.HTLCProgram; using Train.Solver.Blockchain.Solana.Programs.HTLCProgram.Models; using Train.Solver.Workflow.Solana.Programs.HTLCProgram.Models; -using Train.Solver.Workflow.Solana.Programs.HtlcSolProgram.Models; using Train.Solver.Workflow.Solana.Programs.HtlcSplProgram.Models; namespace Train.Solver.Workflow.Solana.Programs.HTLCProgram; @@ -152,23 +151,4 @@ private static HTLCSplPdaResponse GetSplHtlcPdaParams( HtlcBump = htlcBump }; } - - private static HtlcSolPdaResponse GetSolHtlcParams( - byte[] Id, - PublicKey htlcProgramIdKey) - { - var htlc = PublicKey.TryFindProgramAddress( - new List() - { - Id - }, - htlcProgramIdKey, - out PublicKey htlcPubKey, - out _); - - return new() - { - HtlcPublicKey = htlcPubKey - }; - } }