private async Task HandleAcceptAsync(ClientSwap swap, ClientSwap clientSwap) { // check party requisites if (clientSwap.PartyAddress == null) { throw new InternalException( code: Errors.InvalidWallets, description: $"Incorrect party address for swap {swap.Id}"); } //if (IsCriminal(clientSwap.PartyAddress)) // throw new InternalException( // code: Errors.IsCriminalWallet, // description: $"Party wallet is criminal for swap {swap.Id}"); if (clientSwap.RewardForRedeem < 0) { throw new InternalException( code: Errors.InvalidRewardForRedeem, description: $"Incorrect reward for redeem for swap {swap.Id}"); } swap.PartyAddress = clientSwap.PartyAddress; swap.PartyRewardForRedeem = clientSwap.PartyRewardForRedeem; RaiseSwapUpdated(swap, SwapStateFlags.Empty); // broadcast initiator payment await GetCurrencySwap(swap.SoldCurrency) .BroadcastPaymentAsync(swap) .ConfigureAwait(false); await GetCurrencySwap(swap.PurchasedCurrency) .PrepareToReceiveAsync(swap) .ConfigureAwait(false); }
private JObject InitParams( ClientSwap swap, long refundTimestamp, long redeemFeeAmount) { return(JObject.Parse(@"{'prim':'Left','args':[{'prim':'Left','args':[{'prim':'Pair','args':[{'string':'" + swap.PartyAddress + "'},{'prim':'Pair','args':[{'prim':'Pair','args':[{'bytes':'" + swap.SecretHash.ToHexString() + "'},{'int':'" + refundTimestamp + "'}]},{'int':'" + redeemFeeAmount + "'}]}]}]}]}")); }
private async Task HandleExistingSwapAsync(ClientSwap swap, ClientSwap clientSwap) { try { if (IsInitiate(swap, clientSwap) && swap.IsAcceptor) { // handle initiate await HandleInitiateAsync(swap, clientSwap) .ConfigureAwait(false); } else if (IsAccept(swap, clientSwap) && swap.IsInitiator) { // handle accept await HandleAcceptAsync(swap, clientSwap) .ConfigureAwait(false); } else if (IsPartyPayment(swap, clientSwap)) { // party payment await GetCurrencySwap(swap.PurchasedCurrency) .HandlePartyPaymentAsync(swap, clientSwap) .ConfigureAwait(false); } } catch (Exception e) { Log.Error(e, "Existing swap handle error"); } // update swap status swap.Status = clientSwap.Status; RaiseSwapUpdated(swap, SwapStateFlags.Empty); }
public Task <bool> AddSwapAsync(ClientSwap swap) { if (!_swapById.TryAdd(swap.Id, swap)) { return(Task.FromResult(false)); } try { lock (_syncRoot) { using (var db = new LiteDatabase(ConnectionString, _bsonMapper)) { db.GetCollection <ClientSwap>(SwapsCollectionName) .Insert(swap); } return(Task.FromResult(true)); } } catch (Exception e) { Log.Error(e, "Swap add error"); } return(Task.FromResult(false)); }
public static SwapViewModel CreateSwapViewModel(ClientSwap swap) { var fromCurrency = CurrencyViewModelCreator.CreateViewModel( currency: swap.SoldCurrency, subscribeToUpdates: false); var toCurrency = CurrencyViewModelCreator.CreateViewModel( currency: swap.PurchasedCurrency, subscribeToUpdates: false); var fromAmount = AmountHelper.QtyToAmount(swap.Side, swap.Qty, swap.Price); var toAmount = AmountHelper.QtyToAmount(swap.Side.Opposite(), swap.Qty, swap.Price); return(new SwapViewModel { Id = swap.Id.ToString(), CompactState = CompactStateBySwap(swap), Mode = ModeBySwap(swap), Time = swap.TimeStamp, FromBrush = new SolidColorBrush(fromCurrency.AmountColor), FromAmount = fromAmount, FromAmountFormat = fromCurrency.CurrencyFormat, FromCurrencyCode = fromCurrency.CurrencyCode, ToBrush = new SolidColorBrush(toCurrency.AmountColor), ToAmount = toAmount, ToAmountFormat = toCurrency.CurrencyFormat, ToCurrencyCode = toCurrency.CurrencyCode, Price = swap.Price, PriceFormat = $"F{swap.Symbol.Quote.Digits}" }); }
private async Task GetSecretAsync(ClientSwap swap, ITxPoint spentPoint) { Log.Debug("Try to get CounterParty's payment spent output {@hash}:{@no} for swap {@swapId}", spentPoint.Hash, spentPoint.Index, swap.Id); var soldCurrency = swap.SoldCurrency; var swapInput = await((IInOutBlockchainApi)soldCurrency.BlockchainApi) .GetInputAsync(spentPoint.Hash, spentPoint.Index) .ConfigureAwait(false); var secret = swapInput.ExtractSecret(); var secretHash = CreateSwapSecretHash(secret); if (!secretHash.SequenceEqual(swap.SecretHash)) { throw new InternalException( code: Errors.InvalidSecretHash, description: "Invalid secret hash"); } swap.Secret = secret; RaiseSwapUpdated(swap, SwapStateFlags.HasSecret); }
private async Task RestoreForPurchasedCurrencyAsync(ClientSwap swap) { if (swap.RewardForRedeem > 0 && swap.StateFlags.HasFlag(SwapStateFlags.IsPaymentBroadcast)) { // may be swap already redeemed by someone else await WaitForRedeemAsync(swap) .ConfigureAwait(false); } else if (swap.StateFlags.HasFlag(SwapStateFlags.IsRedeemBroadcast) && !swap.StateFlags.HasFlag(SwapStateFlags.IsRedeemConfirmed)) { if (!(swap.RedeemTx is TezosTransaction redeemTx)) { Log.Error("Can't restore swap {@id}. Redeem tx is null", swap.Id); return; } TaskPerformer.EnqueueTask(new TransactionConfirmationCheckTask { Currency = Currency, Swap = swap, Interval = DefaultConfirmationCheckInterval, TxId = redeemTx.Id, CompleteHandler = RedeemConfirmedEventHandler }); } }
private async Task BroadcastTxAsync( ClientSwap swap, TezosTransaction tx, CancellationToken cancellationToken = default(CancellationToken)) { var txId = await Xtz.BlockchainApi .BroadcastAsync(tx, cancellationToken) .ConfigureAwait(false); if (txId == null) { throw new Exception("Transaction Id is null"); } Log.Debug("TxId {@id} for swap {@swapId}", txId, swap.Id); // account new unconfirmed transaction await Account .UpsertTransactionAsync( tx : tx, updateBalance : true, notifyIfUnconfirmed : true, notifyIfBalanceUpdated : true, cancellationToken : cancellationToken) .ConfigureAwait(false); // todo: transaction receipt status control }
private async Task <IBitcoinBasedTransaction> CreateRefundTxAsync( ClientSwap swap, IBitcoinBasedTransaction paymentTx, string refundAddress, DateTimeOffset lockTime) { Log.Debug("Create refund tx for swap {@swapId}", swap.Id); var tx = await _transactionFactory .CreateSwapRefundTxAsync( paymentTx : paymentTx, swap : swap, refundAddress : refundAddress, lockTime : lockTime) .ConfigureAwait(false); if (tx == null) { throw new InternalException( code: Errors.TransactionCreationError, description: $"Refund tx creation error for swap {swap.Id}"); } Log.Debug("Refund tx successfully created for swap {@swapId}", swap.Id); return(tx); }
public override Task PrepareToReceiveAsync(ClientSwap swap) { // initiator waits "accepted" event, acceptor waits "initiated" event var handler = swap.IsInitiator ? SwapAcceptedEventHandler : (OnTaskDelegate)SwapInitiatedEventHandler; var lockTimeSeconds = swap.IsInitiator ? DefaultAcceptorLockTimeInSeconds : DefaultInitiatorLockTimeInSeconds; var refundTimeStampUtcInSec = new DateTimeOffset(swap.TimeStamp.ToUniversalTime().AddSeconds(lockTimeSeconds)).ToUnixTimeSeconds(); TaskPerformer.EnqueueTask(new TezosSwapInitiatedControlTask { Currency = Currency, Swap = swap, Interval = TimeSpan.FromSeconds(30), RefundTimestamp = refundTimeStampUtcInSec, CompleteHandler = handler, CancelHandler = SwapCanceledEventHandler }); return(Task.CompletedTask); }
private bool IsPartyPayment(ClientSwap swap, ClientSwap clientSwap) { var isInitiatorPaymentReceived = swap.IsStatusChanged(clientSwap.Status, SwapStatus.InitiatorPaymentReceived); var isAcceptorPaymentReceived = swap.IsStatusChanged(clientSwap.Status, SwapStatus.AcceptorPaymentReceived); return(swap.IsAcceptor && isInitiatorPaymentReceived || swap.IsInitiator && isAcceptorPaymentReceived); }
public async Task <IBlockchainTransaction> CreateSwapPaymentTxTest() { var bitcoinApi = new Mock <IInOutBlockchainApi>(); bitcoinApi.Setup(a => a.GetUnspentOutputsAsync(It.IsAny <string>(), null, new CancellationToken())) .Returns(Task.FromResult(GetTestOutputs(Common.Alice.PubKey, NBitcoin.Network.TestNet))); var litecoinApi = new Mock <IInOutBlockchainApi>(); litecoinApi.Setup(a => a.GetUnspentOutputsAsync(It.IsAny <string>(), null, new CancellationToken())) .Returns(Task.FromResult(GetTestOutputs(Common.Bob.PubKey, AltNetworkSets.Litecoin.Testnet))); var tempCurrencies = new Currencies(Common.CurrenciesConfiguration.GetSection(Atomix.Core.Network.TestNet.ToString())); var bitcoin = tempCurrencies.Get <Bitcoin>(); bitcoin.BlockchainApi = bitcoinApi.Object; var litecoin = tempCurrencies.Get <Litecoin>(); litecoin.BlockchainApi = litecoinApi.Object; var aliceBtcWallet = GetWallet(bitcoin, Common.Alice.PubKey); //var aliceLtcWallet = GetWallet(litecoin, Common.Alice.PubKey); var bobBtcWallet = GetWallet(bitcoin, Common.Bob.PubKey); //var bobLtcWallet = GetWallet(litecoin, Common.Bob.PubKey); const decimal lastPrice = 0.000001m; const decimal lastQty = 10m; var swap = new ClientSwap { Symbol = new Symbol { Base = litecoin, Quote = bitcoin }, Side = Side.Buy, Price = lastPrice, Qty = lastQty }; var tx = await new BitcoinBasedSwapTransactionFactory() .CreateSwapPaymentTxAsync( currency: bitcoin, swap: swap, fromWallets: new [] { aliceBtcWallet.Address }, refundAddress: aliceBtcWallet.Address, toAddress: bobBtcWallet.Address, lockTime: DateTimeOffset.UtcNow.AddHours(1), secretHash: Common.SecretHash, secretSize: Common.Secret.Length, outputsSource: new BlockchainTxOutputSource()) .ConfigureAwait(false); Assert.NotNull(tx); return(tx); }
public override async Task PartyRedeemAsync(ClientSwap swap) { Log.Debug("Create redeem for counterParty for swap {@swapId}", swap.Id); var walletAddress = (await Account .GetUnspentAddressesAsync( currency: Currency, amount: 0, fee: Eth.RedeemGasLimit, feePrice: Eth.GasPriceInGwei, isFeePerTransaction: false, addressUsagePolicy: AddressUsagePolicy.UseOnlyOneAddress) .ConfigureAwait(false)) .FirstOrDefault(); if (walletAddress == null) { Log.Error("Insufficient balance for party redeem. Cannot find the address containing the required amount of funds."); return; } var nonce = await EthereumNonceManager.Instance .GetNonce(Eth, walletAddress.Address) .ConfigureAwait(false); var message = new RedeemFunctionMessage { FromAddress = walletAddress.Address, HashedSecret = swap.SecretHash, Secret = swap.Secret, Nonce = nonce, GasPrice = Atomix.Ethereum.GweiToWei(Eth.GasPriceInGwei), }; message.Gas = await EstimateGasAsync(message, new BigInteger(Eth.RedeemGasLimit)) .ConfigureAwait(false); var txInput = message.CreateTransactionInput(Eth.SwapContractAddress); var redeemTx = new EthereumTransaction(Eth, txInput) { Type = EthereumTransaction.OutputTransaction }; var signResult = await SignTransactionAsync(redeemTx) .ConfigureAwait(false); if (!signResult) { Log.Error("Transaction signing error"); return; } await BroadcastTxAsync(swap, redeemTx) .ConfigureAwait(false); }
public static ClientSwap ResolveSymbol(this ClientSwap swap, ISymbols symbols) { swap.Symbol = symbols.FirstOrDefault(s => s.Name == swap.Symbol?.Name); if (swap.Symbol == null) { throw new Exception("Symbol resolving error"); } return(swap); }
public override async Task HandlePartyPaymentAsync(ClientSwap swap, ClientSwap clientSwap) { Log.Debug("Handle party's payment txId for swap {@swapId}", swap.Id); if (swap.PartyPaymentTxId != null) { throw new InternalException( code: Errors.WrongSwapMessageOrder, description: $"Party's payment txId already received for swap {swap.Id}"); } if (clientSwap.PartyPaymentTxId == null) { throw new InternalException( code: Errors.InvalidPaymentTxId, description: "TxId is null"); } swap.PartyPaymentTxId = clientSwap.PartyPaymentTxId; RaiseSwapUpdated(swap, SwapStateFlags.Empty); // get party payment tx from block-chain var currency = (BitcoinBasedCurrency)swap.PurchasedCurrency; var tx = await GetPaymentTxAsync(currency, swap.PartyPaymentTxId) .ConfigureAwait(false); var refundLockTime = swap.IsInitiator ? DefaultAcceptorLockTimeInSeconds : DefaultInitiatorLockTimeInSeconds; if (!BitcoinBasedTransactionVerifier.TryVerifyPartyPaymentTx( tx: tx, swap: swap, secretHash: swap.SecretHash, refundLockTime: refundLockTime, error: out var error)) { throw new InternalException(error); } swap.PartyPaymentTx = tx; RaiseSwapUpdated(swap, SwapStateFlags.HasPartyPayment); // track initiator payment confirmation TaskPerformer.EnqueueTask(new TransactionConfirmationCheckTask { Currency = swap.PurchasedCurrency, Swap = swap, TxId = swap.PartyPaymentTxId, Interval = DefaultConfirmationCheckInterval, CompleteHandler = PartyPaymentConfirmedEventHandler }); }
public static ClientSwap ResolveRelationshipsByName( this ClientSwap swap, IList <Symbol> symbols) { if (swap == null) { return(null); } swap.Symbol = symbols.FirstOrDefault(s => s.Name == swap.Symbol?.Name); return(swap); }
public override async Task RedeemAsync(ClientSwap swap) { var currency = swap.PurchasedCurrency; var redeemAddress = await Account .GetFreeInternalAddressAsync(currency) .ConfigureAwait(false); // create redeem tx swap.RedeemTx = await CreateRedeemTxAsync( swap : swap, paymentTx : (IBitcoinBasedTransaction)swap.PartyPaymentTx, redeemAddress : redeemAddress.Address) .ConfigureAwait(false); // sign redeem tx swap.RedeemTx = await SignRedeemTxAsync( swap : swap, redeemTx : (IBitcoinBasedTransaction)swap.RedeemTx, paymentTx : (IBitcoinBasedTransaction)swap.PartyPaymentTx, redeemAddress : redeemAddress) .ConfigureAwait(false); swap.SetRedeemSigned(); RaiseSwapUpdated(swap, SwapStateFlags.IsRedeemSigned); // broadcast redeem tx await BroadcastRedeemAsync( swap : swap, redeemTx : swap.RedeemTx) .ConfigureAwait(false); swap.SetRedeemBroadcast(); RaiseSwapUpdated(swap, SwapStateFlags.IsRedeemBroadcast); // add new unconfirmed transaction await Account .UpsertTransactionAsync( tx : swap.RedeemTx, updateBalance : true) .ConfigureAwait(false); TaskPerformer.EnqueueTask(new TransactionConfirmationCheckTask { Currency = swap.PurchasedCurrency, Swap = swap, Interval = DefaultConfirmationCheckInterval, TxId = swap.RedeemTx.Id, CompleteHandler = RedeemConfirmedEventHandler }); }
private Task RestoreForSoldCurrencyAsync(ClientSwap swap) { if (swap.StateFlags.HasFlag(SwapStateFlags.IsPaymentBroadcast)) { if (!(swap.PaymentTx is EthereumTransaction)) { Log.Error("Can't restore swap {@id}. Payment tx is null.", swap.Id); return(Task.CompletedTask); } var lockTimeInSeconds = swap.IsInitiator ? DefaultInitiatorLockTimeInSeconds : DefaultAcceptorLockTimeInSeconds; // start redeem control TaskPerformer.EnqueueTask(new EthereumRedeemControlTask { Currency = Currency, RefundTimeUtc = swap.TimeStamp.ToUniversalTime().AddSeconds(lockTimeInSeconds), Swap = swap, CompleteHandler = RedeemControlCompletedEventHandler, CancelHandler = RedeemControlCanceledEventHandler }); } else { if (DateTime.UtcNow < swap.TimeStamp.ToUniversalTime() + DefaultMaxSwapTimeout) { if (swap.IsInitiator) { // todo: initiate swap //await InitiateSwapAsync(swapState) // .ConfigureAwait(false); } else { // todo: request secret hash from server } } else { swap.Cancel(); RaiseSwapUpdated(swap, SwapStateFlags.IsCanceled); } } return(Task.CompletedTask); }
public Task <IBitcoinBasedTransaction> CreateSwapRefundTxAsync( IBitcoinBasedTransaction paymentTx, ClientSwap swap, string refundAddress, DateTimeOffset lockTime) { var currency = (BitcoinBasedCurrency)paymentTx.Currency; var orderAmount = (long)(AmountHelper.QtyToAmount(swap.Side, swap.Qty, swap.Price) * currency.DigitsMultiplier); var swapOutputs = paymentTx.Outputs .Cast <BitcoinBasedTxOutput>() .Where(o => o.Value == orderAmount && o.IsSwapPayment) .ToList(); if (swapOutputs.Count != 1) { throw new Exception("Payment tx must have only one swap payment output"); } var estimatedSigSize = EstimateSigSize(swapOutputs, forRefund: true); var txSize = currency .CreateSwapRefundTx( unspentOutputs: swapOutputs, destinationAddress: refundAddress, changeAddress: refundAddress, amount: orderAmount, fee: 0, lockTime: lockTime) .VirtualSize(); var fee = (long)(currency.FeeRate * (txSize + estimatedSigSize)); if (orderAmount - fee < 0) { throw new Exception($"Insufficient funds for fee. Available {orderAmount}, required {fee}"); } var tx = currency.CreateSwapRefundTx( unspentOutputs: swapOutputs, destinationAddress: refundAddress, changeAddress: refundAddress, amount: orderAmount - fee, fee: fee, lockTime: lockTime); return(Task.FromResult(tx)); }
private async Task BroadcastRedeemAsync(ClientSwap swap, IBlockchainTransaction redeemTx) { var currency = swap.PurchasedCurrency; var txId = await currency.BlockchainApi .BroadcastAsync(redeemTx) .ConfigureAwait(false); if (txId == null) { throw new Exception("Transaction Id is null"); } Log.Debug("Redeem tx {@txId} successfully broadcast for swap {@swapId}", txId, swap.Id); }
public override async Task BroadcastPaymentAsync(ClientSwap swap) { var lockTimeInSeconds = swap.IsInitiator ? DefaultInitiatorLockTimeInSeconds : DefaultAcceptorLockTimeInSeconds; var txs = (await CreatePaymentTxsAsync(swap, lockTimeInSeconds) .ConfigureAwait(false)) .ToList(); if (txs.Count == 0) { Log.Error("Can't create payment transactions"); return; } var signResult = await SignPaymentTxsAsync(txs) .ConfigureAwait(false); if (!signResult) { return; } swap.PaymentTx = txs.First(); swap.SetPaymentSigned(); RaiseSwapUpdated(swap, SwapStateFlags.IsPaymentSigned); foreach (var tx in txs) { await BroadcastTxAsync(swap, tx) .ConfigureAwait(false); } swap.PaymentTx = txs.First(); swap.SetPaymentBroadcast(); RaiseSwapUpdated(swap, SwapStateFlags.IsPaymentBroadcast); // start redeem control TaskPerformer.EnqueueTask(new EthereumRedeemControlTask { Currency = Currency, RefundTimeUtc = swap.TimeStamp.ToUniversalTime().AddSeconds(lockTimeInSeconds), Swap = swap, CompleteHandler = RedeemControlCompletedEventHandler, CancelHandler = RedeemControlCanceledEventHandler }); }
private static SwapCompactState CompactStateBySwap(ClientSwap swap) { if (swap.IsComplete) { return(SwapCompactState.Completed); } if (swap.IsCanceled) { return(SwapCompactState.Canceled); } return(swap.IsRefunded ? SwapCompactState.Refunded : SwapCompactState.InProgress); }
public override Task WaitForRedeemAsync(ClientSwap swap) { Log.Debug("Wait redeem for swap {@swapId}", swap.Id); // start redeem control TaskPerformer.EnqueueTask(new TezosRedeemControlTask { Currency = Currency, RefundTimeUtc = swap.TimeStamp.ToUniversalTime().AddSeconds(DefaultAcceptorLockTimeInSeconds), Swap = swap, CompleteHandler = RedeemPartyControlCompletedEventHandler, CancelHandler = RedeemPartyControlCanceledEventHandler }); return(Task.CompletedTask); }
public async Task HandleSwapAsync(ClientSwap clientSwap) { var swap = await _account .GetSwapByIdAsync(clientSwap.Id) .ConfigureAwait(false); if (swap == null) { await RunSwapAsync(clientSwap) .ConfigureAwait(false); } else { await HandleExistingSwapAsync(swap, clientSwap) .ConfigureAwait(false); } }
public override async Task PartyRedeemAsync(ClientSwap swap) { Log.Debug("Create redeem for acceptor for swap {@swapId}", swap.Id); var walletAddress = (await Account .GetUnspentAddressesAsync( currency: Currency, amount: 0, fee: Xtz.RedeemFee.ToTez() + Xtz.RedeemStorageLimit.ToTez(), feePrice: 0, isFeePerTransaction: false, addressUsagePolicy: AddressUsagePolicy.UseOnlyOneAddress) .ConfigureAwait(false)) .FirstOrDefault(); if (walletAddress == null) { Log.Error("Insufficient balance for party redeem. Cannot find the address containing the required amount of funds."); return; } var redeemTx = new TezosTransaction(Xtz) { From = walletAddress.Address, To = Xtz.SwapContractAddress, Amount = 0, Fee = Xtz.RedeemFee, GasLimit = Xtz.RedeemGasLimit, StorageLimit = Xtz.RedeemStorageLimit, Params = RedeemParams(swap), Type = TezosTransaction.OutputTransaction }; var signResult = await SignTransactionAsync(redeemTx) .ConfigureAwait(false); if (!signResult) { Log.Error("Transaction signing error"); return; } await BroadcastTxAsync(swap, redeemTx) .ConfigureAwait(false); }
public Task <IBitcoinBasedTransaction> CreateSwapRedeemTxAsync( IBitcoinBasedTransaction paymentTx, ClientSwap swap, string redeemAddress) { var currency = (BitcoinBasedCurrency)paymentTx.Currency; var orderAmount = (long)(AmountHelper.QtyToAmount(swap.Side.Opposite(), swap.Qty, swap.Price) * currency.DigitsMultiplier); var swapOutputs = paymentTx .SwapOutputs() .ToList(); if (swapOutputs.Count != 1) { throw new Exception("Payment tx must have only one swap payment output"); } var estimatedSigSize = EstimateSigSize(swapOutputs); var txSize = currency .CreateP2PkhTx( unspentOutputs: swapOutputs, destinationAddress: redeemAddress, changeAddress: redeemAddress, amount: orderAmount, fee: 0) .VirtualSize(); var fee = (long)(currency.FeeRate * (txSize + estimatedSigSize)); if (orderAmount - fee < 0) { throw new Exception($"Insufficient funds for fee. Available {orderAmount}, required {fee}"); } var tx = currency.CreateP2PkhTx( unspentOutputs: swapOutputs, destinationAddress: redeemAddress, changeAddress: redeemAddress, amount: orderAmount - fee, fee: fee); return(Task.FromResult(tx)); }
private async Task InitiateSwapAsync(ClientSwap swap) { Log.Debug("Initiate swap {@swapId}", swap.Id); if (swap.Secret == null) { var secret = _account.Wallet .GetDeterministicSecret(swap.SoldCurrency, swap.TimeStamp); swap.Secret = secret.SubArray(0, CurrencySwap.DefaultSecretSize); RaiseSwapUpdated(swap, SwapStateFlags.HasSecret); secret.Clear(); } if (swap.SecretHash == null) { swap.SecretHash = CurrencySwap.CreateSwapSecretHash(swap.Secret); RaiseSwapUpdated(swap, SwapStateFlags.HasSecretHash); } if (swap.ToAddress == null) { swap.ToAddress = (await _account .GetRedeemAddressAsync(swap.PurchasedCurrency) .ConfigureAwait(false)) .Address; RaiseSwapUpdated(swap, SwapStateFlags.Empty); } var purchasedCurrencyBalance = await _account .GetBalanceAsync(swap.PurchasedCurrency) .ConfigureAwait(false); swap.RewardForRedeem = purchasedCurrencyBalance.Available < swap.PurchasedCurrency.GetDefaultRedeemFee() && !(swap.PurchasedCurrency is BitcoinBasedCurrency) ? swap.PurchasedCurrency.GetDefaultRedeemFee() * 2 : 0; RaiseSwapUpdated(swap, SwapStateFlags.Empty); _swapClient.SwapInitiateAsync(swap); }
private async Task <IBitcoinBasedTransaction> CreatePaymentTxAsync( ClientSwap swap, string refundAddress, DateTimeOffset lockTime) { var currency = (BitcoinBasedCurrency)swap.SoldCurrency; Log.Debug("Create swap payment {@currency} tx for swap {@swapId}", currency.Name, swap.Id); var unspentAddresses = (await Account .GetUnspentAddressesAsync(currency) .ConfigureAwait(false)) .ToList() .SortList((a, b) => a.AvailableBalance().CompareTo(b.AvailableBalance())) .Select(a => a.Address); var tx = await _transactionFactory .CreateSwapPaymentTxAsync( currency : currency, swap : swap, fromWallets : unspentAddresses, refundAddress : refundAddress, toAddress : swap.PartyAddress, lockTime : lockTime, secretHash : swap.SecretHash, secretSize : DefaultSecretSize, outputsSource : new LocalTxOutputSource(Account)) .ConfigureAwait(false); if (tx == null) { throw new InternalException( code: Errors.TransactionCreationError, description: $"Payment tx creation error for swap {swap.Id}"); } Log.Debug("Payment tx successfully created for swap {@swapId}", swap.Id); return(tx); }
private async Task <IBitcoinBasedTransaction> SignPaymentTxAsync( ClientSwap swap, IBitcoinBasedTransaction paymentTx) { Log.Debug("Sign payment tx for swap {@swapId}", swap.Id); var tx = await new BitcoinBasedSwapSigner(Account) .SignPaymentTxAsync(paymentTx) .ConfigureAwait(false); if (tx == null) { throw new InternalException( code: Errors.TransactionSigningError, description: $"Payment tx signing error for swap {swap.Id}"); } Log.Debug("Payment tx successfully signed for swap {@swapId}", swap.Id); return(tx); }
public Task <bool> UpdateSwapAsync(ClientSwap swap) { try { lock (_syncRoot) { using (var db = new LiteDatabase(ConnectionString, _bsonMapper)) { var result = db.GetCollection <ClientSwap>(SwapsCollectionName) .Update(swap); return(Task.FromResult(result)); } } } catch (Exception e) { Log.Error(e, "Swap update error"); } return(Task.FromResult(false)); }