Commit 00fb193c authored by Sergei Zubov's avatar Sergei Zubov

Add fee type to transaction builder

If fee type is "Included", fees is charged from receiver. If fee
type is "Extra", fee is charged from sender.
parent d16395c8
......@@ -42,6 +42,16 @@ namespace NBitcoin
minerReward = fee - deStreamFee;
}
/// <summary>
/// Subtracts fee from sum of fees and transfer funds
/// </summary>
/// <param name="value">Sum of fees and transfer funds</param>
/// <returns>Transfer funds without fees</returns>
public Money SubtractFee(Money value)
{
return Convert.ToInt64(value.Satoshi / (1.0 + this.FeeRate));
}
public bool IsDeStreamAddress(string address)
{
return this.DeStreamWallets.Contains(address);
......
using System;
using System.Linq;
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using NBitcoin;
using Stratis.Bitcoin.Features.Wallet.Interfaces;
using Stratis.Bitcoin.Features.Wallet.Models;
using Stratis.Bitcoin.Utilities;
using Stratis.Bitcoin.Utilities.JsonErrors;
namespace Stratis.Bitcoin.Features.Wallet.Controllers
{
/// <summary>
/// Controller providing operations on a wallet.
/// </summary>
[Route("api/[controller]")]
public class DeStreamWalletController : Controller
{
private readonly ILogger _logger;
private readonly Network _network;
private readonly IWalletTransactionHandler _walletTransactionHandler;
public DeStreamWalletController(Network network, IWalletTransactionHandler walletTransactionHandler,
ILoggerFactory loggerFactory)
{
this._network = network;
this._walletTransactionHandler = walletTransactionHandler;
this._logger = loggerFactory.CreateLogger(this.GetType().FullName);
}
/// <summary>
/// Builds a transaction.
/// </summary>
/// <param name="request">The transaction parameters.</param>
/// <returns>All the details of the transaction, including the hex used to execute it.</returns>
[Route("build-transaction")]
[HttpPost]
public IActionResult BuildTransaction([FromBody] BuildTransactionRequest request)
{
Guard.NotNull(request, nameof(request));
// checks the request is valid
if (!this.ModelState.IsValid) return WalletController.BuildErrorResponse(this.ModelState);
try
{
Script destination = BitcoinAddress.Create(request.DestinationAddress, this._network).ScriptPubKey;
var context = new TransactionBuildContext(
new WalletAccountReference(request.WalletName, request.AccountName),
new[] {new Recipient {Amount = request.Amount, ScriptPubKey = destination}}.ToList(),
request.Password, request.OpReturnData)
{
TransactionFee = string.IsNullOrEmpty(request.FeeAmount) ? null : Money.Parse(request.FeeAmount),
MinConfirmations = request.AllowUnconfirmed ? 0 : 1,
Shuffle =
request.ShuffleOutputs ??
true // We shuffle transaction outputs by default as it's better for anonymity.
};
if (Enum.TryParse(request.FeeType, out DeStreamFeeType feeType))
{
if (feeType == DeStreamFeeType.Included)
{
foreach (Recipient recipient in context.Recipients)
recipient.Amount = this._network.SubtractFee(recipient.Amount);
}
}
else
throw new FormatException($"FeeType {request.FeeType} is not a valid DeStreamFeeType");
Transaction transactionResult = this._walletTransactionHandler.BuildTransaction(context);
var model = new WalletBuildTransactionModel
{
Hex = transactionResult.ToHex(),
Fee = context.TransactionFee,
TransactionId = transactionResult.GetHash()
};
return this.Json(model);
}
catch (Exception e)
{
this._logger.LogError("Exception occurred: {0}", e.ToString());
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString());
}
}
}
}
\ No newline at end of file
......@@ -27,21 +27,21 @@ namespace Stratis.Bitcoin.Features.Wallet.Controllers
{
private readonly IWalletManager walletManager;
private readonly IWalletTransactionHandler walletTransactionHandler;
protected readonly IWalletTransactionHandler walletTransactionHandler;
private readonly IWalletSyncManager walletSyncManager;
private readonly CoinType coinType;
/// <summary>Specification of the network the node runs on - regtest/testnet/mainnet.</summary>
private readonly Network network;
protected readonly Network network;
private readonly IConnectionManager connectionManager;
private readonly ConcurrentChain chain;
/// <summary>Instance logger.</summary>
private readonly ILogger logger;
protected readonly ILogger logger;
private readonly IBroadcasterManager broadcasterManager;
......@@ -1050,7 +1050,7 @@ namespace Stratis.Bitcoin.Features.Wallet.Controllers
/// Builds an <see cref="IActionResult"/> containing errors contained in the <see cref="ControllerBase.ModelState"/>.
/// </summary>
/// <returns>A result containing the errors.</returns>
private static IActionResult BuildErrorResponse(ModelStateDictionary modelState)
protected internal static IActionResult BuildErrorResponse(ModelStateDictionary modelState)
{
List<ModelError> errors = modelState.Values.SelectMany(e => e.Errors).ToList();
return ErrorHelpers.BuildErrorResponse(
......
namespace Stratis.Bitcoin.Features.Wallet
{
public enum DeStreamFeeType
{
/// <summary>
/// Fee is charged from receiver
/// </summary>
Included,
/// <summary>
/// Fee is charged from sender
/// </summary>
Extra
}
}
\ No newline at end of file
......@@ -33,7 +33,7 @@ namespace Stratis.Bitcoin.Features.Wallet
services.AddSingleton<IDeStreamWalletManager, DeStreamWalletManager>();
services.AddSingleton<IWalletManager>(p => p.GetService<IDeStreamWalletManager>());
services.AddSingleton<IWalletFeePolicy, WalletFeePolicy>();
services.AddSingleton<WalletController>();
services.AddSingleton<DeStreamWalletController>();
services.AddSingleton<WalletRPCController>();
services.AddSingleton<IBroadcasterManager, FullNodeBroadcasterManager>();
services.AddSingleton<BroadcasterBehavior>();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment