import React, {Component} from 'react'; import {Row, Col, Form, Button, Tooltip, OverlayTrigger, Dropdown, DropdownButton, ButtonGroup} from 'react-bootstrap'; import AddressDisplay from '../../common/AddressDisplay'; import {toDecimals, fromDecimals} from '../../../utils/eth'; import {isEmptyString, isEmptyArray, isNonEmptyArray} from '../../../utils/ObjectUtils'; import {VictoryChart, VictoryLine, VictoryAxis} from 'victory'; import moment from 'moment'; import SelectedV2PoolContainer from './SelectedV2PoolContainer'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faChevronDown, faQuestionCircle, faSpinner } from '@fortawesome/free-solid-svg-icons' import {getTokenConversionPath, getTokenFundConversionAmount, getTokenWithdrawConversionAmount, getFundAmount, getLiquidateAmount} from '../../../utils/ConverterUtils'; const Decimal = require('decimal.js'); export default class SelectedPool extends Component { constructor(props) { super(props); this.state= {fundAmount: 0, liquidateAmount: 0, reserve1Needed: 0, reserve2Needed: 0, fundAllReservesActive: 'reserve-active', fundOneReserveActive: '', singleTokenFundReserveSelection: '', singleTokenFundReserveAmount: 0, singleTokenFundConversionPaths: [], singleTokenWithdrawReserveSelection: '', singleTokenWithdrawReserveAmount: 0, singleTokenWithdrawConversionPaths: [], withdrawOneReserveActive: '', withdrawAllReservesActive: 'reserve-active', approvalDisplay: false, calculatingFunding: true, fundingCalculateInit: true, calculatingWithdraw: true, withdrawCalculateInit: true, calculatingAllInputFunding: true, calculatingAllInputWithdraw: true, fundReserveSelection: [] } } componentWillMount() { const {pool: {currentSelectedPool}} = this.props; if (isNonEmptyArray(currentSelectedPool.reserves)) { let fundReserveSelection = currentSelectedPool.reserves.map(function(item, idx){ return {'symbol': item.symbol, 'value': 0, 'address': item.address, 'decimals': item.decimals} }) this.setState({'fundReserveSelection': fundReserveSelection}); } } onFundInputChanged = (evt) => { let inputFund = evt.target.value; this.setState({fundAmount: inputFund}); } calculateAllInputFund = (evt) => { const {fundAmount} =this.state; this.calculateFundingAmount(fundAmount); } onLiquidateInputChanged = (evt) => { let inputFund = evt.target.value; this.setState({liquidateAmount: inputFund}); } calculateAllInputWithdraw = (evt) => { const {liquidateAmount} = this.state; this.calculateLiquidateAmount(liquidateAmount); } calculateLiquidateAmount = (inputFund) => { const {pool: {currentSelectedPool}} = this.props; this.setState({calculatingAllInputWithdraw: true}); if (!isNaN(inputFund) && parseFloat(inputFund) > 0) { const totalSupply = currentSelectedPool.totalSupply; const currentReserves = currentSelectedPool.reserves; const inputAmount = toDecimals(inputFund, currentSelectedPool.decimals); let totalRatio = 0; currentSelectedPool.reserves.forEach(function(reserve){ totalRatio += !isNaN(reserve.reserveRatio) ? parseInt(reserve.reserveRatio) : 0; }) if (totalRatio === 0) { totalRatio = 1000000 ; } const reservesAdded = currentReserves.map(function(item){ const currentReserveSupply = item.reserveBalance; return getLiquidateAmount(totalSupply, currentReserveSupply, totalRatio, inputAmount).then(function(response){ const liquidateBalance = (new Decimal(fromDecimals(response, item.decimals))).toFixed(4); return Object.assign({}, item, {addedMin: response, addedDisplay: liquidateBalance}); }); }); const self = this; Promise.all(reservesAdded).then(function(reserveResponse){ self.setState({reservesAdded: reserveResponse, calculatingAllInputWithdraw: false}); }) } } calculateFundingAmount = (inputFund) => { const {pool: {currentSelectedPool}} = this.props; const self = this; this.setState({calculatingAllInputFunding: true}); if (!isNaN(inputFund) && parseFloat(inputFund) > 0) { let totalRatio = 0; currentSelectedPool.reserves.forEach(function(reserve){ totalRatio += !isNaN(reserve.reserveRatio) ? parseInt(reserve.reserveRatio) : 0; }) if (totalRatio === 0) { totalRatio = 1000000 ; } const currentReserves = currentSelectedPool.reserves; const reservesNeededPromise = currentReserves.map(function(item){ const totalSupply = currentSelectedPool.totalSupply; const reserveBalance = item.reserveBalance; const amount = toDecimals(inputFund, currentSelectedPool.decimals); return getFundAmount(totalSupply, reserveBalance, totalRatio, amount).then(function(neededMin){ const neededDisplay = new Decimal(fromDecimals(neededMin, item.decimals)).toFixed(4, Decimal.ROUND_UP); const approvalNeededDisplay = parseFloat(neededDisplay) + 0.05 * neededDisplay; const approvalNeededMin = toDecimals(approvalNeededDisplay, item.decimals); let reserveObject = Object.assign({}, item, {neededMin: approvalNeededMin, neededDisplay: neededDisplay}); return reserveObject; }); }); Promise.all(reservesNeededPromise).then(function(neededResponse){ self.setState({reservesNeeded: neededResponse, calculatingAllInputFunding: false}); }) } } calculateFundingAmountWithOneReserve = (inputFund) => { if (!isNaN(parseFloat(inputFund))) { this.setState({calculatingFunding: true, fundingCalculateInit: false}); const {pool: {currentSelectedPool}} = this.props; const self = this; const {singleTokenFundReserveSelection} = this.state; const currentReserves = currentSelectedPool.reserves; const singleReserveSelection = singleTokenFundReserveSelection.symbol; const totalSupply = currentSelectedPool.totalSupply; let totalRatio = 0; currentSelectedPool.reserves.forEach(function(reserve){ totalRatio += !isNaN(reserve.reserveRatio) ? parseInt(reserve.reserveRatio) : 0; }) if (totalRatio === 0) { totalRatio = 1000000 ; } let selectedBaseReserve = currentSelectedPool.reserves.find(function(item){ if (item.symbol === singleReserveSelection) { return item; } }); let baseReserveBalance = selectedBaseReserve.reserveBalance; const amount = toDecimals(inputFund, currentSelectedPool.decimals); getFundAmount(totalSupply, baseReserveBalance, totalRatio, amount).then(function(baseNeededMin){ const baseNeededDisplay = new Decimal(fromDecimals(baseNeededMin, selectedBaseReserve.decimals)).toFixed(4, Decimal.ROUND_UP); const baseApprovalNeededDisplay = parseFloat(baseNeededDisplay) + 0.05 * baseNeededDisplay; const baseApprovalNeededMin = toDecimals(baseApprovalNeededDisplay, selectedBaseReserve.decimals); let reservesNeeded = []; let reservesMap = currentReserves.map(function(reserveItem){ if (reserveItem.symbol === singleReserveSelection) { const payload = {path: null, totalAmount: baseApprovalNeededMin, conversionAmount: baseNeededMin, quantity: baseApprovalNeededDisplay, token: reserveItem}; reservesNeeded.push(Object.assign({}, reserveItem, {neededMin: baseApprovalNeededMin, neededDisplay: baseApprovalNeededDisplay})); return new Promise((resolve, reject) => (resolve(payload))); } else { return getTokenConversionPath(selectedBaseReserve, reserveItem).then(function(conversionPath){ let usePoolForConversion = false; if (conversionPath.indexOf(currentSelectedPool.address) !== -1) { usePoolForConversion = true; } let currentReserveNeededMin = 0; let currentReserveNeededDisplay = 0; let reserveBalance = reserveItem.reserveBalance; // If pool is being used for conversion then supply will be decreased after conversion return getFundAmount(totalSupply, reserveBalance, totalRatio, amount).then(function(reserveNeededMin){ currentReserveNeededDisplay = new Decimal(fromDecimals(reserveNeededMin, reserveItem.decimals)).toFixed(4, Decimal.ROUND_UP); const currentApprovalNeededDisplay = parseFloat(currentReserveNeededDisplay) + 0.05 * currentReserveNeededDisplay; const currentApprovalNeededMin = toDecimals(currentApprovalNeededDisplay, reserveItem.decimals); return getTokenFundConversionAmount(conversionPath, currentReserveNeededDisplay, baseApprovalNeededDisplay, selectedBaseReserve.decimals, reserveItem.decimals).then(function(response){ const responseAmount = fromDecimals(response.base, selectedBaseReserve.decimals); const responseReserve = response.reserve; const responseReserveDisplay = fromDecimals(responseReserve, reserveItem.decimals); reservesNeeded.push(Object.assign({}, reserveItem, {neededMin: currentApprovalNeededMin, neededDisplay: currentReserveNeededDisplay})); return {path: conversionPath, totalAmount: response.base, conversionAmount: currentReserveNeededMin, quantity: responseAmount, token: reserveItem, usePoolForConversion: usePoolForConversion} }); }); }); } }); Promise.all(reservesMap).then(function(response){ let newBaseReserveBalance = parseFloat(fromDecimals(baseReserveBalance, selectedBaseReserve.decimals)); response.forEach(function(item){ if (item.path !== null && item.usePoolForConversion === true) { newBaseReserveBalance += item.totalAmount; } }); const updatedReserveBalance = toDecimals(newBaseReserveBalance, selectedBaseReserve.decimals); getFundAmount(totalSupply, updatedReserveBalance, totalRatio, amount).then(function(newBaseNeededMin){ let baseItemResponse = response.find((a)=>(a.path === null)); baseItemResponse.totalAmount = newBaseNeededMin; baseItemResponse.quantity = fromDecimals(newBaseNeededMin, baseItemResponse.token.decimals); let neeedBase = reservesNeeded.find((a)=>(a.symbol === selectedBaseReserve.symbol)); neeedBase.neededMin = newBaseNeededMin; self.setState({singleTokenFundConversionPaths: response, reservesNeeded: reservesNeeded, calculatingFunding: false}); }); }); }) } } submitBuyPoolToken = () => { const {fundAmount, reservesNeeded, fundReserveSelection} = this.state; const {pool: {currentSelectedPool}} = this.props; const self = this; const args = {poolTokenProvided: toDecimals(fundAmount, currentSelectedPool.decimals), reservesNeeded: reservesNeeded, converterAddress: currentSelectedPool.converter}; let reserveList = []; let amountList = []; fundReserveSelection.forEach(function(item, idx){ reserveList.push(item.address); if (item.value && parseFloat(item.value) > 0) { amountList.push(toDecimals(item.value, item.decimals)); } else { amountList.push(toDecimals(0, item.decimals)); } }); const converter = currentSelectedPool.converter; this.props.submitPoolBuy(reserveList, amountList, converter); } submitSellPoolToken = () => { const {pool: {currentSelectedPool}} = this.props; const self = this; const existingPoolTokenBalance = fromDecimals(currentSelectedPool.senderBalance, currentSelectedPool.decimals); const {liquidateAmount, reservesAdded} = this.state; const args = {poolTokenSold: toDecimals(liquidateAmount, currentSelectedPool.decimals), reservesAdded: reservesAdded, converterAddress: currentSelectedPool.converter, 'poolAddress': currentSelectedPool.address }; let isError = false; const web3 = window.web3; const converter = currentSelectedPool.converter; const currentWalletAddress = web3.currentProvider ? web3.currentProvider.selectedAddress : ''; if (isEmptyString(currentWalletAddress)) { isError = true; self.props.setErrorMessage(`You need to connect a web3 provider to make this transction.`); } if(parseFloat(liquidateAmount) > parseFloat(existingPoolTokenBalance)) { isError = true; this.props.setErrorMessage(`User balance for ${currentSelectedPool.symbol} is less than needed amount of ${liquidateAmount}`); } let minReserveAmounts = []; const poolReservesList = currentSelectedPool.reserves.map(function(item){ minReserveAmounts.push(1); return item.address; }) if (!isError){ this.props.resetErrorMessage(); const amount = toDecimals(liquidateAmount, currentSelectedPool.decimals); this.props.submitPoolSell(amount, poolReservesList, minReserveAmounts, converter); } } calculateLiquidateAmountWithOneReserve = (liquidateFund) => { const {pool: {currentSelectedPool}} = this.props; this.setState({calculatingWithdraw: true, withdrawCalculateInit: false}); const currentReserves = currentSelectedPool.reserves; const totalSupply = currentSelectedPool.totalSupply; const self = this; let totalRatio = 0; currentSelectedPool.reserves.forEach(function(reserve){ totalRatio += !isNaN(reserve.reserveRatio) ? parseInt(reserve.reserveRatio) : 0; }); if (totalRatio === 0) { totalRatio = 1000000 ; } const inputAmount = toDecimals(liquidateFund, currentSelectedPool.decimals); const reservesAddedPromise = currentReserves.map(function(item){ const currentReserveSupply = item.reserveBalance; return getLiquidateAmount(totalSupply, currentReserveSupply, totalRatio, inputAmount).then(function(response){ const liquidateBalance = (new Decimal(fromDecimals(response, item.decimals))).toFixed(4); return Object.assign({}, item, {addedMin: response, addedDisplay: liquidateBalance}); }); }); const {singleTokenWithdrawReserveSelection} = this.state; Promise.all(reservesAddedPromise).then(function(reservesAdded){ let reservesMap = reservesAdded.map(function(item, idx){ if (item.symbol === singleTokenWithdrawReserveSelection.symbol) { let payload = {path: null, totalAmount: item.addedMin, conversionAmount: item.addedMin, quantity: item.addedDisplay, token: item}; return new Promise((resolve, reject) => resolve(payload)); } else { return getTokenConversionPath(item, singleTokenWithdrawReserveSelection).then(function(conversionPath){ return getTokenWithdrawConversionAmount(conversionPath, item.addedMin).then(function(response){ let quantity = fromDecimals(response.toString(), currentSelectedPool.decimals); return {path: conversionPath, totalAmount: response, conversionAmount: item.addedMin, quantity: quantity, token: item} }); }); } }); Promise.all(reservesMap).then(function(mapData){ self.setState({singleTokenWithdrawConversionPaths: mapData, calculatingWithdraw: false}); }) self.setState({reservesAdded: reservesAdded}); }); } submitBuyPoolTokenWithSingleReserve = () => { const {singleReserveAmount, reservesNeeded, singleTokenFundConversionPaths} = this.state; const {pool: {currentSelectedPool}} = this.props; const fundingArgs = {poolTokenProvided: toDecimals(singleReserveAmount, currentSelectedPool.decimals), reservesNeeded: reservesNeeded, converterAddress: currentSelectedPool.converter}; const payload = {swap: singleTokenFundConversionPaths, fund: fundingArgs}; let totalReserveAmount = new Decimal(0); let baseReserveItem = {}; singleTokenFundConversionPaths.forEach(function(item){ if (item.path === null) { baseReserveItem = item; } totalReserveAmount = totalReserveAmount.add(new Decimal(item.quantity)); }); const userBalance = baseReserveItem.token.userBalance; if (totalReserveAmount.lessThanOrEqualTo(userBalance)) { this.props.submitPoolBuyWithSingleReserve(payload); } else { this.props.setErrorMessage(`Amount needed is more than user balance`); } } submitSellPoolTokenWithSingleReserve = () => { const {pool: {currentSelectedPool}} = this.props; const {singleTokenWithdrawConversionPaths} = this.state; const {singleTokenWithdrawReserveAmount, reservesAdded} = this.state; const args = {poolTokenSold: toDecimals(singleTokenWithdrawReserveAmount, currentSelectedPool.decimals), reservesAdded: reservesAdded, converterAddress: currentSelectedPool.converter, 'poolAddress': currentSelectedPool.address }; const payload = {paths: singleTokenWithdrawConversionPaths, funding: args} const existingPoolTokenBalance = fromDecimals(currentSelectedPool.senderBalance, currentSelectedPool.decimals); let isError = false; if(parseFloat(singleTokenWithdrawReserveAmount) > parseFloat(existingPoolTokenBalance)) { isError = true; this.props.setErrorMessage(`User balance for ${currentSelectedPool.symbol} is less than needed amount of ${singleTokenWithdrawReserveAmount}`); } if (!isError) { this.props.submitPoolSellWithSingleReserve(payload); } } fundReserveToggle = (type) => { const {pool: {currentSelectedPool}} = this.props; if (type === 'all') { this.setState({fundOneReserveActive: '', singleTokenFundReserveAmount: 0}); this.setState({fundAllReservesActive: 'reserve-active', reservesNeeded: [], singleTokenFundConversionPaths: []}); } else { this.setState({fundAllReservesActive: ''}); this.setState({fundOneReserveActive: 'reserve-active'}); this.setState({singleTokenFundReserveSelection: currentSelectedPool.reserves[0], fundAmount: 0}); } } fundSingleBaseChanged = (evtKey, evt) => { const {pool: {currentSelectedPool}} = this.props; const {singleInputFund, inputFund, singleReserveAmount} = this.state; const currentSelection = currentSelectedPool.reserves.find((a)=>(a.symbol === evtKey)); const self = this; this.setState({singleTokenFundReserveSelection: currentSelection}, function(){ self.calculateFundingAmountWithOneReserve(singleReserveAmount); }); } withdrawSingleBaseChanged = (eventKey, evt) => { const {pool: {currentSelectedPool}} = this.props; const {singleTokenWithdrawReserveAmount} = this.state; const self = this; const currentSelection = currentSelectedPool.reserves.find((a)=>(a.symbol === eventKey)); this.setState({singleTokenWithdrawReserveSelection: currentSelection}, function(){ self.calculateLiquidateAmountWithOneReserve(singleTokenWithdrawReserveAmount); }); } withdrawReserveToggle = (type) => { const {pool: {currentSelectedPool}} = this.props; if (type === 'all') { this.setState({withdrawOneReserveActive: '', withdrawAllReservesActive: 'reserve-active', singleTokenWithdrawConversionPaths: []}); } else { this.setState({withdrawOneReserveActive: 'reserve-active', withdrawAllReservesActive: ''}); this.setState({singleTokenWithdrawReserveSelection: currentSelectedPool.reserves[0], liquidateAmount: 0}); } } showApprovalDialog = (type = "pool") => { const {pool: {currentSelectedPool}} = this.props; this.props.setTokenAllowances(1000000000, currentSelectedPool, type); } showRevokeDialog = (type = "pool") => { const {pool: {currentSelectedPool}} = this.props; this.props.revokeTokenAllowances(currentSelectedPool, type); } componentWillReceiveProps(nextProps) { const {pool: {currentSelectedPool, poolApproval}} = nextProps; if (isNonEmptyArray(currentSelectedPool.reserves) && isEmptyArray(this.props.pool.currentSelectedPool.reserves)) { this.setState({singleTokenFundReserveSelection: currentSelectedPool.reserves[0], singleTokenWithdrawReserveSelection: currentSelectedPool.reserves[0]}); let fundReserveSelection = currentSelectedPool.reserves.map(function(item, idx){ return {'symbol': item.symbol, 'value': 0, 'address': item.address, 'decimals': item.decimals} }) this.setState({'fundReserveSelection': fundReserveSelection}); } } toggleApprovalDisplay = () => { this.setState({approvalDisplay: !this.state.approvalDisplay}) } onSingleReserveFundInputChanged = (evt) => { const singleInputFund = evt.target.value; const {singleTokenFundReserveSelection, fundReserveSelection} = this.state; let foundReserve = fundReserveSelection.find((i)=>(i.symbol === singleTokenFundReserveSelection.symbol)); foundReserve.value = singleInputFund; this.setState({singleReserveAmount: singleInputFund, fundReserveSelection: fundReserveSelection}); } calculateSingleInputFund = () => { const {singleReserveAmount} = this.state; if (!isNaN(parseFloat(singleReserveAmount))) { this.calculateFundingAmountWithOneReserve(singleReserveAmount); } } calculateSingleInputWithdraw = () => { const {singleTokenWithdrawReserveAmount} = this.state; this.calculateLiquidateAmountWithOneReserve(singleTokenWithdrawReserveAmount); } onSingleReserveLiquidateFundChanged = (evt) => { const singleLiquidateFund = evt.target.value; // this.calculateLiquidateAmountWithOneReserve(singleLiquidateFund); this.setState({singleTokenWithdrawReserveAmount: singleLiquidateFund}); } fundInputAmountChange = (idx, evt) => { let currentFundingAmount = this.state.fundReserveSelection; currentFundingAmount[idx].value = evt.target.value; this.setState({fundReserveSelection: currentFundingAmount}); } render() { const {pool: {currentSelectedPool, currentSelectedPoolError}} = this.props; const {reservesNeeded, reservesAdded, fundAllReservesActive, fundOneReserveActive, singleTokenFundConversionPaths, withdrawAllReservesActive, withdrawOneReserveActive, singleTokenWithdrawReserveSelection, singleTokenFundReserveSelection, singleTokenWithdrawConversionPaths, calculatingFunding, calculatingWithdraw, submitFundingEnabled, fundingCalculateInit, withdrawCalculateInit, calculatingAllInputFunding, calculatingAllInputWithdraw, fundReserveSelection, withdrawSelection } = this.state; const self = this; let isPurchaseBtnDisabled = false; let isFundingLoading = <span/>; let isWithdrawLoading = <span/>; if (calculatingFunding && !fundingCalculateInit) { isFundingLoading = <FontAwesomeIcon icon={faSpinner} size="lg" rotation={270} pulse/>; } if (calculatingWithdraw && !withdrawCalculateInit) { isWithdrawLoading = <FontAwesomeIcon icon={faSpinner} size="lg" rotation={270} pulse/>; } let isWithdrawBtnDisabled = false; if (calculatingFunding) { // isPurchaseBtnDisabled = true; } if (calculatingWithdraw) { isWithdrawBtnDisabled = true; } let reserveRatio = ''; reserveRatio = currentSelectedPool.reserves && currentSelectedPool.reserves.length > 0 ?currentSelectedPool.reserves.map(function(item){ if (item) { return parseInt(item.reserveRatio) / 10000; } else { return null; } }).filter(Boolean).join("/") : ''; if (currentSelectedPoolError) { return (<div> <div>Could not fetch pool details.</div> <div>Currently only pool contracts which expose reserveTokenCount() and reserveTokens() methods are supported.</div> </div>) } const { fundAmount, liquidateAmount } = this.state; let minTotalSupply = currentSelectedPool.totalSupply ? parseFloat(fromDecimals(currentSelectedPool.totalSupply, currentSelectedPool.decimals)).toFixed(4) : ""; let reserveTokenList = currentSelectedPool.reserves && currentSelectedPool.reserves.length > 0 ? currentSelectedPool.reserves.map(function(item, idx){ return <div key={`token-${idx}`}> <div className="holdings-label">{item.name}</div> <div className="holdings-data"> {parseFloat(fromDecimals(item.reserveBalance, item.decimals)).toFixed(4)}</div> </div> }) : <span/>; let userHoldingsList = currentSelectedPool.reserves && currentSelectedPool.reserves.length > 0 ? currentSelectedPool.reserves.map(function(item, idx){ let userBalanceItem = parseFloat(item.userBalance); let userBalanceDisplay = "-"; if (!isNaN(userBalanceItem)) { userBalanceDisplay = userBalanceItem.toFixed(4); } return (<div key={`token-${idx}`}> <div className="holdings-label">{item.name}</div> <div className="holdings-data"> {userBalanceDisplay}</div> </div>) }) : <span/>; let poolHoldings = ""; if (currentSelectedPool.senderBalance ) { poolHoldings = parseFloat(fromDecimals(currentSelectedPool.senderBalance, currentSelectedPool.decimals)).toFixed(4) + " " + currentSelectedPool.symbol; } let liquidateInfo = <span/>; if (liquidateAmount && liquidateAmount > 0 && reservesAdded && reservesAdded.length > 0) { liquidateInfo = ( <div> <div>You will receive</div> {reservesAdded.map(function(item, idx){ return <div key={`reserve-added-${idx}`}>{item.addedDisplay} {item.symbol}</div> })} </div> ) } let fundInfo = <span/>; if (fundAmount && fundAmount > 0 && reservesNeeded && reservesNeeded.length > 0) { fundInfo = ( <div> <div>You will needs to stake</div> {reservesNeeded.map(function(item, idx){ return <div key={`reserve-needed-${idx}`}>{item.neededDisplay} {item.symbol}</div> })} </div> ) } let conversionFee = ""; if (currentSelectedPool && currentSelectedPool.conversionFee) { conversionFee = currentSelectedPool.conversionFee + "%"; } let poolLiquidateAction = <span/>; let poolFundAction = <span/>; let tokenAllowances = <span/>; let tokenAllowanceRowVisible = "row-hidden"; if (this.state.approvalDisplay) { tokenAllowanceRowVisible = "row-visible"; } if (currentSelectedPool.reserves && currentSelectedPool.reserves.length > 0){ if (withdrawAllReservesActive === 'reserve-active') { poolLiquidateAction = ( <div> <div className="select-reserve-container"> <Form.Control type="number" placeholder="Enter amount to liquidate" onChange={this.onLiquidateInputChanged}/> <Button className="calculate-btn" disabled={isPurchaseBtnDisabled} onClick={this.calculateAllInputWithdraw}>Calculate</Button> </div> <div className="action-info-col"> {liquidateInfo} <Button onClick={this.submitSellPoolToken} className="pool-action-btn">Sell</Button> </div> </div> ) } else if (withdrawOneReserveActive === 'reserve-active') { let reserveOptions = currentSelectedPool.reserves.map(function(item, key){ return <Dropdown.Item eventKey={item.symbol} key={`${item.symbol}-${key}`}>{item.symbol}</Dropdown.Item> }); let withdrawActiveAmount = <span/>; if (singleTokenWithdrawConversionPaths && singleTokenWithdrawConversionPaths.length > 0) { let totalReserveAmount = 0; singleTokenWithdrawConversionPaths.forEach(function(item){ totalReserveAmount += parseFloat(item.quantity); }); totalReserveAmount = totalReserveAmount.toFixed(4); withdrawActiveAmount = <div>{isWithdrawLoading} You will receive {totalReserveAmount} {singleTokenWithdrawReserveSelection.symbol}</div> } poolLiquidateAction = ( <div> <div className="select-reserve-container"> <div> <label> Reserve token in which to withdraw </label> <DropdownButton id="dropdown-basic-button" title={singleTokenWithdrawReserveSelection.symbol} onSelect={this.withdrawSingleBaseChanged}> {reserveOptions} </DropdownButton> </div> <div> <label>Amount of pool tokens to withdraw</label> <div> <Form.Control type="number" placeholder="Pool tokens to withdraw" onChange={this.onSingleReserveLiquidateFundChanged}/> <Button className="calculate-btn" onClick={this.calculateSingleInputWithdraw}>Calculate</Button> </div> </div> </div> <div className="action-info-col"> {withdrawActiveAmount} <Button onClick={this.submitSellPoolTokenWithSingleReserve} disabled={isWithdrawBtnDisabled} className="pool-action-btn">Withdraw</Button> </div> </div> ) } if (fundAllReservesActive === 'reserve-active' && fundReserveSelection.length > 0) { poolFundAction = ( <div className="select-reserve-container"> <div> { fundReserveSelection.map(function(item, idx){ return <Form.Control type="number" placeholder={`Enter amount of ${item.symbol} to fund`} value={item.value} onChange={self.fundInputAmountChange.bind(self, idx)}/> }) } <div> <Button className="calculate-btn" disabled={isPurchaseBtnDisabled} onClick={this.calculateAllInputFund}>Calculate</Button> </div> </div> <div className="action-info-col"> {fundInfo} <Button onClick={this.submitBuyPoolToken} className="pool-action-btn">Purchase</Button> </div> </div> ) } else if (fundOneReserveActive === 'reserve-active') { let reserveOptions = currentSelectedPool.reserves.map(function(item, key){ return <Dropdown.Item eventKey={item.symbol} key={`${item.symbol}-${key}`} >{item.symbol}</Dropdown.Item> }); let fundActiveAmount = <span/>; if (singleTokenFundConversionPaths) { let totalReserveAmount = 0; singleTokenFundConversionPaths.forEach(function(item){ totalReserveAmount += parseFloat(item.quantity); }); totalReserveAmount = totalReserveAmount.toFixed(4); fundActiveAmount = <div>{isFundingLoading} You will need to stake {totalReserveAmount} {singleTokenFundReserveSelection.symbol}</div> } poolFundAction = ( <div> <div className="select-reserve-container"> <div> <label> Reserve token with which to fund. </label> <DropdownButton id="dropdown-basic-button" title={singleTokenFundReserveSelection.symbol} onSelect={this.fundSingleBaseChanged}> {reserveOptions} </DropdownButton> </div> <div> <label>Amount of {singleTokenFundReserveSelection.symbol} to fund</label> <div> <Form.Control type="number" placeholder={`Enter amount of ${singleTokenFundReserveSelection.symbol} to fund`} onChange={this.onSingleReserveFundInputChanged} className="single-reserve-amount-text"/> <Button className="calculate-btn" disabled={isPurchaseBtnDisabled} onClick={this.calculateSingleInputFund}>Calculate</Button> </div> </div> </div> <div className="action-info-col"> {fundActiveAmount} <Button onClick={this.submitBuyPoolTokenWithSingleReserve} className="pool-action-btn">Purchase</Button> </div> </div> ) } let tokenAllowanceReserves = currentSelectedPool.reserves.map(function(item, idx){ return <div key={`allowance-${idx}`} className="selected-pool-balance"> {item.symbol} {item.userAllowance ? parseFloat(item.userAllowance).toFixed(5) : '-'} </div> }); let tokenSwapAllowanceReserves = currentSelectedPool.reserves.map(function(item, idx){ return <div key={`allowance-${idx}`} className="selected-pool-balance"> {item.symbol} {item.swapAllowance ? parseFloat(item.swapAllowance).toFixed(5) : '-'} </div> }); tokenAllowances = <div className={`${tokenAllowanceRowVisible}`}> <div className="approval-type-label"> Approvals for pool converter contract. </div> <Row className={`token-allowances-display-row`}> <Col lg={8}> {tokenAllowanceReserves} </Col> <Col lg={4}> <Button onClick={()=>this.showApprovalDialog("pool")}>Approve reserves </Button> <Button onClick={()=>this.showRevokeDialog("pool")} className="revoke-approval-btn">Revoke approval </Button> </Col> </Row> <div className="approval-type-label"> Approvals for Bancor Network contract. </div> <Row className="single-token-description"> <Col lg={12}> If you using single token funding, it is also recommended that you approve BancorNetwork contract for swaps. </Col> </Row> <Row> <Col lg={8}> {tokenSwapAllowanceReserves} </Col> <Col lg={4}> <Button onClick={()=>this.showApprovalDialog("swap")}>Approve reserves </Button> <Button onClick={()=>this.showRevokeDialog("swap")} className="revoke-approval-btn">Revoke approval </Button> </Col> </Row> </div> } function allowanceToolTip(props) { return <Tooltip {...props}> <div>Token allowances refer to amount you have approved the converter contract to spend.</div> <div>Set allowances for BancorConverter for faster pool funding and liquidation and save on Gas costs</div> </Tooltip>; } let currentPoolTransactions = <span/>; if (currentSelectedPool.poolVersion === '1') { currentPoolTransactions = ( <div> <Col lg={6}> <div className="allowance-label">Fund Pool Holdings</div> {poolFundAction} </Col> <Col lg={6}> <div className="allowance-label">Liquitate Pool Holdings</div> {poolLiquidateAction} </Col> </div> ) } else { currentPoolTransactions = ( <div> <SelectedV2PoolContainer pool={this.props.pool}/> </div> ) } return ( <div> <Row className="select-pool-row-1"> <Col lg={1} className="pool-logo-container"> <img src={currentSelectedPool.imageURI} className="selected-pool-image" alt="pool-token-img"/> </Col> <Col lg={2}> <div className="cell-label">{currentSelectedPool.symbol}</div> <div className="cell-data">{currentSelectedPool.name}</div> </Col> <Col lg={2}> <div className="cell-label">Address</div> <div className="cell-data"><AddressDisplay address={currentSelectedPool.address}/></div> </Col> <Col lg={2}> <div className="cell-label">Pool Supply</div> <div className="cell-data">{minTotalSupply}</div> </Col> <Col lg={3}> <div> <div className="cell-label">Reserves</div> <div className="cell-data">{reserveTokenList}</div> </div> </Col> <Col lg={2}> <div className="cell-label">Reserve Ratio</div> <div className="cell-data">{reserveRatio}</div> </Col> </Row> <Row className="selected-pool-meta-row"> <Col lg={3}> <div className="cell-label">Conversion fee generated</div> <div className="cell-data">{conversionFee}</div> </Col> <Col lg={3}> <div className="cell-label">Your pool token holdings</div> <div className="cell-data">{poolHoldings}</div> </Col> <Col lg={4}> <div className="cell-label">Your reserve token holdings</div> <div className="cell-data">{userHoldingsList}</div> </Col> </Row> <div className="pool-action-container pool-allowance-container"> <Row> <Col lg={12}> <div className="pool-approval-container"> <div className="allowance-label"> Your pool allowances <OverlayTrigger placement="right" delay={{ show: 250, hide: 400 }} overlay={allowanceToolTip}> <FontAwesomeIcon icon={faQuestionCircle} className="info-tooltip-btn"/> </OverlayTrigger> </div> <FontAwesomeIcon icon={faChevronDown} className="show-approval-toggle" onClick={this.toggleApprovalDisplay}/> {tokenAllowances} </div> </Col> </Row> <Row className="selected-pool-buy-sell-row"> <Col lg={12}> {currentPoolTransactions} </Col> </Row> </div> </div> ) } } class VolumeGraph extends Component { componentWillMount() { const {selectedPool} = this.props; this.props.fetchConversionVolume(selectedPool); } componentWillReceiveProps(nextProps) { const {selectedPool, selectedPool: {symbol}} = nextProps; if (symbol !== this.props.selectedPool.symbol || (selectedPool.reserves && selectedPool.reserves.length > 0 && !this.props.selectedPool.reserves)) { this.props.fetchConversionVolume(selectedPool); } } componentWillUnmount() { this.props.resetPoolHistory(); } render() { const web3 = window.web3; const currentNetwork = web3.currentProvider.networkVersion; const {poolHistory, selectedPool: {symbol}} = this.props; let graphData = poolHistory.map(function(item){ return {x: moment(item.timeStamp).format('MM-DD'), y: parseInt(item.data)} }); if (graphData.length === 0) { return <div className="graph-message-text">Volume graph data not available</div> } if (currentNetwork !== '1') { return <div className="graph-message-text">Volume graph is available only on mainnet</div> } return ( <div> <VictoryChart > <VictoryLine style={{ data: { stroke: "#c43a31" }, parent: { border: "1px solid #ccc"} }} data={graphData} /> <VictoryAxis dependentAxis/> <VictoryAxis fixLabelOverlap={true}/> </VictoryChart> <div className="h7 text-center">Daily conversion vol from reserve to {symbol} (ETH)</div> </div> ) } }