public async Task BroadcastTransaction(NeoModules.NEP6.Transactions.Transaction signedTransaction,
                                               OperationAggregate aggregate)
        {
            var txHash = signedTransaction.Hash.ToString().Substring(2);

            var lastBlockHeight = await _blockchainProvider.GetHeightAsync();

            try
            {
                var isSuccess = await _neoRawTransactionSender.SendRequestAsync(signedTransaction.ToHexString());

                if (!isSuccess)
                {
                    throw new Exception("Unknown error while broadcasting the tx");
                }
            }
            catch (RpcResponseException e) when(e.RpcError.Code == -501)
            {
                throw new TransactionAlreadyBroadcastedException(e);
            }

            await _observableOperationRepository.InsertOrReplace(ObervableOperation.Create(aggregate,
                                                                                           BroadcastStatus.InProgress,
                                                                                           txHash,
                                                                                           (int)lastBlockHeight));

            await _unconfirmedTransactionRepository.InsertOrReplace(
                UnconfirmedTransaction.Create(aggregate.OperationId, txHash));

            await _transactionOutputsService.CompleteTxOutputs(aggregate.OperationId, signedTransaction);
        }
Example #2
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)
            }));
        }
 public static ObervableOperation Create(OperationAggregate operation, BroadcastStatus status, string txHash, int updatedAtBlockHeight, DateTime?updated = null)
 {
     return(new ObervableOperation
     {
         OperationId = operation.OperationId,
         Amount = operation.Amount,
         AssetId = operation.AssetId,
         FromAddress = operation.FromAddress,
         IncludeFee = operation.IncludeFee,
         ToAddress = operation.ToAddress,
         Status = status,
         TxHash = txHash,
         Updated = updated ?? DateTime.UtcNow,
         Fee = operation.Fee,
         UpdatedAtBlockHeight = updatedAtBlockHeight
     });
 }
Example #4
0
 public static OperationEntity FromDomain(OperationAggregate aggregate)
 {
     return(new OperationEntity
     {
         ETag = string.IsNullOrEmpty(aggregate.Version) ? "*" : aggregate.Version,
         Fee = aggregate.Fee,
         Amount = aggregate.Amount,
         AssetId = aggregate.AssetId,
         DetectedOnBlockchain = aggregate.DetectedOnBlockchain,
         BroadcastedAt = aggregate.BroadcastedAt,
         FromAddress = aggregate.FromAddress,
         IncludeFee = aggregate.IncludeFee,
         InsertedAt = aggregate.InsertedAt,
         OperationId = aggregate.OperationId,
         ToAddress = aggregate.ToAddress,
         PartitionKey = GeneratePartitionKey(aggregate.OperationId),
         RowKey = GenerateRowKey()
     });
 }
Example #5
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 Task Save(OperationAggregate aggregate)
 {
     return(_storage.ReplaceAsync(OperationEntity.FromDomain(aggregate)));
 }
 public Task Insert(OperationAggregate aggregate)
 {
     return(_storage.InsertAsync(OperationEntity.FromDomain(aggregate)));
 }