public IActionResult EstimateOfflineColdStakingSetupFee([FromBody] SetupOfflineColdStakingRequest request) { Guard.NotNull(request, nameof(request)); // Checks the request is valid. if (!this.ModelState.IsValid) { this.logger.LogTrace("(-)[MODEL_STATE_INVALID]"); return(ModelStateErrors.BuildErrorResponse(this.ModelState)); } try { Money amount = Money.Parse(request.Amount); Money estimatedFee = this.ColdStakingManager.EstimateSetupTransactionFee( this.walletTransactionHandler, request.ColdWalletAddress, request.HotWalletAddress, request.WalletName, request.WalletAccount, null, amount, request.SubtractFeeFromAmount, true, request.SegwitChangeAddress, request.SplitCount); this.logger.LogTrace("(-):'{0}'", estimatedFee); return(this.Json(estimatedFee)); } catch (Exception e) { this.logger.LogError("Exception occurred: {0}", e.ToString()); this.logger.LogTrace("(-)[ERROR]"); return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString())); } }
public IActionResult SetupOfflineColdStaking([FromBody] SetupOfflineColdStakingRequest request) { Guard.NotNull(request, nameof(request)); // Checks the request is valid. if (!this.ModelState.IsValid) { this.logger.LogTrace("(-)[MODEL_STATE_INVALID]"); return(ModelStateErrors.BuildErrorResponse(this.ModelState)); } try { Money amount = Money.Parse(request.Amount); Money feeAmount = Money.Parse(request.Fees); (Transaction transaction, TransactionBuildContext context) = this.ColdStakingManager.GetColdStakingSetupTransaction( this.walletTransactionHandler, request.ColdWalletAddress, request.HotWalletAddress, request.WalletName, request.WalletAccount, null, amount, feeAmount, request.SubtractFeeFromAmount, true, request.SplitCount, request.SegwitChangeAddress); // TODO: We use the same code in the regular wallet for offline signing request construction, perhaps it should be moved to a common method // Need to be able to look up the keypath for the UTXOs that were used. IEnumerable <UnspentOutputReference> spendableTransactions = this.ColdStakingManager.GetSpendableTransactionsInAccount( new WalletAccountReference(request.WalletName, request.WalletAccount)).ToList(); var utxos = new List <UtxoDescriptor>(); var addresses = new List <AddressDescriptor>(); foreach (ICoin coin in context.TransactionBuilder.FindSpentCoins(transaction)) { utxos.Add(new UtxoDescriptor() { Amount = coin.TxOut.Value.ToUnit(MoneyUnit.BTC).ToString(), TransactionId = coin.Outpoint.Hash.ToString(), Index = coin.Outpoint.N.ToString(), ScriptPubKey = coin.TxOut.ScriptPubKey.ToHex() }); UnspentOutputReference outputReference = spendableTransactions.FirstOrDefault(u => u.Transaction.Id == coin.Outpoint.Hash && u.Transaction.Index == coin.Outpoint.N); if (outputReference != null) { bool segwit = outputReference.Transaction.ScriptPubKey.IsScriptType(ScriptType.P2WPKH); addresses.Add(new AddressDescriptor() { Address = segwit ? outputReference.Address.Bech32Address : outputReference.Address.Address, AddressType = segwit ? "p2wpkh" : "p2pkh", KeyPath = outputReference.Address.HdPath }); } } // Return transaction hex, UTXO list, address list. The offline signer will infer from the transaction structure that a cold staking setup is being made. var model = new BuildOfflineSignResponse() { WalletName = request.WalletName, WalletAccount = request.WalletAccount, Fee = context.TransactionFee.ToUnit(MoneyUnit.BTC).ToString(), UnsignedTransaction = transaction.ToHex(), Utxos = utxos, Addresses = addresses }; this.logger.LogTrace("(-):'{0}'", model); return(this.Json(model)); } catch (Exception e) { this.logger.LogError("Exception occurred: {0}", e.ToString()); this.logger.LogTrace("(-)[ERROR]"); return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString())); } }