Ejemplo n.º 1
0
        public void Can_getNewAddress()
        {
            string  apiKey  = "bb7c-fc4a-42a2-cf6e";
            BlockIO blockio = new BlockIO(apiKey);

            JSONAPI json = blockio.GetNewAddress();

            Assert.AreEqual("success", json.Status);
        }
Ejemplo n.º 2
0
        public void Can_getTransactionsReceived()
        {
            string  apiKey  = "bb7c-fc4a-42a2-cf6e";
            BlockIO blockio = new BlockIO(apiKey);

            JSONAPI json = blockio.GetTransactions("received");

            Assert.AreEqual("success", json.Status);
        }
Ejemplo n.º 3
0
        // ask block.io for the Addresses (really we care about the Labels) for a given apikey, and update the UI if successful (this is also how we detect the cointype)
        private bool tryGetAddresses(string apikey)
        {
            try
            {
                BlockIO     client = new BlockIO(apikey);
                APIResponse r      = client.getMyAddresses();

                string net = (string)r.Data["network"];
                currentNet = net;

                // reload the Fractions dropdown to follow the potential change in coin network
                reloadFractions();

                // update the text on the "add tx fee" checkbox, again to follow potential network change
                updateFeeAddCheckbox();

                lblCoinType.Text = string.Format("Using coin type: {0}", net);

                // gather list of addresses-labels-values and put in to a nice dropdown list (replacing current silly "label" textbox)
                ArrayList addresses = (ArrayList)r.Data["addresses"];

                // devnote: store the data properly structured internally (make a class, store as array-of-class), then build display strings for the DDL
                // and in a SelectedIndexChanged handler for that new DDL, update a new Label to show available balance

                labels = new List <LabelAddress>();

                foreach (Dictionary <string, object> a in addresses)
                {
                    LabelAddress la = new LabelAddress();
                    la.net     = net;
                    la.label   = (string)a["label"];
                    la.balance = decimal.Parse((string)a["available_balance"]);

                    labels.Add(la);
                }

                ddlTakeFromLabel.DataSource    = labels;
                ddlTakeFromLabel.SelectedIndex = 0;
                //reloadLabels();
            }
            catch (Exception ex)
            {
                // that didn't work...
                string errmsg = string.Format("Error when asking Block.Io for Address/Label List.\nInternal Error message is:\n{0}", ex.Message);
                MessageBox.Show(errmsg, "Error getting Label List", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return(false);
            }

            return(true);
        }
Ejemplo n.º 4
0
        private void btnGo_Click(object sender, EventArgs e)
        {
            BlockIO client = new BlockIO(txtApiKey.Text.Trim());

            decimal TargetBalance;

            bool includePending = chkIncludePendingBalance.Checked;

            // basic input validations
            if (!decimal.TryParse(txtTargetBalance.Text, out TargetBalance))
            {
                MessageBox.Show("Target Balance must be a valid number", "Error: Target Balance", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            if (TargetBalance <= 0)
            {
                MessageBox.Show("Target Balance must be greater than zero", "Error: Target Balance", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            List <string> addresses = txtInputAddresses.Lines.Where(L => L.Trim() != "").ToList();

            lblSummaryInfo.Text = "... Working ...";


            block_io_sharp.APIResponse response;

            // ask block.io for the balances of all these addresses, in a slightly non-crashy manner
            try
            {
                response = client.getAddressBalance("addresses", addresses);
            }
            catch (Exception ex)
            {
                lblSummaryInfo.Text = "Request failed.";
                return;
            }

            string network = (string)response.Data["network"];

            decimal totalBalanceAvail = decimal.Parse((string)response.Data["available_balance"]);
            decimal totalPending      = decimal.Parse((string)response.Data["pending_received_balance"]);

            List <string> addressesExact = new List <string>(); // addresses with exactly the desired balance
            List <string> addressesZero  = new List <string>(); // addresses with an exactly-zero balance
            List <string> addressesLow   = new List <string>(); // addresses with a non-zero but under-spec balance
            List <string> addressesOver  = new List <string>(); // addresses with a balance over the target amount

            // loop round each balance response received and stick it into the appropriate list
            foreach (Dictionary <string, object> b in (ArrayList)response.Data["balances"])
            {
                Decimal balance = decimal.Parse((string)b["available_balance"]);
                Decimal pending = decimal.Parse((string)b["pending_received_balance"]);;

                if (includePending)
                {
                    balance += pending;
                }

                string address = b["address"].ToString();

                if (balance == TargetBalance)
                {
                    addressesExact.Add(address);
                }
                else if (balance == 0)
                {
                    addressesZero.Add(address);
                }
                else if (balance < TargetBalance)
                {
                    addressesLow.Add(address + " (" + balance.ToString() + ")");
                }
                else if (balance > TargetBalance)
                {
                    addressesOver.Add(address + " (" + balance.ToString() + ")");
                }
            }

            // output the single-line summary
            lblSummaryInfo.Text = string.Format(
                "Coin: {0}, Total Value Available {1}, Total Value Pending {2}. Of {3} Addresses, found {4} with Zero, {5} with Under, {6} with Exact and {7} with Over",
                network,
                totalBalanceAvail,
                totalPending,
                addresses.Count,
                addressesZero.Count,
                addressesLow.Count,
                addressesExact.Count,
                addressesOver.Count);

            // and output the full lists
            txtInputAddresses.Lines = addresses.ToArray();

            txtResultsZero.Lines = addressesZero.ToArray();
            txtResultsLow.Lines  = addressesLow.ToArray();
            txtResultsOK.Lines   = addressesExact.ToArray();
            txtResultsOver.Lines = addressesOver.ToArray();
        }
Ejemplo n.º 5
0
        // actually do the work
        private void btnSend_Click(object sender, EventArgs e)
        {
            // validate API Key
            string apikey = txtApiKey.Text.Trim();

            if (!isValidApiKey(apikey))
            {
                lblApiKeyInvalidFormat.Visible = true;

                MessageBox.Show("API Key is required and must be in expected format (like '0123-4567-890a-bcde')", "Invalid API Key format", MessageBoxButtons.OK, MessageBoxIcon.Error);

                return;
            }

            // load in address-list, check we don't exceed 100
            List <string> addresses = txtAddresses.Lines.Where(L => L.Trim() != "").ToList();

            if (addresses.Count > 100)
            {
                MessageBox.Show(String.Format("Can't send to {0} addresses in one shot, Block.io's maximum is 100", addresses.Count), "Too many Addresses", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            decimal perAddressValue;

            // check the send amount is sensible (a valid number, and greater than zero)
            if (decimal.TryParse(txtSendAmount.Text, out perAddressValue))
            {
                if (perAddressValue <= 0)
                {
                    MessageBox.Show("Send Amount must be greater than zero!", "Invalid Send Amount", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
            }
            else
            {
                MessageBox.Show("Send Amount field is not a valid number", "Invalid Send Amount", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            // a block.io "Label" is required, to be the source of the coins
            string sourceLabel = ((LabelAddress)ddlTakeFromLabel.SelectedValue).label; //txtFromLabels.Text.Trim();

            if (sourceLabel.Length < 1)
            {
                MessageBox.Show("Take From Label must be provided", "Missing 'Take From Label'", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            // the block.io Secret Pin is necessary too
            string secretPin = txtSecretPin.Text.Trim();

            if (secretPin == "")
            {
                MessageBox.Show("Secret PIN must be provided", "Missing 'Secret PIN'", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            perAddressValue = currentFraction.toWholeCoin(perAddressValue);

            // calculate the absolute minimal cost of this operation (Minimal here means 'just the value of the coins, not including any Fees')
            decimal projectedCostMinimal = perAddressValue * addresses.Count;

            // create an instance of the BlockIO API Client, using their library code
            BlockIO client = new BlockIO(txtApiKey.Text.Trim());

            decimal AvailableBalance;
            string  net;

            // look up the available Balance of the specified Label, which will also tell us which Coin (BTC/LTC/DOGE) is being used

            APIResponse response;

            try
            {
                response = client.getAddressByLabel(sourceLabel);
            }
            catch (Exception ex)
            {
                // that didn't work...
                string errmsg = string.Format("Error when asking Block.Io for Label Balance.\nInternal Error message is:\n{0}", ex.Message);
                MessageBox.Show(errmsg, "Error when getting Label Balance", MessageBoxButtons.OK, MessageBoxIcon.Error);

                // bail out of this Function entirely (give up the process)
                return;
            }

            AvailableBalance = decimal.Parse((string)response.Data["available_balance"]);
            net = (string)response.Data["network"];
            string networkName;

            if (net != null)
            {
                networkName = net;
            }
            else
            {
                networkName = "[unknown]";
            }

            // now we know the network in use, we can optionally bump the per-wallet amount and recalculate the projected cost (if user has chosen the "Include Network Fee" option)
            if (chkIncludeWithdrawFee.Checked)
            {
                decimal feeAdd = 0;

                // select fee-included amount based on the coin
                // these are currently hard-coded (erring on the side of being a little over-generous to try to ensure the resultant wallets can be redeemed reasonably quickly and net the recipient the intended amount after fee-adding)

                // a future version may see these turned in to user-facing settings. for now they are hard-coded
                // user can choose to not use this option and adjust the main send amount value instead, if they wish to get finer control

                feeAdd = pickTxFeeBonus(net);

                perAddressValue     += feeAdd;
                projectedCostMinimal = perAddressValue * addresses.Count;
            }

            // compare Label Balance against projected cost
            if (AvailableBalance < projectedCostMinimal)
            {
                MessageBox.Show(string.Format("Not enough funds in selected Account Label - you have {0}, but I think you need at least {1}", currentFraction.formatFraction(AvailableBalance), currentFraction.formatFraction(projectedCostMinimal)), "Insufficient Funds", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }


            // build the list of send-values
            // right now, we only send the same amount to each address
            List <string> sendValues = Enumerable.Repeat(perAddressValue.ToString(), addresses.Count).ToList();

            // estimate the network fee on the intended transaction
            response = client.getNetworkFeeEstimate(sendValues, addresses);

            decimal estFee = decimal.Parse((string)response.Data["estimated_network_fee"]);

            // ensure that the per-wallet amount is a sensible amount - I don't want this app to be used to create Dust Transactions
            // The amount sent to each recipient address must be at least a certain multiple of the Total Fee estimated to send the entire initial transaction.
            // The ratio below is totally artificial and I just pulled it out of thin air. If you REALLY want to send smaller values than I'm allowing, you can just change the value in the next line
            int feeToValueMinRatio = 5;

            if (perAddressValue < (estFee * feeToValueMinRatio))
            {
                MessageBox.Show("This app declines to send your transaction, because your Per-Wallet value is too small compared to the Network Fee about to be incurred. Please increase your Amount To Send, or decrease your number of target wallets. (Note: this is NOT a Block.Io limitation)", "Wallet Value too small", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            // final balance check, now I know what the Estimated Send Fee is...
            decimal finalEstimate = projectedCostMinimal + estFee;

            if (AvailableBalance < finalEstimate)
            {
                MessageBox.Show(string.Format("Not enough funds in nominated Block.Io Label - you have {0}, but after calculating TX Fee you need at least {1}", currentFraction.formatFraction(AvailableBalance), currentFraction.formatFraction(finalEstimate)), "Insufficient Funds", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            // show a final YES/NO confirmation dialog now we've done the pre-flight tests
            if (MessageBox.Show(String.Format("Sure you want to send {0} to each of {1} addresses, with a TX Fee of {2}, total spend {3}?", currentFraction.formatFraction(perAddressValue), addresses.Count, currentFraction.formatFraction(estFee), currentFraction.formatFraction(finalEstimate)), "Final Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.No)
            {
                // they chose not to proceed
                return;
            }

            List <string> labelsList = new List <string>();

            labelsList.Add(sourceLabel);

            // Ask Block.io to perform the intended transaction. Note: This is NOT using client-side tx signing.
            try
            {
                response = client.withdrawFromLabels(labelsList, sendValues, secretPin, addresses);

                MessageBox.Show(string.Format("Amount Sent:{0}, Amount Withdrawn:{1}, Block.IO Fee:{2}, Network Fee:{3}, TX ID:{4}", response.Data["amount_sent"], response.Data["amount_withdrawn"], response.Data["blockio_fee"], response.Data["network_fee"], response.Data["txid"]), "Transaction Completed OK", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                // that didn't work...
                string errmsg = string.Format("Error when asking Block.Io to Send the Transaction - please check your block.io account before retrying, just in case the transaction WAS actually sent.\nInternal Error Message is:\n{0}", ex.Message);
                MessageBox.Show(errmsg, "Error when Sending Transaction", MessageBoxButtons.OK, MessageBoxIcon.Error);

                // bail out of this Function entirely (give up the process)
                return;
            }
        }