diff --git a/README.md b/README.md
index 8ef53890c4..888a2952d4 100644
--- a/README.md
+++ b/README.md
@@ -88,22 +88,21 @@ Clone this repository, run `docker-compose up` and visit localhost:8080
## Contributing
-Please work off the staging branch and make pull requests to that branch. The master branch will only be updated for new releases.
+Please work off the develop branch and make pull requests to that branch. The master branch will only be updated for new releases.
-The Bitshares UI team is supported by this [worker proposal](http://www.bitshares.foundation/workers/2018-02-bitshares-ui). It provides the funds needed to pay the coordinator and the bounties and the Bitshares Foundation.
+The Bitshares UI team is supported by this [worker](https://www.bitshares.foundation/workers/2018-08-bitshares-ui). It provides the funds needed to pay the coordinator and the bounties and the Bitshares Foundation.
If you would like to get involved, we have a [Telegram chatroom](https://t.me/BitSharesDEX) where you can ask questions and get help. You may also join [BitShares on Discord](https://discord.gg/GsjQfAJ)
-- Coordinator: Bill Butler, @billbutler
+- Project Manager: Magnus Anderson, @startail
+- Issue and Funds Coordinator: Bill Butler, @billbutler
- Lead Developer: Sigve Kvalsvik, @sigvek
-- Developer: Calvin Froedge, @calvin
-- Code Review: Fabian Schuh, @xeroc
## Development process
- New issues will, after enough discussion and clarification, be assigned an estimate time to complete, as well as assigned to the next unstarted milestone, by a project coordinator.
- Milestones are numbered YYMMDD and refer to the **anticipated release date of the next Release Candidate**.
-- Release Candidates sits 1 milestone period (2 weeks) for evaluation by the public before release
+- Release Candidates sits 1-2 weeks for evaluation by the public before release
- Bugs are always worked before enhancements
- Developers should work each issue according to a numbered branch corresponding to the issue `git checkout -b 123`
- We pay **bounties** for issues that have been estimated. An estimated issue is prefixed with a number in brackets like this: `[2] An nasty bug`. In this example, the bug is valued at two hours ($125 per hour). If you fix this issue according to these guidelines and your PR is accepted, this will earn you $250 bitUSD. You must have a Bitshares wallet and a Bitshares account to receive payment.
diff --git a/app/api/apiConfig.js b/app/api/apiConfig.js
index 3e6351de40..b330ddb5a6 100644
--- a/app/api/apiConfig.js
+++ b/app/api/apiConfig.js
@@ -45,6 +45,16 @@ export const widechainAPIs = {
DEPOSIT_HISTORY: "/latelyRecharge"
};
+export const citadelAPIs = {
+ BASE: "https://citadel.li/trade",
+ COINS_LIST: "/coins",
+ ACTIVE_WALLETS: "/active-wallets",
+ TRADING_PAIRS: "/trading-pairs",
+ DEPOSIT_LIMIT: "/deposit-limits",
+ ESTIMATE_OUTPUT: "/estimate-output-amount",
+ ESTIMATE_INPUT: "/estimate-input-amount"
+};
+
export const gdex2APIs = {
BASE: "https://api.gdex.io/adjust",
COINS_LIST: "/coins",
@@ -137,12 +147,6 @@ export const settingsAPIs = {
operator: "Witness: openledger-dc",
contact: "telegram:mtopenledger"
},
- {
- url: "wss://bitshares.nu/ws",
- location: "Stockholm",
- region: "Northern Europe",
- country: "Sweden"
- },
{
url: "wss://bit.btsabc.org/ws",
region: "Eastern Asia",
@@ -161,9 +165,138 @@ export const settingsAPIs = {
},
{
url: "wss://japan.bitshares.apasia.tech/ws",
+ location: "Tokyo",
country: "Japan",
region: "Southeastern Asia",
- operator: "APAsia",
+ operator: "Flash Infrastructure Worker",
+ contact: "telegram:murda_ra"
+ },
+ {
+ url: "wss://status200.bitshares.apasia.tech/ws",
+ location: "New Jersey",
+ country: "U.S.A.",
+ region: "Central America",
+ operator: "Flash Infrastructure Worker",
+ contact: "telegram:murda_ra"
+ },
+ {
+ url: "wss://new-york.bitshares.apasia.tech/ws",
+ location: "New York",
+ country: "U.S.A.",
+ region: "Central America",
+ operator: "Flash Infrastructure Worker",
+ contact: "telegram:murda_ra"
+ },
+ {
+ url: "wss://dallas.bitshares.apasia.tech/ws",
+ location: "Dallas",
+ country: "U.S.A.",
+ region: "Central America",
+ operator: "Flash Infrastructure Worker",
+ contact: "telegram:murda_ra"
+ },
+ {
+ url: "wss://chicago.bitshares.apasia.tech/ws",
+ location: "Chicago",
+ country: "U.S.A.",
+ region: "Central America",
+ operator: "Flash Infrastructure Worker",
+ contact: "telegram:murda_ra"
+ },
+ {
+ url: "wss://atlanta.bitshares.apasia.tech/ws",
+ location: "Atlanta",
+ country: "U.S.A.",
+ region: "Central America",
+ operator: "Flash Infrastructure Worker",
+ contact: "telegram:murda_ra"
+ },
+ {
+ url: "wss://us-la.bitshares.apasia.tech/ws",
+ location: "Los Angeles",
+ country: "U.S.A.",
+ region: "Central America",
+ operator: "Flash Infrastructure Worker",
+ contact: "telegram:murda_ra"
+ },
+ {
+ url: "wss://seattle.bitshares.apasia.tech/ws",
+ location: "Seattle",
+ country: "U.S.A.",
+ region: "Central America",
+ operator: "Flash Infrastructure Worker",
+ contact: "telegram:murda_ra"
+ },
+ {
+ url: "wss://miami.bitshares.apasia.tech/ws",
+ location: "Miami",
+ country: "U.S.A.",
+ region: "Central America",
+ operator: "Flash Infrastructure Worker",
+ contact: "telegram:murda_ra"
+ },
+ {
+ url: "wss://valley.bitshares.apasia.tech/ws",
+ location: "Silicone Valley",
+ country: "U.S.A.",
+ region: "Central America",
+ operator: "Flash Infrastructure Worker",
+ contact: "telegram:murda_ra"
+ },
+ {
+ url: "wss://canada6.daostreet.com",
+ location: "Toronto",
+ country: "Canada",
+ region: "Northern America",
+ operator: "Flash Infrastructure Worker",
+ contact: "telegram:murda_ra"
+ },
+ {
+ url: "wss://bitshares.nu/ws",
+ location: "Stockholm",
+ region: "Northern Europe",
+ country: "Sweden",
+ operator: "Flash Infrastructure Worker",
+ contact: "telegram:StaflunD"
+ },
+ {
+ url: "wss://api.open-asset.tech/ws",
+ location: "Frankfurt",
+ region: "Western Europe",
+ country: "Germany",
+ operator: "Flash Infrastructure Worker",
+ contact: "telegram:StaflunD"
+ },
+ {
+ url: "wss://france.bitshares.apasia.tech/ws",
+ location: "Paris",
+ country: "France",
+ region: "Western Europe",
+ operator: "Flash Infrastructure Worker",
+ contact: "telegram:murda_ra"
+ },
+ {
+ url: "wss://england.bitshares.apasia.tech/ws",
+ location: "London",
+ country: "England",
+ region: "Northern Europe",
+ operator: "Flash Infrastructure Worker",
+ contact: "telegram:murda_ra"
+ },
+ {
+ url: "wss://netherlands.bitshares.apasia.tech/ws",
+ location: "Amsterdam",
+ country: "Netherlands",
+ region: "Northern Europe",
+ operator: "Flash Infrastructure Worker",
+ contact: "telegram:murda_ra"
+ },
+ {
+ url: "wss://australia.bitshares.apasia.tech/ws",
+ location: "Sidney",
+ country: "Australia",
+ region: "Australia",
+ operator: "Flash Infrastructure Worker",
contact: "telegram:murda_ra"
},
{
@@ -184,8 +317,11 @@ export const settingsAPIs = {
},
{
url: "wss://dex.rnglab.org",
- location: "Netherlands",
- operator: "Witness: rnglab"
+ region: "Northern Europe",
+ country: "Netherlands",
+ location: "Amsterdam",
+ operator: "Witness: rnglab",
+ contact: "keybase:rnglab"
},
{
url: "wss://la.dexnode.net/ws",
@@ -211,7 +347,6 @@ export const settingsAPIs = {
operator: "Witness: xeldal",
contact: "telegram:xeldal"
},
- {url: "wss://btsza.co.za:8091/ws", location: "Cape Town, South Africa"},
{
url: "wss://api.bts.blckchnd.com",
region: "Western Europe",
@@ -301,9 +436,11 @@ export const settingsAPIs = {
},
{
url: "wss://api.btsxchng.com",
- location:
- "Global (Asia Pacific (Singapore) / US East (N. Virginia) / EU (London))",
- operator: "Witness: elmato"
+ region: "Multiple",
+ country: "Worldwide",
+ location: "Singapore / N. Virginia / London",
+ operator: "Witness: elmato",
+ contact: "telegram:elmato"
},
{
url: "wss://api.bts.network/",
@@ -353,8 +490,11 @@ export const settingsAPIs = {
},
{
url: "wss://bts.proxyhosts.info/wss",
- location: "Germany",
- operator: "Witness: verbaltech2"
+ region: "Western Europe",
+ country: "Germany",
+ location: "",
+ operator: "Witness: verbaltech2",
+ contact: "keybase:jgaltman"
},
{
url: "wss://bts.open.icowallet.net/ws",
@@ -364,6 +504,22 @@ export const settingsAPIs = {
operator: "Witness: magicwallet.witness",
contact: "telegram:plus_wave"
},
+ {
+ url: "wss://de.bts.dcn.cx/ws",
+ region: "Western Europe",
+ country: "Germany",
+ location: "Nuremberg",
+ operator: "Witness: fla01",
+ contact: "telegram:Otherego;telegram:BarefootMouse"
+ },
+ {
+ url: "wss://fi.bts.dcn.cx/ws",
+ region: "Northern Europe",
+ country: "Finland",
+ location: "Helsinki",
+ operator: "Witness: fla01",
+ contact: "telegram:Otherego;telegram:BarefootMouse"
+ },
{
url: "wss://crazybit.online",
region: "Asia",
@@ -444,6 +600,14 @@ export const settingsAPIs = {
operator: "Witness: zapata42-witness",
contact: "telegram:Zapata_42"
},
+ {
+ url: "wss://citadel.li/node",
+ region: "Western Europe",
+ country: "Iceland",
+ location: "Reykjavik",
+ operator: "CITADEL",
+ contact: "email:citadel.li;support"
+ },
// Testnet
{
url: "wss://node.testnet.bitshares.eu",
@@ -466,7 +630,15 @@ export const settingsAPIs = {
region: "TESTNET - Northern America",
country: "U.S.A.",
location: "Dallas",
- operator: "APAsia",
+ operator: "Flash Infrastructure Worker",
+ contact: "telegram:murda_ra"
+ },
+ {
+ url: "wss://testnet-eu.bitshares.apasia.tech/ws",
+ region: "TESTNET - Northern Europe",
+ country: "Netherlands",
+ location: "Amsterdam",
+ operator: "Flash Infrastructure Worker",
contact: "telegram:murda_ra"
},
{
@@ -476,6 +648,14 @@ export const settingsAPIs = {
location: "Paris",
operator: "Witness: zapata42-witness",
contact: "telegram:Zapata_42"
+ },
+ {
+ url: "wss://testnet.bts.dcn.cx/ws",
+ region: "TESTNET - Europe",
+ country: "Germany / Finland",
+ location: "Nurenberg / Helsinki",
+ operator: "Witness: fla-test",
+ contact: "telegram:Otherego;telegram:BarefootMouse"
}
],
DEFAULT_FAUCET: getFaucet().url,
diff --git a/app/assets/locales/locale-de.json b/app/assets/locales/locale-de.json
index c4bbaa3596..2b98baac96 100644
--- a/app/assets/locales/locale-de.json
+++ b/app/assets/locales/locale-de.json
@@ -799,6 +799,13 @@
"calc": "Calculating",
"choose_deposit": "Please select the coin you would like to deposit",
"choose_withdraw": "Please select the coin you would like to withdraw",
+ "citadel": {
+ "coming_soon": "Coming soon",
+ "min_amount": "Minimum amount: %(minAmount)s %(symbol)s",
+ "min_amount_error": "Please enter number >= minimum",
+ "support_block": "For support, please contact citadel.li at:",
+ "under_construction": "Under Construction"
+ },
"contact_TRADE": "Contact Blocktrades",
"convert": "Internal conversion",
"convert_coin": "Convert to %(coin)s (%(symbol)s)",
diff --git a/app/assets/locales/locale-en.json b/app/assets/locales/locale-en.json
index a1dc48c16f..d18a30846e 100644
--- a/app/assets/locales/locale-en.json
+++ b/app/assets/locales/locale-en.json
@@ -253,7 +253,9 @@
"force_settlement_offset_percent": "Percent offset of forced settlements",
"global_settle": "Allow issuer to force a global settling",
"id": "ID",
+ "invalid_backing_asset_change": "You can't change the backing asset of an asset that has a non-zero current supply",
"invalid_market_pair": "Preferred market pairing can not be the same market",
+ "invalid_permissions_change": "You can't enable a permission for an asset that has a non-zero current supply",
"issued_assets": "Issued Assets",
"market": "Preferred market pairing",
"market_fee": "Market fee",
@@ -390,6 +392,10 @@
"understand": "I understand",
"ws_status": "Full node connection status"
},
+ "boolean": {
+ "true": "True",
+ "false": "False"
+ },
"borrow": {
"adjust": "Update position",
"adjust_short": "Adjust",
@@ -461,6 +467,7 @@
"chart_height": "Chart height (pixels)",
"chart_modal": "Chart options",
"chart_type": "Chart type",
+ "checking_for_worth_less_settlement": "Checking if you can get a better price by selling to market, please wait..",
"confirm_buy": "Your order is %(diff)s times higher than the lowest ask, are you sure?",
"confirm_no_orders_buy": "You are placing a buy order in a market with no open sell orders. Are you certain you wish to proceed?",
"confirm_no_orders_sell": "You are placing a sell order in a market with no open buy orders. Are you certain you wish to proceed?",
@@ -553,6 +560,7 @@
"vol_short": "Vol",
"volume": "Volume",
"volume_24": "24hr Volume",
+ "worth_less_settlement_warning": "Warning! You can get a better price by selling to {market_link}.",
"your_price": "Your Call Price",
"zoom": "Zoom",
"zoom_all": "All"
@@ -801,6 +809,13 @@
"calc": "Calculating",
"choose_deposit": "Please select the coin you would like to deposit",
"choose_withdraw": "Please select the coin you would like to withdraw",
+ "citadel": {
+ "coming_soon": "Coming soon",
+ "min_amount": "Minimum amount: %(minAmount)s %(symbol)s",
+ "min_amount_error": "Please enter number >= minimum",
+ "support_block": "For support, please contact citadel.li at:",
+ "under_construction": "Under Construction"
+ },
"contact_TRADE": "Contact Blocktrades",
"convert": "Internal conversion",
"convert_coin": "Convert to %(coin)s (%(symbol)s)",
@@ -884,6 +899,7 @@
"unavailable_OPEN": "The OpenLedger Gateway is down or not responding",
"unavailable_RUDEX": "The RuDEX Gateway is down or not responding",
"unavailable_TRADE": "The Blocktrades Bridge is down or not responding",
+ "unavailable_CITADEL": "The Citadel Bridge is down or not responding",
"unavailable_bridge": "The bridge service for this asset is currently down, please try again later",
"use_copy_button": "PLEASE USE COPY BUTTON TO MAKE COPY OF ADDRESS OR MEMO ON THIS PAGE",
"user_unavailable": "User informaction is current unavailable, please try again later",
@@ -1127,6 +1143,11 @@
},
"ok": "OK",
"proposals": {
+ "actions": {
+ "approve": "Approve proposal",
+ "reject": "Reject proposal",
+ "delete": "Permanently reject proposal"
+ },
"approval_add": "Approval to add",
"approval_remove": "Approval to remove",
"key_approval_add": "Key approval to add",
@@ -1222,9 +1243,9 @@
"no_recent": "No recent transactions",
"override_transfer": "{issuer} transferred {amount} from {from} to {to}",
"pending": "pending %(blocks)s blocks",
- "proposal_create": "{account} created a proposed transaction",
- "proposal_delete": "{account} deleted a proposed transaction",
- "proposal_update": "{account} updated a proposed transaction",
+ "proposal_create": "{account} created the proposed transaction {proposal}",
+ "proposal_delete": "{account} deleted the proposed transaction {proposal}",
+ "proposal_update": "{account} updated the proposed transaction {proposal}",
"publish_feed": "{account} published feed price of {price}",
"reg_account": "{registrar} registered the account {new_account}",
"set_proxy": "{account} set {proxy} as their voting proxy",
@@ -1262,6 +1283,7 @@
"asset_update": "Update the asset {asset} using the account {account}",
"call_order_update": "Change {account} {debtSymbol} debt by {debt} and collateral by {collateral}",
"committee_member_update_global_parameters": "Update committee global parameters by {account}",
+ "delete": "Permanently reject",
"expires": "Expires",
"feed_producer": "Update the feed producers for the asset {asset} using the account {account}",
"limit_order_buy": "Place an order to buy {amount} at {price} for {account}",
@@ -1280,6 +1302,14 @@
"owner_approvals_to_add": "Owner approvals to add",
"owner_approvals_to_remove": "Owner approvals to remove"
},
+ "updated": {
+ "active_approvals_to_add": "Active approval(s) added",
+ "active_approvals_to_remove": "Active approval(s) removed",
+ "key_approvals_to_add": "Key approval(s) added",
+ "key_approvals_to_remove": "Key approval(s) removed",
+ "owner_approvals_to_add": "Owner approval(s) added",
+ "owner_approvals_to_remove": "Owner approval(s) removed"
+ },
"update_account": "Update account data for {account}",
"vesting_balance_withdraw": "Withdraw {amount} from vesting balance of {account}"
},
@@ -1290,6 +1320,9 @@
"proposed_operations": "Proposed operations",
"review_period": "Review period begin"
},
+ "proposal_delete": {
+ "using_owner_authority": "Using owner authority"
+ },
"propose": "Propose",
"qr_address_scanner": {
"address_found": "Address found",
diff --git a/app/assets/locales/locale-es.json b/app/assets/locales/locale-es.json
index 2e785054c1..06daa629e1 100644
--- a/app/assets/locales/locale-es.json
+++ b/app/assets/locales/locale-es.json
@@ -799,6 +799,13 @@
"calc": "Calculando",
"choose_deposit": "Seleccione la moneda que desea depositar",
"choose_withdraw": "Seleccione la moneda que desea retirar",
+ "citadel": {
+ "coming_soon": "Coming soon",
+ "min_amount": "Minimum amount: %(minAmount)s %(symbol)s",
+ "min_amount_error": "Please enter number >= minimum",
+ "support_block": "For support, please contact citadel.li at:",
+ "under_construction": "Under Construction"
+ },
"contact_TRADE": "Contacta Blocktrades",
"convert": "Conversión interna",
"convert_coin": "Convertir a %(coin)s (%(symbol)s)",
diff --git a/app/assets/locales/locale-fr.json b/app/assets/locales/locale-fr.json
index ee43abeae3..d7219f2e27 100644
--- a/app/assets/locales/locale-fr.json
+++ b/app/assets/locales/locale-fr.json
@@ -799,6 +799,13 @@
"calc": "Calculating",
"choose_deposit": "Please select the coin you would like to deposit",
"choose_withdraw": "Please select the coin you would like to withdraw",
+ "citadel": {
+ "coming_soon": "Coming soon",
+ "min_amount": "Minimum amount: %(minAmount)s %(symbol)s",
+ "min_amount_error": "Please enter number >= minimum",
+ "support_block": "For support, please contact citadel.li at:",
+ "under_construction": "Under Construction"
+ },
"contact_TRADE": "Contact Blocktrades",
"convert": "Internal conversion",
"convert_coin": "Convert to %(coin)s (%(symbol)s)",
diff --git a/app/assets/locales/locale-it.json b/app/assets/locales/locale-it.json
index 984554444f..92bbccbb38 100644
--- a/app/assets/locales/locale-it.json
+++ b/app/assets/locales/locale-it.json
@@ -799,8 +799,15 @@
"calc": "Calcolo in corso",
"choose_deposit": "Per favore seleziona la valuta che vuoi depositare",
"choose_withdraw": "Per favore seleziona la valuta che vuoi prelevare",
+ "citadel": {
+ "coming_soon": "Prossimamente",
+ "min_amount": "Minimum amount: %(minAmount)s %(symbol)s",
+ "min_amount_error": "Per favore inserisci un numero >= minimo",
+ "support_block": "Per supporto, contatta citadel.li a:",
+ "under_construction": "Under Construction"
+ },
"contact_TRADE": "Contact Blocktrades",
- "convert": "Conversione interna",
+ "convert": "Quantità minima: %(minAmount)s %(symbol)s",
"convert_coin": "Converti in %(coin)s (%(symbol)s)",
"convert_now": "Converti adesso",
"copy_address": "Copy address",
diff --git a/app/assets/locales/locale-ja.json b/app/assets/locales/locale-ja.json
index 8176c9785d..559f7a926d 100644
--- a/app/assets/locales/locale-ja.json
+++ b/app/assets/locales/locale-ja.json
@@ -799,6 +799,13 @@
"calc": "計算中",
"choose_deposit": "入金したいコインを選択してください",
"choose_withdraw": "出金したいコインを選択してください",
+ "citadel": {
+ "coming_soon": "近日公開",
+ "min_amount": "最低数量: %(minAmount)s %(symbol)s",
+ "min_amount_error": "数字を入力してください >= minimum",
+ "support_block": "サポートについては、citadel.li までご連絡ください。:",
+ "under_construction": "工事中"
+ },
"contact_TRADE": "Blocktradesに連絡する",
"convert": "内部変換",
"convert_coin": "%(coin)s (%(symbol)s) に変換",
diff --git a/app/assets/locales/locale-ko.json b/app/assets/locales/locale-ko.json
index 64b186499d..7f423b9367 100644
--- a/app/assets/locales/locale-ko.json
+++ b/app/assets/locales/locale-ko.json
@@ -799,6 +799,13 @@
"calc": "Calculating",
"choose_deposit": "Please select the coin you would like to deposit",
"choose_withdraw": "Please select the coin you would like to withdraw",
+ "citadel": {
+ "coming_soon": "Coming soon",
+ "min_amount": "Minimum amount: %(minAmount)s %(symbol)s",
+ "min_amount_error": "Please enter number >= minimum",
+ "support_block": "For support, please contact citadel.li at:",
+ "under_construction": "Under Construction"
+ },
"contact_TRADE": "Contact Blocktrades",
"convert": "Internal conversion",
"convert_coin": "Convert to %(coin)s (%(symbol)s)",
diff --git a/app/assets/locales/locale-ru.json b/app/assets/locales/locale-ru.json
index 7de37e62c5..fccce072f8 100644
--- a/app/assets/locales/locale-ru.json
+++ b/app/assets/locales/locale-ru.json
@@ -799,6 +799,13 @@
"calc": "Расчет",
"choose_deposit": "Пожалуйста, выберите валюту, в которой хотели бы разместить депозит",
"choose_withdraw": "Пожалуйста, выберите валюту, которую хотели бы вывести",
+ "citadel": {
+ "coming_soon": "Скоро",
+ "min_amount": "Минимальная сумма: %(minAmount)s %(symbol)s",
+ "min_amount_error": "Пожалуйста введите число >= минимума",
+ "support_block": "Для получения поддержки, пожалуйста, свяжитесь с Citadel по адресу:",
+ "under_construction": "Технические работы"
+ },
"contact_TRADE": "Связаться с Blocktrades",
"convert": "Конвертировать внутри системы",
"convert_coin": "Конвертировать в %(coin)s (%(symbol)s)",
@@ -866,8 +873,7 @@
"coming_soon": "Скоро",
"min_amount": "Минимальная сумма: %(minAmount)s %(symbol)s",
"min_amount_error": "Пожалуйста введите число >= минимума",
- "support_block":
- "Для получения поддержки, пожалуйста, свяжитесь с XBTS по адресу:",
+ "support_block": "Для получения поддержки, пожалуйста, свяжитесь с XBTS по адресу:",
"under_construction": "Технические работы"
},
"scan_qr": "Сканировать QR-код",
@@ -883,6 +889,7 @@
"unavailable_OPEN": "Шлюз OpenLedger не работает или не отвечает",
"unavailable_RUDEX": "Шлюз RuDEX не работает или не отвечает",
"unavailable_TRADE": "Шлюз Blocktrades не работает или не отвечает",
+ "unavailable_CITADEL": "Шлюз Citadel не работает или не отвечает",
"unavailable_bridge": "Мост для данного актива в настоящее время не работает, пожалуйста, повторите попытку позже",
"use_copy_button": "ПОЖАЛУЙСТА ИСПОЛЬЗУЙТЕ КНОПКУ ДЛЯ КОПИРОВАНИЯ АДРЕСА ИЛИ МЕМО",
"user_unavailable": "В настоящий момент информация о пользователе недоступна, повторите попытку позже",
diff --git a/app/assets/locales/locale-tr.json b/app/assets/locales/locale-tr.json
index 1d7772e014..ac9b049693 100644
--- a/app/assets/locales/locale-tr.json
+++ b/app/assets/locales/locale-tr.json
@@ -799,6 +799,13 @@
"calc": "Hesaplanıyor...",
"choose_deposit": "Lütfen yatırmak istediğiniz dijital varlığı seçin",
"choose_withdraw": "Lütfen çekmek istediğiniz dijital varlığı seçin",
+ "citadel": {
+ "coming_soon": "Coming soon",
+ "min_amount": "Minimum amount: %(minAmount)s %(symbol)s",
+ "min_amount_error": "Please enter number >= minimum",
+ "support_block": "For support, please contact citadel.li at:",
+ "under_construction": "Under Construction"
+ },
"contact_TRADE": "Contact Blocktrades",
"convert": "Internal conversion",
"convert_coin": "Convert to %(coin)s (%(symbol)s)",
diff --git a/app/assets/locales/locale-zh.json b/app/assets/locales/locale-zh.json
index c235c918e4..55bb4a90fa 100644
--- a/app/assets/locales/locale-zh.json
+++ b/app/assets/locales/locale-zh.json
@@ -799,6 +799,13 @@
"calc": "计算中",
"choose_deposit": "请选择充值的币种",
"choose_withdraw": "请选择提现的币种",
+ "citadel": {
+ "coming_soon": "即将上线",
+ "min_amount": "最小金额:%(minAmount)s %(symbol)s",
+ "min_amount_error": "请输入大于最小金额的数量",
+ "support_block": "请联系 citadel.li 获取客户支持:",
+ "under_construction": "Under Construction"
+ },
"contact_TRADE": "联系 Blocktrades",
"convert": "内部兑换",
"convert_coin": "兑换成 %(coin)s (%(symbol)s)",
diff --git a/app/assets/stylesheets/themes/_theme-template.scss b/app/assets/stylesheets/themes/_theme-template.scss
index 7027fba360..ac4e513682 100644
--- a/app/assets/stylesheets/themes/_theme-template.scss
+++ b/app/assets/stylesheets/themes/_theme-template.scss
@@ -1920,6 +1920,9 @@
.footer {
padding-right: 0;
}
+ .tab-no-background {
+ background-color: transparent !important;
+ }
.header-container {
background-color: $account-background;
.menu-dropdown-wrapper {
diff --git a/app/components/Account/AccountAssetCreate.jsx b/app/components/Account/AccountAssetCreate.jsx
index b2c339b04c..a9a1e6330f 100644
--- a/app/components/Account/AccountAssetCreate.jsx
+++ b/app/components/Account/AccountAssetCreate.jsx
@@ -42,10 +42,13 @@ class BitAssetOptions extends React.Component {
}
_onInputBackingAsset(asset) {
- this.setState({
- backingAsset: asset.toUpperCase(),
- error: null
- });
+ if (this.props.disableBackingAssetChange)
+ this.props.disabledBackingAssetChangeCallback();
+ else
+ this.setState({
+ backingAsset: asset.toUpperCase(),
+ error: null
+ });
}
_onFoundBackingAsset(asset) {
diff --git a/app/components/Account/AccountAssetUpdate.jsx b/app/components/Account/AccountAssetUpdate.jsx
index d24721bb35..c2d760d839 100644
--- a/app/components/Account/AccountAssetUpdate.jsx
+++ b/app/components/Account/AccountAssetUpdate.jsx
@@ -24,11 +24,19 @@ import AssetFeedProducers from "./AssetFeedProducers";
import BaseModal from "components/Modal/BaseModal";
import ZfApi from "react-foundation-apps/src/utils/foundation-api";
import {withRouter} from "react-router-dom";
+import notify from "actions/NotificationActions";
let GRAPHENE_MAX_SHARE_SUPPLY = new big(
assetConstants.GRAPHENE_MAX_SHARE_SUPPLY
);
+const disabledBackingAssetChangeCallback = () =>
+ notify.error(
+ counterpart.translate(
+ "account.user_issued_assets.invalid_backing_asset_change"
+ )
+ );
+
class AccountAssetUpdate extends React.Component {
static propTypes = {
globalObject: ChainTypes.ChainObject.isRequired
@@ -446,6 +454,10 @@ class AccountAssetUpdate extends React.Component {
bitasset_opts[value] = e;
break;
+ case "minimum_feeds":
+ bitasset_opts[value] = parseInt(e.target.value, 10);
+ break;
+
default:
break;
}
@@ -682,8 +694,35 @@ class AccountAssetUpdate extends React.Component {
this._validateEditFields({});
}
+ _getCurrentSupply() {
+ const {asset, getDynamicObject} = this.props;
+
+ return (
+ getDynamicObject &&
+ getDynamicObject(asset.get("dynamic_asset_data_id")).get(
+ "current_supply"
+ )
+ );
+ }
+
_onPermissionChange(key) {
- let booleans = this.state.permissionBooleans;
+ const {isBitAsset, permissionBooleans} = this.state;
+
+ const disabled = !assetUtils.getFlagBooleans(
+ this.props.asset.getIn(["options", "issuer_permissions"]),
+ isBitAsset
+ )[key];
+
+ if (this._getCurrentSupply() > 0 && disabled) {
+ notify.error(
+ counterpart.translate(
+ "account.user_issued_assets.invalid_permissions_change"
+ )
+ );
+ return;
+ }
+
+ let booleans = permissionBooleans;
booleans[key] = !booleans[key];
this.setState({
permissionBooleans: booleans
@@ -1303,6 +1342,12 @@ class AccountAssetUpdate extends React.Component {
"precision"
)}
assetSymbol={asset.get("symbol")}
+ disableBackingAssetChange={
+ this._getCurrentSupply() > 0
+ }
+ disabledBackingAssetChangeCallback={
+ disabledBackingAssetChangeCallback
+ }
/>
{
diff --git a/app/components/Account/AccountDepositWithdraw.jsx b/app/components/Account/AccountDepositWithdraw.jsx
index 0390aa877b..6d37ea869e 100644
--- a/app/components/Account/AccountDepositWithdraw.jsx
+++ b/app/components/Account/AccountDepositWithdraw.jsx
@@ -6,10 +6,12 @@ import utils from "common/utils";
import Translate from "react-translate-component";
import ChainTypes from "../Utility/ChainTypes";
import BindToChainState from "../Utility/BindToChainState";
+import CitadelGateway from "../DepositWithdraw/citadel/CitadelGateway";
import OpenledgerGateway from "../DepositWithdraw/OpenledgerGateway";
import OpenLedgerFiatDepositWithdrawal from "../DepositWithdraw/openledger/OpenLedgerFiatDepositWithdrawal";
import OpenLedgerFiatTransactionHistory from "../DepositWithdraw/openledger/OpenLedgerFiatTransactionHistory";
import BlockTradesBridgeDepositRequest from "../DepositWithdraw/blocktrades/BlockTradesBridgeDepositRequest";
+import CitadelBridgeDepositRequest from "../DepositWithdraw/citadel/CitadelBridgeDepositRequest";
import HelpContent from "../Utility/HelpContent";
import AccountStore from "stores/AccountStore";
import SettingsStore from "stores/SettingsStore";
@@ -41,6 +43,7 @@ class AccountDepositWithdraw extends React.Component {
rudexService: props.viewSettings.get("rudexService", "gateway"),
xbtsxService: props.viewSettings.get("xbtsxService", "gateway"),
btService: props.viewSettings.get("btService", "bridge"),
+ citadelService: props.viewSettings.get("citadelService", "bridge"),
metaService: props.viewSettings.get("metaService", "bridge"),
activeService: props.viewSettings.get("activeService", 0)
};
@@ -58,10 +61,15 @@ class AccountDepositWithdraw extends React.Component {
nextProps.openLedgerBackedCoins,
this.props.openLedgerBackedCoins
) ||
+ !utils.are_equal_shallow(
+ nextProps.citadelBackedCoins,
+ this.props.citadelBackedCoins
+ ) ||
nextState.olService !== this.state.olService ||
nextState.rudexService !== this.state.rudexService ||
nextState.xbtsxService !== this.state.xbtsxService ||
nextState.btService !== this.state.btService ||
+ nextState.citadelService !== this.state.citadelService ||
nextState.metaService !== this.state.metaService ||
nextState.activeService !== this.state.activeService
);
@@ -111,6 +119,15 @@ class AccountDepositWithdraw extends React.Component {
});
}
+ toggleCitadelService(service) {
+ this.setState({
+ citadelService: service
+ });
+ SettingsActions.changeViewSetting({
+ citadelService: service
+ });
+ }
+
toggleMetaService(service) {
this.setState({
metaService: service
@@ -140,8 +157,13 @@ class AccountDepositWithdraw extends React.Component {
//let services = ["Openledger (OPEN.X)", "BlockTrades (TRADE.X)", "Transwiser", "BitKapital"];
let serList = [];
let {account} = this.props;
- let {olService, btService, rudexService, xbtsxService} = this.state;
-
+ let {
+ olService,
+ btService,
+ rudexService,
+ xbtsxService,
+ citadelService
+ } = this.state;
serList.push({
name: "Openledger (OPEN.X)",
template: (
@@ -376,6 +398,51 @@ class AccountDepositWithdraw extends React.Component {
)
});
+ serList.push({
+ name: "Citadel",
+ template: (
+
+ )
+ });
+
serList.push({
name: "BitKapital",
template: (
@@ -462,7 +529,8 @@ class AccountDepositWithdraw extends React.Component {
"RUDEX",
"TRADE",
"BITKAPITAL",
- "XBTSX"
+ "XBTSX",
+ "CITADEL"
];
const currentServiceName = serviceNames[activeService];
const currentServiceDown = servicesDown.get(currentServiceName);
@@ -597,6 +665,10 @@ export default connect(
"TRADE",
[]
),
+ citadelBackedCoins: GatewayStore.getState().backedCoins.get(
+ "CITADEL",
+ []
+ ),
winexBackedCoins: GatewayStore.getState().backedCoins.get(
"WIN",
[]
diff --git a/app/components/Account/Proposals.jsx b/app/components/Account/Proposals.jsx
index 22ff160c1c..9954076c35 100644
--- a/app/components/Account/Proposals.jsx
+++ b/app/components/Account/Proposals.jsx
@@ -7,9 +7,7 @@ import ProposedOperation, {
import BindToChainState from "components/Utility/BindToChainState";
import ChainTypes from "components/Utility/ChainTypes";
import utils from "common/utils";
-import ProposalApproveModal, {
- finalRequiredPerms
-} from "../Modal/ProposalApproveModal";
+import ProposalModal, {finalRequiredPerms} from "../Modal/ProposalModal";
import NestedApprovalState from "../Account/NestedApprovalState";
import {ChainStore} from "bitsharesjs";
import counterpart from "counterpart";
@@ -242,7 +240,7 @@ class Proposals extends Component {
)}
-
) : null}
-
+
+
);
diff --git a/app/components/Blockchain/Asset.jsx b/app/components/Blockchain/Asset.jsx
index c2a2590620..3813145057 100644
--- a/app/components/Blockchain/Asset.jsx
+++ b/app/components/Blockchain/Asset.jsx
@@ -610,7 +610,7 @@ class Asset extends React.Component {
content="explorer.asset.feed_producer_text"
/>
@@ -1253,18 +1253,21 @@ class Asset extends React.Component {
}
}
-Asset = connect(Asset, {
- listenTo() {
- return [AccountStore];
- },
- getProps() {
- return {
- currentAccount:
- AccountStore.getState().currentAccount ||
- AccountStore.getState().passwordAccount
- };
+Asset = connect(
+ Asset,
+ {
+ listenTo() {
+ return [AccountStore];
+ },
+ getProps() {
+ return {
+ currentAccount:
+ AccountStore.getState().currentAccount ||
+ AccountStore.getState().passwordAccount
+ };
+ }
}
-});
+);
Asset = AssetWrapper(Asset, {
propNames: ["backingAsset"]
diff --git a/app/components/Blockchain/AssetPublishFeed.jsx b/app/components/Blockchain/AssetPublishFeed.jsx
index a966911145..279a1d4042 100644
--- a/app/components/Blockchain/AssetPublishFeed.jsx
+++ b/app/components/Blockchain/AssetPublishFeed.jsx
@@ -23,7 +23,7 @@ class AssetPublishFeed extends React.Component {
resetState(props = this.props) {
let publisher_id = props.account.get("id");
- const currentFeed = props.base.getIn(["bitasset", "current_feed"]);
+ const currentFeed = props.asset.getIn(["bitasset", "current_feed"]);
/* Might need to check these default values */
let mcr = currentFeed.get("maintenance_collateral_ratio", 1750);
@@ -54,7 +54,7 @@ class AssetPublishFeed extends React.Component {
onSubmit() {
AssetActions.publishFeed({
publisher: this.state.publisher_id,
- asset_id: this.props.base.get("id"),
+ asset_id: this.props.asset.get("id"),
mcr: this.state.mcr,
mssr: this.state.mssr,
settlementPrice: this.state.settlementPrice,
@@ -88,9 +88,16 @@ class AssetPublishFeed extends React.Component {
}
render() {
- const {quote, base} = this.props;
+ const {asset} = this.props;
const {mcrValue, mssrValue, publisher} = this.state;
+ const base = asset.get("id");
+ const quote = asset.getIn([
+ "bitasset",
+ "options",
+ "short_backing_asset"
+ ]);
+
return (
{/* Settlement Price */}
@@ -124,8 +131,8 @@ class AssetPublishFeed extends React.Component {
"settlementPrice"
)}
label="explorer.asset.price_feed.settlement_price"
- quote={quote.get("id")}
- base={base.get("id")}
+ quote={quote}
+ base={base}
/>
{/* MCR */}
@@ -179,10 +186,5 @@ class AssetPublishFeed extends React.Component {
}
AssetPublishFeed = BindToChainState(AssetPublishFeed);
-AssetPublishFeed = AssetWrapper(AssetPublishFeed, {
- propNames: ["quote", "base"],
- defaultProps: {
- quote: "1.3.0"
- }
-});
+AssetPublishFeed = AssetWrapper(AssetPublishFeed);
export default AssetPublishFeed;
diff --git a/app/components/Blockchain/Operation.jsx b/app/components/Blockchain/Operation.jsx
index 67f415d4a4..1bfada4d76 100644
--- a/app/components/Blockchain/Operation.jsx
+++ b/app/components/Blockchain/Operation.jsx
@@ -26,6 +26,15 @@ require("./operations.scss");
let ops = Object.keys(operations);
let listings = account_constants.account_listing;
+const ShortObjectId = ({objectId}) => {
+ if (typeof objectId === "string") {
+ const parts = objectId.split(".");
+ const {length} = parts;
+ if (length > 0) return "#" + parts[length - 1];
+ }
+ return objectId;
+};
+
class TransactionLabel extends React.Component {
shouldComponentUpdate(nextProps) {
return (
@@ -663,25 +672,65 @@ class Operation extends React.Component {
case "asset_settle":
color = "warning";
- column = (
-
-
-
- );
+
+ const baseAmount = op[1].amount;
+ const {
+ result: [resultCode, quoteAmount]
+ } = this.props;
+ const instantSettleCode = 2;
+
+ switch (resultCode) {
+ case instantSettleCode:
+ column = (
+
+
+
+ );
+ break;
+ default:
+ column = (
+
+
+
+ );
+ }
+
break;
case "asset_global_settle":
@@ -827,6 +876,14 @@ class Operation extends React.Component {
type: "account",
value: op[1].fee_paying_account,
arg: "account"
+ },
+ {
+ value: (
+
+ ),
+ arg: "proposal"
}
]}
/>
@@ -853,15 +910,86 @@ class Operation extends React.Component {
break;
case "proposal_update":
+ const fields = [
+ "active_approvals_to_add",
+ "active_approvals_to_remove",
+ "owner_approvals_to_add",
+ "owner_approvals_to_remove",
+ "key_approvals_to_add",
+ "key_approvals_to_remove"
+ ];
+ column = (
+
+
+
+ ),
+ arg: "proposal"
+ }
+ ]}
+ />
+
+
+ {fields.map(field => {
+ if (op[1][field].length) {
+ return (
+
+
+
+ {op[1][field].map(value => {
+ return (
+ -
+ {field.startsWith(
+ "key"
+ )
+ ? value
+ : this.linkToAccount(
+ value
+ )}
+
+ );
+ })}
+
+
+ );
+ } else return null;
+ })}
+
+
+ );
+ break;
+
+ case "proposal_delete":
column = (
+ ),
+ arg: "proposal"
}
]}
/>
@@ -869,17 +997,6 @@ class Operation extends React.Component {
);
break;
- case "proposal_delete":
- column = (
-
-
-
- );
- break;
-
case "withdraw_permission_create":
column = (
diff --git a/app/components/Blockchain/Transaction.jsx b/app/components/Blockchain/Transaction.jsx
index d5bcf502a8..b22d922dad 100644
--- a/app/components/Blockchain/Transaction.jsx
+++ b/app/components/Blockchain/Transaction.jsx
@@ -36,6 +36,13 @@ require("./json-inspector.scss");
let ops = Object.keys(operations);
let listings = Object.keys(account_constants.account_listing);
+const TranslateBoolean = ({value, ...otherProps}) => (
+
+);
+
class OpType extends React.Component {
shouldComponentUpdate(nextProps) {
return nextProps.type !== this.props.type;
@@ -169,7 +176,12 @@ class Transaction extends React.Component {
);
memo = text ? (
- {text} |
+
+ {text}
+ |
) : !text && isMine ? (
@@ -1791,7 +1803,48 @@ class Transaction extends React.Component {
break;
- // proposal_delete
+ case "proposal_delete":
+ color = "cancel";
+ rows.push(
+ |
+
+
+ |
+
+ {this.linkToAccount(op[1].fee_paying_account)}
+ |
+
+ );
+ rows.push(
+
+
+
+ |
+
+
+ |
+
+ );
+ rows.push(
+
+
+
+ |
+ {op[1].proposal} |
+
+ );
+ break;
case "asset_claim_fees":
color = "success";
diff --git a/app/components/Blockchain/operations.scss b/app/components/Blockchain/operations.scss
index 434472d4f7..def0a766b1 100644
--- a/app/components/Blockchain/operations.scss
+++ b/app/components/Blockchain/operations.scss
@@ -7,3 +7,10 @@
.right-td {
text-align: left;
}
+
+.proposal-update {
+ padding-top: 5px;
+ li {
+ font-size: 0.9rem;
+ }
+}
diff --git a/app/components/DepositWithdraw/citadel/CitadelBridgeDepositRequest.jsx b/app/components/DepositWithdraw/citadel/CitadelBridgeDepositRequest.jsx
new file mode 100755
index 0000000000..9881846117
--- /dev/null
+++ b/app/components/DepositWithdraw/citadel/CitadelBridgeDepositRequest.jsx
@@ -0,0 +1,1851 @@
+import React from "react";
+import Translate from "react-translate-component";
+import ChainTypes from "components/Utility/ChainTypes";
+import BindToChainState from "components/Utility/BindToChainState";
+import BaseModal from "../../Modal/BaseModal";
+import ZfApi from "react-foundation-apps/src/utils/foundation-api";
+import AccountBalance from "../../Account/AccountBalance";
+import WithdrawModalCitadel from "./WithdrawModalCitadel";
+import CitadelDepositAddressCache from "common/CitadelDepositAddressCache";
+import utils from "common/utils";
+import AccountActions from "actions/AccountActions";
+import TransactionConfirmStore from "stores/TransactionConfirmStore";
+import {citadelAPIs} from "api/apiConfig";
+import {debounce} from "lodash-es";
+import {checkFeeStatusAsync, checkBalance} from "common/trxHelper";
+import {Asset} from "common/MarketClasses";
+import {ChainStore} from "bitsharesjs/es";
+import {getConversionJson} from "common/gatewayMethods";
+import PropTypes from "prop-types";
+
+class ButtonWithdraw extends React.Component {
+ static propTypes = {
+ balance: ChainTypes.ChainObject,
+ url: PropTypes.string.isRequired
+ };
+
+ getWithdrawModalId() {
+ return "withdraw_asset_" + this.props.gateway + "_bridge";
+ }
+
+ onWithdraw() {
+ ZfApi.publish(this.getWithdrawModalId(), "open");
+ }
+
+ render() {
+ let withdraw_modal_id = this.getWithdrawModalId();
+
+ let button_class = "button disabled";
+ if (
+ Object.keys(this.props.account.get("balances").toJS()).includes(
+ this.props.asset.get("id")
+ )
+ ) {
+ if (
+ !!this.props.amount_to_withdraw &&
+ !(this.props.amount_to_withdraw.indexOf(" ") >= 0) &&
+ !isNaN(this.props.amount_to_withdraw) &&
+ this.props.amount_to_withdraw > 0 &&
+ this.props.amount_to_withdraw <=
+ this.props.balance.toJS().balance /
+ utils.get_asset_precision(
+ this.props.asset.get("precision")
+ )
+ ) {
+ button_class = "button";
+ }
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+ButtonWithdraw = BindToChainState(ButtonWithdraw);
+
+class ButtonWithdrawContainer extends React.Component {
+ static propTypes = {
+ account: ChainTypes.ChainAccount.isRequired,
+ asset: ChainTypes.ChainAsset.isRequired,
+ output_coin_type: PropTypes.string.isRequired,
+ url: PropTypes.string.isRequired
+ };
+
+ render() {
+ let withdraw_button = (
+
+ );
+
+ return
{withdraw_button};
+ }
+}
+
+ButtonWithdrawContainer = BindToChainState(ButtonWithdrawContainer);
+
+class CitadelBridgeDepositRequest extends React.Component {
+ static propTypes = {
+ url: PropTypes.string,
+ gateway: PropTypes.string,
+ account: ChainTypes.ChainAccount,
+ issuer_account: ChainTypes.ChainAccount,
+ initial_deposit_input_coin_type: PropTypes.string,
+ initial_deposit_output_coin_type: PropTypes.string,
+ initial_deposit_estimated_input_amount: PropTypes.string,
+ initial_withdraw_input_coin_type: PropTypes.string,
+ initial_withdraw_output_coin_type: PropTypes.string,
+ initial_withdraw_estimated_input_amount: PropTypes.string
+ };
+
+ constructor(props) {
+ super(props);
+ this.refresh_interval = 2 * 60 * 1000; // update deposit limit/estimates every two minutes
+
+ this.deposit_address_cache = new CitadelDepositAddressCache();
+
+ this.coin_info_request_states = {
+ request_in_progress: 0,
+ request_complete: 1,
+ request_failed: 2
+ };
+
+ this.estimation_directions = {
+ output_from_input: 0,
+ input_from_output: 1
+ };
+
+ this.state = {
+ coin_symbol: "xmr",
+ key_for_withdrawal_dialog: "xmr",
+ supports_output_memos: "",
+ url: citadelAPIs.BASE,
+ error: null,
+
+ // things that get displayed for deposits
+ deposit_input_coin_type: null,
+ deposit_output_coin_type: null,
+ input_address_and_memo: null,
+ deposit_estimated_input_amount:
+ this.props.initial_deposit_estimated_input_amount || "1.0",
+ deposit_estimated_output_amount: null,
+ deposit_limit: null,
+ deposit_error: null,
+ failed_calculate_deposit: null,
+
+ // things that get displayed for withdrawals
+ withdraw_input_coin_type: null,
+ withdraw_output_coin_type: null,
+ withdraw_estimated_input_amount:
+ this.props.initial_withdraw_estimated_input_amount || "1.0",
+ withdraw_estimated_output_amount: null,
+ withdraw_limit: null,
+ withdraw_error: null,
+ failed_calculate_withdraw: null,
+
+ // input address-related
+ coin_info_request_state: this.coin_info_request_states
+ .request_in_progress,
+ input_address_requests_in_progress: {},
+
+ // estimate-related
+ deposit_estimate_direction: this.estimation_directions
+ .output_from_input,
+
+ // deposit limit-related
+ deposit_limit_cache: {},
+ deposit_limit_requests_in_progress: {},
+
+ // generic data from BlockTrades
+ coins_by_type: null,
+ allowed_mappings_for_deposit: null,
+ allowed_mappings_for_withdraw: null,
+
+ // announcements data
+ announcements: []
+ };
+ }
+
+ urlConnection(checkUrl, state_coin_info) {
+ this.setState({
+ url: checkUrl
+ });
+
+ // get basic data from citadel
+ let coin_types_url = checkUrl + "/coins";
+ let coin_types_promise = fetch(coin_types_url, {
+ method: "get",
+ headers: new Headers({Accept: "application/json"})
+ }).then(response => response.json());
+
+ let wallet_types_url = checkUrl + "/wallets";
+ let wallet_types_promise = fetch(wallet_types_url, {
+ method: "get",
+ headers: new Headers({Accept: "application/json"})
+ }).then(response => response.json());
+
+ let trading_pairs_url = checkUrl + "/trading-pairs";
+ let trading_pairs_promise = fetch(trading_pairs_url, {
+ method: "get",
+ headers: new Headers({Accept: "application/json"})
+ }).then(response => response.json());
+
+ let active_wallets_url = checkUrl + "/active-wallets";
+ let active_wallets_promise = fetch(active_wallets_url, {
+ method: "get",
+ headers: new Headers({Accept: "application/json"})
+ }).then(response => response.json());
+
+ Promise.all([
+ coin_types_promise,
+ wallet_types_promise,
+ trading_pairs_promise,
+ active_wallets_promise
+ ])
+ .then(json_responses => {
+ let [
+ coin_types,
+ wallet_types_reply,
+ trading_pairs,
+ active_wallets
+ ] = json_responses;
+
+ // get quick access to coins by their types
+ let coins_by_type = {};
+ coin_types.forEach(
+ coin_type => (coins_by_type[coin_type.coinType] = coin_type)
+ );
+
+ // determine which mappings we will display for deposits and withdrawals
+ let allowed_mappings_for_deposit = {}; // all non-bts to bts
+ let allowed_mappings_for_withdraw = {}; // all bts to non-bts
+ trading_pairs.forEach(pair => {
+ let input_coin_info = coins_by_type[pair.inputCoinType];
+ let output_coin_info = coins_by_type[pair.outputCoinType];
+
+ // filter out pairs where one asset is a backed asset and the other is a backing asset,
+ // those pairs rightly belong under the gateway section, not under the bridge section.
+ if (
+ input_coin_info.backingCoinType !=
+ pair.outputCoinType ||
+ (output_coin_info.backingCoinType !=
+ pair.inputCoinType &&
+ input_coin_info.restricted == false &&
+ output_coin_info.restricted == false)
+ ) {
+ // filter out mappings where one of the wallets is offline
+ if (
+ active_wallets.indexOf(
+ input_coin_info.walletType
+ ) != -1 &&
+ active_wallets.indexOf(
+ output_coin_info.walletType
+ ) != -1
+ ) {
+ if (
+ input_coin_info.walletType != "bitshares2" &&
+ output_coin_info.walletType == "bitshares2"
+ ) {
+ allowed_mappings_for_deposit[
+ pair.inputCoinType
+ ] =
+ allowed_mappings_for_deposit[
+ pair.inputCoinType
+ ] || [];
+ allowed_mappings_for_deposit[
+ pair.inputCoinType
+ ].push(pair.outputCoinType);
+ } else {
+ allowed_mappings_for_withdraw[
+ pair.inputCoinType
+ ] =
+ allowed_mappings_for_withdraw[
+ pair.inputCoinType
+ ] || [];
+ allowed_mappings_for_withdraw[
+ pair.inputCoinType
+ ].push(pair.outputCoinType);
+ }
+ }
+ }
+ });
+
+ // we can now set the input and output coin types
+ let deposit_input_coin_type = null;
+ let deposit_output_coin_type = null;
+ let allowed_deposit_coin_types = Object.keys(
+ allowed_mappings_for_deposit
+ );
+ allowed_deposit_coin_types.forEach(deposit_coin_type => {
+ allowed_mappings_for_deposit[deposit_coin_type].sort();
+ });
+
+ if (allowed_deposit_coin_types.length) {
+ if (
+ this.props.initial_deposit_input_coin_type &&
+ this.props.initial_deposit_input_coin_type in
+ allowed_mappings_for_deposit
+ )
+ deposit_input_coin_type = this.props
+ .initial_deposit_input_coin_type;
+ else
+ deposit_input_coin_type = allowed_deposit_coin_types[0];
+ let output_coin_types_for_this_input =
+ allowed_mappings_for_deposit[deposit_input_coin_type];
+ if (
+ this.props.initial_deposit_output_coin_type &&
+ output_coin_types_for_this_input.indexOf(
+ this.props.initial_deposit_output_coin_type
+ ) != -1
+ )
+ deposit_output_coin_type = this.props
+ .initial_deposit_output_coin_type;
+ else
+ deposit_output_coin_type =
+ output_coin_types_for_this_input[0];
+ }
+
+ let withdraw_input_coin_type = null;
+ let withdraw_output_coin_type = null;
+ let allowed_withdraw_coin_types = Object.keys(
+ allowed_mappings_for_withdraw
+ );
+ allowed_withdraw_coin_types.forEach(withdraw_coin_type => {
+ allowed_mappings_for_withdraw[withdraw_coin_type].sort();
+ });
+
+ if (allowed_withdraw_coin_types.length) {
+ if (
+ this.props.initial_withdraw_input_coin_type &&
+ this.props.initial_withdraw_input_coin_type in
+ allowed_mappings_for_withdraw
+ )
+ withdraw_input_coin_type = this.props
+ .initial_withdraw_input_coin_type;
+ else
+ withdraw_input_coin_type =
+ allowed_withdraw_coin_types[0];
+ let output_coin_types_for_this_input =
+ allowed_mappings_for_withdraw[withdraw_input_coin_type];
+ if (
+ this.props.initial_withdraw_output_coin_type &&
+ output_coin_types_for_this_input.indexOf(
+ this.props.initial_withdraw_output_coin_type
+ ) != -1
+ )
+ withdraw_output_coin_type = this.props
+ .initial_withdraw_output_coin_type;
+ else
+ withdraw_output_coin_type =
+ output_coin_types_for_this_input[0];
+ }
+
+ let input_address_and_memo = this.getCachedOrGeneratedInputAddress(
+ deposit_input_coin_type,
+ deposit_output_coin_type
+ );
+
+ let deposit_limit = this.getCachedOrFreshDepositLimit(
+ "deposit",
+ deposit_input_coin_type,
+ deposit_output_coin_type
+ );
+ let deposit_estimated_output_amount = this.getAndUpdateOutputEstimate(
+ "deposit",
+ deposit_input_coin_type,
+ deposit_output_coin_type,
+ this.state.deposit_estimated_input_amount
+ );
+
+ let withdraw_estimated_output_amount = this.getAndUpdateOutputEstimate(
+ "withdraw",
+ withdraw_input_coin_type,
+ withdraw_output_coin_type,
+ this.state.withdraw_estimated_input_amount
+ );
+ let withdraw_limit = this.getCachedOrFreshDepositLimit(
+ "withdraw",
+ withdraw_input_coin_type,
+ withdraw_output_coin_type
+ );
+
+ if (this.unMounted) return;
+
+ this.setState({
+ coin_info_request_state: this.coin_info_request_states
+ .request_complete,
+ coins_by_type: coins_by_type,
+ allowed_mappings_for_deposit: allowed_mappings_for_deposit,
+ allowed_mappings_for_withdraw: allowed_mappings_for_withdraw,
+ deposit_input_coin_type: deposit_input_coin_type,
+ deposit_output_coin_type: deposit_output_coin_type,
+ input_address_and_memo: input_address_and_memo,
+ deposit_limit: deposit_limit,
+ deposit_estimated_output_amount: deposit_estimated_output_amount,
+ deposit_estimate_direction: this.estimation_directions
+ .output_from_input,
+ withdraw_input_coin_type: withdraw_input_coin_type,
+ withdraw_output_coin_type: withdraw_output_coin_type,
+ withdraw_limit: withdraw_limit,
+ withdraw_estimated_output_amount: withdraw_estimated_output_amount,
+ withdraw_estimate_direction: this.estimation_directions
+ .output_from_input,
+ supports_output_memos:
+ coins_by_type["xmr"].supportsOutputMemos
+ });
+ })
+ .catch(error => {
+ this.setState({
+ coin_info_request_state: state_coin_info,
+ coins_by_type: null,
+ allowed_mappings_for_deposit: null,
+ allowed_mappings_for_withdraw: null
+ });
+ });
+ }
+
+ // functions for periodically updating our deposit limit and estimates
+ updateEstimates() {
+ if (
+ this.state.deposit_input_coin_type &&
+ this.state.deposit_output_coin_type
+ ) {
+ // input address won't usually need refreshing unless there was an error
+ // generating it last time around
+ let new_input_address_and_memo = this.getCachedOrGeneratedInputAddress(
+ this.state.deposit_input_coin_type,
+ this.state.deposit_output_coin_type
+ );
+
+ let new_deposit_limit = this.getCachedOrFreshDepositLimit(
+ "deposit",
+ this.state.deposit_input_coin_type,
+ this.state.deposit_output_coin_type
+ );
+ let new_deposit_estimated_input_amount = this.state
+ .deposit_estimated_input_amount;
+ let new_deposit_estimated_output_amount = this.state
+ .deposit_estimated_output_amount;
+
+ if (
+ this.state.deposit_estimate_direction ==
+ this.estimation_directions.output_from_input
+ )
+ new_deposit_estimated_output_amount = this.getAndUpdateOutputEstimate(
+ "deposit",
+ this.state.deposit_input_coin_type,
+ this.state.deposit_output_coin_type,
+ new_deposit_estimated_input_amount
+ );
+ else
+ new_deposit_estimated_input_amount = this.getAndUpdateInputEstimate(
+ "deposit",
+ this.state.deposit_input_coin_type,
+ this.state.deposit_output_coin_type,
+ new_deposit_estimated_output_amount
+ );
+
+ let new_withdraw_limit = this.getCachedOrFreshDepositLimit(
+ "withdraw",
+ this.state.withdraw_input_coin_type,
+ this.state.withdraw_output_coin_type
+ );
+ let new_withdraw_estimated_input_amount = this.state
+ .withdraw_estimated_input_amount;
+ let new_withdraw_estimated_output_amount = this.state
+ .withdraw_estimated_output_amount;
+
+ if (
+ this.state.withdraw_estimate_direction ==
+ this.estimation_directions.output_from_input
+ )
+ new_withdraw_estimated_output_amount = this.getAndUpdateOutputEstimate(
+ "withdraw",
+ this.state.withdraw_input_coin_type,
+ this.state.withdraw_output_coin_type,
+ new_withdraw_estimated_input_amount
+ );
+ else
+ new_withdraw_estimated_input_amount = this.getAndUpdateinputEstimate(
+ "withdraw",
+ this.state.withdraw_input_coin_type,
+ this.state.withdraw_output_coin_type,
+ new_withdraw_estimated_output_amount
+ );
+
+ this.setState({
+ input_address_and_memo: new_input_address_and_memo,
+ deposit_limit: new_deposit_limit,
+ deposit_estimated_input_amount: new_deposit_estimated_input_amount,
+ deposit_estimated_output_amount: new_deposit_estimated_output_amount,
+ withdraw_limit: new_withdraw_limit,
+ withdraw_estimated_input_amount: new_withdraw_estimated_input_amount,
+ withdraw_estimated_output_amount: new_withdraw_estimated_output_amount,
+ key_for_withdrawal_dialog: new_withdraw_estimated_input_amount
+ });
+ }
+ }
+
+ componentWillMount() {
+ // check
+ let checkUrl = this.state.url;
+ this.urlConnection(checkUrl, 0);
+ let coin_types_promisecheck = fetch(checkUrl + "/coins", {
+ method: "get",
+ headers: new Headers({Accept: "application/json"})
+ }).then(response => response.json());
+ let trading_pairs_promisecheck = fetch(checkUrl + "/trading-pairs", {
+ method: "get",
+ headers: new Headers({Accept: "application/json"})
+ }).then(response => response.json());
+ let active_wallets_promisecheck = fetch(checkUrl + "/active-wallets", {
+ method: "get",
+ headers: new Headers({Accept: "application/json"})
+ }).then(response => response.json());
+ Promise.all([
+ coin_types_promisecheck,
+ trading_pairs_promisecheck,
+ active_wallets_promisecheck
+ ])
+ .then(json_responses => {
+ let [
+ coin_types,
+ trading_pairs,
+ active_wallets
+ ] = json_responses;
+ let coins_by_type = {};
+ coin_types.forEach(
+ coin_type => (coins_by_type[coin_type.coinType] = coin_type)
+ );
+ trading_pairs.forEach(pair => {
+ let input_coin_info = coins_by_type[pair.inputCoinType];
+ let output_coin_info = coins_by_type[pair.outputCoinType];
+ if (
+ input_coin_info.backingCoinType !=
+ pair.outputCoinType &&
+ output_coin_info.backingCoinType != pair.inputCoinType
+ ) {
+ if (
+ active_wallets.indexOf(
+ input_coin_info.walletType
+ ) != -1 &&
+ active_wallets.indexOf(
+ output_coin_info.walletType
+ ) != -1
+ ) {
+ }
+ }
+ });
+ })
+ .catch(error => {
+ this.urlConnection("https://citadel.li/trade", 2);
+ this.setState({
+ coin_info_request_state: 0,
+ coins_by_type: null,
+ allowed_mappings_for_deposit: null,
+ allowed_mappings_for_withdraw: null
+ });
+ });
+ }
+
+ componentDidMount() {
+ this.update_timer = setInterval(
+ this.updateEstimates.bind(this),
+ this.refresh_interval
+ );
+ }
+
+ componentWillUnmount() {
+ clearInterval(this.update_timer);
+ this.unMounted = true;
+ }
+
+ // functions for managing input addresses
+ getCachedInputAddress(input_coin_type, output_coin_type, memo) {
+ let account_name = this.props.account.get("name");
+ return this.deposit_address_cache.getCachedInputAddress(
+ this.props.gateway,
+ account_name,
+ input_coin_type,
+ output_coin_type
+ );
+ }
+
+ cacheInputAddress(input_coin_type, output_coin_type, address, memo) {
+ let account_name = this.props.account.get("name");
+ this.deposit_address_cache.cacheInputAddress(
+ this.props.gateway,
+ account_name,
+ input_coin_type,
+ output_coin_type,
+ address,
+ memo
+ );
+ }
+
+ getCachedOrGeneratedInputAddress(input_coin_type, output_coin_type) {
+ // if we already have an address, just return it
+ let cached_input_address_and_memo = this.getCachedInputAddress(
+ input_coin_type,
+ output_coin_type
+ );
+ if (cached_input_address_and_memo) {
+ return cached_input_address_and_memo;
+ }
+
+ // if we've already asked for this address, return null, it will trigger a refresh when it completes
+ this.state.input_address_requests_in_progress[input_coin_type] =
+ this.state.input_address_requests_in_progress[input_coin_type] ||
+ {};
+ if (
+ this.state.input_address_requests_in_progress[input_coin_type][
+ output_coin_type
+ ]
+ )
+ return null;
+
+ // else, no active request for this mapping, kick one off
+ let body = JSON.stringify({
+ inputCoinType: input_coin_type,
+ outputCoinType: output_coin_type,
+ outputAddress: this.props.account.get("name")
+ });
+
+ this.state.input_address_requests_in_progress[input_coin_type][
+ output_coin_type
+ ] = true;
+
+ fetch(this.state.url + "/simple-api/initiate-trade", {
+ method: "post",
+ headers: new Headers({
+ Accept: "application/json",
+ "Content-Type": "application/json"
+ }),
+ body: body
+ }).then(
+ reply => {
+ reply.json().then(
+ json => {
+ console.assert(
+ json.inputCoinType == input_coin_type,
+ "unexpected reply from initiate-trade"
+ );
+ console.assert(
+ json.outputCoinType == output_coin_type,
+ "unexpected reply from initiate-trade"
+ );
+ if (
+ json.inputCoinType != input_coin_type ||
+ json.outputCoinType != output_coin_type
+ )
+ throw Error("unexpected reply from initiate-trade");
+ this.cacheInputAddress(
+ json.inputCoinType,
+ json.outputCoinType,
+ json.inputAddress,
+ json.inputMemo
+ );
+ delete this.state.input_address_requests_in_progress[
+ input_coin_type
+ ][output_coin_type];
+ if (
+ this.state.deposit_input_coin_type ==
+ json.inputCoinType &&
+ this.state.deposit_output_coin_type ==
+ json.outputCoinType
+ )
+ this.setState({
+ input_address_and_memo: {
+ address: json.inputAddress,
+ memo: json.inputMemo
+ }
+ });
+ },
+ error => {
+ delete this.state.input_address_requests_in_progress[
+ input_coin_type
+ ][output_coin_type];
+ if (
+ this.state.deposit_input_coin_type ==
+ input_coin_type &&
+ this.state.deposit_output_coin_type ==
+ output_coin_type
+ )
+ this.setState({
+ input_address_and_memo: {
+ address: "error generating address",
+ memo: null
+ }
+ });
+ }
+ );
+ },
+ error => {
+ delete this.state.input_address_requests_in_progress[
+ input_coin_type
+ ][output_coin_type];
+ if (
+ this.state.deposit_input_coin_type == input_coin_type &&
+ this.state.deposit_output_coin_type == output_coin_type
+ )
+ this.setState({
+ input_address_and_memo: {
+ address: "error generating address",
+ memo: null
+ }
+ });
+ }
+ );
+ return null;
+ }
+
+ // functions for managing deposit limits
+ getCachedDepositLimit(input_coin_type, output_coin_type) {
+ this.state.deposit_limit_cache[input_coin_type] =
+ this.state.deposit_limit_cache[input_coin_type] || {};
+ if (this.state.deposit_limit_cache[input_coin_type][output_coin_type]) {
+ let deposit_limit_record = this.state.deposit_limit_cache[
+ input_coin_type
+ ][output_coin_type];
+ let cache_age = new Date() - deposit_limit_record.timestamp;
+ if (cache_age < this.refresh_interval) return deposit_limit_record;
+ delete this.state.deposit_limit_cache[input_coin_type][
+ output_coin_type
+ ];
+ }
+ return null;
+ }
+
+ cacheDepositLimit(input_coin_type, output_coin_type, deposit_limit_record) {
+ deposit_limit_record.timestamp = new Date();
+ this.state.deposit_limit_cache[input_coin_type] =
+ this.state.deposit_limit_cache[input_coin_type] || {};
+ this.state.deposit_limit_cache[input_coin_type][
+ output_coin_type
+ ] = deposit_limit_record;
+ }
+
+ getCachedOrFreshDepositLimit(
+ deposit_withdraw_or_convert,
+ input_coin_type,
+ output_coin_type
+ ) {
+ let deposit_limit_record = this.getCachedDepositLimit(
+ input_coin_type,
+ output_coin_type
+ );
+
+ if (deposit_limit_record) return deposit_limit_record;
+
+ this.state.deposit_limit_requests_in_progress[input_coin_type] =
+ this.state.input_address_requests_in_progress[input_coin_type] ||
+ {};
+ this.state.deposit_limit_requests_in_progress[input_coin_type][
+ output_coin_type
+ ] = true;
+
+ let deposit_limit_url =
+ this.state.url +
+ "/deposit-limits?inputCoinType=" +
+ encodeURIComponent(input_coin_type) +
+ "&outputCoinType=" +
+ encodeURIComponent(output_coin_type);
+
+ let deposit_limit_promise = fetch(deposit_limit_url, {
+ method: "get",
+ headers: new Headers({Accept: "application/json"})
+ }).then(response => response.json());
+ deposit_limit_promise.then(
+ reply => {
+ if (this.unMounted) return;
+ console.assert(
+ reply.inputCoinType == input_coin_type &&
+ reply.outputCoinType == output_coin_type,
+ "unexpected reply from deposit-limits"
+ );
+ if (
+ reply.inputCoinType != input_coin_type ||
+ reply.outputCoinType != output_coin_type
+ )
+ throw Error("unexpected reply from deposit-limits");
+ let new_deposit_limit_record = {
+ timestamp: new Date(),
+ limit: reply.depositLimit
+ };
+ this.cacheDepositLimit(
+ input_coin_type,
+ output_coin_type,
+ new_deposit_limit_record
+ );
+ delete this.state.deposit_limit_requests_in_progress[
+ input_coin_type
+ ][output_coin_type];
+ if (
+ this.state[
+ deposit_withdraw_or_convert + "_input_coin_type"
+ ] == input_coin_type &&
+ this.state[
+ deposit_withdraw_or_convert + "_output_coin_type"
+ ] == output_coin_type
+ )
+ this.setState({
+ [deposit_withdraw_or_convert +
+ "_limit"]: new_deposit_limit_record
+ });
+ },
+ error => {
+ delete this.state.deposit_limit_requests_in_progress[
+ input_coin_type
+ ][output_coin_type];
+ }
+ );
+ return null;
+ }
+
+ getAndUpdateOutputEstimate(
+ deposit_withdraw_or_convert,
+ input_coin_type,
+ output_coin_type,
+ input_amount
+ ) {
+ if (this.unMounted) return;
+ if (deposit_withdraw_or_convert == "deposit") {
+ this.setState({failed_calculate_deposit: null});
+ }
+ if (deposit_withdraw_or_convert == "withdraw") {
+ this.setState({failed_calculate_withdraw: null});
+ }
+
+ let estimate_output_url =
+ this.state.url +
+ "/estimate-output-amount?inputAmount=" +
+ encodeURIComponent(input_amount) +
+ "&inputCoinType=" +
+ encodeURIComponent(input_coin_type) +
+ "&outputCoinType=" +
+ encodeURIComponent(output_coin_type);
+
+ let estimate_output_promise = fetch(estimate_output_url, {
+ method: "get",
+ headers: new Headers({Accept: "application/json"})
+ }).then(response => response.json());
+ estimate_output_promise.then(
+ reply => {
+ if (this.unMounted) return;
+ if (reply.error) {
+ if (
+ this.state[
+ deposit_withdraw_or_convert + "_input_coin_type"
+ ] == input_coin_type &&
+ this.state[
+ deposit_withdraw_or_convert + "_output_coin_type"
+ ] == output_coin_type &&
+ this.state[
+ deposit_withdraw_or_convert +
+ "_estimated_input_amount"
+ ] == input_amount &&
+ this.state[
+ deposit_withdraw_or_convert + "_estimate_direction"
+ ] == this.estimation_directions.output_from_input
+ ) {
+ let user_message = reply.error.message;
+
+ if (deposit_withdraw_or_convert == "deposit") {
+ this.setState({
+ failed_calculate_deposit: "Failed to calculate"
+ });
+ }
+ if (deposit_withdraw_or_convert == "withdraw") {
+ this.setState({
+ failed_calculate_withdraw: "Failed to calculate"
+ });
+ }
+
+ let expected_prefix = "Internal Server Error: ";
+ if (user_message.startsWith(expected_prefix))
+ user_message = user_message.substr(
+ expected_prefix.length
+ );
+
+ this.setState({
+ [deposit_withdraw_or_convert +
+ "_error"]: user_message
+ });
+ }
+ } else {
+ console.assert(
+ reply.inputCoinType == input_coin_type &&
+ reply.outputCoinType == output_coin_type &&
+ reply.inputAmount == input_amount,
+ "unexpected reply from estimate-output-amount"
+ );
+ if (
+ reply.inputCoinType != input_coin_type ||
+ reply.outputCoinType != output_coin_type ||
+ reply.inputAmount != input_amount
+ ) {
+ this.setState({
+ [deposit_withdraw_or_convert +
+ "_estimated_output_amount"]: reply.outputAmount,
+ [deposit_withdraw_or_convert + "_error"]: null
+ });
+ }
+ if (
+ this.state[
+ deposit_withdraw_or_convert + "_input_coin_type"
+ ] == input_coin_type &&
+ this.state[
+ deposit_withdraw_or_convert + "_output_coin_type"
+ ] == output_coin_type &&
+ this.state[
+ deposit_withdraw_or_convert +
+ "_estimated_input_amount"
+ ] == input_amount &&
+ this.state[
+ deposit_withdraw_or_convert + "_estimate_direction"
+ ] == this.estimation_directions.output_from_input
+ ) {
+ this.setState({
+ [deposit_withdraw_or_convert +
+ "_estimated_output_amount"]: reply.outputAmount,
+ [deposit_withdraw_or_convert + "_error"]: null
+ });
+ }
+ }
+ },
+ error => {}
+ );
+
+ return null;
+ }
+
+ getAndUpdateInputEstimate(
+ deposit_withdraw_or_convert,
+ input_coin_type,
+ output_coin_type,
+ output_amount
+ ) {
+ if (this.unMounted) return;
+ if (deposit_withdraw_or_convert == "deposit") {
+ this.setState({failed_calculate_deposit: null});
+ }
+ if (deposit_withdraw_or_convert == "withdraw") {
+ this.setState({failed_calculate_withdraw: null});
+ }
+
+ let estimate_input_url =
+ this.state.url +
+ "/estimate-input-amount?outputAmount=" +
+ encodeURIComponent(output_amount) +
+ "&inputCoinType=" +
+ encodeURIComponent(input_coin_type) +
+ "&outputCoinType=" +
+ encodeURIComponent(output_coin_type);
+ let estimate_input_promise = fetch(estimate_input_url, {
+ method: "get",
+ headers: new Headers({Accept: "application/json"})
+ }).then(response => response.json());
+ estimate_input_promise.then(
+ reply => {
+ if (this.unMounted) return;
+
+ console.assert(
+ reply.inputCoinType == input_coin_type &&
+ reply.outputCoinType == output_coin_type &&
+ reply.outputAmount == output_amount,
+ "unexpected reply from estimate-input-amount"
+ );
+ if (
+ reply.inputCoinType != input_coin_type ||
+ reply.outputCoinType != output_coin_type ||
+ reply.outputAmount != output_amount
+ ) {
+ if (deposit_withdraw_or_convert == "deposit") {
+ this.setState({
+ failed_calculate_deposit: "Failed to calculate"
+ });
+ }
+ if (deposit_withdraw_or_convert == "withdraw") {
+ this.setState({
+ failed_calculate_withdraw: "Failed to calculate"
+ });
+ }
+ }
+
+ if (
+ this.state[
+ deposit_withdraw_or_convert + "_input_coin_type"
+ ] == input_coin_type &&
+ this.state[
+ deposit_withdraw_or_convert + "_output_coin_type"
+ ] == output_coin_type &&
+ this.state[
+ deposit_withdraw_or_convert + "_estimated_output_amount"
+ ] == output_amount &&
+ this.state[
+ deposit_withdraw_or_convert + "_estimate_direction"
+ ] == this.estimation_directions.input_from_output
+ )
+ this.setState({
+ [deposit_withdraw_or_convert +
+ "_estimated_input_amount"]: reply.inputAmount,
+ key_for_withdrawal_dialog: reply.inputAmount
+ });
+ },
+ error => {}
+ );
+
+ return null;
+ }
+
+ onInputAmountChanged(deposit_withdraw_or_convert, event) {
+ let new_estimated_input_amount = event.target.value;
+ if (new_estimated_input_amount == "") {
+ new_estimated_input_amount = "0";
+ }
+
+ let new_estimated_output_amount = this.getAndUpdateOutputEstimate(
+ deposit_withdraw_or_convert,
+ this.state[deposit_withdraw_or_convert + "_input_coin_type"],
+ this.state[deposit_withdraw_or_convert + "_output_coin_type"],
+ new_estimated_input_amount
+ );
+
+ this.setState({
+ [deposit_withdraw_or_convert +
+ "_estimated_input_amount"]: new_estimated_input_amount,
+ [deposit_withdraw_or_convert +
+ "_estimated_output_amount"]: new_estimated_output_amount,
+ [deposit_withdraw_or_convert + "_estimate_direction"]: this
+ .estimation_directions.output_from_input,
+ key_for_withdrawal_dialog: new_estimated_input_amount
+ });
+ }
+
+ onOutputAmountChanged(deposit_withdraw_or_convert, event) {
+ let new_estimated_output_amount = event.target.value;
+ if (new_estimated_output_amount == "") {
+ new_estimated_output_amount = "0";
+ }
+
+ let new_estimated_input_amount = this.getAndUpdateInputEstimate(
+ deposit_withdraw_or_convert,
+ this.state[deposit_withdraw_or_convert + "_input_coin_type"],
+ this.state[deposit_withdraw_or_convert + "_output_coin_type"],
+ new_estimated_output_amount
+ );
+
+ this.setState({
+ [deposit_withdraw_or_convert +
+ "_estimated_output_amount"]: new_estimated_output_amount,
+ [deposit_withdraw_or_convert +
+ "_estimated_input_amount"]: new_estimated_input_amount,
+ [deposit_withdraw_or_convert + "_estimate_direction"]: this
+ .estimation_directions.input_from_output
+ });
+ }
+
+ getWithdrawModalId() {
+ return "withdraw_asset_" + this.props.gateway + "_bridge";
+ }
+
+ onWithdraw() {
+ ZfApi.publish(this.getWithdrawModalId(), "open");
+ }
+
+ onInputCoinTypeChanged(deposit_withdraw_or_convert, event) {
+ let estimated_input_output_amount = null;
+ let estimated_input_output_amount_state = "_estimated_output_amount";
+ let new_input_coin_type = event.target.value;
+ let possible_output_coin_types = this.state[
+ "allowed_mappings_for_" + deposit_withdraw_or_convert
+ ][new_input_coin_type];
+ let new_output_coin_type = possible_output_coin_types[0];
+
+ if (
+ possible_output_coin_types.indexOf(
+ this.state[deposit_withdraw_or_convert + "_output_coin_type"]
+ ) != -1
+ )
+ new_output_coin_type = this.state[
+ deposit_withdraw_or_convert + "_output_coin_type"
+ ];
+
+ let new_input_address_and_memo = this.state.input_address_and_memo;
+ if (deposit_withdraw_or_convert == "deposit")
+ new_input_address_and_memo = this.getCachedOrGeneratedInputAddress(
+ new_input_coin_type,
+ new_output_coin_type
+ );
+ let new_deposit_limit = this.getCachedOrFreshDepositLimit(
+ deposit_withdraw_or_convert,
+ new_input_coin_type,
+ new_output_coin_type
+ );
+
+ if (
+ !this.state[deposit_withdraw_or_convert + "_estimated_input_amount"]
+ ) {
+ estimated_input_output_amount = this.getAndUpdateInputEstimate(
+ deposit_withdraw_or_convert,
+ new_input_coin_type,
+ new_output_coin_type,
+ this.state[
+ deposit_withdraw_or_convert + "_estimated_output_amount"
+ ]
+ );
+ estimated_input_output_amount_state = "_estimated_input_amount";
+ } else {
+ estimated_input_output_amount = this.getAndUpdateOutputEstimate(
+ deposit_withdraw_or_convert,
+ new_input_coin_type,
+ new_output_coin_type,
+ this.state[
+ deposit_withdraw_or_convert + "_estimated_input_amount"
+ ]
+ );
+ }
+
+ if (deposit_withdraw_or_convert == "withdraw") {
+ possible_output_coin_types.forEach(
+ allowed_withdraw_output_coin_type => {
+ if (
+ new_output_coin_type ===
+ allowed_withdraw_output_coin_type
+ ) {
+ this.setState({
+ coin_symbol: new_input_coin_type + "input",
+ supports_output_memos: this.state.coins_by_type[
+ allowed_withdraw_output_coin_type
+ ].supportsOutputMemos
+ });
+ }
+ }
+ );
+ }
+
+ this.setState({
+ [deposit_withdraw_or_convert +
+ "_input_coin_type"]: new_input_coin_type,
+ [deposit_withdraw_or_convert +
+ "_output_coin_type"]: new_output_coin_type,
+ input_address_and_memo: new_input_address_and_memo,
+ [deposit_withdraw_or_convert + "_limit"]: new_deposit_limit,
+ [deposit_withdraw_or_convert +
+ estimated_input_output_amount_state]: estimated_input_output_amount,
+ [deposit_withdraw_or_convert + "_estimate_direction"]: this
+ .estimation_directions.output_from_input
+ });
+ }
+
+ onOutputCoinTypeChanged(deposit_withdraw_or_convert, event) {
+ let estimated_input_output_amount = null;
+ let estimated_input_output_amount_state = "_estimated_output_amount";
+ let new_output_coin_type = event.target.value;
+ let withdraw_output_coin_types = this.state
+ .allowed_mappings_for_withdraw[this.state.withdraw_input_coin_type];
+
+ if (deposit_withdraw_or_convert == "withdraw") {
+ withdraw_output_coin_types.forEach(
+ allowed_withdraw_output_coin_type => {
+ if (
+ new_output_coin_type ===
+ allowed_withdraw_output_coin_type
+ ) {
+ this.setState({
+ coin_symbol: new_output_coin_type + "output",
+ supports_output_memos: this.state.coins_by_type[
+ allowed_withdraw_output_coin_type
+ ].supportsOutputMemos,
+ key_for_withdrawal_dialog: new_output_coin_type
+ });
+ }
+ }
+ );
+ }
+
+ let new_input_address_and_memo = this.state.input_address_and_memo;
+ if (deposit_withdraw_or_convert == "deposit")
+ new_input_address_and_memo = this.getCachedOrGeneratedInputAddress(
+ this.state[deposit_withdraw_or_convert + "_input_coin_type"],
+ new_output_coin_type
+ );
+ let new_deposit_limit = this.getCachedOrFreshDepositLimit(
+ deposit_withdraw_or_convert,
+ this.state[deposit_withdraw_or_convert + "_input_coin_type"],
+ new_output_coin_type
+ );
+
+ if (
+ !this.state[deposit_withdraw_or_convert + "_estimated_input_amount"]
+ ) {
+ estimated_input_output_amount = this.getAndUpdateInputEstimate(
+ deposit_withdraw_or_convert,
+ this.state[deposit_withdraw_or_convert + "_input_coin_type"],
+ new_output_coin_type,
+ this.state[
+ deposit_withdraw_or_convert + "_estimated_output_amount"
+ ]
+ );
+ estimated_input_output_amount_state = "_estimated_input_amount";
+ } else {
+ estimated_input_output_amount = this.getAndUpdateOutputEstimate(
+ deposit_withdraw_or_convert,
+ this.state[deposit_withdraw_or_convert + "_input_coin_type"],
+ new_output_coin_type,
+ this.state[
+ deposit_withdraw_or_convert + "_estimated_input_amount"
+ ]
+ );
+ }
+
+ this.setState({
+ [deposit_withdraw_or_convert +
+ "_output_coin_type"]: new_output_coin_type,
+ input_address_and_memo: new_input_address_and_memo,
+ [deposit_withdraw_or_convert + "_limit"]: new_deposit_limit,
+ [deposit_withdraw_or_convert +
+ estimated_input_output_amount_state]: estimated_input_output_amount,
+ [deposit_withdraw_or_convert + "_estimate_direction"]: this
+ .estimation_directions.output_from_input
+ });
+ }
+
+ render() {
+ if (
+ !this.props.account ||
+ !this.props.issuer_account ||
+ !this.props.gateway
+ )
+ return
;
+
+ let announcements,
+ deposit_body,
+ deposit_header,
+ withdraw_body,
+ withdraw_header,
+ conversion_body,
+ conversion_header,
+ withdraw_modal_id,
+ conversion_modal_id;
+
+ if (
+ this.state.coin_info_request_state ==
+ this.coin_info_request_states.request_failed
+ ) {
+ return (
+
+
+ Error connecting to citadel.li, please try again later
+
+
+ );
+ } else if (
+ this.state.coin_info_request_state ==
+ this.coin_info_request_states.never_requested ||
+ this.state.coin_info_request_state ==
+ this.coin_info_request_states.request_in_progress
+ ) {
+ return (
+
+
Retrieving current trade data from citadel.li
+
+ );
+ } else {
+ // depending on what wallets are online, we might support deposits, withdrawals, conversions, all, or neither at any given time.
+ let deposit_table = null;
+ let withdraw_table = null;
+ let amount_to_withdraw = null;
+
+ let calcTextDeposit =
;
+ if (this.state.failed_calculate_deposit != null) {
+ calcTextDeposit = this.state.failed_calculate_deposit;
+ }
+ let calcTextWithdraw =
;
+ if (this.state.failed_calculate_withdraw != null) {
+ calcTextWithdraw = this.state.failed_calculate_withdraw;
+ }
+ let calcTextConversion =
;
+ if (this.state.failed_calculate_conversion != null) {
+ calcTextConversion = this.state.failed_calculate_conversion;
+ }
+
+ if (
+ Object.getOwnPropertyNames(
+ this.state.allowed_mappings_for_deposit
+ ).length > 0
+ ) {
+ // deposit
+ let deposit_input_coin_type_options = [];
+ Object.keys(this.state.allowed_mappings_for_deposit)
+ .sort()
+ .forEach(allowed_deposit_input_coin_type => {
+ deposit_input_coin_type_options.push(
+
+ );
+ });
+ let deposit_input_coin_type_select = (
+
+ );
+
+ let deposit_output_coin_type_options = [];
+ let deposit_output_coin_types = this.state
+ .allowed_mappings_for_deposit[
+ this.state.deposit_input_coin_type
+ ];
+ deposit_output_coin_types.forEach(
+ allowed_deposit_output_coin_type => {
+ deposit_output_coin_type_options.push(
+
+ );
+ }
+ );
+ let deposit_output_coin_type_select = (
+
+ );
+
+ let input_address_and_memo = this.state.input_address_and_memo
+ ? this.state.input_address_and_memo
+ : {address: "unknown", memo: null};
+
+ let estimated_input_amount_text = this.state
+ .deposit_estimated_input_amount;
+ let estimated_output_amount_text = this.state
+ .deposit_estimated_output_amount;
+
+ let deposit_input_amount_edit_box = estimated_input_amount_text ? (
+
+ ) : (
+ calcTextDeposit
+ );
+ let deposit_output_amount_edit_box = estimated_output_amount_text ? (
+
+ ) : (
+ calcTextDeposit
+ );
+
+ let deposit_limit_element =
updating;
+ if (this.state.deposit_limit) {
+ if (this.state.deposit_limit.limit)
+ deposit_limit_element = (
+
+
+
+
+
+ );
+ else deposit_limit_element = null;
+ //else
+ // deposit_limit_element =
no limit;
+ }
+
+ let deposit_error_element = null;
+ if (this.state.deposit_error)
+ deposit_error_element = (
+
{this.state.deposit_error}
+ );
+
+ deposit_header = (
+
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ );
+
+ let deposit_address_and_memo_element = null;
+ if (input_address_and_memo.memo)
+ deposit_address_and_memo_element = (
+
+ );
+ else
+ deposit_address_and_memo_element = (
+
{input_address_and_memo.address}
+ );
+ //
with memo {input_address_and_memo.memo};
+
+ deposit_body = (
+
+
+
+
+
+
+ {deposit_input_coin_type_select}
+
+
+ {deposit_input_amount_edit_box}
+
+
+ →
+
+
+ {deposit_output_coin_type_select}
+
+
+ {deposit_output_amount_edit_box}
+
+
+ {deposit_error_element}
+
+ |
+
+
+ |
+
+ {deposit_address_and_memo_element}
+
+ {deposit_limit_element}
+ |
+
+
+ );
+ }
+
+ if (
+ Object.getOwnPropertyNames(
+ this.state.allowed_mappings_for_withdraw
+ ).length > 0
+ ) {
+ withdraw_modal_id = this.getWithdrawModalId();
+ let withdraw_asset_symbol = this.state.coins_by_type[
+ this.state.withdraw_input_coin_type
+ ].symbol;
+
+ // withdrawal
+
+ amount_to_withdraw = this.state.withdraw_estimated_input_amount;
+
+ let withdraw_input_coin_type_options = [];
+ Object.keys(this.state.allowed_mappings_for_withdraw)
+ .sort()
+ .forEach(allowed_withdraw_input_coin_type => {
+ withdraw_input_coin_type_options.push(
+
+ );
+ });
+ let withdraw_input_coin_type_select = (
+
+ );
+
+ let withdraw_output_coin_type_options = [];
+ let withdraw_output_coin_types = this.state
+ .allowed_mappings_for_withdraw[
+ this.state.withdraw_input_coin_type
+ ];
+ withdraw_output_coin_types.forEach(
+ allowed_withdraw_output_coin_type => {
+ withdraw_output_coin_type_options.push(
+
+ );
+ }
+ );
+ let withdraw_output_coin_type_select = (
+
+ );
+
+ let estimated_input_amount_text = this.state
+ .withdraw_estimated_input_amount;
+
+ let withdraw_input_amount_edit_box = estimated_input_amount_text ? (
+
+ ) : (
+ calcTextWithdraw
+ );
+
+ let estimated_output_amount_text = this.state
+ .withdraw_estimated_output_amount;
+
+ let withdraw_output_amount_edit_box = estimated_output_amount_text ? (
+
+ ) : (
+ calcTextWithdraw
+ );
+
+ let withdraw_button = (
+
+ );
+
+ let withdraw_error_element = null;
+ if (this.state.withdraw_error)
+ withdraw_error_element = (
+
{this.state.withdraw_error}
+ );
+
+ let withdraw_limit_element =
...;
+ if (this.state.withdraw_limit) {
+ if (this.state.withdraw_limit.limit)
+ withdraw_limit_element = (
+
+
+
+
+
+ );
+ else
+ withdraw_limit_element = (
+
+ no limit
+
+ );
+ }
+
+ withdraw_header = (
+
+
+
+
+ |
+
+
+ |
+ |
+
+
+ );
+
+ withdraw_body = (
+
+
+
+
+
+
+ {withdraw_input_coin_type_select}
+
+
+ {withdraw_input_amount_edit_box}
+
+
+ →
+
+
+ {withdraw_output_coin_type_select}
+
+
+ {withdraw_output_amount_edit_box}
+
+
+ {withdraw_error_element}
+
+ |
+
+
+ |
+
+ {withdraw_button}
+
+ {withdraw_limit_element}
+ |
+
+
+ );
+ }
+
+ if (this.state.announcements.length > 0) {
+ announcements = (
+
+ {this.state.announcements.map(function(data, index) {
+ let add_color = "txtann info";
+
+ if (data.status === 10) {
+ add_color = "txtann error";
+ } else if (data.status === 20) {
+ add_color = "txtann warning";
+ } else if (data.status === 30) {
+ add_color = "txtann success";
+ } else if (data.status === 40) {
+ add_color = "txtann info";
+ }
+
+ if (data.format === 1) {
+ data.message.replace(/\r\n|\r|\n/g, "
");
+ }
+
+ return (
+
+ {data.message}
+
+ );
+ }, this)}
+
+ );
+ }
+
+ return (
+
+
+
+
+ {announcements}
+
+ {deposit_header}
+ {deposit_body}
+ {withdraw_header}
+ {withdraw_body}
+
+
+ );
+ }
+ }
+} // CitadelBridgeDepositRequest
+
+export default BindToChainState(CitadelBridgeDepositRequest);
diff --git a/app/components/DepositWithdraw/citadel/CitadelGateway.jsx b/app/components/DepositWithdraw/citadel/CitadelGateway.jsx
new file mode 100755
index 0000000000..92abb939f0
--- /dev/null
+++ b/app/components/DepositWithdraw/citadel/CitadelGateway.jsx
@@ -0,0 +1,291 @@
+import React from "react";
+import CitadelGatewayDepositRequest from "./CitadelGatewayDepositRequest";
+import Translate from "react-translate-component";
+import {connect} from "alt-react";
+import SettingsStore from "stores/SettingsStore";
+import SettingsActions from "actions/SettingsActions";
+import {
+ RecentTransactions,
+ TransactionWrapper
+} from "components/Account/RecentTransactions";
+import Immutable from "immutable";
+import cnames from "classnames";
+import LoadingIndicator from "../../LoadingIndicator";
+
+class CitadelGateway extends React.Component {
+ constructor(props) {
+ super();
+
+ const action = props.viewSettings.get(
+ `${props.provider}Action`,
+ "deposit"
+ );
+ this.state = {
+ activeCoin: this._getActiveCoin(props, {action}),
+ action
+ };
+ }
+
+ _getActiveCoin(props, state) {
+ let cachedCoin = props.viewSettings.get(
+ `activeCoin_citadel_${state.action}`,
+ null
+ );
+ let firstTimeCoin = null;
+ if (state.action == "deposit") {
+ firstTimeCoin = "XMR";
+ }
+ if (state.action == "withdraw") {
+ firstTimeCoin = "XMR";
+ }
+ let activeCoin = cachedCoin ? cachedCoin : firstTimeCoin;
+ return activeCoin;
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (nextProps.provider !== this.props.provider) {
+ this.setState({
+ activeCoin: this._getActiveCoin(nextProps, this.state.action)
+ });
+ }
+ }
+
+ onSelectCoin(e) {
+ this.setState({
+ activeCoin: e.target.value
+ });
+
+ let setting = {};
+ setting[`activeCoin_citadel_${this.state.action}`] = e.target.value;
+ SettingsActions.changeViewSetting(setting);
+ }
+
+ changeAction(type) {
+ let activeCoin = this._getActiveCoin(this.props, {action: type});
+
+ this.setState({
+ action: type,
+ activeCoin: activeCoin
+ });
+
+ SettingsActions.changeViewSetting({
+ [`${this.props.provider}Action`]: type
+ });
+ }
+
+ render() {
+ let {coins, account, provider} = this.props;
+ let {activeCoin, action} = this.state;
+
+ if (!coins.length) {
+ return
;
+ }
+
+ let filteredCoins = coins.filter(a => {
+ if (!a || !a.symbol) {
+ return false;
+ } else {
+ return action === "deposit"
+ ? a.depositAllowed
+ : a.withdrawalAllowed;
+ }
+ });
+
+ let coinOptions = filteredCoins
+ .map(coin => {
+ let option =
+ action === "deposit"
+ ? coin.backingCoinType.toUpperCase()
+ : coin.symbol;
+ return (
+
+ );
+ })
+ .filter(a => {
+ return a !== null;
+ });
+
+ let coin = filteredCoins.filter(coin => {
+ return action === "deposit"
+ ? coin.backingCoinType.toUpperCase() === activeCoin
+ : coin.symbol === activeCoin;
+ })[0];
+
+ if (!coin) coin = filteredCoins[0];
+
+ let isDeposit = this.state.action === "deposit";
+
+ let supportUrl = "https://citadel.li";
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ {!coin ? null : (
+
+
+
+ {coin && coin.symbol ? (
+
+ {({asset, to, fromAccount}) => {
+ return (
+
+ }
+ customFilter={{
+ fields: [
+ "to",
+ "from",
+ "asset_id"
+ ],
+ values: {
+ to: to.get("id"),
+ from: fromAccount.get("id"),
+ asset_id: asset.get("id")
+ }
+ }}
+ />
+ );
+ }}
+
+ ) : null}
+
+ )}
+
+ );
+ }
+}
+
+export default connect(
+ CitadelGateway,
+ {
+ listenTo() {
+ return [SettingsStore];
+ },
+ getProps() {
+ return {
+ viewSettings: SettingsStore.getState().viewSettings
+ };
+ }
+ }
+);
diff --git a/app/components/DepositWithdraw/citadel/CitadelGatewayDepositRequest.jsx b/app/components/DepositWithdraw/citadel/CitadelGatewayDepositRequest.jsx
new file mode 100755
index 0000000000..f6e50ca063
--- /dev/null
+++ b/app/components/DepositWithdraw/citadel/CitadelGatewayDepositRequest.jsx
@@ -0,0 +1,594 @@
+import React from "react";
+import Translate from "react-translate-component";
+import {ChainStore} from "bitsharesjs/es";
+import ChainTypes from "components/Utility/ChainTypes";
+import BindToChainState from "components/Utility/BindToChainState";
+import WithdrawModalCitadel from "./WithdrawModalCitadel";
+import BaseModal from "../../Modal/BaseModal";
+import ZfApi from "react-foundation-apps/src/utils/foundation-api";
+import AccountBalance from "../../Account/AccountBalance";
+import AssetName from "components/Utility/AssetName";
+import LinkToAccountById from "components/Utility/LinkToAccountById";
+import {requestDepositAddress, getDepositAddress} from "common/gatewayMethods";
+import {blockTradesAPIs, openledgerAPIs, citadelAPIs} from "api/apiConfig";
+import LoadingIndicator from "components/LoadingIndicator";
+import counterpart from "counterpart";
+import PropTypes from "prop-types";
+
+class CitadelGatewayDepositRequest extends React.Component {
+ static propTypes = {
+ url: PropTypes.string,
+ gateway: PropTypes.string,
+ deposit_coin_type: PropTypes.string,
+ deposit_asset_name: PropTypes.string,
+ deposit_account: PropTypes.string,
+ receive_coin_type: PropTypes.string,
+ account: ChainTypes.ChainAccount,
+ issuer_account: ChainTypes.ChainAccount,
+ deposit_asset: PropTypes.string,
+ deposit_wallet_type: PropTypes.string,
+ receive_asset: ChainTypes.ChainAsset,
+ deprecated_in_favor_of: ChainTypes.ChainAsset,
+ deprecated_message: PropTypes.string,
+ action: PropTypes.string,
+ supports_output_memos: PropTypes.bool.isRequired
+ };
+
+ static defaultProps = {
+ autosubscribe: false
+ };
+
+ constructor(props) {
+ super(props);
+
+ let urls = {
+ blocktrades: blockTradesAPIs.BASE,
+ openledger: openledgerAPIs.BASE,
+ citadel: citadelAPIs.BASE
+ };
+
+ this.state = {
+ receive_address: null,
+ url: props.url || urls[props.gateway],
+ loading: false,
+ emptyAddressDeposit: false
+ };
+
+ this.addDepositAddress = this.addDepositAddress.bind(this);
+ this._copy = this._copy.bind(this);
+ document.addEventListener("copy", this._copy);
+ }
+
+ _copy(e) {
+ try {
+ if (this.state.clipboardText)
+ e.clipboardData.setData("text/plain", this.state.clipboardText);
+ else
+ e.clipboardData.setData(
+ "text/plain",
+ counterpart
+ .translate("gateway.use_copy_button")
+ .toUpperCase()
+ );
+ e.preventDefault();
+ } catch (err) {
+ console.error(err);
+ }
+ }
+
+ _getDepositObject() {
+ return {
+ inputCoinType: this.props.deposit_coin_type,
+ outputCoinType: this.props.receive_coin_type,
+ outputAddress: this.props.account.get("name"),
+ url: this.state.url,
+ stateCallback: this.addDepositAddress
+ };
+ }
+
+ componentWillMount() {
+ getDepositAddress({
+ coin: this.props.receive_coin_type,
+ account: this.props.account.get("name"),
+ stateCallback: this.addDepositAddress
+ });
+ }
+
+ componentWillUnmount() {
+ document.removeEventListener("copy", this._copy);
+ }
+
+ componentWillReceiveProps(np) {
+ if (np.account !== this.props.account) {
+ getDepositAddress({
+ coin: np.receive_coin_type,
+ account: np.account.get("name"),
+ stateCallback: this.addDepositAddress
+ });
+ }
+ }
+
+ addDepositAddress(receive_address) {
+ if (receive_address.error) {
+ receive_address.error.message === "no_address"
+ ? this.setState({emptyAddressDeposit: true})
+ : this.setState({emptyAddressDeposit: false});
+ }
+
+ this.setState({receive_address});
+ this.setState({
+ loading: false
+ });
+ this.setState({receive_address});
+ }
+
+ requestDepositAddressLoad() {
+ this.setState({
+ loading: true,
+ emptyAddressDeposit: false
+ });
+ requestDepositAddress(this._getDepositObject());
+ }
+
+ getWithdrawModalId() {
+ // console.log( "this.props.issuer: ", this.props.issuer_account.toJS() )
+ // console.log( "this.receive_asset.issuer: ", this.props.receive_asset.toJS() )
+ return (
+ "withdraw_asset_" +
+ this.props.issuer_account.get("name") +
+ "_" +
+ this.props.receive_asset.get("symbol")
+ );
+ }
+
+ onWithdraw() {
+ ZfApi.publish(this.getWithdrawModalId(), "open");
+ }
+
+ toClipboard(clipboardText) {
+ try {
+ this.setState({clipboardText}, () => {
+ document.execCommand("copy");
+ });
+ } catch (err) {
+ console.error(err);
+ }
+ }
+
+ render() {
+ const isDeposit = this.props.action === "deposit";
+ let emptyRow =
;
+ if (
+ !this.props.account ||
+ !this.props.issuer_account ||
+ !this.props.receive_asset
+ )
+ return emptyRow;
+
+ let account_balances_object = this.props.account.get("balances");
+
+ const {gateFee} = this.props;
+
+ let balance = "0 " + this.props.receive_asset.get("symbol");
+ if (this.props.deprecated_in_favor_of) {
+ let has_nonzero_balance = false;
+ let balance_object_id = account_balances_object.get(
+ this.props.receive_asset.get("id")
+ );
+ if (balance_object_id) {
+ let balance_object = ChainStore.getObject(balance_object_id);
+ if (balance_object) {
+ let balance = balance_object.get("balance");
+ if (balance != 0) has_nonzero_balance = true;
+ }
+ }
+ if (!has_nonzero_balance) return emptyRow;
+ }
+
+ let receive_address = this.state.receive_address;
+ let {emptyAddressDeposit} = this.state;
+ let indicatorButtonAddr = this.state.loading;
+
+ if (!receive_address) {
+ return (
+
+
+
+ );
+ }
+
+ let withdraw_modal_id = this.getWithdrawModalId();
+ let deposit_address_fragment = null;
+ let deposit_memo = null;
+
+ let clipboardText = "";
+ let memoText;
+ if (this.props.deposit_account) {
+ deposit_address_fragment = (
+
{this.props.deposit_account}
+ );
+ clipboardText =
+ this.props.receive_coin_type +
+ ":" +
+ this.props.account.get("name");
+ deposit_memo =
{clipboardText};
+ var withdraw_memo_prefix = this.props.deposit_coin_type + ":";
+ } else {
+ if (receive_address.memo) {
+ // This is a client that uses a deposit memo (like ethereum), we need to display both the address and the memo they need to send
+ memoText = receive_address.memo;
+ clipboardText = receive_address.address;
+ deposit_address_fragment = (
+
{receive_address.address}
+ );
+ deposit_memo =
{receive_address.memo};
+ } else {
+ // This is a client that uses unique deposit addresses to select the output
+ clipboardText = receive_address.address;
+ deposit_address_fragment = (
+
{receive_address.address}
+ );
+ }
+ var withdraw_memo_prefix = "";
+ }
+
+ if (
+ !this.props.isAvailable ||
+ ((isDeposit && !this.props.deposit_account && !receive_address) ||
+ (receive_address && receive_address.address === "unknown"))
+ ) {
+ return (
+
+
+
+ );
+ }
+
+ if (isDeposit) {
+ return (
+
+
+
+
+
+
+
+
+
+ {this.props.deposit_asset}
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+ :
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+ {emptyAddressDeposit ? (
+
+ ) : (
+ deposit_address_fragment
+ )}
+
+ {deposit_memo && (
+ memo: {deposit_memo}
+ )}
+
+
+ {deposit_address_fragment ? (
+
+
+
+ ) : null}
+ {memoText ? (
+
+
+
+ ) : null}
+
+
+
+
+
+
+
+ );
+ } else {
+ return (
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+ {this.props.deposit_asset}
+ |
+
+
+
+
+
+ |
+
+
+
+
+ :
+ |
+
+
+ |
+
+
+
+
+
+ {/*
When you withdraw {this.props.receive_asset.get("symbol")}, you will receive {this.props.deposit_asset} at a 1:1 ratio (minus fees).
*/}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+ }
+}
+
+export default BindToChainState(CitadelGatewayDepositRequest);
diff --git a/app/components/DepositWithdraw/citadel/WithdrawModalCitadel.jsx b/app/components/DepositWithdraw/citadel/WithdrawModalCitadel.jsx
new file mode 100755
index 0000000000..fb8bd87900
--- /dev/null
+++ b/app/components/DepositWithdraw/citadel/WithdrawModalCitadel.jsx
@@ -0,0 +1,856 @@
+import React from "react";
+import Trigger from "react-foundation-apps/src/trigger";
+import Translate from "react-translate-component";
+import ChainTypes from "components/Utility/ChainTypes";
+import BindToChainState from "components/Utility/BindToChainState";
+import utils from "common/utils";
+import BalanceComponent from "components/Utility/BalanceComponent";
+import counterpart from "counterpart";
+import AmountSelector from "components/Utility/AmountSelector";
+import AccountActions from "actions/AccountActions";
+import ZfApi from "react-foundation-apps/src/utils/foundation-api";
+import {validateAddress, WithdrawAddresses} from "common/gatewayMethods";
+import {ChainStore} from "bitsharesjs/es";
+import Modal from "react-foundation-apps/src/modal";
+import {checkFeeStatusAsync, checkBalance} from "common/trxHelper";
+import {debounce} from "lodash-es";
+import {Price, Asset} from "common/MarketClasses";
+import PropTypes from "prop-types";
+
+class WithdrawModalCitadel extends React.Component {
+ static propTypes = {
+ account: ChainTypes.ChainAccount.isRequired,
+ issuer: ChainTypes.ChainAccount.isRequired,
+ asset: ChainTypes.ChainAsset.isRequired,
+ output_coin_name: PropTypes.string.isRequired,
+ output_coin_symbol: PropTypes.string.isRequired,
+ output_coin_type: PropTypes.string.isRequired,
+ url: PropTypes.string,
+ output_wallet_type: PropTypes.string,
+ output_supports_memos: PropTypes.bool.isRequired,
+ amount_to_withdraw: PropTypes.string,
+ balance: ChainTypes.ChainObject
+ };
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ withdraw_amount: this.props.amount_to_withdraw,
+ withdraw_address: WithdrawAddresses.getLast(
+ props.output_wallet_type
+ ),
+ withdraw_address_check_in_progress: true,
+ withdraw_address_is_valid: null,
+ options_is_valid: false,
+ confirmation_is_valid: false,
+ withdraw_address_selected: WithdrawAddresses.getLast(
+ props.output_wallet_type
+ ),
+ memo: "",
+ withdraw_address_first: true,
+ empty_withdraw_value: false,
+ from_account: props.account,
+ fee_asset_id: "1.3.0",
+ feeStatus: {}
+ };
+
+ this._validateAddress(this.state.withdraw_address, props);
+
+ this._checkBalance = this._checkBalance.bind(this);
+ this._updateFee = debounce(this._updateFee.bind(this), 250);
+ }
+
+ componentWillMount() {
+ this._updateFee();
+ this._checkFeeStatus();
+ }
+
+ componentWillUnmount() {
+ this.unMounted = true;
+ }
+
+ componentWillReceiveProps(np) {
+ if (
+ np.account !== this.state.from_account &&
+ np.account !== this.props.account
+ ) {
+ this.setState(
+ {
+ from_account: np.account,
+ feeStatus: {},
+ fee_asset_id: "1.3.0",
+ feeAmount: new Asset({amount: 0})
+ },
+ () => {
+ this._updateFee();
+ this._checkFeeStatus();
+ }
+ );
+ }
+ }
+
+ _updateFee(state = this.state) {
+ let {fee_asset_id, from_account} = state;
+ const {fee_asset_types} = this._getAvailableAssets(state);
+ if (
+ fee_asset_types.length === 1 &&
+ fee_asset_types[0] !== fee_asset_id
+ ) {
+ fee_asset_id = fee_asset_types[0];
+ }
+
+ if (!from_account) return null;
+ checkFeeStatusAsync({
+ accountID: from_account.get("id"),
+ feeID: fee_asset_id,
+ options: ["price_per_kbyte"],
+ data: {
+ type: "memo",
+ content:
+ this.props.output_coin_type +
+ ":" +
+ state.withdraw_address +
+ (state.memo ? ":" + state.memo : "")
+ }
+ }).then(({fee, hasBalance, hasPoolBalance}) => {
+ if (this.unMounted) return;
+
+ this.setState(
+ {
+ feeAmount: fee,
+ hasBalance,
+ hasPoolBalance,
+ error: !hasBalance || !hasPoolBalance
+ },
+ this._checkBalance
+ );
+ });
+ }
+
+ _checkFeeStatus(state = this.state) {
+ let account = state.from_account;
+ if (!account) return;
+
+ const {fee_asset_types: assets} = this._getAvailableAssets(state);
+ // const assets = ["1.3.0", this.props.asset.get("id")];
+ let feeStatus = {};
+ let p = [];
+ assets.forEach(a => {
+ p.push(
+ checkFeeStatusAsync({
+ accountID: account.get("id"),
+ feeID: a,
+ options: ["price_per_kbyte"],
+ data: {
+ type: "memo",
+ content:
+ this.props.output_coin_type +
+ ":" +
+ state.withdraw_address +
+ (state.memo ? ":" + state.memo : "")
+ }
+ })
+ );
+ });
+ Promise.all(p)
+ .then(status => {
+ assets.forEach((a, idx) => {
+ feeStatus[a] = status[idx];
+ });
+ if (!utils.are_equal_shallow(state.feeStatus, feeStatus)) {
+ this.setState({
+ feeStatus
+ });
+ }
+ this._checkBalance();
+ })
+ .catch(err => {
+ console.error(err);
+ });
+ }
+
+ onMemoChanged(e) {
+ this.setState({memo: e.target.value}, this._updateFee);
+ }
+
+ onWithdrawAmountChange({amount}) {
+ this.setState(
+ {
+ withdraw_amount: amount,
+ empty_withdraw_value:
+ amount !== undefined && !parseFloat(amount)
+ },
+ this._checkBalance
+ );
+ }
+
+ onSelectChanged(index) {
+ let new_withdraw_address = WithdrawAddresses.get(
+ this.props.output_wallet_type
+ )[index];
+ WithdrawAddresses.setLast({
+ wallet: this.props.output_wallet_type,
+ address: new_withdraw_address
+ });
+
+ this.setState(
+ {
+ withdraw_address_selected: new_withdraw_address,
+ options_is_valid: false,
+ withdraw_address: new_withdraw_address,
+ withdraw_address_check_in_progress: true,
+ withdraw_address_is_valid: null
+ },
+ this._updateFee
+ );
+ this._validateAddress(new_withdraw_address);
+ }
+
+ onWithdrawAddressChanged(e) {
+ let new_withdraw_address = e.target.value.trim();
+
+ this.setState(
+ {
+ withdraw_address: new_withdraw_address,
+ withdraw_address_check_in_progress: true,
+ withdraw_address_selected: new_withdraw_address,
+ withdraw_address_is_valid: null
+ },
+ this._updateFee
+ );
+ this._validateAddress(new_withdraw_address);
+ }
+
+ _validateAddress(new_withdraw_address, props = this.props) {
+ validateAddress({
+ url: props.url,
+ walletType: props.output_wallet_type,
+ newAddress: new_withdraw_address
+ }).then(isValid => {
+ if (this.state.withdraw_address === new_withdraw_address) {
+ this.setState({
+ withdraw_address_check_in_progress: false,
+ withdraw_address_is_valid: isValid
+ });
+ }
+ });
+ }
+
+ _checkBalance() {
+ const {feeAmount, withdraw_amount} = this.state;
+ const {asset, balance} = this.props;
+ if (!balance || !feeAmount) return;
+ const hasBalance = checkBalance(
+ withdraw_amount,
+ asset,
+ feeAmount,
+ balance
+ );
+ if (hasBalance === null) return;
+ this.setState({balanceError: !hasBalance});
+ return hasBalance;
+ }
+
+ onSubmit() {
+ if (
+ !this.state.withdraw_address_check_in_progress &&
+ (this.state.withdraw_address &&
+ this.state.withdraw_address.length) &&
+ this.state.withdraw_amount !== null
+ ) {
+ if (!this.state.withdraw_address_is_valid) {
+ ZfApi.publish(this.getWithdrawModalId(), "open");
+ } else if (parseFloat(this.state.withdraw_amount) > 0) {
+ if (!WithdrawAddresses.has(this.props.output_wallet_type)) {
+ let withdrawals = [];
+ withdrawals.push(this.state.withdraw_address);
+ WithdrawAddresses.set({
+ wallet: this.props.output_wallet_type,
+ addresses: withdrawals
+ });
+ } else {
+ let withdrawals = WithdrawAddresses.get(
+ this.props.output_wallet_type
+ );
+ if (
+ withdrawals.indexOf(this.state.withdraw_address) == -1
+ ) {
+ withdrawals.push(this.state.withdraw_address);
+ WithdrawAddresses.set({
+ wallet: this.props.output_wallet_type,
+ addresses: withdrawals
+ });
+ }
+ }
+ WithdrawAddresses.setLast({
+ wallet: this.props.output_wallet_type,
+ address: this.state.withdraw_address
+ });
+ let asset = this.props.asset;
+
+ const {feeAmount} = this.state;
+
+ const amount = parseFloat(
+ String.prototype.replace.call(
+ this.state.withdraw_amount,
+ /,/g,
+ ""
+ )
+ );
+ const gateFee =
+ typeof this.props.gateFee != "undefined"
+ ? parseFloat(
+ String.prototype.replace.call(
+ this.props.gateFee,
+ /,/g,
+ ""
+ )
+ )
+ : 0.0;
+
+ let sendAmount = new Asset({
+ asset_id: asset.get("id"),
+ precision: asset.get("precision"),
+ real: amount
+ });
+
+ let balanceAmount = new Asset({
+ asset_id: asset.get("id"),
+ precision: asset.get("precision"),
+ real: 0
+ });
+
+ if (typeof this.props.balance != "undefined") {
+ balanceAmount = sendAmount.clone(
+ this.props.balance.get("balance")
+ );
+ }
+
+ const gateFeeAmount = new Asset({
+ asset_id: asset.get("id"),
+ precision: asset.get("precision"),
+ real: gateFee
+ });
+
+ sendAmount.plus(gateFeeAmount);
+
+ /* Insufficient balance */
+ if (balanceAmount.lt(sendAmount)) {
+ sendAmount = balanceAmount;
+ }
+
+ AccountActions.transfer(
+ this.props.account.get("id"),
+ this.props.issuer.get("id"),
+ sendAmount.getAmount(),
+ asset.get("id"),
+ this.props.output_coin_type +
+ ":" +
+ this.state.withdraw_address +
+ (this.state.memo
+ ? ":" + new Buffer(this.state.memo, "utf-8")
+ : ""),
+ null,
+ feeAmount ? feeAmount.asset_id : "1.3.0"
+ );
+
+ this.setState({
+ empty_withdraw_value: false
+ });
+ } else {
+ this.setState({
+ empty_withdraw_value: true
+ });
+ }
+ }
+ }
+
+ onSubmitConfirmation() {
+ ZfApi.publish(this.getWithdrawModalId(), "close");
+
+ if (!WithdrawAddresses.has(this.props.output_wallet_type)) {
+ let withdrawals = [];
+ withdrawals.push(this.state.withdraw_address);
+ WithdrawAddresses.set({
+ wallet: this.props.output_wallet_type,
+ addresses: withdrawals
+ });
+ } else {
+ let withdrawals = WithdrawAddresses.get(
+ this.props.output_wallet_type
+ );
+ if (withdrawals.indexOf(this.state.withdraw_address) == -1) {
+ withdrawals.push(this.state.withdraw_address);
+ WithdrawAddresses.set({
+ wallet: this.props.output_wallet_type,
+ addresses: withdrawals
+ });
+ }
+ }
+ WithdrawAddresses.setLast({
+ wallet: this.props.output_wallet_type,
+ address: this.state.withdraw_address
+ });
+ let asset = this.props.asset;
+ let precision = utils.get_asset_precision(asset.get("precision"));
+ let amount = String.prototype.replace.call(
+ this.state.withdraw_amount,
+ /,/g,
+ ""
+ );
+
+ const {feeAmount} = this.state;
+
+ AccountActions.transfer(
+ this.props.account.get("id"),
+ this.props.issuer.get("id"),
+ parseInt(amount * precision, 10),
+ asset.get("id"),
+ this.props.output_coin_type +
+ ":" +
+ this.state.withdraw_address +
+ (this.state.memo
+ ? ":" + new Buffer(this.state.memo, "utf-8")
+ : ""),
+ null,
+ feeAmount ? feeAmount.asset_id : "1.3.0"
+ );
+ }
+
+ onDropDownList() {
+ if (WithdrawAddresses.has(this.props.output_wallet_type)) {
+ if (this.state.options_is_valid === false) {
+ this.setState({options_is_valid: true});
+ this.setState({withdraw_address_first: false});
+ }
+
+ if (this.state.options_is_valid === true) {
+ this.setState({options_is_valid: false});
+ }
+ }
+ }
+
+ getWithdrawModalId() {
+ return "confirmation";
+ }
+
+ onAccountBalance() {
+ const {feeAmount} = this.state;
+ if (
+ Object.keys(this.props.account.get("balances").toJS()).includes(
+ this.props.asset.get("id")
+ )
+ ) {
+ let total = new Asset({
+ amount: this.props.balance.get("balance"),
+ asset_id: this.props.asset.get("id"),
+ precision: this.props.asset.get("precision")
+ });
+
+ // Subtract the fee if it is using the same asset
+ if (total.asset_id === feeAmount.asset_id) {
+ total.minus(feeAmount);
+ }
+
+ this.setState(
+ {
+ withdraw_amount: total.getAmount({real: true}),
+ empty_withdraw_value: false
+ },
+ this._checkBalance
+ );
+ }
+ }
+
+ setNestedRef(ref) {
+ this.nestedRef = ref;
+ }
+
+ onFeeChanged({asset}) {
+ this.setState(
+ {
+ fee_asset_id: asset.get("id")
+ },
+ this._updateFee
+ );
+ }
+
+ _getAvailableAssets(state = this.state) {
+ const {from_account, feeStatus} = state;
+ function hasFeePoolBalance(id) {
+ if (feeStatus[id] === undefined) return true;
+ return feeStatus[id] && feeStatus[id].hasPoolBalance;
+ }
+
+ function hasBalance(id) {
+ if (feeStatus[id] === undefined) return true;
+ return feeStatus[id] && feeStatus[id].hasBalance;
+ }
+
+ let fee_asset_types = [];
+ if (!(from_account && from_account.get("balances"))) {
+ return {fee_asset_types};
+ }
+ let account_balances = state.from_account.get("balances").toJS();
+ fee_asset_types = Object.keys(account_balances).sort(utils.sortID);
+ for (let key in account_balances) {
+ let asset = ChainStore.getObject(key);
+ let balanceObject = ChainStore.getObject(account_balances[key]);
+ if (balanceObject && balanceObject.get("balance") === 0) {
+ if (fee_asset_types.indexOf(key) !== -1) {
+ fee_asset_types.splice(fee_asset_types.indexOf(key), 1);
+ }
+ }
+
+ if (asset) {
+ // Remove any assets that do not have valid core exchange rates
+ let priceIsValid = false,
+ p;
+ try {
+ p = new Price({
+ base: new Asset(
+ asset
+ .getIn([
+ "options",
+ "core_exchange_rate",
+ "base"
+ ])
+ .toJS()
+ ),
+ quote: new Asset(
+ asset
+ .getIn([
+ "options",
+ "core_exchange_rate",
+ "quote"
+ ])
+ .toJS()
+ )
+ });
+ priceIsValid = p.isValid();
+ } catch (err) {
+ priceIsValid = false;
+ }
+
+ if (asset.get("id") !== "1.3.0" && !priceIsValid) {
+ fee_asset_types.splice(fee_asset_types.indexOf(key), 1);
+ }
+ }
+ }
+
+ fee_asset_types = fee_asset_types.filter(a => {
+ return hasFeePoolBalance(a) && hasBalance(a);
+ });
+
+ return {fee_asset_types};
+ }
+
+ render() {
+ let {withdraw_address_selected, memo} = this.state;
+ let storedAddress = WithdrawAddresses.get(
+ this.props.output_wallet_type
+ );
+ let balance = null;
+
+ let account_balances = this.props.account.get("balances").toJS();
+ let asset_types = Object.keys(account_balances);
+
+ let withdrawModalId = this.getWithdrawModalId();
+ let invalid_address_message = null;
+ let options = null;
+ let confirmation = null;
+
+ if (this.state.options_is_valid) {
+ options = (
+
+ {storedAddress.map(function(name, index) {
+ return (
+
+ {name}
+
+ );
+ }, this)}
+
+ );
+ }
+
+ if (
+ !this.state.withdraw_address_check_in_progress &&
+ (this.state.withdraw_address && this.state.withdraw_address.length)
+ ) {
+ if (!this.state.withdraw_address_is_valid) {
+ invalid_address_message = (
+
+
+
+ );
+ confirmation = (
+
+
+
+ ×
+
+
+
+
+
+
+
+ );
+ }
+ // if (this.state.withdraw_address_is_valid)
+ // invalid_address_message =
;
+ // else
+ // invalid_address_message =
;
+ }
+
+ let tabIndex = 1;
+ let withdraw_memo = null;
+
+ if (this.props.output_supports_memos) {
+ withdraw_memo = (
+
+
+
+
+ );
+ }
+
+ // Estimate fee VARIABLES
+ let {fee_asset_types} = this._getAvailableAssets();
+
+ if (asset_types.length > 0) {
+ let current_asset_id = this.props.asset.get("id");
+ if (current_asset_id) {
+ let current = account_balances[current_asset_id];
+ balance = (
+
+
+ :
+
+ {current ? (
+
+ ) : (
+ 0
+ )}
+
+
+ );
+ } else balance = "No funds";
+ } else {
+ balance = "No funds";
+ }
+
+ const disableSubmit =
+ this.state.error ||
+ this.state.balanceError ||
+ !this.state.withdraw_amount;
+
+ return (
+
+ );
+ }
+}
+
+export default BindToChainState(WithdrawModalCitadel);
diff --git a/app/components/Exchange/ExchangeHeader.jsx b/app/components/Exchange/ExchangeHeader.jsx
index f528623034..68f56ab06a 100644
--- a/app/components/Exchange/ExchangeHeader.jsx
+++ b/app/components/Exchange/ExchangeHeader.jsx
@@ -399,7 +399,7 @@ export default class ExchangeHeader extends React.Component {
- {
ZfApi.publish("chart_options", "open");
diff --git a/app/components/Forms/AccountSelect.jsx b/app/components/Forms/AccountSelect.jsx
index 68e1a9bfbc..8be52ffeb3 100644
--- a/app/components/Forms/AccountSelect.jsx
+++ b/app/components/Forms/AccountSelect.jsx
@@ -22,6 +22,15 @@ export default class AccountSelect extends React.Component {
);
}
+ componentDidMount() {
+ // setTimeout(() => {
+ // var account_names = this.props.account_names;
+ // if (account_names.length === 1 && !!account_names[0] && account_names[0] !== "" && account_names[0] !== this.state.selected) {
+ // this._selectAccount(account_names[0]);
+ // }
+ // }, 100);
+ }
+
shouldComponentUpdate(nextProps) {
return (
nextProps.selected !== this.props.selected ||
@@ -43,7 +52,6 @@ export default class AccountSelect extends React.Component {
var account_names = this.props.account_names;
var selected_account = this.props.selected;
var placeholder = this.props.placeholder || this.default_placeholder;
- var ikey;
if (this.props.list_size > 1) {
placeholder = (