Ejemplo n.º 1
0
        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
            }));
        }
Ejemplo n.º 4
0
        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
     });
 }
Ejemplo n.º 6
0
 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
     });
 }
Ejemplo n.º 8
0
        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()
            });
        }
Ejemplo n.º 9
0
        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()));
        }
Ejemplo n.º 10
0
        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)
            }));
        }
Ejemplo n.º 11
0
        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
            }));
        }
Ejemplo n.º 12
0
        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)
            }));
        }
Ejemplo n.º 14
0
        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
            }));
        }
Ejemplo n.º 17
0
        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)
            }));
        }