Exemple #1
0
        /// <summary>
        /// Accept an exchange request.
        /// <param name="apiKey">Your apiKey.</param>
        /// <param name="apiSecret">Your apiSecretKey.</param>
        /// <param name="address"/>The address that funds the source amount.</parm>
        /// <param name="privateKey">The privateKey of the sending address.</param>
        /// </summary>
        /// <returns></returns>
        public async Task <Result <bool, ErrorResponse> > AcceptExchangeRequestAsync(
            string apiKey,
            string apiSecret,
            string address,
            string privateKey,
            AcceptExchangeRequest exchangeRequest)
        {
            #region
            IEnumerable <ValidationResult> validation = exchangeRequest.Validate();

            if (validation.Any())
            {
                foreach (var item in validation)
                {
                    throw new ArgumentNullException(item.ErrorMessage);
                }
            }

            if (string.IsNullOrWhiteSpace(address))
            {
                throw new ArgumentNullException(address);
            }
            else if (string.IsNullOrWhiteSpace(privateKey))
            {
                throw new ArgumentNullException(nameof(privateKey));
            }
            #endregion

            string token = getAuthToken(apiKey, apiSecret);

            var    result     = new Result <bool, ErrorResponse>();
            string requestUri = $"{mEnv.BaseUrl}/v1/ExchangeRequests/{exchangeRequest.ID}";

            string     reserveTxnSinatrue  = null;
            string     executeTxnSignature = null;
            string     reclaimTxnSignature = null;
            BigInteger nonce = BigInteger.Parse(GluwaService.GetNonceString());

            if (exchangeRequest.Conversion.Value.IsSourceCurrencyBtc())
            {
                BtcTxnSignature txnSignature = await getBtcTxnSignaturesAsync(
                    exchangeRequest.Conversion.Value.ToSourceCurrency(),
                    address,
                    exchangeRequest.SourceAmount,
                    exchangeRequest.Fee,
                    exchangeRequest.DestinationAddress,
                    exchangeRequest.ReservedFundsAddress,
                    exchangeRequest.ReservedFundsRedeemScript,
                    privateKey);

                reserveTxnSinatrue  = txnSignature.ReserveTxnSignature;
                executeTxnSignature = txnSignature.ExecuteTxnSignature;
                reclaimTxnSignature = txnSignature.ReclaimTxnSignature;
            }
            else
            {
                BigInteger convertExpiryBlockNumber = BigInteger.Parse(exchangeRequest.ExpiryBlockNumber.ToString());

                reserveTxnSinatrue = getGluwacoinReserveTxnSignature(
                    exchangeRequest.Conversion.Value.ToSourceCurrency(),
                    address,
                    exchangeRequest.SourceAmount,
                    exchangeRequest.Fee,
                    exchangeRequest.DestinationAddress,
                    exchangeRequest.Executor,
                    nonce,
                    convertExpiryBlockNumber,
                    privateKey);
            }

            PatchExchangeRequest bodyParams = new PatchExchangeRequest()
            {
                SendingAddress      = address,
                ReserveTxnSignature = reserveTxnSinatrue,
                Nonce = nonce.ToString(),
                ExecuteTxnSignature = executeTxnSignature,
                ReclaimTxnSignature = reclaimTxnSignature
            };

            string        json    = bodyParams.ToJson();
            StringContent content = new StringContent(json, Encoding.UTF8, "application/json");

            HttpRequestMessage request = new HttpRequestMessage
            {
                Method     = new HttpMethod("PATCH"),
                RequestUri = new Uri(requestUri),
                Content    = content
            };

            try
            {
                using (HttpClient httpClient = new HttpClient())
                {
                    httpClient.DefaultRequestHeaders.Add(AUTHORIZATION, $"{BASIC} {token}");

                    using (HttpResponseMessage response = await httpClient.SendAsync(request))
                    {
                        if (response.IsSuccessStatusCode)
                        {
                            result.IsSuccess = true;
                            result.Data      = true;

                            return(result);
                        }

                        string contentString = await response.Content.ReadAsStringAsync();

                        result.Error = ResponseHandler.GetError(response.StatusCode, requestUri, contentString);
                    }
                }
            }
            catch (HttpRequestException)
            {
                result.IsSuccess = false;
                result.Error     = ResponseHandler.GetExceptionError();
            }

            return(result);
        }
