From 5221965bf1a7175c8c8af4609fcbf22e19cd45b9 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 22 Jun 2022 19:21:16 +0100 Subject: [PATCH] Added a web3.js like confirmation method. (#400) * Added a web3.js like confirmation method. * Version bump. --- SharedBuildProperties.props | 2 +- src/Solnet.Programs/Abstract/BaseClient.cs | 13 +++ src/Solnet.Rpc/TransactionUtils.cs | 95 ++++++++++++++++++++++ 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/Solnet.Rpc/TransactionUtils.cs diff --git a/SharedBuildProperties.props b/SharedBuildProperties.props index 9c82ce66..b69d599a 100644 --- a/SharedBuildProperties.props +++ b/SharedBuildProperties.props @@ -2,7 +2,7 @@ xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> Solnet - 6.0.12 + 6.0.13 Copyright 2022 © Solnet blockmountain blockmountain diff --git a/src/Solnet.Programs/Abstract/BaseClient.cs b/src/Solnet.Programs/Abstract/BaseClient.cs index c670b98e..cd1b9828 100644 --- a/src/Solnet.Programs/Abstract/BaseClient.cs +++ b/src/Solnet.Programs/Abstract/BaseClient.cs @@ -166,5 +166,18 @@ protected async Task SubscribeAccount(string accountAddres return res; } + + + /// + /// Confirms a transaction - same method as web3.js. + /// + /// The hash of the transaction. + /// The last valid block height of the blockhash used in the transaction. + /// The state commitment to consider when querying the ledger state. + /// Returns null if the transaction wasn't confirmed, otherwise returns the confirmation slot and possible transaction error. + public async Task> ConfirmTransaction(string hash, ulong validBlockHeight, Commitment commitment = Commitment.Finalized) + { + return await TransactionConfirmationUtils.ConfirmTransaction(this.RpcClient, this.StreamingRpcClient, hash, validBlockHeight, commitment); + } } } \ No newline at end of file diff --git a/src/Solnet.Rpc/TransactionUtils.cs b/src/Solnet.Rpc/TransactionUtils.cs new file mode 100644 index 00000000..55fa1f64 --- /dev/null +++ b/src/Solnet.Rpc/TransactionUtils.cs @@ -0,0 +1,95 @@ +using Solnet.Rpc.Messages; +using Solnet.Rpc.Models; +using Solnet.Rpc.Types; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Solnet.Rpc +{ + /// + /// Utilities to check transaction confirmation status. + /// + public static class TransactionConfirmationUtils + { + /// + /// Confirms a transaction - same method as web3.js. + /// + /// The rpc client instance. + /// The streaming rpc client instance. + /// The hash of the transaction. + /// The last valid block height of the blockhash used in the transaction. + /// The state commitment to consider when querying the ledger state. + /// Returns null if the transaction wasn't confirmed, otherwise returns the confirmation slot and possible transaction error. + public static async Task> ConfirmTransaction(IRpcClient rpc, IStreamingRpcClient streamingRpcClient, + string hash, ulong validBlockHeight, Commitment commitment = Commitment.Finalized) + { + TaskCompletionSource t = new(); + ResponseValue result = null; + + var s = await streamingRpcClient.SubscribeSignatureAsync(hash, (s, e) => + { + result = e; + t.SetResult(); + }, + commitment); + + var checkTask = Task.Run(async () => + { + var currHeight = await rpc.GetBlockHeightAsync(commitment); + while (currHeight.Result < validBlockHeight) + { + await Task.Delay(1000); + currHeight = await rpc.GetBlockHeightAsync(commitment); + } + }); + + + Task.WaitAny(t.Task, checkTask); + + if (!t.Task.IsCompleted) + { + await s.UnsubscribeAsync(); + } + + return result; + } + + /// + /// Confirms a transaction - old web3.js using constant timeout based on commitment parameter. + /// + /// The rpc client instance. + /// The streaming rpc client instance. + /// The hash of the transaction. + /// The state commitment to consider when querying the ledger state. + /// Returns null if the transaction wasn't confirmed, otherwise returns the confirmation slot and possible transaction error. + public static async Task> ConfirmTransaction(IRpcClient rpc, IStreamingRpcClient streamingRpcClient, + string hash, Commitment commitment = Commitment.Finalized) + { + TaskCompletionSource t = new(); + ResponseValue result = null; + + var s = await streamingRpcClient.SubscribeSignatureAsync(hash, (s, e) => + { + result = e; + t.SetResult(); + }, + commitment); + + var timeout = commitment == Commitment.Finalized ? TimeSpan.FromSeconds(60) : TimeSpan.FromSeconds(30); + var delay = Task.Delay(timeout); + + Task.WaitAny(t.Task, delay); + + if (!t.Task.IsCompleted) + { + await s.UnsubscribeAsync(); + } + + return result; + } + + } +}