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 override async Task RedeemForPartyAsync( Swap swap, CancellationToken cancellationToken = default) { if (swap.IsInitiator) { var partyRedeemDeadline = swap.TimeStamp.ToUniversalTime().AddSeconds(DefaultAcceptorLockTimeInSeconds) - PartyRedeemTimeReserve; if (DateTime.UtcNow > partyRedeemDeadline) { Log.Error("Party redeem deadline reached for swap {@swap}", swap.Id); return; } } Log.Debug("Create redeem for counterParty for swap {@swapId}", swap.Id); var ethConfig = EthConfig; var gasPrice = await ethConfig .GetGasPriceAsync(cancellationToken) .ConfigureAwait(false); var walletAddress = await _account .GetAddressAsync(swap.FromAddress, cancellationToken) .ConfigureAwait(false); if (walletAddress == null) { Log.Error("Can't get address {@address} for redeem for party from local db", swap.FromAddress); return; } var feeInEth = ethConfig.GetFeeAmount(ethConfig.RedeemGasLimit, gasPrice); if (walletAddress.Balance < feeInEth) { Log.Error("Insufficient funds for redeem for party"); return; } using var addressLock = await EthereumAccount.AddressLocker .GetLockAsync(walletAddress.Address, cancellationToken) .ConfigureAwait(false); var nonceResult = await EthereumNonceManager.Instance .GetNonceAsync(ethConfig, walletAddress.Address, pending : true, cancellationToken) .ConfigureAwait(false); if (nonceResult.HasError) { Log.Error("Nonce getting error with code {@code} and description {@description}", nonceResult.Error.Code, nonceResult.Error.Description); return; } var message = new RedeemFunctionMessage { FromAddress = walletAddress.Address, HashedSecret = swap.SecretHash, Secret = swap.Secret, Nonce = nonceResult.Value, GasPrice = EthereumConfig.GweiToWei(gasPrice), }; message.Gas = await EstimateGasAsync(message, new BigInteger(ethConfig.RedeemGasLimit)) .ConfigureAwait(false); var txInput = message.CreateTransactionInput(ethConfig.SwapContractAddress); var redeemTx = new EthereumTransaction(ethConfig.Name, txInput) { Type = BlockchainTransactionType.Output | BlockchainTransactionType.SwapRedeem }; var signResult = await SignTransactionAsync(redeemTx, cancellationToken) .ConfigureAwait(false); if (!signResult) { Log.Error("Transaction signing error"); return; } await BroadcastTxAsync(swap, redeemTx, cancellationToken) .ConfigureAwait(false); }
public override async Task RedeemAsync( Swap swap, CancellationToken cancellationToken = default) { var ethConfig = EthConfig; var secretResult = await EthereumSwapRedeemedHelper .IsRedeemedAsync( swap : swap, currency : ethConfig, attempts : MaxRedeemCheckAttempts, attemptIntervalInSec : RedeemCheckAttemptIntervalInSec, cancellationToken : cancellationToken) .ConfigureAwait(false); if (!secretResult.HasError && secretResult.Value != null) { await RedeemConfirmedEventHandler(swap, null, cancellationToken) .ConfigureAwait(false); return; } if (swap.StateFlags.HasFlag(SwapStateFlags.IsRedeemBroadcast) && swap.RedeemTx != null && swap.RedeemTx.CreationTime != null && swap.RedeemTx.CreationTime.Value.ToUniversalTime() + TimeSpan.FromMinutes(30) > DateTime.UtcNow) { // redeem already broadcast _ = TrackTransactionConfirmationAsync( swap: swap, currency: ethConfig, dataRepository: _account.DataRepository, txId: swap.RedeemTx.Id, confirmationHandler: RedeemConfirmedEventHandler, cancellationToken: cancellationToken); return; } if (swap.IsInitiator) { var redeemDeadline = swap.TimeStamp.ToUniversalTime().AddSeconds(DefaultAcceptorLockTimeInSeconds) - RedeemTimeReserve; if (DateTime.UtcNow > redeemDeadline) { Log.Error("Redeem dedline reached for swap {@swap}", swap.Id); return; } } Log.Debug("Create redeem for swap {@swapId}", swap.Id); var gasPrice = await ethConfig .GetGasPriceAsync(cancellationToken) .ConfigureAwait(false); var walletAddress = await _account .GetAddressAsync(swap.RedeemFromAddress, cancellationToken) .ConfigureAwait(false); if (walletAddress == null) { Log.Error("Can't get address {@address} for redeem from local db", swap.RedeemFromAddress); return; } var feeInEth = ethConfig.GetFeeAmount(ethConfig.RedeemGasLimit, gasPrice); if (walletAddress.Balance < feeInEth) { Log.Error("Insufficient funds for redeem"); return; } EthereumTransaction redeemTx; try { await EthereumAccount.AddressLocker .LockAsync(walletAddress.Address, cancellationToken) .ConfigureAwait(false); var nonceResult = await EthereumNonceManager.Instance .GetNonceAsync(ethConfig, walletAddress.Address, pending : true, cancellationToken : cancellationToken) .ConfigureAwait(false); if (nonceResult.HasError) { Log.Error("Nonce getting error with code {@code} and description {@description}", nonceResult.Error.Code, nonceResult.Error.Description); return; } var message = new RedeemFunctionMessage { FromAddress = walletAddress.Address, HashedSecret = swap.SecretHash, Secret = swap.Secret, Nonce = nonceResult.Value, GasPrice = EthereumConfig.GweiToWei(gasPrice), }; message.Gas = await EstimateGasAsync(message, new BigInteger(ethConfig.RedeemGasLimit)) .ConfigureAwait(false); var txInput = message.CreateTransactionInput(ethConfig.SwapContractAddress); redeemTx = new EthereumTransaction(ethConfig.Name, txInput) { Type = BlockchainTransactionType.Output | BlockchainTransactionType.SwapRedeem }; var signResult = await SignTransactionAsync(redeemTx, cancellationToken) .ConfigureAwait(false); if (!signResult) { Log.Error("Transaction signing error"); return; } swap.RedeemTx = redeemTx; swap.StateFlags |= SwapStateFlags.IsRedeemSigned; await UpdateSwapAsync(swap, SwapStateFlags.IsRedeemSigned, cancellationToken) .ConfigureAwait(false); await BroadcastTxAsync(swap, redeemTx, cancellationToken) .ConfigureAwait(false); } catch { throw; } finally { EthereumAccount.AddressLocker.Unlock(walletAddress.Address); } swap.RedeemTx = redeemTx; swap.StateFlags |= SwapStateFlags.IsRedeemBroadcast; await UpdateSwapAsync(swap, SwapStateFlags.IsRedeemBroadcast, cancellationToken) .ConfigureAwait(false); _ = TrackTransactionConfirmationAsync( swap: swap, currency: ethConfig, dataRepository: _account.DataRepository, txId: redeemTx.Id, confirmationHandler: RedeemConfirmedEventHandler, cancellationToken: cancellationToken); }
public override async Task RedeemAsync(ClientSwap swap) { Log.Debug("Create redeem for swap {@swapId}", swap.Id); var walletAddress = (await Account.GetUnspentAddressesAsync( currency: Currency, amount: 0, // todo: account storage fee fee: Eth.RedeemGasLimit, feePrice: Eth.GasPriceInGwei, isFeePerTransaction: true, addressUsagePolicy: AddressUsagePolicy.UseOnlyOneAddress) .ConfigureAwait(false)) .FirstOrDefault(); if (walletAddress == null) { Log.Error("Insufficient funds for redeem"); 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; } swap.RedeemTx = redeemTx; swap.SetRedeemSigned(); RaiseSwapUpdated(swap, SwapStateFlags.IsRedeemSigned); await BroadcastTxAsync(swap, redeemTx) .ConfigureAwait(false); swap.RedeemTx = redeemTx; swap.SetRedeemBroadcast(); RaiseSwapUpdated(swap, SwapStateFlags.IsRedeemBroadcast); TaskPerformer.EnqueueTask(new TransactionConfirmationCheckTask { Currency = Currency, Swap = swap, Interval = DefaultConfirmationCheckInterval, TxId = redeemTx.Id, CompleteHandler = RedeemConfirmedEventHandler }); }
public override async Task RedeemForPartyAsync( Swap swap, CancellationToken cancellationToken = default) { var eth = Eth; if (swap.IsInitiator) { var partyRedeemDeadline = swap.TimeStamp.ToUniversalTime().AddSeconds(DefaultAcceptorLockTimeInSeconds) - PartyRedeemTimeReserve; if (DateTime.UtcNow > partyRedeemDeadline) { Log.Error("Party redeem dedline reached for swap {@swap}", swap.Id); return; } } Log.Debug("Create redeem for counterParty for swap {@swapId}", swap.Id); var walletAddress = (await _account .GetUnspentAddressesAsync( toAddress: null, // todo: get participant address amount: 0, fee: 0, feePrice: 0, feeUsagePolicy: FeeUsagePolicy.EstimatedFee, addressUsagePolicy: AddressUsagePolicy.UseOnlyOneAddress, transactionType: BlockchainTransactionType.SwapRedeem, cancellationToken: cancellationToken) .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 nonceResult = await EthereumNonceManager.Instance .GetNonceAsync(eth, walletAddress.Address, cancellationToken) .ConfigureAwait(false); if (nonceResult.HasError) { Log.Error("Nonce getting error with code {@code} and description {@description}", nonceResult.Error.Code, nonceResult.Error.Description); return; } var message = new RedeemFunctionMessage { FromAddress = walletAddress.Address, HashedSecret = swap.SecretHash, Secret = swap.Secret, Nonce = nonceResult.Value, GasPrice = Atomex.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 = BlockchainTransactionType.Output | BlockchainTransactionType.SwapRedeem }; var signResult = await SignTransactionAsync(redeemTx, cancellationToken) .ConfigureAwait(false); if (!signResult) { Log.Error("Transaction signing error"); return; } await BroadcastTxAsync(swap, redeemTx, cancellationToken) .ConfigureAwait(false); }
public override async Task RedeemAsync( Swap swap, CancellationToken cancellationToken = default) { var eth = Eth; var secretResult = await EthereumSwapRedeemedHelper .IsRedeemedAsync( swap : swap, currency : eth, attempts : MaxRedeemCheckAttempts, attemptIntervalInSec : RedeemCheckAttemptIntervalInSec, cancellationToken : cancellationToken) .ConfigureAwait(false); if (!secretResult.HasError && secretResult.Value != null) { RedeemConfirmedEventHandler(swap, null, cancellationToken); return; } if (swap.StateFlags.HasFlag(SwapStateFlags.IsRedeemBroadcast)) { // redeem already broadcast TrackTransactionConfirmationAsync( swap: swap, currency: eth, txId: swap.RedeemTx.Id, confirmationHandler: RedeemConfirmedEventHandler, cancellationToken: cancellationToken) .FireAndForget(); return; } if (swap.IsInitiator) { var redeemDeadline = swap.TimeStamp.ToUniversalTime().AddSeconds(DefaultAcceptorLockTimeInSeconds) - RedeemTimeReserve; if (DateTime.UtcNow > redeemDeadline) { Log.Error("Redeem dedline reached for swap {@swap}", swap.Id); return; } } Log.Debug("Create redeem for swap {@swapId}", swap.Id); var walletAddress = (await _account .GetUnspentAddressesAsync( toAddress: swap.ToAddress, amount: 0, fee: 0, feePrice: 0, feeUsagePolicy: FeeUsagePolicy.EstimatedFee, addressUsagePolicy: AddressUsagePolicy.UseOnlyOneAddress, transactionType: BlockchainTransactionType.SwapRedeem, cancellationToken: cancellationToken) .ConfigureAwait(false)) .FirstOrDefault(); if (walletAddress == null) { Log.Error("Insufficient funds for redeem"); return; } var nonceResult = await EthereumNonceManager.Instance .GetNonceAsync(eth, walletAddress.Address, cancellationToken) .ConfigureAwait(false); if (nonceResult.HasError) { Log.Error("Nonce getting error with code {@code} and description {@description}", nonceResult.Error.Code, nonceResult.Error.Description); return; } var message = new RedeemFunctionMessage { FromAddress = walletAddress.Address, HashedSecret = swap.SecretHash, Secret = swap.Secret, Nonce = nonceResult.Value, GasPrice = Atomex.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 = BlockchainTransactionType.Output | BlockchainTransactionType.SwapRedeem }; var signResult = await SignTransactionAsync(redeemTx, cancellationToken) .ConfigureAwait(false); if (!signResult) { Log.Error("Transaction signing error"); return; } swap.RedeemTx = redeemTx; swap.StateFlags |= SwapStateFlags.IsRedeemSigned; RaiseSwapUpdated(swap, SwapStateFlags.IsRedeemSigned); await BroadcastTxAsync(swap, redeemTx, cancellationToken) .ConfigureAwait(false); swap.RedeemTx = redeemTx; swap.StateFlags |= SwapStateFlags.IsRedeemBroadcast; RaiseSwapUpdated(swap, SwapStateFlags.IsRedeemBroadcast); TrackTransactionConfirmationAsync( swap: swap, currency: eth, txId: redeemTx.Id, confirmationHandler: RedeemConfirmedEventHandler, cancellationToken: cancellationToken) .FireAndForget(); }