public async Task <int> GetCounter(Atomex.Tezos tezos, string address) { var rpc = new Rpc(tezos.RpcNodeUri); var head = await rpc .GetHeader() .ConfigureAwait(false); return(await GetCounter(tezos, address, head) .ConfigureAwait(false)); }
public TzktApi(Atomex.Tezos currency) { _currency = currency; _baseUri = currency.BaseUri; _rpcNodeUri = currency.RpcNodeUri; _headers = new HttpRequestHeaders { new KeyValuePair <string, IEnumerable <string> >("User-Agent", new string[] { "Atomex" }) }; }
public async Task <int> GetCounter( Atomex.Tezos tezos, string address, JObject head, bool ignoreCache = false) { var rpc = new Rpc(tezos.RpcNodeUri); var account = await rpc .GetAccountForBlock(head["hash"].ToString(), address) .ConfigureAwait(false); var counter = int.Parse(account["counter"].ToString()); lock (_syncRoot) { if (_counters.TryGetValue(address, out var offlineCounter)) { if (!ignoreCache && offlineCounter.Value > counter && DateTime.UtcNow - offlineCounter.LastUpdatedTimeUtc <= ExpirationTimeOut) { return(++offlineCounter.Value); } else { //++counter; _counters[address] = new CounterEntry { Value = ignoreCache ? counter : counter + 1, LastUpdatedTimeUtc = DateTime.UtcNow }; return(++counter); } } else { //++counter; _counters.Add(address, new CounterEntry { Value = ignoreCache ? counter : counter + 1, LastUpdatedTimeUtc = DateTime.UtcNow }); return(++counter); } } }
public static Task StartSwapRedeemedControlAsync( Swap swap, Currency currency, Atomex.Tezos tezos, DateTime refundTimeUtc, TimeSpan interval, bool cancelOnlyIfRefundTimeReached = true, Action <Swap, byte[], CancellationToken> redeemedHandler = null, Action <Swap, DateTime, CancellationToken> canceledHandler = null, CancellationToken cancellationToken = default) { return(Task.Run(async() => { while (!cancellationToken.IsCancellationRequested) { var isRedeemedResult = await IsRedeemedAsync( swap: swap, currency: currency, tezos: tezos, cancellationToken: cancellationToken) .ConfigureAwait(false); if (isRedeemedResult.HasError && isRedeemedResult.Error.Code != Errors.RequestError) // has error { canceledHandler?.Invoke(swap, refundTimeUtc, cancellationToken); break; } else if (!isRedeemedResult.HasError && isRedeemedResult.Value != null) // has secret { redeemedHandler?.Invoke(swap, isRedeemedResult.Value, cancellationToken); break; } if (!cancelOnlyIfRefundTimeReached || DateTime.UtcNow >= refundTimeUtc) { canceledHandler?.Invoke(swap, refundTimeUtc, cancellationToken); break; } await Task.Delay(interval, cancellationToken) .ConfigureAwait(false); } }, cancellationToken)); }
public static Task StartSwapInitiatedControlAsync( Swap swap, Currency currency, Atomex.Tezos tezos, long refundTimeStamp, TimeSpan interval, Action <Swap, CancellationToken> initiatedHandler = null, Action <Swap, CancellationToken> canceledHandler = null, CancellationToken cancellationToken = default) { return(Task.Run(async() => { while (!cancellationToken.IsCancellationRequested) { if (swap.IsCanceled) { canceledHandler?.Invoke(swap, cancellationToken); break; } var isInitiatedResult = await IsInitiatedAsync( swap: swap, currency: currency, tezos: tezos, refundTimeStamp: refundTimeStamp, cancellationToken: cancellationToken) .ConfigureAwait(false); if (isInitiatedResult.HasError && isInitiatedResult.Error.Code != Errors.RequestError) { canceledHandler?.Invoke(swap, cancellationToken); break; } else if (!isInitiatedResult.HasError && isInitiatedResult.Value) { initiatedHandler?.Invoke(swap, cancellationToken); break; } await Task.Delay(interval, cancellationToken) .ConfigureAwait(false); } }, cancellationToken)); }
public static ICurrencySwap Create( Currency currency, IAccount account, ISwapClient swapClient) { return(currency switch { BitcoinBasedCurrency _ => (ICurrencySwap) new BitcoinBasedSwap( account: account.GetCurrencyAccount <BitcoinBasedAccount>(currency.Name), swapClient: swapClient, currencies: account.Currencies), ERC20 _ => (ICurrencySwap) new ERC20Swap( account: account.GetCurrencyAccount <ERC20Account>(currency.Name), ethereumAccount: account.GetCurrencyAccount <EthereumAccount>("ETH"), swapClient: swapClient, currencies: account.Currencies), Atomex.Ethereum _ => (ICurrencySwap) new EthereumSwap( account: account.GetCurrencyAccount <EthereumAccount>(currency.Name), swapClient: swapClient, currencies: account.Currencies), NYX _ => (ICurrencySwap) new NYXSwap( account: account.GetCurrencyAccount <NYXAccount>(currency.Name), tezosAccount: account.GetCurrencyAccount <TezosAccount>("XTZ"), swapClient: swapClient, currencies: account.Currencies), FA2 _ => (ICurrencySwap) new FA2Swap( account: account.GetCurrencyAccount <FA2Account>(currency.Name), tezosAccount: account.GetCurrencyAccount <TezosAccount>("XTZ"), swapClient: swapClient, currencies: account.Currencies), FA12 _ => (ICurrencySwap) new FA12Swap( account: account.GetCurrencyAccount <FA12Account>(currency.Name), tezosAccount: account.GetCurrencyAccount <TezosAccount>("XTZ"), swapClient: swapClient, currencies: account.Currencies), Atomex.Tezos _ => (ICurrencySwap) new TezosSwap( account: account.GetCurrencyAccount <TezosAccount>(currency.Name), swapClient: swapClient, currencies: account.Currencies), _ => throw new NotSupportedException($"Not supported currency {currency.Name}"), });
public static async Task <Result <bool> > IsRefundedAsync( Swap swap, Currency currency, Atomex.Tezos tezos, int attempts, int attemptIntervalInSec, CancellationToken cancellationToken = default) { var attempt = 0; while (!cancellationToken.IsCancellationRequested && attempt < attempts) { ++attempt; var isRefundedResult = await IsRefundedAsync( swap : swap, currency : currency, tezos : tezos, cancellationToken : cancellationToken) .ConfigureAwait(false); if (isRefundedResult.HasError && isRefundedResult.Error.Code != Errors.RequestError) // has error { return(isRefundedResult); } if (!isRefundedResult.HasError) { return(isRefundedResult); } await Task.Delay(TimeSpan.FromSeconds(attemptIntervalInSec), cancellationToken) .ConfigureAwait(false); } return(new Error(Errors.MaxAttemptsCountReached, "Max attempts count reached for refund check")); }
public TezosRevealChecker(Atomex.Tezos tezos) { _tezos = tezos; _addresses = new Dictionary <string, TezosAddressInfo>(); }
public static async Task <Result <bool> > IsRefundedAsync( Swap swap, Currency currency, Atomex.Tezos tezos, CancellationToken cancellationToken = default) { try { Log.Debug("Tezos FA2: check refund event"); var fa2 = (TezosTokens.FA2)currency; var contractAddress = fa2.SwapContractAddress; var blockchainApi = (ITezosBlockchainApi)tezos.BlockchainApi; var txsResult = await blockchainApi .TryGetTransactionsAsync(contractAddress, cancellationToken : cancellationToken) .ConfigureAwait(false); if (txsResult == null) { return(new Error(Errors.RequestError, $"Connection error while getting {contractAddress} transactions")); } if (txsResult.HasError) { Log.Error("Error while get transactions from contract {@contract}. Code: {@code}. Description: {@desc}", contractAddress, txsResult.Error.Code, txsResult.Error.Description); return(txsResult.Error); } var txs = txsResult.Value ?.Cast <TezosTransaction>() .ToList(); if (txs != null) { foreach (var tx in txs) { if (tx.To == contractAddress && IsSwapRefund(tx, swap.SecretHash)) { return(true); } if (tx.BlockInfo?.BlockTime == null) { continue; } var blockTimeUtc = tx.BlockInfo.BlockTime.Value.ToUniversalTime(); var swapTimeUtc = swap.TimeStamp.ToUniversalTime(); if (blockTimeUtc < swapTimeUtc) { break; } } } } catch (Exception e) { Log.Error(e, "Tezos FA2 refund control task error"); return(new Error(Errors.InternalError, e.Message)); } return(false); }
public async Task <bool> AutoFillOperations(Atomex.Tezos tezos, JObject head, JArray operations, bool defaultFee = true) { var runResults = await RunOperations(head, operations) .ConfigureAwait(false); foreach (var result in runResults.SelectToken("contents")) { decimal gas = 0, storage_diff = 0, size = 0, fee = 0; var metaData = result["metadata"]; var operationResult = metaData?["operation_result"]; if (operationResult?["status"]?.ToString() == "applied") { try { gas = tezos.GasReserve + operationResult?["consumed_gas"]?.Value <decimal>() ?? 0; gas += metaData ?.SelectToken("internal_operation_results") ?.Sum(res => res["result"]?["consumed_gas"]?.Value <decimal>() ?? 0) ?? 0; //storage = operationResult?["storage_size"]?.Value<decimal>() ?? 0; storage_diff = operationResult?["paid_storage_size_diff"]?.Value <decimal>() ?? 0; storage_diff += tezos.ActivationStorage * (operationResult?["allocated_destination_contract"]?.ToString() == "True" ? 1 : 0); storage_diff += tezos.ActivationStorage * metaData?["internal_operation_results"] ?.Where(res => res["result"]?["allocated_destination_contract"]?.ToString() == "True") .Count() ?? 0; storage_diff += metaData ?.SelectToken("internal_operation_results") ?.Sum(res => res["result"]?["paid_storage_size_diff"]?.Value <decimal>() ?? 0) ?? 0; var op = operations .Children <JObject>() .FirstOrDefault(o => o["counter"] != null && o["counter"].ToString() == result["counter"].ToString()); op["gas_limit"] = gas.ToString(); op["storage_limit"] = storage_diff.ToString(); JToken forgedOpLocal = Forge.ForgeOperationsLocal(null, op); ///Checking for local and node forging results equality //JToken forgedOp = await ForgeOperations(head, op); //if (forgedOpLocal.ToString() != forgedOp.ToString().Substring((int)tezos.HeadSizeInBytes * 2)) // Log.Error("Local operation forge result differs from remote"); //process the error size = forgedOpLocal.ToString().Length / 2 + Math.Ceiling((tezos.HeadSizeInBytes + tezos.SigSizeInBytes) / operations.Count); fee = tezos.MinimalFee + tezos.MinimalNanotezPerByte * size + (long)Math.Ceiling(tezos.MinimalNanotezPerGasUnit * gas) + 1; if (defaultFee) { op["fee"] = fee.ToString(); } } catch (Exception ex) { Log.Error("Transaction autofilling error: " + ex.Message.ToString()); //process the error return(false); } } else { Log.Error("Transaction running failed: "); //process the error Log.Error(result.ToString()); return(false); } } return(true); }
public static async Task <Result <byte[]> > IsRedeemedAsync( Swap swap, Currency currency, Atomex.Tezos tezos, CancellationToken cancellationToken = default) { try { Log.Debug("Tezos NYX: check redeem event"); var nyx = (TezosTokens.NYX)currency; var contractAddress = nyx.SwapContractAddress; var blockchainApi = (ITezosBlockchainApi)tezos.BlockchainApi; var txsResult = await blockchainApi .TryGetTransactionsAsync(contractAddress, cancellationToken : cancellationToken) .ConfigureAwait(false); if (txsResult == null) { return(new Error(Errors.RequestError, $"Connection error while getting txs from contract {contractAddress}")); } if (txsResult.HasError) { Log.Error("Error while get transactions from contract {@contract}. Code: {@code}. Description: {@desc}", contractAddress, txsResult.Error.Code, txsResult.Error.Description); return(txsResult.Error); } var txs = txsResult.Value ?.Cast <TezosTransaction>() .ToList(); if (txs != null) { foreach (var tx in txs) { if (tx.To == contractAddress && IsSwapRedeem(tx, swap.SecretHash)) { // redeem! var secret = GetSecret(tx); Log.Debug("Redeem event received with secret {@secret}", Convert.ToBase64String(secret)); return(secret); } if (tx.BlockInfo?.BlockTime == null) { continue; } var blockTimeUtc = tx.BlockInfo.BlockTime.Value.ToUniversalTime(); var swapTimeUtc = swap.TimeStamp.ToUniversalTime(); if (blockTimeUtc < swapTimeUtc) { break; } } } } catch (Exception e) { Log.Error(e, "Tezos NYX redeem control task error"); return(new Error(Errors.InternalError, e.Message)); } return((byte[])null); }
public static async Task <Result <bool> > IsInitiatedAsync( Swap swap, Currency currency, Atomex.Tezos tezos, long refundTimeStamp, CancellationToken cancellationToken = default) { try { Log.Debug("Tezos FA12: check initiated event"); var fa12 = (TezosTokens.FA12)currency; var side = swap.Symbol .OrderSideForBuyCurrency(swap.PurchasedCurrency) .Opposite(); var requiredAmountInTokenDigits = AmountHelper .QtyToAmount(side, swap.Qty, swap.Price, fa12.DigitsMultiplier) .ToTokenDigits(fa12.DigitsMultiplier); var requiredRewardForRedeemInTokenDigits = swap.IsAcceptor ? swap.RewardForRedeem.ToTokenDigits(fa12.DigitsMultiplier) : 0; var contractAddress = fa12.SwapContractAddress; var detectedAmountInTokenDigits = 0m; var detectedRedeemFeeAmountInTokenDigits = 0m; long detectedRefundTimestamp = 0; var blockchainApi = (ITezosBlockchainApi)tezos.BlockchainApi; var txsResult = await blockchainApi .TryGetTransactionsAsync(contractAddress, cancellationToken : cancellationToken) .ConfigureAwait(false); if (txsResult == null) { return(new Error(Errors.RequestError, $"Connection error while getting txs from contract {contractAddress}")); } if (txsResult.HasError) { Log.Error("Error while get transactions from contract {@contract}. Code: {@code}. Description: {@desc}", contractAddress, txsResult.Error.Code, txsResult.Error.Description); return(txsResult.Error); } var txs = txsResult.Value ?.Cast <TezosTransaction>() .ToList(); if (txs == null || !txs.Any()) { return(false); } foreach (var tx in txs) { if (tx.IsConfirmed && tx.To == contractAddress) { var detectedPayment = false; if (IsSwapInit(tx, fa12.TokenContractAddress, swap.SecretHash, swap.ToAddress)) { // init payment to secret hash! detectedPayment = true; detectedAmountInTokenDigits += GetAmount(tx); detectedRedeemFeeAmountInTokenDigits = GetRedeemFee(tx); detectedRefundTimestamp = GetRefundTimestamp(tx); } ///not implemented //else if (IsSwapAdd(tx, swap.SecretHash)) //{ // detectedPayment = true; // detectedAmountInMtz += tx.Amount; //} if (detectedPayment && detectedAmountInTokenDigits >= requiredAmountInTokenDigits) { if (swap.IsAcceptor && detectedRedeemFeeAmountInTokenDigits != requiredRewardForRedeemInTokenDigits) { Log.Debug( "Invalid redeem fee in initiated event. Expected value is {@expected}, actual is {@actual}", requiredRewardForRedeemInTokenDigits, detectedRedeemFeeAmountInTokenDigits); return(new Error( code: Errors.InvalidRewardForRedeem, description: $"Invalid redeem fee in initiated event. Expected value is {requiredRewardForRedeemInTokenDigits}, actual is {detectedRedeemFeeAmountInTokenDigits}")); } if (detectedRefundTimestamp != refundTimeStamp) { Log.Debug( "Invalid refund timestamp in initiated event. Expected value is {@expected}, actual is {@actual}", refundTimeStamp, detectedRefundTimestamp); return(new Error( code: Errors.InvalidRewardForRedeem, description: $"Invalid refund timestamp in initiated event. Expected value is {refundTimeStamp}, actual is {detectedRefundTimestamp}")); } return(true); // todo: check also token contract transfers } } if (tx.BlockInfo?.BlockTime == null) { continue; } var blockTimeUtc = tx.BlockInfo.BlockTime.Value.ToUniversalTime(); var swapTimeUtc = swap.TimeStamp.ToUniversalTime(); if (blockTimeUtc < swapTimeUtc) { return(false); } } } catch (Exception e) { Log.Error(e, "Tezos token swap initiated control task error"); return(new Error(Errors.InternalError, e.Message)); } return(false); }