diff --git a/config/default.json b/config/default.json
index dd9fffe..ab880c9 100644
--- a/config/default.json
+++ b/config/default.json
@@ -13,7 +13,8 @@
"add_to_fee": "1.000 GOLOS",
"signing_key": "5K67PNheLkmxkgJ5UjvR8Nyt3GVPoLEN1dMZjFuNETzrNyMecPG",
"delegation": "75000.000000 GESTS",
- "free_regs_per_day": 10
+ "free_regs_per_day": 10,
+ "uias": ["YMUSDT", "YMPZM", "YMHIVE"]
},
"server_session_secret": "exiKdyF+IwRIXJDmtGIl4vWUz4i3eVSISpfZoeYc0s4=",
"session_cookie_key": "X-Reg-ISession",
diff --git a/package.json b/package.json
index 4ea011d..fd1002f 100644
--- a/package.json
+++ b/package.json
@@ -34,6 +34,7 @@
"passport-yandex": "^0.0.5",
"querystring": "^0.2.1",
"react": "^17.0.2",
+ "react-copy-to-clipboard": "^5.1.0",
"react-dom": "^17.0.2",
"react-foundation-components": "git+https://github.com/golos-blockchain/react-foundation-components.git#5dbfb800aff45988c57bb7d09c1c235a8b49b418",
"react-google-recaptcha": "^2.1.0",
diff --git a/src/App.scss b/src/App.scss
index 43ac1a1..74dd489 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -14,6 +14,7 @@
@import "./modules/LoginForm";
@import "./modules/PendingTx";
@import "./modules/PermissionsList";
+@import "./modules/register/UIARegister";
@import "./pages/_common";
@import "./pages/index";
diff --git a/src/elements/register/VerifyWayTabs.jsx b/src/elements/register/VerifyWayTabs.jsx
index 7823c0f..9983c68 100644
--- a/src/elements/register/VerifyWayTabs.jsx
+++ b/src/elements/register/VerifyWayTabs.jsx
@@ -42,12 +42,21 @@ class VerifyWayTabs extends React.Component {
transfer = {transfer}
}
+ let uia = tt('verify_way_tabs_jsx.with_uia')
+ if (currentWay === 'uia') {
+ uia = {uia}
+ } else {
+ uia = {uia}
+ }
+
return
{social}
|
{invite}
|
{transfer}
+ |
+ {uia}
}
}
diff --git a/src/locales/en.json b/src/locales/en.json
index 2696007..20572ff 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -93,6 +93,18 @@
"cannot_reset": "Unfortunately, something went wrong. We cannot set your password. But everything is under control - it is your temporary key. Save it, and contact us: ",
"unload_warning": "Do you saved the key? Or you want to cancel registration? If transfer already sent, there are no refund possible."
},
+ "uia_register_jsx": {
+ "select_uia": "Choose cryptocurrencу",
+ "register_with": "Register with...",
+ "no_such_asset": " - no such UIA.",
+ "deposit_unavailable": " - deposit temporarily unavailable.",
+ "min_amount": "Min amount",
+ "fee": "Fee",
+ "memo_fixed": "Memo",
+ "to": "Send tokens to address/account",
+ "api_error": "Cannot get address. Try again later. If problem still occurs, contact the issuer of ",
+ "api_error_details": "and send the error details:"
+ },
"invites_jsx": {
"claim_wrong_secret": "Wrong secret",
"claim_wrong_secret_fatal": "No such invite check",
@@ -239,8 +251,7 @@
"worker_request_vote": "Voting for worker requests",
"proposal_create": "Creating transaction proposals",
"proposal_delete": "Removing transaction proposals",
- "proposal_update": "Voting in transaction proposals",
- "proposal_update_active": "Voting in transaction proposals with active authorities"
+ "proposal_update": "Voting in transaction proposals"
},
"recovery": {
"change_title": "Change recovery account",
@@ -311,7 +322,8 @@
"verify_way_tabs_jsx": {
"social": "Social",
"invite_code": "Invite-code",
- "transfer": "Transfer"
+ "transfer": "Transfer",
+ "with_uia": "With cryptocurrencies"
},
"g": {
"APP_NAME": "Golos",
diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json
index 7e3383d..2b771f2 100644
--- a/src/locales/ru-RU.json
+++ b/src/locales/ru-RU.json
@@ -93,6 +93,18 @@
"cannot_reset": "К сожалению, случилась какая-то ошибка и мы не смогли установить пароль. Но все под контролем - вот ваш временный ключ. Сохраните его и обратитесь к нам за помощью: ",
"unload_warning": "Вы точно сохранили ключ, или вы хотите отменить регистрацию? Если перевод уже сделан, то автоматического возврата средств не будет."
},
+ "uia_register_jsx": {
+ "select_uia": "Выберите криптовалюту",
+ "register_with": "Регистрация с помощью...",
+ "no_such_asset": " - такого UIA не существует.",
+ "deposit_unavailable": " - депозит временно недоступен.",
+ "min_amount": "Минимальная сумма",
+ "fee": "Комиссия",
+ "memo_fixed": "Заметка/memo",
+ "to": "Отправьте токены на адрес/аккаунт",
+ "api_error": "Не удается получить адрес. Попробуйте позднее. Если проблема сохраняется, свяжитесь с эмитентом ",
+ "api_error_details": "и сообщите подробности ошибки:"
+ },
"invites_jsx": {
"claim_wrong_secret": "Неверно указан ключ",
"claim_wrong_secret_fatal": "Такого чека нет",
@@ -239,8 +251,7 @@
"worker_request_vote": "Голосование за заявки на работу (воркеры)",
"proposal_create": "Создание пропозалов",
"proposal_delete": "Удаление пропозалов",
- "proposal_update": "Голосование в пропозалах",
- "proposal_update_active": "Голосование в пропозалах с активным ключом"
+ "proposal_update": "Голосование в пропозалах"
},
"recovery": {
"change_title": "Задать аккаунт для восстановления",
@@ -311,7 +322,8 @@
"verify_way_tabs_jsx": {
"social": "Соцсети",
"invite_code": "Инвайт-код",
- "transfer": "Перевод с биржи"
+ "transfer": "Перевод с биржи",
+ "with_uia": "С помощью криптовалюты"
},
"g": {
"APP_NAME": "Голос",
diff --git a/src/modules/register/TransferWaiter.jsx b/src/modules/register/TransferWaiter.jsx
new file mode 100644
index 0000000..1804d00
--- /dev/null
+++ b/src/modules/register/TransferWaiter.jsx
@@ -0,0 +1,100 @@
+import React from 'react'
+import tt from 'counterpart'
+import { Asset } from 'golos-lib-js/lib/utils';
+
+import LoadingIndicator from '@/elements/LoadingIndicator'
+import { callApi, } from '@/utils/RegApiClient'
+
+class TransferWaiter extends React.Component {
+ state = {
+ }
+
+ constructor(props) {
+ super(props)
+ }
+
+ poll = async (sym) => {
+ const retry = async () => {
+ await new Promise(resolve => setTimeout(resolve, 1000))
+ if (!this.state.stopped)
+ this.poll(sym)
+ }
+ try {
+ let res = await callApi('/api/reg/wait_for_transfer/' + sym)
+ res = await res.json()
+ if (res.status === 'ok') {
+ const { onTransfer } = this.props
+ onTransfer(Asset(res.delta))
+ } else {
+ console.error(res)
+ await retry()
+ }
+ } catch (err) {
+ console.error('TransferWaiter', err)
+ await retry()
+ }
+ }
+
+ start = async () => {
+ this.setState({
+ seconds: 30*60,
+ stopped: false
+ })
+
+ this.countdown = setInterval(() => {
+ const { seconds } = this.state
+ if (seconds === 0) {
+ console.log('Countdown reached, stop.')
+ this.stop()
+ return
+ }
+ this.setState({
+ seconds: seconds - 1
+ })
+ }, 1000)
+
+ const { sym, } = this.props
+
+ this.poll(sym)
+ }
+
+ componentDidMount() {
+ this.start()
+ }
+
+ stop = () => {
+ if (this.countdown) clearInterval(this.countdown)
+ this.setState({
+ stopped: true
+ })
+ }
+
+ componentWillUnmount() {
+ this.stop()
+ }
+
+ componentDidUpdate(prevProps) {
+ if (this.props.sym !== prevProps.sym) {
+ this.stop()
+ this.start()
+ }
+ }
+
+ render() {
+ const { seconds } = this.state
+ if (!seconds) return null
+ const min = Math.floor(seconds / 60)
+ const sec = seconds % 60
+ const remaining = min.toString().padStart(2, '0') + ':' + sec.toString().padStart(2, '0')
+ const { sym, title } = this.props
+ return
+ {title}
+
+
+ {remaining}
+
+
+ }
+}
+
+export default TransferWaiter
diff --git a/src/modules/register/UIARegister.jsx b/src/modules/register/UIARegister.jsx
new file mode 100644
index 0000000..e702cbb
--- /dev/null
+++ b/src/modules/register/UIARegister.jsx
@@ -0,0 +1,517 @@
+import React from 'react'
+import CopyToClipboard from 'react-copy-to-clipboard'
+import tt from 'counterpart'
+import cn from 'classnames'
+import golos, { api, broadcast } from 'golos-lib-js'
+import { Asset } from 'golos-lib-js/lib/utils';
+import { key_utils, PrivateKey } from 'golos-lib-js/lib/auth/ecc'
+import Link from 'next/link'
+
+import LoadingIndicator from '@/elements/LoadingIndicator'
+import AccountName from '@/elements/register/AccountName'
+import VerifyWayTabs from '@/elements/register/VerifyWayTabs'
+import TransferWaiter from '@/modules/register/TransferWaiter'
+import KeyFile from '@/utils/KeyFile'
+import { emptyAuthority } from '@/utils/RecoveryUtils'
+import { callApi, } from '@/utils/RegApiClient'
+import { withRouterHelpers, } from '@/utils/routing'
+
+function getAssetMeta(asset) {
+ let sym
+ try {
+ sym = asset.supply && asset.supply.split(' ')[1]
+ } catch (err) {
+ console.warn(err)
+ }
+ let res = {}
+ try {
+ let obj = JSON.parse(asset.json_metadata)
+ if (typeof(obj) === 'object' && obj && !Array.isArray(obj)) {
+ res = obj
+ }
+ } catch (err) {
+ }
+ if (sym === 'GOLOS') {
+ res.image_url = '/images/golos.png'
+ } else if (sym === 'GBG') {
+ res.image_url = '/images/gold-golos.png'
+ }
+ return res
+}
+
+const TransferState = {
+ initial: 0,
+ transferring: 1,
+ waiting: 2,
+ received: 3,
+ timeouted: 4,
+};
+
+class APIError extends Error {
+ constructor(errReason, errData) {
+ super('API Error')
+ this.reason = errReason
+ this.data = errData
+ }
+}
+
+class UIARegister extends React.Component {
+ state = {
+ loading: true,
+ error: '',
+ }
+
+ async componentDidMount() {
+ const { clientCfg } = this.props
+
+ golos.config.set('websocket', clientCfg.config.ws_connection_client)
+ if (clientCfg.config.chain_id)
+ golos.config.set('chain_id', clientCfg.config.chain_id)
+
+ const { uias } = clientCfg.config.registrar
+
+ const path = this.getPath()
+ if (path[1]) {
+ const params = new URLSearchParams(path[1])
+ const sym = params.get('uia')
+ if (sym) {
+ let assets
+ assets = await golos.api.getAssetsAsync('', uias)
+
+ let error
+ if (!uias.includes(sym)) {
+ error = sym + tt('uia_register_jsx.no_such_asset')
+ } else {
+ for (const asset of assets) {
+ const symbol = Asset(asset.supply).symbol
+ if (sym === symbol) {
+ const meta = getAssetMeta(asset)
+ const { deposit, telegram } = meta
+ if (deposit.unavailable) {
+ error = sym + tt('uia_register_jsx.deposit_unavailable')
+ break
+ }
+
+ const accName = clientCfg.config.registrar.account
+ let registrar = await golos.api.getAccounts([accName])
+ registrar = registrar[0]
+
+ const { to_type, to_api, } = deposit
+ if (to_type === 'transfer') {
+ /*clearOldAddresses();
+ const addr = loadAddress(sym, asset.creator);
+ if (addr) {
+ this.setState({
+ transferState: TransferState.received,
+ receivedTransfer: {
+ memo: addr,
+ },
+ });
+ }*/
+ }
+
+ this.setState({
+ transferState: TransferState.initial,
+ rules: { ...deposit, creator: asset.creator, telegram },
+ registrar,
+ sym,
+ copied_addr: false,
+ copied_memo: false,
+ }, () => {
+ if (to_type === 'api') {
+ this.doAPI()
+ }
+ })
+ break
+ }
+ }
+ }
+
+ this.setState({
+ loading: false,
+ assets,
+ sym,
+ error
+ })
+ return
+ }
+ }
+
+ let assets = []
+ if (uias.length) {
+ assets = await golos.api.getAssetsAsync('', uias)
+ }
+ this.setState({
+ loading: false,
+ assets,
+ error: null
+ })
+ }
+
+ getPath = () => {
+ const { router } = this.props
+ let path = (router.asPath.split('#')[0])
+ return path.split('?')
+ }
+
+ doReq = async (acc, sym) => {
+ const url = '/api/reg/uia_address/' + sym + '/' + acc
+ let res = await callApi(url)
+ res = await res.json()
+ return res
+ }
+
+ async doAPI() {
+ const { clientCfg } = this.props
+
+ const { sym, registrar, } = this.state
+
+ try {
+ const acc = registrar.name
+ let retried = 0
+ const retryReq = async () => {
+ let res = await this.doReq(acc, sym)
+ if (res.status === 'err') {
+ if (retried < 3 &&
+ (res.error === 'too_many_requests'
+ || res.error === 'cannot_connect_gateway')) {
+ console.error('Repeating /uia_address', res)
+ ++retried
+ await new Promise(resolve => setTimeout(resolve, 1100))
+ await retryReq()
+ return
+ }
+ throw new APIError(res.error, res.error_data)
+ }
+
+ this.setState({
+ apiLoaded: {
+ address: res.address
+ }
+ })
+ }
+ await retryReq()
+ } catch (err) {
+ console.error('/uia_address', err)
+ if (err instanceof APIError) {
+ this.setState({
+ apiLoaded: {
+ error: err.reason,
+ errData: err.data
+ }
+ })
+ } else {
+ this.setState({
+ apiLoaded: {
+ error: 'error_on_golos_blockchain_side',
+ }
+ })
+ }
+ }
+ }
+
+ balanceValue = () => {
+ const { registrar, } = this.state
+ if (registrar) {
+ return registrar.balance
+ }
+ return '0.000 GOLOS'
+ }
+
+ enoughBalance = () => {
+ return Asset(this.balanceValue()).gte(Asset('0.001 GOLOS'));
+ }
+
+ transfer = async () => {
+ this.setState({
+ transferState: TransferState.transferring,
+ }, () => {
+ this.transferAndWait()
+ })
+ }
+
+ waitingTimeout = (10 + 1) * 60 * 1000
+
+ transferAndWait = async () => {
+ const { sym, rules, registrar } = this.state
+ const { to_transfer, memo_transfer, } = rules
+ let stopper
+ let stopStream = api.streamOperations((err, op) => {
+ if (op[0] === 'transfer' && op[1].from === to_transfer
+ && op[1].to === registrar.name) {
+ stopStream();
+ clearTimeout(stopper);
+ saveAddress(sym, rules.creator, op[1].memo);
+ this.setState({
+ transferState: TransferState.received,
+ receivedTransfer: op[1],
+ });
+ }
+ })
+
+ try {
+ const res = await broadcast.transferAsync(registrar.name, to_transfer, '0.001 GOLOS', memo_transfer)
+ } catch (err) {
+ console.error(err)
+ this.setState({
+ transferState: TransferState.initial,
+ })
+ stopStream()
+ return
+ }
+
+ this.setState({
+ transferState: TransferState.waiting,
+ });
+ stopper = setTimeout(() => {
+ if (stopStream) stopStream();
+ this.setState({
+ transferState: TransferState.timeouted,
+ })
+ }, this.waitingTimeout)
+ }
+
+ _renderTo = (to, to_fixed, username) => {
+ let addr = to || to_fixed;
+ if (username)
+ addr = {addr}
+ return addr ?
+ {tt('uia_register_jsx.to')}
+
+ {addr}
+
+
this.setState({copied_addr: true})}>
+
+
+ {this.state.copied_addr ? : null}
+
+
+
+
: null;
+ }
+
+ _renderParams = () => {
+ const { rules, sym, registrar } = this.state
+ const username = registrar.name
+ const { min_amount, fee, memo_fixed } = rules
+ let details = rules.details
+ if (memo_fixed) {
+ details = details.split('').join(username)
+ }
+ return
+
+ {details &&
+ {details}
+
}
+ {min_amount &&
+ {tt('uia_register_jsx.min_amount')} {min_amount} {sym || ''}
}
+ {fee &&
+ {tt('uia_register_jsx.fee') + ': '}{fee} {sym || ''}
}
+
;
+ }
+
+ _renderApi = () => {
+ const { sym, apiLoaded } = this.state
+ if (!apiLoaded) {
+ return (
+
+
+
+
+
+ );
+ }
+ if (apiLoaded.error) {
+ const { rules } = this.state
+ let { creator, telegram } = rules
+ if (telegram) {
+ telegram = 'https://t.me/' + encodeURIComponent(telegram)
+ telegram =
+
+
+ }
+ return (
+ {tt('uia_register_jsx.api_error') + sym + ':'}
+
+ {creator}{telegram}
+
+ {tt('uia_register_jsx.api_error_details')}
+
+ {apiLoaded.error}
+ {'\n'}
+ {apiLoaded.errData ? JSON.stringify(apiLoaded.errData) : null}
+
+
)
+ }
+ const { address } = apiLoaded
+ return (
+ {this._renderTo(address, null)}
+ {this._renderParams(false)}
+ {this._renderWaiter()}
+
)
+ }
+
+ _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) {
+ onTransfer = (delta) => {
+ this.setState({
+ deposited: delta
+ })
+ }
+ }
+ return
+ }
+
+ render() {
+ let content
+
+ const { loading, error } = this.state
+
+ if (loading) {
+ content =
+ } else {
+ const { assets, sym } = this.state
+
+ const path = this.getPath()[0]
+
+ let syms = []
+ for (const asset of assets) {
+ const meta = getAssetMeta(asset)
+ if (meta.deposit) {
+ const symbol = Asset(asset.supply).symbol
+ syms.push(
+
+
+ {symbol}
+
+ )
+ }
+ }
+
+ let form
+ if (error) {
+ form = {error}
+ } else if (sym) {
+ const { deposited } = this.state
+ if (deposited) {
+ form =
+
+ {!embed ?
+ {tt('asset_edit_deposit_jsx.transfer_title_SYM', {
+ SYM: sym || ' ',
+ })}
+
: null}
+
+ {tt('asset_edit_deposit_jsx.you_received')}
+ {deposited.toString()}. {tt('asset_edit_deposit_jsx.you_received2')}
+
+
+ } else {
+ const { rules, registrar, } = this.state
+ const { to, to_type, to_fixed, to_transfer,
+ 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) {
+ const username = registrar.name
+ memo_fixed = memo_fixed.split('').join(username)
+ }
+ form =
+ {this._renderTo(to, to_fixed)}
+ {memo_fixed ?
+ {tt('uia_register_jsx.memo_fixed')}:
+
+ {memo_fixed}
+
+
this.setState({copied_memo: true})}>
+
+ {this.state.copied_memo ? : null}
+
+
+
+
: null}
+ {this._renderParams()}
+ {this._renderWaiter()}
+
+ }
+ }
+ }
+
+ content =
+ {this.state.sym ?
{tt('uia_register_jsx.register_with')}
:
+ {tt('uia_register_jsx.select_uia')}
}
+ {syms}
+ {this.state.sym &&
}
+ {form}
+
+ }
+
+ return
+
+ {content}
+
+ }
+}
+
+export default withRouterHelpers(UIARegister)
diff --git a/src/modules/register/UIARegister.scss b/src/modules/register/UIARegister.scss
new file mode 100644
index 0000000..cd625ba
--- /dev/null
+++ b/src/modules/register/UIARegister.scss
@@ -0,0 +1,27 @@
+.UIARegister {
+ .uia {
+ display: inline-block;
+ border: 1px solid rgba(128,128,128,0.45);
+ border-radius: 5px;
+ margin-right: 0.5rem;
+ margin-bottom: 0.5rem;
+ padding: 0.5rem;
+ cursor: pointer;
+
+ &.selected {
+ background-color: rgba(208,208,208,0.45);
+ border: 1px solid rgba(128,128,128,0.45);
+ }
+
+ img {
+ width: 28px;
+ height: 28px;
+ margin-right: 0.5rem;
+ }
+ }
+
+ .uia:hover {
+ background-color: rgba(208,208,208,0.45);
+ border: 1px solid rgba(128,128,128,0.45);
+ }
+}
diff --git a/src/pages/api/reg/[...all].js b/src/pages/api/reg/[...all].js
index 61ba419..2447f4c 100644
--- a/src/pages/api/reg/[...all].js
+++ b/src/pages/api/reg/[...all].js
@@ -1,7 +1,8 @@
import config from 'config';
import gmailSend from 'gmail-send';
-import golos from 'golos-lib-js';
+import golos, { api } from 'golos-lib-js'
import { hash, } from 'golos-lib-js/lib/auth/ecc';
+import { Asset } from 'golos-lib-js/lib/utils'
import secureRandom from 'secure-random';
import nextConnect from '@/server/nextConnect';
import { throwErr, } from '@/server/error';
@@ -15,6 +16,8 @@ import Tarantool from '@/server/tarantool';
initGolos();
+global.pollIntervals = {}
+
let handler = nextConnect({ attachParams: true, })
.use(regSessionMiddleware)
.use(passport.initialize())
@@ -304,6 +307,65 @@ let handler = nextConnect({ attachParams: true, })
});
})
+ .get('/api/reg/wait_for_transfer/:sym', async (req, res) => {
+ const { sym } = req.params
+
+ if (global.pollIntervals[sym]) throwErr(req, 400, ['Someone already waits for the transfer'])
+
+ 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]
+ })
+ let bal = balances[0][sym]
+ if (bal) {
+ bal = bal.balance
+ return Asset(bal)
+ } else {
+ const assets = await api.getAssetsAsync('', [sym])
+ if (!assets[0]) throwErr(req, 400, ['No such asset'])
+ bal = Asset(assets[0].supply)
+ bal.amount = 0
+ return bal
+ }
+ }
+
+ const stop = () => {
+ if (global.pollIntervals[sym]) {
+ clearInterval(global.pollIntervals[sym].interval)
+ delete global.pollIntervals[sym]
+ }
+ }
+
+ const initBal = await getBalance()
+ const pollMsec = process.env.NODE_ENV === 'development' ? 1000 : 30000
+ let tries = 0
+ global.pollIntervals[sym] = { created: Date.now(), interval: setInterval(async () => {
+ if (tries > 2) {
+ stop()
+ res.json({
+ status: 'err',
+ error: 'Timeouted'
+ })
+ return
+ }
+ ++tries
+
+ const bal = await getBalance()
+ console.log('wait_for_transfer', initBal.toString(), bal.toString())
+ if (bal.amount > initBal.amount) {
+ stop()
+ const delta = Asset(bal.amount - initBal.amount, bal.precision, bal.symbol)
+ res.json({
+ status: 'ok',
+ delta: delta.toString()
+ })
+ }
+ }, pollMsec) }
+ })
+
handler = addModalRoutes(handler);
export default handler;
diff --git a/src/pages/api/reg/uia_address/[symbol]/[account].js b/src/pages/api/reg/uia_address/[symbol]/[account].js
new file mode 100644
index 0000000..d1b1b5a
--- /dev/null
+++ b/src/pages/api/reg/uia_address/[symbol]/[account].js
@@ -0,0 +1,31 @@
+import nextConnect from '@/server/nextConnect'
+
+import getUIAAddress from '@/server/getUIAAddress'
+
+let handler = nextConnect({ attachParams: true, })
+ .get('/api/reg/uia_address/:symbol/:account', async (req, res) => {
+ const { symbol, account, } = req.params;
+
+ const errResp = (errorName, logData, errorData) => {
+ let logErr = logData
+ if (!Array.isArray(logErr)) {
+ logErr = [logErr]
+ }
+ console.error('/uia_address', errorName, symbol, ...logErr)
+ res.json({
+ status: 'err',
+ error: errorName,
+ symbol,
+ error_data: errorData,
+ })
+ }
+
+ await getUIAAddress(account, symbol, (address) => {
+ res.json({
+ status: 'ok',
+ address,
+ })
+ }, errResp)
+ })
+
+export default handler
diff --git a/src/pages/register/[[...client]].jsx b/src/pages/register/[[...client]].jsx
index 2a0bebe..0f8b07f 100644
--- a/src/pages/register/[[...client]].jsx
+++ b/src/pages/register/[[...client]].jsx
@@ -16,6 +16,7 @@ import VerifyWayTabs from '@/elements/register/VerifyWayTabs'
import Tooltip from '@/elements/Tooltip';
import Header from '@/modules/Header';
import TransferRegister from '@/modules/register/TransferRegister'
+import UIARegister from '@/modules/register/UIARegister'
import { obtainUid, getClientCfg, getDailyLimit, } from '@/server/reg';
import { initRegSession, } from '@/server/regSession';
import { withSecureHeadersSSR, } from '@/server/security';
@@ -92,6 +93,12 @@ class Register extends React.Component {
this.setState({
verificationWay,
})
+ } else if (params.has('uia')) {
+ const verificationWay = 'uia'
+ if (this.state.verificationWay !== verificationWay)
+ this.setState({
+ verificationWay,
+ })
} else {
const verificationWay = 'social'
if (!this.state.verificationWay.startsWith(verificationWay))
@@ -397,6 +404,11 @@ class Register extends React.Component {
}
+ } else if (state.verificationWay === 'uia') {
+ form =
}
form = form || (