Exemple #2
0
        private async Task <BtcTxnSignature> getBtcTxnSignaturesAsync(
            ECurrency currency,
            string address,
            string amount,
            string fee,
            string target,
            string reserve,
            string reservedFundsRedeemScript,
            string privateKey
            )
        {
            GluwaClient client = new GluwaClient(mEnv);

            Result <BalanceResponse, ErrorResponse> getUnspentOutput = await client.GetBalanceAsync(currency, address, true);

            List <UnspentOutput> unspentOutputs = getUnspentOutput.Data.UnspentOutputs.OrderByDescending(u => u.Amount).ToList();

            Money amountValue              = Money.Parse(amount);
            Money feeValue                 = Money.Parse(fee);
            Money reserveAmount            = amountValue + feeValue;
            Money totalRequiredAmountMoney = reserveAmount + feeValue;

            BigInteger totalRequiredAmount = new BigInteger(totalRequiredAmountMoney.Satoshi);

            BitcoinAddress sourceAddress  = BitcoinAddress.Create(address, mEnv.Network);
            BitcoinAddress targetAddress  = BitcoinAddress.Create(target, mEnv.Network);
            BitcoinAddress reserveAddress = BitcoinAddress.Create(reserve, mEnv.Network);

            BitcoinSecret secret = new BitcoinSecret(privateKey, mEnv.Network);

            List <UnspentOutput> usingUnspentOutputs      = new List <UnspentOutput>();
            BigInteger           unspentOutputTotalAmount = BigInteger.Zero;

            for (int i = 0; i < unspentOutputs.Count; i++)
            {
                if (unspentOutputTotalAmount < totalRequiredAmount && i >= MAX_UNSPENTOUTPUTS_COUNT)
                {
                    throw new InvalidOperationException($"Could not find up to {MAX_UNSPENTOUTPUTS_COUNT} BTC unspent outputs that can cover the amount and fee.");
                }

                if (unspentOutputTotalAmount >= totalRequiredAmount)
                {
                    break;
                }

                usingUnspentOutputs.Add(unspentOutputs[i]);
                Money sumAmount = Money.Parse(unspentOutputs[i].Amount);
                unspentOutputTotalAmount += new BigInteger(sumAmount.Satoshi);
            }

            List <Coin> coins = new List <Coin>();

            for (int i = 0; i < usingUnspentOutputs.Count; i++)
            {
                coins.Add(new Coin(
                              fromTxHash: new uint256(usingUnspentOutputs[i].TxHash),
                              fromOutputIndex: (uint)usingUnspentOutputs[i].Index,
                              amount: usingUnspentOutputs[i].Amount,
                              scriptPubKey: Script.FromHex(sourceAddress.ScriptPubKey.ToHex())
                              ));
            }

            TransactionBuilder builder = mEnv.Network.CreateTransactionBuilder();

            NBitcoin.Transaction reserveTxSignature = builder
                                                      .AddKeys(secret)
                                                      .AddCoins(coins)
                                                      .Send(reserveAddress, reserveAmount)
                                                      .SetChange(sourceAddress)
                                                      .SendFees(fee)
                                                      .BuildTransaction(true);

            IEnumerable <Coin> reserveTxCoins = reserveTxSignature.Outputs.AsCoins();
            Coin reserveTxCoin = reserveTxCoins.First(
                c => c.TxOut.ScriptPubKey.GetDestinationAddress(mEnv.Network) == reserveAddress);
            Script     reservedFundsRedeemScriptValue = new Script(reservedFundsRedeemScript);
            ScriptCoin reservedCoin = new ScriptCoin(reserveTxCoin, reservedFundsRedeemScriptValue);

            builder = mEnv.Network.CreateTransactionBuilder();
            PSBT executePsbt = builder
                               .AddKeys(secret)
                               .AddCoins(reservedCoin)
                               .Send(targetAddress, amount)
                               .SendFees(feeValue)
                               .SetChange(reserveAddress)
                               .BuildPSBT(true);

            builder = mEnv.Network.CreateTransactionBuilder();
            PSBT reclaimPsbt = builder
                               .AddKeys(secret)
                               .AddCoins(reservedCoin)
                               .Send(sourceAddress, amount)
                               .SendFees(feeValue)
                               .SetChange(reserveAddress)
                               .BuildPSBT(true);

            BtcTxnSignature bTCTxnSignature = new BtcTxnSignature()
            {
                ReserveTxnSignature = reserveTxSignature.ToHex(),
                ExecuteTxnSignature = executePsbt.ToHex(),
                ReclaimTxnSignature = reclaimPsbt.ToHex()
            };

            return(bTCTxnSignature);
        }
