public async Task <IActionResult> GetBalances([FromQuery] int take, [FromQuery] string continuation) { if (take <= 0) { ModelState.AddModelError(nameof(take), "Must be greater than zero"); } ModelState.IsValidContinuationToken(continuation); if (!ModelState.IsValid) { return(BadRequest(ModelState.ToErrorResponce())); } var padedResult = await _balanceService.GetBalances(take, continuation); return(Ok(PaginationResponse.From(padedResult.Continuation, padedResult.Items.Select(p => new WalletBalanceContract { Address = p.Address, Balance = MoneyConversionHelper.SatoshiToContract(p.BalanceSatoshi), AssetId = Constants.Assets.LiteCoin.AssetId, Block = p.UpdatedAtBlockHeight }).ToList().AsReadOnly()))); }
public async Task <IActionResult> GetObservableSingleOperation(Guid operationId) { if (operationId == Guid.Empty) { throw new ValidationApiException("OperationId must be valid guid"); } var result = await _observableOperationService.GetByIdAsync(operationId); if (result == null) { return(NoContent()); } return(Ok(new BroadcastedSingleTransactionResponse { Amount = MoneyConversionHelper.SatoshiToContract(result.Inputs.Single().Amount - (result.IncludeFee ? result.FeeSatoshi : 0)), Fee = MoneyConversionHelper.SatoshiToContract(result.FeeSatoshi), OperationId = result.OperationId, Hash = result.TxHash, Timestamp = result.Updated, State = MapOperationState(result.Status), Block = result.UpdatedAtBlockHeight })); }
public async Task <IActionResult> GetObservableManyOutputsOperation(Guid operationId) { if (operationId == Guid.Empty) { throw new ValidationApiException("OperationId must be valid guid"); } var result = await _observableOperationService.GetByIdAsync(operationId); if (result == null) { return(NoContent()); } return(Ok(new BroadcastedTransactionWithManyOutputsResponse() { Outputs = result.Outputs.Select(o => new BroadcastedTransactionOutputContract { Amount = MoneyConversionHelper.SatoshiToContract(o.Amount), ToAddress = o.Address }).ToList(), Fee = MoneyConversionHelper.SatoshiToContract(result.FeeSatoshi), OperationId = result.OperationId, Hash = result.TxHash, Timestamp = result.Updated, State = MapOperationState(result.Status), Block = result.UpdatedAtBlockHeight })); }
public async Task <IActionResult> GetBalances([FromQuery, Required] int take, [FromQuery] string continuation) { if (take < 1) { return(BadRequest(ErrorResponse.Create("Invalid parameter").AddModelError("take", "Must be positive non zero integer"))); } if (!string.IsNullOrEmpty(continuation)) { try { JsonConvert.DeserializeObject <TableContinuationToken>(Utils.HexToString(continuation)); } catch (JsonReaderException) { return(BadRequest(ErrorResponse.Create("Invalid parameter").AddModelError("continuation", "Must be valid continuation token"))); } } var padedResult = await _balanceService.GetBalances(take, continuation); return(Ok(PaginationResponse.From(padedResult.Continuation, padedResult.Items.Select(p => new WalletBalanceContract { Address = p.Address, Balance = MoneyConversionHelper.SatoshiToContract(p.BalanceSatoshi), AssetId = Constants.Assets.BitcoinGold.AssetId, Block = p.UpdatedAtBlockHeight }).ToList().AsReadOnly()))); }
public static HistoricalTransactionContract ToHistoricalTransaction(this HistoricalTransactionDto source) { return(new HistoricalTransactionContract { ToAddress = source.ToAddress, FromAddress = source.FromAddress, AssetId = source.AssetId, Amount = MoneyConversionHelper.SatoshiToContract(source.AmountSatoshi), Hash = source.TxHash, Timestamp = source.TimeStamp }); }
private HistoricalTransactionContract ToHistoricalTransaction(HistoricalTransactionDto source) { return(new HistoricalTransactionContract { ToAddress = source.ToAddress, FromAddress = source.FromAddress, AssetId = source.AssetId, Amount = MoneyConversionHelper.SatoshiToContract(source.AmountSatoshi), Hash = source.TxHash, Timestamp = DateTime.SpecifyKind(source.TimeStamp, DateTimeKind.Utc), }); }
private HistoricalTransactionContract ToHistoricalTransaction(HistoricalTransactionDto source) { return(new HistoricalTransactionContract { ToAddress = source.ToAddress, FromAddress = source.FromAddress, AssetId = source.AssetId, Amount = MoneyConversionHelper.SatoshiToContract(source.AmountSatoshi), Hash = source.TxHash, Timestamp = source.TimeStamp, TransactionType = source.IsSending ? TransactionType.Send : TransactionType.Receive }); }
public async Task <BuildTransactionResponse> BuildSingle([FromBody] BuildSingleTransactionRequest request) { if (request == null) { throw new BusinessException("Unable deserialize request", ErrorCode.BadInputParameter); } var amountSatoshi = MoneyConversionHelper.SatoshiFromContract(request.Amount); if (amountSatoshi <= 0) { throw new BusinessException($"Amount can't be less or equal to zero: {amountSatoshi}", ErrorCode.BadInputParameter); } if (request.AssetId != Constants.Assets.LiteCoin.AssetId) { throw new BusinessException("Invalid assetId", ErrorCode.BadInputParameter); } var toBitcoinAddress = _addressValidator.GetBitcoinAddress(request.ToAddress); if (toBitcoinAddress == null) { throw new BusinessException("Invalid ToAddress ", ErrorCode.BadInputParameter); } var fromBitcoinAddress = _addressValidator.GetBitcoinAddress(request.FromAddress); if (fromBitcoinAddress == null) { throw new BusinessException("Invalid FromAddress", ErrorCode.BadInputParameter); } if (request.OperationId == Guid.Empty) { throw new BusinessException("Invalid operation id (GUID)", ErrorCode.BadInputParameter); } var tx = await _operationService.GetOrBuildTransferTransaction(request.OperationId, fromBitcoinAddress, toBitcoinAddress, request.AssetId, new Money(amountSatoshi), request.IncludeFee); return(new BuildTransactionResponse { TransactionContext = tx.ToHex() }); }
public async Task <PaginationResponse <WalletBalanceContract> > GetBalances([FromQuery] int take, [FromQuery] string continuation) { if (take <= 0) { throw new BusinessException("Take must be greater than zero", ErrorCode.BadInputParameter); } var padedResult = await _balanceService.GetBalances(take, continuation); return(PaginationResponse.From(padedResult.Continuation, padedResult.Items.Select(p => new WalletBalanceContract { Address = p.Address, Balance = MoneyConversionHelper.SatoshiToContract(p.BalanceSatoshi), AssetId = Constants.Assets.Vertcoin.AssetId }).ToList().AsReadOnly())); }
public async Task <IActionResult> BuildClaim([FromBody] BuildClaimTransactionRequest request) { if (request == null) { return(BadRequest(ErrorResponse.Create("Unable to deserialize request"))); } var addressValid = _addressValidator.IsAddressValid(request.Address); if (!addressValid) { return(BadRequest(ErrorResponse.Create("Invalid address"))); } var built = await _transactionBuilder.BuildClaimTransactions(request.Address); var aggregate = await _operationRepository.GetOrInsert(request.OperationId, () => OperationAggregate.StartNew(request.OperationId, fromAddress: request.Address, toAddress: request.Address, amount: built.availiableGas, assetId: Constants.Assets.Gas.AssetId, fee: 0, includeFee: false)); if (aggregate.IsBroadcasted) { return(Conflict()); } if (!built.tx.Claims.Any()) { return(Accepted(new BuiltClaimTransactionResponse { ClaimedGas = MoneyConversionHelper.ToContract(built.availiableGas, Constants.Assets.Gas.AssetId), AllGas = MoneyConversionHelper.ToContract(built.unclaimedGas, Constants.Assets.Gas.AssetId), TransactionContext = TransactionSerializer.Serialize(built.tx, TransactionType.ClaimTransaction) })); } return(Ok(new BuiltClaimTransactionResponse { ClaimedGas = MoneyConversionHelper.ToContract(built.availiableGas, Constants.Assets.Gas.AssetId), AllGas = MoneyConversionHelper.ToContract(built.unclaimedGas, Constants.Assets.Gas.AssetId), TransactionContext = TransactionSerializer.Serialize(built.tx, TransactionType.ClaimTransaction) })); }
public async Task <IActionResult> GetObservableSingleOperation(Guid operationId) { if (!ModelState.IsValid || !ModelState.IsValidOperationId(operationId)) { return(BadRequest(ErrorResponseFactory.Create(ModelState))); } var result = await _observableOperationRepository.GetById(operationId); if (result == null) { return(new StatusCodeResult((int)HttpStatusCode.NoContent)); } BroadcastedTransactionState MapState(BroadcastStatus status) { switch (status) { case BroadcastStatus.Completed: return(BroadcastedTransactionState.Completed); case BroadcastStatus.Failed: return(BroadcastedTransactionState.Failed); case BroadcastStatus.InProgress: return(BroadcastedTransactionState.InProgress); default: throw new InvalidCastException($"Unknown mapping from {status} "); } } return(Ok(new BroadcastedSingleTransactionResponse { Amount = MoneyConversionHelper.ToContract(result.Amount, result.AssetId), Fee = MoneyConversionHelper.ToContract(result.Fee, result.AssetId), OperationId = result.OperationId, Hash = result.TxHash, Timestamp = result.Updated, State = MapState(result.Status), Block = result.UpdatedAtBlockHeight })); }
public async Task <IActionResult> GetObservableSingleOperation(Guid operationId) { if (operationId == Guid.Empty) { return(BadRequest(ErrorResponse.Create("Invalid parameter").AddModelError(nameof(operationId), "Must be valid guid"))); } var result = await _observableOperationService.GetById(operationId); if (result == null) { return(new StatusCodeResult((int)HttpStatusCode.NoContent)); } BroadcastedTransactionState MapState(BroadcastStatus status) { switch (status) { case BroadcastStatus.Completed: return(BroadcastedTransactionState.Completed); case BroadcastStatus.Failed: return(BroadcastedTransactionState.Failed); case BroadcastStatus.InProgress: return(BroadcastedTransactionState.InProgress); default: throw new InvalidCastException($"Unknown mapping from {status} "); } } return(Ok(new BroadcastedSingleTransactionResponse { Amount = MoneyConversionHelper.SatoshiToContract(result.AmountSatoshi), Fee = MoneyConversionHelper.SatoshiToContract(result.FeeSatoshi), OperationId = result.OperationId, Hash = result.TxHash, Timestamp = result.Updated, State = MapState(result.Status), Block = result.UpdatedAtBlockHeight })); }
public async Task <IActionResult> GetObservableOperation(Guid operationId) { var result = await _observableOperationService.GetById(operationId); if (result == null) { return(new StatusCodeResult((int)HttpStatusCode.NoContent)); } BroadcastedTransactionState MapState(BroadcastStatus status) { switch (status) { case BroadcastStatus.Completed: return(BroadcastedTransactionState.Completed); case BroadcastStatus.Failed: return(BroadcastedTransactionState.Failed); case BroadcastStatus.InProgress: return(BroadcastedTransactionState.InProgress); default: throw new InvalidCastException($"Unknown mapping from {status} "); } } return(Ok(new BroadcastedTransactionResponse { Amount = MoneyConversionHelper.SatoshiToContract(result.AmountSatoshi), Fee = MoneyConversionHelper.SatoshiToContract(result.FeeSatoshi), OperationId = result.OperationId, Hash = result.TxHash, Timestamp = result.Updated, State = MapState(result.Status) })); }
public async Task <IActionResult> BuildSingle([FromBody] BuildSingleTransactionRequest request) { if (request == null) { return(BadRequest(ErrorResponse.Create("Unable to deserialize request"))); } if (!new [] { Constants.Assets.Neo.AssetId, Constants.Assets.Gas.AssetId }.Contains(request.AssetId)) { return(BadRequest(ErrorResponse.Create("Invalid assetId"))); } var amount = MoneyConversionHelper.FromContract(request.Amount, request.AssetId); if (amount <= 0) { return(BadRequest(ErrorResponse.Create($"Amount can't be less or equal to zero: {amount}"))); } if (request.AssetId == Constants.Assets.Neo.AssetId && amount % 1 != 0) { return(BadRequest($"The minimum unit of NEO is 1 and tokens cannot be subdivided.: {amount}")); } var toAddressValid = _addressValidator.IsAddressValid(request.ToAddress); if (!toAddressValid) { return(BadRequest(ErrorResponse.Create("Invalid toAddress"))); } var fromAddressValid = _addressValidator.IsAddressValid(request.FromAddress); if (!fromAddressValid) { return(BadRequest(ErrorResponse.Create("Invalid fromAddress"))); } if (!ModelState.IsValidOperationId(request.OperationId)) { return(BadRequest(ErrorResponseFactory.Create(ModelState))); } if ((await _operationRepository.GetOrDefault(request.OperationId))?.IsBroadcasted ?? false) { return(Conflict()); } Transaction tx; decimal fee = 0; switch (request.AssetId) { case Constants.Assets.Neo.AssetId: (tx, fee) = await _transactionBuilder.BuildNeoContractTransactionAsync(request.FromAddress, request.ToAddress, amount, request.IncludeFee); break; case Constants.Assets.Gas.AssetId: tx = await _transactionBuilder.BuildGasTransactionAsync(request.FromAddress, request.ToAddress, amount); break; default: throw new ArgumentException("Unknown switch", nameof(request.AssetId)); } await _operationRepository.GetOrInsert(request.OperationId, () => OperationAggregate.StartNew(request.OperationId, fromAddress: request.FromAddress, toAddress: request.ToAddress, amount: amount, assetId: request.AssetId, fee: fee, includeFee: request.IncludeFee)); return(Ok(new BuildTransactionResponse { TransactionContext = TransactionSerializer.Serialize(tx, TransactionType.ContractTransaction) })); }
public async Task <IActionResult> BuildSingle([FromBody] BuildSingleTransactionRequest request) { if (request == null) { throw new ValidationApiException("Unable deserialize request"); } var amountSatoshi = MoneyConversionHelper.SatoshiFromContract(request.Amount); if (amountSatoshi <= 0) { throw new ValidationApiException($"Amount can't be less or equal to zero: {amountSatoshi}"); } if (request.AssetId != Constants.Assets.Bitcoin.AssetId) { throw new ValidationApiException("Invalid assetId"); } if (request.OperationId == Guid.Empty) { throw new ValidationApiException("Invalid operation id (GUID)"); } if (await _operationEventRepository.ExistAsync(request.OperationId, OperationEventType.Broadcasted)) { return(Conflict()); } BuiltTransactionInfo tx; try { tx = await _operationService.GetOrBuildTransferTransactionAsync(request.OperationId, new List <OperationInput> { new OperationInput { Address = request.FromAddress, AddressContext = request.FromAddressContext?.DeserializeJson <AddressContextContract>()?.PubKey, Amount = amountSatoshi } }, new List <OperationOutput> { new OperationOutput { Address = request.ToAddress, Amount = amountSatoshi } }, OperationType.Single, request.AssetId, request.IncludeFee); } catch (NotEnoughFundsException) { return(BadRequest(BlockchainErrorResponse.FromKnownError(BlockchainErrorCode.NotEnoughBalance))); } catch (BusinessException e) when(e.Code == ErrorCode.NotEnoughFundsAvailable) { return(BadRequest(BlockchainErrorResponse.FromKnownError(BlockchainErrorCode.NotEnoughBalance))); } return(Ok(new BuildTransactionResponse { TransactionContext = tx.ToJson(_network) })); }
public async Task <IActionResult> BuildSingle([FromBody] BuildSingleTransactionRequest request) { if (request == null) { throw new BusinessException("Unable deserialize request", ErrorCode.BadInputParameter); } var amountSatoshi = MoneyConversionHelper.SatoshiFromContract(request.Amount); if (amountSatoshi <= 0) { throw new BusinessException($"Amount can't be less or equal to zero: {amountSatoshi}", ErrorCode.BadInputParameter); } if (request.AssetId != Constants.Assets.LiteCoin.AssetId) { throw new BusinessException("Invalid assetId", ErrorCode.BadInputParameter); } var toBitcoinAddress = _addressValidator.GetBitcoinAddress(request.ToAddress); if (toBitcoinAddress == null) { throw new BusinessException("Invalid ToAddress ", ErrorCode.BadInputParameter); } var fromBitcoinAddress = _addressValidator.GetBitcoinAddress(request.FromAddress); if (fromBitcoinAddress == null) { throw new BusinessException("Invalid FromAddress", ErrorCode.BadInputParameter); } if (request.OperationId == Guid.Empty) { throw new BusinessException("Invalid operation id (GUID)", ErrorCode.BadInputParameter); } PubKey fromAddressPubkey = null; var pubKeyString = request.FromAddressContext?.DeserializeJson <AddressContextContract>()?.PubKey; if (pubKeyString != null) { if (!_addressValidator.IsPubkeyValid(pubKeyString)) { throw new BusinessException("Invalid pubkey string", ErrorCode.BadInputParameter); } fromAddressPubkey = _addressValidator.GetPubkey(pubKeyString); } if (await _operationEventRepository.Exist(request.OperationId, OperationEventType.Broadcasted)) { return(StatusCode(409)); } var tx = await _operationService.GetOrBuildTransferTransaction(request.OperationId, fromBitcoinAddress, fromAddressPubkey, toBitcoinAddress, request.AssetId, new Money(amountSatoshi), request.IncludeFee); return(Ok(new BuildTransactionResponse { TransactionContext = tx })); }
public async Task <ActionResult> BuildSingle([FromBody] BuildSingleTransactionRequest request) { if (request == null) { throw new BusinessException("Unable deserialize request", ErrorCode.BadInputParameter); } var amountSatoshi = MoneyConversionHelper.SatoshiFromContract(request.Amount); if (amountSatoshi <= 0) { throw new BusinessException($"Amount can't be less or equal to zero: {amountSatoshi}", ErrorCode.BadInputParameter); } if (request.AssetId != (await _assetRepository.GetDefaultAsset()).AssetId) { throw new BusinessException("Invalid assetId", ErrorCode.BadInputParameter); } var toBitcoinAddress = _addressValidator.GetBitcoinAddress(request.ToAddress, _blockChainProvider.Network); if (toBitcoinAddress == null) { throw new BusinessException("Invalid ToAddress ", ErrorCode.BadInputParameter); } var fromBitcoinAddress = _addressValidator.GetBitcoinAddress(request.FromAddress, _blockChainProvider.Network); if (fromBitcoinAddress == null) { throw new BusinessException("Invalid FromAddress", ErrorCode.BadInputParameter); } if (request.OperationId == Guid.Empty) { throw new BusinessException("Invalid operation id (GUID)", ErrorCode.BadInputParameter); } if (await _operationEventRepository.Exist(request.OperationId, OperationEventType.Broadcasted)) { return(Conflict()); } BuildedTransactionInfo tx; try { tx = await _operationService.GetOrBuildTransferTransaction(request.OperationId, fromBitcoinAddress, toBitcoinAddress, request.AssetId, new Money(amountSatoshi), request.IncludeFee); } catch (NotEnoughFundsException) { return(BadRequest(BlockchainErrorResponse.FromKnownError(BlockchainErrorCode.NotEnoughBalance))); } catch (BusinessException e) when(e.Code == ErrorCode.NotEnoughFundsAvailable) { return(BadRequest(BlockchainErrorResponse.FromKnownError(BlockchainErrorCode.NotEnoughBalance))); } return(Ok(new BuildTransactionResponse { TransactionContext = tx.ToJson(_network) })); }