/// <summary> Constructor. </summary> /// /// <remarks> Paul, 05/02/2015. </remarks> /// /// <param name="uid"> The UID. </param> /// <param name="base"> The base. </param> /// <param name="quote"> The quote. </param> /// <param name="bitshares"> The bitshares. </param> /// <param name="bitcoin"> The bitcoin. </param> /// <param name="bitsharesAccount"> The bitshares account. </param> public InternalMarket(MetaDaemonApi daemon, MarketRow market, BitsharesWallet bitshares, BitcoinWallet bitcoin, string bitsharesAccount, CurrenciesRow bitsharesAsset) : base(daemon, market, bitshares, bitcoin, bitsharesAccount) { m_currency = bitsharesAsset; m_flipped = m_market.GetBase(daemon.m_AllCurrencies) != bitsharesAsset; m_asset = m_bitshares.BlockchainGetAsset(CurrencyHelpers.ToBitsharesSymbol(bitsharesAsset)); Dictionary <int, ulong> allBitsharesBalances = m_bitshares.WalletAccountBalance(bitsharesAccount)[bitsharesAccount]; decimal bitcoinBalance = bitcoin.GetBalance(); ComputeMarketPricesAndLimits(ref m_market, allBitsharesBalances, bitcoinBalance); }
/// <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> Constructor. </summary> /// /// <remarks> Paul, 17/01/2015. </remarks> /// /// <param name="bitsharesConfig"> The bitshares configuration. </param> /// <param name="bitcoinConfig"> The bitcoin configuration. </param> /// <param name="bitsharesAccount"> The bitshares account. </param> /// <param name="bitsharesAsset"> The bitshares asset. </param> /// <param name="bitcoinDespositAddress"> The bitcoin desposit address. </param> public DaemonBase( RpcConfig bitsharesConfig, RpcConfig bitcoinConfig, string bitsharesAccount, string bitsharesAsset, string bitcoinDespositAddress) { m_bitshares = new BitsharesWallet(bitsharesConfig.m_url, bitsharesConfig.m_rpcUser, bitsharesConfig.m_rpcPassword); m_bitcoin = new BitcoinWallet(bitcoinConfig.m_url, bitcoinConfig.m_rpcUser, bitcoinConfig.m_rpcPassword, false); m_bitsharesAccount = bitsharesAccount; m_bitsharesAsset = bitsharesAsset; m_bitcoinDespoitAddress = bitcoinDespositAddress; m_asset = m_bitshares.BlockchainGetAsset(bitsharesAsset); m_addressByteType = (byte)(bitcoinConfig.m_useTestnet ? AltCoinAddressTypeBytes.BitcoinTestnet : AltCoinAddressTypeBytes.Bitcoin); }
/// <summary> Constructor. </summary> /// /// <remarks> Paul, 17/01/2015. </remarks> /// /// <param name="bitsharesConfig"> The bitshares configuration. </param> /// <param name="bitcoinConfig"> The bitcoin configuration. </param> /// <param name="bitsharesAccount"> The bitshares account. </param> /// <param name="bitsharesAsset"> The bitshares asset. </param> /// <param name="bitcoinDespositAddress"> The bitcoin desposit address. </param> public DaemonBase(RpcConfig bitsharesConfig, RpcConfig bitcoinConfig, string bitsharesAccount, string bitsharesAsset, string bitcoinDespositAddress) { m_bitshares = new BitsharesWallet(bitsharesConfig.m_url, bitsharesConfig.m_rpcUser, bitsharesConfig.m_rpcPassword); m_bitcoin = new BitcoinWallet(bitcoinConfig.m_url, bitcoinConfig.m_rpcUser, bitcoinConfig.m_rpcPassword, false); m_bitsharesAccount = bitsharesAccount; m_bitsharesAsset = bitsharesAsset; m_bitcoinDespoitAddress = bitcoinDespositAddress; m_asset = m_bitshares.BlockchainGetAsset(bitsharesAsset); m_addressByteType = (byte)(bitcoinConfig.m_useTestnet ? AltCoinAddressTypeBytes.BitcoinTestnet : AltCoinAddressTypeBytes.Bitcoin); }
/// <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> Refund bitshares deposit. </summary> /// /// <remarks> Paul, 05/02/2015. </remarks> /// /// <param name="fromAccount"> from account. </param> /// <param name="larimers"> The larimers. </param> /// <param name="depositId"> Identifier for the deposit. </param> /// <param name="memo"> The memo. </param> /// <param name="asset"> The asset. </param> protected void RefundBitsharesDeposit(string fromAccount, ulong larimers, string depositId, string memo, BitsharesAsset asset, string depositAddress, MetaOrderType orderType) { decimal amount = asset.GetAmountFromLarimers(larimers); // make sure failures after this point don't result in multiple refunds m_daemon.MarkTransactionAsRefundedStart(depositId, depositAddress, m_market.symbol_pair, orderType); BitsharesTransactionResponse response = SendBitAssets(amount, asset, fromAccount, memo, false); //BitsharesTransactionResponse response = m_bitshares.WalletTransfer(amount, asset.symbol, m_bitsharesAccount, fromAccount, memo); m_daemon.MarkTransactionAsRefundedEnd(depositId, response.record_id, MetaOrderStatus.refunded, amount, memo); }
/// <summary> Sends a bit assets to depositor. </summary> /// /// <remarks> Paul, 05/02/2015. </remarks> /// /// <exception cref="RefundBitcoinException"> Thrown when a Refund Bitcoin error condition /// occurs. </exception> /// /// <param name="t"> The TransactionSinceBlock to process. </param> /// <param name="asset"> The asset. </param> /// /// <returns> A BitsharesTransactionResponse. </returns> protected BitsharesTransactionResponse SendBitAssetsToDepositor(TransactionSinceBlock t, BitsharesAsset asset, SenderToDepositRow s2d, MetaOrderType orderType) { // make sure failures after this point do not result in repeated sending m_daemon.MarkDespositAsCreditedStart(t.TxId, s2d.deposit_address, m_market.symbol_pair, orderType, MetaOrderStatus.processing, TransactionPolicy.REPLACE); if (t.Amount > m_market.ask_max) { throw new RefundBitcoinException("Over " + Numeric.SerialisedDecimal(m_market.ask_max) + " " + asset.symbol + "!"); } string bitsharesAccount = s2d.receiving_address; decimal bitAssetAmountNoFee; if (m_flipped) { // they're sending us BTC, not bitAssets because the market is flipped, this is // equivelent to the opposite order type, so we have to use bid here bitAssetAmountNoFee = t.Amount * m_market.bid; } else { bitAssetAmountNoFee = t.Amount / m_market.ask; } // when buying, the fee is charged in bitAssets, // the amount recorded in the transaction is the amount of bitAssets purchased sans fee bitAssetAmountNoFee = asset.Truncate(bitAssetAmountNoFee); decimal fee = (m_market.ask_fee_percent / 100) * bitAssetAmountNoFee; decimal amountAsset = bitAssetAmountNoFee - fee; amountAsset = asset.Truncate(amountAsset); BitsharesTransactionResponse bitsharesTrx = SendBitAssets(amountAsset, asset, bitsharesAccount, "mX: " + orderType + " " + asset.symbol); m_daemon.MarkDespositAsCreditedEnd(t.TxId, bitsharesTrx.record_id, MetaOrderStatus.completed, bitAssetAmountNoFee, m_market.ask, fee); return(bitsharesTrx); }
/// <summary> Updates this object. </summary> /// /// <remarks> Paul, 05/02/2015. </remarks> async public override void Update() { try { // // don't process transactions if the network is in danger // GetInfoResponse info = m_bitshares.GetInfo(); m_suspended = info.blockchain_average_delegate_participation < kMinDelegateParticipation; if (!m_suspended) { Dictionary <string, MarketRow> allMarkets = GetAllMarkets().ToDictionary(m => m.symbol_pair); m_allCurrencies = m_dataAccess.GetAllCurrencies(); // create any handlers we need for new markets CheckMarketHandlers(allMarkets); // get all markets RecomputeTransactionLimitsAndPrices(allMarkets); // // handle bitshares->bitcoin // Dictionary <string, BitsharesLedgerEntry> bitsharesDeposits = HandleBitsharesDesposits(); // // handle bitcoin->bitshares // List <TransactionSinceBlock> bitcoinDeposits = HandleBitcoinDeposits(); // // process bitshares deposits // uint siteLastTid = m_dataAccess.GetSiteLastTransactionUid(); foreach (KeyValuePair <string, BitsharesLedgerEntry> kvpDeposit in bitsharesDeposits) { // figure out which market each deposit belongs to foreach (KeyValuePair <string, MarketBase> kvpHandler in m_marketHandlers) { BitsharesLedgerEntry l = kvpDeposit.Value; MarketRow m = allMarkets[kvpHandler.Key]; BitsharesAsset depositAsset = m_allBitsharesAssets[l.amount.asset_id]; if (!HandleCommand(l, kvpHandler.Value, m, kvpDeposit.Key)) { if (IsDepositForMarket(l.memo, m.symbol_pair)) { // make sure the deposit is for this market! if (kvpHandler.Value.CanDepositAsset(CurrencyHelpers.FromBitsharesSymbol(depositAsset.symbol, m_allCurrencies, depositAsset.IsUia()))) { kvpHandler.Value.HandleBitsharesDeposit(kvpDeposit); } } } } // this needs to happen for every transaction RecomputeTransactionLimitsAndPrices(allMarkets); } // // process bitcoin deposits // List <TransactionsRow> pendingTransactions = m_dataAccess.GetAllPendingTransactions(); foreach (TransactionSinceBlock deposit in bitcoinDeposits) { // figure out which market each deposit belongs to foreach (KeyValuePair <string, MarketBase> kvpHandler in m_marketHandlers) { if (IsDepositForMarket(deposit.Address, allMarkets[kvpHandler.Key].symbol_pair)) { kvpHandler.Value.HandleBitcoinDeposit(deposit); } } // this needs to happen for every transaction RecomputeTransactionLimitsAndPrices(allMarkets); } // // handle changes in transaction status // List <TransactionsRow> updatedTrans = new List <TransactionsRow>(); foreach (TransactionsRow pending in pendingTransactions) { TransactionsRow updated = m_dataAccess.GetTransaction(pending.received_txid); if (updated.status != MetaOrderStatus.pending) { updatedTrans.Add(updated); } } // // push any new transactions, make sure site acknowledges receipt // uint latestTid = m_dataAccess.GetLastTransactionUid(); if (latestTid > siteLastTid || updatedTrans.Count > 0) { List <TransactionsRow> newTrans = m_dataAccess.GetAllTransactionsSince(siteLastTid); // lump them together newTrans.AddRange(updatedTrans); // send 'em all string result = await ApiPush <List <TransactionsRow> >(Routes.kPushTransactions, newTrans); if (bool.Parse(result)) { m_dataAccess.UpdateSiteLastTransactionUid(latestTid); } else { throw new Exception("API push response unknown! " + result); } } // // push market updates // foreach (KeyValuePair <string, MarketBase> kvpHandler in m_marketHandlers) { if (kvpHandler.Value.m_IsDirty) { m_dataAccess.UpdateMarketInDatabase(kvpHandler.Value.m_Market); #pragma warning disable 4014 ApiPush <MarketRow>(Routes.kPushMarket, kvpHandler.Value.m_Market); #pragma warning restore 4014 kvpHandler.Value.m_IsDirty = false; } } // // push fee collections // if (m_bitcoinFeeAddress != null && m_bitshaaresFeeAccount != null) { uint lastFeeId = m_dataAccess.GetSiteLastFeeUid(); // collect our fees foreach (KeyValuePair <string, MarketBase> kvpHandler in m_marketHandlers) { kvpHandler.Value.CollectFees(m_bitcoinFeeAddress, m_bitshaaresFeeAccount); } // keep the site up to date, make sure it acknowledges receipt uint latestFeeId = m_dataAccess.GetLastFeeCollectionUid(); if (latestFeeId > lastFeeId) { List <FeeCollectionRow> fees = m_dataAccess.GetFeeCollectionsSince(lastFeeId); string result = await ApiPush <List <FeeCollectionRow> >(Routes.kPushFees, fees); if (bool.Parse(result)) { m_dataAccess.UpdateSiteLastFeeUid(latestFeeId); } else { throw new Exception("API push response unknown! " + result); } } } } // // wait for a stop command to exit gracefully // if (m_lastCommand == null) { m_lastCommand = ReadConsoleAsync(); string command = await m_lastCommand; // remember we never get here unless a command was entered Console.WriteLine("got command: " + command); if (command == "stop") { m_scheduler.Dispose(); } m_lastCommand = null; } } catch (UnsupportedTransactionException ute) { // ignore so we can move on! m_dataAccess.IgnoreTransaction(ute.m_trxId); // log it LogGeneralException(ute.ToString()); } catch (Exception e) { LogGeneralException(e.ToString()); } }
public AssetNode(BitsharesAsset asset) { m_asset = asset; m_links = new List <AssetNode>(); }
/// <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; } }
public AssetNode(BitsharesAsset asset) { m_asset = asset; m_links = new List<AssetNode>(); }
/// <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 a bit assets to depositor. </summary> /// /// <remarks> Paul, 05/02/2015. </remarks> /// /// <exception cref="RefundBitcoinException"> Thrown when a Refund Bitcoin error condition /// occurs. </exception> /// /// <param name="t"> The TransactionSinceBlock to process. </param> /// <param name="asset"> The asset. </param> /// /// <returns> A BitsharesTransactionResponse. </returns> protected BitsharesTransactionResponse SendBitAssetsToDepositor(TransactionSinceBlock t, BitsharesAsset asset, SenderToDepositRow s2d, MetaOrderType orderType) { // make sure failures after this point do not result in repeated sending m_daemon.MarkDespositAsCreditedStart(t.TxId, s2d.deposit_address, m_market.symbol_pair, orderType, MetaOrderStatus.processing, TransactionPolicy.REPLACE); if (t.Amount > m_market.ask_max) { throw new RefundBitcoinException("Over " + Numeric.SerialisedDecimal(m_market.ask_max) + " " + asset.symbol + "!"); } string bitsharesAccount = s2d.receiving_address; decimal bitAssetAmountNoFee; if (m_flipped) { // they're sending us BTC, not bitAssets because the market is flipped, this is // equivelent to the opposite order type, so we have to use bid here bitAssetAmountNoFee = t.Amount * m_market.bid; } else { bitAssetAmountNoFee = t.Amount / m_market.ask; } // when buying, the fee is charged in bitAssets, // the amount recorded in the transaction is the amount of bitAssets purchased sans fee bitAssetAmountNoFee = asset.Truncate(bitAssetAmountNoFee); decimal fee = (m_market.ask_fee_percent / 100) * bitAssetAmountNoFee; decimal amountAsset = bitAssetAmountNoFee - fee; amountAsset = asset.Truncate(amountAsset); BitsharesTransactionResponse bitsharesTrx = SendBitAssets(amountAsset, asset, bitsharesAccount, "mX: " + orderType + " " + asset.symbol); m_daemon.MarkDespositAsCreditedEnd(t.TxId, bitsharesTrx.record_id, MetaOrderStatus.completed, bitAssetAmountNoFee, m_market.ask, fee); return bitsharesTrx; }
/// <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); } }