mirror of
https://github.com/fullstack-cash/fullstack-gatsby-theme-bch-wallet.git
synced 2026-09-22 01:02:06 -07:00
Got sell UI working
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Row, Col, Box, Button } from 'adminlte-2-react'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
// import JSONPretty from "react-json-pretty"
|
||||
|
||||
let _this
|
||||
class Details extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
_this = this
|
||||
|
||||
this.state = {
|
||||
order: {}
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { order } = _this.state
|
||||
return (
|
||||
<>
|
||||
<Row>
|
||||
<Col sm={12}>
|
||||
<Box className=' border-none mt-2' loaded={!this.state.inFetch}>
|
||||
<Row className='text-center'>
|
||||
<Col sm={12}>
|
||||
<h1 id='SendTokens'>
|
||||
<FontAwesomeIcon
|
||||
className='title-icon'
|
||||
size='xs'
|
||||
icon='info-circle'
|
||||
/>
|
||||
<span>Details</span>
|
||||
</h1>
|
||||
{order._id && (
|
||||
<Box className='border-none details-data-content'>
|
||||
<p>
|
||||
<b>Message Type: </b>
|
||||
{order.messageType}
|
||||
</p>
|
||||
<p>
|
||||
<b>Message Class: </b>
|
||||
{order.messageClass}
|
||||
</p>
|
||||
<p>
|
||||
<b>Create At: </b>
|
||||
{new Date(order.localTimestamp).toLocaleString()}
|
||||
</p>
|
||||
<p>
|
||||
<b>ID: </b>
|
||||
{order._id}
|
||||
</p>
|
||||
<p>
|
||||
<b>Token Id: </b>
|
||||
{order.tokenId}
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<b>Order Type : </b>
|
||||
{order.buyOrSell}
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<b>Rate in Stats : </b>
|
||||
{order.rateInSats}
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<b>Min. Sats to Exchange : </b>
|
||||
{order.minSatsToExchange}
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<b>Num Tokens: </b>
|
||||
{order.numTokens}
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<b>Utxo TxId: </b>
|
||||
{order.utxoTxid}
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<b>Utxo Vout: </b>
|
||||
{order.utxoVout}
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<b>Tx Hex : </b>
|
||||
{order.txHex}
|
||||
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Timestamp : </b>
|
||||
{order.timestamp}
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<b>LocalTimestamp : </b>
|
||||
{order.localTimestamp}
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<b>p2wdbHash : </b>
|
||||
{order.p2wdbHash}
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<b>Offer Hash : </b>
|
||||
{order.offerHash}
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<b>Address References : </b>
|
||||
{order.addrReferences}
|
||||
|
||||
</p>
|
||||
</Box>
|
||||
)}
|
||||
</Col>
|
||||
<Col sm={12}>
|
||||
<div className='btn-wrapper'>
|
||||
<Button
|
||||
text='Take'
|
||||
type='primary'
|
||||
className='btn-lg on-click-event mr-1 ml-1 mt-1 take-btn-lg'
|
||||
onClick={() => _this.props.onTake({ data: order })}
|
||||
/>
|
||||
<Button
|
||||
text='Close'
|
||||
type='primary'
|
||||
className='btn-lg btn-close-entry mr-1 ml-1 mt-1'
|
||||
onClick={_this.handleClose}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Box>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
_this.handleData()
|
||||
}
|
||||
|
||||
componentDidUpdate () {
|
||||
if (_this.props.order._id !== _this.state.order._id) {
|
||||
_this.handleData()
|
||||
}
|
||||
}
|
||||
|
||||
handleData () {
|
||||
try {
|
||||
const { order } = _this.props
|
||||
console.log('order', order)
|
||||
_this.setState({
|
||||
order: order.data
|
||||
})
|
||||
} catch (err) {
|
||||
console.warn('Error in handleData()', err)
|
||||
}
|
||||
}
|
||||
|
||||
handleClose () {
|
||||
_this.props.onClose()
|
||||
}
|
||||
|
||||
// Detects if the input is a string and converts to json object
|
||||
isJson (data) {
|
||||
try {
|
||||
JSON.parse(data)
|
||||
return true
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
Details.propTypes = {
|
||||
order: PropTypes.object,
|
||||
onClose: PropTypes.func,
|
||||
onTake: PropTypes.func
|
||||
}
|
||||
export default Details
|
||||
@@ -0,0 +1,321 @@
|
||||
import React from 'react'
|
||||
import { Row, Col, Content, Box, DataTable, Button, Inputs } from 'adminlte-2-react'
|
||||
import Details from './details'
|
||||
import './orders.css'
|
||||
import Spinner from '../../../images/loader.gif'
|
||||
const { Text } = Inputs
|
||||
|
||||
const axios = require('axios').default
|
||||
const siteConfig = require('../../site-config')
|
||||
|
||||
let SERVER = siteConfig.bchDexUrl
|
||||
|
||||
// const EXPLORER_URL = 'https://explorer.bitcoin.com/bch/tx/'
|
||||
|
||||
let _this
|
||||
class Orders extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
_this = this
|
||||
this.state = {
|
||||
showEntry: false,
|
||||
data: [],
|
||||
orders: [],
|
||||
orderData: null,
|
||||
showTakeModal: false,
|
||||
takeInput: ''
|
||||
}
|
||||
|
||||
this.firstColumns = [
|
||||
{ title: 'Ticker', data: 'ticker' },
|
||||
{
|
||||
title: 'Token Id',
|
||||
data: 'tokenId',
|
||||
render: id => (
|
||||
<a href={`https://simpleledger.info/token/${id.tokenId}`} target='_blank' rel='noreferrer'>{id.subString}</a>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Type',
|
||||
data: 'buyOrSell'
|
||||
},
|
||||
{
|
||||
title: 'Offer hash',
|
||||
data: 'offerHash',
|
||||
render: hash => (
|
||||
<span className='on-click-event action-handler'>{hash.subString}</span>
|
||||
)
|
||||
},
|
||||
{ title: 'Qty', data: 'qty' },
|
||||
{ title: 'Sats Each', data: 'satsEach' },
|
||||
{ title: 'Sats Total', data: 'satsTotal' },
|
||||
{ title: 'USD Total', data: 'usdTotal' },
|
||||
{
|
||||
title: '',
|
||||
data: 'take',
|
||||
// render: order => (order.data.orderStatus === 'posted'
|
||||
// ? (
|
||||
// <div className='take-btn-table-wrapper'>
|
||||
// <Button
|
||||
// text='Take'
|
||||
// type='primary'
|
||||
// className='btn-lg on-click-event take-btn'
|
||||
// onClick={_this.handleTake}
|
||||
// />
|
||||
// </div>
|
||||
// )
|
||||
// : <p />
|
||||
// )
|
||||
render: order => (
|
||||
<div className='take-btn-table-wrapper'>
|
||||
<Button
|
||||
text='Take'
|
||||
type='primary'
|
||||
className='btn-lg on-click-event take-btn'
|
||||
onClick={_this.handleTake}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
render () {
|
||||
const { data, orderData } = _this.state
|
||||
return (
|
||||
<>
|
||||
<Content
|
||||
title='BCH Orders'
|
||||
subTitle='BCH Orders List'
|
||||
browserTitle='BCH Orders List'
|
||||
>
|
||||
<Row>
|
||||
{orderData && (
|
||||
<Col xs={12}>
|
||||
<Details order={orderData} onClose={_this.handleClose} onTake={_this.handleTake} />
|
||||
</Col>
|
||||
)}
|
||||
<Col xs={12}>
|
||||
<Box title='Order List'>
|
||||
<DataTable
|
||||
id='OrdersTable'
|
||||
className='order-table'
|
||||
columns={_this.firstColumns}
|
||||
data={data}
|
||||
options={{
|
||||
paging: true,
|
||||
lengthChange: false,
|
||||
searching: false,
|
||||
ordering: false,
|
||||
info: true,
|
||||
autoWidth: false
|
||||
}}
|
||||
onClickEvents={{
|
||||
onClickEvent: (data, rowIdx, rowData) => {
|
||||
console.log(data)
|
||||
if (data.key === 'hashHandler') { _this.handleHashClick(data) }
|
||||
|
||||
if (data.key === 'takeHandler') { _this.handleTake(data) }
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Col>
|
||||
</Row>
|
||||
</Content>
|
||||
{
|
||||
_this.state.showTakeModal && (
|
||||
<Content
|
||||
title='Taking order...'
|
||||
modal
|
||||
show={_this.state.showTakeModal}
|
||||
modalCloseButton
|
||||
onHide={_this.handleClose}
|
||||
>
|
||||
<div className='take-form-wrapper'>
|
||||
<div className='spinner'>
|
||||
<img alt='Loading...' src={Spinner} width={100} /><br />
|
||||
<p>Taking the other side of the trade...</p>
|
||||
</div>
|
||||
</div>
|
||||
</Content>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
async componentDidMount () {
|
||||
_this.handleOrders()
|
||||
|
||||
// Get data and update the table
|
||||
// every 20 seconds
|
||||
setInterval(() => {
|
||||
_this.handleOrders()
|
||||
}, 30000)
|
||||
}
|
||||
|
||||
async handleOrders () {
|
||||
const orders = await _this.getOrders()
|
||||
await _this.generateDataTable(orders)
|
||||
}
|
||||
|
||||
// REST petition to Get data fron the pw2db
|
||||
async getOrders () {
|
||||
try {
|
||||
// console.log('SERVER: ', SERVER)
|
||||
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${SERVER}/offer/list/`,
|
||||
data: {}
|
||||
}
|
||||
const result = await axios.request(options)
|
||||
// console.log('result.data', result.data)
|
||||
|
||||
_this.setState({
|
||||
orders: result.data
|
||||
})
|
||||
|
||||
return result.data
|
||||
} catch (err) {
|
||||
console.warn('Error in getOrders() ', err)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate table content
|
||||
async generateDataTable (dataArr = []) {
|
||||
try {
|
||||
const data = []
|
||||
// console.log(`dataArr: ${JSON.stringify(dataArr, null, 2)}`)
|
||||
|
||||
if(!this.props.bchWallet) {
|
||||
console.log(`BCH wallet is not initialized. Can not calculate price of BCH or tokens.`)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the spot price for BCH.
|
||||
const bchSpotPrice = await this.props.bchWallet.getUsd()
|
||||
|
||||
// console.log('bchSpotPrice: ', bchSpotPrice)
|
||||
|
||||
for (let i = 0; i < dataArr.length; i++) {
|
||||
const order = dataArr[i]
|
||||
// console.log(`order: ${JSON.stringify(order, null, 2)}`)
|
||||
|
||||
const satsTotal = order.numTokens * order.rateInBaseUnit
|
||||
let usdTotal = bchSpotPrice * this.props.bchWallet.bchjs.BitcoinCash.toBitcoinCash(satsTotal)
|
||||
usdTotal = `$${this.props.bchWallet.bchjs.Util.floor8(usdTotal)}`
|
||||
|
||||
const row = {
|
||||
ticker: order.ticker,
|
||||
tokenId: {
|
||||
tokenId: order.tokenId,
|
||||
subString: _this.cutString(order.tokenId)
|
||||
},
|
||||
// createdAt row data
|
||||
createdAt: new Date(order.timestamp).toLocaleString(),
|
||||
// Transaction id row data
|
||||
buyOrSell: order.buyOrSell,
|
||||
// Hash row data
|
||||
offerHash: {
|
||||
key: 'hashHandler',
|
||||
subString: _this.cutString(order.p2wdbHash),
|
||||
order: order.p2wdbHash,
|
||||
data: order
|
||||
},
|
||||
qty: order.numTokens,
|
||||
satsEach: order.rateInBaseUnit,
|
||||
satsTotal,
|
||||
usdTotal,
|
||||
orderStatus: order.offerStatus,
|
||||
take: {
|
||||
key: 'takeHandler',
|
||||
data: order
|
||||
}
|
||||
}
|
||||
data.push(row)
|
||||
}
|
||||
|
||||
_this.setState({ data })
|
||||
} catch (err) {
|
||||
console.warn('Error in generateDataTable() ', err)
|
||||
}
|
||||
}
|
||||
|
||||
cutString (txid) {
|
||||
try {
|
||||
const subTxid = txid.slice(0, 4)
|
||||
const subTxid2 = txid.slice(-4)
|
||||
return `${subTxid}...${subTxid2}`
|
||||
} catch (err) {
|
||||
console.warn('Error in cutString() ', err)
|
||||
}
|
||||
}
|
||||
|
||||
handleHashClick (data) {
|
||||
try {
|
||||
// data.isValid = data.isValid.toString()
|
||||
_this.setState({
|
||||
orderData: data
|
||||
})
|
||||
} catch (err) {
|
||||
_this.setState({
|
||||
orderData: null
|
||||
})
|
||||
console.warn('Error in handleHashClick() ', err)
|
||||
}
|
||||
}
|
||||
|
||||
handleClose () {
|
||||
_this.setState({
|
||||
orderData: null,
|
||||
showTakeModal: false
|
||||
})
|
||||
}
|
||||
|
||||
async handleTake (order) {
|
||||
console.log('handleTake() order: ', order)
|
||||
|
||||
const offerCid = order.data.p2wdbHash
|
||||
console.log('offerCid: ', offerCid)
|
||||
|
||||
// if (!order.data.status === 'posted') return
|
||||
|
||||
// TODO: Future functionality.
|
||||
// Throw up a modal to query how much they want to take.
|
||||
// _this.setState({
|
||||
// tankenInput: '',
|
||||
// showTakeModal: true
|
||||
// })
|
||||
|
||||
// Show the modal
|
||||
_this.setState({
|
||||
showTakeModal: true
|
||||
})
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${SERVER}/offer/take/`,
|
||||
data: {
|
||||
offerCid
|
||||
}
|
||||
}
|
||||
const result = await axios.request(options)
|
||||
console.log('result of taking offer: ', result)
|
||||
|
||||
// Hide the modal
|
||||
_this.setState({
|
||||
showTakeModal: false
|
||||
})
|
||||
}
|
||||
|
||||
handleModalInputs (event) {
|
||||
const value = event.target.value
|
||||
_this.setState({
|
||||
[event.target.name]: value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default Orders
|
||||
@@ -0,0 +1,29 @@
|
||||
.btn-close-entry{
|
||||
width: 150px;
|
||||
}
|
||||
.action-handler{
|
||||
color: #3c8dbc;
|
||||
cursor: pointer;
|
||||
}
|
||||
.details-data-content{
|
||||
text-align: left;
|
||||
}
|
||||
.take-form-wrapper{
|
||||
/* display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column; */
|
||||
text-align: center;
|
||||
}
|
||||
#OrdersTable> tbody >.odd{
|
||||
height: 55px!important;
|
||||
}
|
||||
#OrdersTable> tbody > .even{
|
||||
height: 55px!important;
|
||||
}
|
||||
.take-btn-lg{
|
||||
width: 150px;
|
||||
}
|
||||
.take-btn-table-wrapper{
|
||||
text-align: end;
|
||||
padding-right: 10px;
|
||||
}
|
||||
@@ -3,11 +3,14 @@ import PropTypes from 'prop-types'
|
||||
import { Content, Row, Col, Box, Button } from 'adminlte-2-react'
|
||||
import TokenCard from './token-card'
|
||||
import TokenModal from './token-modal'
|
||||
import SellModal from './sell-modal'
|
||||
import Spinner from '../../../images/loader.gif'
|
||||
// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import SendTokens from './send-tokens'
|
||||
// import { SlpMutableData } from 'slp-mutable-data'
|
||||
|
||||
let _this
|
||||
|
||||
class Tokens extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
@@ -16,6 +19,7 @@ class Tokens extends React.Component {
|
||||
tokens: [],
|
||||
selectedTokenToView: '',
|
||||
showModal: false,
|
||||
showSellModal: false,
|
||||
inFetch: true,
|
||||
errMsg: '',
|
||||
selectedTokenToSend: '',
|
||||
@@ -99,6 +103,7 @@ class Tokens extends React.Component {
|
||||
id={`token-${i}`}
|
||||
token={val}
|
||||
showToken={_this.showToken}
|
||||
showSellModal={_this.showSellModal}
|
||||
selectToken={_this.selectToken}
|
||||
/>
|
||||
</Col>
|
||||
@@ -112,6 +117,7 @@ class Tokens extends React.Component {
|
||||
)}
|
||||
</Content>
|
||||
)}
|
||||
|
||||
<TokenModal
|
||||
bchWallet={_this.props.bchWallet}
|
||||
token={
|
||||
@@ -123,6 +129,18 @@ class Tokens extends React.Component {
|
||||
show={_this.state.showModal}
|
||||
explorerURL={_this.state.explorerURL}
|
||||
/>
|
||||
|
||||
<SellModal
|
||||
bchWallet={_this.props.bchWallet}
|
||||
token={
|
||||
_this.state.selectedTokenToView
|
||||
? _this.state.selectedTokenToView
|
||||
: {}
|
||||
}
|
||||
handleOnHide={_this.onHandleToggleSellModal}
|
||||
show={_this.state.showSellModal}
|
||||
explorerURL={_this.state.explorerURL}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -196,6 +214,14 @@ class Tokens extends React.Component {
|
||||
_this.onHandleToggleModal()
|
||||
}
|
||||
|
||||
// This is called by the token-card when the 'Sell' button is clicked.
|
||||
showSellModal (selectedTokenToView) {
|
||||
_this.setState({
|
||||
selectedTokenToView
|
||||
})
|
||||
_this.onHandleToggleSellModal()
|
||||
}
|
||||
|
||||
selectToken (selectedTokenToSend) {
|
||||
_this.setState({
|
||||
selectedTokenToSend
|
||||
@@ -215,6 +241,15 @@ class Tokens extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
onHandleToggleSellModal (refresh = null) {
|
||||
_this.setState({
|
||||
showSellModal: !_this.state.showSellModal
|
||||
})
|
||||
if (refresh) {
|
||||
_this.handleGetTokens(true)
|
||||
}
|
||||
}
|
||||
|
||||
handleError (error) {
|
||||
let errMsg = ''
|
||||
if (error.message) {
|
||||
|
||||
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
This modal controlls the selling of tokens.
|
||||
It was adapted from token-modal.js and contains references to 'burn' tokens.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Content, Row, Col, Box, Button, Inputs } from 'adminlte-2-react'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import axios from 'axios'
|
||||
|
||||
// Local libraries
|
||||
import './token.css'
|
||||
const siteConfig = require('../../site-config')
|
||||
|
||||
let _this
|
||||
const { Text } = Inputs
|
||||
|
||||
class SellModal extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
_this = this
|
||||
this.state = {
|
||||
copySuccess: '',
|
||||
isBurnView: false,
|
||||
txId: '',
|
||||
errMsg: '',
|
||||
inFetch: false,
|
||||
sellQty: 1,
|
||||
pricePerToken: 0.02
|
||||
}
|
||||
|
||||
this.modalFooter = (
|
||||
<>
|
||||
<Button text='Close' pullLeft onClick={this.handleModal} />
|
||||
<Button
|
||||
type='primary'
|
||||
text='Sell'
|
||||
pullRight
|
||||
onClick={this.handleConfirm}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
this.confirmFooter = (
|
||||
<>
|
||||
<Button text='No' pullLeft onClick={() => this.sellTokens(false)} />
|
||||
<Button
|
||||
type='primary'
|
||||
text='Yes'
|
||||
pullRight
|
||||
onClick={() => this.sellTokens(true)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
this.onDoneFooter = (
|
||||
<>
|
||||
<Button
|
||||
text='Close'
|
||||
pullLeft
|
||||
onClick={() => this.sellTokens(false)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const token = _this.props.token
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<Content
|
||||
title={_this.state.isBurnView ? `Sell ${token.name}` : token.name}
|
||||
modal
|
||||
modalFooter={
|
||||
!_this.state.isBurnView
|
||||
? this.modalFooter
|
||||
: _this.state.txId || _this.state.errMsg
|
||||
? this.onDoneFooter
|
||||
: this.confirmFooter
|
||||
}
|
||||
show={_this.props.show}
|
||||
modalCloseButton
|
||||
onHide={_this.handleModal}
|
||||
>
|
||||
<Row>
|
||||
<Col sm={12}>
|
||||
{_this.state.isBurnView && !_this.state.txId && (
|
||||
<Box loaded={!_this.state.inFetch} className='border-none'>
|
||||
<p>
|
||||
Are you sure you want to sell {`${this.state.sellQty} `}
|
||||
tokens at {`$${this.state.pricePerToken} `} per token?
|
||||
</p>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{_this.state.isBurnView && _this.state.txId && (
|
||||
<div className='text-center '>
|
||||
<p>Order Submitted</p>
|
||||
<p>P2WDB entry CID:</p>
|
||||
<a
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
href={`https://p2wdb.fullstack.cash/entry/hash/${_this.state.txId}`}
|
||||
>
|
||||
{_this.state.txId}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{_this.state.isBurnView && _this.state.errMsg && (
|
||||
<div className='text-center'>
|
||||
<p className='error-color'> {_this.state.errMsg}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!_this.state.isBurnView && (
|
||||
<Box className=' border-none '>
|
||||
<Row>
|
||||
<Col
|
||||
sm={12}
|
||||
className='text-center tokenModal-info-container'
|
||||
>
|
||||
|
||||
<Row className='tokenModal-info-content mt-1 text-left'>
|
||||
<Col xs={12}>
|
||||
<Row>
|
||||
<Col xs={12} sm={3}>
|
||||
<b>TokenId:</b>
|
||||
</Col>
|
||||
<Col xs={9} sm={7}>
|
||||
{token.tokenId}
|
||||
</Col>
|
||||
<Col
|
||||
xs={3}
|
||||
sm={2}
|
||||
className={
|
||||
_this.state.copySuccess ? 'nopadding' : ''
|
||||
}
|
||||
>
|
||||
{_this.state.copySuccess === 'tokenId'
|
||||
? (
|
||||
<div className='copied-text'>
|
||||
<span>Copied!</span>
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
<FontAwesomeIcon
|
||||
className='icon btn-animation'
|
||||
style={{ cssFloat: 'right' }}
|
||||
size='lg'
|
||||
onClick={() =>
|
||||
_this.copyToClipBoard('tokenId')}
|
||||
icon='copy'
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row className='tokenModal-info-content mt-1 text-left'>
|
||||
<Col xs={12}>
|
||||
<Row>
|
||||
<Col xs={12} sm={3}>
|
||||
<b>Ticker:</b>
|
||||
</Col>
|
||||
<Col xs={12} sm={9}>
|
||||
{token.ticker}
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row className='tokenModal-info-content mt-1 text-left'>
|
||||
<Col xs={12}>
|
||||
<Row>
|
||||
<Col xs={12} sm={3}>
|
||||
<b>Balance:</b>
|
||||
</Col>
|
||||
<Col xs={12} sm={9}>
|
||||
{token.qty}
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row className='tokenModal-info-content mt-1 text-left'>
|
||||
<Col xs={12}>
|
||||
<Row>
|
||||
<Col xs={12} sm={3}>
|
||||
<b>Sell Qty:</b>
|
||||
</Col>
|
||||
<Col xs={12} sm={9}>
|
||||
<Text
|
||||
id='sellQty'
|
||||
name='sellQty'
|
||||
placeholder='1'
|
||||
label=''
|
||||
labelPosition='below'
|
||||
onChange={_this.handleSellQty}
|
||||
className='title-icon'
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row className='tokenModal-info-content mt-1 text-left'>
|
||||
<Col xs={12}>
|
||||
<Row>
|
||||
<Col xs={12} sm={3}>
|
||||
<b>Price Per<br />Token (USD):</b>
|
||||
</Col>
|
||||
<Col xs={12} sm={9}>
|
||||
<Text
|
||||
id='pricePerToken'
|
||||
name='pricePerTOken'
|
||||
placeholder='0.02'
|
||||
label=''
|
||||
labelPosition='below'
|
||||
onChange={_this.handlePricePerToken}
|
||||
className='title-icon'
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
</Box>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Content>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// Update the quantity of tokens to sell
|
||||
handleSellQty(event) {
|
||||
const value = event.target.value
|
||||
// console.log('value: ', value)
|
||||
|
||||
_this.setState({
|
||||
sellQty: parseFloat(value)
|
||||
})
|
||||
|
||||
// console.log('_this.state.sellQty: ', _this.state.sellQty)
|
||||
}
|
||||
|
||||
handlePricePerToken(event) {
|
||||
const value = event.target.value
|
||||
// console.log('value: ', value)
|
||||
|
||||
_this.setState({
|
||||
pricePerToken: parseFloat(value)
|
||||
})
|
||||
}
|
||||
|
||||
// copy info to clipboard
|
||||
copyToClipBoard (key) {
|
||||
const val = _this.props.token[key]
|
||||
const textArea = document.createElement('textarea')
|
||||
textArea.value = val // copyText.textContent;
|
||||
document.body.appendChild(textArea)
|
||||
textArea.select()
|
||||
document.execCommand('Copy')
|
||||
textArea.remove()
|
||||
|
||||
_this.handleCopySuccess(key)
|
||||
}
|
||||
|
||||
handleCopySuccess (key) {
|
||||
_this.setState({
|
||||
copySuccess: key
|
||||
})
|
||||
setTimeout(() => {
|
||||
_this.setState({
|
||||
copySuccess: ''
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
// This is called when the user clicks the 'Sell' button to place their order.
|
||||
handleConfirm () {
|
||||
// Verify that inputs are valid.
|
||||
if(isNaN(_this.state.sellQty)) {
|
||||
alert('Token quantity must be a number.')
|
||||
return
|
||||
}
|
||||
if(isNaN(_this.state.pricePerToken)) {
|
||||
alert('Price per token must be a number.')
|
||||
return
|
||||
}
|
||||
|
||||
_this.setState({
|
||||
isBurnView: true
|
||||
})
|
||||
}
|
||||
|
||||
handleModal () {
|
||||
// if txId exist refresh tokens on close
|
||||
_this.props.handleOnHide(_this.state.txId)
|
||||
|
||||
setTimeout(() => {
|
||||
_this.setState({
|
||||
isBurnView: false,
|
||||
txId: '',
|
||||
errMsg: '',
|
||||
inFetch: false
|
||||
})
|
||||
}, 200)
|
||||
}
|
||||
|
||||
async sellTokens(isConfirmed) {
|
||||
try {
|
||||
// Dismiss
|
||||
if (!isConfirmed) {
|
||||
_this.handleModal()
|
||||
return
|
||||
}
|
||||
|
||||
console.log('Putting in order to sell tokens...')
|
||||
|
||||
// Throw up spinny-waiting-gif
|
||||
_this.setState({
|
||||
inFetch: true
|
||||
})
|
||||
|
||||
const { bchWallet, token } = _this.props
|
||||
console.log('token: ', token)
|
||||
|
||||
// Error if trying to sell more tokens than the wallet holds.
|
||||
if(token.qty < _this.state.sellQty) {
|
||||
alert(`Error: The wallet has ${token.qty} tokens, which is less than the ${_this.state.sellQty} tokens you are trying to sell.`)
|
||||
}
|
||||
|
||||
console.log(`bch-dex server: ${siteConfig.bchDexUrl}`)
|
||||
|
||||
// Convert the dollar amount of tokens to sats.
|
||||
const bchSpotPrice = await bchWallet.getUsd()
|
||||
console.log('bchSpotPrice: ', bchSpotPrice)
|
||||
const bchPerToken = _this.state.pricePerToken/bchSpotPrice
|
||||
console.log('bchPerToken: ', bchPerToken)
|
||||
const satsPerToken = Math.floor(bchWallet.bchjs.BitcoinCash.toSatoshi(bchPerToken))
|
||||
console.log('satsPerToken: ', satsPerToken)
|
||||
|
||||
const orderObj = {
|
||||
order: {
|
||||
lokadId: 'SWP',
|
||||
messageType: 1,
|
||||
messageClass: 1,
|
||||
tokenId: token.tokenId,
|
||||
buyOrSell: 'sell',
|
||||
numTokens: _this.state.sellQty,
|
||||
rateInBaseUnit: satsPerToken,
|
||||
minUnitsToExchange: satsPerToken*_this.state.sellQty,
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios.post(`${siteConfig.bchDexUrl}/order`, orderObj)
|
||||
console.log(`result.data: `, result.data)
|
||||
|
||||
// console.log('starting sleep')
|
||||
// await bchWallet.bchjs.Util.sleep(5000)
|
||||
// console.log('ended sleep')
|
||||
|
||||
// Remove spinny-waiting-gif and display the TXID of the transaction.
|
||||
_this.setState({
|
||||
txId: result.data.hash,
|
||||
inFetch: false
|
||||
})
|
||||
} catch(error) {
|
||||
console.warn(error)
|
||||
_this.setState({
|
||||
errMsg: error.message,
|
||||
inFetch: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated. This handled the burn API call.
|
||||
async handleBurnAll (isConfirmed) {
|
||||
try {
|
||||
// Dismiss
|
||||
if (!isConfirmed) {
|
||||
_this.handleModal()
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* BURN ALL
|
||||
*
|
||||
*/
|
||||
_this.setState({
|
||||
inFetch: true
|
||||
})
|
||||
const { bchWallet, token } = _this.props
|
||||
|
||||
const result = await bchWallet.burnAll(token.tokenId)
|
||||
console.log('burn txid: ', result)
|
||||
_this.setState({
|
||||
txId: result,
|
||||
inFetch: false
|
||||
})
|
||||
} catch (error) {
|
||||
console.warn(error)
|
||||
_this.setState({
|
||||
errMsg: error.message,
|
||||
inFetch: false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SellModal.propTypes = {
|
||||
token: PropTypes.object.isRequired,
|
||||
show: PropTypes.bool.isRequired,
|
||||
handleOnHide: PropTypes.func.isRequired,
|
||||
bchWallet: PropTypes.object, // get minimal-slp-wallet instance
|
||||
explorerURL: PropTypes.string
|
||||
}
|
||||
|
||||
export default SellModal
|
||||
@@ -3,7 +3,9 @@ import PropTypes from 'prop-types'
|
||||
import { Row, Col, Box, Button } from 'adminlte-2-react'
|
||||
import Jdenticon from 'react-jdenticon'
|
||||
import './token.css'
|
||||
|
||||
let _this
|
||||
|
||||
class TokenCard extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
@@ -42,7 +44,7 @@ class TokenCard extends React.Component {
|
||||
</div>
|
||||
</Col>
|
||||
|
||||
<Col xs={6} className='text-center mb-1'>
|
||||
<Col xs={4} className='text-center mb-1'>
|
||||
<Button
|
||||
text='Info'
|
||||
type='primary'
|
||||
@@ -52,7 +54,19 @@ class TokenCard extends React.Component {
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col xs={6} className='text-center'>
|
||||
|
||||
<Col xs={4} className='text-center mb-1'>
|
||||
<Button
|
||||
text='Sell'
|
||||
type='primary'
|
||||
className='btn-lg max-width'
|
||||
onClick={() => {
|
||||
_this.props.showSellModal(token)
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<Col xs={4} className='text-center'>
|
||||
<Button
|
||||
text='Send'
|
||||
type='primary'
|
||||
|
||||
@@ -5,11 +5,18 @@ import Wallet from './admin-lte/wallet'
|
||||
import Tokens from './admin-lte/tokens'
|
||||
import Configure from './admin-lte/configure'
|
||||
import SendReceive from './admin-lte/send-receive'
|
||||
import Orders from './admin-lte/orders'
|
||||
|
||||
const { Item } = Sidebar
|
||||
|
||||
const MenuComponents = props => {
|
||||
return [
|
||||
{
|
||||
active: true,
|
||||
key: 'Orders',
|
||||
component: <Orders key='Orders' {...props} />,
|
||||
menuItem: <Item icon='fas-coins' key='Orders' text='Orders' />
|
||||
},
|
||||
{
|
||||
key: 'Tokens',
|
||||
component: <Tokens key='Tokens' {...props} />,
|
||||
|
||||
@@ -23,10 +23,24 @@ const config = {
|
||||
|
||||
// Interface used by minial-slp-wallet
|
||||
interface: 'consumer-api',
|
||||
restURL: 'https://free-bch.fullstack.cash'
|
||||
restURL: 'https://free-bch.fullstack.cash',
|
||||
// restURL: 'http://localhost:5005'
|
||||
// interface: 'rest-api',
|
||||
// restURL: 'https://bchn.fullstack.cash/v5/'
|
||||
|
||||
// Set to true if you want to manually override auto-detection of the DEX URL,
|
||||
// and use the setting below.
|
||||
manualDexUrl: false,
|
||||
// Default URL for bch-dex
|
||||
bchDexUrl: 'http://localhost:5700'
|
||||
}
|
||||
|
||||
// Attempt to auto-detect the URL for the bch-dex.
|
||||
if(window && window.document && window.document.domain) {
|
||||
if(window.document.domain.includes('192.168') ||
|
||||
window.document.domain.includes('localhost')) {
|
||||
config.bchDexUrl = `http://${window.document.domain}:5700`
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = config
|
||||
|
||||
Reference in New Issue
Block a user