/// <summary> Refund bitshares deposit. </summary> /// /// <remarks> Paul, 15/01/2015. </remarks> /// /// <param name="fromAccount"> from account. </param> /// <param name="deposit"> The deposit. </param> /// <param name="depositId"> Identifier for the deposit. </param> /// <param name="memo"> The memo. </param> protected virtual void RefundBitsharesDeposit(string fromAccount, ulong larimers, string depositId, string memo) { // make sure failures after this point don't result in multiple refunds MarkTransactionAsRefundedStart(depositId); BitsharesTransactionResponse response; decimal amount = m_asset.GetAmountFromLarimers(larimers); try { BitsharesAccount account = GetAccountFromLedger(fromAccount); response = m_bitshares.WalletTransfer(amount, m_asset.symbol, m_bitsharesAccount, fromAccount, memo); } catch (BitsharesRpcException) { BitsharesTransaction t = m_bitshares.BlockchainGetTransaction(depositId); // get the sender's address from the balance id BitsharesOperation op = t.trx.operations.First(o => o.type == BitsharesTransactionOp.withdraw_op_type); BitsharesBalanceRecord balance = m_bitshares.GetBalance(op.data.balance_id); string senderAddress = balance.condition.data.owner; response = m_bitshares.WalletTransferToAddress(amount, m_asset.symbol, m_bitsharesAccount, senderAddress, memo); } MarkTransactionAsRefundedEnd(depositId, response.record_id, amount, DaemonTransactionType.bitsharesRefund, memo); }
/// <summary> Bitshares account to bitcoin address. </summary> /// /// <remarks> Paul, 15/01/2015. </remarks> /// /// <param name="account"> The account. </param> /// /// <returns> A string. </returns> protected string BitsharesAccountToBitcoinAddress(BitsharesAccount account) { // turn that into a BTC address BitsharesPubKey pubKey = new BitsharesPubKey(account.active_key_history.Last().Values.Last()); return(pubKey.ToBitcoinAddress(true, m_addressByteType)); }
/// <summary> Sends the bitcoins to depositor. </summary> /// /// <remarks> Paul, 05/02/2015. </remarks> /// /// <exception cref="RefundBitsharesException"> Thrown when a Refund Bitshares error condition /// occurs. </exception> /// /// <param name="btcAddress"> The btc address. </param> /// <param name="trxId"> Identifier for the trx. </param> /// <param name="amount"> The amount. </param> /// <param name="asset"> The asset. </param> /// /// <returns> A string. </returns> protected virtual string SendBitcoinsToDepositor(string btcAddress, string trxId, ulong amount, BitsharesAsset asset, string depositAddress, MetaOrderType orderType, bool burnUia) { // make sure failures after this point dont result in multiple credits m_daemon.MarkDespositAsCreditedStart(trxId, depositAddress, m_market.symbol_pair, orderType); decimal bitAssetAmount = asset.GetAmountFromLarimers(amount); if (bitAssetAmount > m_market.bid_max) { throw new RefundBitsharesException("Over " + Numeric.SerialisedDecimal(m_market.bid_max) + " " + asset.symbol + "!"); } // get the BTC amount we need to transfer decimal btcNoFee; if (m_flipped) { // they're sending us bitAssets, not BTC because the market is flipped, this is // equivelent to the opposite order type, so we have to use ask here btcNoFee = bitAssetAmount / m_market.ask; } else { btcNoFee = bitAssetAmount * m_market.bid; } // when selling, the fee is charged in BTC, // the amount recorded in the transaction is the amount of bitAssets sans fee, obv decimal fee = (m_market.bid_fee_percent / 100) * btcNoFee; decimal btcTotal = Numeric.TruncateDecimal(btcNoFee - fee, 8); // do the transfer string txid = m_bitcoin.SendToAddress(btcAddress, btcTotal, "mX: " + orderType + " " + asset.symbol); // mark this in our records m_daemon.MarkDespositAsCreditedEnd(trxId, txid, MetaOrderStatus.completed, bitAssetAmount, m_market.bid, fee); if (burnUia) { // make sure we were the issuer for this asset before we start burning it! BitsharesAccount account = m_bitshares.WalletGetAccount(m_bitsharesAccount); if (asset.issuer_id == account.id) { m_bitshares.WalletBurn(bitAssetAmount, asset.symbol, m_bitsharesAccount, BurnForOrAgainst.@for, m_bitsharesAccount); } } return(txid); }
/// <summary> Sends bitAssets, either issue or transfer them </summary> /// /// <remarks> Paul, 18/03/2015. </remarks> /// /// <param name="amount"> The amount. </param> /// <param name="asset"> The asset. </param> /// <param name="sendTo"> to account. </param> /// /// <returns> A BitsharesTransactionResponse. </returns> protected BitsharesTransactionResponse SendBitAssets(decimal amount, BitsharesAsset asset, string sendTo, string memo = "", bool allowIssue = true) { BitsharesAccount account = m_bitshares.WalletGetAccount(m_bitsharesAccount); if (asset.issuer_id == account.id && allowIssue) { // issue it return(m_bitshares.WalletUiaIssue(amount, asset.symbol, sendTo, memo)); } else { // transfer it return(m_bitshares.WalletTransfer(amount, asset.symbol, m_bitsharesAccount, sendTo, memo)); } }
/// <summary> This is virtual because implementors might like a different way </summary> /// /// <remarks> Paul, 16/12/2014. </remarks> /// /// <param name="account"> The account. </param> /// /// <returns> A string. </returns> /*protected virtual string BitsharesTransactionToBitcoinAddress(BitsharesLedgerEntry l) * { * // THIS CANNOT WORK DUE TO MEMO SIZE = 19 bytes!!!!!!! * * * // expect the BTC address to be inside the memo somewhere * string[] memo = l.memo.Split(' '); * AddressBase address = null; * foreach (string s in memo) * { * try * { * address = new AddressBase(s); * break; * } * catch (ArgumentException){} * } * * if (address == null) * { * throw new RefundBitsharesException("Unable to find desired bitcoin address in transction memo!"); * } * * return address.AddressBase58; * }*/ /// <summary> Bitshares transaction to bitcoin address. </summary> /// /// <remarks> Paul, 25/01/2015. </remarks> /// /// <exception cref="RefundBitsharesException"> Thrown when a Refund Bitshares error condition /// occurs. </exception> /// /// <param name="l"> The BitsharesLedgerEntry to process. </param> /// <param name="t"> The TransactionSinceBlock to process. </param> /// /// <returns> A string. </returns> protected virtual string BitsharesTransactionToBitcoinAddress(BitsharesLedgerEntry l, BitsharesTransaction t) { try { // get the public key of the sender BitsharesAccount account = GetAccountFromLedger(l.from_account); // turn into into a bitshares address return(BitsharesAccountToBitcoinAddress(account)); } catch (BitsharesRpcException) { throw new RefundBitsharesException("Unregistered acct!"); } }
/// <summary> Bitshares account to bitcoin address. </summary> /// /// <remarks> Paul, 15/01/2015. </remarks> /// /// <param name="account"> The account. </param> /// /// <returns> A string. </returns> protected string BitsharesAccountToBitcoinAddress(BitsharesAccount account) { // turn that into a BTC address BitsharesPubKey pubKey = new BitsharesPubKey(account.active_key_history.Last().Values.Last()); return pubKey.ToBitcoinAddress(true, m_addressByteType); }
/// <summary> Executes the submit address action. </summary> /// /// <remarks> Paul, 05/02/2015. </remarks> /// /// <exception cref="ApiExceptionMessage"> Thrown when an API exception message error /// condition occurs. </exception> /// <exception cref="UnexpectedCaseException"> Thrown when an Unexpected Case error condition /// occurs. </exception> /// /// <param name="receivingAddress"> The receiving address. </param> /// <param name="orderType"> Type of the order. </param> /// /// <returns> A SubmitAddressResponse. </returns> public override SubmitAddressResponse OnSubmitAddress(string receivingAddress, MetaOrderType orderType, uint referralUser) { SubmitAddressResponse response; if (orderType == MetaOrderType.buy) { string accountName = receivingAddress; bool isPublicKey = BitsharesPubKey.IsValidPublicKey(accountName); // check for theoretical validity if (!isPublicKey && !BitsharesWallet.IsValidAccountName(accountName)) { throw new ApiExceptionInvalidAccount(accountName); } // try and retrieve a previous entry SenderToDepositRow senderToDeposit = m_daemon.GetSenderDepositFromReceiver(accountName, m_market.symbol_pair, referralUser); if (senderToDeposit == null) { // no dice, create a new entry // check for actual validity string rcA; if (!isPublicKey) { BitsharesAccount account = m_bitshares.GetAccount(accountName); if (account == null) { throw new ApiExceptionInvalidAccount(accountName); } rcA = account.name; } else { rcA = accountName; } // generate a new bitcoin address and tie it to this account string depositAdress = m_bitcoin.GetNewAddress(); senderToDeposit = m_daemon.InsertSenderToDeposit(rcA, depositAdress, m_market.symbol_pair, referralUser); } response = new SubmitAddressResponse { deposit_address = senderToDeposit.deposit_address, receiving_address = senderToDeposit.receiving_address }; } else if (orderType == MetaOrderType.sell) { string bitcoinAddress = receivingAddress; // validate bitcoin address byte[] check = Util.Base58CheckToByteArray(bitcoinAddress); if (check == null) { throw new ApiExceptionInvalidAddress(bitcoinAddress); } // try and retrieve a previous entry SenderToDepositRow senderToDeposit = m_daemon.GetSenderDepositFromReceiver(bitcoinAddress, m_market.symbol_pair, referralUser); if (senderToDeposit == null) { // generate a memo field to use instead senderToDeposit = m_daemon.InsertSenderToDeposit(bitcoinAddress, MarketBase.CreateMemo(bitcoinAddress, m_market.symbol_pair, referralUser), m_market.symbol_pair, referralUser); } response = new SubmitAddressResponse { deposit_address = m_bitsharesAccount, receiving_address = senderToDeposit.receiving_address, memo = senderToDeposit.deposit_address }; } else { throw new UnexpectedCaseException(); } return(response); }
/// <summary> Calculates the market prices and limits. </summary> /// /// <remarks> Paul, 05/02/2015. </remarks> /// /// <param name="market"> [in,out] The market. </param> /// <param name="bitsharesBalances"> The bitshares balances. </param> /// <param name="bitcoinBalance"> The bitcoin balance. </param> public override void ComputeMarketPricesAndLimits(ref MarketRow market, Dictionary <int, ulong> bitsharesBalances, decimal bitcoinBalance) { base.ComputeMarketPricesAndLimits(ref market, bitsharesBalances, bitcoinBalance); decimal baseBalance = 0; decimal quoteBalance = bitcoinBalance; if (bitsharesBalances.ContainsKey(m_asset.id)) { // only non zero balances return data, so this guard is necessary baseBalance = m_asset.GetAmountFromLarimers(bitsharesBalances[m_asset.id]); } decimal maxTransactionFactor; if (m_currency.uia) { // with UIA we got to handle the maximum buy size differently BitsharesAccount account = m_bitshares.WalletGetAccount(m_bitsharesAccount); if (m_asset.issuer_id == account.id) { // we are the issuer! // refresh the asset m_asset = m_bitshares.BlockchainGetAsset(m_asset.symbol); // this is how much we can issue, so lets stick that in there baseBalance = m_asset.GetAmountFromLarimers(m_asset.max_supply - m_asset.current_supply); } else { throw new UnexpectedCaseException(); } maxTransactionFactor = 1; //maxTransactionFactor = kMaxTransactionFactor; } else { maxTransactionFactor = kMaxTransactionFactor; } decimal newAskMax, newBidMax; // askMax is in BITCOINS // bidMax is in BITASSETS if (m_flipped) { // BTC_bitUSD // baseBalance = 10 bitUSD // ask = 240 // askMax = 10 / 240 = 0.04 BTC newAskMax = Numeric.TruncateDecimal((baseBalance / m_market.ask) * maxTransactionFactor, 8); newBidMax = Numeric.TruncateDecimal((quoteBalance * m_market.bid) * maxTransactionFactor, 8); } else { // BTS_BTC // // baseBalance = 1 BTS // ask = 0.00004 // askMax = 1 * 0.0004 = 0.0004 BTC newAskMax = Numeric.TruncateDecimal((baseBalance * m_market.ask) * maxTransactionFactor, 8); newBidMax = Numeric.TruncateDecimal((quoteBalance / m_market.bid) * maxTransactionFactor, 8); } m_isDirty |= newAskMax != m_market.ask_max || newBidMax != m_market.bid_max; market.ask_max = newAskMax; market.bid_max = newBidMax; if (m_market.price_discovery) { // // update price discovery engine // decimal bitsharesBalance = m_asset.GetAmountFromLarimers(bitsharesBalances[m_asset.id]); if (m_asset.symbol == CurrencyHelpers.kBtcSymbol) { m_lastFeedPrice = 1; } else { m_lastFeedPrice = RecomputeFeedPriceInBtc(); } decimal inventoryRatio = ComputeInventoryRatio(bitsharesBalances, bitcoinBalance); if (m_prices == null) { // // initialise the price discovery engine // m_prices = new PriceDiscovery(market.spread_percent, market.window_percent, m_lastFeedPrice, inventoryRatio); } decimal oldBid = m_market.bid; decimal oldAsk = m_market.ask; m_prices.UpdateParameters(m_lastFeedPrice, inventoryRatio, m_market.spread_percent, m_market.window_percent, out m_market.bid, out m_market.ask); m_isDirty |= oldBid != m_market.bid || oldAsk != m_market.ask; } }
/// <summary> Executes the submit address action. </summary> /// /// <remarks> Paul, 25/01/2015. </remarks> /// /// <exception cref="ApiExceptionMessage"> Thrown when an API exception message error /// condition occurs. </exception> /// <exception cref="ApiExceptionMissingParameter"> Thrown when an API exception missing /// parameter error condition occurs. </exception> /// /// <param name="ctx"> The context. </param> /// <param name="dummy"> The dummy. </param> /// /// <returns> A Task. </returns> Task OnSubmitAddress(RequestContext ctx, IDummy dummy) { CurrencyTypes fromCurrency = RestHelpers.GetPostArg <CurrencyTypes, ApiExceptionMissingParameter>(ctx, WebForms.kFromCurrency); CurrencyTypes toCurrency = RestHelpers.GetPostArg <CurrencyTypes, ApiExceptionMissingParameter>(ctx, WebForms.kToCurrency); string receivingAddress = RestHelpers.GetPostArg <string, ApiExceptionMissingParameter>(ctx, WebForms.kReceivingAddress); SubmitAddressResponse response; if (fromCurrency == CurrencyTypes.BTC && toCurrency == CurrencyTypes.bitBTC) { string accountName = receivingAddress; // try and retrieve a previous entry SenderToDepositRow senderToDeposit = m_database.Query <SenderToDepositRow>("SELECT * FROM sender_to_deposit WHERE receiving_address=@s;", accountName).FirstOrDefault(); if (senderToDeposit == null || !BitsharesWallet.IsValidAccountName(accountName)) { // no dice, create a new entry // validate bitshares account name try { BitsharesAccount account = m_bitshares.WalletGetAccount(accountName); // generate a new bitcoin address and tie it to this account string depositAdress = m_bitcoin.GetNewAddress(); senderToDeposit = InsertSenderToDeposit(account.name, depositAdress); } catch (BitsharesRpcException) { throw new ApiExceptionMessage(accountName + " is not an existing account! Are you sure it is registered?"); } } response = new SubmitAddressResponse { deposit_address = senderToDeposit.deposit_address }; } else if (fromCurrency == CurrencyTypes.bitBTC && toCurrency == CurrencyTypes.BTC) { string bitcoinAddress = receivingAddress; // try and retrieve a previous entry SenderToDepositRow senderToDeposit = m_database.Query <SenderToDepositRow>("SELECT * FROM sender_to_deposit WHERE receiving_address=@s;", bitcoinAddress).FirstOrDefault(); if (senderToDeposit == null) { // validate bitcoin address byte[] check = Util.Base58CheckToByteArray(bitcoinAddress); if (check == null) { throw new ApiExceptionMessage(bitcoinAddress + " is not a valid bitcoin address!"); } // generate a memo field to use instead string start = "meta-"; string memo = start + bitcoinAddress.Substring(0, BitsharesWallet.kBitsharesMaxMemoLength - start.Length); senderToDeposit = InsertSenderToDeposit(bitcoinAddress, memo); } response = new SubmitAddressResponse { deposit_address = m_bitsharesAccount, memo = senderToDeposit.deposit_address }; } else { throw new ApiExceptionUnsupportedTrade(fromCurrency, toCurrency); } ctx.Respond <SubmitAddressResponse>(response); return(null); }