').join(username)
}
+ let minAmount = parseFloat(min_amount)
+ const minAmountForUsd = parseFloat(this.state.minAmount.floatString)
+ minAmount = Math.max(minAmount, minAmountForUsd)
return
{details &&
{details}
}
- {min_amount &&
- {tt('uia_register_jsx.min_amount')} {min_amount} {sym || ''}
}
+ {minAmount &&
+ {tt('uia_register_jsx.min_amount')} {minAmount} {sym || ''}
}
{fee &&
{tt('uia_register_jsx.fee') + ': '}{fee} {sym || ''}
}
;
@@ -351,60 +314,6 @@ class UIARegister extends React.Component {
)
}
- _renderTransfer = () => {
- const { rules, sym, transferState, receivedTransfer, } = this.state
- const { to_transfer, memo_transfer, } = rules
-
- const transferring = transferState === TransferState.transferring
-
- const enough = this.enoughBalance()
-
- if (transferState === TransferState.received) {
- const { registrar, } = this.state
- const { memo, } = receivedTransfer;
- return (
- {this._renderTo(receivedTransfer.memo, null, registrar.name)}
- {this._renderParams(false)}
-
);
- }
-
- if (transferState === TransferState.timeouted) {
- return (
- {tt('asset_edit_deposit_jsx.timeouted')}
- {sym || ''}
- .
-
);
- }
-
- if (transferState === TransferState.waiting) {
- return (
- {tt('asset_edit_deposit_jsx.waiting')}
-
-
-
-
-
-
- );
- }
-
- return (
- {tt('uia_register_jsx.transfer_desc')}
-
{to_transfer || ''}
- {tt('uia_register_jsx.transfer_desc_2')}
-
{memo_transfer || ''}
- {transferring ?
-
: null}
-
- {!enough ?
- {tt('transfer_jsx.insufficient_funds')}
-
: null}
- {this._renderParams()}
-
);
- }
-
_renderWaiter = () => {
const { sym, registrar, onTransfer } = this.state
if (!onTransfer) {
@@ -465,12 +374,10 @@ class UIARegister extends React.Component {
} else {
const { rules, registrar, } = this.state
- const { to, to_type, to_fixed, to_transfer,
+ const { to, to_type, to_fixed,
min_amount, fee, details, } = rules
if (to_type === 'api') {
form = this._renderApi()
- } else if (to_type === 'transfer') {
- form = this._renderTransfer()
} else {
let memo_fixed = rules.memo_fixed
if (memo_fixed) {
diff --git a/src/pages/api/reg/[...all].js b/src/pages/api/reg/[...all].js
index 2447f4c..e79e682 100644
--- a/src/pages/api/reg/[...all].js
+++ b/src/pages/api/reg/[...all].js
@@ -16,7 +16,7 @@ import Tarantool from '@/server/tarantool';
initGolos();
-global.pollIntervals = {}
+global.pollers = {}
let handler = nextConnect({ attachParams: true, })
.use(regSessionMiddleware)
@@ -307,24 +307,80 @@ let handler = nextConnect({ attachParams: true, })
});
})
- .get('/api/reg/wait_for_transfer/:sym', async (req, res) => {
- const { sym } = req.params
+ .get('/api/reg/get_free_poller/:amount', async (req, res) => {
+ const amountStr = req.params.amount.split('%20').join(' ')
+ let amount
+ try {
+ amount = Asset(amountStr)
+ } catch (err) {
+ res.json({
+ status: 'err',
+ error: 'Asset parse error'
+ })
+ return
+ }
+
+ let freeAmount
+ try {
+ freeAmount = await Tarantool.instance('tarantool')
+ .call('get_free_reg_poller',
+ parseFloat(amount.amountFloat),
+ amount.symbol
+ )
+ freeAmount = freeAmount[0][0]
+ } catch (err) {
+ console.error('ERROR: cannot logout all server tokens', err);
+ }
+
+ if (freeAmount) {
+ const ret = Asset(0, amount.precision, amount.symbol)
+ ret.amountFloat = freeAmount.toString()
+ res.json({
+ status: 'ok',
+ amount: ret.toString()
+ })
+ return
+ }
+
+ res.json({
+ status: 'err',
+ error: 'Tarantool error'
+ })
+ })
+
+ .get('/api/reg/wait_for_transfer/:amount', async (req, res) => {
+ const amountStr = req.params.amount
+ const amount = Asset(amountStr)
+
+ const now = Date.now()
+ const uid = req.session.uid
- if (global.pollIntervals[sym]) throwErr(req, 400, ['Someone already waits for the transfer'])
+ if (!uid) throwErr(rew, 400, ['Not authorized - no uid in session'])
+
+ const poller = global.pollers[amountStr]
+ if (poller) {
+ if (poller.uid !== uid) {
+ throwErr(req, 400, ['Someone already waits for the transfer with ' + amountStr])
+ }
+ if ((now - poller.created) > 15*60*1000) {
+ delete global.pollers[amountStr]
+ poller = null
+ }
+ }
const username = config.get('registrar.account')
if (!username) throwErr(req, 400, ['No registrar.account in config'])
const getBalance = async () => {
const balances = await api.getAccountsBalancesAsync([username], {
- symbols: [sym]
+ symbols: [amount.symbol]
})
- let bal = balances[0][sym]
+ let bal = balances[0][amount.symbol]
if (bal) {
bal = bal.balance
return Asset(bal)
} else {
- const assets = await api.getAssetsAsync('', [sym])
+ const assets = await api.getAssetsAsync('', [amount.symbol])
if (!assets[0]) throwErr(req, 400, ['No such asset'])
bal = Asset(assets[0].supply)
bal.amount = 0
@@ -333,18 +389,29 @@ let handler = nextConnect({ attachParams: true, })
}
const stop = () => {
- if (global.pollIntervals[sym]) {
- clearInterval(global.pollIntervals[sym].interval)
- delete global.pollIntervals[sym]
+ if (global.pollers[amountStr]) {
+ delete global.pollers[amountStr]
}
}
- const initBal = await getBalance()
+ if (!poller) {
+ let initBal
+ try {
+ initBal = await getBalance()
+ } catch (err) {
+ throwErr(req, 400, ['Blockchain unavailable'])
+ }
+
+ global.pollers[amountStr] = {
+ created: now,
+ initBal,
+ uid
+ }
+ }
const pollMsec = process.env.NODE_ENV === 'development' ? 1000 : 30000
let tries = 0
- global.pollIntervals[sym] = { created: Date.now(), interval: setInterval(async () => {
+ for ( ;; ) {
if (tries > 2) {
- stop()
res.json({
status: 'err',
error: 'Timeouted'
@@ -353,17 +420,58 @@ let handler = nextConnect({ attachParams: true, })
}
++tries
- const bal = await getBalance()
+ let bal
+ try {
+ bal = await getBalance()
+ } catch (err) {
+ throwErr(req, 400, ['Blockchain unavailable'])
+ }
+
console.log('wait_for_transfer', initBal.toString(), bal.toString())
- if (bal.amount > initBal.amount) {
+ const delta = bal.amount.minus(initBal.amount)
+ if (delta.gte(amount)) {
+ let stopMe = false
+
+ let hist
+ try {
+ hist = await api.getAccountHistoryAsync(username, -1, 1000, {select_ops: ['transfer']})
+ } catch (err) {
+ throwErr(req, 400, ['Blockchain unavailable'])
+ }
+
+ const created = global.pollers[amountStr].created
+ for (let i = hist.length - 1; i >= 0; --i) {
+ const timestamp = +new Date(hist[i][1].timestamp)
+ if (timestamp < created) {
+ break
+ }
+
+ const [ opType, opData ] = hist[i][1].op
+ if (opType === 'transfer') {
+ if (opData.to === username && opData.amount === amountStr) {
+ stopMe = true
+ break
+ }
+ }
+ }
+
+ if (!req.session.deposited) {
+ req.session.deposited = {}
+ }
+ req.session.deposited[amount.symbol] = delta.toString()
+ await req.session.save()
+
stop()
- const delta = Asset(bal.amount - initBal.amount, bal.precision, bal.symbol)
+
res.json({
status: 'ok',
- delta: delta.toString()
+ deposited: amountStr
})
+ return
}
- }, pollMsec) }
+
+ await new Promise(resolve => setTimeout(resolve, pollMsec))
+ }
})
handler = addModalRoutes(handler);
diff --git a/src/server/reg.js b/src/server/reg.js
index 698d418..e260fd4 100644
--- a/src/server/reg.js
+++ b/src/server/reg.js
@@ -134,6 +134,9 @@ export function getClientCfg(req, params, locale = '') {
if (!cfg.registrar.uias)
cfg.registrar.uias = []
+ cfg.apidex_service = config.has('apidex_service')
+ && config.get('apidex_service')
+
let data = {
config: cfg,
oauthEnabled: config.has('oauth'),
diff --git a/src/utils/ApidexApiClient.js b/src/utils/ApidexApiClient.js
new file mode 100644
index 0000000..d82bd3c
--- /dev/null
+++ b/src/utils/ApidexApiClient.js
@@ -0,0 +1,84 @@
+import { fetchEx } from 'golos-lib-js/lib/utils'
+
+const request_base = {
+ timeout: 2000,
+ method: 'get',
+ headers: {
+ Accept: 'application/json',
+ 'Content-Type': 'application/json'
+ }
+}
+
+const pageBaseURL = 'https://coinmarketcap.com/currencies/'
+
+const getPageURL = (slug) => {
+ return new URL(slug + '/', pageBaseURL).toString()
+}
+
+export const apidexUrl = (apidex_service, pathname) => {
+ try {
+ return new URL(pathname, apidex_service.host).toString();
+ } catch (err) {
+ console.error('apidexUrl', err)
+ return ''
+ }
+}
+
+let cached = {}
+
+export async function apidexGetPrices(apidex_service, sym) {
+ const empty = {
+ price_usd: null,
+ price_rub: null,
+ page_url: null
+ }
+ if (!apidex_service || !apidex_service.host) return empty
+ let request = Object.assign({}, request_base)
+ try {
+ const now = new Date()
+ const cache = cached[sym]
+ if (cache && (now - cache.time) < 60000) {
+ return cache.resp
+ } else {
+ let resp = await fetchEx(apidexUrl(apidex_service, `/api/v1/cmc/${sym}`), request)
+ resp = await resp.json()
+ if (resp.data && resp.data.slug)
+ resp['page_url'] = getPageURL(resp.data.slug)
+ else
+ resp['page_url'] = null
+ cached[sym] = {
+ resp, time: now
+ }
+ return resp
+ }
+ } catch (err) {
+ console.error('apidexGetPrices', err)
+ return empty
+ }
+}
+
+let cachedAll = {}
+
+export async function apidexGetAll(apidex_service) {
+ const empty = {
+ data: {}
+ }
+ if (!apidex_service || !apidex_service.host) return empty
+ let request = Object.assign({}, request_base)
+ try {
+ const now = new Date()
+ if (cachedAll && (now - cachedAll.time) < 60000) {
+ return cachedAll.resp
+ } else {
+ let resp = await fetchEx(apidexUrl(apidex_service, `/api/v1/cmc`), request)
+ resp = await resp.json()
+ cachedAll = {
+ resp, time: now
+ }
+ return resp
+ }
+ } catch (err) {
+ console.error('apidexGetAll', err)
+ return empty
+ }
+}