Exemple #3
0
        /// <summary>
        /// Accept quote received from POST /v1/Quote endpoint.
        /// </summary>
        /// <param name="currency">The source currency of the quote.</param>
        /// <param name="address">The sending address of the quote.</param>
        /// <param name="privateKey">The privateKey of the sending address.</param>
        /// <param name="quoteRequest">Request body.</param>
        /// <response code="202">Quote is accepted.</response>
        /// <response code="400_InvalidUrlParameters">Invalid URL parameters.</response>
        /// <response code="400_MissingBody">Request body is missing.</response>
        /// <response code="400_InvalidBody">Request validation errors. See InnerErrors.</response>
        /// <response code="400_ValidationError">Request validation errors. See InnerErrors.</response>
        /// <response code="403">Invalid checksum. Checksum may be wrong or expired.</response>
        /// <response code="404">One of the matched orders are no longer available.</response>
        /// <response code="500">Server error.</response>
        /// <returns></returns>
        public async Task <Result <AcceptQuoteResponse, ErrorResponse> > AcceptQuoteAsync(
            ECurrency currency,
            string address,
            string privateKey,
            AcceptQuoteRequest quoteRequest)
        {
            #region
            if (!currency.IsGluwaExchangeCurrency())
            {
                throw new ArgumentOutOfRangeException($"Unsupported currency: {currency}");
            }

            foreach (var order in quoteRequest.MatchedOrders)
            {
                IEnumerable <ValidationResult> validation = quoteRequest.Validate(currency);

                if (validation.Any())
                {
                    foreach (var item in validation)
                    {
                        throw new ArgumentNullException(item.ErrorMessage);
                    }
                }
            }

            if (string.IsNullOrWhiteSpace(address))
            {
                throw new ArgumentNullException(nameof(address));
            }
            else if (string.IsNullOrWhiteSpace(privateKey))
            {
                throw new ArgumentNullException(nameof(privateKey));
            }
            #endregion

            var    result     = new Result <AcceptQuoteResponse, ErrorResponse>();
            string requestUri = $"{mEnv.BaseUrl}/v1/Quote";

            List <MatchedOrderRequest> matchedOrders = new List <MatchedOrderRequest>();
            foreach (var matchedOrder in quoteRequest.MatchedOrders)
            {
                if (currency.IsGluwaCoinCurrency())
                {
                    BigInteger nonce = BigInteger.Parse(GluwaService.GetNonceString());
                    BigInteger convertExpiryBlockNumber = BigInteger.Parse(matchedOrder.ExpiryBlockNumber);

                    string signature = getGluwacoinReserveTxnSignature(
                        currency,
                        address,
                        matchedOrder.SourceAmount,
                        matchedOrder.Fee,
                        matchedOrder.DestinationAddress,
                        matchedOrder.Executor,
                        nonce,
                        convertExpiryBlockNumber,
                        privateKey);

                    MatchedOrderRequest matchedOrderRequest = new MatchedOrderRequest()
                    {
                        OrderID             = matchedOrder.OrderID,
                        ReserveTxnSignature = signature,
                        Nonce = nonce.ToString()
                    };

                    matchedOrders.Add(matchedOrderRequest);
                }
                else
                {
                    BtcTxnSignature txnSignature = await getBtcTxnSignaturesAsync(
                        currency,
                        address,
                        matchedOrder.SourceAmount,
                        matchedOrder.Fee,
                        matchedOrder.DestinationAddress,
                        matchedOrder.ReservedFundsAddress,
                        matchedOrder.ReservedFundsRedeemScript,
                        privateKey);

                    MatchedOrderRequest matchedOrderRequest = new MatchedOrderRequest()
                    {
                        OrderID             = matchedOrder.OrderID,
                        ReserveTxnSignature = txnSignature.ReserveTxnSignature,
                        ExecuteTxnSignature = txnSignature.ExecuteTxnSignature,
                        ReclaimTxnSignature = txnSignature.ReclaimTxnSignature
                    };

                    matchedOrders.Add(matchedOrderRequest);
                }
            }

            PutQuoteRequest bodyParams = new PutQuoteRequest()
            {
                MatchedOrders = matchedOrders,
                Checksum      = quoteRequest.Checksum
            };

            string        json    = bodyParams.ToJson();
            StringContent content = new StringContent(json, Encoding.UTF8, "application/json");

            try
            {
                using (HttpClient httpClient = new HttpClient())
                    using (HttpResponseMessage response = await httpClient.PutAsync(requestUri, content))
                    {
                        if (response.IsSuccessStatusCode)
                        {
                            AcceptQuoteResponse quoteResponse = await response.Content.ReadAsAsync <AcceptQuoteResponse>();

                            result.IsSuccess = true;
                            result.Data      = quoteResponse;

                            return(result);
                        }

                        string contentString = await response.Content.ReadAsStringAsync();

                        result.Error = ResponseHandler.GetError(response.StatusCode, requestUri, contentString);
                    }
            }
            catch (HttpRequestException)
            {
                result.IsSuccess = false;
                result.Error     = ResponseHandler.GetExceptionError();
            }

            return(result);
        }