diff --git a/app/api/apiConfig.js b/app/api/apiConfig.js index 78763eca36..142b48e317 100644 --- a/app/api/apiConfig.js +++ b/app/api/apiConfig.js @@ -83,6 +83,12 @@ export const xbtsxAPIs = { COINS_LIST: "/coin" }; +export const beosAPIs = { + BASE: "https://gateway.beos.world/api/v2", + COINS_LIST: "/coins", + TRADING_PAIRS: "/trading-pairs" +}; + export const nodeRegions = [ // region of the node follows roughly https://en.wikipedia.org/wiki/Subregion#/media/File:United_Nations_geographical_subregions.png "Northern Europe", diff --git a/app/assets/locales/locale-en.json b/app/assets/locales/locale-en.json index 8d508aa604..c221a2aef6 100644 --- a/app/assets/locales/locale-en.json +++ b/app/assets/locales/locale-en.json @@ -1115,6 +1115,25 @@ "status": "Status", "support_block": "For DEPOSIT AND WITHDRAWAL issues using \"OpenLedger TRANSFER SERVICE\", please contact Openledger at:", "support_gdex": "For support, please contact GDEX", + "bitshares_beos": { + "account_label": "Bitshares EOS account", + "account_validation_label": "Please wait, validating address...", + "amount_to_send_label": "Amount to send to BEOS account", + "account_validation_error": "Illegal BEOS account name: account must have less then 13 symbols (a-z lower case only, 1-5 and . not at the end are allowed)", + "create_account_checkbox": "Create BitShares EOS account", + "beos_conversion": "Convert to %(name)s", + "send_button_label": "Send", + "transfer_button_label": "Transfer BTS to BitShares EOS account", + "modal_title": "Transfer BTS to BitShares EOS account", + "maintenance_modal_label": "Gateway to BitShares EOS is down for maintenance", + "maintenance_error": "Gateway to BitShares EOS is down for maintenance", + "maintenance_button_label": "Ok", + "memo_label": "Memo (optional)", + "no_account_error": "BEOS account doesn't exist, create it by paying an additional %(btsAmount)s BTS. This new account will be controlled by the same keys as your BitShares account.", + "account_creation_pending": "This account is currently in the stage of creation.", + "no_account_error_without_creation": "BEOS account doesn't exist, to create it please select BTS as an asset.", + "multi_sig_error": "Current BTS account is multi-sig, so it cannot create a BEOS account." + }, "symbol": "Symbol", "time": "Time", "title": "Deposit & Withdraw", diff --git a/app/components/Account/AccountDepositWithdraw.jsx b/app/components/Account/AccountDepositWithdraw.jsx index f0cd186b69..b34ecc1de2 100644 --- a/app/components/Account/AccountDepositWithdraw.jsx +++ b/app/components/Account/AccountDepositWithdraw.jsx @@ -16,6 +16,7 @@ import AccountStore from "stores/AccountStore"; import SettingsStore from "stores/SettingsStore"; import SettingsActions from "actions/SettingsActions"; import {openledgerAPIs} from "api/apiConfig"; +import BitsharesBeos from "../DepositWithdraw/BitsharesBeos"; import BitKapital from "../DepositWithdraw/BitKapital"; import RuDexGateway from "../DepositWithdraw/rudex/RuDexGateway"; import GatewayStore from "stores/GatewayStore"; @@ -495,6 +496,17 @@ class AccountDepositWithdraw extends React.Component { ) }); + serList.push({ + name: "BitShares EOS", + template: ( + + ) + }); + serList.push({ name: "BitKapital", template: ( @@ -582,6 +594,7 @@ class AccountDepositWithdraw extends React.Component { "RUDEX", "SPARKDEX", "TRADE", + "BEOS", "BITKAPITAL", "XBTSX", "CITADEL" diff --git a/app/components/Account/Proposals.jsx b/app/components/Account/Proposals.jsx index 92ad853556..3d068211d6 100644 --- a/app/components/Account/Proposals.jsx +++ b/app/components/Account/Proposals.jsx @@ -46,10 +46,10 @@ class Proposals extends Component { componentDidMount() { /* - * Account objects don't get updated by underlying proposal changes, but - * the ChainStore does, so in order to update this component when a proposal - * changes, we need to update it whenever the ChainStore itself updates - */ + * Account objects don't get updated by underlying proposal changes, but + * the ChainStore does, so in order to update this component when a proposal + * changes, we need to update it whenever the ChainStore itself updates + */ ChainStore.subscribe(this.forceUpdate); } diff --git a/app/components/DepositWithdraw/BitsharesBeos.jsx b/app/components/DepositWithdraw/BitsharesBeos.jsx new file mode 100644 index 0000000000..6d545d9d79 --- /dev/null +++ b/app/components/DepositWithdraw/BitsharesBeos.jsx @@ -0,0 +1,337 @@ +import React from "react"; +import BitsharesBeosModal from "./BitsharesBeosModal"; +import counterpart from "counterpart"; +import ChainTypes from "components/Utility/ChainTypes"; +import Translate from "react-translate-component"; +import BindToChainState from "components/Utility/BindToChainState"; +import QueryString from "query-string"; +import {Modal} from "bitshares-ui-style-guide"; +import {beosAPIs} from "api/apiConfig"; + +class BitsharesBeosModalContainer extends React.Component { + static propTypes = { + asset: ChainTypes.ChainAsset.isRequired, + assets: ChainTypes.ChainAssetsList + }; + + constructor(props) { + super(props); + + this.state = { + isModalVisible: false + }; + + this.showModal = this.showModal.bind(this); + this.hideModal = this.hideModal.bind(this); + } + + showModal() { + this.setState({ + isModalVisible: true + }); + } + + hideModal() { + this.setState({ + isModalVisible: false + }); + } + + getTransferBtsId() { + return "transfer_bts"; + } + + onTransferBts() { + this.showModal(); + } + + getParams() { + const {params} = this.props; + return { + beosFee: "500", + beosApiUrl: beosAPIs.BASE, + beosIssuer: "beos.gateway", + ...QueryString.parse(params.search) + }; + } + + getBalances = () => { + const {assets, account} = this.props; + return assets.filter(a => !!a).map(a => { + return account.get("balances").toJS()[a.get("id")]; + }); + }; + + getBalanceById = id => { + return this.getBalances()[id] || null; + }; + + render() { + let transferBtsId = this.getTransferBtsId(); + const {beosFee, beosIssuer, beosApiUrl} = this.getParams(); + + return ( +
+

+ To qualify for the initial BEOS token distribution, a member + must join BEOS LCA (BLCA is Utah Limited Cooperative + Association, operating on a non-profit basis for the benefit + of its members), create an account on the BEOS network (the + “BEOS Account”) and fund that BEOS account with any amount + of BTS via the BEOS network gateway. +

+

+ Any member may create one or more BEOS accounts for a + one-time fee of {beosFee} BTS each. The BEOS network gateway + will read the public keys of the Bitshares account from + which the account creation fee is paid and automatically + assign the same keys to the Member’s newly created BEOS + account, so that the member can use the same private keys to + control both accounts. Thereafter the member may change + their keys at any time on either account. +

+

+ When a member deposits BTS via the BEOS network gateway, the + member’s BEOS account is credited with an equivalent amount + of a BTS IOU token. BLCA will retain 2/7 of the + 3,674,470,000 authorized BEOS tokens in order to promote + Association purposes. The remainder will be distributed to + Members. +

+

+ BEOS tokens will be distributed to account holders on an + hourly basis over a period of 89 days and 64GB of RAM will + be distributed over an 80-week. Both periods begin on April + 9, 2019. +

+

+ During the applicable distribution periods, BEOS and RAM + tokens will be apportioned between member account holders + based upon their proportionate holdings of BTS IOU tokens. + For example, if there are only 2 BEOS accounts at the end of + an hourly reward period, one holding 90 tokens and the other + holding 10 tokens, the 90 holder will receive 90% of the + BEOS awarded for that reward period and the 10 holder will + receive the remaining 10%. +

+

+ BEOS tokens are backed by BLCA Quint holdings, such that 1 + million BEOS tokens equals 1 Quint. For more information, + see{" "} + + www.quintric.com + + . +

+

+ IMPORTANT NOTE : Until a withdrawal is made, the “actual + BTS” deposited to the BEOS network gateway will be held in + an account called “beos.gateway” on the Bitshares network. + This account is managed by the Board, and by making deposits + to this account, a Member explicitly grants the Board the + right to vote the BTS so deposited in ways that support the + health and growth of the BEOS network. +

+

+ Members have the option of withdrawing their BTS IOU tokens + via the BEOS network gateway at any time (*) back to their + own control. +

+

+ * The BLCA directors will do their best to keep the gateway + operational at all times, but a malfunction either on the + gateway or the new BEOS chain itself could result in BTS + funds being locked up temporarily until the problem is + resolved. If you lose the keys to your BEOS account, the + directors cannot refund BTS locked up as BTS IOU tokens in + your BEOS account, because the directors cannot reclaim the + IOU tokens and have no way to determine if the account is + permanently lost. +

+

+ Please read the full BLCA member agreement located at{" "} + + http://www.beos.world/membership_agreement.html + {" "} + before proceeding. +

+

+ By clicking on the button below and purchasing a BEOS + account, you are agreeing to the terms of the BEOS LCA + membership agreement. +

+ +

+ After you create your account, you can access it via the + BEOS wallets here:{" "} + + https://get.beos.world/wallets/ + + . +

+ + !!a) + .map(a => a.get("symbol"))} + assetMemoCoinTypes={this.props.assetMemoCoinTypes} + balance={this.getBalanceById( + this.props.asset.get("id") + )} + balances={this.getBalances()} + creator={"eosio"} + issuer={beosIssuer} + beosApiUrl={beosApiUrl} + beosFee={beosFee} + beosCoins={this.props.beosCoins} + owner_key={ + "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" + } + ram={"0.0006 SYS"} + account_contract={"beos.token"} + action={"lock"} + from={"beos.token"} + /> + +
+ ); + } +} + +BitsharesBeosModalContainer = BindToChainState(BitsharesBeosModalContainer); + +class BitsharesBeos extends React.Component { + static propTypes = { + asset: ChainTypes.ChainAsset.isRequired, + assets: ChainTypes.ChainAssetsList + }; + + constructor(props) { + super(props); + + this.state = { + assetMemoCoinTypes: {}, + beosAssets: [], + beosCoins: [] + }; + } + + componentWillMount() { + let assetMemoCoinTypes = {}; + let beosAssets = []; + let beosCoins = []; + + let coinTypesPromisecheck = fetch(beosAPIs.BASE + beosAPIs.COINS_LIST, { + method: "get", + headers: new Headers({Accept: "application/json"}) + }).then(response => response.json()); + let tradingPairsPromisecheck = fetch( + beosAPIs.BASE + beosAPIs.TRADING_PAIRS, + { + method: "get", + headers: new Headers({Accept: "application/json"}) + } + ).then(response => response.json()); + Promise.all([coinTypesPromisecheck, tradingPairsPromisecheck]).then( + json_responses => { + let [coinTypes, tradingPairs] = json_responses; + + coinTypes.forEach(element => { + if (element.walletType === "beos") { + let memoCoinType = null; + memoCoinType = element.coinType; + assetMemoCoinTypes[element.walletSymbol] = memoCoinType; + } else if (element.walletType === "bitshares2") { + let coinType = null; + let memoCoinType = null; + + coinType = element.coinType; + + tradingPairs.find(element => { + if (element.inputCoinType === coinType) { + memoCoinType = element.outputCoinType; + } + }); + + if (element.walletSymbol !== "BEOS") { + assetMemoCoinTypes[ + element.walletSymbol + ] = memoCoinType; + } + + beosAssets.push(element.walletSymbol); + + if (element.walletSymbol === "BEOS") { + tradingPairs.forEach(tradingPair => { + if ( + tradingPair.inputCoinType === + element.coinType + ) { + coinTypes.find(coinTypeObject => { + if ( + coinTypeObject.coinType === + tradingPair.outputCoinType + ) { + beosCoins.push(coinTypeObject); + } + }); + } + }); + } + } + }); + this.setState({ + assetMemoCoinTypes, + beosAssets, + beosCoins + }); + } + ); + } + + render() { + let beosAssets = this.state.beosAssets; + let assetMemoCoinTypes = this.state.assetMemoCoinTypes; + let beosCoins = this.state.beosCoins; + + return ( +
+ +
+ ); + } +} + +export default BindToChainState(BitsharesBeos); diff --git a/app/components/DepositWithdraw/BitsharesBeosModal.jsx b/app/components/DepositWithdraw/BitsharesBeosModal.jsx new file mode 100644 index 0000000000..ddc726a84e --- /dev/null +++ b/app/components/DepositWithdraw/BitsharesBeosModal.jsx @@ -0,0 +1,1096 @@ +import React from "react"; +import AmountSelector from "components/Utility/AmountSelector"; +import Translate from "react-translate-component"; +import counterpart from "counterpart"; +import ChainTypes from "components/Utility/ChainTypes"; +import BalanceComponent from "components/Utility/BalanceComponent"; +import BindToChainState from "components/Utility/BindToChainState"; +import PropTypes from "prop-types"; +import {checkFeeStatusAsync, checkBalance} from "common/trxHelper"; +import {Asset} from "common/MarketClasses"; +import AccountActions from "actions/AccountActions"; +import utils from "common/utils"; +import {Button, Modal} from "bitshares-ui-style-guide"; +import ls from "common/localStorage"; +import {ChainStore} from "bitsharesjs"; +import WalletDb from "stores/WalletDb"; +import axios from "axios"; + +const STORAGE_KEY = "__beos__"; +let lsBeos = new ls(STORAGE_KEY); + +class BitsharesBeosModal extends React.Component { + static propTypes = { + account: ChainTypes.ChainAccount.isRequired, + asset: ChainTypes.ChainAsset.isRequired, + assets: ChainTypes.ChainAssetsList, + creator: PropTypes.string.isRequired, + issuer: ChainTypes.ChainAccount.isRequired, + owner_key: PropTypes.string.isRequired, + ram: PropTypes.string.isRequired, + account_contract: PropTypes.string.isRequired, + action: PropTypes.string.isRequired, + from: PropTypes.string.isRequired, + balance: ChainTypes.ChainObject, + balances: ChainTypes.ChainObjectsList, + beosApiUrl: PropTypes.string.isRequired, + beosFee: PropTypes.string.isRequired + }; + + constructor(props) { + super(props); + + this.state = { + account: "", + btsAmount: "500", + is_account_validation: false, + is_account_creation_checkbox: false, + isConfirmationModalVisible: false, + account_validation_error: false, + amount_to_send: "", + creator: this.props.creator, + owner_key: this.props.owner_key, + ram: this.props.ram, + is_account_creation: false, + account_contract: this.props.account_contract, + from_account: props.account, + action: this.props.action, + fee_amount_creation: 0, + fee_asset_id: "1.3.0", + from: this.props.from, + empty_amount_to_send_error: false, + balance_error: false, + maintenance_error: false, + memo: "", + no_account_error: false, + selectedAssetId: "1.3.0", + no_account_error_without_creation: false, + multiSigError: false, + currentBeosCheckbox: "" + }; + + this.showConfirmationModal = this.showConfirmationModal.bind(this); + this.hideConfirmationModal = this.hideConfirmationModal.bind(this); + } + + componentWillMount() { + if (this.props.beosCoins && this.props.beosCoins.length) { + this.setState({ + currentBeosCheckbox: + this.props.beosCoins[0]["walletSymbol"] || "BEOS" + }); + } + this._updateFee(); + this._updateMultiSigError(); + } + + componentWillUnmount() { + this.unMounted = true; + } + + componentWillReceiveProps(np) { + if ( + np.account !== this.state.from_account && + np.account !== this.props.account + ) { + this._updateMultiSigError(); + this.setState( + { + from_account: np.account, + fee_asset_id: this.getAssetById( + this.state.selectedAssetId + ).get("id"), + fee_amount: new Asset({amount: 0}) + }, + () => { + this._updateFee(); + } + ); + } + } + + _updateMultiSigError() { + let accountData = ChainStore.getAccount(this.props.account).toJS(); + if (accountData && accountData.active && accountData.owner) { + if ( + accountData.active.account_auths.length === 0 && + accountData.active.address_auths.length === 0 && + accountData.active.key_auths.length === 1 && + accountData.owner.account_auths.length === 0 && + accountData.owner.address_auths.length === 0 && + accountData.owner.key_auths.length === 1 + ) { + this.setState({ + multiSigError: false + }); + } else { + this.setState({ + multiSigError: true + }); + } + } + } + + showConfirmationModal() { + this.setState({ + isConfirmationModalVisible: true + }); + } + + hideConfirmationModal() { + this.setState({ + isConfirmationModalVisible: false + }); + } + + _updateFee(state = this.state) { + let {from_account} = state; + + const asset = this.getAssetById(this.state.selectedAssetId); + let pxasset = this.getProxyAsset(asset.get("symbol")); + if (asset.get("symbol") === "BEOS") { + pxasset = this.getProxyAsset(this.state.currentBeosCheckbox); + } + let memo; + + if (asset.get("symbol") === "BTS" && this.state.is_account_creation) { + memo = this.createMemoForAsset(pxasset, true); + } else { + memo = this.createMemoForAsset(pxasset); + } + + if (!from_account) return null; + if (asset.get("id") !== "1.3.0") return null; + checkFeeStatusAsync({ + accountID: from_account.get("id"), + feeID: asset.get("id"), + options: ["price_per_kbyte"], + data: { + type: "memo", + content: memo + } + }).then(({fee}) => { + if (this.unMounted) return; + + this.setState( + { + fee_amount: fee + }, + this._checkBalance + ); + }); + } + + getBalanceForAsset(assetId) { + return this.props.balances.filter( + b => b.get("asset_type") === assetId + )[0]; + } + + getAssetById(assetId) { + return this.props.assets.filter(a => a.get("id") === assetId)[0]; + } + + _checkBalance() { + const {selectedAssetId} = this.state; + const {amount_to_send, fee_amount, fee_amount_creation} = this.state; + const asset = this.getAssetById(selectedAssetId); + const balance = this.getBalanceForAsset(selectedAssetId); + let fee_amount_amount = 0; + if (fee_amount) { + fee_amount_amount = fee_amount.amount; + } + let feeAmount = new Asset({ + amount: fee_amount_creation + fee_amount_amount, + asset_id: asset.get("id"), + precision: asset.get("precision") + }); + if (!balance || !feeAmount) return; + let hasBalance = null; + if (asset.get("id") === "1.3.0") { + hasBalance = checkBalance( + amount_to_send, + asset, + feeAmount, + balance + ); + } else { + if ( + parseInt( + this.state.amount_to_send * + utils.get_asset_precision(asset.get("precision")), + 10 + ) <= balance.get("balance") + ) { + hasBalance = true; + } else { + hasBalance = false; + } + } + if (hasBalance === null) return; + this.setState({balance_error: !hasBalance}); + return hasBalance; + } + + onAccountValidation(url, account) { + const asset = this.getAssetById(this.state.selectedAssetId); + if (typeof this._source != typeof undefined) { + this._source.cancel(); + } + this._source = axios.CancelToken.source(); + this.setState({ + is_account_creation_checkbox: false + }); + if (account === "") { + this.setState({ + is_account_validation: false + }); + } else { + this.setState({ + is_account_validation: true + }); + } + let validation_url = + url + "/wallets/beos/address-validator?address=" + account; + return axios(validation_url, { + method: "get", + headers: new Headers({Accept: "application/json"}), + cancelToken: this._source.token + }) + .then(result => { + if (result && result.data) { + result = result.data; + } else { + return; + } + if (result && result.error) { + this.setState({ + is_account_validation: false, + maintenance_error: true, + is_account_creation_checkbox: false, + account_validation_error: false, + no_account_error: false + }); + return; + } + if ( + result.isValid && + this.getPendingAccounts().includes(account) + ) { + this.removePendingAccount(account); + this.setState({ + account_creation_transfer_success_info: false + }); + } + + let re = /^[a-z1-5.]+$/; + setTimeout(() => { + this.setState({ + account_validation_error: false, + is_account_validation: false, + maintenance_error: false, + no_account_error: false + }); + if ( + account.length < 13 && + re.test(account) && + account.substr(account.length - 1) !== "." + ) { + if (account.length === 12) { + this.setState({ + btsAmount: this.props.beosFee + }); + } else if (account.length < 12) { + this.setState({ + btsAmount: this.props.beosFee + }); + } + if ( + !result.isValid && + !this.state.is_account_creation + ) { + if (asset.get("symbol") === "BTS") { + this.setState({ + no_account_error: true + }); + } else { + this.setState({ + no_account_error_without_creation: true, + is_account_creation_checkbox: false, + is_account_creation: false + }); + } + } else { + this.setState( + { + fee_amount_creation: 0, + is_account_creation: false, + no_account_error: false, + no_account_error_without_creation: false + }, + this._checkBalance + ); + } + this.setState({ + is_account_creation_checkbox: + !result.isValid && + asset.get("symbol") === "BTS", + is_account_validation: false + }); + } else { + this.setState({ + is_account_creation_checkbox: false, + is_account_validation: false, + account_validation_error: true, + no_account_error: false + }); + } + + if ( + !result.isValid && + this.getPendingAccounts().includes(account) + ) { + this.setState({ + no_account_error: false, + is_account_creation_checkbox: false, + account_validation_error: false, + account_creation_transfer_success_info: true + }); + } + }, 200); + }) + .catch(error => { + if (axios.isCancel(error)) { + this.setState({ + is_account_validation: true, + maintenance_error: false, + is_account_creation_checkbox: false, + account_validation_error: false, + no_account_error: false + }); + } else { + if (account === "") { + this.setState({ + maintenance_error: false + }); + } else { + this.setState({ + maintenance_error: true + }); + } + this.setState({ + is_account_validation: false, + is_account_creation_checkbox: false, + account_validation_error: false, + no_account_error: false + }); + } + }); + } + + onMaintenance() { + this.showConfirmationModal(); + } + + onAccountBalance() { + const {selectedAssetId, fee_amount, fee_amount_creation} = this.state; + const asset = this.getAssetById(selectedAssetId); + const balance = this.getBalanceForAsset(selectedAssetId); + if (balance) { + let total = new Asset({ + amount: balance.get("balance"), + asset_id: asset.get("id"), + precision: asset.get("precision") + }); + + let fee_amount_amount = 0; + + if (fee_amount && Number.isInteger(fee_amount.amount)) { + fee_amount_amount = fee_amount.amount; + } + + let totalFeeAmount = new Asset({ + amount: fee_amount_creation + fee_amount_amount, + asset_id: asset.get("id"), + precision: asset.get("precision") + }); + + if (asset.get("id") === "1.3.0") { + total.minus(totalFeeAmount); + if (total.getAmount({real: true})) { + const i = total + .getAmount({real: true}) + .toString() + .indexOf("."); + if (i > -1) { + const newAmount = total + .getAmount({real: true}) + .toString() + .substr(0, i + 5); + if (!newAmount.endsWith(".")) { + this.setState( + { + amount_to_send: newAmount, + empty_amount_to_send_error: false + }, + this._checkBalance + ); + return; + } + } + } + this.setState( + { + amount_to_send: total.getAmount({real: true}), + empty_amount_to_send_error: false + }, + this._checkBalance + ); + } else { + this.setState({ + amount_to_send: total.getAmount({real: true}), + balance_error: false, + empty_amount_to_send_error: false + }); + } + } + } + + onAccountChanged(e) { + if (e.target.value !== "") { + if (this.state.maintenance_error === false) { + this.setState({ + is_account_validation: false, + maintenance_error: false, + no_account_error: false + }); + } + this.onAccountValidation(this.props.beosApiUrl, e.target.value); // need to be set + } + this.setState({account_validation_error: false}); + this.setState({account: e.target.value}, this._updateFee); + } + + onAmountToSendChange({amount, asset}) { + if (asset.get("id") !== this.state.selectedAssetId) { + this.setState( + { + is_account_creation_checkbox: false, + is_account_creation: false, + no_account_error: false, + no_account_error_without_creation: false, + account_validation_error: false, + selectedAssetId: asset.get("id") + }, + () => { + this.onAccountValidation( + this.props.beosApiUrl, + this.state.account + ); + } + ); + } + + if (asset.get("symbol") !== "BTS") { + this.setState({ + is_account_creation: false, + is_account_creation_checkbox: false, + no_account_error: false + }); + } + + if (amount) { + const i = amount.toString().indexOf("."); + if (i > -1) { + const newAmount = amount.toString().substr(0, i + 5); + if (!newAmount.endsWith(".")) { + this.setState( + { + amount_to_send: newAmount, + empty_amount_to_send_error: + amount !== undefined && !parseFloat(newAmount) + }, + this._checkBalance + ); + return; + } + } + } + + this.setState( + { + amount_to_send: amount, + empty_amount_to_send_error: + amount !== undefined && !parseFloat(amount) + }, + this._checkBalance + ); + } + + onCreateAccountCheckbox() { + if (this.state.is_account_creation) { + let re = /^[a-z1-5.]+$/; + if ( + this.state.account.length < 13 && + re.test(this.state.account) && + !this.state.no_account_error && + !this.state.maintenance_error && + this.state.account.substr(this.state.account.length - 1) !== "." + ) { + this.setState({no_account_error: true}); + } + } else { + let re = /^[a-z1-5.]+$/; + if ( + this.state.account.length < 13 && + re.test(this.state.account) && + this.state.no_account_error && + this.state.account.substr(this.state.account.length - 1) !== "." + ) { + this.setState({no_account_error: false}); + } + } + if (this.state.is_account_creation) { + this.setState( + { + fee_amount_creation: 0, + is_account_creation: !this.state.is_account_creation + }, + this._checkBalance + ); + } else { + const fee = parseFloat(this.props.beosFee) * 100000; + if (this.state.account.length === 12) { + this.setState( + { + fee_amount_creation: fee, + is_account_creation: !this.state.is_account_creation + }, + this._checkBalance + ); + } else if (this.state.account.length < 12) { + this.setState( + { + fee_amount_creation: fee, + is_account_creation: !this.state.is_account_creation + }, + this._checkBalance + ); + } + } + } + + onBeosCheckboxes(walletSymbol) { + this.setState({currentBeosCheckbox: walletSymbol}); + } + + onMemoChanged(e) { + this.setState( + {memo: e.target.value.replace(/:/g, "")}, + this._updateFee + ); + } + + getPendingAccounts = () => { + const accounts = lsBeos.get("pendingAccounts"); + return accounts.length ? lsBeos.get("pendingAccounts") : []; + }; + + setPendingAccount = account => { + const newAccounts = [...this.getPendingAccounts(), account]; + lsBeos.set("pendingAccounts", newAccounts); + }; + + removePendingAccount = account => { + const newAccounts = this.getPendingAccounts().filter( + a => a !== account + ); + lsBeos.set("pendingAccounts", newAccounts); + }; + + validationInterval = accountName => { + const validation_url = `${ + this.props.beosApiUrl + }/wallets/beos/address-validator?address=${accountName}`; + const interval = setInterval(async () => { + try { + const response = await fetch(validation_url, { + method: "get", + headers: new Headers({Accept: "application/json"}) + }); + const {isValid} = await response.json(); + + if (isValid) { + this.removePendingAccount(account); + clearInterval(interval); + } + } catch (e) { + throw e; + } + }, 5000); + + setTimeout(() => { + this.removePendingAccount(accountName); + this.setState({account_creation_transfer_success_info: false}); + clearInterval(interval); + }, 35000); + }; + + createMemoForAsset(pxasset, isAccountCreation = false) { + if (!pxasset) { + throw new Error("No asset found for memo"); + } + + const memo = []; + const {memo: userMemo} = this.state; + + memo.push( + pxasset, + this.state.account, + userMemo.trim() ? userMemo : "", + isAccountCreation ? "create" : "" + ); + + return memo.join(":"); + } + + getProxyAsset(assetSymbol) { + return this.props.assetMemoCoinTypes[assetSymbol]; + } + + getAvailableAssets = () => { + return this.props.assets + .filter(a => !!this.getBalanceForAsset(a.get("id"))) + .map(a => a.get("id")); + }; + + async onSubmit() { + let validation_url = + "https://gateway.beos.world/api/v2/wallets/beos/address-validator?address=blocktrades"; + let validation_promise = fetch(validation_url, { + method: "get", + headers: new Headers({Accept: "application/json"}) + }).then(response => response.json()); + await validation_promise + .then(result => { + if (result && result.error) { + this.setState({ + is_account_validation: false, + maintenance_error: true, + is_account_creation_checkbox: false, + account_validation_error: false, + no_account_error: false + }); + return; + } + + const asset = this.getAssetById(this.state.selectedAssetId); + + let newAmountToSend = parseInt( + this.state.amount_to_send * + utils.get_asset_precision(asset.get("precision")), + 10 + ); + + let pxasset = this.getProxyAsset(asset.get("symbol")); + if (asset.get("symbol") === "BEOS") { + pxasset = this.getProxyAsset( + this.state.currentBeosCheckbox + ); + } + let memo; + + if ( + asset.get("symbol") === "BTS" && + this.state.is_account_creation + ) { + memo = this.createMemoForAsset(pxasset, true); + newAmountToSend = + newAmountToSend + this.state.fee_amount_creation; + } else { + memo = this.createMemoForAsset(pxasset); + } + + try { + AccountActions.transfer( + this.props.account.get("id"), + this.props.issuer.get("id"), + newAmountToSend, + asset.get("id"), + memo, + null, + "1.3.0" + ); + + if (this.state.is_account_creation) { + if (!WalletDb.isLocked()) { + this.setPendingAccount(this.state.account); + this.setState({ + is_account_creation_checkbox: false, + account_creation_transfer_success_info: true, + is_account_creation: false + }); + this.validationInterval(this.state.account); + } + } + } catch (e) { + this.onMaintenance(); + throw e; + } + }) + .catch(() => { + this.setState({ + is_account_validation: false, + maintenance_error: true, + is_account_creation_checkbox: false, + account_validation_error: false, + no_account_error: false + }); + }); + } + + render() { + let account_creation_checkbox = null; + let beos_checkboxes = null; + let balance = null; + let account_balances = this.props.account.get("balances").toJS(); + let asset_types = Object.keys(account_balances); + let maintenanceDialog = null; + + if (asset_types.length > 0) { + let current_asset_id = this.state.selectedAssetId; + if (current_asset_id && this.getBalanceForAsset(current_asset_id)) { + let current = this.getBalanceForAsset(current_asset_id).get( + "id" + ); + balance = ( + + +  :  + + {current ? ( + + ) : ( + 0 + )} + + + ); + } else balance = "No funds"; + } else { + balance = "No funds"; + } + + if ( + this.getAssetById(this.state.selectedAssetId).get("symbol") === + "BTS" && + this.state.is_account_creation_checkbox && + this.state.account !== "" && + !this.state.maintenance_error && + !this.state.multiSigError + ) { + account_creation_checkbox = ( + + + + + + + +
+ + : + +
+ +
+
+ ); + } else if ( + this.getAssetById(this.state.selectedAssetId).get("symbol") === + "BEOS" + ) { + beos_checkboxes = ( + + + {this.props.beosCoins.map(element => { + if (element) { + let checked = false; + if ( + element.walletSymbol === + this.state.currentBeosCheckbox + ) { + checked = true; + } + return ( + + + + + ); + } + })} + +
+ + : + +
+ +
+
+ ); + } + + maintenanceDialog = ( + + +
+ +
+ ); + + const disableSubmit = + !this.state.amount_to_send || + this.state.balance_error || + this.state.account === "" || + this.state.account_validation_error || + this.state.no_account_error || + this.state.is_account_validation || + this.state.no_account_error_without_creation || + this.getPendingAccounts().includes(this.state.account) || + (this.state.multiSigError && + (this.state.no_account_error || + this.state.no_account_error_without_creation)) || + this.state.maintenance_error; + + return ( +
+
+
+ {/* Amount to send to BEOS account */} +
+ + {this.state.empty_amount_to_send_error ? ( +

+ +

+ ) : null} + {this.state.balance_error ? ( +

+ +

+ ) : null} +
+ {/* Bitshares EOS account */} +
+ +
+ +
+ {this.state.account_validation_error && + this.state.account !== "" ? ( +

+ +

+ ) : null} + {this.state.is_account_validation ? ( +

+ +

+ ) : null} + {this.state.maintenance_error ? ( +

+ +

+ ) : null} +
+ {/* Memo */} +
+ +