public EstimateFeeResult EstimateFee(ScTxFeeEstimateRequest request) { Features.Wallet.Wallet wallet = this.walletManager.GetWallet(request.WalletName); HdAccount account = wallet.GetAccount(request.AccountName); if (account == null) { return(EstimateFeeResult.Failure(AccountNotInWalletError, $"No account with the name '{request.AccountName}' could be found.")); } HdAddress senderAddress = account.GetCombinedAddresses().FirstOrDefault(x => x.Address == request.Sender); if (senderAddress == null) { return(EstimateFeeResult.Failure(SenderNotInWalletError, $"The given address {request.Sender} was not found in the wallet.")); } if (!this.CheckBalance(senderAddress.Address)) { return(EstimateFeeResult.Failure(InsufficientBalanceError, SenderNoBalanceError)); } List <OutPoint> selectedInputs = this.SelectInputs(request.WalletName, request.Sender, request.Outpoints); if (!selectedInputs.Any()) { return(EstimateFeeResult.Failure(InvalidOutpointsError, "Invalid list of request outpoints have been passed to the method. Please ensure that the outpoints are spendable by the sender address.")); } var recipients = new List <Recipient>(); foreach (RecipientModel recipientModel in request.Recipients) { BitcoinAddress bitcoinAddress = BitcoinAddress.Create(recipientModel.DestinationAddress, this.network); // If it's a potential SC address, check if it's a contract. if (bitcoinAddress is BitcoinPubKeyAddress bitcoinPubKeyAddress) { var address = new uint160(bitcoinPubKeyAddress.Hash.ToBytes()); if (this.stateRoot.IsExist(address)) { return(EstimateFeeResult.Failure(TransferFundsToContractError, $"The recipient address {recipientModel.DestinationAddress} is a contract. Transferring funds directly to a contract is not supported.")); } } recipients.Add(new Recipient { ScriptPubKey = bitcoinAddress.ScriptPubKey, Amount = recipientModel.Amount }); } // Build context var context = new TransactionBuildContext(this.network) { AccountReference = new WalletAccountReference(request.WalletName, request.AccountName), MinConfirmations = MinConfirmationsAllChecks, Shuffle = false, OpReturnData = request.OpReturnData, OpReturnAmount = string.IsNullOrEmpty(request.OpReturnAmount) ? null : Money.Parse(request.OpReturnAmount), SelectedInputs = selectedInputs, AllowOtherInputs = false, Recipients = recipients, ChangeAddress = senderAddress, // Unique for fee estimation TransactionFee = null, FeeType = FeeParser.Parse(request.FeeType), Sign = false, }; Money fee = this.walletTransactionHandler.EstimateFee(context); return(EstimateFeeResult.Success(fee)); }
public BuildCallContractTransactionResponse BuildCallTx(BuildCallContractTransactionRequest request) { if (!this.CheckBalance(request.Sender)) { return(BuildCallContractTransactionResponse.Failed(SenderNoBalanceError)); } List <OutPoint> selectedInputs = this.SelectInputs(request.WalletName, request.Sender, request.Outpoints); if (!selectedInputs.Any()) { return(BuildCallContractTransactionResponse.Failed("Invalid list of request outpoints have been passed to the method. Please ensure that the outpoints are spendable by the sender address")); } uint160 addressNumeric = request.ContractAddress.ToUint160(this.network); ContractTxData txData; if (request.Parameters != null && request.Parameters.Any()) { try { object[] methodParameters = this.methodParameterStringSerializer.Deserialize(request.Parameters); txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasPrice, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasLimit, addressNumeric, request.MethodName, methodParameters); } catch (MethodParameterStringSerializerException exception) { return(BuildCallContractTransactionResponse.Failed(exception.Message)); } } else { txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasPrice, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasLimit, addressNumeric, request.MethodName); } HdAddress senderAddress = null; if (!string.IsNullOrWhiteSpace(request.Sender)) { Features.Wallet.Wallet wallet = this.walletManager.GetWallet(request.WalletName); HdAccount account = wallet.GetAccount(request.AccountName); if (account == null) { return(BuildCallContractTransactionResponse.Failed($"No account with the name '{request.AccountName}' could be found.")); } senderAddress = account.GetCombinedAddresses().FirstOrDefault(x => x.Address == request.Sender); } ulong totalFee = (request.GasPrice * request.GasLimit) + Money.Parse(request.FeeAmount); var context = new TransactionBuildContext(this.network) { AccountReference = new WalletAccountReference(request.WalletName, request.AccountName), TransactionFee = totalFee, ChangeAddress = senderAddress, SelectedInputs = selectedInputs, MinConfirmations = MinConfirmationsAllChecks, WalletPassword = request.Password, Recipients = new[] { new Recipient { Amount = request.Amount, ScriptPubKey = new Script(this.callDataSerializer.Serialize(txData)) } }.ToList() }; try { Transaction transaction = this.walletTransactionHandler.BuildTransaction(context); return(BuildCallContractTransactionResponse.Succeeded(request.MethodName, transaction, context.TransactionFee)); } catch (Exception exception) { return(BuildCallContractTransactionResponse.Failed(exception.Message)); } }
public BuildCreateContractTransactionResponse BuildCreateTx(BuildCreateContractTransactionRequest request) { if (!this.CheckBalance(request.Sender)) { return(BuildCreateContractTransactionResponse.Failed(SenderNoBalanceError)); } List <OutPoint> selectedInputs = this.SelectInputs(request.WalletName, request.Sender, request.Outpoints); if (!selectedInputs.Any()) { return(BuildCreateContractTransactionResponse.Failed("Invalid list of request outpoints have been passed to the method. Please ensure that the outpoints are spendable by the sender address")); } ContractTxData txData; if (request.Parameters != null && request.Parameters.Any()) { try { object[] methodParameters = this.methodParameterStringSerializer.Deserialize(request.Parameters); txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasPrice, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasLimit, request.ContractCode.HexToByteArray(), methodParameters); } catch (MethodParameterStringSerializerException exception) { return(BuildCreateContractTransactionResponse.Failed(exception.Message)); } } else { txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasPrice, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasLimit, request.ContractCode.HexToByteArray()); } HdAddress senderAddress = null; if (!string.IsNullOrWhiteSpace(request.Sender)) { Features.Wallet.Wallet wallet = this.walletManager.GetWallet(request.WalletName); HdAccount account = wallet.GetAccount(request.AccountName); if (account == null) { return(BuildCreateContractTransactionResponse.Failed($"No account with the name '{request.AccountName}' could be found.")); } senderAddress = account.GetCombinedAddresses().FirstOrDefault(x => x.Address == request.Sender); } ulong totalFee = (request.GasPrice * request.GasLimit) + Money.Parse(request.FeeAmount); var walletAccountReference = new WalletAccountReference(request.WalletName, request.AccountName); byte[] serializedTxData = this.callDataSerializer.Serialize(txData); Result <ContractTxData> deserialized = this.callDataSerializer.Deserialize(serializedTxData); // We also want to ensure we're sending valid data: AKA it can be deserialized. if (deserialized.IsFailure) { return(BuildCreateContractTransactionResponse.Failed("Invalid data. If network requires code signing, check the code contains a signature.")); } var recipient = new Recipient { Amount = request.Amount ?? "0", ScriptPubKey = new Script(serializedTxData) }; var context = new TransactionBuildContext(this.network) { AccountReference = walletAccountReference, TransactionFee = totalFee, ChangeAddress = senderAddress, SelectedInputs = selectedInputs, MinConfirmations = MinConfirmationsAllChecks, WalletPassword = request.Password, Recipients = new[] { recipient }.ToList() }; try { Transaction transaction = this.walletTransactionHandler.BuildTransaction(context); uint160 contractAddress = this.addressGenerator.GenerateAddress(transaction.GetHash(), 0); return(BuildCreateContractTransactionResponse.Succeeded(transaction, context.TransactionFee, contractAddress.ToBase58Address(this.network))); } catch (Exception exception) { return(BuildCreateContractTransactionResponse.Failed(exception.Message)); } }
public BuildContractTransactionResult BuildTx(BuildContractTransactionRequest request) { Features.Wallet.Wallet wallet = this.walletManager.GetWallet(request.WalletName); HdAccount account = wallet.GetAccount(request.AccountName); if (account == null) { return(BuildContractTransactionResult.Failure(AccountNotInWalletError, $"No account with the name '{request.AccountName}' could be found.")); } HdAddress senderAddress = account.GetCombinedAddresses().FirstOrDefault(x => x.Address == request.Sender); if (senderAddress == null) { return(BuildContractTransactionResult.Failure(SenderNotInWalletError, $"The given address {request.Sender} was not found in the wallet.")); } if (!this.CheckBalance(senderAddress.Address)) { return(BuildContractTransactionResult.Failure(InsufficientBalanceError, SenderNoBalanceError)); } (List <OutPoint> selectedInputs, string message) = SelectInputs(request.WalletName, request.Sender, request.Outpoints); if (!string.IsNullOrEmpty(message)) { return(BuildContractTransactionResult.Failure(InvalidOutpointsError, message)); } var recipients = new List <Recipient>(); foreach (RecipientModel recipientModel in request.Recipients) { var bitcoinAddress = BitcoinAddress.Create(recipientModel.DestinationAddress, this.network); // If it's a potential SC address, check if it's a contract. if (bitcoinAddress is BitcoinPubKeyAddress bitcoinPubKeyAddress) { var address = new uint160(bitcoinPubKeyAddress.Hash.ToBytes()); if (this.stateRoot.IsExist(address)) { return(BuildContractTransactionResult.Failure(TransferFundsToContractError, $"The recipient address {recipientModel.DestinationAddress} is a contract. Transferring funds directly to a contract is not supported.")); } } recipients.Add(new Recipient { ScriptPubKey = bitcoinAddress.ScriptPubKey, Amount = recipientModel.Amount }); } var context = new TransactionBuildContext(this.network) { AccountReference = new WalletAccountReference(request.WalletName, request.AccountName), TransactionFee = string.IsNullOrEmpty(request.FeeAmount) ? null : Money.Parse(request.FeeAmount), MinConfirmations = MinConfirmationsAllChecks, Shuffle = false, OpReturnData = request.OpReturnData, OpReturnAmount = string.IsNullOrEmpty(request.OpReturnAmount) ? null : Money.Parse(request.OpReturnAmount), WalletPassword = request.Password, SelectedInputs = selectedInputs, AllowOtherInputs = false, Recipients = recipients, ChangeAddress = senderAddress }; Transaction transaction = this.walletTransactionHandler.BuildTransaction(context); var model = new WalletBuildTransactionModel { Hex = transaction.ToHex(), Fee = context.TransactionFee, TransactionId = transaction.GetHash() }; return(BuildContractTransactionResult.Success(model)); }