diff --git a/server/api/controllers/admin.js b/server/api/controllers/admin.js index 0e07188ec8..979c86f3cd 100644 --- a/server/api/controllers/admin.js +++ b/server/api/controllers/admin.js @@ -1229,6 +1229,71 @@ const putBurn = (req, res) => { }); }; +const performDirectWithdrawalByAdmin = (req, res) => { + const { + user_id, + address, + currency, + amount, + network + } = req.swagger.params.data.value; + + const userId = user_id; + loggerAdmin.verbose( + req.uuid, + 'controller/admin/performDirectWithdrawal auth', + 'address', + address, + 'amount', + amount, + 'currency', + currency, + 'network', + network + ); + + toolsLib.wallet.performDirectWithdrawal( + userId, + address, + currency, + amount, + { + network, + additionalHeaders: { + 'x-forwarded-for': req.headers['x-forwarded-for'] + } + }) + .then((data) => { + toolsLib.user.createAuditLog({ email: req?.auth?.sub?.email, session_id: req?.session_id }, req?.swagger?.apiPath, req?.swagger?.operationPath?.[2], req?.swagger?.params?.data?.value); + loggerAdmin.verbose( + req.uuid, + 'controller/admin/performDirectWithdrawal done', + 'transaction_id', + data.transaction_id, + 'fee', + data.fee, + data + ); + return res.json({ + message: 'Withdrawal request is in the queue and will be processed.', + id: data.id, + transaction_id: data.transaction_id, + amount: data.amount, + currency: data.currency, + fee: data.fee, + fee_coin: data.fee_coin + }); + }) + .catch((err) => { + loggerAdmin.error( + req.uuid, + 'controller/admin/performDirectWithdrawal', + err.message + ); + return res.status(err.statusCode || 400).json({ message: errorMessageConverter(err, req?.auth?.sub?.lang) }); + }); +}; + const postKitUserMeta = (req, res) => { loggerAdmin.verbose(req.uuid, 'controllers/admin/postKitUserMeta', req.auth.sub); @@ -2897,5 +2962,6 @@ module.exports = { updateTransactionLimit, deleteTransactionLimit, getUserBalanceHistoryByAdmin, - createTradeByAdmin + createTradeByAdmin, + performDirectWithdrawalByAdmin }; diff --git a/server/api/controllers/otp.js b/server/api/controllers/otp.js index b95597febb..11b11d5d09 100644 --- a/server/api/controllers/otp.js +++ b/server/api/controllers/otp.js @@ -4,6 +4,21 @@ const { INVALID_OTP_CODE } = require('../../messages'); const { loggerOtp } = require('../../config/logger'); const toolsLib = require('hollaex-tools-lib'); const { errorMessageConverter } = require('../../utils/conversion'); +const { EVENTS_CHANNEL } = require('../../constants'); +const { publisher } = require('../../db/pubsub'); +const { sendEmail } = require('../../mail'); +const { MAILTYPE } = require('../../mail/strings'); + +const sendOtpEmailNotification = async (userId, status, ip, domain) => { + const user = await toolsLib.user.getUserByKitId(userId); + const time = new Date(); + const data = { + ip, + time + } + + sendEmail(status ? MAILTYPE.OTP_ENABLED : MAILTYPE.OTP_DISABLED, user.email, data, user.settings, domain); +}; const requestOtp = (req, res) => { loggerOtp.verbose(req.uuid, 'controllers/otp/requestOtp', req.auth); @@ -30,6 +45,9 @@ const activateOtp = (req, res) => { loggerOtp.verbose(req.uuid, 'controllers/otp/activateOtp', req.auth); const { id } = req.auth.sub; const { code } = req.swagger.params.data.value; + const ip = req.headers['x-real-ip']; + const domain = req.headers['x-real-origin']; + loggerOtp.verbose( req.uuid, 'controllers/otp/activateOtp/code', @@ -53,6 +71,15 @@ const activateOtp = (req, res) => { 'controllers/otp/activateOtp', user.dataValues ); + sendOtpEmailNotification(id, true, ip, domain); + publisher.publish(EVENTS_CHANNEL, JSON.stringify({ + type: 'user', + data: { + action: 'otp_enabled', + user_id: id + } + })); + return res.json({ message: 'OTP enabled' }); }) .catch((err) => { @@ -65,6 +92,9 @@ const deactivateOtp = (req, res) => { loggerOtp.verbose(req.uuid, 'controllers/otp/deactivateOtp', req.auth); const { id } = req.auth.sub; const { code } = req.swagger.params.data.value; + const ip = req.headers['x-real-ip']; + const domain = req.headers['x-real-origin']; + loggerOtp.verbose( req.uuid, 'controllers/otp/deactivateOtp/code', @@ -82,6 +112,15 @@ const deactivateOtp = (req, res) => { return toolsLib.security.updateUserOtpEnabled(id, false); }) .then(() => { + sendOtpEmailNotification(id, false, ip, domain); + publisher.publish(EVENTS_CHANNEL, JSON.stringify({ + type: 'user', + data: { + action: 'otp_disabled', + user_id: id + } + })); + return res.json({ message: 'OTP disabled' }); }) .catch((err) => { diff --git a/server/api/controllers/user.js b/server/api/controllers/user.js index 0cef8e2c37..55c6cdc436 100644 --- a/server/api/controllers/user.js +++ b/server/api/controllers/user.js @@ -413,7 +413,6 @@ const requestResetPassword = (req, res) => { let email = req.swagger.params.email.value; const ip = req.headers['x-real-ip']; const domain = req.headers['x-real-origin']; - const captcha = req.swagger.params.captcha.value; loggerUser.info( req.uuid, @@ -437,7 +436,7 @@ const requestResetPassword = (req, res) => { email = email.toLowerCase(); - toolsLib.security.sendResetPasswordCode(email, captcha, ip, domain) + toolsLib.security.sendResetPasswordCode(email, null, ip, domain) .then(() => { return res.json({ message: `Password request sent to: ${email}` }); }) @@ -1037,6 +1036,19 @@ const addUserBank = (req, res) => { { fields: ['bank_account'] } ); + sendEmail( + MAILTYPE.ALERT, + null, + { + type: 'New bank added by a user', + data: `

User email ${email} just added a new bank.
Details:
${Object.keys(bank_account).map(key => { + return `${key}: ${bank_account[key]}
` + }).join('')}

` + }, + {} + ); + + return res.json(updatedUser.bank_account); }) .catch((err) => { @@ -1244,10 +1256,10 @@ const fetchUserProfitLossInfo = (req, res) => { 'controllers/user/fetchUserProfitLossInfo/auth', req.auth ); - + const { period } = req.swagger.params; const user_id = req.auth.sub.id; - toolsLib.user.fetchUserProfitLossInfo(user_id) + toolsLib.user.fetchUserProfitLossInfo(user_id, { period: period.value || 7 }) .then((data) => { return res.json(data); }) diff --git a/server/api/swagger/admin.yaml b/server/api/swagger/admin.yaml index 9dc04e37d5..743bcdee61 100644 --- a/server/api/swagger/admin.yaml +++ b/server/api/swagger/admin.yaml @@ -3899,4 +3899,61 @@ paths: x-security-scopes: - admin x-token-permissions: - - can_trade \ No newline at end of file + - can_trade + /admin/withdrawal: + x-swagger-router-controller: admin + post: + operationId: performDirectWithdrawalByAdmin + description: Make a withdrawal without email confirmation for admin + tags: + - User + parameters: + - name: data + in: body + required: true + schema: + type: object + required: + - user_id + - address + - amount + - currency + properties: + user_id: + type: number + format: int32 + description: user_id + address: + type: string + maxLength: 256 + description: Destination address + currency: + type: string + maxLength: 256 + description: Currency to be withdrawn (btc, eth, etc) + amount: + type: number + format: double + description: Amount to transfer (in btc, eth, bch) + network: + type: string + description: Blockchain network + minLength: 2 + maxLength: 10 + responses: + 200: + description: Success + schema: + $ref: "#/definitions/TransactionsResponse" + default: + description: Error + schema: + $ref: "#/definitions/MessageResponse" + security: + - Token: [] + x-security-types: + - hmac + x-security-scopes: + - admin + x-token-permissions: + - can_withdraw \ No newline at end of file diff --git a/server/api/swagger/common.yaml b/server/api/swagger/common.yaml index 32395b66a7..401cbdb22a 100644 --- a/server/api/swagger/common.yaml +++ b/server/api/swagger/common.yaml @@ -259,11 +259,6 @@ paths: required: true type: string maxLength: 256 - - name: captcha - in: query - required: false - type: string - maxLength: 5000 responses: 200: description: Success diff --git a/server/api/swagger/definitions.yaml b/server/api/swagger/definitions.yaml index 77dd064321..67a3f80bb1 100644 --- a/server/api/swagger/definitions.yaml +++ b/server/api/swagger/definitions.yaml @@ -23,9 +23,6 @@ definitions: otp_code: type: string maxLength: 256 - captcha: - type: string - maxLength: 5000 referral: type: string maxLength: 256 @@ -1033,7 +1030,7 @@ definitions: maxLength: 256 description: type: string - maxLength: 256 + maxLength: 5000 native_currency_limit: type: boolean PostTierBody: @@ -1056,7 +1053,7 @@ definitions: maxLength: 256 description: type: string - maxLength: 256 + maxLength: 5000 note: type: string maxLength: 256 diff --git a/server/api/swagger/fiat.yaml b/server/api/swagger/fiat.yaml index dabbcc1898..198514fddd 100644 --- a/server/api/swagger/fiat.yaml +++ b/server/api/swagger/fiat.yaml @@ -65,9 +65,6 @@ paths: currency: type: string maxLength: 256 - captcha: - type: string - maxLength: 1024 required: - amount - bank_id diff --git a/server/api/swagger/swagger.js b/server/api/swagger/swagger.js index 7674532d9e..edc1b3929a 100644 --- a/server/api/swagger/swagger.js +++ b/server/api/swagger/swagger.js @@ -4,7 +4,7 @@ const definition = { swagger: '2.0', info: { title: 'HollaEx Kit', - version: '2.10.2' + version: '2.10.3' }, host: 'api.hollaex.com', basePath: '/v2', diff --git a/server/api/swagger/user.yaml b/server/api/swagger/user.yaml index 5a06d24f4f..dee417d7fa 100644 --- a/server/api/swagger/user.yaml +++ b/server/api/swagger/user.yaml @@ -732,6 +732,13 @@ paths: get: description: Get the user p/l info operationId: fetchUserProfitLossInfo + parameters: + - in: query + name: period + description: "Period for fetching pnl data" + required: false + type: string + enum: ['7', '30', '90'] tags: - User responses: diff --git a/server/constants.js b/server/constants.js index 5421185f20..adb3d86c14 100644 --- a/server/constants.js +++ b/server/constants.js @@ -132,6 +132,7 @@ const updateKitInfo = (newInfo) => { const updateKit = (newKitConfig) => { Object.assign(configuration.kit, newKitConfig); + overrideNetworkFields(); }; const updateSecrets = (newSecretsConfig) => { @@ -150,6 +151,17 @@ const updateFrozenUser = (action, userId) => { } }; +const overrideNetworkFields = () => { + for (let coin of Object.values(configuration.coins)) { + if (coin.type === 'fiat') { + configuration.coins[coin.symbol] = { + ...coin, + ...configuration?.kit?.fiat_fees?.[coin.symbol] + } + } + } +}; + exports.GET_COINS = () => cloneDeep(configuration.coins); exports.GET_PAIRS = () => cloneDeep(configuration.pairs); exports.GET_TIERS = () => cloneDeep(configuration.tiers); @@ -370,8 +382,8 @@ exports.DEFAULT_ORDER_RISK_PERCENTAGE = 90; // used in settings in percentage to // SECURITY CONSTANTS START -------------------------------------------------- -exports.TOKEN_TIME_NORMAL = '24h'; -exports.TOKEN_TIME_LONG = '30d'; +exports.TOKEN_TIME_NORMAL = '7d'; +exports.TOKEN_TIME_LONG = '90d'; exports.TOKEN_TYPES = { HMAC: 'hmac' diff --git a/server/db/migrations/20240415123263-add-otp-enable-email-template.js b/server/db/migrations/20240415123263-add-otp-enable-email-template.js new file mode 100644 index 0000000000..db5d2ec71a --- /dev/null +++ b/server/db/migrations/20240415123263-add-otp-enable-email-template.js @@ -0,0 +1,58 @@ +'use strict'; +const TABLE = 'Status'; + +const languages = { + 'en': require('../../mail/strings/en.json').en.OTP_ENABLED, + 'ar': require('../../mail/strings/ar.json').ar.OTP_ENABLED, + 'de': require('../../mail/strings/de.json').de.OTP_ENABLED, + 'es': require('../../mail/strings/es.json').es.OTP_ENABLED, + 'fa': require('../../mail/strings/fa.json').fa.OTP_ENABLED, + 'fr': require('../../mail/strings/fr.json').fr.OTP_ENABLED, + 'id': require('../../mail/strings/id.json').id.OTP_ENABLED, + 'ja': require('../../mail/strings/ja.json').ja.OTP_ENABLED, + 'ko': require('../../mail/strings/ko.json').ko.OTP_ENABLED, + 'pt': require('../../mail/strings/pt.json').pt.OTP_ENABLED, + 'vi': require('../../mail/strings/vi.json').vi.OTP_ENABLED, + 'zh': require('../../mail/strings/zh.json').zh.OTP_ENABLED, +}; +const models = require('../models'); + + +module.exports = { + async up(queryInterface) { + + const statusModel = models[TABLE]; + const status = await statusModel.findOne({}); + + if(!status?.email) return; + const emailTemplates = { + ...status.email, + }; + + let hasTemplate = true; + for (const [language, emailTemplate] of Object.entries(languages)) { + + if (status.email && status.email[language] && !status.email[language].hasOwnProperty('OTP_ENABLED')) { + hasTemplate = false; + emailTemplates[language] = { + ...status.email[language], + OTP_ENABLED: emailTemplate + }; + } + } + + if (!hasTemplate) { + await statusModel.update( + { email: emailTemplates }, + { where: { id: status.id } } + ); + } + + }, + + down: () => { + return new Promise((resolve) => { + resolve(); + }); + } +}; diff --git a/server/db/migrations/20240415123276-add-otp-disable-email-template.js b/server/db/migrations/20240415123276-add-otp-disable-email-template.js new file mode 100644 index 0000000000..ce050e415e --- /dev/null +++ b/server/db/migrations/20240415123276-add-otp-disable-email-template.js @@ -0,0 +1,58 @@ +'use strict'; +const TABLE = 'Status'; + +const languages = { + 'en': require('../../mail/strings/en.json').en.OTP_DISABLED, + 'ar': require('../../mail/strings/ar.json').ar.OTP_DISABLED, + 'de': require('../../mail/strings/de.json').de.OTP_DISABLED, + 'es': require('../../mail/strings/es.json').es.OTP_DISABLED, + 'fa': require('../../mail/strings/fa.json').fa.OTP_DISABLED, + 'fr': require('../../mail/strings/fr.json').fr.OTP_DISABLED, + 'id': require('../../mail/strings/id.json').id.OTP_DISABLED, + 'ja': require('../../mail/strings/ja.json').ja.OTP_DISABLED, + 'ko': require('../../mail/strings/ko.json').ko.OTP_DISABLED, + 'pt': require('../../mail/strings/pt.json').pt.OTP_DISABLED, + 'vi': require('../../mail/strings/vi.json').vi.OTP_DISABLED, + 'zh': require('../../mail/strings/zh.json').zh.OTP_DISABLED, +}; +const models = require('../models'); + + +module.exports = { + async up(queryInterface) { + + const statusModel = models[TABLE]; + const status = await statusModel.findOne({}); + + if(!status?.email) return; + const emailTemplates = { + ...status.email, + }; + + let hasTemplate = true; + for (const [language, emailTemplate] of Object.entries(languages)) { + + if (status.email && status.email[language] && !status.email[language].hasOwnProperty('OTP_DISABLED')) { + hasTemplate = false; + emailTemplates[language] = { + ...status.email[language], + OTP_DISABLED: emailTemplate + }; + } + } + + if (!hasTemplate) { + await statusModel.update( + { email: emailTemplates }, + { where: { id: status.id } } + ); + } + + }, + + down: () => { + return new Promise((resolve) => { + resolve(); + }); + } +}; diff --git a/server/db/migrations/20240428095817-change-tier-description.js b/server/db/migrations/20240428095817-change-tier-description.js new file mode 100644 index 0000000000..da0730bf20 --- /dev/null +++ b/server/db/migrations/20240428095817-change-tier-description.js @@ -0,0 +1,17 @@ +'use strict'; + +const TABLE = 'Tiers'; +const COLUMN = 'description'; + +module.exports = { + up: (queryInterface, Sequelize) => + queryInterface.changeColumn(TABLE, COLUMN, { + type: Sequelize.TEXT, + allowNull: true, + }), + down: (queryInterface, Sequelize) => + queryInterface.changeColumn(TABLE, COLUMN, { + type: Sequelize.STRING, + allowNull: true, + }) +}; \ No newline at end of file diff --git a/server/db/models/tier.js b/server/db/models/tier.js index 38a85ebeb3..7f787fdfd2 100644 --- a/server/db/models/tier.js +++ b/server/db/models/tier.js @@ -18,7 +18,7 @@ module.exports = function (sequelize, DataTypes) { defaultValue: '' }, description: { - type: DataTypes.STRING, + type: DataTypes.TEXT, allowNull: false }, fees: { diff --git a/server/init.js b/server/init.js index 22be2ea226..218d2c2f8d 100644 --- a/server/init.js +++ b/server/init.js @@ -138,7 +138,14 @@ const checkStatus = () => { const exchangePairs = []; for (let coin of exchange.coins) { - configuration.coins[coin.symbol] = coin; + if (coin.type === 'fiat') { + configuration.coins[coin.symbol] = { + ...coin, + ...configuration?.kit?.fiat_fees?.[coin.symbol] + } + } else { + configuration.coins[coin.symbol] = coin; + } } for (let pair of exchange.pairs) { diff --git a/server/mail/index.js b/server/mail/index.js index 64362454ec..957aa776da 100644 --- a/server/mail/index.js +++ b/server/mail/index.js @@ -13,6 +13,9 @@ const SEND_EMAIL_COPY = () => GET_KIT_SECRETS().emails.send_email_to_support; const API_NAME = () => GET_KIT_CONFIG().api_name; const SUPPORT_SOURCE = () => `'${API_NAME()} Support <${SENDER_EMAIL()}>'`; const BCC_ADDRESSES = () => SEND_EMAIL_COPY() ? [AUDIT_EMAIL()] : []; +const SMTP_SERVER = () => GET_KIT_SECRETS().smtp.server; +const SMTP_USER = () => GET_KIT_SECRETS().smtp.user; + const DEFAULT_LANGUAGE = () => { try { return GET_KIT_CONFIG().defaults.language; @@ -77,6 +80,13 @@ const sendEmail = ( to.ToAddresses = [AUDIT_EMAIL()]; break; } + case MAILTYPE.OTP_DISABLED: + case MAILTYPE.OTP_ENABLED:{ + if (data.time) data.time = formatDate(data.time, language); + if (data.ip) data.country = getCountryFromIp(data.ip); + to.BccAddresses = BCC_ADDRESSES(); + break; + } default: return; } @@ -111,13 +121,15 @@ const sendRawEmail = ( }; const send = (params) => { - return sendSMTPEmail(params) - .then((info) => { - return info; - }) - .catch((error) => { - loggerEmail.error('mail/index/sendSTMPEmail', error); - }); + if (SMTP_SERVER() && SMTP_USER() && SMTP_SERVER().length > 1) { + return sendSMTPEmail(params) + .then((info) => { + return info; + }) + .catch((error) => { + loggerEmail.error('mail/index/sendSTMPEmail', error); + }); + } }; const testSendSMTPEmail = (receiver = '', smtp = {}) => { diff --git a/server/mail/strings/ar.json b/server/mail/strings/ar.json index 117c860288..86f24ab71b 100644 --- a/server/mail/strings/ar.json +++ b/server/mail/strings/ar.json @@ -115,6 +115,14 @@ "USER_DELETED": { "html": "

عزيزي $ {name}

طلب حذف الحساب قيد التقدم. ستتم إزالة المعلومات الموجودة في حسابك وفقًا لشروط الخدمة.

يعتبر
${api_name} team

", "title": "طلب حذف الحساب" - } + }, + "OTP_DISABLED": { + "html": "

عزيزي/عزيزتي ${name}

لقد سجلنا أن المصادقة ذات العاملين (2FA) تم تعطيلها على حسابك. يُوصَى بشدة بإعادة تمكين 2FA لتأمين حسابك.

الوقت: ${time}
البلد: ${country}
عنوان IP: ${ip}

إذا كان هذا لم يكن أنت، يرجى الاتصال بنا على الفور.

بكل تقدير
فريق ${api_name}

", + "title": "تم تعطيل 2FA" + }, + "OTP_ENABLED": { + "html": "

عزيزي/عزيزتي ${name}

لقد سجلنا أن المصادقة ذات العاملين (2FA) تم تمكينها على حسابك.

الوقت: ${time}
البلد: ${country}
عنوان IP: ${ip}

بكل تقدير
فريق ${api_name}

", + "title": "تم تمكين 2FA" + } } } \ No newline at end of file diff --git a/server/mail/strings/de.json b/server/mail/strings/de.json index 29f54c981d..6e1f4d08ad 100644 --- a/server/mail/strings/de.json +++ b/server/mail/strings/de.json @@ -115,6 +115,14 @@ "USER_DELETED": { "html": "

Lieber ${name}

Der Antrag auf Kontolöschung wird derzeit bearbeitet. Die Informationen in Ihrem Konto werden gemäß den Nutzungsbedingungen entfernt.

Grüße
${api_name} Team

", "title": "Antrag auf Kontolöschung" - } + }, + "OTP_DISABLED": { + "html": "

Lieber/Liebe ${name}

Wir haben festgestellt, dass Ihre Zwei-Faktor-Authentifizierung (2FA) deaktiviert wurde auf Ihrem Konto. Es wird dringend empfohlen, die 2FA erneut zu aktivieren, um Ihr Konto zu sichern.

Zeit: ${time}
Land: ${country}
IP-Adresse: ${ip}

Wenn dies nicht Sie waren, kontaktieren Sie uns bitte sofort.

Mit freundlichen Grüßen
Team ${api_name}

", + "title": "OTP Deaktiviert" + }, + "OTP_ENABLED": { + "html": "

Lieber/Liebe ${name}

Wir haben festgestellt, dass Ihre Zwei-Faktor-Authentifizierung (2FA) aktiviert wurde auf Ihrem Konto.

Zeit: ${time}
Land: ${country}
IP-Adresse: ${ip}

Mit freundlichen Grüßen
Team ${api_name}

", + "title": "OTP Aktiviert" + } } } \ No newline at end of file diff --git a/server/mail/strings/en.json b/server/mail/strings/en.json index 2d5ff16c18..0e8ecaa424 100644 --- a/server/mail/strings/en.json +++ b/server/mail/strings/en.json @@ -115,6 +115,14 @@ "USER_DELETED": { "html": "

Dear ${name}

The request for account deletion is in progress. The information on your account is going to be removed according to the terms of service.

Regards
${api_name} team

", "title": "Account Deletion Request" + }, + "OTP_DISABLED": { + "html": "

Dear ${name}

We have recorded that your two-factor authentication (2FA) has been disabled on your account. It is highly recommended that you re-enable 2FA to secure your account.

Time: ${time}
Country: ${country}
IP Address: ${ip}

If this was not you please contact us immediately.

Regards
${api_name} team

", + "title": "OTP Disabled" + }, + "OTP_ENABLED": { + "html": "

Dear ${name}

We have recorded that your two-factor authentication (2FA) has been enabled on your account.

Time: ${time}
Country: ${country}
IP Address: ${ip}

Regards
${api_name} team

", + "title": "OTP Enabled" } } } \ No newline at end of file diff --git a/server/mail/strings/es.json b/server/mail/strings/es.json index efe3bcb8ce..476039ce3c 100644 --- a/server/mail/strings/es.json +++ b/server/mail/strings/es.json @@ -115,6 +115,14 @@ "USER_DELETED": { "html": "

Estimado ${name}

La solicitud de eliminación de cuenta está en curso. La información de su cuenta se eliminará de acuerdo con los términos de servicio.

Saludos
equipo de ${api_name}

", "title": "Solicitud de eliminación de cuenta" - } + }, + "OTP_DISABLED": { + "html": "

Estimado/a ${name}

Hemos registrado que se ha desactivado la autenticación de dos factores (2FA) en su cuenta. Se recomienda encarecidamente que vuelva a activar 2FA para asegurar su cuenta.

Hora: ${time}
País: ${country}
Dirección IP: ${ip}

Si no ha sido usted, por favor contáctenos de inmediato.

Saludos
Equipo ${api_name}

", + "title": "OTP Desactivada" + }, + "OTP_ENABLED": { + "html": "

Estimado/a ${name}

Hemos registrado que se ha activado la autenticación de dos factores (2FA) en su cuenta.

Hora: ${time}
País: ${country}
Dirección IP: ${ip}

Saludos
Equipo ${api_name}

", + "title": "OTP Activada" + } } } \ No newline at end of file diff --git a/server/mail/strings/fa.json b/server/mail/strings/fa.json index 66fab39a1d..c35b82651b 100644 --- a/server/mail/strings/fa.json +++ b/server/mail/strings/fa.json @@ -115,6 +115,15 @@ "USER_DELETED": { "html": "

${name} عزیز

درخواست حذف حساب در حال انجام است. اطلاعات حساب شما طبق شرایط خدمات حذف می شود.

با احترام
تیم ${api_name}

", "title": "درخواست حذف اکانت" - } + }, + "OTP_DISABLED": { + "html": "

عزیز ${name}

ما ثبت کرده‌ایم که احراز هویت دو عاملی (2FA) در حساب شما غیرفعال شده است. برای امن کردن حساب خود، توصیه می‌شود که دوباره 2FA را فعال کنید.

زمان: ${time}
کشور: ${country}
آدرس IP: ${ip}

اگر این کار شما نبوده است، لطفاً بلافاصله با ما تماس بگیرید.

با احترام
تیم ${api_name}

", + "title": "احراز هویت دو عاملی غیرفعال شد" + }, + "OTP_ENABLED": { + "html": "

عزیز ${name}

ما ثبت کرده‌ایم که احراز هویت دو عاملی (2FA) در حساب شما فعال شده است.

زمان: ${time}
کشور: ${country}
آدرس IP: ${ip}

با احترام
تیم ${api_name}

", + "title": "احراز هویت دو عاملی فعال شد" + } + } } \ No newline at end of file diff --git a/server/mail/strings/fr.json b/server/mail/strings/fr.json index 280d66501e..6c63b3050e 100644 --- a/server/mail/strings/fr.json +++ b/server/mail/strings/fr.json @@ -115,6 +115,14 @@ "USER_DELETED": { "html": "

Bonjour ${name}

La demande de suppression de compte est en cours. Les informations de votre compte seront supprimées conformément aux conditions de service.

Cordialement
l'équipe de ${api_name}

", "title": "Demande de suppression de compte" - } + }, + "OTP_DISABLED": { + "html": "

Cher/Chère ${name}

Nous avons enregistré que votre authentification à deux facteurs (2FA) a été désactivée sur votre compte. Il est fortement recommandé de réactiver le 2FA pour sécuriser votre compte.

Heure: ${time}
Pays: ${country}
Adresse IP: ${ip}

Si ce n'était pas vous, veuillez nous contacter immédiatement.

Cordialement
Équipe ${api_name}

", + "title": "OTP Désactivé" + }, + "OTP_ENABLED": { + "html": "

Cher/Chère ${name}

Nous avons enregistré que votre authentification à deux facteurs (2FA) a été activée sur votre compte.

Heure: ${time}
Pays: ${country}
Adresse IP: ${ip}

Cordialement
Équipe ${api_name}

", + "title": "OTP Activé" + } } } \ No newline at end of file diff --git a/server/mail/strings/id.json b/server/mail/strings/id.json index 67f0797e4f..ea66450d89 100644 --- a/server/mail/strings/id.json +++ b/server/mail/strings/id.json @@ -115,6 +115,14 @@ "USER_DELETED": { "html": "

Yang terhormat ${name}

Permintaan penghapusan akun sedang berlangsung. Informasi di akun Anda akan dihapus sesuai dengan persyaratan layanan.

Salam
${api_name} tim

", "title": "Permintaan Penghapusan Akun" - } + }, + "OTP_DISABLED": { + "html": "

प्रिय ${name}

हमने देखा है कि आपके खाते पर दो-कारक प्रमाणीकरण (2FA) निष्क्रिय कर दिया गया है। अपने खाते को सुरक्षित बनाने के लिए 2FA को पुनः सक्रिय करना अत्यंत अनिवार्य है।

समय: ${time}
देश: ${country}
IP पता: ${ip}

यदि यह आप नहीं थे, तो कृपया हमसे तुरंत संपर्क करें।

शुभकामनाएं
${api_name} टीम

", + "title": "2FA निष्क्रिय किया गया" + }, + "OTP_ENABLED": { + "html": "

प्रिय ${name}

हमने देखा है कि आपके खाते पर दो-कारक प्रमाणीकरण (2FA) सक्रिय हो गया है

समय: ${time}
देश: ${country}
IP पता: ${ip}

शुभकामनाएं
${api_name} टीम

", + "title": "2FA सक्रिय किया गया" + } } } \ No newline at end of file diff --git a/server/mail/strings/index.js b/server/mail/strings/index.js index 40bd8bc907..684a4456e7 100644 --- a/server/mail/strings/index.js +++ b/server/mail/strings/index.js @@ -49,7 +49,11 @@ const MAILTYPE = { // KYC DOC_REJECTED: 'doc_rejected', - DOC_VERIFIED: 'doc_verified' + DOC_VERIFIED: 'doc_verified', + + // OTP + OTP_DISABLED: 'otp_disabled', + OTP_ENABLED: 'otp_enabled', }; const languageFile = (lang) => { diff --git a/server/mail/strings/ja.json b/server/mail/strings/ja.json index 2ff5ba9e52..806a75f5fb 100644 --- a/server/mail/strings/ja.json +++ b/server/mail/strings/ja.json @@ -115,6 +115,14 @@ "USER_DELETED": { "html": "

${name} 様

アカウント削除のリクエストが進行中です。 あなたのアカウントの情報は利用規約に従って削除されます。

よろしく
${api_name} チーム

", "title": "アカウント削除リクエスト" - } + }, + "OTP_DISABLED": { + "html": "

${name} 様

お客様のアカウントで二段階認証(2FA)が無効になったことを記録しました。 アカウントを保護するために2FAを再度有効にすることを強くお勧めします。

時間: ${time}
国: ${country}
IPアドレス: ${ip}

もしこれがあなたでない場合、すぐにお問い合わせください。

敬具
${api_name} チーム

", + "title": "OTP 無効化" + }, + "OTP_ENABLED": { + "html": "

${name} 様

お客様のアカウントで二段階認証(2FA)が有効になったことを記録しました

時間: ${time}
国: ${country}
IPアドレス: ${ip}

敬具
${api_name} チーム

", + "title": "OTP 有効化" + } } } \ No newline at end of file diff --git a/server/mail/strings/ko.json b/server/mail/strings/ko.json index d7c2c9e724..757c7ab490 100644 --- a/server/mail/strings/ko.json +++ b/server/mail/strings/ko.json @@ -115,6 +115,14 @@ "USER_DELETED": { "html": "

${name}님, 친애하는

계정 삭제 요청이 진행 중입니다. 귀하의 계정 정보는 서비스 약관에 따라 삭제됩니다.

감사합니다.
${api_name} 팀

", "title": "계정 삭제 요청" - } + }, + "OTP_DISABLED": { + "html": "

${name} 님

귀하의 계정에서 이중 인증 (2FA)이 해제되었다는 것을 기록했습니다. 귀하의 계정을 보호하기 위해 2FA를 다시 활성화하는 것이 강력히 권장됩니다.

시간: ${time}
국가: ${country}
IP 주소: ${ip}

만약 귀하가 아니라면 즉시 저희에게 연락해주십시오.

성의를 다하여
${api_name} 팀

", + "title": "OTP 비활성화됨" + }, + "OTP_ENABLED": { + "html": "

${name} 님

귀하의 계정에서 이중 인증 (2FA)이 활성화되었다는 것을 기록했습니다.

시간: ${time}
국가: ${country}
IP 주소: ${ip}

성의를 다하여
${api_name} 팀

", + "title": "OTP 활성화됨" + } } } \ No newline at end of file diff --git a/server/mail/strings/mn.json b/server/mail/strings/mn.json index 36aa45df4e..6d932f1270 100644 --- a/server/mail/strings/mn.json +++ b/server/mail/strings/mn.json @@ -115,6 +115,14 @@ "USER_DELETED": { "html": "

Эрхэм ${name}

Бүртгэлийг устгах хүсэлт хийгдэж байна. Үйлчилгээний нөхцлийн дагуу таны дансны мэдээллийг устгах болно.

Хүндэтгэсэн
${api_name} баг

", "title": "Бүртгэл устгах хүсэлт" + }, + "OTP_DISABLED": { + "html": "

${name}

Таны дансны хоёр чухал хамгаалалт (2FA) идэвхигүй болсон байна гэж бичлэгт орууллаа. Таны дансаа аюулгүй болгохын тулд 2FA-г дахин идэвхжүүлэхийг та нарийвчлан зөвшөөрч байна.

Цаг: ${time}
Улс: ${country}
IP Хаяг: ${ip}

Энэ нь та биш эсэхийг баталгаажуулж байгаа бол тухайлбарыг бидэнтэй холбогдоно уу.

Таны нэртэй
${api_name} баг

", + "title": "OTP Идэвхгүй болсон" + }, + "OTP_ENABLED": { + "html": "

${name}

Таны дансны хоёр чухал хамгаалалт (2FA) идэвхлэгдсэн байна гэж бичлэгт орууллаа.

Цаг: ${time}
Улс: ${country}
IP Хаяг: ${ip}

Таны нэртэй
${api_name} баг

", + "title": "OTP Идэвхлэгдсэн" } } } \ No newline at end of file diff --git a/server/mail/strings/pt.json b/server/mail/strings/pt.json index b546dc043a..ff336d8d26 100644 --- a/server/mail/strings/pt.json +++ b/server/mail/strings/pt.json @@ -115,6 +115,14 @@ "USER_DELETED": { "html": "

Caro ${name}

A solicitação de exclusão da conta está em andamento. As informações da sua conta serão removidas de acordo com os termos de serviço.

Atenciosamente
equipe ${api_name}

", "title": "Solicitação de exclusão de conta" - } + }, + "OTP_DISABLED": { + "html": "

Prezado/a ${name}

Registramos que a sua autenticação de dois fatores (2FA) foi desativada na sua conta. É altamente recomendável que você reative o 2FA para proteger a sua conta.

Horário: ${time}
País: ${country}
Endereço IP: ${ip}

Se isso não foi você, por favor, entre em contato conosco imediatamente.

Atenciosamente
Equipe ${api_name}

", + "title": "OTP Desativado" + }, + "OTP_ENABLED": { + "html": "

Prezado/a ${name}

Registramos que a sua autenticação de dois fatores (2FA) foi ativada na sua conta.

Horário: ${time}
País: ${country}
Endereço IP: ${ip}

Atenciosamente
Equipe ${api_name}

", + "title": "OTP Ativado" + } } } \ No newline at end of file diff --git a/server/mail/strings/tr.json b/server/mail/strings/tr.json index 908edd6cac..ed86a923ed 100644 --- a/server/mail/strings/tr.json +++ b/server/mail/strings/tr.json @@ -115,6 +115,14 @@ "USER_DELETED": { "html": "

Sayın ${name}

Hesap silme talebi devam ediyor. Hesabınızdaki bilgiler hizmet şartlarına göre kaldırılacaktır.

Saygılarımızla
${api_name} ekibi

", "title": "Hesap Silme Talebi" + }, + "OTP_DISABLED": { + "html": "

Sayın ${name}

Hesabınızda iki faktörlü kimlik doğrulama (2FA) devre dışı bırakıldı olarak kaydedilmiştir. Hesabınızı güvence altına almak için lütfen yeniden 2FA etkinleştirin.

Zaman: ${time}
Ülke: ${country}
IP Adresi: ${ip}

Bu siz değilseniz lütfen derhal bizimle iletişime geçin.

Saygılarımla
${api_name} Ekibi

", + "title": "OTP Devre Dışı" + }, + "OTP_ENABLED": { + "html": "

Sayın ${name}

Hesabınızda iki faktörlü kimlik doğrulama (2FA) etkinleştirildi olarak kaydedilmiştir.

Zaman: ${time}
Ülke: ${country}
IP Adresi: ${ip}

Saygılarımla
${api_name} Ekibi

", + "title": "OTP Etkin" } } } diff --git a/server/mail/strings/ur.json b/server/mail/strings/ur.json index 4063ab7ee8..2320d3db74 100644 --- a/server/mail/strings/ur.json +++ b/server/mail/strings/ur.json @@ -115,6 +115,14 @@ "USER_DELETED": { "html": "

پیارے ${name}

لاگ ان کی بہت زیادہ کوششوں کی وجہ سے آپ کا اکاؤنٹ ${login_timeout} منٹ کے لیے مقفل کر دیا گیا ہے۔
اکاؤنٹ حذف کرنے کی درخواست جاری ہے۔ سروس کی شرائط کے مطابق آپ کے اکاؤنٹ پر موجود معلومات کو ہٹا دیا جائے گا۔

حوالے
${api_name} ٹیم

", "title": "اکاؤنٹ حذف کرنے کی درخواست" + }, + "OTP_DISABLED": { + "html": "

عزیز ${name}

ہم نے ریکارڈ کیا ہے کہ آپ کی دو مرحلی تصدیق (2FA) غیر فعال کردی گئی ہے آپ کے اکاؤنٹ پر۔ آپ کے اکاؤنٹ کی حفاظت کے لئے 2FA کو دوبارہ فعال کرنا بہت زیادہ مشورہ دیا گیا ہے۔

وقت: ${time}
ملک: ${country}
IP پتہ: ${ip}

اگر یہ آپ نہیں تھے تو براہ کرم فوراً ہم سے رابطہ کریں۔

خیریت واحترام
${api_name} ٹیم

", + "title": "OTP غیر فعال" + }, + "OTP_ENABLED": { + "html": "

عزیز ${name}

ہم نے ریکارڈ کیا ہے کہ آپ کی دو مرحلی تصدیق (2FA) فعال کردی گئی ہے آپ کے اکاؤنٹ پر۔

وقت: ${time}
ملک: ${country}
IP پتہ: ${ip}

خیریت واحترام
${api_name} ٹیم

", + "title": "OTP فعال" } } } diff --git a/server/mail/strings/vi.json b/server/mail/strings/vi.json index 2c995c52aa..4ea2b84ef2 100644 --- a/server/mail/strings/vi.json +++ b/server/mail/strings/vi.json @@ -115,6 +115,14 @@ "USER_DELETED": { "html": "

Kính gửi ${name}

Yêu cầu xóa tài khoản đang được tiến hành. Thông tin trên tài khoản của bạn sẽ bị xóa theo điều khoản dịch vụ.

Trân trọng
nhóm ${api_name}

", "title": "Yêu cầu xóa tài khoản" - } + }, + "OTP_DISABLED": { + "html": "

Kính gửi ${name}

Chúng tôi đã ghi nhận rằng xác thực hai yếu tố (2FA) đã bị tắt trên tài khoản của bạn. Đề nghị bạn kích hoạt lại 2FA để bảo vệ tài khoản của mình.

Thời gian: ${time}
Quốc gia: ${country}
Địa chỉ IP: ${ip}

Nếu đây không phải là bạn, vui lòng liên hệ với chúng tôi ngay lập tức.

Trân trọng
Đội ${api_name}

", + "title": "OTP Đã Tắt" + }, + "OTP_ENABLED": { + "html": "

Kính gửi ${name}

Chúng tôi đã ghi nhận rằng xác thực hai yếu tố (2FA) đã được kích hoạt trên tài khoản của bạn.

Thời gian: ${time}
Quốc gia: ${country}
Địa chỉ IP: ${ip}

Trân trọng
Đội ${api_name}

", + "title": "OTP Đã Kích Hoạt" + } } } \ No newline at end of file diff --git a/server/mail/strings/zh.json b/server/mail/strings/zh.json index dcdda86345..e541d3f38c 100644 --- a/server/mail/strings/zh.json +++ b/server/mail/strings/zh.json @@ -111,6 +111,14 @@ "USER_DELETED": { "html": "

亲爱的${name}

删除帐户的请求正在进行中。 根据服务条款,您帐户上的信息将被删除。。

此致
${api_name}团队

", "title": "帐户删除请求" - } + }, + "OTP_DISABLED": { + "html": "

尊敬的 ${name} 先生/女士

我们记录到您的账户上的 双因素认证 (2FA) 已经被停用。强烈建议您重新启用 2FA 以保护您的账户。

时间:${time}
国家:${country}
IP 地址:${ip}

如果这不是您本人操作,请立即联系我们。

敬启
${api_name} 团队

", + "title": "OTP 已停用" + }, + "OTP_ENABLED": { + "html": "

尊敬的 ${name} 先生/女士

我们记录到您的账户上的 双因素认证 (2FA) 已经被启用

时间:${time}
国家:${country}
IP 地址:${ip}

敬启
${api_name} 团队

", + "title": "OTP 已启用" + } } } \ No newline at end of file diff --git a/server/mail/templates/index.js b/server/mail/templates/index.js index f40af8097d..ea3eaecc4b 100644 --- a/server/mail/templates/index.js +++ b/server/mail/templates/index.js @@ -420,6 +420,20 @@ const replaceHTMLContent = (type, html = '', email, data, language, domain) => { html = html.replace(/\$\{name\}/g, email || ''); html = html.replace(/\$\{api_name\}/g, API_NAME() || ''); } + else if (type === MAILTYPE.OTP_DISABLED) { + html = html.replace(/\$\{time\}/g, data.time || ''); + html = html.replace(/\$\{country\}/g, data.country || ''); + html = html.replace(/\$\{ip\}/g, data.ip || ''); + html = html.replace(/\$\{name\}/g, email || ''); + html = html.replace(/\$\{api_name\}/g, API_NAME() || ''); + } + else if (type === MAILTYPE.OTP_ENABLED) { + html = html.replace(/\$\{time\}/g, data.time || ''); + html = html.replace(/\$\{country\}/g, data.country || ''); + html = html.replace(/\$\{ip\}/g, data.ip || ''); + html = html.replace(/\$\{name\}/g, email || ''); + html = html.replace(/\$\{api_name\}/g, API_NAME() || ''); + } return html; }; diff --git a/server/mail/utils.js b/server/mail/utils.js index e076626bb4..9bf9f4640f 100644 --- a/server/mail/utils.js +++ b/server/mail/utils.js @@ -80,11 +80,12 @@ const sendSMTPTestEmail = async (params, smtp) => { let transport; if (Object.keys(smtp).length > 0) { transport = nodemailer.createTransport({ - host: smtp.server, - port: smtp.port, + host: smtp.server || SMTP_SERVER(), + port: smtp.port || SMTP_PORT(), + secure: false, auth: { - user: smtp.user, - pass: smtp.password + user: smtp.user || SMTP_USER(), + pass: smtp.password || SMTP_PASSWORD() }, logger: true }); @@ -92,6 +93,7 @@ const sendSMTPTestEmail = async (params, smtp) => { transport = nodemailer.createTransport({ host: SMTP_SERVER(), port: SMTP_PORT(), + secure: false, auth: { user: SMTP_USER(), pass: SMTP_PASSWORD() diff --git a/server/package.json b/server/package.json index a0a6a17170..3ccc48dc95 100644 --- a/server/package.json +++ b/server/package.json @@ -1,5 +1,5 @@ { - "version": "2.10.2", + "version": "2.10.3", "private": false, "description": "HollaEx Kit", "keywords": [ diff --git a/server/tools/hollaex-kit.env.local.example b/server/tools/hollaex-kit.env.local.example index 608e25b8fb..2c04d5f82d 100644 --- a/server/tools/hollaex-kit.env.local.example +++ b/server/tools/hollaex-kit.env.local.example @@ -9,14 +9,12 @@ ACTIVATION_CODE= NATIVE_CURRENCY= -CURRENCIES=xht,usdt,btc,eth,bch,xrp DEFAULT_THEME=dark EMAILS_TIMEZONE=UTC LOGO_IMAGE= NEW_USER_DEFAULT_LANGUAGE=en NEW_USER_IS_ACTIVATED=true NODE_ENV=development -PAIRS=xht-usdt PORT=10010 WEBSOCKET_PORT=10080 @@ -33,12 +31,11 @@ SENDER_EMAIL=support@holla.tech SUPPORT_EMAIL=support@holla.tech VALID_LANGUAGES=en - ADMIN_EMAIL=admin@hollaex.com ADMIN_PASSWORD= -ISSUER=bitholla -SECRET=secret +ISSUER=hollaex +SECRET=secretkey CAPTCHA_SECRET_KEY= CAPTCHA_SITE_KEY= @@ -52,30 +49,11 @@ DB_USERNAME=admin DB_DIALECT=postgres DB_SSL=false -FRESHDESK_HOST= -FRESHDESK_AUTH= -FRESHDESK_KEY= - -ZENDESK_HOST= -ZENDESK_KEY= - -ID_DOCS_BUCKET= -S3_ACCESSKEYID= -S3_SECRETACCESSKEY= -SES_ACCESSKEYID= -SES_REGION= -SES_SECRETACCESSKEY= -SNS_ACCESSKEYID= -SNS_REGION= -SNS_SECRETACCESSKEY= - SMTP_SERVER= SMTP_PORT= SMTP_USER= SMTP_PASSWORD= -PLUGINS= - SEND_EMAIL_TO_SUPPORT= ALLOWED_DOMAINS= diff --git a/server/utils/hollaex-tools-lib/tools/broker.js b/server/utils/hollaex-tools-lib/tools/broker.js index 89d8f0961c..a00e73120e 100644 --- a/server/utils/hollaex-tools-lib/tools/broker.js +++ b/server/utils/hollaex-tools-lib/tools/broker.js @@ -205,7 +205,7 @@ const isFairPriceForBroker = async (broker) => { }; const calculatePrice = async (side, spread, formula, refresh_interval, brokerId, isOracle = false) => { - const regex = /([a-zA-Z]+(?:_[a-zA-Z]+)+(?:-[a-zA-Z]+))/g; + const regex = /([a-zA-Z0-9]+(?:_[a-zA-Z0-9]+)+(?:-[a-zA-Z0-9]+))/g; const variables = formula.match(regex); if (!isArray(variables)) diff --git a/server/utils/hollaex-tools-lib/tools/common.js b/server/utils/hollaex-tools-lib/tools/common.js index ce93594422..8eeed97b7d 100644 --- a/server/utils/hollaex-tools-lib/tools/common.js +++ b/server/utils/hollaex-tools-lib/tools/common.js @@ -297,6 +297,30 @@ const joinKitConfig = (existingKitConfig = {}, newKitConfig = {}) => { if (coin.deposit_fee && !isNumber(coin.deposit_fee)) { throw new Error('deposit fee is not a number'); } + + if (coin.min && coin.min < 0) { + throw new Error('min amount cannot be negative'); + } + + if (coin.min && !isNumber(coin.min)) { + throw new Error('min amount is not a number'); + } + + if (coin.max && coin.max < 0) { + throw new Error('max amount cannot be negative'); + } + + if (coin.max && !isNumber(coin.max)) { + throw new Error('max amount is not a number'); + } + + if (coin.increment_unit && coin.increment_unit < 0) { + throw new Error('increment unit cannot be negative'); + } + + if (coin.increment_unit && !isNumber(coin.increment_unit)) { + throw new Error('increment unit is not a number'); + } } } diff --git a/server/utils/hollaex-tools-lib/tools/security.js b/server/utils/hollaex-tools-lib/tools/security.js index d4aac46ac5..ff2e4f593c 100644 --- a/server/utils/hollaex-tools-lib/tools/security.js +++ b/server/utils/hollaex-tools-lib/tools/security.js @@ -87,34 +87,8 @@ const checkIp = async (remoteip = '') => { }; const checkCaptcha = (captcha = '', remoteip = '') => { - if (!captcha) { - if (NODE_ENV === 'development') { - return resolve(); - } else { - return reject(new Error(INVALID_CAPTCHA)); - } - } else if (!getKitSecrets().captcha || !getKitSecrets().captcha.secret_key) { - return resolve(); - } - - const options = { - method: 'POST', - form: { - secret: getKitSecrets().captcha.secret_key, - response: captcha, - remoteip - }, - uri: CAPTCHA_ENDPOINT - }; - - return rp(options) - .then((response) => JSON.parse(response)) - .then((response) => { - if (!response.success) { - throw new Error(INVALID_CAPTCHA); - } - return; - }); + // Google Recaptcha is deprecated feature from v2.10.3. + return; }; const validatePassword = (userPassword, inputPassword) => { @@ -382,7 +356,8 @@ const checkOtp = (userId) => { */ const generateOtpSecret = () => { const seed = otp({ - name: getKitConfig().api_name + name: getKitConfig().api_name, + keySize: 16 }); return seed.secret; }; @@ -542,9 +517,9 @@ const verifyBearerTokenMiddleware = (req, authOrSecDef, token, cb, isSocket = fa } else if (!has(req.headers, 'api-key') && has(req.headers, 'authorization')) { // Swagger endpoint scopes - const endpointScopes = req.swagger + const endpointScopes = (req.swagger ? req.swagger.operation['x-security-scopes'] - : BASE_SCOPES; + : BASE_SCOPES) || []; let ip = req.headers ? req.headers['x-real-ip'] : undefined; @@ -565,6 +540,10 @@ const verifyBearerTokenMiddleware = (req, authOrSecDef, token, cb, isSocket = fa decodedToken.sub ); + if (req?.path?.includes('/admin') && !endpointScopes?.includes(ROLES.ADMIN)) { + endpointScopes.push(ROLES.ADMIN); + } + // Check set of permissions that are available with the token and set of acceptable permissions set on swagger endpoint if (intersection(decodedToken.scopes, endpointScopes).length === 0) { loggerAuth.error( @@ -817,6 +796,10 @@ const verifyHmacTokenPromise = (apiKey, apiSignature, apiExpires, method, origin } else { return findTokenByApiKey(apiKey) .then((token) => { + if (originalUrl?.includes('/admin') && !scopes?.includes(ROLES.ADMIN)) { + scopes.push(ROLES.ADMIN); + } + if(token.role !== ROLES.ADMIN && scopes.includes(ROLES.ADMIN)) { throw new Error(NOT_AUTHORIZED); } @@ -877,8 +860,8 @@ const createSession = async (token, loginId, userId) => { const userRole = await getUserRole({ kit_id: userId }); - const base64Payload = token.split(".")[1]; - const payloadBuffer = Buffer.from(base64Payload, "base64"); + const base64Payload = token.split('.')[1]; + const payloadBuffer = Buffer.from(base64Payload, 'base64'); const decoded = JSON.parse(payloadBuffer.toString()); const hashedToken = crypto.createHash('md5').update(token).digest('hex'); @@ -890,15 +873,15 @@ const createSession = async (token, loginId, userId) => { status: true, last_seen: new Date(), expiry_date: new Date(decoded.exp * 1000) - }) -} + }); +}; const getExpirationDateInSeconds = (expiryDate) => { const end = moment(expiryDate); const now = moment(new Date()); const duration = moment.duration(moment(end).diff(now)); return Number(duration.asSeconds().toFixed(0)); -} +}; const verifySession = async (token) => { @@ -933,13 +916,13 @@ const verifySession = async (token) => { } return session; -} +}; const findSession = async (token) => { const hashedToken = crypto.createHash('md5').update(token).digest('hex'); - let session = await client.getAsync(hashedToken) + let session = await client.getAsync(hashedToken); if (!session) { loggerAuth.verbose( @@ -971,7 +954,7 @@ const findSession = async (token) => { ); return JSON.parse(session); } -} +}; /** * Function that checks to see if user's scope is valid for the endpoint. diff --git a/server/utils/hollaex-tools-lib/tools/stake.js b/server/utils/hollaex-tools-lib/tools/stake.js index 602af764c4..1bce9695bc 100644 --- a/server/utils/hollaex-tools-lib/tools/stake.js +++ b/server/utils/hollaex-tools-lib/tools/stake.js @@ -2,7 +2,7 @@ const { getModel } = require('./database/model'); const { SERVER_PATH } = require('../constants'); -const { STAKE_SUPPORTED_PLANS } = require(`${SERVER_PATH}/constants`) +const { STAKE_SUPPORTED_PLANS } = require(`${SERVER_PATH}/constants`); const { getUserByKitId, createAuditLog } = require('./user'); const { subscribedToCoin, getKitConfig, getAssetsPrices } = require('./common'); const { transferAssetByKitIds, getUserBalanceByKitId } = require('./wallet'); @@ -16,184 +16,184 @@ const { MAILTYPE } = require('../../../mail/strings'); const { NO_DATA_FOR_CSV, - STAKE_INVALID_STATUS, - STAKE_ONBOARDING_STATUS_ERROR, - STAKE_PERPETUAL_CONDITION_ERROR, - ACCOUNT_ID_NOT_EXIST, - SOURCE_ACCOUNT_INSUFFICIENT_BALANCE, - STAKE_POOL_NOT_FOUND, - TERMINATED_STAKE_POOL_INVALID_ACTION, - INVALID_STAKE_POOL_ACTION, - INVALID_ONBOARDING_ACTION, - INVALID_TERMINATION_ACTION, - FUNDING_ACCOUNT_INSUFFICIENT_BALANCE, - STAKE_POOL_NOT_EXIST, - USER_NOT_FOUND, - STAKE_POOL_ACCEPT_USER_ERROR, - STAKE_POOL_NOT_ACTIVE, - AMOUNT_INSUFFICIENT_ERROR, - STAKE_POOL_MAX_AMOUNT_ERROR, - STAKE_POOL_MIN_AMOUNT_ERROR, - STAKER_NOT_EXIST, - STAKE_POOL_NOT_ACTIVE_FOR_UNSTAKING_ONBOARDING, - STAKE_POOL_NOT_ACTIVE_FOR_UNSTAKING_STATUS, - UNSTAKE_PERIOD_ERROR, - STAKE_UNSUPPORTED_EXCHANGE_PLAN, - REWARD_CURRENCY_CANNOT_BE_SAME, - STAKE_MAX_ACTIVE + STAKE_INVALID_STATUS, + STAKE_ONBOARDING_STATUS_ERROR, + STAKE_PERPETUAL_CONDITION_ERROR, + ACCOUNT_ID_NOT_EXIST, + SOURCE_ACCOUNT_INSUFFICIENT_BALANCE, + STAKE_POOL_NOT_FOUND, + TERMINATED_STAKE_POOL_INVALID_ACTION, + INVALID_STAKE_POOL_ACTION, + INVALID_ONBOARDING_ACTION, + INVALID_TERMINATION_ACTION, + FUNDING_ACCOUNT_INSUFFICIENT_BALANCE, + STAKE_POOL_NOT_EXIST, + USER_NOT_FOUND, + STAKE_POOL_ACCEPT_USER_ERROR, + STAKE_POOL_NOT_ACTIVE, + AMOUNT_INSUFFICIENT_ERROR, + STAKE_POOL_MAX_AMOUNT_ERROR, + STAKE_POOL_MIN_AMOUNT_ERROR, + STAKER_NOT_EXIST, + STAKE_POOL_NOT_ACTIVE_FOR_UNSTAKING_ONBOARDING, + STAKE_POOL_NOT_ACTIVE_FOR_UNSTAKING_STATUS, + UNSTAKE_PERIOD_ERROR, + STAKE_UNSUPPORTED_EXCHANGE_PLAN, + REWARD_CURRENCY_CANNOT_BE_SAME, + STAKE_MAX_ACTIVE } = require(`${SERVER_PATH}/messages`); const calculateSlashAmount = async (staker, stakePool) => { - let slashingPrinciple = new BigNumber(0); - let slashingEarning = new BigNumber(0); + let slashingPrinciple = new BigNumber(0); + let slashingEarning = new BigNumber(0); - let isSlashed = false; + let isSlashed = false; - const unstakedDate = moment(); - const closedDate = staker.closing && moment(staker.closing); + const unstakedDate = moment(); + const closedDate = staker.closing && moment(staker.closing); - // If we unstaked before the closing date, it means we unstaked early, set isSlashed to true in this case. - // If there is no closing date, it means we are in a perpatual stake pool, so no slashing. - if (closedDate && unstakedDate && closedDate > unstakedDate) { - isSlashed = true; - } + // If we unstaked before the closing date, it means we unstaked early, set isSlashed to true in this case. + // If there is no closing date, it means we are in a perpatual stake pool, so no slashing. + if (closedDate && unstakedDate && closedDate > unstakedDate) { + isSlashed = true; + } - if (isSlashed) { + if (isSlashed) { - if (stakePool.slashing_principle_percentage) { - const stakeAmount = new BigNumber(staker.amount); - const slashingPrinciplePercentage = new BigNumber(stakePool.slashing_principle_percentage); - slashingPrinciple = stakeAmount.multipliedBy(slashingPrinciplePercentage).dividedBy(100); - } + if (stakePool.slashing_principle_percentage) { + const stakeAmount = new BigNumber(staker.amount); + const slashingPrinciplePercentage = new BigNumber(stakePool.slashing_principle_percentage); + slashingPrinciple = stakeAmount.multipliedBy(slashingPrinciplePercentage).dividedBy(100); + } - if (stakePool.slashing_earning_percentage) { - const stakerReward = new BigNumber(staker.reward); - const slashingEarningPercentage = new BigNumber(stakePool.slashing_earning_percentage); - slashingEarning = stakerReward.multipliedBy(slashingEarningPercentage).dividedBy(100); - } + if (stakePool.slashing_earning_percentage) { + const stakerReward = new BigNumber(staker.reward); + const slashingEarningPercentage = new BigNumber(stakePool.slashing_earning_percentage); + slashingEarning = stakerReward.multipliedBy(slashingEarningPercentage).dividedBy(100); + } - } + } - return { slashingPrinciple: slashingPrinciple.toNumber(), slashingEarning: slashingEarning.toNumber() }; -} + return { slashingPrinciple: slashingPrinciple.toNumber(), slashingEarning: slashingEarning.toNumber() }; +}; const calculateStakingRewards = (stakers) => { - const rewards = stakers.map(staker => staker.reward).reduce((a, b) => a + b, 0); - const slashes = stakers.map(staker => staker.slashed).reduce((a, b) => a + b, 0); + const rewards = stakers.map(staker => staker.reward).reduce((a, b) => a + b, 0); + const slashes = stakers.map(staker => staker.slashed).reduce((a, b) => a + b, 0); - return (new BigNumber(rewards).minus(new BigNumber(slashes))).toNumber(); -} + return (new BigNumber(rewards).minus(new BigNumber(slashes))).toNumber(); +}; const calculateStakingAmount = (stakers) => { - const totalAmount = stakers.map(staker => staker.amount).reduce((a, b) => a + b, 0); - return totalAmount; -} + const totalAmount = stakers.map(staker => staker.amount).reduce((a, b) => a + b, 0); + return totalAmount; +}; const distributeStakingRewards = async (stakers, account_id, currency, reward_currency, admin_id) => { - for (const staker of stakers) { + for (const staker of stakers) { - await staker.update({ status: 'closed' }, { + await staker.update({ status: 'closed' }, { fields: ['status'] - }); + }); - const amountAfterSlash = new BigNumber(staker.reward).minus(new BigNumber(staker.slashed)).toNumber(); - let totalAmount = staker.amount; + const amountAfterSlash = new BigNumber(staker.reward).minus(new BigNumber(staker.slashed)).toNumber(); + let totalAmount = staker.amount; - //Add them together since they are of same currency. - if (reward_currency === currency) { - totalAmount = (new BigNumber(staker.amount).plus(amountAfterSlash)).toNumber(); - } + //Add them together since they are of same currency. + if (reward_currency === currency) { + totalAmount = (new BigNumber(staker.amount).plus(amountAfterSlash)).toNumber(); + } - const user = await getUserByKitId(staker.user_id); - - try { - if(totalAmount > 0) { - await transferAssetByKitIds(account_id, user.id, currency, totalAmount, 'Admin transfer stake', false, { category: 'stake' }); - } - if (reward_currency !== currency && amountAfterSlash > 0) { - await transferAssetByKitIds(account_id, user.id, reward_currency, amountAfterSlash, 'Admin transfer stake', false, { category: 'stake' }); - } - - } catch (error) { - const adminAccount = await getUserByKitId(admin_id); - sendEmail( - MAILTYPE.ALERT, - adminAccount.email, - { - type: 'Error! Unstaking failed for an exchange user', - data: `Unstaking failed while transfering funds for user id ${user.id} Error message: ${error.message}` - }, - adminAccount.settings - ); - } - } -} + const user = await getUserByKitId(staker.user_id); + + try { + if(totalAmount > 0) { + await transferAssetByKitIds(account_id, user.id, currency, totalAmount, 'Admin transfer stake', false, { category: 'stake' }); + } + if (reward_currency !== currency && amountAfterSlash > 0) { + await transferAssetByKitIds(account_id, user.id, reward_currency, amountAfterSlash, 'Admin transfer stake', false, { category: 'stake' }); + } + + } catch (error) { + const adminAccount = await getUserByKitId(admin_id); + sendEmail( + MAILTYPE.ALERT, + adminAccount.email, + { + type: 'Error! Unstaking failed for an exchange user', + data: `Unstaking failed while transfering funds for user id ${user.id} Error message: ${error.message}` + }, + adminAccount.settings + ); + } + } +}; const getSourceAccountBalance = async (account_id, coin) => { - const balance = await getUserBalanceByKitId(account_id); - let symbols = {}; - - for (const key of Object.keys(balance)) { - if (key.includes('available') && balance[key]) { - let symbol = key?.split('_')?.[0]; - symbols[symbol] = balance[key]; - } - } + const balance = await getUserBalanceByKitId(account_id); + let symbols = {}; + + for (const key of Object.keys(balance)) { + if (key.includes('available') && balance[key]) { + let symbol = key?.split('_')?.[0]; + symbols[symbol] = balance[key]; + } + } - return symbols[coin]; -} + return symbols[coin]; +}; const fetchStakers = async (stakePoolId) => { - return getModel('staker').findAll({ where: { stake_id: stakePoolId } }); -} + return getModel('staker').findAll({ where: { stake_id: stakePoolId } }); +}; const validateExchangeStake = (stake) => { - if (new BigNumber(stake.min_amount).comparedTo(0) !== 1) { + if (new BigNumber(stake.min_amount).comparedTo(0) !== 1) { throw new Error('Stake minimum amount must be bigger than zero.'); } - if (new BigNumber(stake.max_amount).comparedTo(0) !== 1) { + if (new BigNumber(stake.max_amount).comparedTo(0) !== 1) { throw new Error('Stake maximum amount must be bigger than zero.'); } - if (new BigNumber(stake.max_amount).comparedTo(new BigNumber(stake.min_amount)) !== 1) { + if (new BigNumber(stake.max_amount).comparedTo(new BigNumber(stake.min_amount)) !== 1) { throw new Error('Stake minimum amount cannot be bigger than maximum amount'); } - if (new BigNumber(stake.apy).comparedTo(0) !== 1) { + if (new BigNumber(stake.apy).comparedTo(0) !== 1) { throw new Error('Stake apy must be bigger than zero.'); } - if (stake.duration !== null && new BigNumber(stake.duration).comparedTo(0) !== 1) { + if (stake.duration !== null && new BigNumber(stake.duration).comparedTo(0) !== 1) { throw new Error('Stake duration must be bigger than zero.'); } - if (stake.slashing_principle_percentage && new BigNumber(stake.slashing_principle_percentage).comparedTo(100) === 1) { + if (stake.slashing_principle_percentage && new BigNumber(stake.slashing_principle_percentage).comparedTo(100) === 1) { throw new Error('Stake slash principle cannot be bigger than 100.'); } - if (stake.slashing_earning_percentage && new BigNumber(stake.slashing_earning_percentage).comparedTo(100) === 1) { + if (stake.slashing_earning_percentage && new BigNumber(stake.slashing_earning_percentage).comparedTo(100) === 1) { throw new Error('Stake slash earnings cannot be bigger than 100.'); } - if (stake.slashing_principle_percentage && new BigNumber(stake.slashing_principle_percentage).comparedTo(0) !== 1) { + if (stake.slashing_principle_percentage && new BigNumber(stake.slashing_principle_percentage).comparedTo(0) !== 1) { throw new Error('Stake slash principle cannot be smaller than 0.'); } - if (stake.slashing_earning_percentage && new BigNumber(stake.slashing_earning_percentage).comparedTo(0) !== 1) { + if (stake.slashing_earning_percentage && new BigNumber(stake.slashing_earning_percentage).comparedTo(0) !== 1) { throw new Error('Stake slash earnings cannot be smaller than 0.'); } -} +}; const getExchangeStakePools = async (opts = { - limit: null, - page: null, - order_by: null, - order: null, - start_date: null, - end_date: null, - format: null + limit: null, + page: null, + order_by: null, + order: null, + start_date: null, + end_date: null, + format: null }) => { - const pagination = paginationQuery(opts.limit, opts.page); + const pagination = paginationQuery(opts.limit, opts.page); const ordering = orderingQuery(opts.order_by, opts.order); const timeframe = timeframeQuery(opts.start_date, opts.end_date); @@ -203,7 +203,7 @@ const getExchangeStakePools = async (opts = { }, order: [ordering], ...(!opts.format && pagination), - } + }; if (opts.format) { @@ -220,100 +220,100 @@ const getExchangeStakePools = async (opts = { } }); } else { - return dbQuery.findAndCountAllWithRows('stake', query) - .then(async (stakePools) => { + return dbQuery.findAndCountAllWithRows('stake', query) + .then(async (stakePools) => { - //Calculate reward amount per stake pool - for (const stakePool of stakePools.data) { - const stakers = await fetchStakers(stakePool.id); - stakePool.reward = calculateStakingRewards(stakers); - } + //Calculate reward amount per stake pool + for (const stakePool of stakePools.data) { + const stakers = await fetchStakers(stakePool.id); + stakePool.reward = calculateStakingRewards(stakers); + } - return stakePools; - }) + return stakePools; + }); } }; const createExchangeStakePool = async (stake) => { validateExchangeStake(stake); - const { - currency, - reward_currency, - account_id, - duration, - slashing, - early_unstake, - max_amount, - status, - onboarding, - } = stake; + const { + currency, + reward_currency, + account_id, + duration, + slashing, + early_unstake, + max_amount, + status, + onboarding, + } = stake; - if (status !== 'uninitialized') { - throw new Error(STAKE_INVALID_STATUS); - } + if (status !== 'uninitialized') { + throw new Error(STAKE_INVALID_STATUS); + } - if (onboarding) { - throw new Error(STAKE_ONBOARDING_STATUS_ERROR); - } + if (onboarding) { + throw new Error(STAKE_ONBOARDING_STATUS_ERROR); + } - if (duration === null && (early_unstake || slashing)) { - throw new Error(STAKE_PERPETUAL_CONDITION_ERROR); - } + if (duration === null && (early_unstake || slashing)) { + throw new Error(STAKE_PERPETUAL_CONDITION_ERROR); + } - if (!subscribedToCoin(currency)) { - throw new Error('Invalid coin ' + currency); - } + if (!subscribedToCoin(currency)) { + throw new Error('Invalid coin ' + currency); + } - if (reward_currency && !subscribedToCoin(reward_currency)) { - throw new Error('Invalid coin ' + reward_currency); - } + if (reward_currency && !subscribedToCoin(reward_currency)) { + throw new Error('Invalid coin ' + reward_currency); + } - if (reward_currency !== currency) { - const conversions = await getAssetsPrices([currency], reward_currency, 1); - if (conversions[currency] === -1) { - throw new Error(NO_ORACLE_PRICE_FOUND) - } - } + if (reward_currency !== currency) { + const conversions = await getAssetsPrices([currency], reward_currency, 1); + if (conversions[currency] === -1) { + throw new Error(NO_ORACLE_PRICE_FOUND); + } + } - const exchangeInfo = getKitConfig().info; + const exchangeInfo = getKitConfig().info; - if(!STAKE_SUPPORTED_PLANS.includes(exchangeInfo.plan)) { - throw new Error(STAKE_UNSUPPORTED_EXCHANGE_PLAN); - } + if(!STAKE_SUPPORTED_PLANS.includes(exchangeInfo.plan)) { + throw new Error(STAKE_UNSUPPORTED_EXCHANGE_PLAN); + } - const accountOwner = await getUserByKitId(account_id); + const accountOwner = await getUserByKitId(account_id); - if (!accountOwner) { - throw new Error(ACCOUNT_ID_NOT_EXIST); - } + if (!accountOwner) { + throw new Error(ACCOUNT_ID_NOT_EXIST); + } - stake.slashing = (stake.slashing_principle_percentage || stake.slashing_earning_percentage) ? true : false; + stake.slashing = (stake.slashing_principle_percentage || stake.slashing_earning_percentage) ? true : false; - if (!reward_currency) { - stake.reward_currency = currency; - } + if (!reward_currency) { + stake.reward_currency = currency; + } return getModel('stake').create(stake, { fields: [ 'name', - 'user_id', - 'currency', - 'reward_currency', - 'account_id', - 'apy', - 'duration', - 'slashing', - 'slashing_earning_percentage', - 'slashing_principle_percentage', - 'early_unstake', - 'min_amount', - 'max_amount', - 'status', - 'onboarding', - 'disclaimer', - 'paused_date' + 'user_id', + 'currency', + 'reward_currency', + 'account_id', + 'apy', + 'duration', + 'slashing', + 'slashing_earning_percentage', + 'slashing_principle_percentage', + 'early_unstake', + 'min_amount', + 'max_amount', + 'status', + 'onboarding', + 'disclaimer', + 'paused_date' ] }); }; @@ -324,26 +324,26 @@ const updateExchangeStakePool = async (id, data, auditInfo) => { throw new Error(STAKE_POOL_NOT_FOUND); } - const { - currency, - reward_currency, - name, - account_id, - duration, - slashing, - early_unstake, - slashing_principle_percentage, - slashing_earning_percentage, - status, - onboarding, - } = data; + const { + currency, + reward_currency, + name, + account_id, + duration, + slashing, + early_unstake, + slashing_principle_percentage, + slashing_earning_percentage, + status, + onboarding, + } = data; - if (stakePool.status === 'terminated') { - throw new Error(TERMINATED_STAKE_POOL_INVALID_ACTION); - } + if (stakePool.status === 'terminated') { + throw new Error(TERMINATED_STAKE_POOL_INVALID_ACTION); + } - if(status !== 'uninitialized' && ( - (currency && currency !== stakePool.currency) + if(status !== 'uninitialized' && ( + (currency && currency !== stakePool.currency) || (name && name !== stakePool.name) || (reward_currency && reward_currency !== stakePool.reward_currency) || (account_id && account_id !== stakePool.account_id) @@ -352,139 +352,139 @@ const updateExchangeStakePool = async (id, data, auditInfo) => { || (early_unstake && early_unstake !== stakePool.early_unstake) || (slashing_principle_percentage && slashing_principle_percentage !== stakePool.slashing_principle_percentage) || (slashing_earning_percentage && slashing_earning_percentage !== stakePool.slashing_earning_percentage) - )) { - throw new Error(INVALID_STAKE_POOL_ACTION); - } + )) { + throw new Error(INVALID_STAKE_POOL_ACTION); + } - if (onboarding && stakePool.status === 'uninitialized') { - throw new Error(INVALID_ONBOARDING_ACTION); - } + if (onboarding && stakePool.status === 'uninitialized') { + throw new Error(INVALID_ONBOARDING_ACTION); + } - if (status === 'terminated' && stakePool.status !== 'paused') { - throw new Error(INVALID_TERMINATION_ACTION); - } + if (status === 'terminated' && stakePool.status !== 'paused') { + throw new Error(INVALID_TERMINATION_ACTION); + } - if (status === 'paused') { - data.paused_date = new Date(); - } + if (status === 'paused') { + data.paused_date = new Date(); + } - if (status === 'terminated') { - const balance = await getSourceAccountBalance(stakePool.account_id, stakePool.currency); + if (status === 'terminated') { + const balance = await getSourceAccountBalance(stakePool.account_id, stakePool.currency); - const stakers = await getModel('staker').findAll({ where: { stake_id: stakePool.id, status: { [Op.or]: ['staking', 'unstaking'] } } }); - const reward = calculateStakingRewards(stakers); - let totalAmount = calculateStakingAmount(stakers) + const stakers = await getModel('staker').findAll({ where: { stake_id: stakePool.id, status: { [Op.or]: ['staking', 'unstaking'] } } }); + const reward = calculateStakingRewards(stakers); + let totalAmount = calculateStakingAmount(stakers); - if (stakePool.reward_currency === stakePool.currency) { - totalAmount = new BigNumber(totalAmount).plus(new BigNumber(reward)).toNumber(); - } + if (stakePool.reward_currency === stakePool.currency) { + totalAmount = new BigNumber(totalAmount).plus(new BigNumber(reward)).toNumber(); + } - if(new BigNumber(balance).comparedTo(totalAmount) !== 1) { - throw new Error(FUNDING_ACCOUNT_INSUFFICIENT_BALANCE); - } + if(new BigNumber(balance).comparedTo(totalAmount) !== 1) { + throw new Error(FUNDING_ACCOUNT_INSUFFICIENT_BALANCE); + } - if(stakePool.reward_currency !== stakePool.currency) { - const reward_balance = await getSourceAccountBalance(stakePool.account_id, stakePool.reward_currency); + if(stakePool.reward_currency !== stakePool.currency) { + const reward_balance = await getSourceAccountBalance(stakePool.account_id, stakePool.reward_currency); - if(new BigNumber(reward_balance).comparedTo(reward) !== 1) { - throw new Error(FUNDING_ACCOUNT_INSUFFICIENT_BALANCE); - } - } + if(new BigNumber(reward_balance).comparedTo(reward) !== 1) { + throw new Error(FUNDING_ACCOUNT_INSUFFICIENT_BALANCE); + } + } - await distributeStakingRewards(stakers, stakePool.account_id, stakePool.currency, stakePool.reward_currency, stakePool.user_id); + await distributeStakingRewards(stakers, stakePool.account_id, stakePool.currency, stakePool.reward_currency, stakePool.user_id); - } + } - if (currency && !subscribedToCoin(currency)) { - throw new Error('Invalid coin ' + currency); - } + if (currency && !subscribedToCoin(currency)) { + throw new Error('Invalid coin ' + currency); + } - if (reward_currency && !subscribedToCoin(reward_currency)) { - throw new Error('Invalid coin ' + reward_currency); - } + if (reward_currency && !subscribedToCoin(reward_currency)) { + throw new Error('Invalid coin ' + reward_currency); + } - if (reward_currency) { - const conversions = await getAssetsPrices([currency], reward_currency, 1); - if (conversions[currency] === -1) { - throw new Error(NO_ORACLE_PRICE_FOUND) - } - } + if (reward_currency) { + const conversions = await getAssetsPrices([currency], reward_currency, 1); + if (conversions[currency] === -1) { + throw new Error(NO_ORACLE_PRICE_FOUND); + } + } - if (account_id) { - const accountOwner = await getUserByKitId(account_id); + if (account_id) { + const accountOwner = await getUserByKitId(account_id); - if (!accountOwner) { - throw new Error(ACCOUNT_ID_NOT_EXIST); - } + if (!accountOwner) { + throw new Error(ACCOUNT_ID_NOT_EXIST); + } - } + } - const updatedStakePool = { + const updatedStakePool = { ...stakePool.get({ plain: true }), ...Object.fromEntries(Object.entries(data).filter(([_, v]) => v !== undefined)) }; - updatedStakePool.slashing = (updatedStakePool.slashing_principle_percentage || updatedStakePool.slashing_earning_percentage) ? true : false; + updatedStakePool.slashing = (updatedStakePool.slashing_principle_percentage || updatedStakePool.slashing_earning_percentage) ? true : false; validateExchangeStake(updatedStakePool); - createAuditLog({ email: auditInfo.userEmail, session_id: auditInfo.sessionId }, auditInfo.apiPath, auditInfo.method, updatedStakePool, stakePool.dataValues); + createAuditLog({ email: auditInfo.userEmail, session_id: auditInfo.sessionId }, auditInfo.apiPath, auditInfo.method, updatedStakePool, stakePool.dataValues); return stakePool.update(updatedStakePool, { fields: [ 'name', - 'currency', - 'reward_currency', - 'account_id', - 'apy', - 'duration', - 'slashing', - 'slashing_earning_percentage', - 'slashing_principle_percentage', - 'early_unstake', - 'min_amount', - 'max_amount', - 'status', - 'onboarding', - 'disclaimer', - 'paused_date' + 'currency', + 'reward_currency', + 'account_id', + 'apy', + 'duration', + 'slashing', + 'slashing_earning_percentage', + 'slashing_principle_percentage', + 'early_unstake', + 'min_amount', + 'max_amount', + 'status', + 'onboarding', + 'disclaimer', + 'paused_date' ] }); -} +}; const getExchangeStakers = async ( - opts = { - user_id: null, - limit: null, - page: null, - order_by: null, - order: null, - start_date: null, - end_date: null, - format: null -}) => { - - const pagination = paginationQuery(opts.limit, opts.page); + opts = { + user_id: null, + limit: null, + page: null, + order_by: null, + order: null, + start_date: null, + end_date: null, + format: null + }) => { + + const pagination = paginationQuery(opts.limit, opts.page); const ordering = orderingQuery(opts.order_by, opts.order); const timeframe = timeframeQuery(opts.start_date, opts.end_date); const query = { where: { - created_at: timeframe, - ...(opts.user_id && { user_id: opts.user_id }) + created_at: timeframe, + ...(opts.user_id && { user_id: opts.user_id }) }, - order: [ordering], - include: [ + order: [ordering], + include: [ { model: getModel('stake'), as: 'stake', } ], ...(!opts.format && pagination), - } + }; @@ -503,200 +503,200 @@ const getExchangeStakers = async ( }); } else { return dbQuery.findAndCountAllWithRows('staker', query); - } + } -} +}; const createExchangeStaker = async (stake_id, amount, user_id) => { - const stakePool = await getModel('stake').findOne({ where: { id: stake_id } }); + const stakePool = await getModel('stake').findOne({ where: { id: stake_id } }); - if (!stakePool) { - throw new Error(STAKE_POOL_NOT_EXIST); - } - const user = await getUserByKitId(user_id); + if (!stakePool) { + throw new Error(STAKE_POOL_NOT_EXIST); + } + const user = await getUserByKitId(user_id); - if (!user) { - throw new Error(USER_NOT_FOUND); - } + if (!user) { + throw new Error(USER_NOT_FOUND); + } - if (!stakePool.onboarding) { - throw new Error(STAKE_POOL_ACCEPT_USER_ERROR); - } + if (!stakePool.onboarding) { + throw new Error(STAKE_POOL_ACCEPT_USER_ERROR); + } - if (stakePool.status !== 'active') { - throw new Error(STAKE_POOL_NOT_ACTIVE); - } + if (stakePool.status !== 'active') { + throw new Error(STAKE_POOL_NOT_ACTIVE); + } - const userBalance = await getSourceAccountBalance(user_id, stakePool.currency); + const userBalance = await getSourceAccountBalance(user_id, stakePool.currency); - if (new BigNumber(userBalance).comparedTo(new BigNumber(amount)) !== 1) { - throw new Error(AMOUNT_INSUFFICIENT_ERROR); - } + if (new BigNumber(userBalance).comparedTo(new BigNumber(amount)) === -1) { + throw new Error(AMOUNT_INSUFFICIENT_ERROR); + } - if (new BigNumber(amount).comparedTo(new BigNumber(stakePool.max_amount)) === 1) { - throw new Error(STAKE_POOL_MAX_AMOUNT_ERROR); - } + if (new BigNumber(amount).comparedTo(new BigNumber(stakePool.max_amount)) === 1) { + throw new Error(STAKE_POOL_MAX_AMOUNT_ERROR); + } - if (new BigNumber(amount).comparedTo(new BigNumber(stakePool.min_amount)) === -1) { - throw new Error(STAKE_POOL_MIN_AMOUNT_ERROR); - } + if (new BigNumber(amount).comparedTo(new BigNumber(stakePool.min_amount)) === -1) { + throw new Error(STAKE_POOL_MIN_AMOUNT_ERROR); + } - const stakers = await getModel('staker').findAll({ where: { user_id, status: 'staking' } }); + const stakers = await getModel('staker').findAll({ where: { user_id, status: 'staking' } }); - if (stakers.length >= 12) { - throw new Error(STAKE_MAX_ACTIVE); - } + if (stakers.length >= 12) { + throw new Error(STAKE_MAX_ACTIVE); + } - const staker = { - user_id, - stake_id, - amount, - currency: stakePool.currency, - reward_currency: stakePool.reward_currency || stakePool.currency, - status: 'staking', - ...(stakePool.duration && { closing: moment().add(stakePool.duration, 'days') }) - } + const staker = { + user_id, + stake_id, + amount, + currency: stakePool.currency, + reward_currency: stakePool.reward_currency || stakePool.currency, + status: 'staking', + ...(stakePool.duration && { closing: moment().add(stakePool.duration, 'days') }) + }; - await transferAssetByKitIds(user_id, stakePool.account_id, stakePool.currency, amount, 'User transfer stake', false, { category: 'stake' }); + await transferAssetByKitIds(user_id, stakePool.account_id, stakePool.currency, amount, 'User transfer stake', false, { category: 'stake' }); - const stakerData = await getModel('staker').create(staker, { + const stakerData = await getModel('staker').create(staker, { fields: [ 'user_id', - 'stake_id', - 'amount', - 'currency', - 'reward_currency', - 'status', - 'closing', - 'unstaked_date' + 'stake_id', + 'amount', + 'currency', + 'reward_currency', + 'status', + 'closing', + 'unstaked_date' ] - }); + }); - return stakerData; -} + return stakerData; +}; const deleteExchangeStaker = async (staker_id, user_id) => { - const user = await getUserByKitId(user_id); + const user = await getUserByKitId(user_id); - if (!user) { - throw new Error(USER_NOT_FOUND); - } + if (!user) { + throw new Error(USER_NOT_FOUND); + } - const staker = await getModel('staker').findOne({ where: { id: staker_id, user_id, status: 'staking' } }); + const staker = await getModel('staker').findOne({ where: { id: staker_id, user_id, status: 'staking' } }); - if (!staker) { - throw new Error(STAKER_NOT_EXIST); - } + if (!staker) { + throw new Error(STAKER_NOT_EXIST); + } - const stakePool = await getModel('stake').findOne({ where: { id: staker.stake_id } }); + const stakePool = await getModel('stake').findOne({ where: { id: staker.stake_id } }); - if (!stakePool) { - throw new Error(STAKE_POOL_NOT_EXIST); - } + if (!stakePool) { + throw new Error(STAKE_POOL_NOT_EXIST); + } - if (!stakePool.onboarding) { - throw new Error(STAKE_POOL_NOT_ACTIVE_FOR_UNSTAKING_ONBOARDING); - } + if (!stakePool.onboarding) { + throw new Error(STAKE_POOL_NOT_ACTIVE_FOR_UNSTAKING_ONBOARDING); + } - if (stakePool.status !== 'active') { - throw new Error(STAKE_POOL_NOT_ACTIVE_FOR_UNSTAKING_STATUS); - } + if (stakePool.status !== 'active') { + throw new Error(STAKE_POOL_NOT_ACTIVE_FOR_UNSTAKING_STATUS); + } - // check if matured for unstaking or not - const now = moment(); - const startingDate = moment(staker.created_at); + // check if matured for unstaking or not + const now = moment(); + const startingDate = moment(staker.created_at); const stakinDays = now.diff(startingDate, 'days'); - const remaininDays = (stakePool?.duration || 0) - stakinDays; + const remaininDays = (stakePool?.duration || 0) - stakinDays; - if (!stakePool.early_unstake && stakePool.duration && remaininDays > 0) { - throw new Error(UNSTAKE_PERIOD_ERROR); - } - - const slashedValues = await calculateSlashAmount(staker, stakePool); - const updatedStaker = { - ...staker, - status: 'unstaking', - amount: new BigNumber(staker.amount).minus(new BigNumber(slashedValues.slashingPrinciple)).toNumber(), - slashed: slashedValues.slashingEarning, - unstaked_date: new Date() - } - return staker.update(updatedStaker, { + if (!stakePool.early_unstake && stakePool.duration && remaininDays > 0) { + throw new Error(UNSTAKE_PERIOD_ERROR); + } + + const slashedValues = await calculateSlashAmount(staker, stakePool); + const updatedStaker = { + ...staker, + status: 'unstaking', + amount: new BigNumber(staker.amount).minus(new BigNumber(slashedValues.slashingPrinciple)).toNumber(), + slashed: slashedValues.slashingEarning, + unstaked_date: new Date() + }; + return staker.update(updatedStaker, { fields: [ - 'status', - 'amount', - 'reward', - 'slashed', - 'unstaked_date' + 'status', + 'amount', + 'reward', + 'slashed', + 'unstaked_date' ] }); -} +}; const unstakeEstimateSlash = async (staker_id) => { - const staker = await getModel('staker').findOne({ where: { id: staker_id } }); + const staker = await getModel('staker').findOne({ where: { id: staker_id } }); - if (!staker) { - throw new Error(STAKER_NOT_EXIST); - } + if (!staker) { + throw new Error(STAKER_NOT_EXIST); + } - const stakePool = await getModel('stake').findOne({ where: { id: staker.stake_id } }); + const stakePool = await getModel('stake').findOne({ where: { id: staker.stake_id } }); - if (!stakePool) { - throw new Error(STAKE_POOL_NOT_EXIST); - } + if (!stakePool) { + throw new Error(STAKE_POOL_NOT_EXIST); + } - const slashedAmount = await calculateSlashAmount(staker, stakePool); + const slashedAmount = await calculateSlashAmount(staker, stakePool); - return slashedAmount; -} + return slashedAmount; +}; const unstakeEstimateSlashAdmin = async (id) => { - const stakePool = await getModel('stake').findOne({ where: { id } }); + const stakePool = await getModel('stake').findOne({ where: { id } }); - if (!stakePool) { - throw new Error(STAKE_POOL_NOT_EXIST); - } + if (!stakePool) { + throw new Error(STAKE_POOL_NOT_EXIST); + } - const stakers = await getModel('staker').findAll({ where: { stake_id: stakePool.id, status: { [Op.or]: ['staking', 'unstaking'] } } }); - const reward = calculateStakingRewards(stakers); + const stakers = await getModel('staker').findAll({ where: { stake_id: stakePool.id, status: { [Op.or]: ['staking', 'unstaking'] } } }); + const reward = calculateStakingRewards(stakers); - return { reward }; -} + return { reward }; +}; const fetchStakeAnalytics = async () => { - const stakingAmount = await getModel('staker').findAll({ - attributes: [ - 'currency', - [fn('sum', col('amount')), 'total_amount'], - ], - group: ['currency'], - }); - const unstakingAmount = await getModel('staker').findAll({ - where: { status: 'unstaking' }, - attributes: [ - 'currency', - [fn('sum', col('amount')), 'total_amount'], - ], - group: ['currency'], - }); - - return { - stakingAmount, - unstakingAmount - } -} + const stakingAmount = await getModel('staker').findAll({ + attributes: [ + 'currency', + [fn('sum', col('amount')), 'total_amount'], + ], + group: ['currency'], + }); + const unstakingAmount = await getModel('staker').findAll({ + where: { status: 'unstaking' }, + attributes: [ + 'currency', + [fn('sum', col('amount')), 'total_amount'], + ], + group: ['currency'], + }); + + return { + stakingAmount, + unstakingAmount + }; +}; module.exports = { getExchangeStakePools, - createExchangeStakePool, - updateExchangeStakePool, - getExchangeStakers, - createExchangeStaker, - deleteExchangeStaker, - unstakeEstimateSlash, - unstakeEstimateSlashAdmin, - fetchStakeAnalytics + createExchangeStakePool, + updateExchangeStakePool, + getExchangeStakers, + createExchangeStaker, + deleteExchangeStaker, + unstakeEstimateSlash, + unstakeEstimateSlashAdmin, + fetchStakeAnalytics }; \ No newline at end of file diff --git a/server/utils/hollaex-tools-lib/tools/user.js b/server/utils/hollaex-tools-lib/tools/user.js index 9255633c97..df8dff5a45 100644 --- a/server/utils/hollaex-tools-lib/tools/user.js +++ b/server/utils/hollaex-tools-lib/tools/user.js @@ -621,7 +621,6 @@ const getAllUsersAdmin = (opts = { email: null, username: null, full_name: null, - pending_verification: null, dob_start_date: null, dob_end_date: null, gender: null, @@ -649,7 +648,7 @@ const getAllUsersAdmin = (opts = { const timeframe = timeframeQuery(opts.start_date, opts.end_date); const dob_timeframe = timeframeQuery(dob_start_date, dob_end_date); - let orderBy = 'updated_at'; + let orderBy = 'id'; let order = 'desc'; if (opts.order_by) { orderBy = opts.order_by; @@ -1203,7 +1202,7 @@ const updateUserNote = (userId, note, auditInfo) => { if (!user) { throw new Error(USER_NOT_FOUND); } - createAuditLog({ email: auditInfo.userEmail, session_id: auditInfo.sessionId }, auditInfo.apiPath, auditInfo.method, note, user.note); + createAuditLog({ email: auditInfo.userEmail, session_id: auditInfo.sessionId }, auditInfo.apiPath, auditInfo.method, { note, user_id: user.id }, { note: user.note, user_id: user.id }); return user.update({ note }, { fields: ['note'] }); }); }; @@ -2457,7 +2456,7 @@ const getUserBalanceHistory = (opts = { -const fetchUserProfitLossInfo = async (user_id) => { +const fetchUserProfitLossInfo = async (user_id, opts = { period: 7 }) => { const exchangeInfo = getKitConfig().info; @@ -2468,15 +2467,14 @@ const fetchUserProfitLossInfo = async (user_id) => { if(!getKitConfig()?.balance_history_config?.active) { throw new Error(BALANCE_HISTORY_NOT_ACTIVE); } - const data = await client.getAsync(`${user_id}user-pl-info`); + const data = await client.getAsync(`${user_id}-${opts.period}user-pl-info`); if (data) return JSON.parse(data); const { getAllUserTradesByKitId } = require('./order'); const { getUserWithdrawalsByKitId, getUserDepositsByKitId } = require('./wallet'); const balanceHistoryModel = getModel('balanceHistory'); - // Set it to weeks instead of years - const startDate = moment().subtract(1, 'weeks').toDate(); + const startDate = moment().subtract(opts.period, 'days').toDate(); const endDate = moment().toDate(); const timeframe = timeframeQuery(startDate, endDate); const userTrades = await getAllUserTradesByKitId(user_id, null, null, null, 'timestamp', 'asc', startDate, endDate, 'all'); @@ -2562,6 +2560,8 @@ const fetchUserProfitLossInfo = async (user_id) => { case '1m': dateThreshold.subtract(1, 'month'); break; + case '3m': + dateThreshold.subtract(3, 'months'); case '6m': dateThreshold.subtract(6, 'months'); break; @@ -2575,7 +2575,7 @@ const fetchUserProfitLossInfo = async (user_id) => { return data.filter((entry) => (moment(entry.created_at || entry.timestamp).isSameOrAfter(dateThreshold)) && (conditionalDate ? moment(entry.created_at || entry.timestamp).isAfter(moment(conditionalDate)) : true)); }; - const timeIntervals = ['1d', '7d', '1m', '6m', '1y']; + const timeIntervals = ['1d', '7d', '1m', '3m', '6m', '1y']; const results = {}; @@ -2663,37 +2663,39 @@ const fetchUserProfitLossInfo = async (user_id) => { }); } - if (results['7d']) { - const weightedAverage = (prices, weights) => { - const [sum, weightSum] = weights.reduce( - (acc, w, i) => { - acc[0] = acc[0] + prices[i] * w; - acc[1] = acc[1] + w; - return acc; - }, - [0, 0] - ); - return sum / weightSum; - }; - - let total = 0; - let percentageValues = []; - let prices = []; - const assets = Object.keys(results['7d']); - - assets?.forEach(asset => { - total += results['7d'][asset].cumulativePNL; - if (conversions[asset]) { - prices.push(conversions[asset]); - percentageValues.push(results['7d'][asset].cumulativePNLPercentage); - } - }); - results['7d'].total = total; - const weightedPercentage = weightedAverage(percentageValues, prices); - results['7d'].totalPercentage = weightedPercentage ? weightedPercentage.toFixed(2) : null; + const weightedAverage = (prices, weights) => { + const [sum, weightSum] = weights.reduce( + (acc, w, i) => { + acc[0] = acc[0] + prices[i] * w; + acc[1] = acc[1] + w; + return acc; + }, + [0, 0] + ); + return sum / weightSum; + }; + + for (const interval of ['7d', '1m', '3m']) { + if (results[interval]) { + let total = 0; + let percentageValues = []; + let prices = []; + const assets = Object.keys(results[interval]); + + assets?.forEach(asset => { + total += results[interval][asset].cumulativePNL; + if (conversions[asset]) { + prices.push(conversions[asset]); + percentageValues.push(results[interval][asset].cumulativePNLPercentage); + } + }); + results[interval].total = total; + const weightedPercentage = weightedAverage(percentageValues, prices); + results[interval].totalPercentage = weightedPercentage ? weightedPercentage.toFixed(2) : null; + } } - client.setexAsync(`${user_id}user-pl-info`, 3600, JSON.stringify(results)); + client.setexAsync(`${user_id}-${opts.period}user-pl-info`, 3600, JSON.stringify(results)); return results; }; diff --git a/settings/configmap b/settings/configmap index 543b58938d..83050ee66e 100644 --- a/settings/configmap +++ b/settings/configmap @@ -159,3 +159,5 @@ ENVIRONMENT_WEB_SERVER_REPLICAS=1 ENVIRONMENT_KUBERNETES_API_HPA_ENABLE=false ENVIRONMENT_KUBERNETES_STREAM_HPA_ENABLE=false ENVIRONMENT_KUBERNETES_WEB_HPA_ENABLE=false + +HOLLAEX_CONFIGMAP_ISSUER=$ENVIRONMENT_EXCHANGE_NAME \ No newline at end of file diff --git a/test/Cypress/cypress/integration/Gherkin/footer/footer.js b/test/Cypress/cypress/integration/Gherkin/footer/footer.js index e2e4c298db..85099e4a45 100644 --- a/test/Cypress/cypress/integration/Gherkin/footer/footer.js +++ b/test/Cypress/cypress/integration/Gherkin/footer/footer.js @@ -33,16 +33,19 @@ When ('I change Footer small text',()=>{ cy.contains('Back to Website').click() }) Then ('Footer small text should be changed',()=>{ - cy.contains('Terms of Service',{matchCase:false}) - .invoke('removeAttr', 'target').click({force: true}) - cy.url().should('contain',website+"term"+randomTest) - cy.go('back') - cy.get('.app_bar-icon').click() - cy.wait(6000) - cy.get('.footer-row-bottom > .d-flex').contains('Privacy Policy',{matchCase:false}) - .invoke('removeAttr', 'target').click({force: true}) - cy.url().should('contain',website+"privacy"+randomTest) - cy.go('back') + cy.contains('Terms of Service', { matchCase: false }).then($link => { + const url = $link.prop('href'); + console.log('URL:', url); + expect(url).to.include(website+"term"+randomTest); // Assert that the URL contains + }); + + cy.get('.footer-row-bottom > .d-flex').contains('Privacy Policy',{matchCase:false}) + .then($link => { + const url = $link.prop('href'); + console.log('URL:', url); + expect(url).to.include(website+"privacy"+randomTest); // Assert that the URL contains + }); + }) When ('I change Referral Badge',()=>{ @@ -63,10 +66,12 @@ Then ('Referral Badge should be changed',()=>{ cy.get('.app_bar-icon').click() cy.get('.footer-row-bottom > :nth-child(1)') .contains('For white label exchange services - Visit HollaEx.com'+randomTest) - .invoke('removeAttr', 'target').click({force: true}) - cy.url().should('contain',website+randomTest) - cy.go('back') - cy.wait(3000) + .then($link => { + const url = $link.prop('href'); + console.log('URL:', url); + expect(url).to.include(website+randomTest); // Assert that the URL contains + }); + }) When ('I add a column in Footer Links',()=>{ cy.contains('Operator controls').click({force:true}) @@ -106,9 +111,12 @@ Then ('The column should be on the Footer page',()=>{ cy.get('.footer-links-section--title').last().contains(randomTest) cy.get('.flex-column').last() .contains(randomTest) - .invoke('removeAttr', 'target').click({force: true}) - cy.url().should('contain',website+randomTest) - cy.go('back') + .then($link => { + const url = $link.prop('href'); + console.log('URL:', url); + expect(url).to.include(website+randomTest); // Assert that the URL contains + }); + }) When ('I delete a column in Footer Links',()=>{ cy.contains('Operator controls').click({force:true}) @@ -134,8 +142,7 @@ When ('I hided referral badge',()=>{ cy.get('.ant-checkbox-input') .should('not.be.checked').check() cy.get(':nth-child(4) > div.w-100 > .ant-btn').click() - - cy.get('.ant-message-notice-content').contains('Updated successfully') + cy.get('.ant-message-notice-content').contains('Updated successfully') }) Then ('Referral badge should be hidden',()=>{ cy.contains('Back to Website').click() diff --git a/test/Cypress/cypress/integration/Gherkin/landingPage.feature b/test/Cypress/cypress/integration/Gherkin/landingPage.feature index 200914e7d6..dc56137a8c 100644 --- a/test/Cypress/cypress/integration/Gherkin/landingPage.feature +++ b/test/Cypress/cypress/integration/Gherkin/landingPage.feature @@ -7,12 +7,12 @@ I want to check elements existence and sequencing of the landing page Scenario: Successfull Landing Page Arrangements Given I am on the Hollaex landing page - When All elements exist + And I record all elements locations Then I should be able to click on the live market - And I login check quick trade + And I login and check quick trade Then I visit the landing page in edit mode And I check the sequence of elements Title, Market list, and Quick trade calculator When I change the sequence - Then Elements should move - And I arrange elements in Market list, and Quick trade calculator + Then Elements should be arranged + diff --git a/test/Cypress/cypress/integration/Gherkin/landingPage/landingPage.js b/test/Cypress/cypress/integration/Gherkin/landingPage/landingPage.js index 5eda9d40ed..e07273b645 100644 --- a/test/Cypress/cypress/integration/Gherkin/landingPage/landingPage.js +++ b/test/Cypress/cypress/integration/Gherkin/landingPage/landingPage.js @@ -1,110 +1,187 @@ import {Given, When, Then, And} from "cypress-cucumber-preprocessor/steps" +let arrangedElements = []; +let textsArray = []; +let elementPositions = []; +const elementsToArrange = [ + 'Open-Source Crypto Exchange', + 'HollaEx/USD Tether', + 'Best prices' , + 'Quick Trade', + 'Live Markets' ,]; +const elementDescriptions = { + 'Best prices' : "Key icon features", + 'Open-Source Crypto Exchange' :"Title/heading", + 'HollaEx/USD Tether' : "Moving ticker cards", + 'Quick Trade' : "Quick trade calculator", + 'Live Markets' : "Market list", }; Given ('I am on the Hollaex landing page',()=>{ - cy.visit(Cypress.env('LANDING_PAGE')) + cy.visit(Cypress.env('LANDING_PAGE')) }) -When ('All elements exist',()=>{ - cy.contains('Open-Source Crypto Exchange',{ matchCase: false }) - cy.contains('VIEW EXCHANGE',{ matchCase: false }) - cy.contains('START TRADING',{ matchCase: false }) - - cy.contains('Markets',{ matchCase: false }) - cy.contains('LOGIN',{ matchCase: false }) - cy.contains('SIGN UP',{ matchCase: false }) - cy.contains('Quick Trade',{ matchCase: false }) - cy.get('.quick_trade-section_wrapper > .holla-button') - - cy.contains('LAST PRICE',{ matchCase: false }) - cy.contains('CHANGE',{ matchCase: false }) - cy.contains('24H HIGH',{ matchCase: false }) - cy.contains('24H LOW',{ matchCase: false }) - cy.contains('BEST BID',{ matchCase: false }) - cy.contains('BEST ASK',{ matchCase: false }) - cy.contains('24H VOLUME',{ matchCase: false }) +And ('I record all elements locations',()=>{ + // Get the position of each element + cy.wrap(elementsToArrange).each((element) => { + cy.contains(element, { matchCase: false }).then(($el) => { + $el[0].scrollIntoView(); + const position = $el.offset() + cy.log(element,position) + const { top } = position; + elementPositions.push({ element: elementDescriptions[element] || element, top }); + }); + }).then(() => { + // Sort elements by their top position + elementPositions.sort((a, b) => a.top - b.top); + cy.log(JSON.stringify(elementPositions)) + // Store the arranged elements in an array + const arrangedElements = elementPositions.map((item) => item.element); + // Log the arranged elements + cy.log('Elements arranged from top to down:'); + arrangedElements.forEach((item) => { + cy.log(item); + }); + }); }) Then ('I should be able to click on the live market',()=>{ - cy.get('.pt-1').click() - cy.contains('Markets',{ matchCase: false }) - cy.contains('LOGIN',{ matchCase: false }) + cy.get('.pt-1').click() + cy.contains('Markets',{ matchCase: false }) + cy.contains('LOGIN',{ matchCase: false }) }) -And ('I login check quick trade',()=>{ - cy.visit(Cypress.env('LANDING_PAGE')) - cy.get('.quick_trade-section_wrapper > .holla-button').click() - cy.get('.holla-button').should('be.visible').should('be.disabled') - cy.get('[name="email"]').clear().type(Cypress.env('ADMIN_USER')) - cy.get('[name="password"]').clear().type(Cypress.env('ADMIN_PASS')) - cy.get('.holla-button').should('be.visible').should('be.enabled').click() - cy.get('.warning_text').should('not.exist') - cy.wait(2000) +And ('I login and check quick trade',()=>{ + cy.visit(Cypress.env('LANDING_PAGE')) + cy.get('.quick_trade-section_wrapper > .holla-button').should('be.disabled') + cy.get('[href="/login"] > .holla-button').click() + cy.get('.holla-button').should('be.visible').should('be.disabled') + cy.get('[name="email"]').clear().type(Cypress.env('ADMIN_USER')) + cy.get('[name="password"]').clear().type(Cypress.env('ADMIN_PASS')) + cy.get('.holla-button').should('be.visible').should('be.enabled').click() + cy.get('.warning_text').should('not.exist') + cy.wait(2000) }) Then ('I visit the landing page in edit mode',()=>{ - cy.visit(Cypress.env('LANDING_PAGE')) - cy.contains('Enter edit mode').click() + cy.visit(Cypress.env('LANDING_PAGE')) + cy.contains('Enter edit mode').click() }) And ('I check the sequence of elements Title, Market list, and Quick trade calculator',()=>{ - cy.get('[style="position: fixed; right: 5px; top: calc((100vh - 160px) / 2); z-index: 1;"] > .edit-wrapper__icons-container > .edit-wrapper__icon-wrapper') - .click() - cy.get('.operator-controls__modal-title').contains('Sections') - cy.get('table.mt-4 > tbody > :nth-child(1) > :nth-child(2)') - .contains('Title/heading',{ matchCase: false }) - cy.get('table.mt-4 > tbody > :nth-child(2) > :nth-child(2)') - .contains('Card Section',{ matchCase: false }) - cy.get('table.mt-4 > tbody > :nth-child(3) > :nth-child(2)') - .contains('Market list',{ matchCase: false }) - cy.get('table.mt-4 > tbody > :nth-child(4) > :nth-child(2)') - .contains('Quick trade calculator',{ matchCase: false }) + cy.log('Please wait while the test is running') + //cy.get('[style="position: fixed; right: 5px; top: calc((100vh - 160px) / 2); z-index: 1;"] > .edit-wrapper__icons-container > .edit-wrapper__icon-wrapper') .click() + cy.get('*').filter((index, element) => { + return Cypress.$(element).css('position') === 'fixed'; + }).then((fixedElements) => { + // fixedElements now contains all elements with position: fixed + // You can interact with these elements as needed + // For example, clicking the first fixed element: + if (fixedElements.length > 0) { + cy.wrap(fixedElements[0]).click(); + } + }); + + cy.get('table.mt-4 > tbody > tr').then(($rows) => { + const numRows = $rows.length; + cy.log(numRows) + for (let n = 1; n <= numRows; n++) { + cy.get(`table.mt-4 > tbody > :nth-child(${n}) > :nth-child(2)`) + .invoke('text') + .then((text) => { + cy.log(text); + textsArray.push(n+'.'+text); + cy.log((JSON.stringify(textsArray))) + }); + cy.log((JSON.stringify(textsArray))) + } + + // Select the last row of the 1st column + const lastRow = cy.get(`table.mt-4 > tbody > :nth-child(${numRows}) > :nth-child(1)`); + // Now you can perform actions with the last row element as needed + }); + // Ensure both arrays have the same length + expect(arrangedElements.length).to.equal(textsArray.length); + + // Loop through each element of arrangedElements and compare with textsArray + arrangedElements.forEach((element, index) => { + expect(element).to.equal(textsArray[index]); + }); }) When ('I change the sequence',()=>{ - cy.get('table.mt-4 > tbody > :nth-child(1) > :nth-child(2)') + textsArray = []; + cy.get('table.mt-4 > tbody > :nth-child(1) > :nth-child(2)') .drag('table.mt-4 > tbody > :nth-child(3) > :nth-child(2)',{force: true}) + cy.contains('Confirm',{ matchCase: false }).click() cy.contains('PUBLISH',{ matchCase: false }).click() cy.get('.d-flex > .ml-1').click() - cy.wait(2000) - cy.get('.mx-2 > :nth-child(3)') - cy.contains('Open-Source Crypto Exchange',{ matchCase: false }) -}) -Then ('Elements should move',()=>{ + cy.wait(5000) cy.contains('Enter edit mode').click() - cy.get('[style="position: fixed; right: 5px; top: calc((100vh - 160px) / 2); z-index: 1;"] > .edit-wrapper__icons-container > .edit-wrapper__icon-wrapper') - .click() - cy.get('.operator-controls__modal-title').contains('Sections') - cy.get('table.mt-4 > tbody > :nth-child(4) > :nth-child(2)') - .contains('Title/heading',{ matchCase: false }) - cy.get('table.mt-4 > tbody > :nth-child(1) > :nth-child(2)') - .contains('Card section',{ matchCase: false }) - cy.get('table.mt-4 > tbody > :nth-child(2) > :nth-child(2)') - .contains('Market list',{ matchCase: false }) - - cy.get('table.mt-4 > tbody > :nth-child(1) > :nth-child(2)') - .drag('table.mt-4 > tbody > :nth-child(4) > :nth-child(2)',{force: true}) - cy.contains('Confirm',{ matchCase: false }).click() + cy.get('*').filter((index, element) => { + return Cypress.$(element).css('position') === 'fixed'; + }).then((fixedElements) => { + // fixedElements now contains all elements with position: fixed + if (fixedElements.length > 0) { + // Create an alias for the first fixed element + cy.wrap(fixedElements[0]).as('firstFixedElement').click(); + } + }); - cy.wait(2000) - cy.get('[style="position: fixed; right: 5px; top: calc((100vh - 160px) / 2); z-index: 1;"] > .edit-wrapper__icons-container > .edit-wrapper__icon-wrapper') - .click() - cy.get('table.mt-4 > tbody > :nth-child(1) > :nth-child(2)') - .drag('table.mt-4 > tbody > :nth-child(4) > :nth-child(2)',{force: true}) - cy.contains('Confirm',{ matchCase: false }).click() + cy.get('table.mt-4 > tbody > tr').then(($rows) => { + const numRows = $rows.length; + cy.log(numRows) + for (let n = 1; n <= numRows; n++) { + cy.get(`table.mt-4 > tbody > :nth-child(${n}) > :nth-child(2)`) + .invoke('text') + .then((text) => { + cy.log + cy.log(text); + textsArray.push(n+'.'+text); + cy.log((JSON.stringify(textsArray))) + }); + cy.log((JSON.stringify(textsArray))) + } + + // Select the last row of the 1st column + const lastRow = cy.get(`table.mt-4 > tbody > :nth-child(${numRows}) > :nth-child(1)`); + // Now you can perform actions with the last row element as needed + }); - cy.wait(2000) - cy.get('[style="position: fixed; right: 5px; top: calc((100vh - 160px) / 2); z-index: 1;"] > .edit-wrapper__icons-container > .edit-wrapper__icon-wrapper') - .click() - cy.get('table.mt-4 > tbody > :nth-child(1) > :nth-child(2)') - .drag('table.mt-4 > tbody > :nth-child(4) > :nth-child(2)',{force: true}) + cy.get('.action_notification-text > .anticon > svg').click() +}) +Then ('Elements should be arranged',()=>{ + arrangedElements = [] + elementPositions= [] + // Get the position of each element + cy.wrap(elementsToArrange).each((element) => { cy.contains(element, { matchCase: false }).then(($el) => { + $el[0].scrollIntoView(); + const position = $el.offset() + cy.log(element,position) + const { top } = position; + elementPositions.push({ element: elementDescriptions[element] || element, top }); + }); + }).then(() => { + // Sort elements by their top position + elementPositions.sort((a, b) => a.top - b.top); + cy.log(JSON.stringify(elementPositions)) + // Store the arranged elements in an array + const arrangedElements = elementPositions.map((item) => item.element); + // Log the arranged elements + cy.log('Elements arranged from top to down:'); + arrangedElements.forEach((item) => { + cy.log(item); + }); + // Ensure both arrays have the same length + expect(arrangedElements.length).to.equal(textsArray.length); + + // Loop through each element of arrangedElements and compare with textsArray + textsArray.forEach((element, index) => { + // Remove numbering and period (e.g., "1.Market list" becomes "Market list") + const cleanElement = element.substring(element.indexOf('.') + 1).trim(); + expect(cleanElement).to.equal(arrangedElements[index]); + }); + + }); + }) -And ('I arrange elements in Market list, and Quick trade calculator',()=>{ - cy.contains('Confirm',{ matchCase: false }).click() - cy.contains('PUBLISH',{ matchCase: false }).click() - cy.get('.d-flex > .ml-1').click() - cy.wait(2000) - cy.get('.mx-2 > :nth-child(3)') - cy.contains('Open-Source Crypto Exchange',{ matchCase: false }) -}) \ No newline at end of file diff --git a/test/Cypress/cypress/integration/Gherkin/roles/roles.js b/test/Cypress/cypress/integration/Gherkin/roles/roles.js index bab3e6a0a1..f14188e9e5 100644 --- a/test/Cypress/cypress/integration/Gherkin/roles/roles.js +++ b/test/Cypress/cypress/integration/Gherkin/roles/roles.js @@ -151,19 +151,19 @@ Then ('I must be able to do KYCs tasks',()=>{ }) cy.reload() cy.get('#rc-tabs-0-tab-bank').click() - cy.contains('Add bank').click() - cy.get('.ant-select-selector').click().type('{downarrow}{enter}') - cy.get('.ant-modal-footer > .ant-btn-primary').click() - cy.get(':nth-child(1) > :nth-child(2) > .d-flex > .ant-input').clear().type('test') - cy.get(':nth-child(2) > :nth-child(2) > .d-flex > .ant-input').clear().type('123456') - cy.get(':nth-child(3) > :nth-child(2) > .d-flex > .ant-input').clear().type('123456') - cy.get('.ant-modal-footer > .ant-btn-primary').click() - cy.get('.ant-notification-notice').contains('Success') - cy.contains('123456') - cy.get('.ant-card-extra > .anticon > svg').click() - cy.get('.ant-popover-buttons > .ant-btn-primary').click() - cy.get('.ant-message-notice-content').contains('Bank deleted') - cy.contains('123456').should('not.exist'); + // cy.contains('Add bank').click() + // cy.get('.ant-select-selector').click().type('{downarrow}{enter}') + // cy.get('.ant-modal-footer > .ant-btn-primary').click() + // cy.get(':nth-child(1) > :nth-child(2) > .d-flex > .ant-input').clear().type('test') + // cy.get(':nth-child(2) > :nth-child(2) > .d-flex > .ant-input').clear().type('123456') + // cy.get(':nth-child(3) > :nth-child(2) > .d-flex > .ant-input').clear().type('123456') + // cy.get('.ant-modal-footer > .ant-btn-primary').click() + // cy.get('.ant-notification-notice').contains('Success') + // cy.contains('123456') + // cy.get('.ant-card-extra > .anticon > svg').click() + // cy.get('.ant-popover-buttons > .ant-btn-primary').click() + // cy.get('.ant-message-notice-content').contains('Bank deleted') + // cy.contains('123456').should('not.exist'); cy.get('#rc-tabs-0-tab-orders').click() cy.contains('Download table').should('not.exist') @@ -312,16 +312,16 @@ Then ('I must be able to do Supports tasks',()=>{ }) cy.reload() cy.get('#rc-tabs-0-tab-bank').click() - cy.contains('Add bank').click() - cy.get('.ant-select-selector').click().type('{downarrow}{enter}') - cy.get('.ant-modal-footer > .ant-btn-primary').click() - cy.get(':nth-child(1) > :nth-child(2) > .d-flex > .ant-input').clear().type('test') - cy.get(':nth-child(2) > :nth-child(2) > .d-flex > .ant-input').clear().type('123456') - cy.get(':nth-child(3) > :nth-child(2) > .d-flex > .ant-input').clear().type('123456') - cy.get('.ant-modal-footer > .ant-btn-primary').click() - cy.get('.ant-modal-body > :nth-child(7) > strong') - .should('contain','Access denied: User is not authorized to access this endpoint') - cy.get('.ant-modal-close-x').click() + // cy.contains('Add bank').click() + // cy.get('.ant-select-selector').click().type('{downarrow}{enter}') + // cy.get('.ant-modal-footer > .ant-btn-primary').click() + // cy.get(':nth-child(1) > :nth-child(2) > .d-flex > .ant-input').clear().type('test') + // cy.get(':nth-child(2) > :nth-child(2) > .d-flex > .ant-input').clear().type('123456') + // cy.get(':nth-child(3) > :nth-child(2) > .d-flex > .ant-input').clear().type('123456') + // cy.get('.ant-modal-footer > .ant-btn-primary').click() + // cy.get('.ant-modal-body > :nth-child(7) > strong') + // .should('contain','Access denied: User is not authorized to access this endpoint') + // cy.get('.ant-modal-close-x').click() // cy.get('.ant-notification-notice').contains('Success') // cy.contains('123456') // cy.get('.ant-card-extra > .anticon > svg').click() @@ -468,19 +468,19 @@ Then ('I must be able to do Supervisors tasks',()=>{ cy.reload() cy.get('#rc-tabs-0-tab-bank').click() - cy.contains('Add bank').click() - cy.get('.ant-select-selector').click().type('{downarrow}{enter}') - cy.get('.ant-modal-footer > .ant-btn-primary').click() - cy.get(':nth-child(1) > :nth-child(2) > .d-flex > .ant-input').clear().type('test') - cy.get(':nth-child(2) > :nth-child(2) > .d-flex > .ant-input').clear().type('123456') - cy.get(':nth-child(3) > :nth-child(2) > .d-flex > .ant-input').clear().type('123456') - cy.get('.ant-modal-footer > .ant-btn-primary').click() - cy.get('.ant-notification-notice').contains('Success') - cy.contains('123456') - cy.get('.ant-card-extra > .anticon > svg').click() - cy.get('.ant-popover-buttons > .ant-btn-primary').click() - cy.get('.ant-message-notice-content').contains('Bank deleted') - cy.contains('123456').should('not.exist'); + // cy.contains('Add bank').click() + // cy.get('.ant-select-selector').click().type('{downarrow}{enter}') + // cy.get('.ant-modal-footer > .ant-btn-primary').click() + // cy.get(':nth-child(1) > :nth-child(2) > .d-flex > .ant-input').clear().type('test') + // cy.get(':nth-child(2) > :nth-child(2) > .d-flex > .ant-input').clear().type('123456') + // cy.get(':nth-child(3) > :nth-child(2) > .d-flex > .ant-input').clear().type('123456') + // cy.get('.ant-modal-footer > .ant-btn-primary').click() + // cy.get('.ant-notification-notice').contains('Success') + // cy.contains('123456') + // cy.get('.ant-card-extra > .anticon > svg').click() + // cy.get('.ant-popover-buttons > .ant-btn-primary').click() + // cy.get('.ant-message-notice-content').contains('Bank deleted') + // cy.contains('123456').should('not.exist'); cy.get('#rc-tabs-0-tab-balance').click() cy.get('[data-row-key="1"] > .ant-table-row-expand-icon-cell').click() @@ -573,9 +573,9 @@ Then ('I must be able to do Supervisors tasks',()=>{ //cy.get('#rc-tabs-0-tab-1').click() cy.contains('Summary').click() - cy.get('.app_container-content > :nth-child(1) > .ant-alert-description') + cy.get('.ant-message-notice-content') .contains('Access denied: User is not authorized to access this endpoint') - cy.get('.ant-card-body > .ant-alert > .ant-alert-description') + cy.get('.ant-alert-description') .contains('Access denied: User is not authorized to access this endpoint') //cy.get('#rc-tabs-0-tab-2').click() diff --git a/version b/version index d332044b15..fe1824995d 100644 --- a/version +++ b/version @@ -1 +1 @@ -2.10.2 \ No newline at end of file +2.10.3 \ No newline at end of file diff --git a/web/package-lock.json b/web/package-lock.json index 8160c7305d..3a40aebdc1 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,6 +1,6 @@ { "name": "hollaex-kit", - "version": "2.10.1", + "version": "2.10.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -17913,11 +17913,6 @@ "react-webcam": "^5.0.1" } }, - "react-recaptcha-v3": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/react-recaptcha-v3/-/react-recaptcha-v3-2.0.1.tgz", - "integrity": "sha512-ZQ+auotgu+E/6YAbPqk53sf9xgcp8TFvVVfL4bVR7PXj98nQmdaONugdLJEA7g1iTZ4yQqC4mZbcVUGZmwb25Q==" - }, "react-redux": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-6.0.1.tgz", diff --git a/web/package.json b/web/package.json index 09fbe8cb40..63f94adf81 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "hollaex-kit", - "version": "2.10.2", + "version": "2.10.3", "private": true, "dependencies": { "@ant-design/compatible": "1.0.5", @@ -63,7 +63,6 @@ "react-modal": "3.8.1", "react-moment": "0.9.2", "react-qr-barcode-scanner": "1.0.6", - "react-recaptcha-v3": "2.0.1", "react-redux": "6.0.1", "react-router": "3.2.1", "react-sortable-hoc": "1.10.1", diff --git a/web/public/assets/images/account-summary-tab-01.svg b/web/public/assets/images/account-summary-tab-01.svg new file mode 100644 index 0000000000..b387914ddc --- /dev/null +++ b/web/public/assets/images/account-summary-tab-01.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/web/public/assets/images/appstore-apple_logo_black.svg b/web/public/assets/images/appstore-apple_logo_black.svg new file mode 100644 index 0000000000..82b0cddcdb --- /dev/null +++ b/web/public/assets/images/appstore-apple_logo_black.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/public/assets/images/asset-prices-mobile-tab-01.svg b/web/public/assets/images/asset-prices-mobile-tab-01.svg new file mode 100644 index 0000000000..8c2c37a015 --- /dev/null +++ b/web/public/assets/images/asset-prices-mobile-tab-01.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/web/public/assets/images/card-active-plugin-mobile-tab-01.svg b/web/public/assets/images/card-active-plugin-mobile-tab-01.svg new file mode 100644 index 0000000000..f8b133da39 --- /dev/null +++ b/web/public/assets/images/card-active-plugin-mobile-tab-01.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/web/public/assets/images/google-auth-icon.svg b/web/public/assets/images/google-auth-icon.svg new file mode 100644 index 0000000000..814730f469 --- /dev/null +++ b/web/public/assets/images/google-auth-icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/web/public/assets/images/google-play.svg b/web/public/assets/images/google-play.svg new file mode 100644 index 0000000000..db26ae0fd0 --- /dev/null +++ b/web/public/assets/images/google-play.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/web/public/assets/images/pro-trade-markets-mobile-tab-01 copy-01.svg b/web/public/assets/images/pro-trade-markets-mobile-tab-01 copy-01.svg new file mode 100644 index 0000000000..68f847f71a --- /dev/null +++ b/web/public/assets/images/pro-trade-markets-mobile-tab-01 copy-01.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/web/public/assets/images/quick-trade-convert-mobile-tab-01.svg b/web/public/assets/images/quick-trade-convert-mobile-tab-01.svg new file mode 100644 index 0000000000..3f1a2e6fc8 --- /dev/null +++ b/web/public/assets/images/quick-trade-convert-mobile-tab-01.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/web/public/assets/images/wallet-mobile-tab-02-01 (1).svg b/web/public/assets/images/wallet-mobile-tab-02-01 (1).svg new file mode 100644 index 0000000000..6dc1e26e2b --- /dev/null +++ b/web/public/assets/images/wallet-mobile-tab-02-01 (1).svg @@ -0,0 +1,7 @@ + + + + + diff --git a/web/public/index.html b/web/public/index.html index e94dd182f7..89bce0c4df 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -2,7 +2,7 @@ - + diff --git a/web/src/_variables.scss b/web/src/_variables.scss index a023143cc7..5e9ff4d77b 100644 --- a/web/src/_variables.scss +++ b/web/src/_variables.scss @@ -212,6 +212,10 @@ $title--font-family: $raleway-extrabold--font-family; $dialog-min-width: 25rem; $dialog-min-height: 30rem; +// SUCCESS OTP DIALOG +$otp-dialog-min-width: 20rem; +$otp-dialog-min-height: 14rem; + // APP BAR $navigation_bar-height: 4rem; $navigation_bar-height-lg: 5rem; diff --git a/web/src/actions/appActions.js b/web/src/actions/appActions.js index 4a604b5b19..75605f6350 100644 --- a/web/src/actions/appActions.js +++ b/web/src/actions/appActions.js @@ -96,6 +96,8 @@ export const TOGGLE_DIGITAL_ASSETS_SORT = 'TOGGLE_DIGITAL_ASSETS_SORT'; export const SET_ADMIN_SORT = 'SET_ADMIN_SORT'; export const SET_ADMIN_WALLET_SORT = 'SET_ADMIN_WALLET_SORT'; export const SET_ADMIN_DIGITAL_ASSETS_SORT = 'SET_ADMIN_DIGITAL_ASSETS_SORT'; +export const SET_SELECTED_ACCOUNT = 'SET_SELECTED_ACCOUNT'; +export const SET_SELECTED_STEP = 'SET_SELECTED_STEP'; export const SORT = { VOL: 'volume', @@ -283,6 +285,10 @@ export const openContactForm = () => { return openHelpfulResourcesForm(); }; +export const setSelectedStep = (selectedStep) => { + return { type: SET_SELECTED_STEP, payload: { selectedStep } }; +}; + export const openHelpfulResourcesForm = (data = {}) => setNotification(HELPFUL_RESOURCES_FORM, data, true); @@ -529,6 +535,13 @@ export const openRiskPortfolioOrderWarning = (data = {}) => export const logoutconfirm = (data = {}) => setNotification(LOGOUT_CONFORMATION, data, true); +export const setSelectedAccount = (selectedAccount) => ({ + type: SET_SELECTED_ACCOUNT, + payload: { + selectedAccount, + }, +}); + export const requestPlugins = () => axios.get(`${PLUGIN_URL}/plugins`); export const requestInitial = () => axios.get('/kit'); export const requestConstant = () => axios.get('/constants'); diff --git a/web/src/components/AdminForm/captchaField.js b/web/src/components/AdminForm/captchaField.js deleted file mode 100644 index 52764f1113..0000000000 --- a/web/src/components/AdminForm/captchaField.js +++ /dev/null @@ -1,80 +0,0 @@ -import React, { Component } from 'react'; -import classnames from 'classnames'; -import { ReCaptcha } from 'react-recaptcha-v3'; -import { connect } from 'react-redux'; -import withConfig from 'components/ConfigProvider/withConfig'; -import { DEFAULT_CAPTCHA_SITEKEY } from 'config/constants'; - -class CaptchaField extends Component { - state = { - active: true, - ready: false, - }; - - componentDidMount() { - this.expiryTime = setInterval(() => { - this.captcha.execute(); - }, 120000); - } - - UNSAFE_componentWillReceiveProps(nextProps) { - if ( - nextProps.input.value === '' && - nextProps.input.value !== this.props.input.value - ) { - this.captcha.execute(); - } - } - - setRef = (el) => { - this.captcha = el; - }; - - onVerifyCallback = (data) => { - this.props.input.onChange(data); - }; - - onExpiredCallback = () => { - this.props.input.onChange(''); - this.captcha.execute(); - }; - - componentWillUnmount() { - if (this.expiryTime) { - clearInterval(this.expiryTime); - } - } - - render() { - const { - language, - constants: { captcha = {} }, - defaults: { language: DEFAULT_LANGUAGE }, - } = this.props; - const { ready, active } = this.state; - - return ( - active && ( -
- -
- ) - ); - } -} - -const mapStateToProps = (state) => ({ - constants: state.app.constants, -}); - -export default connect(mapStateToProps)(withConfig(CaptchaField)); diff --git a/web/src/components/AdminForm/utils.js b/web/src/components/AdminForm/utils.js index 0d1aa60d33..7ffdb9e4f5 100644 --- a/web/src/components/AdminForm/utils.js +++ b/web/src/components/AdminForm/utils.js @@ -11,7 +11,6 @@ import { renderBooleanField, } from './fields'; import { FileField } from './FileField'; -import CaptchaField from './captchaField'; import Editor from './Editor'; const renderFields = (fields, disableAllFields) => { @@ -50,9 +49,6 @@ const renderFields = (fields, disableAllFields) => { case 'range': component = renderRangeField; break; - case 'captcha': - component = CaptchaField; - break; case 'file': component = FileField; break; diff --git a/web/src/components/AppBar/MarketSelector.js b/web/src/components/AppBar/MarketSelector.js index 3eab9a6315..399298c3a6 100644 --- a/web/src/components/AppBar/MarketSelector.js +++ b/web/src/components/AppBar/MarketSelector.js @@ -132,7 +132,7 @@ class MarketSelector extends Component { }; onViewAssetsClick = () => { - browserHistory.push('/assets'); + browserHistory.push('/prices'); if (isMobile) { window.location.reload(); } @@ -242,6 +242,7 @@ class MarketSelector extends Component { this.handleSearch(e.target && e.target.value) } showCross + isFocus={true} />
diff --git a/web/src/components/AppBar/PairTabs.js b/web/src/components/AppBar/PairTabs.js index 3f5250b944..7c7b92b436 100644 --- a/web/src/components/AppBar/PairTabs.js +++ b/web/src/components/AppBar/PairTabs.js @@ -13,10 +13,13 @@ import { Slider, EditWrapper, PriceChange } from 'components'; import withConfig from 'components/ConfigProvider/withConfig'; import { formatToCurrency } from 'utils/currency'; import { MarketsSelector } from 'containers/Trade/utils'; +import { getSparklines } from 'actions/chartAction'; +import SparkLine from 'containers/TradeTabs/components/SparkLine'; class PairTabs extends Component { state = { activePairTab: '', + sparkLine: [], }; componentDidMount() { @@ -27,6 +30,9 @@ class PairTabs extends Component { } this.setState({ activePairTab: active }); this.initTabs(pairs, active); + getSparklines(Object.keys(pairs)).then((sparkLine) => + this.setState({ sparkLine }) + ); } UNSAFE_componentWillReceiveProps(nextProps) { @@ -84,6 +90,7 @@ class PairTabs extends Component { activePairTab, isMarketSelectorVisible, isToolsSelectorVisible, + sparkLine, } = this.state; const { location, favourites, markets, quicktrade } = this.props; @@ -149,7 +156,24 @@ class PairTabs extends Component {
{formatToCurrency(close, increment_price)}
- + +
) : (
@@ -175,7 +199,7 @@ class PairTabs extends Component { {favourites && favourites.length > 0 && ( @@ -237,7 +261,7 @@ const mapStateToProps = (state) => { favourites, constants, markets: MarketsSelector(state), - quicktrade + quicktrade, }; }; diff --git a/web/src/components/AppBar/SearchBox.js b/web/src/components/AppBar/SearchBox.js index 4b3751b7fd..01d572e674 100644 --- a/web/src/components/AppBar/SearchBox.js +++ b/web/src/components/AppBar/SearchBox.js @@ -23,6 +23,7 @@ class SearchBox extends React.Component { outlineClassName = '', name, showCross = false, + isFocus, } = this.props; const searchField = { search: { @@ -41,6 +42,7 @@ class SearchBox extends React.Component { handleSearch(''); this.props.dispatch(reset(FORM_NAME)); }, + isFocus, }, }; diff --git a/web/src/components/AppBar/Tab.js b/web/src/components/AppBar/Tab.js index b779eaebdc..bcbd2415af 100644 --- a/web/src/components/AppBar/Tab.js +++ b/web/src/components/AppBar/Tab.js @@ -19,7 +19,7 @@ const Tab = ({ pair: { increment_price } = {}, ticker: { close } = {}, display_name, - type + type, } = market; return ( @@ -45,7 +45,7 @@ const Tab = ({
{display_name}
{increment_price && ( <> -
+
: {formatToCurrency(close, increment_price)}
@@ -57,7 +57,6 @@ const Tab = ({
)} -
diff --git a/web/src/components/AppBar/_AppBar.scss b/web/src/components/AppBar/_AppBar.scss index 03bb515f45..504501e153 100644 --- a/web/src/components/AppBar/_AppBar.scss +++ b/web/src/components/AppBar/_AppBar.scss @@ -753,6 +753,10 @@ $app-menu-width: calc(100vw - 40rem); } } + .fav-price-label { + color: $colors-main-black !important; + } + .app_bar-currency-txt { font-weight: 600; } diff --git a/web/src/components/ConfigProvider/index.js b/web/src/components/ConfigProvider/index.js index 5d3ca2c338..27be6d1a7c 100644 --- a/web/src/components/ConfigProvider/index.js +++ b/web/src/components/ConfigProvider/index.js @@ -1,10 +1,8 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; -import { loadReCaptcha } from 'react-recaptcha-v3'; import { getIconByKey, generateAllIcons, addDefaultLogo } from 'utils/icon'; import { calculateThemes } from 'utils/color'; import merge from 'lodash.merge'; -import { DEFAULT_CAPTCHA_SITEKEY } from 'config/constants'; export const ProjectConfig = React.createContext('appConfig'); @@ -39,17 +37,7 @@ class ConfigProvider extends Component { sections, }; } - - componentDidMount() { - const { - initialConfig: { captcha = {} }, - } = this.props; - - // ReCaptcha Initialization - const siteKey = captcha.site_key || DEFAULT_CAPTCHA_SITEKEY; - loadReCaptcha(siteKey, () => {}); - } - + UNSAFE_componentWillUpdate(_, nextState) { const { color, icons } = this.state; if (JSON.stringify(color) !== JSON.stringify(nextState.color)) { diff --git a/web/src/components/Dialog/DesktopDialog.js b/web/src/components/Dialog/DesktopDialog.js index 099bf2c31e..3fb8da06e3 100644 --- a/web/src/components/Dialog/DesktopDialog.js +++ b/web/src/components/Dialog/DesktopDialog.js @@ -23,6 +23,11 @@ class Dialog extends PureComponent { } }; + onHandleBack = () => { + const { onHandleEnableBack } = this.props; + onHandleEnableBack(2); + }; + render() { const languageClasses = getClasesForLanguage(getLanguage()); const { @@ -36,6 +41,7 @@ class Dialog extends PureComponent { className, bodyOpenClassName, isEditMode, + isEnableOtpForm, } = this.props; return ( @@ -68,6 +74,15 @@ class Dialog extends PureComponent { + + )} ); } diff --git a/web/src/components/Dialog/MessageDisplay.js b/web/src/components/Dialog/MessageDisplay.js index 36a3677814..ddd3b837c5 100644 --- a/web/src/components/Dialog/MessageDisplay.js +++ b/web/src/components/Dialog/MessageDisplay.js @@ -14,7 +14,13 @@ const MessageDisplay = ({ style, }) => (
@@ -28,6 +34,13 @@ const MessageDisplay = ({ {title}
)} + {iconId === 'OTP_ACTIVE' && ( +
+ + {STRINGS['ACCOUNT_SECURITY.OTP.ACCOUNT_SECURED']} + +
+ )}
{text}
diff --git a/web/src/components/Dialog/_Dialog.scss b/web/src/components/Dialog/_Dialog.scss index c4298ce5ad..844128facb 100644 --- a/web/src/components/Dialog/_Dialog.scss +++ b/web/src/components/Dialog/_Dialog.scss @@ -45,6 +45,12 @@ $close-button--margin: 1.5rem; padding-bottom: $dialog--padding; } } +.cancel-withdraw-pop-up { + .ReactModal__Content { + width: 25% !important; + padding: 0rem 2.5rem 2.5rem 2.5rem !important; + } +} .ReactModal__Content { padding: $dialog--padding !important; @@ -80,6 +86,12 @@ $close-button--margin: 1.5rem; } } +.success_disable-display-wrapper, +.success_active-display-wrapper { + min-width: $otp-dialog-min-width; + min-height: $otp-dialog-min-height; +} + .success_display-content { height: auto; flex: 1; diff --git a/web/src/components/Form/FormFields/Captcha.js b/web/src/components/Form/FormFields/Captcha.js deleted file mode 100644 index cc4dbb4552..0000000000 --- a/web/src/components/Form/FormFields/Captcha.js +++ /dev/null @@ -1,81 +0,0 @@ -import React, { Component } from 'react'; -import classnames from 'classnames'; -import { ReCaptcha } from 'react-recaptcha-v3'; -import { connect } from 'react-redux'; -import withConfig from 'components/ConfigProvider/withConfig'; - -import { DEFAULT_CAPTCHA_SITEKEY } from 'config/constants'; - -class CaptchaField extends Component { - state = { - active: true, - ready: false, - }; - - componentDidMount() { - this.expiryTime = setInterval(() => { - this.captcha.execute(); - }, 120000); - } - - UNSAFE_componentWillReceiveProps(nextProps) { - if ( - nextProps.input.value === '' && - nextProps.input.value !== this.props.input.value - ) { - this.captcha.execute(); - } - } - - setRef = (el) => { - this.captcha = el; - }; - - onVerifyCallback = (data) => { - this.props.input.onChange(data); - }; - - onExpiredCallback = () => { - this.props.input.onChange(''); - this.captcha.execute(); - }; - - componentWillUnmount() { - if (this.expiryTime) { - clearInterval(this.expiryTime); - } - } - - render() { - const { - language, - constants: { captcha = {} }, - defaults: { language: DEFAULT_LANGUAGE }, - } = this.props; - const { ready, active } = this.state; - - return ( - active && ( -
- -
- ) - ); - } -} - -const mapStateToProps = (state) => ({ - constants: state.app.constants, -}); - -export default connect(mapStateToProps)(withConfig(CaptchaField)); diff --git a/web/src/components/Form/FormFields/InputField.js b/web/src/components/Form/FormFields/InputField.js index cdcb461aba..214008addc 100644 --- a/web/src/components/Form/FormFields/InputField.js +++ b/web/src/components/Form/FormFields/InputField.js @@ -29,6 +29,7 @@ const InputField = (props) => { onCrossClick, showCross, ishorizontalfield, + isFocus, ...rest } = props; const displayError = touched && error && !active; @@ -46,6 +47,7 @@ const InputField = (props) => { {...input} {...rest} onBlur={() => {}} + autoFocus={isFocus} />
diff --git a/web/src/components/Form/FormFields/PinInput.js b/web/src/components/Form/FormFields/PinInput.js index d8853324b5..cc00362dae 100644 --- a/web/src/components/Form/FormFields/PinInput.js +++ b/web/src/components/Form/FormFields/PinInput.js @@ -73,13 +73,13 @@ const PinInput = ({ onChange={handleMasterValueChange} onBlur={handleOnMasterBlur} /> -
+
-
+
diff --git a/web/src/components/Form/FormFields/_PinInput.scss b/web/src/components/Form/FormFields/_PinInput.scss index fd6897dde3..d46fdcf26e 100644 --- a/web/src/components/Form/FormFields/_PinInput.scss +++ b/web/src/components/Form/FormFields/_PinInput.scss @@ -28,17 +28,22 @@ } .active { - border: 1px solid $colors-black !important; + border: 2px solid $colors-black !important; } .error { border: 1px solid $colors-notifications-red !important; } - .group { + .leftGroup, + .rightGroup { padding: 1rem; & input { margin: 0 0.25rem; } } + + .leftGroup .error:first-child { + border: 2px solid $colors-notifications-red !important; + } } diff --git a/web/src/components/Form/factoryFields.js b/web/src/components/Form/factoryFields.js index ff935fd017..746ccfcb22 100644 --- a/web/src/components/Form/factoryFields.js +++ b/web/src/components/Form/factoryFields.js @@ -8,7 +8,6 @@ import DateField from './FormFields/DateField'; import DropdownDateField from './FormFields/DropdownDateField'; import CheckField from './FormFields/CheckField'; import EditableInputField from './FormFields/EditableInputField'; -import CaptchaField from './FormFields/Captcha'; import ToggleField from './FormFields/ToggleField'; import DumbField from './FormFields/DumbFieldForm'; import PinInput from './FormFields/PinInput'; @@ -29,8 +28,6 @@ const renderFields = (fields = {}, callback) => { }; switch (type) { - case 'captcha': - return ; case 'hidden': return ( (
diff --git a/web/src/components/Notification/NewOrder.js b/web/src/components/Notification/NewOrder.js index 386a3a4161..e4716c7ff8 100644 --- a/web/src/components/Notification/NewOrder.js +++ b/web/src/components/Notification/NewOrder.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React from 'react'; import EventListener from 'react-event-listener'; import { CURRENCY_PRICE_FORMAT } from 'config/constants'; import STRINGS from 'config/localizedStrings'; @@ -9,7 +9,6 @@ import { } from './Notification'; import { Button } from '../'; import { formatToCurrency } from 'utils/currency'; -import { RiskyTrade } from 'containers/QuickTrade/components/RiskyTrade'; const generateRows = ({ order, pairData }) => { const { type, side, price, size } = order; @@ -73,23 +72,6 @@ const NewOrderNotification = ({ coins, icons: ICONS, }) => { - const { order } = data; - - const getShowCoinRisky = () => { - - const {is_risky, code} = coins[order.symbol]; - if(order.side === "buy" && is_risky) { - const localRiskyItems = localStorage.getItem('riskyItems'); - const riskyItems = localRiskyItems ? JSON.parse(localRiskyItems) : {}; - const isNotWarn = !riskyItems[code]; - return isNotWarn; - } - - return false; - } - - const [showRisky, setShowRisky] = useState(getShowCoinRisky()); - const rows = generateRows(data); const onConfirmClick = () => { onConfirm(); @@ -103,33 +85,25 @@ const NewOrderNotification = ({ }; return ( - <> - {showRisky ? - ( - - - - ) - : ( - - - <> - - -
-
- -
- ) } + <> + + + <> + + +
+
+ +
); diff --git a/web/src/components/OtpForm/index.js b/web/src/components/OtpForm/index.js index 8b2f783c64..76b7994c05 100644 --- a/web/src/components/OtpForm/index.js +++ b/web/src/components/OtpForm/index.js @@ -52,29 +52,48 @@ class Form extends Component { error, onClickHelp, icons: ICONS, + isEnableOtpForm, } = this.props; const { formValues } = this.state; return (
- -
- {onClickHelp && ( - + - )} -
+
+ {STRINGS['OTP_FORM.INPUT_TEXT']} +
+ + ) : ( + + )} + {!isEnableOtpForm && ( +
+ {onClickHelp && ( + + )} +
+ )}
{renderFields(formValues, { @@ -83,12 +102,15 @@ class Form extends Component { handleSubmit, })}
-
- {STRINGS['OTP_FORM.OTP_FORM_INFO']} -
-
- {STRINGS['OTP_FORM.OTP_FORM_SUBNOTE_LINE_1']} - {STRINGS['OTP_FORM.OTP_FORM_SUBNOTE_LINE_2']} + {!isEnableOtpForm && ( +
+ {STRINGS['OTP_FORM.OTP_FORM_INFO']} +
+ )} +
+ {isEnableOtpForm + ? STRINGS['OTP_FORM.OTP_FORM_SUBNOTE_LINE_3'] + : `${STRINGS['OTP_FORM.OTP_FORM_SUBNOTE_LINE_1']} ${STRINGS['OTP_FORM.OTP_FORM_SUBNOTE_LINE_2']}`}
diff --git a/web/src/components/PriceChange/_PriceChange.scss b/web/src/components/PriceChange/_PriceChange.scss index 509b80a8b0..c9853446a9 100644 --- a/web/src/components/PriceChange/_PriceChange.scss +++ b/web/src/components/PriceChange/_PriceChange.scss @@ -63,6 +63,13 @@ } } } + + .markets-drop-down { + display: flex; + } + .markets-drop-down.down:before { + margin: 10% 0 0 0; + } } .hollaex-token-wrapper { diff --git a/web/src/components/QuickTrade/InputGroup.js b/web/src/components/QuickTrade/InputGroup.js index adcbf4f366..e5057affe8 100644 --- a/web/src/components/QuickTrade/InputGroup.js +++ b/web/src/components/QuickTrade/InputGroup.js @@ -125,7 +125,15 @@ class InputGroup extends React.PureComponent { open={isOpen} size="default" showSearch - filterOption={true} + filterOption={(input, option) => { + const coin = coins[option.value] || DEFAULT_COIN_DATA; + return ( + option.value.toLowerCase().indexOf(input.toLowerCase()) >= + 0 || + coin.fullname.toLowerCase().indexOf(input.toLowerCase()) >= + 0 + ); + }} className={ isOpen ? 'input-group__select_disabled' diff --git a/web/src/components/QuickTrade/_InputGroup.scss b/web/src/components/QuickTrade/_InputGroup.scss index fed47e1064..4b750084a0 100644 --- a/web/src/components/QuickTrade/_InputGroup.scss +++ b/web/src/components/QuickTrade/_InputGroup.scss @@ -1,6 +1,7 @@ .quick-trade-select-wrapper { .input-group__coin-icons-wrap { display: flex; + pointer-events: none; } } .input-group__container { diff --git a/web/src/components/QuickTrade/index.js b/web/src/components/QuickTrade/index.js index 9f08ea6387..4de9f6b236 100644 --- a/web/src/components/QuickTrade/index.js +++ b/web/src/components/QuickTrade/index.js @@ -237,6 +237,22 @@ const QuickTrade = ({ } }; + const addSecondsToExpiry = (expiry) => { + const expiryDate = new Date(expiry); + + if (!expiry || !moment(expiryDate).isBefore(moment())) { + return expiry; + } + + const dateObj = new Date(); + const SECONDS_TO_ADD = 20; + expiryDate.setTime(dateObj.getTime() + SECONDS_TO_ADD * 1000); + + const updatedExpiry = expiryDate.toISOString(); + + return updatedExpiry; + }; + const getQuote = ({ sourceAmount: spending_amount, targetAmount: receiving_amount, @@ -267,7 +283,7 @@ const QuickTrade = ({ }) => { setSpending(); setToken(token); - setExpiry(expiry); + setExpiry(addSecondsToExpiry(expiry)); setTargetAmount(receiving_amount); setSourceAmount(spending_amount); } diff --git a/web/src/components/Sidebar/_Sidebar.scss b/web/src/components/Sidebar/_Sidebar.scss index 5f90fddd79..94eb29fa69 100644 --- a/web/src/components/Sidebar/_Sidebar.scss +++ b/web/src/components/Sidebar/_Sidebar.scss @@ -18,8 +18,6 @@ $row--size: calc(#{$row--height} - #{$sidebar-sections-padding}); min-width: 3rem; } } -.cell-wrapper { -} .sidebar-bottom-wrapper { min-height: $bottom_bar--height; @@ -30,6 +28,18 @@ $row--size: calc(#{$row--height} - #{$sidebar-sections-padding}); padding: 0.5rem 0.5rem 0.8rem 0.5rem; justify-content: space-around; + .sidebar-bottom-button-account { + .sidebar-bottom-icon { + svg { + g { + .assets-icon-tab { + opacity: 0.5; + } + } + } + } + } + .sidebar-bottom-button { justify-content: space-between; flex-direction: column; @@ -45,7 +55,7 @@ $row--size: calc(#{$row--height} - #{$sidebar-sections-padding}); .quick-trade-tab-active, .assets-icon-tab, .onramp-icon { - fill: $base_top-bar-navigation_text_inactive; + fill: $colors-black; } //ToDo: tab-wallet0 @@ -54,9 +64,8 @@ $row--size: calc(#{$row--height} - #{$sidebar-sections-padding}); .trade-active-2, .chat-0, .quick-trade-tab-active, - .assets-icon-tab // - { - stroke: $base-top-bar-navigation_text_inactive; + .assets-icon-tab { + stroke: $colors-black; } .tab-wallet0, @@ -88,7 +97,7 @@ $row--size: calc(#{$row--height} - #{$sidebar-sections-padding}); } } .bottom-bar-text { - font-size: 10px; + font-size: 9px; text-align: center; margin-top: -17px; font-weight: lighter; @@ -181,69 +190,6 @@ $row--size: calc(#{$row--height} - #{$sidebar-sections-padding}); } } -@media screen and (max-width: 375px) { - .mobile-trade-wrapper { - .pro-trade-footer-icon { - height: 50%; - margin-top: 20% !important; - } - .quick-trade-footer-icon { - margin-top: 20% !important; - } - .quick-trade-field, - .pro-trade-field { - width: 65px !important; - height: 65px !important; - } - } - .sidebar-bottom-button-trade { - height: 130%; - width: 20% !important; - } -} - -@media screen and (max-width: 450px) and (min-width: 375px) { - .mobile-trade-wrapper { - width: 50%; - .pro-trade-footer-icon { - height: 50%; - margin-top: 18% !important; - } - .quick-trade-footer-icon { - height: 40%; - margin-top: 20% !important; - } - .quick-trade-field, - .pro-trade-field { - width: 75px !important; - height: 75px !important; - } - } - .sidebar-bottom-button-trade { - height: 135%; - width: 18% !important; - } -} - -@media screen and (max-width: 550px) and (min-width: 450px) { - .mobile-trade-wrapper { - width: 50%; - .quick-trade-field, - .pro-trade-field { - width: 85px !important; - height: 85px !important; - } - .pro-trade-footer-icon { - height: 40%; - margin-top: 25% !important; - } - .quick-trade-footer-icon { - height: 35%; - margin-top: 25% !important; - } - } -} - @media screen and (max-height: 600px) { .sidebar-bottom-icon { svg { @@ -252,12 +198,75 @@ $row--size: calc(#{$row--height} - #{$sidebar-sections-padding}); } } -@media screen and (max-width: 768px) { - .mobile-trade-wrapper { - .quick-trade-field, - .pro-trade-field { - width: 100px; - height: 100px; - } - } -} +// @media screen and (max-width: 375px) { +// .mobile-trade-wrapper { +// .pro-trade-footer-icon { +// height: 50%; +// margin-top: 20% !important; +// } +// .quick-trade-footer-icon { +// margin-top: 20% !important; +// } +// .quick-trade-field, +// .pro-trade-field { +// width: 65px !important; +// height: 65px !important; +// } +// } +// .sidebar-bottom-button-trade { +// height: 130%; +// width: 20% !important; +// } +// } + +// @media screen and (max-width: 450px) and (min-width: 375px) { +// .mobile-trade-wrapper { +// width: 50%; +// .pro-trade-footer-icon { +// height: 50%; +// margin-top: 18% !important; +// } +// .quick-trade-footer-icon { +// height: 40%; +// margin-top: 20% !important; +// } +// .quick-trade-field, +// .pro-trade-field { +// width: 75px !important; +// height: 75px !important; +// } +// } +// .sidebar-bottom-button-trade { +// height: 135%; +// width: 18% !important; +// } +// } + +// @media screen and (max-width: 550px) and (min-width: 450px) { +// .mobile-trade-wrapper { +// width: 50%; +// .quick-trade-field, +// .pro-trade-field { +// width: 85px !important; +// height: 85px !important; +// } +// .pro-trade-footer-icon { +// height: 40%; +// margin-top: 25% !important; +// } +// .quick-trade-footer-icon { +// height: 35%; +// margin-top: 25% !important; +// } +// } +// } + +// @media screen and (max-width: 768px) { +// .mobile-trade-wrapper { +// .quick-trade-field, +// .pro-trade-field { +// width: 100px; +// height: 100px; +// } +// } +// } diff --git a/web/src/config/constants.js b/web/src/config/constants.js index f01f2e77c4..ef887c41b9 100644 --- a/web/src/config/constants.js +++ b/web/src/config/constants.js @@ -197,13 +197,6 @@ export const EXPLORERS_ENDPOINT = (currency) => { export const BALANCE_ERROR = 'Insufficient balance to perform the order'; -export const DEFAULT_CAPTCHA_SITEKEY = - '6LeuOKoUAAAAAGVoZcSWXJH60GHt4crvIaNXn1YA'; // default recaptcha v3; // default recaptcha v3 - -export const CAPTCHA_TIMEOUT = process.env.REACT_APP_CAPTCHA_TIMEOUT - ? parseInt(process.env.REACT_APP_CAPTCHA_TIMEOUT, 10) - : 2000; - export const TIME_ZONE = process.env.REACT_APP_TIMEZONE || 'GMT'; export const TOKEN_EMAIL = 'token::email'; export const TOKEN_MAX_AGE = 23 * 60 * 60; diff --git a/web/src/config/icons/dark.js b/web/src/config/icons/dark.js index 226657c7b1..52301cc2b8 100644 --- a/web/src/config/icons/dark.js +++ b/web/src/config/icons/dark.js @@ -163,6 +163,7 @@ const nestedIcons = { COPY_NEW: '/assets/images/copy.svg', COPY_NOTIFICATION: '/assets/images/copy-icon-snack-notification.svg', ACCOUNT_LINE: '/assets/images/account.svg', + FOOTER_ACCOUNT_LINE: '/assets/images/account-summary-tab-01.svg', ACCOUNT_RECOVERY: '/assets/images/account-recovery.svg', BITCOIN_WALLET: '/assets/images/bitcoin-wallet.svg', CHECK_SENDING_BITCOIN: '/assets/images/check-sending-bitcoin.svg', @@ -217,8 +218,11 @@ const nestedIcons = { SIDEBAR_ACCOUNT_INACTIVE: '/assets/images/account_2-inactive.svg', SIDEBAR_POST_ACTIVE: '/assets/images/post-active.svg', SIDEBAR_TRADING_ACTIVE: '/assets/images/trade-active.svg', + FOOTER_TRADING_ACTIVE: + '/assets/images/pro-trade-markets-mobile-tab-01 copy-01.svg', SIDEBAR_QUICK_TRADING_ACTIVE: '/assets/images/quick-trade-tab-selected-01.svg', + FOOTER_QUICK_ACTIVE: '/assets/images/quick-trade-convert-mobile-tab-01.svg', SIDEBAR_QUICK_TRADING_INACTIVE: '/assets/images/quick-trade-tab-01-01.svg', SIDEBAR_ADMIN_DASH_ACTIVE: '/assets/images/admin-dash-icon.svg', ARROW_TRANSFER_HISTORY_ACTIVE: '/assets/images/arrow-trans-history.svg', @@ -240,6 +244,7 @@ const nestedIcons = { NOTE_KYC: '/assets/images/note-KYC.svg', SIDEBAR_HELP: '/assets/images/help-question-mark-icon.svg', + FOOTER_PLUGIN: '/assets/images/card-active-plugin-mobile-tab-01.svg', CONNECT_LOADING: '/assets/images/connect-loading.svg', FIAT_KYC: '/assets/images/fiat-kyc.svg', @@ -294,9 +299,8 @@ const nestedIcons = { WALLET_ARROW_DOWN: '/assets/images/deposit-arrow-down.svg', WALLET_ARROW_UP: '/assets/images/withdraw-arrow-up.svg', - FOOTERBAR_ASSETS_TRADE: '/assets/images/assets-list-bottom-nav-mobile.svg', - FOOTERBAR_HISTORY_TRADE: - '/assets/images/transaction-history-bottom-nav-mobile.svg', + FOOTERBAR_ASSETS_TRADE: '/assets/images/asset-prices-mobile-tab-01.svg', + WALLET_FOOTER: '/assets/images/wallet-mobile-tab-02-01 (1).svg', }; const icons = flatten(nestedIcons, options); diff --git a/web/src/config/icons/static.js b/web/src/config/icons/static.js index ddeb098e84..0f91d83ced 100644 --- a/web/src/config/icons/static.js +++ b/web/src/config/icons/static.js @@ -191,8 +191,9 @@ const icons = { REVOKE_KEY: '/assets/images/revoke-key-red-slash.svg', SMALL_WHITE_KEY: '/assets/images/small-white-key.svg', TRANSPARENT_KEY: '/assets/images/transperent-key.svg', - FOOTERBAR_PRO_TRADE: '/assets/images/pro-trade-bottom-nav-mobile.svg', - FOOTERBAR_QUICK_TRADE: '/assets/images/quick-trade-tab-white.svg', + GOOGLE_AUTHENTICATOR: '/assets/images/google-auth-icon.svg', + GOOGLE_PLAY: '/assets/images/google-play.svg', + APP_STORE: '/assets/images/appstore-apple_logo_black.svg', }; export default icons; diff --git a/web/src/config/lang/de.json b/web/src/config/lang/de.json index 53ffb0ebdf..2e9fae05d0 100644 --- a/web/src/config/lang/de.json +++ b/web/src/config/lang/de.json @@ -228,7 +228,9 @@ "ERROR_INVALID": "Ungültiger OTP-Code", "OTP_FORM_INFO": "Geben Sie Ihren 6-stelligen Code ein, um fortzufahren", "OTP_FORM_SUBNOTE_LINE_1": "Ihr Code wird auch als Zwei-Faktor-Authentifikator (2FA) oder OTP-Code bezeichnet.", - "OTP_FORM_SUBNOTE_LINE_2": "Wenn Sie Ihren Code verloren haben, wenden Sie sich bitte an den Support." + "OTP_FORM_SUBNOTE_LINE_2": "Wenn Sie Ihren Code verloren haben, wenden Sie sich bitte an den Support.", + "OTP_FORM_SUBNOTE_LINE_3": "(To add a new account to Google Authenticator, click the '+' button. You can either scan the provided QR code in the previous step or manually enter the key to add your account to the app.)", + "INPUT_TEXT": "Input the 6-digit code from your phone's Google Authenticator app:" }, "QUICK_TRADE_COMPONENT": { "TITLE": "Quick", @@ -624,7 +626,8 @@ "INTERNAL": "intern", "BLOCKCHAIN": "Blockchain", "STAKE": "Einsatz", - "REFERRAL": "Verweisung" + "REFERRAL": "Verweisung", + "QUICK_TRADE_TOOLTIP": "Order executed through convert system" }, "ACCOUNT_SECURITY": { "OTP": { @@ -647,14 +650,45 @@ "MESSAGE_4": "Sie können diesen Code sicher speichern, um Ihren 2FA wiederherzustellen, falls Sie Ihr Mobiltelefon in Zukunft wechseln oder verlieren.", "MESSAGE_5": "Anleitung", "INPUT": "Einmaliges Passwort (OTP) eingeben", - "WARNING": "Wir empfehlen Ihnen dringend, die 2-Faktor-Authentifizierung (2FA) einzurichten. Dadurch wird die Sicherheit Ihrer Gelder erheblich erhöht.", + "WARNING": "Wir {0} {1}", "ENABLE": "Aktivieren der Zwei-Faktoren-Authentifizierung", - "DISABLE": "Zwei-Faktor-Authentifizierung deaktivieren" + "DISABLE": "Zwei-Faktor-Authentifizierung deaktivieren", + "STRONGLY_RECOMMEND": "sehr zu empfehlen", + "WARNING_CONTENT": "dass Sie die Zwei-Faktor-Authentifizierung aktivieren, um die Sicherheit Ihres Kontos zu erhöhen und Ihr Geld zu schützen" }, "FORM": { "PLACEHOLDER": "Geben Sie Ihr von Google Authenticator bereitgestelltes OTP ein.", "BUTTON": "2FA aktivieren" - } + }, + "DOWNLOAD_APP": "Download and install", + "GOOGLE_AUTHENTICATOR": "Google Authenticator:", + "DOWNLOAD_FROM": "Download from", + "GOOGLE_PLAY": "GooglePlay", + "APPLE": "App Store", + "APP_INFO": "Google Authenticator will generate random codes that are necessary for sensitive actions, such as logging in and withdrawing funds.", + "DONT_UNINSTALL": "Bitte deinstallieren Sie die App nicht.", + "INFO_UNINSTALL": "Bei versehentlichem Verlust des Telefons oder unbeabsichtigter Deinstallation kann es zu Verzögerungen bei der Wiederherstellung des Zugriffs auf Ihr Konto kommen. In solchen Fällen müssen Sie sich an unser Supportteam wenden, um Hilfe zu erhalten.", + "BACK": "ZURÜCK", + "PROCEED": "FORTFAHREN", + "NEXT": "Nächste", + "NOTE": "Hinweis: {0} {1}", + "MANUEL_DESCRIPTION": "Bitte geben Sie den manuellen 2FA-Seed aus dem vorherigen Schritt ein.", + "MANUEL_DESCRIPTION_2": "Bitte notieren und bewahren Sie die oben stehende manuelle Bestätigung sorgfältig auf.", + "MANUEL_DESCRIPTION_3": "Schlüssel, da dieser zur Kontowiederherstellung verwendet werden kann.", + "MANUEL_KEY": "Manueller 2FA-Seed", + "MANUEL_PLACEHOLDER": "Geben Sie Ihren 2FA-Seed ein", + "MANUEL_WARNING": "(Stellen Sie sicher, dass Sie es aufgeschrieben und für die zukünftige Verwendung sicher aufbewahrt haben.)", + "MANUEL_ERROR_1": "Bitte geben Sie den manuellen 2FA-Seed ein", + "MANUEL_ERROR_2": "Bitte geben Sie den manuellen 2FA-Seed aus dem vorherigen Schritt ein", + "ACCOUNT_SECURED": "Konto gesichert", + "2FA_ENABLED": "2FA AKTIVIERT", + "2FA_DISABLED": "2FA DEAKTIVIERT", + "2FA_CONTENT_ONE": "{0} {1} wird aktiviert.", + "2FA_CONTENT_TWO": "(Zwei-Faktor-Authentifizierung) bietet eine zusätzliche Schutzebene zum Schutz Ihrer Gelder. Sobald diese Funktion aktiviert ist, müssen Sie bei jedem Einloggen oder Abheben von Guthaben einen Bestätigungscode von Ihrem Telefon aus eingeben. Diese Funktion erhöht die Sicherheit Ihres Kontos erheblich", + "2FA_CONTENT_THREE": "(Zwei-Faktor-Authentifizierung) bietet eine zusätzliche Schutzebene zum Schutz Ihrer Gelder", + "2FA_CONTENT_FOUR": "Um 2FA zu deaktivieren, müssen Sie Ihren 2FA-Code (OTP) eingeben", + "INPUT_SIX_DIGIT_CODE": "Geben Sie den 6-stelligen Code aus der Google Authenticator-App Ihres Telefons ein:", + "INPUT_CONTENT": "(Um ein neues Konto zu Google Authenticator hinzuzufügen, klicken Sie auf die Schaltfläche „+“. Sie können entweder den im vorherigen Schritt bereitgestellten QR-Code scannen oder den Schlüssel manuell eingeben, um Ihr Konto zur App hinzuzufügen.)" }, "CHANGE_PASSWORD": { "TITLE": "Passwort ändern", @@ -1689,7 +1723,8 @@ "QUICK_TRADE": "Schneller Handel", "SUBTITLE": "Um die Kauf- und Verkaufspreise für {0} anzuzeigen, besuchen Sie {1}", "FOOTER": "Für alle über Quick Trade ausgeführten Geschäfte fallen Market-Taker-Handelsgebühren an." - } + }, + "PRO_TRADE": "{0} MARKT ANZEIGEN" }, "ACCORDIAN": { "ACCORDIAN_ASSETS": "Alle Wallet-Assets", @@ -1872,8 +1907,14 @@ "SELECT_END_DATE": "Wählen Sie das Enddatum aus", "CUSTOM": "Brauch", "BACK_CUSTOM": "ZURÜCK", - "PROOCED_CUSTOM": "PROOCED" + "PROOCED_CUSTOM": "PROOCED", + "PL_DAYS": "Tages-Gewinn- und Verlustrechnung", + "VIEW_MORE": "Mehr anzeigen {0}", + "SHOW_ASSET_BREAKDOWN": "Vermögensaufschlüsselung anzeigen", + "PL_SUMMARY": "Gewinn- und Verlustübersicht", + "BALANCE_HISTORY": "Kontostandverlauf" }, "ASSET_INFO": "Asset-Informationen", - "TAKER_FEES_APPLIED": "Es fallen Taker-Gebühren an" + "TAKER_FEES_APPLIED": "Es fallen Taker-Gebühren an", + "CANCEL_WITHDRAWAL_ADDRESS": "Auszahlungsadresse" } \ No newline at end of file diff --git a/web/src/config/lang/en.json b/web/src/config/lang/en.json index dc73dadcae..b504854bdf 100644 --- a/web/src/config/lang/en.json +++ b/web/src/config/lang/en.json @@ -4,8 +4,9 @@ "LOGOUT_CONFIRM_TEXT": "Are you sure you want to logout?", "ADD_TRADING_PAIR": "Select a market", "TRADE_TOOLS": "Tools", - "CANCEL_BASE_WITHDRAWAL": "Cancel {0} Withdrawal", - "CANCEL_WITHDRAWAL": "Cancel Withdrawal", + "CANCEL_BASE_WITHDRAWAL": "Cancel {0}", + "CANCEL_WITHDRAWAL_ADDRESS": "Withdrawal Address", + "CANCEL_WITHDRAWAL": "Cancel", "CANCEL_WITHDRAWAL_POPUP_CONFIRM": "Do you want to cancel your pending withdrawal of:", "TIMESTAMP_FORMAT": "YYYY/MM/DD HH:mm:ss", "DEFAULT_TIMESTAMP_FORMAT": "DD, MMMM, YYYY", @@ -183,11 +184,13 @@ "OTP_FORM_INFO": "Enter your 6-digit code to continue", "OTP_FORM_SUBNOTE_LINE_1": "Your code is also known as a two-factor authenticator (2FA) or OTP code.", "OTP_FORM_SUBNOTE_LINE_2": "If you've lost your code, please reach out to support.", + "OTP_FORM_SUBNOTE_LINE_3": "(To add a new account to Google Authenticator, click the '+' button. You can either scan the provided QR code in the previous step or manually enter the key to add your account to the app.)", "OTP_LABEL": "OTP Code", "OTP_PLACEHOLDER": "Enter the authentication code", "OTP_TITLE": "Authenticator Code", "OTP_BUTTON": "submit", - "ERROR_INVALID": "Invalid OTP Code" + "ERROR_INVALID": "Invalid OTP Code", + "INPUT_TEXT": "Input the 6-digit code from your phone's Google Authenticator app:" }, "EMAIL_CODE_FORM": { "TITLE": "Input you security codes", @@ -201,7 +204,7 @@ "OTP_PLACEHOLDER": "Enter your 6-digit two-factor authentication code" }, "QUICK_TRADE_COMPONENT": { - "TITLE": "Quick Trade", + "TITLE": "Convert", "BUTTON": "Review Quick Trade", "INFO": "Fastest and simplest way to trade your crypto", "CHANGE_TEXT": "change", @@ -605,30 +608,52 @@ "INTERNAL": "internal", "BLOCKCHAIN": "blockchain", "STAKE": "stake", - "REFERRAL": "referral" + "REFERRAL": "referral", + "QUICK_TRADE_TOOLTIP": "Order executed through convert system" }, "ACCOUNT_SECURITY": { "TITLE_TEXT": "Adjust the security settings for your account. From Two-factor authentication, password, API keys and other security related functions.", "OTP": { + "DOWNLOAD_APP": "Download and install", + "GOOGLE_AUTHENTICATOR": "Google Authenticator:", + "DOWNLOAD_FROM": "Download from", + "GOOGLE_PLAY": "GooglePlay", + "APPLE": "App Store", + "APP_INFO": "Google Authenticator will generate random codes that are necessary for sensitive actions, such as logging in and withdrawing funds.", + "DONT_UNINSTALL": "Please do not uninstall the app.", + "INFO_UNINSTALL": "In the event of accidental phone loss or unintentional uninstallation, you may experience delays in regaining access to your account. In such cases, you will need to contact our support team for assistance.", "TITLE": "2FA", "OTP_ENABLED": "otp enabled", "OTP_DISABLED": "PLEASE TURN ON 2FA", + "BACK": "BACK", + "PROCEED": "PROCEED", + "NEXT": "Next", + "NOTE": "Note: {0} {1}", + "MANUEL_DESCRIPTION": "Please enter the manual 2FA seed from the previous step.", + "MANUEL_DESCRIPTION_2": "Please carefully write down and store the above manual verification", + "MANUEL_DESCRIPTION_3": "key as it can be used for account recovery.", + "MANUEL_KEY": "Manual 2FA seed", + "MANUEL_PLACEHOLDER": "Enter your 2FA seed", + "MANUEL_WARNING": "(Make sure you have written it down and stored it safely for future use.)", + "MANUEL_ERROR_1": "Please enter the manual 2FA seed", + "MANUEL_ERROR_2": "Please input the manual 2FA seed from the previous step", "ENABLED_TEXTS": { "TEXT_1": "Require OTP when logging in", "TEXT_2": "Require OTP when withdrawing funds" }, "DIALOG": { - "SUCCESS": "You have successfully activated 2FA", - "REVOKE": "You have successfully deactivated 2FA" + "SUCCESS": "You've successfully enabled 2FA", + "REVOKE": "You've disabled 2FA" }, "CONTENT": { - "TITLE": "Activate Two-Factor Authentication", - "MESSAGE_1": "Scan", - "MESSAGE_2": "Scan the qrcode below with Google Authenticator or Authy to automatically setup two-factor authentication in your device.", + "TITLE": "2FA Setup", + "MESSAGE_1": "Scan the QR code with Google Authenticator app below :", "MESSAGE_3": "If you have problems scanning this, you can manually enter the code below", "MESSAGE_4": "You must store this code securely to recover your 2FA in case you change or lose your mobile phone in future.", - "MESSAGE_5": "Manual", - "WARNING": "We highly recommend you set up two-factor authentication (2FA). Doing so will greatly increase the security of your funds.", + "MESSAGE_5": "Manual 2FA seed:", + "WARNING": "We {0} {1}", + "STRONGLY_RECOMMEND": "strongly recommend", + "WARNING_CONTENT": "that you enable two-factor authentication to enhance the security of your account and protect your funds", "ENABLE": "Enable Two-Factor Authentication", "DISABLE": "Disable Two-Factor Authentication", "INPUT": "Please enter your OTP" @@ -636,7 +661,16 @@ "FORM": { "PLACEHOLDER": "Enter your OTP provided by Google Authenticator.", "BUTTON": "Enable 2FA" - } + }, + "ACCOUNT_SECURED": "Account Secured", + "2FA_ENABLED": "2FA ENABLED", + "2FA_DISABLED": "2FA DISABLED", + "2FA_CONTENT_ONE": "Activating {0} {1} .", + "2FA_CONTENT_TWO": "(Two-Factor Authentication) adds an extra layer of protection to safeguard your funds. Once enabled, you will be required to enter a verification code from your phone each time you log in or withdraw assets. This feature significantly enhances the security of your account", + "2FA_CONTENT_THREE": "(Two-Factor Authentication) adds an extra layer of protection to safeguard your funds", + "2FA_CONTENT_FOUR": "To disable 2FA you will need to enter your 2FA (OTP) code", + "INPUT_SIX_DIGIT_CODE": "Input the 6-digit code from your phone's Google Authenticator app:", + "INPUT_CONTENT": "(To add a new account to Google Authenticator, click the '+' button. You can either scan the provided QR code in the previous step or manually enter the key to add your account to the app.)" }, "CHANGE_PASSWORD": { "TITLE": "Password", @@ -1530,6 +1564,7 @@ "WALLET_PERFORMANCE_TITLE": "Balance performance", "WALLET_PERFORMANCE_DESCRIPTION": "Your wallet's balance performance over time and the asset breakdown. Click the dates below to see your wallet's performance on that day.", "PL_7_DAY": "7 Day P&L", + "PL_DAYS": "Day P&L", "EST_TOTAL_BALANCE": "Est. Total Balance", "WEEK": "week", "MONTH": "month", @@ -1541,13 +1576,17 @@ "ASSET_NAME": "Asset name", "BALANCE_AMOUNT": "Balance Amount", "VALUE": "Value", - "PERFORMANCE_TREND": "7 Day Performance Trend", + "PERFORMANCE_TREND": "7 Day Balance Trend", "CUSTOM_DATE_DESCRIPTION": "Select specific dates to fetch your history data (date difference cannot go further than 3 months)", "SELECT_START_DATE": "Select start date", "SELECT_END_DATE": "Select end date", "CUSTOM": "Custom", "BACK_CUSTOM": "BACK", - "PROOCED_CUSTOM": "PROOCED" + "PROOCED_CUSTOM": "PROOCED", + "VIEW_MORE": "View More {0}", + "SHOW_ASSET_BREAKDOWN": "Show Asset Breakdown", + "PL_SUMMARY": "P&L Summary", + "BALANCE_HISTORY": "Balance History" }, "ASSET_INFO": "Asset info", "TAKER_FEES_APPLIED": "Taker fees are applied", @@ -1557,8 +1596,8 @@ "GO_BACK": "Go back", "ASSETS_INFO": "Below are available assets on the platform.", "ASSETS_INFO_DETAIL": "You can click an asset on the list below to learn more.", - "QUICK_TRADE": "QUICK TRADE", - "PRO_TRADE": "PRO TRADE", + "QUICK_TRADE": "CONVERT", + "PRO_TRADE": "MARKETS", "MARKETS": "MARKETS", "WALLET": "WALLET", "PRICE_SOURCE": "Price source", @@ -1581,7 +1620,8 @@ "MORE": "More", "INFO": "info", "TRADE": "TRADE", - "QUICK_TRADE": "QUICK TRADE {0}", + "QUICK_TRADE": "CONVERT {0}", + "PRO_TRADE": "VIEW {0} MARKET", "PRICES": { "TITLE": "Prices", "QUICK_TRADE": "Quick Trade", diff --git a/web/src/config/lang/es.json b/web/src/config/lang/es.json index 41aa883c21..be6e6fdff9 100644 --- a/web/src/config/lang/es.json +++ b/web/src/config/lang/es.json @@ -236,7 +236,9 @@ "ERROR_INVALID": "Código OTP Inválido", "OTP_FORM_INFO": "Ingresa tu código de 6 dígitos para continuar", "OTP_FORM_SUBNOTE_LINE_1": "Su código también se conoce como autenticador de dos factores (2FA) o código OTP.", - "OTP_FORM_SUBNOTE_LINE_2": "Si ha perdido su código, comuníquese con el soporte." + "OTP_FORM_SUBNOTE_LINE_2": "Si ha perdido su código, comuníquese con el soporte.", + "OTP_FORM_SUBNOTE_LINE_3": "(Para agregar una nueva cuenta a Google Authenticator, haga clic en el botón '+'. Puede escanear el código QR proporcionado en el paso anterior o ingresar manualmente la clave para agregar su cuenta a la aplicación).", + "INPUT_TEXT": "Ingresa el código de 6 dígitos de la aplicación Google Authenticator de tu teléfono:" }, "QUICK_TRADE_COMPONENT": { "TITLE": "Rápido", @@ -632,7 +634,8 @@ "INTERNAL": "interno", "BLOCKCHAIN": "cadena de bloques", "STAKE": "apostar", - "REFERRAL": "remisión" + "REFERRAL": "remisión", + "QUICK_TRADE_TOOLTIP": "Orden ejecutada a través del sistema de conversión." }, "CEFI_STAKE": { "STAKE_POOL_TITLE": "Piscinas de Staking de CeFi Locales", @@ -709,7 +712,7 @@ "MESSAGE_4": "Puede almacenar este código de forma segura para recuperar su 2FA en caso de que cambie o pierda su teléfono móvil en el futuro.", "MESSAGE_5": "Manual", "INPUT": "Introducir contraseña de una-vez (OTP)", - "WARNING": "Le recomendamos encarecidamente que establezca una autenticación de dos factores (2FA). Haciendo esto aumentará enormemente la seguridad de sus fondos.", + "WARNING": "Nosotros {0} {1}", "ENABLE": "Habilitar la autenticación de dos factores", "DISABLE": "Desactivar la autenticación de dos factores", "SECRET_1": "Enter yor secret key", @@ -718,12 +721,43 @@ "INPUT_1": "Secret Key", "TITLE_2": "Enter One-Time Password (OTP)", "MESSAGE_6": "Please enter your 6-digit one-time password below.", - "INPUT_2": "One-Time Password (OTP)" + "INPUT_2": "One-Time Password (OTP)", + "STRONGLY_RECOMMEND": "fuertemente recomendado", + "WARNING_CONTENT": "que habilite la autenticación de dos factores para mejorar la seguridad de su cuenta y proteger sus fondos" }, "FORM": { "PLACEHOLDER": "Introducir su OTP proporcionado por Google Authenticator.", "BUTTON": "Activar 2FA" - } + }, + "DOWNLOAD_APP": "Descargar e instalar", + "GOOGLE_AUTHENTICATOR": "Autenticador de Google:", + "DOWNLOAD_FROM": "Descargar desde", + "GOOGLE_PLAY": "Google Play", + "APPLE": "Tienda de aplicaciones", + "APP_INFO": "Google Authenticator generará códigos aleatorios que son necesarios para acciones sensibles, como iniciar sesión y retirar fondos.", + "DONT_UNINSTALL": "No desinstale la aplicación.", + "INFO_UNINSTALL": "En caso de pérdida accidental del teléfono o desinstalación involuntaria, es posible que experimente demoras para recuperar el acceso a su cuenta. En tales casos, deberá comunicarse con nuestro equipo de soporte para obtener ayuda.", + "BACK": "ATRÁS", + "PROCEED": "PROCEDER", + "NEXT": "Próximo", + "NOTE": "Nota: {0} {1}", + "MANUEL_DESCRIPTION": "Ingrese la semilla 2FA manual del paso anterior.", + "MANUEL_DESCRIPTION_2": "Anote y guarde cuidadosamente la verificación manual anterior.", + "MANUEL_DESCRIPTION_3": "clave ya que se puede utilizar para la recuperación de la cuenta.", + "MANUEL_KEY": "Semilla manual 2FA", + "MANUEL_PLACEHOLDER": "Ingrese su semilla 2FA", + "MANUEL_WARNING": "(Asegúrese de haberlo escrito y guardado de forma segura para usarlo en el futuro).", + "MANUEL_ERROR_1": "Por favor ingrese la semilla 2FA manual", + "MANUEL_ERROR_2": "Ingrese la semilla 2FA manual del paso anterior.", + "ACCOUNT_SECURED": "Cuenta asegurada", + "2FA_ENABLED": "2FA HABILITADO", + "2FA_DISABLED": "2FA DESHABILITADO", + "2FA_CONTENT_ONE": "Activando {0} {1} .", + "2FA_CONTENT_TWO": "(Autenticación de dos factores) agrega una capa adicional de protección para salvaguardar sus fondos. Una vez habilitado, se le pedirá que ingrese un código de verificación desde su teléfono cada vez que inicie sesión o retire activos. Esta característica mejora significativamente la seguridad de su cuenta.", + "2FA_CONTENT_THREE": "(Autenticación de dos factores) agrega una capa adicional de protección para salvaguardar sus fondos", + "2FA_CONTENT_FOUR": "Para desactivar 2FA, deberá ingresar su código 2FA (OTP)", + "INPUT_SIX_DIGIT_CODE": "Ingresa el código de 6 dígitos de la aplicación Google Authenticator de tu teléfono:", + "INPUT_CONTENT": "(Para agregar una nueva cuenta a Google Authenticator, haga clic en el botón '+'. Puede escanear el código QR proporcionado en el paso anterior o ingresar manualmente la clave para agregar su cuenta a la aplicación)." }, "CHANGE_PASSWORD": { "TITLE": "Cambiar Contraseña", @@ -1703,7 +1737,12 @@ "SELECT_END_DATE": "Seleccionar fecha de finalización", "CUSTOM": "Costumbre", "BACK_CUSTOM": "ATRÁS", - "PROOCED_CUSTOM": "PROCEDIDO" + "PROOCED_CUSTOM": "PROCEDIDO", + "PL_DAYS": "PyG del día", + "VIEW_MORE": "Ver más {0}", + "SHOW_ASSET_BREAKDOWN": "Mostrar desglose de activos", + "PL_SUMMARY": "Resumen de pérdidas y ganancias", + "BALANCE_HISTORY": "Historial de saldo" }, "ASSET_INFO": "Información del activo", "TAKER_FEES_APPLIED": "Se aplican tarifas al tomador", @@ -1746,7 +1785,8 @@ }, "LIST": "¿Quiere enumerar sus activos digitales?", "WEBSITE": "Sitio web", - "EXPLORER": "Explorador" + "EXPLORER": "Explorador", + "PRO_TRADE": "VER {0} MERCADO" }, "ACCORDIAN": { "ACCORDIAN_ASSETS": "Todos los activos de la billetera", @@ -1878,5 +1918,6 @@ "CONFIRM": "CONFIRMAR" } } - } + }, + "CANCEL_WITHDRAWAL_ADDRESS": "Dirección de retiro" } \ No newline at end of file diff --git a/web/src/config/lang/fa.json b/web/src/config/lang/fa.json index 33669dd859..acbc51dbef 100644 --- a/web/src/config/lang/fa.json +++ b/web/src/config/lang/fa.json @@ -581,7 +581,15 @@ "TEXT_DOWNLOAD": "دانلود تاریخچه", "TRADES": "خریدو فروش", "DEPOSITS": "واریزی ها", - "WITHDRAWALS": "برداشت ها" + "WITHDRAWALS": "برداشت ها", + "ORDERID": "سفارش ID", + "TRIGGER_STOP_PRICE": "قیمت هشدار", + "TIME_OF_LAST_TRADE": "تاریخ معامله", + "INTERNAL": "داخلی", + "BLOCKCHAIN": "بلاکچین", + "STAKE": "استیک", + "REFERRAL": "رفرال", + "QUICK_TRADE_TOOLTIP": "سفارش توسط سیسقم تبدیل انجام شده" }, "ACCOUNT_SECURITY": { "TITLE_TEXT": "Adjust the security settings for your account. From Two-factor authentication, password, API keys and other security related functions. ", diff --git a/web/src/config/lang/fr.json b/web/src/config/lang/fr.json index 28c718c667..c9299c2bc3 100644 --- a/web/src/config/lang/fr.json +++ b/web/src/config/lang/fr.json @@ -232,7 +232,9 @@ "ERROR_INVALID": "Code OTP invalide", "OTP_FORM_INFO": "Entrez votre code à 6 chiffres pour continuer", "OTP_FORM_SUBNOTE_LINE_1": "Votre code est également appelé code d’authentification à deux facteurs (2FA) ou code OTP.", - "OTP_FORM_SUBNOTE_LINE_2": "Si vous avez perdu votre code, veuillez contacter l'assistance." + "OTP_FORM_SUBNOTE_LINE_2": "Si vous avez perdu votre code, veuillez contacter l'assistance.", + "OTP_FORM_SUBNOTE_LINE_3": "(Pour ajouter un nouveau compte à Google Authenticator, cliquez sur le bouton « + ». Vous pouvez soit scanner le code QR fourni à l'étape précédente, soit saisir manuellement la clé pour ajouter votre compte à l'application.)", + "INPUT_TEXT": "Saisissez le code à 6 chiffres de l'application Google Authenticator de votre téléphone :" }, "QUICK_TRADE_COMPONENT": { "TITLE": "Quick Trade", @@ -628,7 +630,8 @@ "INTERNAL": "interne", "BLOCKCHAIN": "chaîne de blocs", "STAKE": "miser", - "REFERRAL": "référence" + "REFERRAL": "référence", + "QUICK_TRADE_TOOLTIP": "Ordre exécuté via le système de conversion" }, "ACCOUNT_SECURITY": { "TITLE_TEXT": "Ajustez les paramètres de sécurité de votre compte. De l'authentification à deux facteurs au mot de passe, en passant par les clés API et d'autres fonctions liées à la sécurité.", @@ -652,7 +655,7 @@ "MESSAGE_4": "Vous pouvez sauvegarder ce code en toute sécurité pour récupérer votre 2FA au cas où vous changeriez ou perdriez votre téléphone portable.", "MESSAGE_5": "Manuellement", "INPUT": "Entrez votre One-Time Password (OTP)", - "WARNING": "Nous vous recommandons vivement de configurer l'authentification à 2 facteurs (2FA). Cela augmentera considérablement la sécurité de vos fonds.", + "WARNING": "Nous {0} {1}", "ENABLE": "Activer l'authentification à deux facteurs ", "DISABLE": "Désactiver l'authentification à deux facteurs", "SECRET_1": "Entrez votre clé secrète", @@ -661,12 +664,43 @@ "INPUT_1": "Clé Secrète", "TITLE_2": "Entrez votre One-Time Password (OTP)", "MESSAGE_6": "Veuillez saisir votre mot de passe unique à 6 chiffres ci-dessous.", - "INPUT_2": "One-Time Password (OTP)" + "INPUT_2": "One-Time Password (OTP)", + "STRONGLY_RECOMMEND": "fortement recommandé", + "WARNING_CONTENT": "que vous activez l'authentification à deux facteurs pour améliorer la sécurité de votre compte et protéger vos fonds" }, "FORM": { "PLACEHOLDER": "Saisissez votre OTP fourni par Google Authenticator.", "BUTTON": "Activer 2FA" - } + }, + "DOWNLOAD_APP": "Télécharger et installer", + "GOOGLE_AUTHENTICATOR": "Authentificateur Google :", + "DOWNLOAD_FROM": "Télécharger à partir de", + "GOOGLE_PLAY": "Jeu de Google", + "APPLE": "Magasin d'applications", + "APP_INFO": "Google Authenticator générera des codes aléatoires nécessaires aux actions sensibles, telles que la connexion et le retrait de fonds.", + "DONT_UNINSTALL": "Veuillez ne pas désinstaller l'application.", + "INFO_UNINSTALL": "En cas de perte accidentelle de téléphone ou de désinstallation involontaire, vous pourriez rencontrer des retards pour retrouver l'accès à votre compte. Dans de tels cas, vous devrez contacter notre équipe d’assistance pour obtenir de l’aide.", + "BACK": "DOS", + "PROCEED": "PROCÉDER", + "NEXT": "Suivant", + "NOTE": "Remarque : {0} {1}", + "MANUEL_DESCRIPTION": "Veuillez saisir la graine 2FA manuelle de l'étape précédente.", + "MANUEL_DESCRIPTION_2": "Veuillez noter et conserver soigneusement la vérification manuelle ci-dessus", + "MANUEL_DESCRIPTION_3": "clé car elle peut être utilisée pour la récupération de compte.", + "MANUEL_KEY": "Graine 2FA manuelle", + "MANUEL_PLACEHOLDER": "Entrez votre graine 2FA", + "MANUEL_WARNING": "(Assurez-vous de l'avoir noté et stocké en toute sécurité pour une utilisation future.)", + "MANUEL_ERROR_1": "Veuillez saisir la graine 2FA manuelle", + "MANUEL_ERROR_2": "Veuillez saisir la graine 2FA manuelle de l'étape précédente", + "ACCOUNT_SECURED": "Compte sécurisé", + "2FA_ENABLED": "2FA ACTIVÉ", + "2FA_DISABLED": "2FA DÉSACTIVÉ", + "2FA_CONTENT_ONE": "Activation de {0} {1} .", + "2FA_CONTENT_TWO": "(Authentification à deux facteurs) ajoute une couche de protection supplémentaire pour protéger vos fonds. Une fois activé, vous devrez saisir un code de vérification depuis votre téléphone chaque fois que vous vous connecterez ou retirerez des actifs. Cette fonctionnalité améliore considérablement la sécurité de votre compte", + "2FA_CONTENT_THREE": "(Authentification à deux facteurs) ajoute une couche de protection supplémentaire pour protéger vos fonds", + "2FA_CONTENT_FOUR": "Pour désactiver 2FA, vous devrez entrer votre code 2FA (OTP)", + "INPUT_SIX_DIGIT_CODE": "Saisissez le code à 6 chiffres de l'application Google Authenticator de votre téléphone :", + "INPUT_CONTENT": "(Pour ajouter un nouveau compte à Google Authenticator, cliquez sur le bouton « + ». Vous pouvez soit scanner le code QR fourni à l'étape précédente, soit saisir manuellement la clé pour ajouter votre compte à l'application.)" }, "CHANGE_PASSWORD": { "TITLE": "Changer le mot de passe", @@ -1709,7 +1743,12 @@ "SELECT_END_DATE": "Sélectionnez la date de fin", "CUSTOM": "Coutume", "BACK_CUSTOM": "DOS", - "PROOCED_CUSTOM": "PROCÉDÉ" + "PROOCED_CUSTOM": "PROCÉDÉ", + "PL_DAYS": "Jour P&L", + "VIEW_MORE": "Voir plus {0}", + "SHOW_ASSET_BREAKDOWN": "Afficher la répartition des actifs", + "PL_SUMMARY": "Résumé des résultats", + "BALANCE_HISTORY": "Historique du solde" }, "ASSET_INFO": "Informations sur l'actif", "TAKER_FEES_APPLIED": "Des frais de preneur sont appliqués", @@ -1752,7 +1791,8 @@ }, "LIST": "Vous souhaitez lister vos actifs numériques ?", "WEBSITE": "Site web", - "EXPLORER": "Explorateur" + "EXPLORER": "Explorateur", + "PRO_TRADE": "VOIR {0} MARCHÉ" }, "ACCORDIAN": { "ACCORDIAN_ASSETS": "Tous les actifs du portefeuille", @@ -1890,5 +1930,6 @@ "CONFIRM": "CONFIRMER" } } - } + }, + "CANCEL_WITHDRAWAL_ADDRESS": "Adresse de retrait" } \ No newline at end of file diff --git a/web/src/config/lang/id.json b/web/src/config/lang/id.json index 57dbc8808a..5a2d5c9d59 100644 --- a/web/src/config/lang/id.json +++ b/web/src/config/lang/id.json @@ -227,7 +227,9 @@ "ERROR_INVALID": "Kode OTP yang tidak valid", "OTP_FORM_INFO": "Masukkan kode 6 digit Anda untuk melanjutkan", "OTP_FORM_SUBNOTE_LINE_1": "Kode Anda juga dikenal sebagai autentikator dua faktor (2FA) atau kode OTP.", - "OTP_FORM_SUBNOTE_LINE_2": "Jika Anda kehilangan kode, hubungi dukungan." + "OTP_FORM_SUBNOTE_LINE_2": "Jika Anda kehilangan kode, hubungi dukungan.", + "OTP_FORM_SUBNOTE_LINE_3": "(Untuk menambahkan akun baru ke Google Authenticator, klik tombol '+'. Anda dapat memindai kode QR yang diberikan pada langkah sebelumnya atau memasukkan kunci secara manual untuk menambahkan akun Anda ke aplikasi.)", + "INPUT_TEXT": "Masukkan kode 6 digit dari aplikasi Google Authenticator ponsel Anda:" }, "QUICK_TRADE_COMPONENT": { "TITLE": "Cepat", @@ -623,7 +625,8 @@ "INTERNAL": "intern", "BLOCKCHAIN": "blockchain", "STAKE": "mempertaruhkan", - "REFERRAL": "rujukan" + "REFERRAL": "rujukan", + "QUICK_TRADE_TOOLTIP": "Pesanan dieksekusi melalui sistem konversi" }, "ACCOUNT_SECURITY": { "TITLE_TEXT": "Atur pengaturan keamanan akun Anda. Mulai dari Autentikasi dua-faktor, kata sandi, Kunci API, dan fungsi lain yang berkaitan dengan keamanan.", @@ -647,7 +650,7 @@ "MESSAGE_4": "Anda dapat menyimpan kode ini dengan aman untuk memulihkan 2FA Anda apabila Anda mengubah atau kehilangan ponsel Anda di masa depan.", "MESSAGE_5": "Manual", "INPUT": "Masukkan Kata Sandi Sekali Pakai (OTP)", - "WARNING": "Kami sangat menyarankan Anda mengaktifkan autentikasi 2 faktor (2FA). Dengan mengaktifkannya, tingkat keamanan dana Anda akan sangat ditingkatkan.", + "WARNING": "Kami {0} {1}", "ENABLE": "Aktifkan Autentikasi Dua-Faktor", "DISABLE": "Nonaktifkan Autentikasi Dua-Faktor", "SECRET_1": "Enter yor secret key", @@ -656,12 +659,43 @@ "INPUT_1": "Secret Key", "TITLE_2": "Masukkan One-Time Password (OTP)", "MESSAGE_6": "Silakan masukkan 6 digit one-time password di bawah.", - "INPUT_2": "One-Time Password (OTP)" + "INPUT_2": "One-Time Password (OTP)", + "STRONGLY_RECOMMEND": "sangat merekomendasikan", + "WARNING_CONTENT": "bahwa Anda mengaktifkan otentikasi dua faktor untuk meningkatkan keamanan akun Anda dan melindungi dana Anda" }, "FORM": { "PLACEHOLDER": "Masukkan OTP yang ditunjukkan di aplikasi Autentikasi Google.", "BUTTON": "Aktifkan 2FA" - } + }, + "DOWNLOAD_APP": "Unduh dan pasang", + "GOOGLE_AUTHENTICATOR": "Google Otentikator:", + "DOWNLOAD_FROM": "Unduh dari", + "GOOGLE_PLAY": "Google Play", + "APPLE": "Toko aplikasi", + "APP_INFO": "Google Authenticator akan menghasilkan kode acak yang diperlukan untuk tindakan sensitif, seperti masuk dan menarik dana.", + "DONT_UNINSTALL": "Tolong jangan uninstall aplikasinya.", + "INFO_UNINSTALL": "Jika ponsel hilang secara tidak sengaja atau pencopotan pemasangan yang tidak disengaja, Anda mungkin mengalami keterlambatan dalam mendapatkan kembali akses ke akun Anda. Dalam kasus seperti itu, Anda perlu menghubungi tim dukungan kami untuk mendapatkan bantuan.", + "BACK": "KEMBALI", + "PROCEED": "MELANJUTKAN", + "NEXT": "Berikutnya", + "NOTE": "Catatan: {0} {1}", + "MANUEL_DESCRIPTION": "Silakan masukkan seed 2FA manual dari langkah sebelumnya.", + "MANUEL_DESCRIPTION_2": "Harap hati-hati menuliskan dan menyimpan verifikasi manual di atas", + "MANUEL_DESCRIPTION_3": "kunci karena dapat digunakan untuk pemulihan akun.", + "MANUEL_KEY": "Benih manual 2FA", + "MANUEL_PLACEHOLDER": "Masukkan benih 2FA Anda", + "MANUEL_WARNING": "(Pastikan Anda telah menuliskannya dan menyimpannya dengan aman untuk digunakan di masa mendatang.)", + "MANUEL_ERROR_1": "Silakan masukkan benih 2FA manual", + "MANUEL_ERROR_2": "Silakan masukan seed 2FA manual dari langkah sebelumnya", + "ACCOUNT_SECURED": "Akun Aman", + "2FA_ENABLED": "2FA DIAKTIFKAN", + "2FA_DISABLED": "2FA DONAKTIFKAN", + "2FA_CONTENT_ONE": "Mengaktifkan {0} {1} .", + "2FA_CONTENT_TWO": "(Otentikasi Dua Faktor) menambahkan lapisan perlindungan ekstra untuk melindungi dana Anda. Setelah diaktifkan, Anda akan diminta memasukkan kode verifikasi dari ponsel Anda setiap kali Anda masuk atau menarik aset. Fitur ini secara signifikan meningkatkan keamanan akun Anda", + "2FA_CONTENT_THREE": "(Otentikasi Dua Faktor) menambahkan lapisan perlindungan ekstra untuk melindungi dana Anda", + "2FA_CONTENT_FOUR": "Untuk menonaktifkan 2FA Anda harus memasukkan kode 2FA (OTP) Anda", + "INPUT_SIX_DIGIT_CODE": "Masukkan kode 6 digit dari aplikasi Google Authenticator ponsel Anda:", + "INPUT_CONTENT": "(Untuk menambahkan akun baru ke Google Authenticator, klik tombol '+'. Anda dapat memindai kode QR yang diberikan pada langkah sebelumnya atau memasukkan kunci secara manual untuk menambahkan akun Anda ke aplikasi.)" }, "CHANGE_PASSWORD": { "TITLE": "Ubah Kata Sandi", @@ -1664,7 +1698,8 @@ "QUICK_TRADE": "Perdagangan Cepat", "SUBTITLE": "Untuk melihat harga beli dan jual untuk {0} kunjungi {1}", "FOOTER": "Biaya perdagangan pengambil pasar berlaku untuk semua perdagangan yang dilakukan melalui Quick Trade." - } + }, + "PRO_TRADE": "LIHAT {0} PASAR" }, "ACCORDIAN": { "ACCORDIAN_ASSETS": "Semua aset dompet", @@ -1842,8 +1877,14 @@ "SELECT_END_DATE": "Pilih tanggal akhir", "CUSTOM": "Kebiasaan", "BACK_CUSTOM": "KEMBALI", - "PROOCED_CUSTOM": "DIPROSES" + "PROOCED_CUSTOM": "DIPROSES", + "PL_DAYS": "Hari P&L", + "VIEW_MORE": "Lihat Selengkapnya {0}", + "SHOW_ASSET_BREAKDOWN": "Tampilkan Perincian Aset", + "PL_SUMMARY": "Ringkasan Keuntungan dan Kerugian", + "BALANCE_HISTORY": "Riwayat Saldo" }, "ASSET_INFO": "Informasi aset", - "TAKER_FEES_APPLIED": "Biaya pengambil diterapkan" + "TAKER_FEES_APPLIED": "Biaya pengambil diterapkan", + "CANCEL_WITHDRAWAL_ADDRESS": "Alamat Penarikan" } \ No newline at end of file diff --git a/web/src/config/lang/it.json b/web/src/config/lang/it.json index 3d324dc257..3b4be8f1a7 100644 --- a/web/src/config/lang/it.json +++ b/web/src/config/lang/it.json @@ -179,7 +179,9 @@ "OTP_PLACEHOLDER": "Inserisci il codice di autenticazione", "OTP_TITLE": "Codice autenticatore", "OTP_BUTTON": "invia", - "ERROR_INVALID": "Codice OTP non valido" + "ERROR_INVALID": "Codice OTP non valido", + "OTP_FORM_SUBNOTE_LINE_3": "(Per aggiungere un nuovo account a Google Authenticator, fai clic sul pulsante \"+\". Puoi scansionare il codice QR fornito nel passaggio precedente o inserire manualmente la chiave per aggiungere il tuo account all'app.)", + "INPUT_TEXT": "Inserisci il codice a 6 cifre dall'app Google Authenticator del tuo telefono:" }, "EMAIL_CODE_FORM": { "TITLE": "Inserisci i tuoi codici di sicurezza", @@ -594,7 +596,8 @@ "INTERNAL": "interno", "BLOCKCHAIN": "blockchain", "STAKE": "palo", - "REFERRAL": "rinvio" + "REFERRAL": "rinvio", + "QUICK_TRADE_TOOLTIP": "Ordine eseguito tramite sistema di conversione" }, "ACCOUNT_SECURITY": { "TITLE_TEXT": "Regola le impostazioni di sicurezza per il tuo account. Da Autenticazione a due fattori, password, chiavi API e altre funzioni relative alla sicurezza.", @@ -617,15 +620,46 @@ "MESSAGE_3": "Se hai problemi con la scansione di questo, puoi inserire manualmente il codice qui sotto", "MESSAGE_4": "Devi archiviare questo codice in modo sicuro per recuperare il tuo 2FA nel caso in cui dovessi cambiare o perdere il tuo telefono cellulare in futuro.", "MESSAGE_5": "Manuale", - "WARNING": "Ti consigliamo vivamente di impostare l'autenticazione a due fattori (2FA). In questo modo aumenterai notevolmente la sicurezza dei tuoi fondi.", + "WARNING": "Noi {0} {1}", "ENABLE": "Abilita l'autenticazione a due fattori", "DISABLE": "Disabilita l'autenticazione a due fattori", - "INPUT": "Inserisci la tua OTP" + "INPUT": "Inserisci la tua OTP", + "STRONGLY_RECOMMEND": "fortemente raccomandato", + "WARNING_CONTENT": "di abilitare l'autenticazione a due fattori per migliorare la sicurezza del tuo account e proteggere i tuoi fondi" }, "FORM": { "PLACEHOLDER": "Inserisci la tua OTP fornita da Google Authenticator.", "BUTTON": "Abilita 2FA" - } + }, + "DOWNLOAD_APP": "Scarica e installa", + "GOOGLE_AUTHENTICATOR": "Autenticatore di Google:", + "DOWNLOAD_FROM": "Scarica da", + "GOOGLE_PLAY": "Google Play", + "APPLE": "App Store", + "APP_INFO": "Google Authenticator genererà codici casuali necessari per azioni sensibili, come l'accesso e il prelievo di fondi.", + "DONT_UNINSTALL": "Si prega di non disinstallare l'app.", + "INFO_UNINSTALL": "In caso di perdita accidentale del telefono o disinstallazione involontaria, potresti riscontrare ritardi nel riottenere l'accesso al tuo account. In questi casi, dovrai contattare il nostro team di supporto per ricevere assistenza.", + "BACK": "INDIETRO", + "PROCEED": "PROCEDERE", + "NEXT": "Prossimo", + "NOTE": "Nota: {0} {1}", + "MANUEL_DESCRIPTION": "Inserisci il seed 2FA manuale del passaggio precedente.", + "MANUEL_DESCRIPTION_2": "Si prega di annotare attentamente e conservare la verifica manuale di cui sopra", + "MANUEL_DESCRIPTION_3": "chiave in quanto può essere utilizzata per il recupero dell'account.", + "MANUEL_KEY": "Seme manuale 2FA", + "MANUEL_PLACEHOLDER": "Inserisci il tuo seme 2FA", + "MANUEL_WARNING": "(Assicurati di averlo annotato e di conservarlo in modo sicuro per un uso futuro.)", + "MANUEL_ERROR_1": "Inserisci il seed 2FA manuale", + "MANUEL_ERROR_2": "Inserisci il seed 2FA manuale dal passaggio precedente", + "ACCOUNT_SECURED": "Conto protetto", + "2FA_ENABLED": "2FA ABILITATO", + "2FA_DISABLED": "2FA DISABILITATO", + "2FA_CONTENT_ONE": "Attivazione di {0} {1} .", + "2FA_CONTENT_TWO": "(Autenticazione a due fattori) aggiunge un ulteriore livello di protezione per salvaguardare i tuoi fondi. Una volta abilitato, ti verrà richiesto di inserire un codice di verifica dal tuo telefono ogni volta che accedi o prelevi risorse. Questa funzione migliora significativamente la sicurezza del tuo account", + "2FA_CONTENT_THREE": "(Autenticazione a due fattori) aggiunge un ulteriore livello di protezione per salvaguardare i tuoi fondi", + "2FA_CONTENT_FOUR": "Per disabilitare 2FA dovrai inserire il tuo codice 2FA (OTP).", + "INPUT_SIX_DIGIT_CODE": "Inserisci il codice a 6 cifre dall'app Google Authenticator del tuo telefono:", + "INPUT_CONTENT": "(Per aggiungere un nuovo account a Google Authenticator, fai clic sul pulsante \"+\". Puoi scansionare il codice QR fornito nel passaggio precedente o inserire manualmente la chiave per aggiungere il tuo account all'app.)" }, "CHANGE_PASSWORD": { "TITLE": "Parola d'ordine", @@ -1508,7 +1542,8 @@ "QUICK_TRADE": "Commercio veloce", "SUBTITLE": "Per visualizzare i prezzi di acquisto e vendita di {0}, visita {1}", "FOOTER": "Le commissioni di negoziazione del market take vengono applicate a tutte le operazioni eseguite tramite Quick Trade." - } + }, + "PRO_TRADE": "VISUALIZZA MERCATO {0}" }, "ACCORDIAN": { "ACCORDIAN_ASSETS": "Tutte le risorse del portafoglio", @@ -1692,8 +1727,14 @@ "SELECT_END_DATE": "Seleziona la data di fine", "CUSTOM": "Costume", "BACK_CUSTOM": "INDIETRO", - "PROOCED_CUSTOM": "PROCEDUTO" + "PROOCED_CUSTOM": "PROCEDUTO", + "PL_DAYS": "Conti e perdite giornalieri", + "VIEW_MORE": "Visualizza altro {0}", + "SHOW_ASSET_BREAKDOWN": "Mostra la suddivisione delle risorse", + "PL_SUMMARY": "Riepilogo profitti e perdite", + "BALANCE_HISTORY": "Storia dell'equilibrio" }, "ASSET_INFO": "Informazioni sulle risorse", - "TAKER_FEES_APPLIED": "Vengono applicate le commissioni dell'acquirente" + "TAKER_FEES_APPLIED": "Vengono applicate le commissioni dell'acquirente", + "CANCEL_WITHDRAWAL_ADDRESS": "Indirizzo di ritiro" } \ No newline at end of file diff --git a/web/src/config/lang/ja.json b/web/src/config/lang/ja.json index 3d17c8a38c..3cb47952c9 100644 --- a/web/src/config/lang/ja.json +++ b/web/src/config/lang/ja.json @@ -228,7 +228,9 @@ "ERROR_INVALID": "無効なOTPコードです。", "OTP_FORM_INFO": "続行するには6桁のコードを入力してください", "OTP_FORM_SUBNOTE_LINE_1": "コードは、2 要素認証 (2FA) または OTP コードとも呼ばれます。", - "OTP_FORM_SUBNOTE_LINE_2": "コードを紛失した場合は、サポートまでご連絡ください。" + "OTP_FORM_SUBNOTE_LINE_2": "コードを紛失した場合は、サポートまでご連絡ください。", + "OTP_FORM_SUBNOTE_LINE_3": "(Google Authenticator に新しいアカウントを追加するには、「+」ボタンをクリックします。前の手順で提供された QR コードをスキャンするか、キーを手動で入力してアプリにアカウントを追加できます。)", + "INPUT_TEXT": "携帯電話の Google Authenticator アプリから 6 桁のコードを入力します。" }, "QUICK_TRADE_COMPONENT": { "TITLE": "クイック取引", @@ -610,7 +612,8 @@ "INTERNAL": "内部", "BLOCKCHAIN": "ブロックチェーン", "STAKE": "賭け金", - "REFERRAL": "照会" + "REFERRAL": "照会", + "QUICK_TRADE_TOOLTIP": "変換システムを通じて実行される注文" }, "ACCOUNT_SECURITY": { "TITLE_TEXT": "2段階認証、パスワード、APIキー、その他のセキュリティ関連機能でアカウントのセキュリティ設定を調整できます。", @@ -628,7 +631,7 @@ "MESSAGE_4": "この2段階認証コードを安全に保存して、携帯電話を変更または、紛失した場合に復元することができます。", "MESSAGE_5": "手動で入力", "INPUT": "OTPコードを入力してください。", - "WARNING": "アカウントのセキュリティをより強化するため、認証アプリによる2段階認証(2FA)の設定を強く推奨いたします。", + "WARNING": "私たち{0} {1}", "ENABLE": "2段階認証を設定する", "DISABLE": "2段階認証を解除する", "SECRET_1": "Enter yor secret key", @@ -637,12 +640,43 @@ "INPUT_1": "Secret Key", "TITLE_2": "OTP(One-Time Password)コード入力", "MESSAGE_6": "下に6桁のOTPコードを入力してください。", - "INPUT_2": "OTPコード" + "INPUT_2": "OTPコード", + "STRONGLY_RECOMMEND": "強くお勧めします", + "WARNING_CONTENT": "アカウントのセキュリティを強化し、資金を保護するために2要素認証を有効にしてください" }, "FORM": { "PLACEHOLDER": "Google Authenticatorから発行されたOTPコードを入力してください。", "BUTTON": "2段階認証を設定する" - } + }, + "DOWNLOAD_APP": "Download and install", + "GOOGLE_AUTHENTICATOR": "Google Authenticator:", + "DOWNLOAD_FROM": "ダウンロードはこちら", + "GOOGLE_PLAY": "グーグルプレイ", + "APPLE": "アプリストア", + "APP_INFO": "Google Authenticator は、ログインや資金の引き出しなどの機密性の高い操作に必要なランダムなコードを生成します。", + "DONT_UNINSTALL": "アプリをアンインストールしないでください。", + "INFO_UNINSTALL": "誤って携帯電話を紛失したり、意図せずにアンインストールしたりした場合、アカウントへのアクセスが回復するまでに時間がかかることがあります。そのような場合は、サポート チームに連絡して支援を受ける必要があります。", + "BACK": "戻る", + "PROCEED": "進む", + "NEXT": "次", + "NOTE": "注記: {0} {1}", + "MANUEL_DESCRIPTION": "前の手順で手動の 2FA シードを入力してください。", + "MANUEL_DESCRIPTION_2": "上記の手動検証を注意深く書き留めて保管してください", + "MANUEL_DESCRIPTION_3": "アカウントの回復に使用できるキーです。", + "MANUEL_KEY": "手動 2FA シード", + "MANUEL_PLACEHOLDER": "2FAシードを入力してください", + "MANUEL_WARNING": "(将来使用するために必ず書き留めて安全に保管してください。)", + "MANUEL_ERROR_1": "手動2FAシードを入力してください", + "MANUEL_ERROR_2": "前の手順で手動2FAシードを入力してください", + "ACCOUNT_SECURED": "アカウントは保護されています", + "2FA_ENABLED": "2FAが有効", + "2FA_DISABLED": "2FAが無効です", + "2FA_CONTENT_ONE": "{0} {1} をアクティブ化しています。", + "2FA_CONTENT_TWO": "(2要素認証)は、資金を保護するための追加の保護層を追加します。有効にすると、ログインまたは資産を引き出すたびに、携帯電話から確認コードを入力する必要があります。この機能により、アカウントのセキュリティが大幅に強化されます。", + "2FA_CONTENT_THREE": "(2要素認証)は、資金を保護するための追加の保護層を追加します", + "2FA_CONTENT_FOUR": "2FAを無効にするには、2FA(OTP)コードを入力する必要があります", + "INPUT_SIX_DIGIT_CODE": "携帯電話の Google Authenticator アプリから 6 桁のコードを入力します。", + "INPUT_CONTENT": "(Google Authenticator に新しいアカウントを追加するには、「+」ボタンをクリックします。前の手順で提供された QR コードをスキャンするか、キーを手動で入力してアプリにアカウントを追加できます。)" }, "CHANGE_PASSWORD": { "TITLE": "パスワードの変更", @@ -1655,7 +1689,8 @@ "QUICK_TRADE": "クイックトレード", "SUBTITLE": "{0} の売買価格を確認するには、{1} にアクセスしてください", "FOOTER": "マーケットテイカー取引手数料は、クイックトレードを通じて実行されるすべての取引に適用されます。" - } + }, + "PRO_TRADE": "{0} マーケットを見る" }, "ACCORDIAN": { "ACCORDIAN_ASSETS": "すべてのウォレット資産", @@ -1806,8 +1841,14 @@ "SELECT_END_DATE": "終了日を選択してください", "CUSTOM": "カスタム", "BACK_CUSTOM": "戻る", - "PROOCED_CUSTOM": "進行済み" + "PROOCED_CUSTOM": "進行済み", + "PL_DAYS": "日次損益", + "VIEW_MORE": "もっと見る{0}", + "SHOW_ASSET_BREAKDOWN": "資産の内訳を表示", + "PL_SUMMARY": "損益サマリー", + "BALANCE_HISTORY": "残高履歴" }, "ASSET_INFO": "資産情報", - "TAKER_FEES_APPLIED": "テイカー手数料が適用されます" + "TAKER_FEES_APPLIED": "テイカー手数料が適用されます", + "CANCEL_WITHDRAWAL_ADDRESS": "出金先住所" } \ No newline at end of file diff --git a/web/src/config/lang/ko.json b/web/src/config/lang/ko.json index c743e35b98..8e481e2d9d 100644 --- a/web/src/config/lang/ko.json +++ b/web/src/config/lang/ko.json @@ -218,7 +218,9 @@ "ERROR_INVALID": "OTP 번호가 유효하지 않습니다.", "OTP_FORM_INFO": "계속하려면 6자리 코드를 입력하세요.", "OTP_FORM_SUBNOTE_LINE_1": "귀하의 코드는 2FA(2단계 인증기) 또는 OTP 코드라고도 합니다.", - "OTP_FORM_SUBNOTE_LINE_2": "코드를 분실한 경우 지원팀에 문의하세요." + "OTP_FORM_SUBNOTE_LINE_2": "코드를 분실한 경우 지원팀에 문의하세요.", + "OTP_FORM_SUBNOTE_LINE_3": "(Google OTP에 새 계정을 추가하려면 '+' 버튼을 클릭하세요. 이전 단계에서 제공된 QR 코드를 스캔하거나 수동으로 키를 입력하여 앱에 계정을 추가할 수 있습니다.)", + "INPUT_TEXT": "휴대전화의 Google OTP 앱에서 6자리 코드를 입력하세요." }, "QUICK_TRADE_COMPONENT": { "TITLE": "퀵트레이딩", @@ -602,7 +604,8 @@ "INTERNAL": "내부", "BLOCKCHAIN": "블록체인", "STAKE": "말뚝", - "REFERRAL": "추천" + "REFERRAL": "추천", + "QUICK_TRADE_TOOLTIP": "전환 시스템을 통해 실행된 주문" }, "ACCOUNT_SECURITY": { "TITLE_TEXT": "계정의 보안 설정을 변경하세요. 이중인증, 비밀번호, API 키 및 관련 보안기능", @@ -620,7 +623,7 @@ "MESSAGE_4": "이 2단계 인증 키를 안전하게 저장하시어 향후 휴대전화의 변경 또는 분실의 경우 복구를 위한 대비를 하실 수 있습니다.", "MESSAGE_5": "수동", "INPUT": "OTP 코드를 입력해주시기바랍니다.", - "WARNING": "2단계 인증(2FA) 설정을 적극권장합니다. 이를 통해 회원님의 자산의 보안이 크게 향상됩니다.", + "WARNING": "우리는 {0} {1}", "ENABLE": "2단계 인증(2FA) 사용함", "DISABLE": "2단계 인증(2FA) 사용안함", "SECRET_1": "Enter yor secret key", @@ -629,12 +632,43 @@ "INPUT_1": "Secret Key", "TITLE_2": "OTP 입력", "MESSAGE_6": "6자리 OTP 숫자를 입력하세요.", - "INPUT_2": "OTP 입력" + "INPUT_2": "OTP 입력", + "STRONGLY_RECOMMEND": "강력 추천", + "WARNING_CONTENT": "계정 보안을 강화하고 자금을 보호하기 위해 이중 인증을 활성화한다는 점" }, "FORM": { "PLACEHOLDER": "Google Authenticator에서 제공된 OTP를 입력해주시기바랍니다.", "BUTTON": "2단계 인증(2FA)활성화" - } + }, + "DOWNLOAD_APP": "다운로드 및 설치", + "GOOGLE_AUTHENTICATOR": "Google OTP:", + "DOWNLOAD_FROM": "에서 다운로드", + "GOOGLE_PLAY": "구글 플레이", + "APPLE": "앱 스토어", + "APP_INFO": "Google OTP는 로그인 및 자금 인출과 같은 민감한 작업에 필요한 임의의 코드를 생성합니다.", + "DONT_UNINSTALL": "앱을 제거하지 마세요.", + "INFO_UNINSTALL": "실수로 휴대폰을 분실하거나 의도치 않게 제거하는 경우 계정에 다시 액세스하는 데 지연이 발생할 수 있습니다. 이러한 경우에는 지원팀에 문의하여 도움을 받아야 합니다.", + "BACK": "뒤쪽에", + "PROCEED": "진행하다", + "NEXT": "다음", + "NOTE": "참고: {0} {1}", + "MANUEL_DESCRIPTION": "이전 단계의 수동 2FA 시드를 입력하세요.", + "MANUEL_DESCRIPTION_2": "위의 수동 인증 내용을 꼼꼼히 적어서 보관해주세요.", + "MANUEL_DESCRIPTION_3": "계정 복구에 사용될 수 있는 키입니다.", + "MANUEL_KEY": "수동 2FA 시드", + "MANUEL_PLACEHOLDER": "2FA 시드를 입력하세요", + "MANUEL_WARNING": "(나중에 사용할 수 있도록 적어서 안전하게 보관하십시오.)", + "MANUEL_ERROR_1": "수동 2FA 시드를 입력하세요.", + "MANUEL_ERROR_2": "이전 단계의 수동 2FA 시드를 입력하세요.", + "ACCOUNT_SECURED": "계정이 보호됨", + "2FA_ENABLED": "2FA 활성화됨", + "2FA_DISABLED": "2FA 비활성화됨", + "2FA_CONTENT_ONE": "{0} {1} 활성화 중입니다.", + "2FA_CONTENT_TWO": "(2단계 인증)은 귀하의 자금을 보호하기 위해 추가 보호 계층을 추가합니다. 활성화되면 로그인하거나 자산을 출금할 때마다 휴대폰에서 인증 코드를 입력해야 합니다. 이 기능은 계정 보안을 크게 강화합니다.", + "2FA_CONTENT_THREE": "(2단계 인증)은 귀하의 자금을 보호하기 위해 추가 보호 계층을 추가합니다.", + "2FA_CONTENT_FOUR": "2FA를 비활성화하려면 2FA(OTP) 코드를 입력해야 합니다.", + "INPUT_SIX_DIGIT_CODE": "휴대전화의 Google OTP 앱에서 6자리 코드를 입력하세요.", + "INPUT_CONTENT": "(Google OTP에 새 계정을 추가하려면 '+' 버튼을 클릭하세요. 이전 단계에서 제공된 QR 코드를 스캔하거나 수동으로 키를 입력하여 앱에 계정을 추가할 수 있습니다.)" }, "CHANGE_PASSWORD": { "TITLE": "비밀번호 변경", @@ -1653,7 +1687,12 @@ "SELECT_END_DATE": "종료일 선택", "CUSTOM": "관습", "BACK_CUSTOM": "뒤쪽에", - "PROOCED_CUSTOM": "처리됨" + "PROOCED_CUSTOM": "처리됨", + "PL_DAYS": "일손익", + "VIEW_MORE": "더보기 {0}", + "SHOW_ASSET_BREAKDOWN": "자산 분석 표시", + "PL_SUMMARY": "손익 요약", + "BALANCE_HISTORY": "잔액 내역" }, "ASSET_INFO": "자산 정보", "TAKER_FEES_APPLIED": "테이커 수수료가 적용됩니다.", @@ -1696,7 +1735,8 @@ }, "LIST": "디지털 자산을 나열하고 싶으십니까?", "WEBSITE": "웹사이트", - "EXPLORER": "탐침" + "EXPLORER": "탐침", + "PRO_TRADE": "{0} 시장 보기" }, "ACCORDIAN": { "ACCORDIAN_ASSETS": "모든 지갑 자산", "ACCORDIAN_INFO": "자산 정보 페이지", "ACCORDIAN_HISTORY": "역사" }, "ASYNC_LINK": { @@ -1801,5 +1841,6 @@ "CONFIRM": "확인하다" } } - } + }, + "CANCEL_WITHDRAWAL_ADDRESS": "출금주소" } \ No newline at end of file diff --git a/web/src/config/lang/mn.json b/web/src/config/lang/mn.json index fb6fad6b46..cb5e72294d 100644 --- a/web/src/config/lang/mn.json +++ b/web/src/config/lang/mn.json @@ -168,7 +168,9 @@ "OTP_PLACEHOLDER": "Баталгаажуулах код оруулна уу", "OTP_TITLE": "Баталгаажуулах код", "OTP_BUTTON": "илгээх", - "ERROR_INVALID": "OTP Code буруу байна" + "ERROR_INVALID": "OTP Code буруу байна", + "OTP_FORM_SUBNOTE_LINE_3": "(Google Authenticator-д шинэ бүртгэл нэмэхийн тулд '+' товчийг дарна уу. Та өмнөх алхамд өгөгдсөн QR кодыг сканнердах эсвэл өөрийн бүртгэлээ апп-д нэмэх түлхүүрийг гараар оруулах боломжтой.)", + "INPUT_TEXT": "Утасныхаа Google Authenticator програмаас 6 оронтой кодыг оруулна уу:" }, "EMAIL_CODE_FORM": { "TITLE": "Аюулгүй байдлын кодыг оруулна уу", @@ -574,7 +576,8 @@ "INTERNAL": "дотоод", "BLOCKCHAIN": "блокчейн", "STAKE": "гадас", - "REFERRAL": "лавлагаа" + "REFERRAL": "лавлагаа", + "QUICK_TRADE_TOOLTIP": "Захиалгыг хөрвүүлэх системээр гүйцэтгэдэг" }, "ACCOUNT_SECURITY": { "TITLE_TEXT": "2FA баталгаажуулалт, нууц үг, API түлхүүрүүд болон бусад аюулгүй байдлын тохиргоо.", @@ -597,15 +600,46 @@ "MESSAGE_3": "Хэрэв qr code уншуулах боломжгүй тохиолдолд, гараар оруулах боломжтой", "MESSAGE_4": "Та гар утсаа солих эсвэл алдах тохиолдолд 2FA-аа сэргээхийн тулд энэ кодыг найдвартай хадгалах ёстой.", "MESSAGE_5": "Гараар тохируулах", - "WARNING": "Аюулгүй байдлыг нэмэгдүүлэхийн тулд давхар баталгаажуулалтыг идэвхижүүлнэ үү.", + "WARNING": "Бид {0} {1}", "ENABLE": "Идэвхижүүлэх", "DISABLE": "Идэвхигүй болгох", - "INPUT": "OTP код оруулна уу" + "INPUT": "OTP код оруулна уу", + "STRONGLY_RECOMMEND": "хүчтэй зөвлөж байна", + "WARNING_CONTENT": "Та өөрийн дансны аюулгүй байдлыг сайжруулж, мөнгөө хамгаалахын тулд хоёр хүчин зүйлийн баталгаажуулалтыг идэвхжүүлнэ" }, "FORM": { "PLACEHOLDER": "Google Authenticator-с өгсөн кодыг оруулна уу.", "BUTTON": "2FA идэвхижүүлэх" - } + }, + "DOWNLOAD_APP": "Download and install", + "GOOGLE_AUTHENTICATOR": "Google Authenticator:", + "DOWNLOAD_FROM": "Download from", + "GOOGLE_PLAY": "Google Play", + "APPLE": "App Store", + "APP_INFO": "Google Authenticator нь нэвтрэх, мөнгө татах гэх мэт эмзэг үйлдэлд шаардлагатай санамсаргүй кодуудыг үүсгэх болно.", + "DONT_UNINSTALL": "Програмыг устгаж болохгүй.", + "INFO_UNINSTALL": "Санамсаргүй утсаа алдсан эсвэл санамсаргүй устгасан тохиолдолд та өөрийн бүртгэл рүү нэвтрэх эрхээ сэргээхэд саатал гарч болзошгүй. Ийм тохиолдолд та манай тусламжийн багтай холбогдож тусламж авах шаардлагатай.", + "BACK": "БУЦАХ", + "PROCEED": "ҮРГЭЛЖЛҮҮЛ", + "NEXT": "Дараачийн", + "NOTE": "Жич: {0} {1}", + "MANUEL_DESCRIPTION": "Өмнөх алхамаас гарын авлагын 2FA үрийг оруулна уу.", + "MANUEL_DESCRIPTION_2": "Дээрх гарын авлагын баталгаажуулалтыг анхааралтай бичиж хадгална уу", + "MANUEL_DESCRIPTION_3": "Түлхүүрийг бүртгэл сэргээхэд ашиглаж болно.", + "MANUEL_KEY": "Гарын авлагын 2FA үр", + "MANUEL_PLACEHOLDER": "2FA үрээ оруулна уу", + "MANUEL_WARNING": "(Та үүнийг бичиж, ирээдүйд ашиглахын тулд аюулгүй хадгалсан эсэхээ шалгаарай.)", + "MANUEL_ERROR_1": "Гарын авлагын 2FA үрийг оруулна уу", + "MANUEL_ERROR_2": "Өмнөх алхамаас гарын авлагын 2FA үрийг оруулна уу", + "ACCOUNT_SECURED": "Дансны хамгаалалттай", + "2FA_ENABLED": "2FA-г идэвхжүүлсэн", + "2FA_DISABLED": "2FA ИДЭВХЖҮҮЛСЭН", + "2FA_CONTENT_ONE": "{0} {1}-г идэвхжүүлж байна.", + "2FA_CONTENT_TWO": "(Хоёр хүчин зүйлийн баталгаажуулалт) нь таны мөнгийг хамгаалах нэмэлт хамгаалалтын давхаргыг нэмж өгдөг. Идэвхжүүлсний дараа та нэвтрэх эсвэл хөрөнгө татах бүртээ утсан дээрээ баталгаажуулах код оруулах шаардлагатай болно. Энэ функц нь таны дансны аюулгүй байдлыг ихээхэн нэмэгдүүлдэг", + "2FA_CONTENT_THREE": "(Хоёр хүчин зүйлийн баталгаажуулалт) нь таны хөрөнгийг хамгаалах нэмэлт хамгаалалтын давхаргыг нэмж өгдөг", + "2FA_CONTENT_FOUR": "2FA-г идэвхгүй болгохын тулд та 2FA (OTP) кодоо оруулах шаардлагатай", + "INPUT_SIX_DIGIT_CODE": "Утасныхаа Google Authenticator програмаас 6 оронтой кодыг оруулна уу:", + "INPUT_CONTENT": "(Google Authenticator-д шинэ бүртгэл нэмэхийн тулд '+' товчийг дарна уу. Та өмнөх алхамд өгөгдсөн QR кодыг сканнердах эсвэл өөрийн бүртгэлээ апп-д нэмэх түлхүүрийг гараар оруулах боломжтой.)" }, "CHANGE_PASSWORD": { "TITLE": "Нууц үг солих", @@ -1507,7 +1541,12 @@ "SELECT_END_DATE": "Дуусах огноог сонгоно уу", "CUSTOM": "Захиалгат", "BACK_CUSTOM": "БУЦАХ", - "PROOCED_CUSTOM": "НЭМЭГДСЭН" + "PROOCED_CUSTOM": "НЭМЭГДСЭН", + "PL_DAYS": "Өдрийн P&L", + "VIEW_MORE": "Илүү ихийг үзэх {0}", + "SHOW_ASSET_BREAKDOWN": "Хөрөнгийн задаргааг харуулах", + "PL_SUMMARY": "P&L хураангуй", + "BALANCE_HISTORY": "Тэнцвэрийн түүх" }, "ASSET_INFO": "Хөрөнгийн мэдээлэл", "TAKER_FEES_APPLIED": "Хүлээн авагчийн хураамж авдаг", @@ -1550,7 +1589,8 @@ }, "LIST": "Та дижитал хөрөнгөө жагсаахыг хүсч байна уу?", "WEBSITE": "Вэб сайт", - "EXPLORER": "Судлаач" + "EXPLORER": "Судлаач", + "PRO_TRADE": "{0} ЗАХ ХАРАХ" }, "ACCORDIAN": { "ACCORDIAN_ASSETS": "Бүх түрийвчний хөрөнгө", @@ -1688,5 +1728,6 @@ "CONFIRM": "БАТАЛГААХ" } } - } + }, + "CANCEL_WITHDRAWAL_ADDRESS": "Татгалзах хаяг" } \ No newline at end of file diff --git a/web/src/config/lang/nl.json b/web/src/config/lang/nl.json index db724f4dca..c051e960dc 100644 --- a/web/src/config/lang/nl.json +++ b/web/src/config/lang/nl.json @@ -179,7 +179,9 @@ "OTP_PLACEHOLDER": "Voer de authenticatiecode in", "OTP_TITLE": "Verificatiecode", "OTP_BUTTON": "indienen", - "ERROR_INVALID": "Ongeldige OTP-code" + "ERROR_INVALID": "Ongeldige OTP-code", + "OTP_FORM_SUBNOTE_LINE_3": "(To add a new account to Google Authenticator, click the '+' button. You can either scan the provided QR code in the previous step or manually enter the key to add your account to the app.)", + "INPUT_TEXT": "Input the 6-digit code from your phone's Google Authenticator app:" }, "EMAIL_CODE_FORM": { "TITLE": "Voer uw beveiligingscodes in", @@ -594,7 +596,8 @@ "INTERNAL": "intern", "BLOCKCHAIN": "blockchain", "STAKE": "inzet", - "REFERRAL": "verwijzing" + "REFERRAL": "verwijzing", + "QUICK_TRADE_TOOLTIP": "Order executed through convert system" }, "ACCOUNT_SECURITY": { "TITLE_TEXT": "Pas de beveiligingsinstellingen voor uw account aan. Van tweefactorauthenticatie, wachtwoord, API-sleutels en andere beveiligingsgerelateerde functies.", @@ -617,15 +620,46 @@ "MESSAGE_3": "Als u problemen heeft met het scannen hiervan, kunt u de onderstaande code handmatig invoeren", "MESSAGE_4": "U moet deze code veilig opslaan om uw 2FA te herstellen voor het geval u uw mobiele telefoon in de toekomst wijzigt of kwijtraakt.", "MESSAGE_5": "Handmatig", - "WARNING": "We raden u ten zeerste aan om tweefactorauthenticatie (2FA) in te stellen. Als u dit doet, wordt de veiligheid van uw geld aanzienlijk vergroot.", + "WARNING": "Wij {0} {1}", "ENABLE": "Schakel tweefactorauthenticatie in", "DISABLE": "Schakel tweefactorauthenticatie uit", - "INPUT": "Voer uw OTP in" + "INPUT": "Voer uw OTP in", + "STRONGLY_RECOMMEND": "sterk aangeraden", + "WARNING_CONTENT": "dat u tweefactorauthenticatie inschakelt om de veiligheid van uw account te verbeteren en uw geld te beschermen" }, "FORM": { "PLACEHOLDER": "Voer uw OTP in die wordt verstrekt door Google Authenticator.", "BUTTON": "Schakel 2FA in" - } + }, + "DOWNLOAD_APP": "Download and install", + "GOOGLE_AUTHENTICATOR": "Google Authenticator:", + "DOWNLOAD_FROM": "Download from", + "GOOGLE_PLAY": "GooglePlay", + "APPLE": "App Store", + "APP_INFO": "Google Authenticator will generate random codes that are necessary for sensitive actions, such as logging in and withdrawing funds.", + "DONT_UNINSTALL": "Verwijder de app alstublieft niet.", + "INFO_UNINSTALL": "In het geval van onbedoeld verlies van uw telefoon of onbedoelde verwijdering, kunt u vertraging ondervinden bij het opnieuw verkrijgen van toegang tot uw account. In dergelijke gevallen moet u contact opnemen met ons ondersteuningsteam voor hulp.", + "BACK": "RUG", + "PROCEED": "DOORGAAN", + "NEXT": "Volgende", + "NOTE": "Opmerking: {0} {1}", + "MANUEL_DESCRIPTION": "Voer het handmatige 2FA-zaad uit de vorige stap in.", + "MANUEL_DESCRIPTION_2": "Noteer de bovenstaande handmatige verificatie zorgvuldig en bewaar deze", + "MANUEL_DESCRIPTION_3": "sleutel omdat deze kan worden gebruikt voor accountherstel.", + "MANUEL_KEY": "Handmatig 2FA zaad", + "MANUEL_PLACEHOLDER": "Voer uw 2FA-zaad in", + "MANUEL_WARNING": "(Zorg ervoor dat u het opschrijft en veilig bewaart voor toekomstig gebruik.)", + "MANUEL_ERROR_1": "Voer de handleiding 2FA Seed in", + "MANUEL_ERROR_2": "Voer het handmatige 2FA-zaad uit de vorige stap in", + "ACCOUNT_SECURED": "Account beveiligd", + "2FA_ENABLED": "2FA INGESCHAKELD", + "2FA_DISABLED": "2FA UITGESCHAKELD", + "2FA_CONTENT_ONE": "Activeren van {0} {1} .", + "2FA_CONTENT_TWO": "(Two-Factor Authentication) voegt een extra beschermingslaag toe om uw geld te beschermen. Eenmaal ingeschakeld, moet u elke keer dat u inlogt of activa opneemt, vanaf uw telefoon een verificatiecode invoeren. Deze functie verbetert de veiligheid van uw account aanzienlijk", + "2FA_CONTENT_THREE": "(Two-Factor Authentication) voegt een extra beschermingslaag toe om uw geld te beschermen", + "2FA_CONTENT_FOUR": "Om 2FA uit te schakelen, moet u uw 2FA (OTP)-code invoeren", + "INPUT_SIX_DIGIT_CODE": "Voer de 6-cijferige code in via de Google Authenticator-app van uw telefoon:", + "INPUT_CONTENT": "(Om een nieuw account aan Google Authenticator toe te voegen, klikt u op de knop '+'. U kunt de meegeleverde QR-code in de vorige stap scannen of handmatig de sleutel invoeren om uw account aan de app toe te voegen.)" }, "CHANGE_PASSWORD": { "TITLE": "Wachtwoord", @@ -1508,7 +1542,8 @@ "QUICK_TRADE": "Snelle handel", "SUBTITLE": "Ga naar {1} om de koop- en verkoopprijzen voor {0} te bekijken", "FOOTER": "Er worden handelskosten voor marktnemers in rekening gebracht op alle transacties die via de Quick Trade worden uitgevoerd." - } + }, + "PRO_TRADE": "BEKIJK {0} MARKT" }, "ACCORDIAN": { "ACCORDIAN_ASSETS": "Alle portefeuilleactiva", @@ -1686,8 +1721,14 @@ "SELECT_END_DATE": "Selecteer einddatum", "CUSTOM": "Aangepast", "BACK_CUSTOM": "RUG", - "PROOCED_CUSTOM": "VERWERKT" + "PROOCED_CUSTOM": "VERWERKT", + "PL_DAYS": "Dag P&L", + "VIEW_MORE": "Bekijk meer {0}", + "SHOW_ASSET_BREAKDOWN": "Activaverdeling weergeven", + "PL_SUMMARY": "P&L-samenvatting", + "BALANCE_HISTORY": "Saldogeschiedenis" }, "ASSET_INFO": "Informatie over activa", - "TAKER_FEES_APPLIED": "Er worden takerkosten in rekening gebracht" + "TAKER_FEES_APPLIED": "Er worden takerkosten in rekening gebracht", + "CANCEL_WITHDRAWAL_ADDRESS": "Herroepingsadres" } \ No newline at end of file diff --git a/web/src/config/lang/pt.json b/web/src/config/lang/pt.json index 71d34946bb..bc65d927db 100644 --- a/web/src/config/lang/pt.json +++ b/web/src/config/lang/pt.json @@ -227,7 +227,9 @@ "ERROR_INVALID": "Código OTP inválido", "OTP_FORM_INFO": "Digite seu código de 6 dígitos para continuar", "OTP_FORM_SUBNOTE_LINE_1": "Seu código também é conhecido como autenticador de dois fatores (2FA) ou código OTP.", - "OTP_FORM_SUBNOTE_LINE_2": "Se você perdeu seu código, entre em contato com o suporte." + "OTP_FORM_SUBNOTE_LINE_2": "Se você perdeu seu código, entre em contato com o suporte.", + "OTP_FORM_SUBNOTE_LINE_3": "(To add a new account to Google Authenticator, click the '+' button. You can either scan the provided QR code in the previous step or manually enter the key to add your account to the app.)", + "INPUT_TEXT": "Input the 6-digit code from your phone's Google Authenticator app:" }, "QUICK_TRADE_COMPONENT": { "TITLE": "Trade Rápido", @@ -623,7 +625,8 @@ "INTERNAL": "interno", "BLOCKCHAIN": "blockchain", "STAKE": "estaca", - "REFERRAL": "referência" + "REFERRAL": "referência", + "QUICK_TRADE_TOOLTIP": "Order executed through convert system" }, "ACCOUNT_SECURITY": { "TITLE_TEXT": "Ajuste as configurações de segurança para sua conta: autenticação de dois fatores (2FA), senha, chaves API e outras funções relacionadas à segurança.", @@ -643,15 +646,46 @@ "MESSAGE_3": "Se você tiver alguma problema ao fazer o escaneamento, você pode inserir manualmente o código abaixo.", "MESSAGE_4": "Você deve guardar este código com em um lugar seguro pois ele será necessário quando você for recuperar seu 2FA. Isso pode acontecer caso você troque ou perca seu telefone celular.", "MESSAGE_5": "Manual", - "WARNING": "É altamente recomendável que você configure a autenticação de dois fatores (2FA). Isso aumentará muito a segurança de seus recursos.", + "WARNING": "Nós {0} {1}", "ENABLE": "Ativar autenticação de dois fatores", "DISABLE": "Desativar autenticação de dois fatores", - "INPUT": "Insira seu OTP" + "INPUT": "Insira seu OTP", + "STRONGLY_RECOMMEND": "fortemente recomendado", + "WARNING_CONTENT": "que você habilite a autenticação de dois fatores para aumentar a segurança de sua conta e proteger seus fundos" }, "FORM": { "PLACEHOLDER": "Digite seu OTP fornecido pelo Google Authenticator.", "BUTTON": "Ativar 2FA" - } + }, + "DOWNLOAD_APP": "Download and install", + "GOOGLE_AUTHENTICATOR": "Autenticador do Google:", + "DOWNLOAD_FROM": "Download from", + "GOOGLE_PLAY": "GooglePlay", + "APPLE": "App Store", + "APP_INFO": "Google Authenticator will generate random codes that are necessary for sensitive actions, such as logging in and withdrawing funds.", + "DONT_UNINSTALL": "Por favor, não desinstale o aplicativo.", + "INFO_UNINSTALL": "No caso de perda acidental do telefone ou desinstalação não intencional, você poderá enfrentar atrasos na recuperação do acesso à sua conta. Nesses casos, você precisará entrar em contato com nossa equipe de suporte para obter assistência.", + "BACK": "VOLTAR", + "PROCEED": "CONTINUAR", + "NEXT": "Próximo", + "NOTE": "Observação: {0} {1}", + "MANUEL_DESCRIPTION": "Insira a semente 2FA manual da etapa anterior.", + "MANUEL_DESCRIPTION_2": "Anote e guarde cuidadosamente a verificação manual acima", + "MANUEL_DESCRIPTION_3": "chave, pois pode ser usada para recuperação de conta.", + "MANUEL_KEY": "Semente 2FA manual", + "MANUEL_PLACEHOLDER": "Insira sua semente 2FA", + "MANUEL_WARNING": "(Certifique-se de anotá-lo e armazená-lo com segurança para uso futuro.)", + "MANUEL_ERROR_1": "Por favor, insira a semente 2FA manual", + "MANUEL_ERROR_2": "Insira a semente 2FA manual da etapa anterior", + "ACCOUNT_SECURED": "Conta protegida", + "2FA_ENABLED": "2FA ATIVADO", + "2FA_DISABLED": "2FA DESATIVADO", + "2FA_CONTENT_ONE": "Ativando {0} {1} .", + "2FA_CONTENT_TWO": "(Autenticação de dois fatores) adiciona uma camada extra de proteção para proteger seus fundos. Uma vez ativado, você será solicitado a inserir um código de verificação do seu telefone sempre que fizer login ou retirar ativos. Este recurso aumenta significativamente a segurança da sua conta", + "2FA_CONTENT_THREE": "(Autenticação de dois fatores) adiciona uma camada extra de proteção para proteger seus fundos", + "2FA_CONTENT_FOUR": "Para desativar o 2FA, você precisará inserir seu código 2FA (OTP)", + "INPUT_SIX_DIGIT_CODE": "Insira o código de 6 dígitos do aplicativo Google Authenticator do seu telefone:", + "INPUT_CONTENT": "(Para adicionar uma nova conta ao Google Authenticator, clique no botão ‘+’. Você pode digitalizar o código QR fornecido na etapa anterior ou inserir manualmente a chave para adicionar sua conta ao aplicativo.)" }, "CHANGE_PASSWORD": { "TITLE": "Senha", @@ -1666,7 +1700,8 @@ "QUICK_TRADE": "Negociação rápida", "SUBTITLE": "Para visualizar os preços de compra e venda de {0}, visite {1}", "FOOTER": "As taxas de negociação do tomador de mercado são aplicadas a todas as negociações executadas por meio do Quick Trade." - } + }, + "PRO_TRADE": "VER {0} MERCADO" }, "ACCORDIAN": { "ACCORDIAN_ASSETS": "Todos os ativos da carteira", @@ -1850,8 +1885,14 @@ "SELECT_END_DATE": "Selecione a data de término", "CUSTOM": "Personalizado", "BACK_CUSTOM": "VOLTAR", - "PROOCED_CUSTOM": "PROCEDIDO" + "PROOCED_CUSTOM": "PROCEDIDO", + "PL_DAYS": "P&L do dia", + "VIEW_MORE": "Ver mais {0}", + "SHOW_ASSET_BREAKDOWN": "Mostrar detalhamento de ativos", + "PL_SUMMARY": "Resumo de lucros e perdas", + "BALANCE_HISTORY": "Histórico de saldo" }, "ASSET_INFO": "Informações do recurso", - "TAKER_FEES_APPLIED": "Taxas de tomador são aplicadas" + "TAKER_FEES_APPLIED": "Taxas de tomador são aplicadas", + "CANCEL_WITHDRAWAL_ADDRESS": "Withdrawal Address" } \ No newline at end of file diff --git a/web/src/config/lang/ru.json b/web/src/config/lang/ru.json index 01d1dd6bcf..78e799fb95 100644 --- a/web/src/config/lang/ru.json +++ b/web/src/config/lang/ru.json @@ -1,1598 +1,1749 @@ { - "APP_TITLE": "HollaEx", - "APP_SUB_TITLE": "Открыть криптобиржу", - "LOGOUT_CONFIRM_TEXT": "Вы действительно хотите выйти?", - "ADD_TRADING_PAIR": "Выберите рынок", - "CANCEL_BASE_WITHDRAWAL": "Отменить {0} снятие средств", - "CANCEL_WITHDRAWAL": "Отменить вывод средств", - "CANCEL_WITHDRAWAL_POPUP_CONFIRM": "Вы хотите отменить ожидающий отзыв:", - "NO_ACTIVE_ORDERS": "Заказов не обнаружено. Размещайте ордера на странице Pro или Quick trade", - "NO_ACTIVE_TRADES": "Вроде пока нет сделок", - "NO_ACTIVE_DEPOSITS": "Депозитов вроде пока нет.", - "NO_ACTIVE_WITHDRAWALS": "Выводов пока нет", - "LOGIN_TEXT": "Авторизоваться", - "SIGN_IN": "Войти", - "SIGNUP_TEXT": "Зарегистрироваться", - "REGISTER_TEXT": "регистр", - "ACCOUNT_TEXT": "Счет", - "CLOSE_TEXT": "Закрывать", - "COPY_TEXT": "Копировать", - "COPY_SUCCESS_TEXT": "Успешно скопировано", - "CANCEL_SUCCESS_TEXT": "Успешно отменено!", - "ADD_FILES": "ДОБАВИТЬ ФАЙЛЫ", - "OR_TEXT": "Или", - "CONTACT_US_TEXT": "Связаться с нами", - "HELPFUL_RESOURCES_TEXT": "Полезные ресурсы", - "HELP_RESOURCE_GUIDE": { - "CONTACT_US": "связаться с нами", - "TEXT": "Не стесняйтесь {0} для получения дополнительной информации и любых проблем, отправив нам электронное письмо" - }, - "HELP_TELEGRAM_TEXT": "Ознакомьтесь с открытой документацией по API:", - "HELP_TELEGRAM_LINK": "https://apidocs.hollaex.com", - "NEED_HELP_TEXT": "Нужна помощь?", - "HELP_TEXT": "помощь", - "SUCCESS_TEXT": "Успех", - "ERROR_TEXT": "Ошибка", - "PROCEED": "ПРОДОЛЖИТЬ", - "EDIT_TEXT": "Редактировать", - "BACK_TEXT": "Назад", - "NO_OPTIONS": "Нет доступных вариантов", - "SECONDS": "секунды", - "VIEW_MARKET": "просмотреть рынки", - "GO_TRADE": "Начни торговать", - "VIEW_INFO": "Посмотреть информационную страницу", - "APPLY_HERE": "Подать заявку здесь", - "CONVERT": "Конвертировать", - "SWAP": "Выключатель", - "TO": "К", - "NA": "Н/Д", - "HOME": { - "MAIN_TITLE": "Биржа торговли криптовалютой", - "MAIN_TEXT": "Легко покупать и продавать криптоактивы. Просто зарегистрируйтесь со своей электронной почтой и торгуйте основными криптоактивами 24/7.", - "TRADE_CRYPTO": "Начать торговлю", - "VIEW_EXCHANGE": "Посмотреть обмен" - }, - "FOOTER": { - "FOOTER_LANGUAGE_TEXT": "ЯЗЫК", - "TERMS_OF_SERVICE": "Условия использования", - "PRIVACY_POLICY": "политика конфиденциальности", - "CLICK_HERE": "кликните сюда", - "VISIT_HERE": "посетите здесь" - }, - "ACCOUNTS": { - "TITLE": "Счет", - "TAB_VERIFICATION": "Проверка", - "TAB_SECURITY": "Безопасность", - "TAB_SETTINGS": "Настройки", - "TAB_APPS": "Программы", - "TAB_WALLET": "Кошелек", - "TAB_SUMMARY": "Краткое содержание", - "TAB_HISTORY": "История", - "TAB_SIGNOUT": "Выход", - "TAB_STAKE": "Ставка", - "TAB_FIAT": "Фиат контролирует" - }, - "CONTACT_FORM": { - "CATEGORY_LABEL": "Категория", - "CATEGORY_PLACEHOLDER": "Выберите категорию, которая лучше всего подходит для вашей проблемы", - "CATEGORY_OPTIONS": { - "OPTION_VERIFY": "Проверка пользователя", - "OPTION_LEVEL": "Повысить уровень пользователя", - "OPTION_DEPOSIT": "Депозит и вывод", - "OPTION_BUG": "Сообщить об ошибке", - "OPTION_PERSONAL_INFO": "Изменить личную информацию", - "OPTION_BANK_TRANSFER": "Банковский перевод", - "OPTION_REQUEST": "Запросить приглашение на биржу HollaEx" - }, - "SUBJECT_LABEL": "Предмет", - "SUBJECT_PLACEHOLDER": "Введите тему вашего вопроса", - "DESCRIPTION_LABEL": "Описание", - "DESCRIPTION_PLACEHOLDER": "Напишите подробно в чем проблема", - "ATTACHMENT_LABEL": "Добавить вложения (максимум 3)", - "ATTACHMENT_PLACEHOLDER": "Добавьте файл, чтобы сообщить о своей проблеме. Принимаются файлы PDF, JPG, PNG и GIF.", - "SUCCESS_TITLE": "Сообщение отправлено", - "SUCCESS_MESSAGE_1": "Ваша проблема была отправлена в службу поддержки.", - "SUCCESS_MESSAGE_2": "Ожидайте ответа в течение 1-3 дней." - }, - "DEPOSIT": { - "CRYPTO_LABELS": { - "ADDRESS": "Ваш {0} адрес получателя", - "DESTINATION_TAG": "Ваш целевой тег {0}", - "MEMO": "Ваша заметка {0}" - }, - "QR_CODE": "Этот QR-код содержит информацию о депозите и может быть просканирован с помощью считывателя QR-кодов.", - "NO_DATA": "Информация отсутствует", - "QR_CODE_TITLE": "{0} Информация о депозите" - }, - "QR_CODE": { - "SCAN": "Сканировать", - "SHOW": "Показать QR-код", - "NO_RESULT": "Безрезультатно", - "NOT_FOUND": "Не найдено", - "PERMISSION_DENIED": "Доступ запрещен" - }, - "LOGIN": { - "LOGIN_TO": "Войти в {0}", - "CANT_LOGIN": "Не можете войти?", - "NO_ACCOUNT": "У вас нет аккаунта?", - "CREATE_ACCOUNT": "Создайте здесь", - "LOOKING_PRICES": "Ищете цены?", - "VIEW_MARKETS": "Посмотреть рынки", - "HELP": "Помощь" - }, - "FORM_FIELDS": { - "EMAIL_LABEL": "Электронная почта", - "EMAIL_PLACEHOLDER": "Введите адрес электронной почты", - "PASSWORD_LABEL": "Пароль", - "PASSWORD_PLACEHOLDER": "Введите свой пароль", - "PASSWORD_REPEAT_LABEL": "Введите свой пароль снова", - "PASSWORD_REPEAT_PLACEHOLDER": "Введите свой пароль снова" - }, - "VALIDATIONS": { - "OTP_LOGIN": "Введите OTP-код для входа", - "CAPTCHA": "Просроченная сессия. Пожалуйста, обновите страницу", - "FROZEN_ACCOUNT": "Этот аккаунт заморожен", - "INVALID_EMAIL": "Неверный адрес электронной почты", - "TYPE_EMAIL": "Введите адрес электронной почты", - "REQUIRED": "Обязательное поле", - "INVALID_DATE": "Недействительная дата", - "INVALID_PASSWORD": "Неверный пароль. Он должен содержать не менее 8 символов, цифру в пароле и специальный символ.", - "INVALID_PASSWORD_2": "Неверный пароль. Он должен содержать не менее 8 символов, не менее одной цифры и одного символа.", - "INVALID_CURRENCY": "Недопустимый адрес {0} ({1})", - "INVALID_BALANCE": "Недостаточно доступного баланса ({0}) для выполнения операции ({1}).", - "MIN_VALUE": "Значение должно быть {0} или выше.", - "MAX_VALUE": "Значение должно быть {0} или меньше.", - "MIN_VALUE_NE": "Значение должно быть больше {0}.", - "MAX_VALUE_NE": "Значение должно быть меньше {0}.", - "INSUFFICIENT_BALANCE": "Недостаточный баланс", - "PASSWORDS_DONT_MATCH": "Пароль не подходит", - "USER_EXIST": "Электронная почта уже зарегистрирована", - "ACCEPT_TERMS": "Вы не согласны с Условиями использования и Политикой конфиденциальности", - "STEP": "Недопустимое значение, шаг {0}", - "ONLY_NUMBERS": "Значение может содержать только числа" - }, - "NOTIFICATIONS": { - "BUTTONS": { - "OKAY": "Хорошо", - "START_TRADING": "начать торговать", - "SEE_HISTORY": "посмотреть историю" - }, - "DEPOSITS": { - "TITLE_RECEIVED": "{0} Депозит получен", - "TITLE_INCOMING": "Входящий {0}", - "SUBTITLE_RECEIVED": "Вы получили депозит {0}", - "SUBTITLE_INCOMING": "У вас есть входящие {0}", - "INFORMATION_PENDING_1": "Ваш {0} требует 1 подтверждения, прежде чем вы сможете начать торговлю.", - "INFORMATION_PENDING_2": "Это может занять 10-30 минут. Мы отправим электронное письмо, как только ваш {0} будет подтвержден в блокчейне." - } - }, - "REFERRAL_SUCCESS": { "TITLE": "Запрос отправлен", "BUTTON_TEXT": "Хорошо" }, - "OTP_FORM": { - "OTP_FORM_TITLE": "Введите код аутентификации, чтобы продолжить", - "OTP_FORM_INFO": "Введите свой 6-значный код, чтобы продолжить", - "OTP_FORM_SUBNOTE_LINE_1": "Ваш код также известен как двухфакторный аутентификатор (2FA) или OTP-код.", - "OTP_FORM_SUBNOTE_LINE_2": "Если вы потеряли код, обратитесь в службу поддержки.", - "OTP_LABEL": "OTP-код", - "OTP_PLACEHOLDER": "Введите код аутентификации", - "OTP_TITLE": "Код аутентификатора", - "OTP_BUTTON": "представлять на рассмотрение", - "ERROR_INVALID": "Неверный OTP-код" - }, - "EMAIL_CODE_FORM": { - "TITLE": "Введите коды безопасности", - "LABEL": "Введите код (пожалуйста, проверьте свою электронную почту)", - "PLACEHOLDER": "Введите код, отправленный на вашу электронную почту", - "FORM_TITLE": "На ваш адрес электронной почты был отправлен уникальный код, необходимый для завершения процесса. Пожалуйста, введите код, отправленный на вашу электронную почту ниже, вместе с кодом OTP.", - "BUTTON": "представлять на рассмотрение", - "ERROR_INVALID": "Код, который вы ввели, неверен. Пожалуйста, попробуйте еще раз", - "OTP_LABEL": "Код 2FA (OTP)", - "OTP_PLACEHOLDER": "Введите 6-значный код двухфакторной аутентификации." - }, - "QUICK_TRADE_COMPONENT": { - "TITLE": "Быстрая торговля", - "BUTTON": "Просмотреть заказ", - "INFO": "Самый быстрый и простой способ обменять вашу криптовалюту", - "CHANGE_TEXT": "изменять", - "HIGH_24H": "24Ч ВЫСОКИЙ", - "LOW_24H": "24 ч НИЗКИЙ", - "BEST_BID": "ЛУЧШЕЕ ПРЕДЛОЖЕНИЕ", - "BEST_ASK": "ЛУЧШИЙ СПРОС", - "FOOTER_TEXT": "Плата за быструю торговлю использует ставки маркет-тейкера", - "FOOTER_TEXT_1": "Источник из", - "GO_TO_TEXT": "Идти к", - "SOURCE_TEXT": "Брокерская внебиржевая сделка", - "SOURCE_TEXT_NETWORK": "Внебиржевая сделка сетевого брокера", - "VISIT": "Посетите{0}", - "ASSET_INFO_PAGE": "Страница информации об активах" - }, - "PREVIOUS_PAGE": "Предыдущая страница", - "NEXT_PAGE": "Следующая Страница", - "WALLET": { - "LOADING_ASSETS": "Загрузка ресурсов...", - "TOTAL_ASSETS": "Всего активов", - "AVAILABLE_WITHDRAWAL": "Доступно для торговли", - "ORDERS_PLURAL": "заказы", - "ORDERS_SINGULAR": "заказ", - "HOLD_ORDERS": "У вас {0} открыто {1}, в результате чего {2} {3} заблокировано на вашем балансе {4}" - }, - "REQUEST_RESET_PASSWORD": { - "TITLE": "Восстановление аккаунта", - "SUBTITLE": "Восстановите свою учетную запись ниже", - "SUPPORT": "Контактная поддержка", - "BUTTON": "Отправить ссылку для восстановления" - }, - "REQUEST_RESET_PASSWORD_SUCCESS": { - "TITLE": "Сброс пароля отправлен", - "TEXT": "Если для адреса электронной почты существует учетная запись, на нее было отправлено электронное письмо с инструкциями по сбросу. Пожалуйста, проверьте свою электронную почту и нажмите на ссылку, чтобы завершить сброс пароля." - }, - "RESET_PASSWORD": { - "TITLE": "Установить новый пароль", - "SUBTITLE": "Установить новый пароль", - "BUTTON": "Установить новый пароль" - }, - "RESET_PASSWORD_SUCCESS": { - "TEXT_1": "Вы успешно установили новый пароль.", - "TEXT_2": "Нажмите «Войти» ниже, чтобы продолжить." - }, - "SIGN_UP": { - "SIGNUP_TO": "Зарегистрируйтесь в {0}", - "NO_EMAIL": "Не получили письмо?", - "REQUEST_EMAIL": "Запросите еще один здесь", - "HAVE_ACCOUNT": "У вас уже есть аккаунт?", - "GOTO_LOGIN": "Перейти на страницу входа", - "AFFILIATION_CODE": "Реферальный код (необязательно)", - "AFFILIATION_CODE_PLACEHOLDER": "Введите свой реферальный код", - "TERMS": { - "terms": "Общие условия", - "policy": "политика конфиденциальности", - "text": "Я прочитал и согласен с {0} и {1}" - } - }, - "VERIFICATION_TEXTS": { - "TITLE": "Письмо отправлено", - "TEXT_1": "Проверьте свою электронную почту и нажмите на ссылку, чтобы подтвердить свою электронную почту.", - "TEXT_2": "Если вы не получили подтверждения по электронной почте и проверили нежелательную почту, вы можете попробовать нажать кнопку «Отправить повторно» ниже." - }, - "VERIFICATION_EMAIL_REQUEST": { - "TITLE": "Повторно отправить запрос по электронной почте", - "BUTTON": "Электронная почта запроса", - "SUBTITLE": "Сделайте еще один запрос на подтверждение электронной почты ниже", - "SUPPORT": "Контактная поддержка" - }, - "VERIFICATION_EMAIL_REQUEST_SUCCESS": { - "TITLE": "Повторное письмо", - "TEXT_1": "Если через несколько минут вы все еще не получили подтверждение по электронной почте, пожалуйста, свяжитесь с нами ниже." - }, - "VERIFICATION_EMAIL": { - "INVALID_UUID": "Неверный код", - "TEXT_1": "Вы успешно подтвердили свою электронную почту.", - "TEXT_2": "Теперь вы можете перейти к входу в систему", - "CANCEL_BUTTON": "ЗАКРЫТЬ", - "CONFIRM_BUTTON": "ПОДТВЕРДИТЬ РЕГИСТРАЦИЮ", - "CONFIRM_TEXT": "Подтвердить регистрацию", - "CONFIRM_DESCRIPTION": "Пожалуйста, подтвердите регистрацию вашей учетной записи, нажав на кнопку ниже" - }, - "USER_VERIFICATION": { - "INFO_TXT": "Здесь вы можете следить за своим прогрессом в проверке и обновлении учетной записи.", - "INFO_TXT_1": "Пожалуйста, отправьте соответствующую информацию, необходимую для каждого раздела ниже. Только после того, как все разделы будут заполнены, ваша информация будет рассмотрена и одобрена для повышения уровня учетной записи.", - "COMPLETED": "Завершенный", - "PENDING_VERIFICATION": "Ожидает подтвержения", - "TITLE_EMAIL": "Электронная почта", - "MY_EMAIL": "Моя электронная почта", - "TITLE_USER_DOCUMENTATION": "Идентификация", - "TITLE_ID_DOCUMENTS": "Загрузить", - "TITLE_BANK_ACCOUNT": "Банковский счет", - "TITLE_MOBILE_PHONE": "Мобильный телефон", - "TITLE_PERSONAL_INFORMATION": "Персональная информация", - "VERIFY_EMAIL": "Подтвердить Email", - "VERIFY_MOBILE_PHONE": "Подтвердить мобильный телефон", - "VERIFY_USER_DOCUMENTATION": "Проверка пользовательской документации", - "VERIFY_ID_DOCUMENTS": "Подтвердить документы, удостоверяющие личность", - "TITLE_IDENTITY": "Личность", - "TITLE_MOBILE": "Мобильный", - "TITLE_BANK": "Банк", - "CHANGE_VALUE": "Изменить значение", - "PENDING_VERIFICATION_PERSONAL_INFORMATION": "Ваша личная информация обрабатывается", - "PENDING_VERIFICATION_DOCUMENTS": "Ваши документы проходят проверку", - "GOTO_VERIFICATION": "Перейти к проверке", - "GOTO_WALLET": "Перейти к кошельку", - "INCOMPLETED": "Незавершенный", - "BANK_VERIFICATION": "Банковская проверка", - "IDENTITY_VERIFICATION": "Проверка личности", - "PHONE_VERIFICATION": "Проверка телефона", - "DOCUMENT_VERIFICATION": "Проверка документов", - "START_BANK_VERIFICATION": "Начать проверку банка", - "START_IDENTITY_VERIFICATION": "Начать проверку личности", - "START_PHONE_VERIFICATION": "Начать проверку телефона", - "START_DOCUMENTATION_SUBMISSION": "Начать подачу документации", - "GO_BACK": "Возвращаться", - "BANK_VERIFICATION_TEXT_1": "Вы можете добавить свои банковские счета здесь и получить подтверждение. Международные банковские счета потребуют от вас обращения в службу поддержки и будут иметь ограниченные лимиты на снятие средств.", - "BANK_VERIFICATION_TEXT_2": "Подтвердив свой банковский счет, вы можете получить следующее:", - "BASE_WITHDRAWAL": "вывод фиата", - "BASE_DEPOSITS": "Фиатные депозиты", - "ADD_ANOTHER_BANK_ACCOUNT": "Добавить еще один банковский счет", - "BANK_NAME": "Название банка", - "ACCOUNT_NUMBER": "Номер счета", - "BANK_VERIFICATION_HELP_TEXT": "Чтобы этот раздел был проверен, вы должны заполнить раздел {0}.", - "DOCUMENT_SUBMISSION": "Подача документов", - "REVIEW_IDENTITY_VERIFICATION": "Проверить верификацию личности", - "PHONE_DETAILS": "Детали телефона", - "PHONE_COUNTRY_ORIGIN": "Страна происхождения телефона", - "MOBILE_NUMBER": "Номер мобильного телефона", - "DOCUMENT_PROOF_SUBMISSION": "Предоставление подтверждающих документов", - "START_DOCUMENTATION_RESUBMISSION": "Начать повторную подачу документации", - "SUBMISSION_PENDING_TXT": "*Этот раздел уже отправлен. Внесение изменений и повторная отправка заменят вашу предыдущую информацию.", - "CUSTOMER_SUPPORT_MESSAGE": "Сообщение службы поддержки", - "DOCUMENT_PENDING_NOTE": "Ваши документы отправлены и ожидают рассмотрения. Пожалуйста, будьте терпеливы.", - "DOCUMENT_VERIFIED_NOTE": "Ваши документы готовы.", - "NOTE_FROM_VERIFICATION_DEPARTMENT": "Справка из отдела проверки", - "CODE_EXPIRES_IN": "Срок действия кода истекает через", - "EMAIL_VERIFICATION": "Отправить письмо с подтверждением", - "VERIFICATION_SENT": "Подтверждение отправлено", - "VERIFICATION_SENT_INFO": "Проверьте свою электронную почту и нажмите на ссылку, чтобы подтвердить электронную почту.", - "OKAY": "Хорошо", - "USER_DOCUMENTATION_FORM": { - "FORM_FIELDS": { - "FULL_NAME_LABEL": "Ваше полное имя", - "FULL_NAME_PLACEHOLDER": "Введите свое полное имя, как оно указано в документе, удостоверяющем личность.", - "GENDER_LABEL": "Пол", - "GENDER_PLACEHOLDER": "Укажите свой пол", - "GENDER_OPTIONS": { "MAN": "Мужской", "WOMAN": "Женский" }, - "NATIONALITY_LABEL": "Национальность", - "NATIONALITY_PLACEHOLDER": "Укажите национальность в вашем документе, удостоверяющем личность", - "DOB_LABEL": "Дата рождения", - "COUNTRY_LABEL": "Страна вашего проживания", - "COUNTRY_PLACEHOLDER": "Выберите страну, в которой вы проживаете в настоящее время", - "CITY_LABEL": "Город", - "CITY_PLACEHOLDER": "Введите город, в котором вы живете", - "ADDRESS_LABEL": "Адрес", - "ADDRESS_PLACEHOLDER": "Введите адрес, по которому вы сейчас проживаете", - "POSTAL_CODE_LABEL": "Почтовый индекс", - "POSTAL_CODE_PLACEHOLDER": "Введите свой почтовый индекс", - "PHONE_CODE_LABEL": "Страна", - "PHONE_CODE_PLACEHOLDER": "Выберите страну, к которой подключен ваш телефон", - "PHONE_CODE_DISPLAY": "({0}) {1}", - "PHONE_NUMBER_LABEL": "Номер телефона", - "PHONE_NUMBER_PLACEHOLDER": "Введите свой номер телефона", - "CONNECTING_LOADING": "Подключение", - "SMS_SEND": "Отправить смс", - "SMS_CODE_LABEL": "SMS-код", - "SMS_CODE_PLACEHOLDER": "Введите код из СМС" - }, - "INFORMATION": { - "TEXT": "ВАЖНО: Введите свое имя в поля точно так, как оно указано в вашем документе, удостоверяющем личность (полное имя, любые отчества/инициалы и полные фамилии). Вы бизнес? Обратитесь в службу поддержки для корпоративной учетной записи.", - "TITLE_PERSONAL_INFORMATION": "Персональная информация", - "TITLE_PHONE": "Телефон", - "PHONE_VERIFICATION_TXT": "Предоставление действительных контактных данных очень поможет нам в разрешении конфликтов и предотвращении нежелательных транзакций в вашей учетной записи.", - "PHONE_VERIFICATION_TXT_1": "Получайте в режиме реального времени обновления для депозитов и снятия средств, поделившись своим номером мобильного телефона.", - "PHONE_VERIFICATION_TXT_2": "Далее подтвердите свою личность и адрес, предоставив свой номер телефона в локальной сети (необязательно)." - } - }, - "ID_DOCUMENTS_FORM": { - "VALIDATIONS": { - "ID_NUMBER": "Пожалуйста, введите номер ваших документов", - "ISSUED_DATE": "Пожалуйста, выберите дату, когда был выдан ваш документ", - "EXPIRATION_DATE": "Пожалуйста, выберите дату истечения срока действия вашего документа", - "FRONT": "Пожалуйста, загрузите скан вашего паспорта или национального удостоверения личности", - "PROOF_OF_RESIDENCY": "Пожалуйста, загрузите отсканированный документ, подтверждающий адрес вашего текущего проживания.", - "SELFIE_PHOTO_ID": "Пожалуйста, загрузите селфи с паспортом или национальным удостоверением личности и отметьте" - }, - "FORM_FIELDS": { - "ID_NUMBER_LABEL": "Номер паспорта или национальный идентификационный номер", - "ID_NUMBER_PLACEHOLDER": "Введите номер своего паспорта или национальный идентификационный номер", - "ISSUED_DATE_LABEL": "Дата выдачи паспорта или национального удостоверения личности", - "EXPIRATION_DATE_LABEL": "Срок действия паспорта или национального удостоверения личности", - "FRONT_LABEL": "Паспорт или национальное удостоверение личности", - "FRONT_PLACEHOLDER": "Добавьте копию своего паспорта или национального удостоверения личности", - "POR_LABEL": "Документ, подтверждающий ваш адрес", - "POR_PLACEHOLDER": "Добавьте копию документа, подтверждающего ваш адрес", - "SELFIE_PHOTO_ID_LABEL": "Ваше селфи с паспортом или национальным удостоверением личности и заметкой", - "SELFIE_PHOTO_ID_PLACEHOLDER": "Добавьте копию своего селфи с паспортом или национальным удостоверением личности и заметку" - }, - "INFORMATION": { - "PROOF_OF_RESIDENCY": "Подтверждение проживания", - "ID_SECTION": { - "TITLE": "Пожалуйста, убедитесь, что представленные документы:", - "LIST_ITEM_0": "Общий размер всех документов не должен превышать {0} МБ.", - "LIST_ITEM_1": "Четкое цветное изображение высокого качества", - "LIST_ITEM_2": "ВИДИМЫ ПОЛНОСТЬЮ (водяные знаки разрешены).", - "LIST_ITEM_3": "ДЕЙСТВИТЕЛЬНО, с ясно видимой датой истечения срока действия.", - "WARNING_1": "Принимается только действующий паспорт; Допускаются высококачественные фотографии или отсканированные изображения этих документов:", - "WARNING_3": "Пожалуйста, не предоставляйте паспорт в качестве подтверждения проживания.", - "VIOLATION_ERROR": "Общий размер всех загруженных вами документов превышает ограничение на загрузку в {0} МБ. Пожалуйста, загрузите файлы меньшего размера, чтобы продолжить." - }, - "POR": { - "SECTION_1_TEXT_1": "Во избежание задержек при подтверждении учетной записи убедитесь, что:", - "SECTION_1_TEXT_2": "Ваше ИМЯ, АДРЕС, ДАТА ВЫДАЧИ и ЭМИТЕНТ четко видны.", - "SECTION_1_TEXT_3": "Представленный документ, подтверждающий место жительства, НЕ СТАРШЕ ТРЕХ МЕСЯЦЕВ.", - "SECTION_1_TEXT_4": "Вы отправляете цветные фотографии или отсканированные изображения в ВЫСОКОМ КАЧЕСТВЕ (не менее 300 DPI)", - "SECTION_2_TITLE": "ПРИЕМЛЕМЫМ ДОКАЗАТЕЛЬСТВОМ ПРОЖИВАНИЯ ЯВЛЯЕТСЯ:", - "SECTION_2_LIST_ITEM_1": "Выписка с банковского счета.", - "SECTION_2_LIST_ITEM_2": "Коммунальные платежи (электричество, вода, интернет и т.д.).", - "SECTION_2_LIST_ITEM_3": "Документ государственного образца (налоговая декларация, свидетельство о резидентстве и т.д.).", - "WARNING": "Мы не можем принять адрес, указанный в представленном вами документе, удостоверяющем личность, в качестве действительного подтверждения места жительства." - }, - "SELFIE": { - "TITLE": "Селфи с паспортом и запиской", - "INFO_TEXT": "Пожалуйста, предоставьте фотографию, на которой вы держите паспорт. На том же изображении есть ссылка на URL-адрес обмена, сегодняшняя дата и ваша подпись. Убедитесь, что ваше лицо хорошо видно, а данные вашего удостоверения личности хорошо читаются.", - "REQUIRED": "Необходимый:", - "INSTRUCTION_1": "Ваше лицо ясно видно", - "INSTRUCTION_2": "Паспорт хорошо читается", - "INSTRUCTION_3": "Напишите название биржи", - "INSTRUCTION_4": "Напишите сегодняшнюю дату", - "INSTRUCTION_5": "Напишите свою подпись", - "WARNING": "Селфи с другим паспортом с загруженным контентом будет отклонено" - } - } - }, - "BANK_ACCOUNT_FORM": { - "VALIDATIONS": { - "ACCOUNT_NUMBER_MAX_LENGTH": "Номер вашего банковского счета имеет ограничение в 50 символов" - }, - "FORM_FIELDS": { - "BANK_NAME_LABEL": "Название банка", - "BANK_NAME_PLACEHOLDER": "Введите название вашего банка", - "ACCOUNT_NUMBER_LABEL": "Номер банковского счета", - "ACCOUNT_NUMBER_PLACEHOLDER": "Введите номер своего банковского счета", - "ACCOUNT_OWNER_LABEL": "Имя владельца банковского счета", - "CARD_NUMBER_PLACEHOLDER": "Введите 16-значный номер, указанный на лицевой стороне вашей банковской карты." - } - }, - "WARNING": { - "TEXT_1": "Подтвердив свою личность, вы можете получить следующее:", - "LIST_ITEM_1": "Увеличенные лимиты вывода", - "LIST_ITEM_2": "Увеличенные лимиты депозитов", - "LIST_ITEM_3": "Более низкие сборы" - }, - "TITLE_PAYMENT": "Добавить способы оплаты", - "PAYMENT_VERIFICATION": "Проверка платежа", - "START_PAYMENT_VERIFICATION": "Начать проверку", - "PAYMENT_VERIFICATION_TEXT_1": "Добавьте данные своего платежного счета ниже.", - "PAYMENT_VERIFICATION_TEXT_2": "После того, как они будут проверены, ваша учетная запись получит больше способов вывода и депозита для различных валют.", - "ADD_ANOTHER_PAYMENT_METHOD": "ДОБАВИТЬ ДРУГОЙ СПОСОБ ОПЛАТЫ", - "PAYMENT_VERIFICATION_HELP_TEXT": "Чтобы этот раздел был проверен, вы должны заполнить раздел {0}." - }, - "USER_SETTINGS": { - "TITLE_TEXT_1": "Измените настройки своей учетной записи. Из интерфейса, уведомлений, имени пользователя и других настроек.", - "TITLE_TEXT_2": "Сохранение ваших настроек применит изменения и сохранит их.", - "TITLE_NOTIFICATION": "Уведомление", - "TITLE_INTERFACE": "Интерфейс", - "TITLE_LANGUAGE": "Язык", - "TITLE_CHAT": "Чат", - "TITLE_AUDIO_CUE": "Звуковые подсказки", - "TITLE_MANAGE_RISK": "Управление рисками", - "ORDERBOOK_LEVEL": "Уровни книги заказов (максимум 20)", - "SET_TXT": "НАБОР", - "CREATE_ORDER_WARING": "Предупреждение о создании заказа", - "RISKY_TRADE_DETECTED": "Обнаружена рискованная сделка", - "RISKY_WARNING_TEXT_1": "Стоимость этого заказа превышает указанную вами лимитную сумму заказа, которую вы установили {0} .", - "RISKY_WARNING_TEXT_2": "({0} портфолио)", - "RISKY_WARNING_TEXT_3": " Пожалуйста, проверьте и убедитесь, что вы действительно хотите совершить эту сделку.", - "GO_TO_RISK_MANAGMENT": "ПЕРЕЙТИ К УПРАВЛЕНИЮ РИСКАМИ", - "CREATE_ORDER_WARING_TEXT": "Создайте всплывающее окно с предупреждением, когда ваш торговый ордер использует более {0} вашего портфеля", - "ORDER_PORTFOLIO_LABEL": "Процентная сумма портфеля:", - "NOTIFICATION_FORM": { - "POPUP_ORDER_CONFIRMATION": "Запрашивайте подтверждение перед отправкой заказов", - "POPUP_ORDER_COMPLETED": "Показать всплывающее окно, когда заказ был выполнен", - "POPUP_ORDER_PARTIALLY_FILLED": "Показать всплывающее окно, когда заказ частично выполнен" - }, - "AUDIO_CUE_FORM": { - "ALL_AUDIO": "Все звуковые сигналы", - "PUBLIC_TRADE_AUDIO": "Когда была совершена публичная сделка", - "ORDERS_PARTIAL_AUDIO": "Когда один из ваших заказов частично выполнен", - "ORDERS_PLACED_AUDIO": "Когда заказ размещен", - "ORDERS_CANCELED_AUDIO": "Когда заказ отменяется", - "ORDERS_COMPLETED_AUDIO": "Когда один из ваших заказов полностью выполнен", - "CLICK_AMOUNTS_AUDIO": "При нажатии сумм и цен в книге заказов", - "GET_QUICK_TRADE_AUDIO": "При получении котировки для быстрой торговли", - "SUCCESS_QUICK_TRADE_AUDIO": "Когда происходит успешная быстрая сделка", - "QUICK_TRADE_TIMEOUT_AUDIO": "Когда тайм-аут быстрой торговли" - }, - "RISK_MANAGEMENT": { - "INFO_TEXT": "Создайте всплывающее окно с предупреждением, когда стоимость торгового ордера превышает установленный процент суммы вашего портфеля.", - "INFO_TEXT_1": "Общая стоимость активов в {0}: {1}", - "PORTFOLIO": "Процент портфеля", - "VALUE_ASSET": "Приблизительное значение", - "ADJUST": "(НАСТРОЙКА В ПРОЦЕНТАХ)", - "ACTIVATE_RISK_MANAGEMENT": "Активировать управление рисками", - "WARNING_POP_UP": "Предупреждающие всплывающие окна" - } - }, - "USER_APPS": { - "TITLE": "Ваши приложения для обмена", - "SUBTITLE": "Информация о приложениях вашей учетной записи Exchange и дополнительные функции ниже.", - "ALL_APPS": { - "TAB_TITLE": "Все приложения", - "TITLE": "Обмен приложениями", - "SUBTITLE": "Получите больше функциональности от своей учетной записи на бирже, просто выбрав приложение ниже и нажав кнопку «Добавить».", - "SEARCH_PLACEHOLDER": "Поиск приложений...", - "ADD": { - "SUCCESSFUL": "Вы успешно добавили новое приложение!", - "FAILED": "Что-то пошло не так" - } - }, - "MY_APPS": { - "TAB_TITLE": "Мои приложения", - "TITLE": "Мои обменные приложения", - "SUBTITLE": "Ниже представлены ваши активные заявки на обмен. Вы можете щелкнуть, чтобы просмотреть информацию о каждом приложении, функции и добавить/удалить их. Приложения предназначены для того, чтобы предоставить вам больше возможностей для обмена." - }, - "TABLE": { - "APP_NAME": "Имя приложения", - "DESCRIPTION": "Описание", - "ACTION": "Действие", - "CONFIGURE": "Настроить", - "VIEW_APP": "Посмотреть приложение", - "ADD": "Добавлять", - "NOT_FOUND": "Не могу найти это приложение...", - "RETRY": "Попробуйте другой поисковый запрос" - }, - "APP_DETAILS": { - "BACK_PLACEHOLDER": "{0} {1}", - "BACK_TO_APPS": "в мои приложения", - "BACK": "Назад" - }, - "CONFIGURE": { - "TITLE": "Настроить приложение", - "SUBTITLE": "Настройте свое приложение ниже:", - "REMOVE": "Удалить приложение", - "TEXT": "Если у вас возникли проблемы с вашим приложением, свяжитесь с нами, нажав «Справка» ниже.", - "BACK": "НАЗАД", - "HELP": "ПОМОЩЬ" - }, - "REMOVE": { - "TITLE": "Удалить приложение", - "SUBTITLE": "Приложение будет удалено из списка «Мои приложения».", - "TEXT": "Вы уверены, что хотите удалить это приложение?", - "BACK": "НАЗАД", - "CONFIRM": "ПОДТВЕРЖДАТЬ" - } - }, - "TRANSACTION_HISTORY": { - "TITLE": "История", - "TITLE_TRADES": "История сделок", - "TITLE_DEPOSITS": "История депозитов", - "TITLE_WITHDRAWALS": "История вывода средств", - "TEXT_DOWNLOAD": "СКАЧАТЬ ИСТОРИИ", - "TRADES": "Сделки", - "DEPOSITS": "Депозиты", - "WITHDRAWALS": "Снятие средств", - "ORDERID": "номер заказа", - "TRIGGER_STOP_PRICE": "Триггер/стоп цена", - "TIME_OF_LAST_TRADE": "Время последней сделки" - }, - "ACCOUNT_SECURITY": { - "TITLE_TEXT": "Настройте параметры безопасности для своей учетной записи. От двухфакторной аутентификации, пароля, ключей API и других функций, связанных с безопасностью.", - "OTP": { - "TITLE": "2FA", - "OTP_ENABLED": "OTP включен", - "OTP_DISABLED": "ПОЖАЛУЙСТА, ВКЛЮЧИТЕ 2FA", - "ENABLED_TEXTS": { - "TEXT_1": "Требовать OTP при входе", - "TEXT_2": "Требовать OTP при выводе средств" - }, - "DIALOG": { - "SUCCESS": "Вы успешно активировали 2FA", - "REVOKE": "Вы успешно деактивировали 2FA" - }, - "CONTENT": { - "TITLE": "Активировать двухфакторную аутентификацию", - "MESSAGE_1": "Сканировать", - "MESSAGE_2": "Отсканируйте приведенный ниже qrcode с помощью Google Authenticator или Authy, чтобы автоматически настроить двухфакторную аутентификацию на вашем устройстве.", - "MESSAGE_3": "Если у вас возникли проблемы со сканированием, вы можете вручную ввести код ниже", - "MESSAGE_4": "Вы должны хранить этот код в надежном месте, чтобы восстановить двухфакторную аутентификацию в случае, если вы в будущем поменяете или потеряете свой мобильный телефон.", - "MESSAGE_5": "Руководство", - "WARNING": "Мы настоятельно рекомендуем вам настроить двухфакторную аутентификацию (2FA). Это значительно повысит безопасность ваших средств.", - "ENABLE": "Включить двухфакторную аутентификацию", - "DISABLE": "Отключить двухфакторную аутентификацию", - "INPUT": "Пожалуйста, введите свой OTP" - }, - "FORM": { - "PLACEHOLDER": "Введите OTP, предоставленный Google Authenticator.", - "BUTTON": "Включить двухфакторную аутентификацию" - } - }, - "CHANGE_PASSWORD": { - "TITLE": "Пароль", - "ACTIVE": "АКТИВНЫЙ", - "DIALOG": { - "EMAIL_CONFIRMATION": "Вам будет отправлено электронное письмо для авторизации смены пароля." - }, - "FORM": { - "BUTTON": "Изменить пароль", - "CURRENT_PASSWORD": { - "label": "Текущий пароль", - "placeholder": "введите ваш текущий пароль" - }, - "NEW_PASSWORD": { - "label": "Новый пароль", - "placeholder": "Введите новый пароль" - }, - "NEW_PASSWORD_REPEAT": { - "label": "Подтвердите новый пароль", - "placeholder": "Повторите новый пароль" - } - } - }, - "LOGIN": { - "TITLE": "История входа", - "IP_ADDRESS": "Айпи адрес", - "TIME": "Дата/время", - "CONTENT": { "TITLE": "История входов" } - }, - "FREEZE": { - "TITLE": "Заморозить аккаунт", - "CONTENT": { - "MESSAGE_1": "Блокировка вашей учетной записи остановит снятие средств и остановит все торговые операции.", - "WARNING_1": "Используйте, только если вы опасаетесь, что ваша учетная запись была скомпрометирована", - "TITLE_1": "Заморозить свою учетную запись", - "TITLE_2": "Блокировка аккаунта", - "MESSAGE_2": "Блокировка вашей учетной записи может помочь защитить вашу учетную запись от кибератак.", - "MESSAGE_3": "Если вы решите заморозить свою учетную запись, произойдет следующее:", - "MESSAGE_4": "1. Незавершенные выводы будут отменены.", - "MESSAGE_5": "2. Все торги будут остановлены, а невыполненные ордера будут аннулированы.", - "MESSAGE_6": "3. Для повторной активации вашей учетной записи потребуется поддержка.", - "WARNING_2": "Вы действительно хотите заморозить свой аккаунт?" - } - } - }, - "STAKE": { - "NETWORK_WARNING": "Несовместимая сеть. Измените свою сеть на {0}", - "EARN": "Зарабатывать", - "TITLE": "Ставка", - "MODAL_TITLE": "Делайте ставки и зарабатывайте {0}", - "REVIEW_MODAL_TITLE": "Проверить и подтвердить ставку", - "AVAILABLE_TOKEN": "{0} ({1}) доступно для ставок: {2}", - "DEFI_TITLE": "Ставка активов DeFi", - "DEFI_TEXT": "В стиле стейкинга DeFi будет использоваться ваш собственный кошелек вне биржи. Для начала вам необходимо установить соединение, после чего вы можете сделать ставку и начать зарабатывать прямо из своего кошелька.", - "GET_STAKES": "Получить ставки", - "CURRENT_ETH_BLOCK": "Текущий блок ETH: {0}", - "ON_EXCHANGE_XHT": "На бирже {0} баланс: {1} {2}", - "LOGIN_HERE": "Войти здесь", - "MOVE_XHT": "Переместить {0}", - "ESTIMATED_STAKED": "Расчетная стоимость общей суммы ставок", - "ESTIMATED_EARNINGS": "Расчетная стоимость дохода", - "CONNECT_WALLET": "Подключить кошелек", - "BACK": "Назад", - "NEXT": "Следующий", - "REVIEW": "Обзор", - "ESTIMATED": "ВОСТОК.", - "BLOCK": "Блокировать", - "CANCEL": "Отмена", - "PROCEED": "Продолжить", - "GO_TO_WALLET": "перейти к кошельку", - "AMOUNT_LABEL": "Сумма ставки", - "PERIOD_SUBTITLE": "Чем дольше вы делаете ставки, тем больше вы получаете вознаграждение. Выберите продолжительность ставки ниже.", - "STAKE_AND_EARN_DETAILS": "Сделайте ставку на ~{0} и заработайте {1}", - "PREDICTED_EARNINGS": "Прогнозируемый доход", - "VARIABLE_TITLE": "Переменная*", - "VARIABLE_TEXT": "*{0} о том, как работает переменная ставка.", - "READ_MORE": "Читать далее", - "CURRENT_BLOCK": "Текущий блок: {0}", - "END_BLOCK": "Конечный блок: {0}", - "DURATION": "Продолжительность", - "END_ON_BLOCK": "Конец в блоке: {0}", - "SLASHING_TITLE": "Слэшинг (ранняя отмена ставок)", - "SLASHING_TEXT_1": "Принцип {0}% от ваших ставок", - "SLASHING_TEXT_2": "Весь заработок конфискован", - "REVIEW_NOTE": "Продолжительность измеряется временем блоков Ethereum. Пожалуйста, проверьте и подтвердите приведенные выше данные, прежде чем делать ставки, так как преждевременная отмена ставок приведет к вычету процента от суммы ваших ставок и потере прибыли.", - "WAITING_TITLE": "Ожидание подтверждения", - "WAITING_TEXT": "Подтвердите эту транзакцию в своем кошельке", - "PENDING_TEXT": "Ожидается транзакция...", - "CHECKING_ALLOWANCE": "Проверка допуска {0}...", - "WAITING_PROMPT": "{0} {1} {2}", - "WAITING_STAKE": "Подтвердить сумму ставки", - "WAITING_WITHDRAW": "Разрешение расходов", - "WAITING_UNSTAKE": "Отменить ставку", - "WAITING_STAKE_ING": "Ожидание ставок", - "WAITING_WITHDRAW_ING": "Обработка разрешения расходов", - "WAITING_UNSTAKE_ING": "Анстейкинг", - "SUCCESSFUL_STAKE_TITLE": "Вы успешно поставили {0}", - "SUCCESSFUL_STAKE_AMOUNT": "Сумма ставки", - "SUCCESSFUL_STAKE_DURATION_KEY": "Продолжительность", - "SUCCESSFUL_STAKE_DURATION_DEF": "Заканчивается блоком {0} ({1})", - "SUCCESSFUL_STAKE_DESTINATION": "Место назначения", - "SUCCESSFUL_UNSTAKE_ADDRESS": "Мой адрес", - "OKAY": "Хорошо", - "ERROR_TITLE": "Ошибка: {0} отклонено", - "ERROR_SUBTITLE": "Если это была ошибка, вы можете вернуться и повторить попытку.", - "SUCCESSFUL_UNSTAKE_TITLE": "Вы успешно сняли ставки {0}", - "SUCCESSFUL_UNSTAKE_AMOUNT": "Всего получить", - "EARNINGS": "Доход", - "ORIGINAL_AMOUNT": "Первоначальная сумма ставки", - "CONNECT_A_WALLET": "Подключиться к кошельку", - "CONNECT_WALLET_TABLE": "{0}, чтобы просмотреть исторические события ставок", - "ZERO_STAKES": "Нулевые ставки", - "PENDING_TRANSACTIONS": "В ожидании {0} {1}", - "VIEW_ON": "Посмотреть на {0}", - "BLOCKCHAIN": "блокчейн", - "VIEW_POT": "Посмотреть ПО для дистрибутива", - "COMPLETED": "Созревший", - "COMPLETED_TOOLTIP": "Ставка зрелая. Продолжайте делать ставки, чтобы заработать больше вознаграждений, или отмените ставки, чтобы получить вознаграждения.", - "CONNECT_ERROR": "Пожалуйста, проверьте свой кошелек", - "INSTALL_METAMASK": "Вы должны установить Metamask в свой браузер: https://metamask.io/download.html", - "INSTALL_METAMASK_TITLE": "Метамаска не обнаружена" - }, - "UNSTAKE": { - "TITLE": "Отменить ставку", - "EARLY_TITLE": "Отменить ставку раньше", - "EARLY_WARNING_TITLE": "Похоже, вы пытаетесь отменить ставку раньше", - "EARLY_WARNING_TEXT_1": "Это может привести к вычету процента от вашей первоначальной основной ставки и потере всех доходов.", - "EARLY_WARNING_TEXT_2": "Вы уверены, что хотите продолжить?", - "BACK": "ВОЗВРАЩАТЬСЯ", - "REVIEW": "УДАЛИТЬ СТАВКУ", - "DURATION": "ВОСТОК. продолжительность созревания", - "CANCEL": "Отмена", - "PROCEED": "Продолжить", - "EARNINGS_FORFEITED": "Прибыль конфискована", - "PRICE_FORMAT": "{0} {1}", - "EST_PENDING": "ВОСТОК. в ожидании: {0}", - "AMOUNT_SLASHED": "Урезанная сумма*", - "AMOUNT_TO_RECEIVE": "Сумма для получения", - "SLASH_FOOTNOTE": "*Все урезанные суммы распределяются между оставшимися стейкерами. Пожалуйста, примите во внимание сумму, сокращенную от первоначального принципа, конфискованную прибыль и оставшуюся продолжительность, и определите, стоит ли стоимость, потерянная при ранней отмене ставок.", - "AMOUNT_NOTE": "Суммы будут распределены на адрес вашего кошелька", - "TOTAL_EARNT": "Общий доход", - "PENDING_EARNINGS": "Ожидаемый доход*", - "PENDING_EARNINGS_FOOTNOTE": "*Незавершенные доходы — это суммы, которые не были очищены и требуют транзакции в блокчейне, чтобы быть добавленными к вашей общей сумме получения." - }, - "STAKE_TABLE": { - "CURRENCY": "Валюта", - "AVAILABLE": "Доступно для ставок", - "TOTAL": "Всего поставлено", - "REWARD_RATE": "Ставка вознаграждения", - "EARNINGS": "Доход", - "STAKE": "Ставка", - "VARIABLE": "Переменная" - }, - "STAKE_LIST": { - "AMOUNT": "СУММЫ СТАВКИ", - "DURATION": "ВОСТОК. ПРОДОЛЖИТЕЛЬНОСТЬ СОЗРЕВАНИЯ", - "START": "НАЧАЛО СТАВИТЬ", - "END": "КОНЕЦ СТАВКИ", - "EARNINGS": "ПРИБЫЛЬ", - "STAKE": "СТАВКА" - }, - "STAKE_DETAILS": { - "BACK_SUBTITLE": "{0} на страницу ставок", - "GO_BACK": "Возвращаться", - "CONTRACT_SUBTITLE": "Контракт токена: {0}", - "VIEW_MORE": "ПОСМОТРЕТЬ БОЛЬШЕ", - "VIEW": "Вид", - "TOKEN": "{0} Токен", - "TABS": { - "PUBLIC_INFO": "Общедоступная информация", - "DISTRIBUTIONS": "Распределения", - "MY_STAKING": "Мои ставки" - }, - "PUBLIC_INFO": { - "TITLE": "Информация о стекинге", - "SUBTITLE": "Ниже представлена токеномика ставок для {0} ({1}).", - "TOTAL_DISTRIBUTED_REWARDS": "Всего распределенных вознаграждений ({0})", - "POT_BALANCE": "пот баланс", - "UNCLAIMED_REWARDS": "Невостребованные награды", - "TOTAL_STAKED": "Всего поставлено", - "REWARD_RATE": "Ставка вознаграждения", - "MY_STAKE": "Моя ставка ({0}%)", - "MY_STAKE_PERCENTLESS": "Моя ставка", - "OTHER_STAKE": "Другие доли ({0}%)", - "EVENTS_TITLE": "Последние распределенные награды" - }, - "DISTRIBUTIONS": { - "TITLE": "Распределено {0} наград", - "SUBTITLE": "Ниже приведен исторический список раздач, сделанных стейкерам {0}.", - "TIME": "Распределенное время", - "TRANSACTION_ID": "ID транзакции", - "AMOUNT": "Распределенная сумма" - }, - "MY_STAKING": { - "TITLE": "Мои ставки", - "SUBTITLE": "Ниже представлена информация и некоторые исторические события, связанные со стекингом {0}.", - "EVENTS_TITLE": "Исторические события ставок", - "TIME": "Время", - "EVENT": "Событие", - "TRANSACTION_ID": "ID транзакции", - "AMOUNT": "Количество" - } - }, - "CEFI_STAKE": { - "STAKE_POOL_TITLE": "Локальные пулы стейкинга CeFi", - "INTRODUCTION_1": "Зарабатывайте награды на активах, которые вы храните в своем кошельке на локальной бирже. Просто нажмите 'СТЕЙК', введите сумму, которую хотите поставить в стейкинг, и начните зарабатывать.", - "CURRENT_STAKING_VALUE": "Текущая стоимость стейкинга", - "DURATION_LABEL": "Длительность", - "APY_LABEL": "Годовая процентная доходность (APY)", - "REWARDS_IN_LABEL": "Награды будут выплачиваться в", - "ALL_STAKING_EVENTS": "Все события стейкинга", - "MONITOR_ACTIVE_STAKES": "Отслеживайте активные стейки и их доходы, получаемые из пулов стейкинга.", - "USE_FILTERS_FOR_HISTORICAL_EVENTS": "Используйте фильтры, чтобы найти все исторические события стейкинга. Все доходы от завершенных стейкингов будут переведены на ваш кошелек.", - "ESTIMATED_TOTAL_STAKED": "Приблизительная общая стоимость стейкинга", - "ESTIMATED_EARNINGS_VALUE": "Приблизительная стоимость доходов", - "READ_BEFORE_AGREE_AND_EARN": "Прочитайте перед согласием и заработком", - "AGREEMENT_INTRODUCTION_1": "Заблокировать средства и участвовать в стейкинге действительно может быть прибыльным способом заработка наград. Однако важно знать, что могут быть налагаемы штрафы за раннее снятие и возможно долгосрочные сроки блокировки. Поэтому крайне важно полностью понимать правила пула перед стейкингом. Нажав 'Я согласен и понимаю' ниже, вы признаете, что вы будете", - "AGREEMENT_INTRODUCTION_2": "внимательно читать условия в последующих шагах,", - "AGREEMENT_INTRODUCTION_3": "и вы соглашаетесь с потенциальными рисками и штрафами, связанными с ранним снятием. Продолжение с таким пониманием обеспечит более осознанный и безопасный опыт стейкинга.", - "BACK_BUTTON": "Назад", - "PROCEED_BUTTON": "Продолжить", - "I_UNDERSTAND_BUTTON": "Я понимаю", - "AMOUNT_TO_STAKE_LABEL": "Сумма для стейкинга", - "NEXT_BUTTON": "Далее", - "LOCKUP_DURATION_LABEL": "Срок блокировки", - "PENALTY_UPON_INITIAL_STAKE_PRINCIPLE": "Штраф при начальном принципе стейкинга", - "FORFEITURE_OF_EARNINGS_LABEL": "Утрата доходов", - "SLASHING_RULES_ENFORCED_LABEL": "! Правила сокращения применяются в этом пуле", - "SLASHING_RULES_NOTICE": "Имейте в виду, что отказ от средств до указанного срока может повлечь штраф, как указано в правилах сокращения, упомянутых выше. Прежде чем обязательно начать стейкинговый период, важно оценить свою финансовую стабильность, так как запуск процесса раннего снятия может привести к уменьшению общей стоимости вашего начального стейкинга.", - "UNSTAKE_ANYTIME_LABEL": "Этот пул позволяет вам снимать стейкинг в любое время без последствий.", - "FULL_DURATION_LOCK_LABEL": "Этот пул блокирует вас на весь срок. Пожалуйста, обязательно оцените свою финансовую устойчивость, прежде чем начать стейкинговый пул, так как снятие до окончания срока будет невозможным.", - "CHECK_STAKE_DETAILS_BUTTON": "Проверить детали стейкинга", - "STAKING_POOL_LABEL": "Пул стейкинга", - "ANNUAL_PERCENTAGE_YIELD_LABEL": "Годовая процентная доходность (APY)", - "PENALTY_UPON_INITIAL_STAKE_PRINCIPLE_LABEL": "Штраф при начальном принципе стейкинга", - "FORFEITURE_OF_EARNINGS_DETAILS_LABEL": "Утрата доходов", - "STAKE_AMOUNT_LABEL": "Сумма стейкинга", - "DISCLAIMER_NOTICE": "Отказ от ответственности: обратите внимание, что суммы, оцениваемые в долларах США свыше 1 000 долларов, потребуют завершения идентификации. Эта сумма включает в себя доходы, и платформа оставляет за собой право запросить дополнительную информацию о пользователе.", - "SETTLEMENT_NOTICE": "Расчет: после снятия будет применен 24-часовой период расчета.", - "CONFIRM_BUTTON": "Подтвердить", - "STAKE_RULES_NOTICE": "Как только вы начнете стейкинг, вы обязуетесь соблюдать правила пула.", - "I_UNDERSTAND_AGAIN_BUTTON": "Я понимаю еще раз", - "CONGRATULATIONS_NOTICE": "Поздравляем!", - "EARNINGS_START_NOTICE": "Ваш стейкинг начнет приносить доход.", - "REVIEW_PROGRESS_LABEL": "Вы можете просмотреть прогресс своего стейкинга на странице 'Активные стейки'.", - "TIME_REMAINING_LABEL": "Оставшееся время", - "PENALTY_UPON_INITIAL_STAKE_PRINCIPLE_LABEL_2": "Штраф при начальном принципе стейкинга", - "FORFEITURE_OF_EARNINGS_LABEL_2": "Утрата доходов", - "AMOUNT_TO_RECEIVE_LABEL": "Сумма к получению", - "REQUIRES_24H_TO_SETTLE_NOTICE": "Требуется 24 часа для завершения.", - "SUCCESSFUL_UNSTAKE_NOTICE": "Вы успешно сняли стейкинг", - "VISIT_WALLET_BUTTON": "ПОСЕТИТЬ КОШЕЛЕК", - "CLOSE_BUTTON": "ЗАКРЫТЬ" - }, - "MOVE_XHT": { - "TITLE": "Переместить XHT", - "TEXT_1": "Чтобы сделать ставку XHT, вы должны сначала перевести XHT в свой собственный кошелек.", - "TEXT_2": "Адрес вашего текущего подключенного кошелька:", - "LABEL": "Адрес кошелька", - "TEXT_3": "Важно убедиться, что указанный выше адрес кошелька является безопасным. XHT будет перемещен на указанный выше адрес кошелька." - }, - "MOVE_AMOUNT": { - "TITLE": "Сумма ввода", - "PROMPT": "Введите сумму, которую вы хотите перевести.", - "BALANCE": "{0} баланс: {1}", - "LABEL": "Сумма для перемещения", - "FEE": "Комиссия за транзакцию: {0} {1}" - }, - "CURRENCY": "Валюта", - "TYPE": "Тип", - "TYPES_VALUES": { "market": "рынок", "limit": "ограничение" }, - "TYPES": { "MARKET": "рынок", "LIMIT": "ограничение" }, - "SIDE": "Сторона", - "SIDES_VALUES": { "buy": "купить", "sell": "продавать" }, - "SIDES_VERBS": { "buy": "купил", "sell": "продал" }, - "SIDES": { "BUY": "купить", "SELL": "продавать" }, - "DEFAULT_TOGGLE_OPTIONS": { "ON": "на", "OFF": "выключенный" }, - "SIZE": "Размер", - "PRICE": "Цена", - "AVERAGE": "Средний", - "FEE": "Платеж", - "FEES": "Сборы", - "TIME": "Время", - "MORE": "Более", - "VIEW": "Вид", - "STATUS": "Положение дел", - "AMOUNT": "Количество", - "COMPLETE": "Полный", - "PENDING": "В ожидании", - "REJECTED": "Отклоненный", - "ORDERBOOK": "Книга заказов", - "CANCEL": "Отмена", - "CANCEL_ALL": "Отменить все", - "ORDER_ENTRY": "порядок въезда", - "TRADE_HISTORY": "история", - "CHART": "ценовой график", - "ORDERS": "мои активные заказы", - "RECENT_TRADES": "мои последние сделки", - "ORDER_HISTORY": "История заказов", - "PUBLIC_SALES": "публичные продажи", - "REMAINING": "Оставшийся", - "FULLFILLED": "{0} % заполнено", - "FILLED": "Заполненный", - "LOWEST_PRICE": "Самая низкая цена ({0})", - "PHASE": "Фаза", - "INCOMING": "Входящий", - "PRICE_CURRENCY": "Цена", - "AMOUNT_SYMBOL": "Количество", - "ESTIMATED_PRICE": "Ориентировочная цена", - "ORDER_PRICE": "Стоимость заказа", - "NO_DATA": "Нет данных", - "CHART_TEXTS": { - "d": "Дата", - "o": "Открыть", - "h": "Высокий", - "l": "Низкий", - "c": "Закрывать", - "v": "Объем" - }, - "QUICK_TRADE": "Быстрая торговля", - "PRO_TRADE": "Про трейд", - "WALLET_TITLE": "Кошелек", - "CURRENCY_WALLET": { - "BACK": "Назад", - "WALLET_PAGE": "{0} на страницу кошелька", - "LEARN_MORE": "Узнать больше", - "ABOUT": "{0} о" - }, - "LOGOUT": "Выйти", - "WITHDRAWALS_MIN_VALUE_ERROR": "Транзакция слишком мала для отправки. Попробуйте большую сумму.", - "WITHDRAWALS_MAX_VALUE_ERROR": "Транзакция слишком велика для отправки. Попробуйте меньшее количество.", - "WITHDRAWALS_LOWER_BALANCE": "Недостаточно баланса для продолжения. Для этой транзакции требуется {0}.", - "WITHDRAWALS_BTC_INVALID_ADDRESS": "Биткойн-адрес недействителен. Пожалуйста, внимательно проверьте и введите еще раз", - "WITHDRAWALS_ETH_INVALID_ADDRESS": "Адрес Ethereum недействителен. Пожалуйста, внимательно проверьте и введите еще раз", - "WITHDRAWALS_BUTTON_TEXT": "отозвать отзыв", - "WITHDRAWALS_FORM_NETWORK_LABEL": "Сеть", - "DEPOSIT_FORM_NETWORK_WARNING": "Убедитесь, что выбранная сеть совместима с сетью кошельков отправителя.", - "DEPOSIT_FORM_MIN_WARNING": "Принимаются только депозиты в размере {0} {1} или более. Любые депозиты ниже этого минимального порога приведут к безвозвратной потере средств.", - "DEPOSIT_FORM_TITLE_WARNING_DESTINATION_TAG": "Введите адрес и тег, которые необходимы для успешного внесения средств на ваш счет.", - "WITHDRAW_PAGE_DESTINATION_TAG_NONE": "Никто", - "WITHDRAW_PAGE_DESTINATION_TAG_MESSAGE": "Тег назначения: {0}", - "WITHDRAW_PAGE_NETWORK_TYPE_MESSAGE": "{0} тип сети адреса: {1}", - "WITHDRAWALS_FORM_NETWORK_WARNING": "Убедитесь, что выбранная сеть совместима с целевым кошельком.", - "WITHDRAWALS_FORM_FEE_WARNING": "{0} ({1}) является обязательным условием для изъятия этого актива.", - "WITHDRAWALS_FORM_DESTINATION_TAG_WARNING": "Проверьте, требуется ли для получения адреса тег. Также известен как памятка, цифровое удостоверение личности, метка и заметки.", - "WITHDRAWALS_FORM_NETWORK_PLACEHOLDER": "Выберите сеть", - "WITHDRAWALS_FORM_ADDRESS_LABEL": "Адрес назначения", - "WITHDRAWALS_FORM_ADDRESS_PLACEHOLDER": "Введите адрес", - "WITHDRAWALS_FORM_DESTINATION_TAG_LABEL": "Тег назначения (необязательно)", - "WITHDRAWALS_FORM_MEMO_LABEL": "Памятка (необязательно)", - "WITHDRAWALS_FORM_DESTINATION_TAG_PLACEHOLDER": "Введите тег назначения", - "WITHDRAWALS_FORM_AMOUNT_LABEL": "{0} сумма для вывода", - "WITHDRAWALS_FORM_AMOUNT_PLACEHOLDER": "Введите сумму {0}, которую вы хотите снять", - "WITHDRAWALS_FORM_FEE_COMMON_LABEL": "Комиссия на перевод", - "WITHDRAWALS_FORM_FEE_COMMON_LABEL_COIN": "Комиссия за транзакцию ({0})", - "WITHDRAWALS_FORM_FEE_PLACEHOLDER": "Введите сумму {0}, которую вы хотите использовать в качестве комиссии за транзакцию.", - "DEPOSIT_BANK_REFERENCE": "Добавьте этот код \"{0}\" к банковскому переводу, чтобы идентифицировать депозит.", - "QUOTE_SUCCESS_REVIEW_MESSAGE": "Вы успешно {0} {1} {2}", - "COUNTDOWN_ERROR_MESSAGE": "Обратный отсчет завершен", - "WITHDRAW_PAGE": { - "BANK_TO_WITHDRAW": "Банк для вывода", - "MESSAGE_ABOUT_SEND": "Вы собираетесь отправить", - "MESSAGE_BTC_WARNING": "Пожалуйста, убедитесь в правильности этого адреса, поскольку переводы {0} необратимы.", - "MESSAGE_FEE": "Комиссия за транзакцию в размере {0} ({1}) включена", - "MESSAGE_FEE_COIN": "Комиссия за транзакцию {0}", - "BASE_MESSAGE_1": "Вы можете вывести средства только на банковский счет на имя, совпадающее с именем, зарегистрированным в вашей учетной записи.", - "BASE_MESSAGE_2": "Минимальная сумма вывода", - "BASE_MESSAGE_3": "Максимальная сумма ежедневного вывода", - "CONFIRM_VIA_EMAIL": "Подтвердить по электронной почте", - "CONFIRM_VIA_EMAIL_1": "Мы отправили вам электронное письмо с подтверждением вывода средств.", - "CONFIRM_VIA_EMAIL_2": "Для завершения процесса вывода, пожалуйста, подтвердите", - "CONFIRM_VIA_EMAIL_3": "вывод на вашу электронную почту в течение 5 минут.", - "WITHDRAW_CONFIRM_SUCCESS_1": "Ваш запрос на вывод средств подтвержден. Он будет обработан в ближайшее время.", - "WITHDRAW_CONFIRM_SUCCESS_2": "Чтобы просмотреть статус вывода средств, посетите страницу истории вывода средств.", - "GO_WITHDRAWAL_HISTORY": "Перейти к истории вывода средств", - "WITHDRAWALS_FORM_ERROR_TITLE": "Неверная информация о переводе", - "WITHDRAWALS_FORM_ERROR": "Ваш перевод средств не удался. Для отправки средств на электронную почту необходимо, чтобы у пользователя была учетная запись на этой бирже. Проверьте правильность адреса электронной почты и повторите попытку.", - "WITHDRAWAL_CONFIRM_FINAL": "Окончательное подтверждение вывода", - "WITHDRAWAL_CONFIRM_WARNING": "Пожалуйста, проверьте ваш вывод ниже перед подтверждением.", - "WITHDRAWAL_CONFIRM_AMOUNT": "Сумма", - "WITHDRAWAL_CONFIRM_FEE": "Комиссия", - "WITHDRAWAL_CONFIRM_ADDRESS": "Адрес", - "WITHDRAWAL_CONFIRM_NETWORK": "Сеть", - "WITHDRAWAL_CANCEL_BUTTON": "ЗАКРЫТЬ", - "WITHDRAWAL_CONFIRM_BUTTON": "ПОДТВЕРДИТЬ ВЫВОД" - }, - "WALLET_BUTTON_BASE_DEPOSIT": "депозит", - "WALLET_BUTTON_BASE_WITHDRAW": "отзывать", - "WALLET_BUTTON_CRYPTOCURRENCY_DEPOSIT": "депозит", - "WALLET_BUTTON_CRYPTOCURRENCY_WITHDRAW": "отправлять", - "24H_MAX": "24-часовой максимум:", - "24H_MIN": "24 часа Низкий", - "24H_VAL": "24ч Том", - "AVAILABLE_BALANCE_TEXT": "Доступный баланс {0}: {1} {2}", - "BALANCE_TEXT": "Баланс", - "CURRENCY_BALANCE_TEXT": "{0} Баланс", - "WALLET_ALL_ASSETS": "Все активы", - "WALLET_HIDE_ZERO_BALANCE": "Скрыть нулевой баланс", - "WALLET_ESTIMATED_TOTAL_BALANCE": "Расчетный общий баланс", - "WALLET_ASSETS_SEARCH_TXT": "Поиск", - "PAGINATOR_FORMAT": "{0} / {1}", - "ORDERBOOK_SELLERS": "Продавцы", - "ORDERBOOK_BUYERS": "Покупатели", - "ORDERBOOK_SPREAD": "распространение {0}", - "CALCULATE_MAX": "Макс", - "VERIFICATION_WARNING_TITLE": "Проверка ваших банковских реквизитов", - "VERIFICATION_WARNING_MESSAGE": "Перед снятием средств вам необходимо подтвердить свои банковские реквизиты.", - "ORDER_SPENT": "Потраченный", - "ORDER_RECEIVED": "Полученный", - "ORDER_SOLD": "Продал", - "ORDER_BOUGHT": "Купил", - "ORDER_AVERAGE_PRICE": "Средняя цена", - "ORDER_TITLE_CREATED": "Успешно создан лимит {0}", - "ORDER_TITLE_FULLY_FILLED": "Заказ {0} успешно выполнен", - "ORDER_TITLE_PARTIALLY_FILLED": "{0} заказ частично выполнен", - "ORDER_TITLE_TRADE_COMPLETE": "{0} {1} заказ выполнен успешно", - "LOGOUT_TITLE": "Вы вышли из системы", - "LOGOUT_ERROR_TOKEN_EXPIRED": "Срок действия вашего сеанса истек. Пожалуйста, войдите снова.", - "LOGOUT_ERROR_LOGIN_AGAIN": "Войти снова", - "LOGOUT_ERROR_INVALID_TOKEN": "Недействительный токен", - "LOGOUT_ERROR_INACTIVE": "Вы вышли из системы, потому что были неактивны", - "ORDER_ENTRY_BUTTON": "{0} {1}", - "ORDER_ENTRY_ADVANCED": "Передовой", - "QUICK_TRADE_OUT_OF_LIMITS": "Размер заказа выходит за пределы", - "QUICK_TRADE_TOKEN_USED": "Токен был использован", - "QUICK_TRADE_QUOTE_EXPIRED": "Срок действия предложения истек", - "QUICK_TRADE_QUOTE_INVALID": "Неверная цитата", - "QUICK_TRADE_QUOTE_CALCULATING_ERROR": "Ошибка расчета котировки", - "QUICK_TRADE_ORDER_CAN_NOT_BE_FILLED": "Проскальзывание для выбранной суммы слишком велико на рынке", - "QUICK_TRADE_ORDER_NOT_FILLED": "Заказ не заполнен", - "QUICK_TRADE_NO_BALANCE": "Недостаточно средств для выполнения заказа", - "QUICK_TRADE_SUCCESS": "Успех!", - "QUICK_TRADE_INSUFFICIENT_FUND": "Недостаточно средств", - "QUICK_TRADE_INSUFFICIENT_FUND_MESSAGE": "В вашем кошельке недостаточно средств для завершения этой транзакции.", - "QUICK_TRADE_BROKER_NOT_AVAILABLE_MESSAGE": "Брокерская внебиржевая сделка в настоящее время недоступна.", - "SUBMIT": "представлять на рассмотрение", - "RESUBMIT": "Отправить повторно", - "VERIFICATION_NOTIFICATION_SKIP_TITLE": "Недостающие документы!", - "VERIFICATION_NOTIFICATION_SKIP_TEXT": "Чтобы получить полный доступ к функциям вывода и депозита, вы должны предоставить документы, удостоверяющие личность, на странице вашей учетной записи.", - "VERIFICATION_NOTIFICATION_SUCCESS_TITLE": "Успех!", - "VERIFICATION_NOTIFICATION_SUCCESS_TEXT": "Вы получите уведомление по электронной почте, когда ваша информация будет обработана. Обработка обычно занимает 1-3 дня.", - "VERIFICATION_NOTIFICATION_BUTTON": "ПЕРЕЙТИ К ОБМЕНУ", - "ERROR_USER_ALREADY_VERIFIED": "Пользователь уже проверен", - "ERROR_INVALID_CARD_USER": "Информация о банке или карте указана неверно", - "ERROR_INVALID_CARD_NUMBER": "Не верный номер карты", - "ERROR_LOGIN_USER_NOT_VERIFIED": "Пользователь не проверен", - "ERROR_LOGIN_USER_NOT_ACTIVATED": "Пользователь не активирован", - "ERROR_LOGIN_INVALID_CREDENTIALS": "Неверные учетные данные", - "SMS_SENT_TO": "SMS отправлено на {0}", - "SMS_ERROR_SENT_TO": "Ошибка при отправке SMS на {0}. Пожалуйста, обновите страницу и повторите попытку.", - "WITHDRAW_NOTIFICATION_TRANSACTION_ID": "ID транзакции:", - "CHECK_ORDER": "Проверьте и подтвердите свой заказ", - "CHECK_ORDER_TYPE": "{0} {1}", - "CONFIRM_TEXT": "Подтверждать", - "INVALID_CAPTCHA": "неверная капча", - "NO_FEE": "Н/Д", - "SETTINGS_LANGUAGE_LABEL": "Языковые настройки (включая электронную почту)", - "SETTINGS_THEME_LABEL": "Тема пользовательского интерфейса", - "SETTING_BUTTON": "сохранять", - "VERIFICATION_NO_WITHDRAW_TITLE": "Снятие средств отключено", - "VERIFICATION_NO_WITHDRAW_MESSAGE": "Ваш аккаунт отключен для снятия средств", - "UP_TO_MARKET": "До рынка", - "VIEW_MY_FEES": "Посмотреть мои сборы", - "ABOUT_LINK": "О {0}", - "DEVELOPER_SECTION": { - "TITLE": "API-ключ", - "INFORMATION_TEXT": "API предоставляет такие функции, как получение баланса кошелька, управление заказами на покупку/продажу, запрос на снятие средств, а также рыночные данные, такие как последние сделки, книга заказов и тикер.", - "ERROR_INACTIVE_OTP": "Для генерации ключа API необходимо включить двухфакторную аутентификацию.", - "ENABLE_2FA": "Включить двухфакторную аутентификацию", - "WARNING_TEXT": "Не сообщайте свой ключ API другим лицам.", - "GENERATE_KEY": "Сгенерировать API-ключ", - "ACTIVE": "Активный", - "INACTIVE": "Неактивный", - "INVALID_LEVEL": "Вам необходимо повысить уровень подтверждения, чтобы получить доступ к этой функции." - }, - "DEVELOPERS_TOKEN": { - "API_KEY": "API-ключ", - "SECRET_KEY": "Секретный ключ", - "ACCESS": "Доступ", - "BASIC_ACCESS": "Базовый доступ", - "BASIC_ACCESS_PROMPT": "Выберите, к чему может получить доступ этот ключ API.", - "READING_ACCESS": "Чтение (балансы кошельков и т. д.)", - "TRADING_ACCESS": "Трейдинг", - "IP_ACCESS": "IP-доступ", - "IP_ACCESS_PROMPT": "Настройте, какой IP-адрес будет работать с этим ключом API.", - "ANY_IP_ADDRESS": "Любой IP-адрес", - "ONLY_TRUSTED_IPS": "Только проверенные IP", - "ADD_IP_PH": "Введите IP-адрес. Вы можете добавить несколько IP-адресов", - "ADD_IP": "Добавлять", - "ADVANCED_ACCESS": "Расширенный доступ", - "ADVANCED_ACCESS_PROMPT": "Требует активации доверенных IP-адресов.", - "WITHDRAWAL_ACCESS": "Снятие средств", - "SAVE": "Сохранять", - "BEWARE": "Осторожно, снятие средств сопряжено с определенными рисками!" - }, - "DEVELOPERS_TOKENS_POPUP": { - "GENERATE_TITLE": "Сгенерировать API-ключ", - "GENERATE_TEXT": "Пожалуйста, назовите свой ключ API и держите его в секрете после его создания. Вы не сможете восстановить его позже.", - "GENERATE": "Создать", - "DELETE_TITLE": "Удалить ключ API", - "DELETE_TEXT": "Удаление вашего ключа API необратимо, хотя вы можете создать новый ключ API в любое время. Вы хотите удалить свой ключ API?", - "DELETE": "УДАЛИТЬ", - "FORM_NAME_LABEL": "Имя", - "FORM_LABLE_PLACEHOLDER": "Имя для ключа API", - "API_KEY_LABEL": "API-ключ", - "SECRET_KEY_LABEL": "Секретный ключ", - "CREATED_TITLE": "Скопируйте свой ключ API", - "CREATED_TEXT_1": "Пожалуйста, скопируйте свой ключ API, так как в будущем он будет недоступен.", - "CREATED_TEXT_2": "Держите ключ в секрете." - }, - "DEVELOPERS_TOKENS_TABLE": { - "NAME": "Имя", - "API_KEY": "API-ключ", - "SECRET": "Секрет", - "CREATED": "Дата создания", - "REVOKE": "Отозвать", - "REVOKED": "Аннулировано", - "REVOKE_TOOLTIP": "Вы должны включить 2FA, чтобы отозвать токен" - }, - "CHAT": { - "READ_MORE": "Читать далее", - "SHOW_IMAGE": "Показать изображение", - "HIDE_IMAGE": "Скрыть изображение", - "CHAT_MESSAGE_BOX_PLACEHOLDER": "Сообщение", - "TROLLBOX": "Тролльбокс ({0})", - "SET_USERNAME": "УСТАНОВИТЬ ИМЯ ПОЛЬЗОВАТЕЛЯ В ЧАТ" - }, - "INVALID_USERNAME": "Имя пользователя должно содержать от 3 до 15 символов. Содержит только строчные буквы, цифры и подчеркивание", - "USERNAME_TAKEN": "Это имя пользователя уже занято. Пожалуйста, попробуйте другой.", - "USERNAME_LABEL": "Имя пользователя (используется для чата)", - "USERNAME_PLACEHOLDER": "Имя пользователя", - "TAB_USERNAME": "Имя пользователя", - "USERNAME_WARNING": "Имя пользователя можно изменить только один раз. Пожалуйста, убедитесь, что ваше имя пользователя желательно.", - "USERNAME_CANNOT_BE_CHANGED": "Имя пользователя не может быть изменено", - "UPGRADE_LEVEL": "Повысить уровень аккаунта", - "LEVELS": { - "LABEL_LEVEL": "Уровень", - "LABEL_LEVEL_1": "Один", - "LABEL_LEVEL_2": "Два", - "LABEL_LEVEL_3": "Три", - "LABEL_BASE_DEPOSIT": "Ежедневный депозит в евро", - "LABEL_BASE_WITHDRAWAL": "Ежедневный вывод евро", - "LABEL_BTC_DEPOSIT": "Ежедневный биткойн-депозит", - "LABEL_BTC_WITHDRAWAL": "Ежедневный вывод биткойнов", - "LABEL_ETH_DEPOSIT": "Ежедневный депозит Ethereum", - "LABEL_ETH_WITHDRAWAL": "Ежедневный вывод Эфириума", - "LABEL_PAIR_MAKER_FEE": "Комиссия производителя {0}", - "LABEL_PAIR_TAKER_FEE": "{0} Комиссия тейкера", - "UNLIMITED": "Неограниченный", - "BLOCKED": "Неполноценный" - }, - "WALLET_ADDRESS_TITLE": "Создать кошелек {0}", - "WALLET_ADDRESS_GENERATE": "Создать", - "WALLET_ADDRESS_MESSAGE": "Когда вы создаете кошелек, вы создаете депозитный адрес.", - "WALLET_ADDRESS_ERROR": "Ошибка при создании адреса, обновите и повторите попытку.", - "DEPOSIT_WITHDRAW": "Депозит/Снятие", - "GENERATE_WALLET": "Создать кошелек", - "TRADE_TAB_CHART": "Диаграмма", - "TRADE_TAB_TRADE": "Торговля", - "TRADE_TAB_ORDERS": "Заказы", - "TRADE_TAB_POSTS": "Объявления", - "WALLET_TAB_WALLET": "Кошелек", - "WALLET_TAB_TRANSACTIONS": "Транзакции", - "RECEIVE_CURRENCY": "Депозит {0}", - "SEND_CURRENCY": "Отправить {0}", - "COPY_ADDRESS": "Копировать адрес", - "SUCCESFUL_COPY": "Успешно скопировано!", - "QUICK_TRADE_MODE": "Режим быстрой торговли", - "JUST_NOW": "прямо сейчас", - "PAIR": "Пара", - "ZERO_ASSET": "У вас нет активов", - "DEPOSIT_ASSETS": "Депозитные активы", - "SEARCH_TXT": "Поиск", - "SEARCH_ASSETS": "Искать активы", - "TOTAL_ASSETS_VALUE": "Общая стоимость активов в {0}: {1}", - "SUMMARY": { - "TITLE": "Краткое содержание", - "URGENT_REQUIREMENTS": "Срочные требования", - "TRADING_VOLUME": "Объем торгов", - "ACCOUNT_ASSETS": "Активы аккаунта", - "ACCOUNT_DETAILS": "Детали учетной записи", - "VIEW_FEE_STRUCTURE": "Посмотреть структуру комиссий и лимиты", - "UPGRADE_ACCOUNT": "Обновить аккаунт", - "ACTIVE_2FA_SECURITY": "Активная двухфакторная аутентификация", - "ACCOUNT_ASSETS_TXT_1": "Отображается сводка всех ваших активов.", - "ACCOUNT_ASSETS_TXT_2": "Владение большим количеством активов дает вам право на повышение уровня учетной записи, которое включает в себя уникальный значок и более низкие торговые сборы.", - "ACCOUNT_DETAILS_TXT_1": "Тип вашей учетной записи определяет значок вашей учетной записи, торговую комиссию, депозиты и лимиты на снятие средств.", - "ACCOUNT_DETAILS_TXT_2": "Возраст вашего торгового счета, уровень активности и общая сумма активов на счете будут определять, имеет ли ваша учетная запись право на обновление.", - "ACCOUNT_DETAILS_TXT_3": "Поддержание уровня вашего счета требует постоянной торговли и поддержания определенного количества депонированных активов.", - "ACCOUNT_DETAILS_TXT_4": "Периодическое понижение уровня учетных записей будет происходить, если активность и активы не поддерживаются.", - "REQUIREMENTS": "Требования", - "ONE_REQUIREMENT": "Только одно требование:", - "REQUEST_ACCOUNT_UPGRADE": "Запросить обновление учетной записи", - "FEES_AND_LIMIT": "{0} Структура комиссий и лимитов", - "FEES_AND_LIMIT_TXT_1": "Стать криптотрейдером означает новое начало. Вооружившись смекалкой, волей и скоростью, только рискуя и торгуя, вы сможете обновить свой счет.", - "FEES_AND_LIMIT_TXT_2": "Каждая учетная запись имеет свои собственные комиссии и лимиты на ввод и вывод средств.", - "TRADING_FEE_STRUCTURE": "Структура торговых комиссий", - "DEPOSIT_AND_WITHDRAWAL_FEES": "Комиссия за ввод и вывод средств", - "WITHDRAWAL": "Снятие", - "DEPOSIT": "Депозит", - "TAKER": "Берущий", - "MAKER": "Создатель", - "DEPOSITS": "Депозиты", - "WITHDRAWALS": "Снятие средств", - "NOMINAL_TRADING_WITH_MONTH": "Номинальная торговая последняя {0}", - "LEVEL_OF_ACCOUNT": "Аккаунт {0} уровня", - "TITLE_OF_ACCOUNT": "{0} Аккаунт", - "LEVEL_TXT_DEFAULT": "Добавьте сюда описание вашего уровня", - "CURRENT_TXT": "Текущий", - "EMAIL_VERIFICATION": "Подтверждение по элетронной почте", - "DOCUMENTS": "Документы", - "HAP_TEXT": "Партнерская программа HollaEx (HAP) {0}", - "LOCK_AN_EXCHANGE": "Заблокировать биржу {0}", - "WALLET_SUBSCRIPTION_USERS": "Пользователи подписки на Сейф {0}", - "TRADE_OVER_XHT": "Торгуйте на сумму более {0} USDT", - "XHT_IN_WALLET": "{0} XHT в кошельке", - "TASKS": "Задания", - "INVITE_USER": "Приглашайте пользователей и получайте комиссионные от их торговли", - "DISCOUNT": "(скидка {0}% )", - "MY_FEES_LIMITS": " Мои комиссии и лимиты", - "MARKETS": "Рынки", - "CHANGE_24H": "24-часовая смена", - "VOLUME_24H": "24-часовой объем", - "VIEW_MORE_MARKETS": "Посмотреть больше рынков", - "VIEW_VERIFICATION": "просмотреть подтверждение", - "EARN_COMMISSION": "Зарабатывайте комиссионные", - "ID_VERIFICATION": "Проверка личности:" - }, - "REFERRAL_LINK": { - "TITLE": "Пригласи своего друга", - "INFO_TEXT": "Пригласите своих друзей, раздав эту ссылку, и получайте выгоду от регистрации других людей.", - "COPY_FIELD_LABEL": "Поделитесь ссылкой ниже с друзьями и зарабатывайте комиссионные:", - "REFERRED_USER_COUT": "Вы привлекли {0} пользователей", - "COPY_LINK_BUTTON": "КОПИРОВАТЬ РЕФЕРАЛЬНУЮ ССЫЛКУ", - "VIEW": "вид", - "TABLE_TITLE": "Все успешные рефералы", - "USER": "Пользователь/электронная почта", - "TIME": "Время регистрации" - }, - "NOT_LOGGEDIN": { - "TEXT_GENERAL": "Для просмотра необходимо авторизоваться", - "TXT_1": "Чтобы начать торговать, вам необходимо авторизоваться", - "TXT_2": "{0} или {1}", - "LOGIN_HERE": "войти здесь" - }, - "USER_LEVEL": "Уровень пользователя", - "LIMIT_AMOUNT": "Предельная сумма", - "FEE_AMOUNT": "Величина платежа", - "COINS": "Монеты", - "PAIRS": "Пары", - "NOTE_FOR_EDIT_COIN": "Примечание. Для добавления и удаления {0} см.{1}.", - "REFER_DOCS_LINK": "документы", - "EXPIRED_INFO_1": "Ваше испытание закончилось.", - "EXPIRED_INFO_2": "Обеспечьте свою биржу, чтобы активировать ее снова.", - "EXPIRED_BUTTON_TXT": "АКТИВИРОВАТЬ ОБМЕН", - "TRADE_POSTS": { "LEARN_MORE": "Узнать больше" }, - "OPEN_WALLET": "Открытый кошелек", - "CUMULATIVE_AMOUNT_SYMBOL": "Общий", - "POST_ONLY": "Опубликовать только", - "CLEAR": "Прозрачный", - "ORDER_TYPE": "Тип", - "ORDER_MODE": "Режим заказа", - "TRIGGER_CONDITIONS": "Условия срабатывания", - "TRANSACTION_STATUS": { - "PENDING": "В ожидании", - "REJECTED": "Отклоненный", - "COMPLETED": "Завершенный" - }, - "DEPOSIT_STATUS": { - "NEW": "Новый", - "SEARCH_FIELD_LABEL": "Вставьте идентификатор транзакции", - "CHECK_DEPOSIT_STATUS": "Проверить статус депозита", - "SEARCH_BLOCKCHAIN_FOR_DEPOSIT": "Поиск блокчейна для вашего депозита", - "STATUS_DESCRIPTION": "Вы можете проверить статус своего депозита, передав идентификатор транзакции (хэш) ниже.", - "TRANSACTION_ID": "Идентификатор транзакции (хэш)", - "SEARCH_SUCCESS": "Поиск завершен", - "ADDRESS_FIELD_LABEL": "Вставьте свой адрес", - "CURRENCY_FIELD_LABEL": "Выберите валюту", - "DESTINATION_TAG_LABEL": "Тег (необязательно)", - "MEMO_LABEL": "Памятка (необязательно)", - "DESTINATION_TAG_PLACEHOLDER": "Вставьте свой тег", - "MEMO_PLACEHOLDER": "Вставьте свою заметку" - }, - "CANCEL_ORDERS": { - "HEADING": "Отменить заказы", - "SUB_HEADING": "Отменить все заказы", - "INFO_1": "Это отменит ваши открытые ордера для рынка {0}.", - "INFO_2": "Вы уверены, что хотите отменить все открытые ордера?" - }, - "AMOUNT_IN": "Сумма в", - "LIMITS_BLOCK": { - "HEADER_ROW_DESCRIPTION": "Разрешение на ввод и вывод средств в течение 24 часов для всех активов ({0})", - "HEADER_ROW_TYPE": "Тип (все активы)", - "HEADER_ROW_AMOUNT": "Сумма за 24 часа ({0})" - }, - "MARKETS_TABLE": { - "TITLE": "Живые рынки", - "MARKETS": "Рынки", - "LAST_PRICE": "Последняя цена", - "CHANGE_24H": "Изменение (24 часа)", - "VOLUME_24h": "Объем (24 часа)", - "CHART_24H": "График (24 часа)", - "VIEW_MARKETS": "Посмотреть рынки", - "TRADING_SYMBOL": "Торговый символ", - "TYPE": "Тип", - "SOURCE": "ИСТОЧНИК" - }, - "SUMMARY_MARKETS": { - "HOLLAEX": "Хотите перечислить свои цифровые активы? Начните свой собственный рынок с HollaEx {0} {1}", - "WHITE_LABEL": "белая этикетка", - "SOLUTION": "решение", - "VISIT_COIN_INFO_PAGE": "Посетите страницу информации о монете {0}", - "HERE": "здесь" - }, - "PAGE_UNDER_CONSTRUCTION": "Эта страница находится в стадии разработки. Пожалуйста, вернитесь к этой странице в ближайшее время.", - "UNDEFINED_ERROR_TITLE": "Вы столкнулись с неизвестной ошибкой", - "UNDEFINED_ERROR": "Ух ты! Произошла неизвестная ошибка. Это может быть проблема с подключением или ряд других вещей. Вы можете повторить попытку позже или попробовать обновить.", - "POST_ONLY_TOOLTIP": "Ордера только на размещение выполняются только как лимитные ордера.", - "REFRESH": "Обновить", - "FEE_REDUCTION": "Снижение комиссии", - "FEE_REDUCTION_DESCRIPTION": "*к вашему аккаунту применяется скидка на комиссию. Снижение применяется к торговым сборам в зависимости от вашей учетной записи.", - "CHANGE_PASSWORD_FAILED": "смена пароля не удалась", - "MARKET_OPTIONS": { "LIST": "Список", "CARD": "Карта" }, - "ALL": "Все", - "ASSET_TXT": "Объект", - "ONE_DAY": "1 день", - "ONE_WEEK": "1 неделя", - "MONTH": "{0} месяц", - "START_DATE": "Дата начала", - "END_DATE": "Дата окончания", - "REGULAR": "Обычный", - "STOPS": "Остановки", - "VIEW_ALL": "посмотреть все", - "TRIGGER_PRICE": "Триггерная цена", - "SPEND_AMOUNT": "Сумма расходов", - "ESTIMATE_RECEIVE_AMOUNT": "Расчетная сумма получения", - "TOOLS": { - "ORDERBOOK": "Книга заказов", - "CHART": "Диаграмма", - "PUBLIC_SALES": "Публичные продажи", - "ORDER_ENTRY": "Порядок въезда", - "RECENT_TRADES": "Недавние сделки", - "OPEN_ORDERS": "Открытые ордера", - "WALLET": "Кошелек", - "DEPTH_CHART": "График глубины", - "COMING_SOON": "вскоре" - }, - "RESET_LAYOUT": "Сбросить раскладку", - "WALLET_BALANCE_LOADING": "Загрузка баланса...", - "LOADING": "Загрузка...", - "CONNECT_VIA_DESKTOP": { - "TITLE": "Подключиться через рабочий стол", - "SUBTITLE": "Ставки DeFi через мобильные устройства в настоящее время не поддерживаются.", - "TEXT": "Для подключения кошелька используйте настольный/портативный компьютер." - }, - "ORDER_HISTORY_CLOSED": "Закрыто", - "FIAT": { - "UNVERIFIED": { - "TITLE": "Полная проверка", - "TEXT": "Чтобы сделать {0}, вам необходимо пройти проверку, которая включает проверку ваших банковских реквизитов. Нажмите кнопку «Продолжить» ниже.", - "DEPOSIT": "депозит", - "WITHDRAWAL": "снятие" - }, - "REVIEW_DEPOSIT": { - "TITLE": "Проверьте и подтвердите данные депозита", - "SUBTITLE": "Пожалуйста, проверьте информацию о депозите ниже, чтобы убедиться, что все верно.", - "FORMAT": "{0} {1}", - "AMOUNT": "сумма для перечисления", - "FEE": "Комиссия за депозит", - "TRANSACTION_ID": "ID транзакции", - "NOTE": "Во избежание задержек убедитесь, что сумма вашего депозита, примечание и идентификатор транзакции совпадают с указанными выше данными. Если возникнут проблемы, обратитесь в службу поддержки.", - "BACK": "Назад", - "PROCEED": "Продолжить" - } - }, - "DEPOSIT_FEE_NOTE": "Обратите внимание, что общая сумма, внесенная на ваш счет, будет равна сумме за вычетом комиссии за депозит.", - "AMOUNT_LABEL": "Введите сумму депозита (должна совпадать с фактической суммой)", - "TRANSACTION_ID_LABEL": "Введите идентификатор транзакции депозита", - "FEE_LABEL": "Комиссия за депозит", - "AMOUNT_FORMAT": "{0} {1}", - "PENDING_DEPOSIT_TITLE": "Ожидание депозита", - "PENDING_DEPOSIT_TEXT_1": "Ваш депозит находится в очереди на проверку и будет оставаться в состоянии ожидания до тех пор, пока он не будет очищен.", - "PENDING_DEPOSIT_TEXT_2": "Обычно депозиты очищаются в течение 24 часов, но может занять до 48 часов, после того как сумма депозита за вычетом комиссии будет зачислена на ваш баланс.", - "DEPOSIT_HOME_NOTE": "Чтобы внести депозит, пожалуйста, введите сумму, которую вы будете вносить со своего банковского счета. Во избежание задержек убедитесь, что введенная ниже сумма соответствует фактической сумме, которую вы отправите или отправили с вашего банковского счета.", - "DEPOSIT_TXID_NOTE": "Чтобы избежать задержек с вашим депозитом, не забудьте ввести свое уникальное примечание, отображаемое выше, при внесении банковского депозита в примечание или сообщение о вашей транзакции. Убедитесь, что указанная ниже сумма соответствует сумме, которую вы фактически вносите в свой банк, и что вы указали идентификатор транзакции, предоставленный вашим банком после ее совершения.", - "DEPOSIT_BANK_TEXT": "Используйте банковские реквизиты ниже, чтобы сделать депозит.", - "MIN_DEPOSIT": "Минимальный депозит", - "MAX_DEPOSIT": "Максимальный депозит", - "BACK": "Назад", - "DONE": "Сделанный", - "PENDING_WITHDRAWAL_TITLE": "Ожидается вывод средств", - "PENDING_WITHDRAWAL_TEXT_1": "Ваш вывод сейчас находится в очереди на проверку и будет оставаться в состоянии ожидания до тех пор, пока не будет очищен.", - "DEPOSIT_AMOUNT_MIN_VALIDATION": "Транзакция слишком мала для отправки. Попробуйте большую сумму.", - "DEPOSIT_AMOUNT_MAX_VALIDATION": "Транзакция слишком велика для отправки. Попробуйте меньшее количество.", - "ACCOUNT_NAME": "Имя учетной записи", - "ACCOUNT_NUMBER": "номер счета", - "BANK_NAME": "название банка", - "VERIFY_BANK_WITHDRAW": "Чтобы вывести средства, вам необходимо пройти верификацию, которая включает проверку ваших банковских реквизитов. Нажмите кнопку «Продолжить» ниже.", - "VERIFICATION_TITLE": "Полная проверка", - "WITHDRAW_NOTE": "Обратите внимание: вы можете вывести средства только на счет, открытый на ваше имя.", - "USER_PAYMENT": { "TITLE": "Платежи" }, - "QUOTE_CONFIRMATION_MSG_TEXT_1": "Пожалуйста, проверьте свой заказ и подтвердите его ниже.", - "QUOTE_CONFIRMATION_MSG_TEXT_2": "Сумма к получению является приблизительной и не включает комиссию за торговлю.", - "ORDER_EXPIRED_MSG": "Срок действия заказа истек. Пожалуйста, обновите!", - "WITHDRAWALS_FORM_METHOD": "Метод", - "WITHDRAWALS_FORM_ADDRESS_EXCHANGE": "Обмен электронной почтой пользователя", - "WITHDRAWALS_FORM_EXCHANGE_PLACEHOLDER": "Введите адрес электронной почты пользователя на этой бирже", - "WITHDRAWALS_FORM_MAIL_INFO": "Введите адрес электронной почты учетной записи пользователя HollaEx, который находится на этой бирже, и переведите его бесплатно.", - "DUST": { - "TITLE": "Кошелек тряпка", - "LINK": "Кошелек тряпка", - "TOOLTIP": "Конвертируйте крошечные остатки кошелька (пыль) в криптовалюту", - "BACK_PLACEHOLDER": "{0} {1}", - "BACK_TO_WALLET": "в мой кошелек", - "BACK": "Назад", - "ESTIMATED_TOTAL": "Расчетное общее количество пыли", - "CONVERT_ALL": "конвертировать", - "NO_DUST": "Никаких пылевых активов!", - "SECTION": { - "TITLE": "Преобразовать бумажную пыль", - "TEXT_1": "Преобразуйте все низкие остатки кошелька, которые содержат значение {0} {1} или меньше.", - "TEXT_2": "Все разговоры будут урегулированы в {0} ({1})." - }, - "CONFIRMATION": { - "TITLE": "Преобразование пыли", - "SUBTITLE": "Преобразуемые активы", - "BACK": "назад", - "CONFIRM": "подтверждать", - "GET": "Ты получишь", - "FEE": "Плата за конвертацию", - "PLEASE": "Пожалуйста, обрати внимание", - "NOTE": "Преобразовывать пыль в {0} можно каждые 24 часа. Будут преобразованы только балансы со значением менее {1} {2}." - }, - "SUCCESSFUL": { - "TITLE": "Преобразование пыли успешно!", - "TEXT": "Вы получили", - "VIEW_HISTORY": "Посмотреть историю", - "CLOSE": "закрывать" - } - }, - "ASSET_INFO_PAGE": "Страница информации об объекте", - "MARKET_ROW": { "NATIVE": "Родной", "NO_NATIVE": "Нет родного источника" }, - "DIGITAL_ASSETS": { - "DIGITAL_ASSETS_TITLE": "Цифровые активы", - "GO_BACK": "Возвращаться", - "ASSETS_INFO": "Ниже представлены доступные активы на платформе.", - "ASSETS_INFO_DETAIL": "Вы можете щелкнуть актив в списке ниже, чтобы узнать больше.", - "QUICK_TRADE": "БЫСТРАЯ ТОРГОВЛЯ", - "MARKETS": "РЫНКИ", - "WALLET": "КОШЕЛЕК", - "PRICE_SOURCE": "Источник цены" - }, - "HOLLAEX_TOKEN": { - "REMOVE_FAVOURITES": "Удалить из избранного", - "ADD_FAVOURITES": "Добавить в избранное", - "GO_BACK": " Возвращаться", - "BALANCE": "Баланс:", - "OPEN_WALLET": "(Открыть кошелек)", - "ABOUT": "О", - "VIEW": "Посмотреть больше", - "COMING_SOON": "Вскоре...", - "NO_DESCRIPTION": "Этот цифровой актив в настоящее время не имеет описания.", - "STAKE": "Ставка", - "MORE": "Более", - "INFO": "Информация", - "TRADE": "ТОРГОВЛЯ", - "LIST": "Хотите перечислить свои цифровые активы?", - "WEBSITE": "Веб-сайт", - "EXPLORER": "Исследователь" - }, - "ACCORDIAN": { - "ACCORDIAN_ASSETS": "Все активы кошелька", - "ACCORDIAN_INFO": "СТРАНИЦА ИНФОРМАЦИИ ОБ АКТИВАХ", - "ACCORDIAN_HISTORY": "ИСТОРИЯ" - }, - "ASYNC_LINK": { - "TITLE": "Страница не открывается автоматически?", - "TEXT": "Ваш браузер может блокировать страницу. Вместо этого попробуйте скопировать и вставить ссылку ниже:" - }, - "TERMS_OF_SERVICES": { - "TO_GET_ACCESS": "Чтобы получить доступ, обратитесь к" - }, - "FEES_AND_LIMITS": { - "LINK": "просмотреть мои сборы и лимиты", - "COIN_PAGE_LINK": "Торговые сборы и лимиты", - "BACK": { - "PLACEHOLDER": "{0} {1}", - "BACK": "Возвращаться", - "TO": "резюмировать" - }, - "TITLE": "Комиссии и лимиты", - "SEARCH_PLACEHOLDER": "Поиск...", - "TABS": { - "TRADING_FEES": { - "TITLE": "Торговые сборы", - "TABLE": { - "TITLE": "Торговые сборы на рынке", - "SUBTITLE": "Ниже приведены правила торговых комиссий для отдельных рынков.", - "HEADER": { - "MARKET": "Рынок", - "MAKER": "Торговые сборы Maker (лимитные ордера)", - "TAKER": "Торговые сборы тейкера (рыночные ордера)" - } - } - }, - "WITHDRAWAL_FEES": { - "TITLE": "Комиссия за снятие денег", - "TABLE": { - "TITLE": "Комиссия удерживается при выводе средств", - "SUBTITLE": "Ниже приведены все комиссии при выводе актива.", - "NOT_ALLOWED": "Не допускается", - "HEADER": { - "CURRENCY": "Валюта/актив", - "DEPOSIT": "Комиссия за депозит", - "WITHDRAWAL": "Комиссия за снятие средств" - } - } - }, - "WITHDRAWAL_LIMITS": { - "TITLE": "Лимиты на вывод", - "TABLE_1": { - "TITLE": "Независимый лимит вывода", - "SUBTITLE": "Активы, которые отделены от правила коллективного лимита, которые следуют собственному независимому правилу вывода средств в течение 24 часов:", - "HEADER": { "CURRENCY": "Валюта/актив", "LIMIT": "24-часовой лимит" }, - "LIMIT_TEXT": "(Активы, которые имеют общую сумму лимита снятия)" - }, - "TABLE_2": { - "TITLE": "Коллективный общий стандартный лимит на снятие средств", - "SUBTITLE": "Ниже приведены активы, для которых действует одно и то же стандартное правило суммы вывода средств за 24 часа:" - } - } - } - }, - "STATUS_TEXT": { - "INCOMPLETE": "неполный", - "PENDING": "в ожидании", - "REJECTED": "отклоненный", - "VERIFIED": "проверено" - }, - "LOGINS_HISTORY": { - "TAB": "История входа", - "CONTENT": { - "TITLE": "Запись попыток входа", - "SUBTITLE": "Историческая запись всех неудачных попыток входа в вашу учетную запись и их потенциальное происхождение.", - "DOWNLOAD_HISTORY": "СКАЧАТЬ_ИСТОРИЯ", - "FILTER": { - "STATUS": "Положение дел", - "ALL": "Все", - "FAILED": "Неуспешный", - "SUCCESS": "Успех" - }, - "TABLE": { - "HEADER": { - "LAST_SEEN": { - "TITLE": "Последнее посещение", - "DESCRIPTION": "(Самый недавний в начале)" - }, - "FAILED_ATTEMPTS": "Неудачные попытки", - "COUNTRY": "Страна", - "IP_ADDRESS": "Айпи адрес" - }, - "CELL": { - "FAILED_LOGIN": "Неудачный вход: {0}x", - "SUCCESS": "Успех", - "DEVICE": "Устройство" - } - } - } - }, - "SESSIONS": { - "TAB": "Сессии", - "CONTENT": { - "TITLE": "Активная сессия", - "SUBTITLE": { - "TEXT": "Ниже приведены активные сеансы вашей учетной записи. Чтобы завершить активный сеанс, нажмите {0}, и он выйдет из этого сеанса.", - "LINK": "Отозвать" - }, - "DOWNLOAD": "Скачать", - "TABLE": { - "HEADER": { - "LAST_SEEN": { - "TITLE": "Последнее посещение", - "DESCRIPTION": "(Самый недавний в начале)" - }, - "SESSION_STARTED": "Сеанс начался", - "SESSION_EXPIRY": "Срок действия сеанса", - "STATUS": "Положение дел" - }, - "CELL": { - "ACTIVE": "Активный", - "COUNTRY": "Страна", - "IP_ADDRESS": "Айпи адрес", - "DEVICE": "Устройство", - "LOGIN_TIME": "Время успешного входа" - } - }, - "NOTIFICATION": "Сессия отменена/выход из системы", - "MODAL": { - "TITLE": "Отменить сеанс", - "PROMPT": "Вы уверены, что хотите отозвать и выйти из этой сессии?", - "BACK": "НАЗАД", - "CONFIRM": "ПОДТВЕРЖДАТЬ" - } - } - } -} + "APP_TITLE": "HollaEx", + "APP_SUB_TITLE": "Открыть криптобиржу", + "LOGOUT_CONFIRM_TEXT": "Вы действительно хотите выйти?", + "ADD_TRADING_PAIR": "Выберите рынок", + "CANCEL_BASE_WITHDRAWAL": "Отменить {0} снятие средств", + "CANCEL_WITHDRAWAL": "Отменить вывод средств", + "CANCEL_WITHDRAWAL_POPUP_CONFIRM": "Вы хотите отменить ожидающий отзыв:", + "NO_ACTIVE_ORDERS": "Заказов не обнаружено. Размещайте ордера на странице Pro или Quick trade", + "NO_ACTIVE_TRADES": "Вроде пока нет сделок", + "NO_ACTIVE_DEPOSITS": "Депозитов вроде пока нет.", + "NO_ACTIVE_WITHDRAWALS": "Выводов пока нет", + "LOGIN_TEXT": "Авторизоваться", + "SIGN_IN": "Войти", + "SIGNUP_TEXT": "Зарегистрироваться", + "REGISTER_TEXT": "регистр", + "ACCOUNT_TEXT": "Счет", + "CLOSE_TEXT": "Закрывать", + "COPY_TEXT": "Копировать", + "COPY_SUCCESS_TEXT": "Успешно скопировано", + "CANCEL_SUCCESS_TEXT": "Успешно отменено!", + "ADD_FILES": "ДОБАВИТЬ ФАЙЛЫ", + "OR_TEXT": "Или", + "CONTACT_US_TEXT": "Связаться с нами", + "HELPFUL_RESOURCES_TEXT": "Полезные ресурсы", + "HELP_RESOURCE_GUIDE": { + "CONTACT_US": "связаться с нами", + "TEXT": "Не стесняйтесь {0} для получения дополнительной информации и любых проблем, отправив нам электронное письмо" + }, + "HELP_TELEGRAM_TEXT": "Ознакомьтесь с открытой документацией по API:", + "HELP_TELEGRAM_LINK": "https://apidocs.hollaex.com", + "NEED_HELP_TEXT": "Нужна помощь?", + "HELP_TEXT": "помощь", + "SUCCESS_TEXT": "Успех", + "ERROR_TEXT": "Ошибка", + "PROCEED": "ПРОДОЛЖИТЬ", + "EDIT_TEXT": "Редактировать", + "BACK_TEXT": "Назад", + "NO_OPTIONS": "Нет доступных вариантов", + "SECONDS": "секунды", + "VIEW_MARKET": "просмотреть рынки", + "GO_TRADE": "Начни торговать", + "VIEW_INFO": "Посмотреть информационную страницу", + "APPLY_HERE": "Подать заявку здесь", + "CONVERT": "Конвертировать", + "SWAP": "Выключатель", + "TO": "К", + "NA": "Н/Д", + "HOME": { + "MAIN_TITLE": "Биржа торговли криптовалютой", + "MAIN_TEXT": "Легко покупать и продавать криптоактивы. Просто зарегистрируйтесь со своей электронной почтой и торгуйте основными криптоактивами 24/7.", + "TRADE_CRYPTO": "Начать торговлю", + "VIEW_EXCHANGE": "Посмотреть обмен" + }, + "FOOTER": { + "FOOTER_LANGUAGE_TEXT": "ЯЗЫК", + "TERMS_OF_SERVICE": "Условия использования", + "PRIVACY_POLICY": "политика конфиденциальности", + "CLICK_HERE": "кликните сюда", + "VISIT_HERE": "посетите здесь" + }, + "ACCOUNTS": { + "TITLE": "Счет", + "TAB_VERIFICATION": "Проверка", + "TAB_SECURITY": "Безопасность", + "TAB_SETTINGS": "Настройки", + "TAB_APPS": "Программы", + "TAB_WALLET": "Кошелек", + "TAB_SUMMARY": "Краткое содержание", + "TAB_HISTORY": "История", + "TAB_SIGNOUT": "Выход", + "TAB_STAKE": "Ставка", + "TAB_FIAT": "Фиат контролирует", + "TAB_TRADE": "Торговля", + "PRO_TRADE": "Про", + "QUICK_TRADE": "Быстрый" + }, + "CONTACT_FORM": { + "CATEGORY_LABEL": "Категория", + "CATEGORY_PLACEHOLDER": "Выберите категорию, которая лучше всего подходит для вашей проблемы", + "CATEGORY_OPTIONS": { + "OPTION_VERIFY": "Проверка пользователя", + "OPTION_LEVEL": "Повысить уровень пользователя", + "OPTION_DEPOSIT": "Депозит и вывод", + "OPTION_BUG": "Сообщить об ошибке", + "OPTION_PERSONAL_INFO": "Изменить личную информацию", + "OPTION_BANK_TRANSFER": "Банковский перевод", + "OPTION_REQUEST": "Запросить приглашение на биржу HollaEx" + }, + "SUBJECT_LABEL": "Предмет", + "SUBJECT_PLACEHOLDER": "Введите тему вашего вопроса", + "DESCRIPTION_LABEL": "Описание", + "DESCRIPTION_PLACEHOLDER": "Напишите подробно в чем проблема", + "ATTACHMENT_LABEL": "Добавить вложения (максимум 3)", + "ATTACHMENT_PLACEHOLDER": "Добавьте файл, чтобы сообщить о своей проблеме. Принимаются файлы PDF, JPG, PNG и GIF.", + "SUCCESS_TITLE": "Сообщение отправлено", + "SUCCESS_MESSAGE_1": "Ваша проблема была отправлена в службу поддержки.", + "SUCCESS_MESSAGE_2": "Ожидайте ответа в течение 1-3 дней." + }, + "DEPOSIT": { + "CRYPTO_LABELS": { + "ADDRESS": "Ваш {0} адрес получателя", + "DESTINATION_TAG": "Ваш целевой тег {0}", + "MEMO": "Ваша заметка {0}" + }, + "QR_CODE": "Этот QR-код содержит информацию о депозите и может быть просканирован с помощью считывателя QR-кодов.", + "NO_DATA": "Информация отсутствует", + "QR_CODE_TITLE": "{0} Информация о депозите" + }, + "QR_CODE": { + "SCAN": "Сканировать", + "SHOW": "Показать QR-код", + "NO_RESULT": "Безрезультатно", + "NOT_FOUND": "Не найдено", + "PERMISSION_DENIED": "Доступ запрещен" + }, + "LOGIN": { + "LOGIN_TO": "Войти в {0}", + "CANT_LOGIN": "Не можете войти?", + "NO_ACCOUNT": "У вас нет аккаунта?", + "CREATE_ACCOUNT": "Создайте здесь", + "LOOKING_PRICES": "Ищете цены?", + "VIEW_MARKETS": "Посмотреть рынки", + "HELP": "Помощь" + }, + "FORM_FIELDS": { + "EMAIL_LABEL": "Электронная почта", + "EMAIL_PLACEHOLDER": "Введите адрес электронной почты", + "PASSWORD_LABEL": "Пароль", + "PASSWORD_PLACEHOLDER": "Введите свой пароль", + "PASSWORD_REPEAT_LABEL": "Введите свой пароль снова", + "PASSWORD_REPEAT_PLACEHOLDER": "Введите свой пароль снова" + }, + "VALIDATIONS": { + "OTP_LOGIN": "Введите OTP-код для входа", + "CAPTCHA": "Просроченная сессия. Пожалуйста, обновите страницу", + "FROZEN_ACCOUNT": "Этот аккаунт заморожен", + "INVALID_EMAIL": "Неверный адрес электронной почты", + "TYPE_EMAIL": "Введите адрес электронной почты", + "REQUIRED": "Обязательное поле", + "INVALID_DATE": "Недействительная дата", + "INVALID_PASSWORD": "Неверный пароль. Он должен содержать не менее 8 символов, цифру в пароле и специальный символ.", + "INVALID_PASSWORD_2": "Неверный пароль. Он должен содержать не менее 8 символов, не менее одной цифры и одного символа.", + "INVALID_CURRENCY": "Недопустимый адрес {0} ({1})", + "INVALID_BALANCE": "Недостаточно доступного баланса ({0}) для выполнения операции ({1}).", + "MIN_VALUE": "Значение должно быть {0} или выше.", + "MAX_VALUE": "Значение должно быть {0} или меньше.", + "MIN_VALUE_NE": "Значение должно быть больше {0}.", + "MAX_VALUE_NE": "Значение должно быть меньше {0}.", + "INSUFFICIENT_BALANCE": "Недостаточный баланс", + "PASSWORDS_DONT_MATCH": "Пароль не подходит", + "USER_EXIST": "Электронная почта уже зарегистрирована", + "ACCEPT_TERMS": "Вы не согласны с Условиями использования и Политикой конфиденциальности", + "STEP": "Недопустимое значение, шаг {0}", + "ONLY_NUMBERS": "Значение может содержать только числа" + }, + "NOTIFICATIONS": { + "BUTTONS": { + "OKAY": "Хорошо", + "START_TRADING": "начать торговать", + "SEE_HISTORY": "посмотреть историю" + }, + "DEPOSITS": { + "TITLE_RECEIVED": "{0} Депозит получен", + "TITLE_INCOMING": "Входящий {0}", + "SUBTITLE_RECEIVED": "Вы получили депозит {0}", + "SUBTITLE_INCOMING": "У вас есть входящие {0}", + "INFORMATION_PENDING_1": "Ваш {0} требует 1 подтверждения, прежде чем вы сможете начать торговлю.", + "INFORMATION_PENDING_2": "Это может занять 10-30 минут. Мы отправим электронное письмо, как только ваш {0} будет подтвержден в блокчейне." + } + }, + "REFERRAL_SUCCESS": { "TITLE": "Запрос отправлен", "BUTTON_TEXT": "Хорошо" }, + "OTP_FORM": { + "OTP_FORM_TITLE": "Введите код аутентификации, чтобы продолжить", + "OTP_FORM_INFO": "Введите свой 6-значный код, чтобы продолжить", + "OTP_FORM_SUBNOTE_LINE_1": "Ваш код также известен как двухфакторный аутентификатор (2FA) или OTP-код.", + "OTP_FORM_SUBNOTE_LINE_2": "Если вы потеряли код, обратитесь в службу поддержки.", + "OTP_LABEL": "OTP-код", + "OTP_PLACEHOLDER": "Введите код аутентификации", + "OTP_TITLE": "Код аутентификатора", + "OTP_BUTTON": "представлять на рассмотрение", + "ERROR_INVALID": "Неверный OTP-код", + "OTP_FORM_SUBNOTE_LINE_3": "(Чтобы добавить новую учетную запись в Google Authenticator, нажмите кнопку «+». Вы можете либо отсканировать предоставленный QR-код на предыдущем шаге, либо вручную ввести ключ, чтобы добавить свою учетную запись в приложение.)", + "INPUT_TEXT": "Введите 6-значный код из приложения Google Authenticator на вашем телефоне:" + }, + "EMAIL_CODE_FORM": { + "TITLE": "Введите коды безопасности", + "LABEL": "Введите код (пожалуйста, проверьте свою электронную почту)", + "PLACEHOLDER": "Введите код, отправленный на вашу электронную почту", + "FORM_TITLE": "На ваш адрес электронной почты был отправлен уникальный код, необходимый для завершения процесса. Пожалуйста, введите код, отправленный на вашу электронную почту ниже, вместе с кодом OTP.", + "BUTTON": "представлять на рассмотрение", + "ERROR_INVALID": "Код, который вы ввели, неверен. Пожалуйста, попробуйте еще раз", + "OTP_LABEL": "Код 2FA (OTP)", + "OTP_PLACEHOLDER": "Введите 6-значный код двухфакторной аутентификации.", + "FORM_TITLE_TEXT": "На ваш адрес электронной почты был отправлен уникальный код, необходимый для завершения процесса. Введите ниже код, отправленный на ваш адрес электронной почты{0}.", + "OTP_NOTE": " вместе с вашим OTP-кодом" + }, + "QUICK_TRADE_COMPONENT": { + "TITLE": "Быстрая торговля", + "BUTTON": "Просмотреть заказ", + "INFO": "Самый быстрый и простой способ обменять вашу криптовалюту", + "CHANGE_TEXT": "изменять", + "HIGH_24H": "24Ч ВЫСОКИЙ", + "LOW_24H": "24 ч НИЗКИЙ", + "BEST_BID": "ЛУЧШЕЕ ПРЕДЛОЖЕНИЕ", + "BEST_ASK": "ЛУЧШИЙ СПРОС", + "FOOTER_TEXT": "Плата за быструю торговлю использует ставки маркет-тейкера", + "FOOTER_TEXT_1": "Источник из", + "GO_TO_TEXT": "Идти к", + "SOURCE_TEXT": "Брокерская внебиржевая сделка", + "SOURCE_TEXT_NETWORK": "Внебиржевая сделка сетевого брокера", + "VISIT": "Посетите{0}", + "ASSET_INFO_PAGE": "Страница информации об активах", + "CHANGE_TEXT_7D": "7D Изменение", + "HIGH_7D": "7D ВЫСОКИЙ", + "LOW_7D": "7D НИЗКИЙ", + "7D": "7Д", + "1D": "1Д", + "COIN_NAME": "{0} ({1})", + "COIN_INFORMATION": "{0} информация", + "PRICE_TREND": "Ценовой тренд", + "VIEW_TREND": "Посмотреть тренд" + }, + "PREVIOUS_PAGE": "Предыдущая страница", + "NEXT_PAGE": "Следующая Страница", + "WALLET": { + "LOADING_ASSETS": "Загрузка ресурсов...", + "TOTAL_ASSETS": "Всего активов", + "AVAILABLE_WITHDRAWAL": "Доступно для торговли", + "ORDERS_PLURAL": "заказы", + "ORDERS_SINGULAR": "заказ", + "HOLD_ORDERS": "У вас {0} открыто {1}, в результате чего {2} {3} заблокировано на вашем балансе {4}" + }, + "REQUEST_RESET_PASSWORD": { + "TITLE": "Восстановление аккаунта", + "SUBTITLE": "Восстановите свою учетную запись ниже", + "SUPPORT": "Контактная поддержка", + "BUTTON": "Отправить ссылку для восстановления" + }, + "REQUEST_RESET_PASSWORD_SUCCESS": { + "TITLE": "Сброс пароля отправлен", + "TEXT": "Если для адреса электронной почты существует учетная запись, на нее было отправлено электронное письмо с инструкциями по сбросу. Пожалуйста, проверьте свою электронную почту и нажмите на ссылку, чтобы завершить сброс пароля." + }, + "RESET_PASSWORD": { + "TITLE": "Установить новый пароль", + "SUBTITLE": "Установить новый пароль", + "BUTTON": "Установить новый пароль" + }, + "RESET_PASSWORD_SUCCESS": { + "TEXT_1": "Вы успешно установили новый пароль.", + "TEXT_2": "Нажмите «Войти» ниже, чтобы продолжить." + }, + "SIGN_UP": { + "SIGNUP_TO": "Зарегистрируйтесь в {0}", + "NO_EMAIL": "Не получили письмо?", + "REQUEST_EMAIL": "Запросите еще один здесь", + "HAVE_ACCOUNT": "У вас уже есть аккаунт?", + "GOTO_LOGIN": "Перейти на страницу входа", + "AFFILIATION_CODE": "Реферальный код (необязательно)", + "AFFILIATION_CODE_PLACEHOLDER": "Введите свой реферальный код", + "TERMS": { + "terms": "Общие условия", + "policy": "политика конфиденциальности", + "text": "Я прочитал и согласен с {0} и {1}" + } + }, + "VERIFICATION_TEXTS": { + "TITLE": "Письмо отправлено", + "TEXT_1": "Проверьте свою электронную почту и нажмите на ссылку, чтобы подтвердить свою электронную почту.", + "TEXT_2": "Если вы не получили подтверждения по электронной почте и проверили нежелательную почту, вы можете попробовать нажать кнопку «Отправить повторно» ниже." + }, + "VERIFICATION_EMAIL_REQUEST": { + "TITLE": "Повторно отправить запрос по электронной почте", + "BUTTON": "Электронная почта запроса", + "SUBTITLE": "Сделайте еще один запрос на подтверждение электронной почты ниже", + "SUPPORT": "Контактная поддержка" + }, + "VERIFICATION_EMAIL_REQUEST_SUCCESS": { + "TITLE": "Повторное письмо", + "TEXT_1": "Если через несколько минут вы все еще не получили подтверждение по электронной почте, пожалуйста, свяжитесь с нами ниже." + }, + "VERIFICATION_EMAIL": { + "INVALID_UUID": "Неверный код", + "TEXT_1": "Вы успешно подтвердили свою электронную почту.", + "TEXT_2": "Теперь вы можете перейти к входу в систему", + "CANCEL_BUTTON": "ЗАКРЫТЬ", + "CONFIRM_BUTTON": "ПОДТВЕРДИТЬ РЕГИСТРАЦИЮ", + "CONFIRM_TEXT": "Подтвердить регистрацию", + "CONFIRM_DESCRIPTION": "Пожалуйста, подтвердите регистрацию вашей учетной записи, нажав на кнопку ниже" + }, + "USER_VERIFICATION": { + "INFO_TXT": "Здесь вы можете следить за своим прогрессом в проверке и обновлении учетной записи.", + "INFO_TXT_1": "Пожалуйста, отправьте соответствующую информацию, необходимую для каждого раздела ниже. Только после того, как все разделы будут заполнены, ваша информация будет рассмотрена и одобрена для повышения уровня учетной записи.", + "COMPLETED": "Завершенный", + "PENDING_VERIFICATION": "Ожидает подтвержения", + "TITLE_EMAIL": "Электронная почта", + "MY_EMAIL": "Моя электронная почта", + "TITLE_USER_DOCUMENTATION": "Идентификация", + "TITLE_ID_DOCUMENTS": "Загрузить", + "TITLE_BANK_ACCOUNT": "Банковский счет", + "TITLE_MOBILE_PHONE": "Мобильный телефон", + "TITLE_PERSONAL_INFORMATION": "Персональная информация", + "VERIFY_EMAIL": "Подтвердить Email", + "VERIFY_MOBILE_PHONE": "Подтвердить мобильный телефон", + "VERIFY_USER_DOCUMENTATION": "Проверка пользовательской документации", + "VERIFY_ID_DOCUMENTS": "Подтвердить документы, удостоверяющие личность", + "TITLE_IDENTITY": "Личность", + "TITLE_MOBILE": "Мобильный", + "TITLE_BANK": "Банк", + "CHANGE_VALUE": "Изменить значение", + "PENDING_VERIFICATION_PERSONAL_INFORMATION": "Ваша личная информация обрабатывается", + "PENDING_VERIFICATION_DOCUMENTS": "Ваши документы проходят проверку", + "GOTO_VERIFICATION": "Перейти к проверке", + "GOTO_WALLET": "Перейти к кошельку", + "INCOMPLETED": "Незавершенный", + "BANK_VERIFICATION": "Банковская проверка", + "IDENTITY_VERIFICATION": "Проверка личности", + "PHONE_VERIFICATION": "Проверка телефона", + "DOCUMENT_VERIFICATION": "Проверка документов", + "START_BANK_VERIFICATION": "Начать проверку банка", + "START_IDENTITY_VERIFICATION": "Начать проверку личности", + "START_PHONE_VERIFICATION": "Начать проверку телефона", + "START_DOCUMENTATION_SUBMISSION": "Начать подачу документации", + "GO_BACK": "Возвращаться", + "BANK_VERIFICATION_TEXT_1": "Вы можете добавить свои банковские счета здесь и получить подтверждение. Международные банковские счета потребуют от вас обращения в службу поддержки и будут иметь ограниченные лимиты на снятие средств.", + "BANK_VERIFICATION_TEXT_2": "Подтвердив свой банковский счет, вы можете получить следующее:", + "BASE_WITHDRAWAL": "вывод фиата", + "BASE_DEPOSITS": "Фиатные депозиты", + "ADD_ANOTHER_BANK_ACCOUNT": "Добавить еще один банковский счет", + "BANK_NAME": "Название банка", + "ACCOUNT_NUMBER": "Номер счета", + "BANK_VERIFICATION_HELP_TEXT": "Чтобы этот раздел был проверен, вы должны заполнить раздел {0}.", + "DOCUMENT_SUBMISSION": "Подача документов", + "REVIEW_IDENTITY_VERIFICATION": "Проверить верификацию личности", + "PHONE_DETAILS": "Детали телефона", + "PHONE_COUNTRY_ORIGIN": "Страна происхождения телефона", + "MOBILE_NUMBER": "Номер мобильного телефона", + "DOCUMENT_PROOF_SUBMISSION": "Предоставление подтверждающих документов", + "START_DOCUMENTATION_RESUBMISSION": "Начать повторную подачу документации", + "SUBMISSION_PENDING_TXT": "*Этот раздел уже отправлен. Внесение изменений и повторная отправка заменят вашу предыдущую информацию.", + "CUSTOMER_SUPPORT_MESSAGE": "Сообщение службы поддержки", + "DOCUMENT_PENDING_NOTE": "Ваши документы отправлены и ожидают рассмотрения. Пожалуйста, будьте терпеливы.", + "DOCUMENT_VERIFIED_NOTE": "Ваши документы готовы.", + "NOTE_FROM_VERIFICATION_DEPARTMENT": "Справка из отдела проверки", + "CODE_EXPIRES_IN": "Срок действия кода истекает через", + "EMAIL_VERIFICATION": "Отправить письмо с подтверждением", + "VERIFICATION_SENT": "Подтверждение отправлено", + "VERIFICATION_SENT_INFO": "Проверьте свою электронную почту и нажмите на ссылку, чтобы подтвердить электронную почту.", + "OKAY": "Хорошо", + "USER_DOCUMENTATION_FORM": { + "FORM_FIELDS": { + "FULL_NAME_LABEL": "Ваше полное имя", + "FULL_NAME_PLACEHOLDER": "Введите свое полное имя, как оно указано в документе, удостоверяющем личность.", + "GENDER_LABEL": "Пол", + "GENDER_PLACEHOLDER": "Укажите свой пол", + "GENDER_OPTIONS": { "MAN": "Мужской", "WOMAN": "Женский" }, + "NATIONALITY_LABEL": "Национальность", + "NATIONALITY_PLACEHOLDER": "Укажите национальность в вашем документе, удостоверяющем личность", + "DOB_LABEL": "Дата рождения", + "COUNTRY_LABEL": "Страна вашего проживания", + "COUNTRY_PLACEHOLDER": "Выберите страну, в которой вы проживаете в настоящее время", + "CITY_LABEL": "Город", + "CITY_PLACEHOLDER": "Введите город, в котором вы живете", + "ADDRESS_LABEL": "Адрес", + "ADDRESS_PLACEHOLDER": "Введите адрес, по которому вы сейчас проживаете", + "POSTAL_CODE_LABEL": "Почтовый индекс", + "POSTAL_CODE_PLACEHOLDER": "Введите свой почтовый индекс", + "PHONE_CODE_LABEL": "Страна", + "PHONE_CODE_PLACEHOLDER": "Выберите страну, к которой подключен ваш телефон", + "PHONE_CODE_DISPLAY": "({0}) {1}", + "PHONE_NUMBER_LABEL": "Номер телефона", + "PHONE_NUMBER_PLACEHOLDER": "Введите свой номер телефона", + "CONNECTING_LOADING": "Подключение", + "SMS_SEND": "Отправить смс", + "SMS_CODE_LABEL": "SMS-код", + "SMS_CODE_PLACEHOLDER": "Введите код из СМС" + }, + "INFORMATION": { + "TEXT": "ВАЖНО: Введите свое имя в поля точно так, как оно указано в вашем документе, удостоверяющем личность (полное имя, любые отчества/инициалы и полные фамилии). Вы бизнес? Обратитесь в службу поддержки для корпоративной учетной записи.", + "TITLE_PERSONAL_INFORMATION": "Персональная информация", + "TITLE_PHONE": "Телефон", + "PHONE_VERIFICATION_TXT": "Предоставление действительных контактных данных очень поможет нам в разрешении конфликтов и предотвращении нежелательных транзакций в вашей учетной записи.", + "PHONE_VERIFICATION_TXT_1": "Получайте в режиме реального времени обновления для депозитов и снятия средств, поделившись своим номером мобильного телефона.", + "PHONE_VERIFICATION_TXT_2": "Далее подтвердите свою личность и адрес, предоставив свой номер телефона в локальной сети (необязательно)." + } + }, + "ID_DOCUMENTS_FORM": { + "VALIDATIONS": { + "ID_NUMBER": "Пожалуйста, введите номер ваших документов", + "ISSUED_DATE": "Пожалуйста, выберите дату, когда был выдан ваш документ", + "EXPIRATION_DATE": "Пожалуйста, выберите дату истечения срока действия вашего документа", + "FRONT": "Пожалуйста, загрузите скан вашего паспорта или национального удостоверения личности", + "PROOF_OF_RESIDENCY": "Пожалуйста, загрузите отсканированный документ, подтверждающий адрес вашего текущего проживания.", + "SELFIE_PHOTO_ID": "Пожалуйста, загрузите селфи с паспортом или национальным удостоверением личности и отметьте" + }, + "FORM_FIELDS": { + "ID_NUMBER_LABEL": "Номер паспорта или национальный идентификационный номер", + "ID_NUMBER_PLACEHOLDER": "Введите номер своего паспорта или национальный идентификационный номер", + "ISSUED_DATE_LABEL": "Дата выдачи паспорта или национального удостоверения личности", + "EXPIRATION_DATE_LABEL": "Срок действия паспорта или национального удостоверения личности", + "FRONT_LABEL": "Паспорт или национальное удостоверение личности", + "FRONT_PLACEHOLDER": "Добавьте копию своего паспорта или национального удостоверения личности", + "POR_LABEL": "Документ, подтверждающий ваш адрес", + "POR_PLACEHOLDER": "Добавьте копию документа, подтверждающего ваш адрес", + "SELFIE_PHOTO_ID_LABEL": "Ваше селфи с паспортом или национальным удостоверением личности и заметкой", + "SELFIE_PHOTO_ID_PLACEHOLDER": "Добавьте копию своего селфи с паспортом или национальным удостоверением личности и заметку" + }, + "INFORMATION": { + "PROOF_OF_RESIDENCY": "Подтверждение проживания", + "ID_SECTION": { + "TITLE": "Пожалуйста, убедитесь, что представленные документы:", + "LIST_ITEM_0": "Общий размер всех документов не должен превышать {0} МБ.", + "LIST_ITEM_1": "Четкое цветное изображение высокого качества", + "LIST_ITEM_2": "ВИДИМЫ ПОЛНОСТЬЮ (водяные знаки разрешены).", + "LIST_ITEM_3": "ДЕЙСТВИТЕЛЬНО, с ясно видимой датой истечения срока действия.", + "WARNING_1": "Принимается только действующий паспорт; Допускаются высококачественные фотографии или отсканированные изображения этих документов:", + "WARNING_3": "Пожалуйста, не предоставляйте паспорт в качестве подтверждения проживания.", + "VIOLATION_ERROR": "Общий размер всех загруженных вами документов превышает ограничение на загрузку в {0} МБ. Пожалуйста, загрузите файлы меньшего размера, чтобы продолжить." + }, + "POR": { + "SECTION_1_TEXT_1": "Во избежание задержек при подтверждении учетной записи убедитесь, что:", + "SECTION_1_TEXT_2": "Ваше ИМЯ, АДРЕС, ДАТА ВЫДАЧИ и ЭМИТЕНТ четко видны.", + "SECTION_1_TEXT_3": "Представленный документ, подтверждающий место жительства, НЕ СТАРШЕ ТРЕХ МЕСЯЦЕВ.", + "SECTION_1_TEXT_4": "Вы отправляете цветные фотографии или отсканированные изображения в ВЫСОКОМ КАЧЕСТВЕ (не менее 300 DPI)", + "SECTION_2_TITLE": "ПРИЕМЛЕМЫМ ДОКАЗАТЕЛЬСТВОМ ПРОЖИВАНИЯ ЯВЛЯЕТСЯ:", + "SECTION_2_LIST_ITEM_1": "Выписка с банковского счета.", + "SECTION_2_LIST_ITEM_2": "Коммунальные платежи (электричество, вода, интернет и т.д.).", + "SECTION_2_LIST_ITEM_3": "Документ государственного образца (налоговая декларация, свидетельство о резидентстве и т.д.).", + "WARNING": "Мы не можем принять адрес, указанный в представленном вами документе, удостоверяющем личность, в качестве действительного подтверждения места жительства." + }, + "SELFIE": { + "TITLE": "Селфи с паспортом и запиской", + "INFO_TEXT": "Пожалуйста, предоставьте фотографию, на которой вы держите паспорт. На том же изображении есть ссылка на URL-адрес обмена, сегодняшняя дата и ваша подпись. Убедитесь, что ваше лицо хорошо видно, а данные вашего удостоверения личности хорошо читаются.", + "REQUIRED": "Необходимый:", + "INSTRUCTION_1": "Ваше лицо ясно видно", + "INSTRUCTION_2": "Паспорт хорошо читается", + "INSTRUCTION_3": "Напишите название биржи", + "INSTRUCTION_4": "Напишите сегодняшнюю дату", + "INSTRUCTION_5": "Напишите свою подпись", + "WARNING": "Селфи с другим паспортом с загруженным контентом будет отклонено" + } + } + }, + "BANK_ACCOUNT_FORM": { + "VALIDATIONS": { + "ACCOUNT_NUMBER_MAX_LENGTH": "Номер вашего банковского счета имеет ограничение в 50 символов" + }, + "FORM_FIELDS": { + "BANK_NAME_LABEL": "Название банка", + "BANK_NAME_PLACEHOLDER": "Введите название вашего банка", + "ACCOUNT_NUMBER_LABEL": "Номер банковского счета", + "ACCOUNT_NUMBER_PLACEHOLDER": "Введите номер своего банковского счета", + "ACCOUNT_OWNER_LABEL": "Имя владельца банковского счета", + "CARD_NUMBER_PLACEHOLDER": "Введите 16-значный номер, указанный на лицевой стороне вашей банковской карты." + } + }, + "WARNING": { + "TEXT_1": "Подтвердив свою личность, вы можете получить следующее:", + "LIST_ITEM_1": "Увеличенные лимиты вывода", + "LIST_ITEM_2": "Увеличенные лимиты депозитов", + "LIST_ITEM_3": "Более низкие сборы" + }, + "TITLE_PAYMENT": "Добавить способы оплаты", + "PAYMENT_VERIFICATION": "Проверка платежа", + "START_PAYMENT_VERIFICATION": "Начать проверку", + "PAYMENT_VERIFICATION_TEXT_1": "Добавьте данные своего платежного счета ниже.", + "PAYMENT_VERIFICATION_TEXT_2": "После того, как они будут проверены, ваша учетная запись получит больше способов вывода и депозита для различных валют.", + "ADD_ANOTHER_PAYMENT_METHOD": "ДОБАВИТЬ ДРУГОЙ СПОСОБ ОПЛАТЫ", + "PAYMENT_VERIFICATION_HELP_TEXT": "Чтобы этот раздел был проверен, вы должны заполнить раздел {0}." + }, + "USER_SETTINGS": { + "TITLE_TEXT_1": "Измените настройки своей учетной записи. Из интерфейса, уведомлений, имени пользователя и других настроек.", + "TITLE_TEXT_2": "Сохранение ваших настроек применит изменения и сохранит их.", + "TITLE_NOTIFICATION": "Уведомление", + "TITLE_INTERFACE": "Интерфейс", + "TITLE_LANGUAGE": "Язык", + "TITLE_CHAT": "Чат", + "TITLE_AUDIO_CUE": "Звуковые подсказки", + "TITLE_MANAGE_RISK": "Управление рисками", + "ORDERBOOK_LEVEL": "Уровни книги заказов (максимум 20)", + "SET_TXT": "НАБОР", + "CREATE_ORDER_WARING": "Предупреждение о создании заказа", + "RISKY_TRADE_DETECTED": "Обнаружена рискованная сделка", + "RISKY_WARNING_TEXT_1": "Стоимость этого заказа превышает указанную вами лимитную сумму заказа, которую вы установили {0} .", + "RISKY_WARNING_TEXT_2": "({0} портфолио)", + "RISKY_WARNING_TEXT_3": " Пожалуйста, проверьте и убедитесь, что вы действительно хотите совершить эту сделку.", + "GO_TO_RISK_MANAGMENT": "ПЕРЕЙТИ К УПРАВЛЕНИЮ РИСКАМИ", + "CREATE_ORDER_WARING_TEXT": "Создайте всплывающее окно с предупреждением, когда ваш торговый ордер использует более {0} вашего портфеля", + "ORDER_PORTFOLIO_LABEL": "Процентная сумма портфеля:", + "NOTIFICATION_FORM": { + "POPUP_ORDER_CONFIRMATION": "Запрашивайте подтверждение перед отправкой заказов", + "POPUP_ORDER_COMPLETED": "Показать всплывающее окно, когда заказ был выполнен", + "POPUP_ORDER_PARTIALLY_FILLED": "Показать всплывающее окно, когда заказ частично выполнен" + }, + "AUDIO_CUE_FORM": { + "ALL_AUDIO": "Все звуковые сигналы", + "PUBLIC_TRADE_AUDIO": "Когда была совершена публичная сделка", + "ORDERS_PARTIAL_AUDIO": "Когда один из ваших заказов частично выполнен", + "ORDERS_PLACED_AUDIO": "Когда заказ размещен", + "ORDERS_CANCELED_AUDIO": "Когда заказ отменяется", + "ORDERS_COMPLETED_AUDIO": "Когда один из ваших заказов полностью выполнен", + "CLICK_AMOUNTS_AUDIO": "При нажатии сумм и цен в книге заказов", + "GET_QUICK_TRADE_AUDIO": "При получении котировки для быстрой торговли", + "SUCCESS_QUICK_TRADE_AUDIO": "Когда происходит успешная быстрая сделка", + "QUICK_TRADE_TIMEOUT_AUDIO": "Когда тайм-аут быстрой торговли" + }, + "RISK_MANAGEMENT": { + "INFO_TEXT": "Создайте всплывающее окно с предупреждением, когда стоимость торгового ордера превышает установленный процент суммы вашего портфеля.", + "INFO_TEXT_1": "Общая стоимость активов в {0}: {1}", + "PORTFOLIO": "Процент портфеля", + "VALUE_ASSET": "Приблизительное значение", + "ADJUST": "(НАСТРОЙКА В ПРОЦЕНТАХ)", + "ACTIVATE_RISK_MANAGEMENT": "Активировать управление рисками", + "WARNING_POP_UP": "Предупреждающие всплывающие окна" + }, + "TITLE_ACCOUNT": "Счет", + "DELETE_ACCOUNT": { + "ACCESS": { + "TEXT": "Хотите удалить свою учетную запись? Нажмите {0}.", + "LINK": "здесь", + "DANGER_ZONE": { + "TITLE": "Зона опасности", + "TEXT": "Удаление вашей учетной записи сделает недоступными как вашу учетную запись, так и средства.", + "PROCEED": "Продолжить", + "CANCEL": "Отмена" + } + }, + "CONFIRMATION": { + "TITLE": "Удалить аккаунт", + "TEXT_1": "Удаление вашей учетной записи приведет к безвозвратному удалению всех ваших данных с биржи. Это действие необратимо.", + "TEXT_2": "ВВЕДИТЕ {0}, чтобы подтвердить удаление учетной записи.", + "KEY": "Я ПОНИМАЮ", + "DELETE": "УДАЛИТЬ", + "CANCEL": "Отмена", + "PLACEHOLDER": "Введите здесь" + } + } + }, + "USER_APPS": { + "TITLE": "Ваши приложения для обмена", + "SUBTITLE": "Информация о приложениях вашей учетной записи Exchange и дополнительные функции ниже.", + "ALL_APPS": { + "TAB_TITLE": "Все приложения", + "TITLE": "Обмен приложениями", + "SUBTITLE": "Получите больше функциональности от своей учетной записи на бирже, просто выбрав приложение ниже и нажав кнопку «Добавить».", + "SEARCH_PLACEHOLDER": "Поиск приложений...", + "ADD": { + "SUCCESSFUL": "Вы успешно добавили новое приложение!", + "FAILED": "Что-то пошло не так" + } + }, + "MY_APPS": { + "TAB_TITLE": "Мои приложения", + "TITLE": "Мои обменные приложения", + "SUBTITLE": "Ниже представлены ваши активные заявки на обмен. Вы можете щелкнуть, чтобы просмотреть информацию о каждом приложении, функции и добавить/удалить их. Приложения предназначены для того, чтобы предоставить вам больше возможностей для обмена." + }, + "TABLE": { + "APP_NAME": "Имя приложения", + "DESCRIPTION": "Описание", + "ACTION": "Действие", + "CONFIGURE": "Настроить", + "VIEW_APP": "Посмотреть приложение", + "ADD": "Добавлять", + "NOT_FOUND": "Не могу найти это приложение...", + "RETRY": "Попробуйте другой поисковый запрос" + }, + "APP_DETAILS": { "BACK_PLACEHOLDER": "{0} {1}", "BACK_TO_APPS": "в мои приложения", "BACK": "Назад" }, + "CONFIGURE": { + "TITLE": "Настроить приложение", + "SUBTITLE": "Настройте свое приложение ниже:", + "REMOVE": "Удалить приложение", + "TEXT": "Если у вас возникли проблемы с вашим приложением, свяжитесь с нами, нажав «Справка» ниже.", + "BACK": "НАЗАД", + "HELP": "ПОМОЩЬ" + }, + "REMOVE": { + "TITLE": "Удалить приложение", + "SUBTITLE": "Приложение будет удалено из списка «Мои приложения».", + "TEXT": "Вы уверены, что хотите удалить это приложение?", + "BACK": "НАЗАД", + "CONFIRM": "ПОДТВЕРЖДАТЬ" + } + }, + "TRANSACTION_HISTORY": { + "TITLE": "История", + "TITLE_TRADES": "История сделок", + "TITLE_DEPOSITS": "История депозитов", + "TITLE_WITHDRAWALS": "История вывода средств", + "TEXT_DOWNLOAD": "СКАЧАТЬ ИСТОРИИ", + "TRADES": "Сделки", + "DEPOSITS": "Депозиты", + "WITHDRAWALS": "Снятие средств", + "ORDERID": "номер заказа", + "TRIGGER_STOP_PRICE": "Триггер/стоп цена", + "TIME_OF_LAST_TRADE": "Время последней сделки", + "INTERNAL": "внутренний", + "BLOCKCHAIN": "блокчейн", + "STAKE": "ставка", + "REFERRAL": "направления", + "QUICK_TRADE_TOOLTIP": "Заказ выполнен через систему конвертации" + }, + "ACCOUNT_SECURITY": { + "TITLE_TEXT": "Настройте параметры безопасности для своей учетной записи. От двухфакторной аутентификации, пароля, ключей API и других функций, связанных с безопасностью.", + "OTP": { + "TITLE": "2FA", + "OTP_ENABLED": "OTP включен", + "OTP_DISABLED": "ПОЖАЛУЙСТА, ВКЛЮЧИТЕ 2FA", + "ENABLED_TEXTS": { + "TEXT_1": "Требовать OTP при входе", + "TEXT_2": "Требовать OTP при выводе средств" + }, + "DIALOG": { + "SUCCESS": "Вы успешно активировали 2FA", + "REVOKE": "Вы успешно деактивировали 2FA" + }, + "CONTENT": { + "TITLE": "Активировать двухфакторную аутентификацию", + "MESSAGE_1": "Сканировать", + "MESSAGE_2": "Отсканируйте приведенный ниже qrcode с помощью Google Authenticator или Authy, чтобы автоматически настроить двухфакторную аутентификацию на вашем устройстве.", + "MESSAGE_3": "Если у вас возникли проблемы со сканированием, вы можете вручную ввести код ниже", + "MESSAGE_4": "Вы должны хранить этот код в надежном месте, чтобы восстановить двухфакторную аутентификацию в случае, если вы в будущем поменяете или потеряете свой мобильный телефон.", + "MESSAGE_5": "Руководство", + "WARNING": "Мы {0} {1}", + "ENABLE": "Включить двухфакторную аутентификацию", + "DISABLE": "Отключить двухфакторную аутентификацию", + "INPUT": "Пожалуйста, введите свой OTP", + "STRONGLY_RECOMMEND": "настоятельно рекомендую", + "WARNING_CONTENT": "что вы включаете двухфакторную аутентификацию для повышения безопасности вашей учетной записи и защиты ваших средств" + }, + "FORM": { + "PLACEHOLDER": "Введите OTP, предоставленный Google Authenticator.", + "BUTTON": "Включить двухфакторную аутентификацию" + }, + "DOWNLOAD_APP": "Загрузить и установить", + "GOOGLE_AUTHENTICATOR": "Google Аутентификатор:", + "DOWNLOAD_FROM": "Скачать с", + "GOOGLE_PLAY": "Гугл игры", + "APPLE": "Магазин приложений", + "APP_INFO": "Google Authenticator будет генерировать случайные коды, необходимые для конфиденциальных действий, таких как вход в систему и вывод средств.", + "DONT_UNINSTALL": "Пожалуйста, не удаляйте приложение.", + "INFO_UNINSTALL": "В случае случайной потери телефона или непреднамеренного удаления у вас могут возникнуть задержки в восстановлении доступа к вашей учетной записи. В таких случаях вам необходимо обратиться в нашу службу поддержки за помощью.", + "BACK": "НАЗАД", + "PROCEED": "ПРОДОЛЖИТЬ", + "NEXT": "Следующий", + "NOTE": "Примечание: {0} {1}", + "MANUEL_DESCRIPTION": "Пожалуйста, введите вручную начальное значение 2FA из предыдущего шага.", + "MANUEL_DESCRIPTION_2": "Пожалуйста, внимательно запишите и сохраните приведенную выше ручную проверку.", + "MANUEL_DESCRIPTION_3": "ключ, так как его можно использовать для восстановления учетной записи.", + "MANUEL_KEY": "Ручное семя 2FA", + "MANUEL_PLACEHOLDER": "Введите начальное значение 2FA", + "MANUEL_WARNING": "(Убедитесь, что вы записали его и сохранили для будущего использования.)", + "MANUEL_ERROR_1": "Введите вручную начальное значение 2FA.", + "MANUEL_ERROR_2": "Пожалуйста, введите вручную начальное значение 2FA из предыдущего шага.", + "ACCOUNT_SECURED": "Аккаунт защищен", + "2FA_ENABLED": "2ФА ВКЛЮЧЕНА", + "2FA_DISABLED": "2FA ОТКЛЮЧЕНА", + "2FA_CONTENT_ONE": "Активация {0} {1} .", + "2FA_CONTENT_TWO": "(Двухфакторная аутентификация) добавляет дополнительный уровень защиты для сохранности ваших средств. После включения вам потребуется вводить код подтверждения со своего телефона каждый раз, когда вы входите в систему или снимаете активы. Эта функция значительно повышает безопасность вашей учетной записи.", + "2FA_CONTENT_THREE": "(Двухфакторная аутентификация) добавляет дополнительный уровень защиты для сохранности ваших средств.", + "2FA_CONTENT_FOUR": "Чтобы отключить 2FA, вам нужно будет ввести код 2FA (OTP).", + "INPUT_SIX_DIGIT_CODE": "Введите 6-значный код из приложения Google Authenticator на вашем телефоне:", + "INPUT_CONTENT": "(Чтобы добавить новую учетную запись в Google Authenticator, нажмите кнопку «+». Вы можете либо отсканировать предоставленный QR-код на предыдущем шаге, либо вручную ввести ключ, чтобы добавить свою учетную запись в приложение.)" + }, + "CHANGE_PASSWORD": { + "TITLE": "Пароль", + "ACTIVE": "АКТИВНЫЙ", + "DIALOG": { + "EMAIL_CONFIRMATION": "Вам будет отправлено электронное письмо для авторизации смены пароля." + }, + "FORM": { + "BUTTON": "Изменить пароль", + "CURRENT_PASSWORD": { "label": "Текущий пароль", "placeholder": "введите ваш текущий пароль" }, + "NEW_PASSWORD": { "label": "Новый пароль", "placeholder": "Введите новый пароль" }, + "NEW_PASSWORD_REPEAT": { "label": "Подтвердите новый пароль", "placeholder": "Повторите новый пароль" } + } + }, + "LOGIN": { + "TITLE": "История входа", + "IP_ADDRESS": "Айпи адрес", + "TIME": "Дата/время", + "CONTENT": { "TITLE": "История входов" } + }, + "FREEZE": { + "TITLE": "Заморозить аккаунт", + "CONTENT": { + "MESSAGE_1": "Блокировка вашей учетной записи остановит снятие средств и остановит все торговые операции.", + "WARNING_1": "Используйте, только если вы опасаетесь, что ваша учетная запись была скомпрометирована", + "TITLE_1": "Заморозить свою учетную запись", + "TITLE_2": "Блокировка аккаунта", + "MESSAGE_2": "Блокировка вашей учетной записи может помочь защитить вашу учетную запись от кибератак.", + "MESSAGE_3": "Если вы решите заморозить свою учетную запись, произойдет следующее:", + "MESSAGE_4": "1. Незавершенные выводы будут отменены.", + "MESSAGE_5": "2. Все торги будут остановлены, а невыполненные ордера будут аннулированы.", + "MESSAGE_6": "3. Для повторной активации вашей учетной записи потребуется поддержка.", + "WARNING_2": "Вы действительно хотите заморозить свой аккаунт?" + } + } + }, + "STAKE": { + "NETWORK_WARNING": "Несовместимая сеть. Измените свою сеть на {0}", + "EARN": "Зарабатывать", + "TITLE": "Ставка", + "MODAL_TITLE": "Делайте ставки и зарабатывайте {0}", + "REVIEW_MODAL_TITLE": "Проверить и подтвердить ставку", + "AVAILABLE_TOKEN": "{0} ({1}) доступно для ставок: {2}", + "DEFI_TITLE": "Ставка активов DeFi", + "DEFI_TEXT": "В стиле стейкинга DeFi будет использоваться ваш собственный кошелек вне биржи. Для начала вам необходимо установить соединение, после чего вы можете сделать ставку и начать зарабатывать прямо из своего кошелька.", + "GET_STAKES": "Получить ставки", + "CURRENT_ETH_BLOCK": "Текущий блок ETH: {0}", + "ON_EXCHANGE_XHT": "На бирже {0} баланс: {1} {2}", + "LOGIN_HERE": "Войти здесь", + "MOVE_XHT": "Переместить {0}", + "ESTIMATED_STAKED": "Расчетная стоимость общей суммы ставок", + "ESTIMATED_EARNINGS": "Расчетная стоимость дохода", + "CONNECT_WALLET": "Подключить кошелек", + "BACK": "Назад", + "NEXT": "Следующий", + "REVIEW": "Обзор", + "ESTIMATED": "ВОСТОК.", + "BLOCK": "Блокировать", + "CANCEL": "Отмена", + "PROCEED": "Продолжить", + "GO_TO_WALLET": "перейти к кошельку", + "AMOUNT_LABEL": "Сумма ставки", + "PERIOD_SUBTITLE": "Чем дольше вы делаете ставки, тем больше вы получаете вознаграждение. Выберите продолжительность ставки ниже.", + "STAKE_AND_EARN_DETAILS": "Сделайте ставку на ~{0} и заработайте {1}", + "PREDICTED_EARNINGS": "Прогнозируемый доход", + "VARIABLE_TITLE": "Переменная*", + "VARIABLE_TEXT": "*{0} о том, как работает переменная ставка.", + "READ_MORE": "Читать далее", + "CURRENT_BLOCK": "Текущий блок: {0}", + "END_BLOCK": "Конечный блок: {0}", + "DURATION": "Продолжительность", + "END_ON_BLOCK": "Конец в блоке: {0}", + "SLASHING_TITLE": "Слэшинг (ранняя отмена ставок)", + "SLASHING_TEXT_1": "Принцип {0}% от ваших ставок", + "SLASHING_TEXT_2": "Весь заработок конфискован", + "REVIEW_NOTE": "Продолжительность измеряется временем блоков Ethereum. Пожалуйста, проверьте и подтвердите приведенные выше данные, прежде чем делать ставки, так как преждевременная отмена ставок приведет к вычету процента от суммы ваших ставок и потере прибыли.", + "WAITING_TITLE": "Ожидание подтверждения", + "WAITING_TEXT": "Подтвердите эту транзакцию в своем кошельке", + "PENDING_TEXT": "Ожидается транзакция...", + "CHECKING_ALLOWANCE": "Проверка допуска {0}...", + "WAITING_PROMPT": "{0} {1} {2}", + "WAITING_STAKE": "Подтвердить сумму ставки", + "WAITING_WITHDRAW": "Разрешение расходов", + "WAITING_UNSTAKE": "Отменить ставку", + "WAITING_STAKE_ING": "Ожидание ставок", + "WAITING_WITHDRAW_ING": "Обработка разрешения расходов", + "WAITING_UNSTAKE_ING": "Анстейкинг", + "SUCCESSFUL_STAKE_TITLE": "Вы успешно поставили {0}", + "SUCCESSFUL_STAKE_AMOUNT": "Сумма ставки", + "SUCCESSFUL_STAKE_DURATION_KEY": "Продолжительность", + "SUCCESSFUL_STAKE_DURATION_DEF": "Заканчивается блоком {0} ({1})", + "SUCCESSFUL_STAKE_DESTINATION": "Место назначения", + "SUCCESSFUL_UNSTAKE_ADDRESS": "Мой адрес", + "OKAY": "Хорошо", + "ERROR_TITLE": "Ошибка: {0} отклонено", + "ERROR_SUBTITLE": "Если это была ошибка, вы можете вернуться и повторить попытку.", + "SUCCESSFUL_UNSTAKE_TITLE": "Вы успешно сняли ставки {0}", + "SUCCESSFUL_UNSTAKE_AMOUNT": "Всего получить", + "EARNINGS": "Доход", + "ORIGINAL_AMOUNT": "Первоначальная сумма ставки", + "CONNECT_A_WALLET": "Подключиться к кошельку", + "CONNECT_WALLET_TABLE": "{0}, чтобы просмотреть исторические события ставок", + "ZERO_STAKES": "Нулевые ставки", + "PENDING_TRANSACTIONS": "В ожидании {0} {1}", + "VIEW_ON": "Посмотреть на {0}", + "BLOCKCHAIN": "блокчейн", + "VIEW_POT": "Посмотреть ПО для дистрибутива", + "COMPLETED": "Созревший", + "COMPLETED_TOOLTIP": "Ставка зрелая. Продолжайте делать ставки, чтобы заработать больше вознаграждений, или отмените ставки, чтобы получить вознаграждения.", + "CONNECT_ERROR": "Пожалуйста, проверьте свой кошелек", + "INSTALL_METAMASK": "Вы должны установить Metamask в свой браузер: https://metamask.io/download.html", + "INSTALL_METAMASK_TITLE": "Метамаска не обнаружена", + "INSTALL_METAMASK_DETAILS": { + "TITLE": "Установить МетаМаск", + "PROMPT": "Вам необходимо установить Metamask в свой браузер: {0}" + } + }, + "UNSTAKE": { + "TITLE": "Отменить ставку", + "EARLY_TITLE": "Отменить ставку раньше", + "EARLY_WARNING_TITLE": "Похоже, вы пытаетесь отменить ставку раньше", + "EARLY_WARNING_TEXT_1": "Это может привести к вычету процента от вашей первоначальной основной ставки и потере всех доходов.", + "EARLY_WARNING_TEXT_2": "Вы уверены, что хотите продолжить?", + "BACK": "ВОЗВРАЩАТЬСЯ", + "REVIEW": "УДАЛИТЬ СТАВКУ", + "DURATION": "ВОСТОК. продолжительность созревания", + "CANCEL": "Отмена", + "PROCEED": "Продолжить", + "EARNINGS_FORFEITED": "Прибыль конфискована", + "PRICE_FORMAT": "{0} {1}", + "EST_PENDING": "ВОСТОК. в ожидании: {0}", + "AMOUNT_SLASHED": "Урезанная сумма*", + "AMOUNT_TO_RECEIVE": "Сумма для получения", + "SLASH_FOOTNOTE": "*Все урезанные суммы распределяются между оставшимися стейкерами. Пожалуйста, примите во внимание сумму, сокращенную от первоначального принципа, конфискованную прибыль и оставшуюся продолжительность, и определите, стоит ли стоимость, потерянная при ранней отмене ставок.", + "AMOUNT_NOTE": "Суммы будут распределены на адрес вашего кошелька", + "TOTAL_EARNT": "Общий доход", + "PENDING_EARNINGS": "Ожидаемый доход*", + "PENDING_EARNINGS_FOOTNOTE": "*Незавершенные доходы — это суммы, которые не были очищены и требуют транзакции в блокчейне, чтобы быть добавленными к вашей общей сумме получения." + }, + "STAKE_TABLE": { + "CURRENCY": "Валюта", + "AVAILABLE": "Доступно для ставок", + "TOTAL": "Всего поставлено", + "REWARD_RATE": "Ставка вознаграждения", + "EARNINGS": "Доход", + "STAKE": "Ставка", + "VARIABLE": "Переменная" + }, + "STAKE_LIST": { + "AMOUNT": "СУММЫ СТАВКИ", + "DURATION": "ВОСТОК. ПРОДОЛЖИТЕЛЬНОСТЬ СОЗРЕВАНИЯ", + "START": "НАЧАЛО СТАВИТЬ", + "END": "КОНЕЦ СТАВКИ", + "EARNINGS": "ПРИБЫЛЬ", + "STAKE": "СТАВКА" + }, + "STAKE_DETAILS": { + "BACK_SUBTITLE": "{0} на страницу ставок", + "GO_BACK": "Возвращаться", + "CONTRACT_SUBTITLE": "Контракт токена: {0}", + "VIEW_MORE": "ПОСМОТРЕТЬ БОЛЬШЕ", + "VIEW": "Вид", + "TOKEN": "{0} Токен", + "TABS": { + "PUBLIC_INFO": "Общедоступная информация", + "DISTRIBUTIONS": "Распределения", + "MY_STAKING": "Мои ставки" + }, + "PUBLIC_INFO": { + "TITLE": "Информация о стекинге", + "SUBTITLE": "Ниже представлена токеномика ставок для {0} ({1}).", + "TOTAL_DISTRIBUTED_REWARDS": "Всего распределенных вознаграждений ({0})", + "POT_BALANCE": "пот баланс", + "UNCLAIMED_REWARDS": "Невостребованные награды", + "TOTAL_STAKED": "Всего поставлено", + "REWARD_RATE": "Ставка вознаграждения", + "MY_STAKE": "Моя ставка ({0}%)", + "MY_STAKE_PERCENTLESS": "Моя ставка", + "OTHER_STAKE": "Другие доли ({0}%)", + "EVENTS_TITLE": "Последние распределенные награды" + }, + "DISTRIBUTIONS": { + "TITLE": "Распределено {0} наград", + "SUBTITLE": "Ниже приведен исторический список раздач, сделанных стейкерам {0}.", + "TIME": "Распределенное время", + "TRANSACTION_ID": "ID транзакции", + "AMOUNT": "Распределенная сумма" + }, + "MY_STAKING": { + "TITLE": "Мои ставки", + "SUBTITLE": "Ниже представлена информация и некоторые исторические события, связанные со стекингом {0}.", + "EVENTS_TITLE": "Исторические события ставок", + "TIME": "Время", + "EVENT": "Событие", + "TRANSACTION_ID": "ID транзакции", + "AMOUNT": "Количество" + } + }, + "CEFI_STAKE": { + "STAKE_POOL_TITLE": "Локальные пулы стейкинга CeFi", + "INTRODUCTION_1": "Зарабатывайте награды на активах, которые вы храните в своем кошельке на локальной бирже. Просто нажмите 'СТЕЙК', введите сумму, которую хотите поставить в стейкинг, и начните зарабатывать.", + "CURRENT_STAKING_VALUE": "Текущая стоимость стейкинга", + "DURATION_LABEL": "Длительность", + "APY_LABEL": "Годовая процентная доходность (APY)", + "REWARDS_IN_LABEL": "Награды будут выплачиваться в", + "ALL_STAKING_EVENTS": "Все события стейкинга", + "MONITOR_ACTIVE_STAKES": "Отслеживайте активные стейки и их доходы, получаемые из пулов стейкинга.", + "USE_FILTERS_FOR_HISTORICAL_EVENTS": "Используйте фильтры, чтобы найти все исторические события стейкинга. Все доходы от завершенных стейкингов будут переведены на ваш кошелек.", + "ESTIMATED_TOTAL_STAKED": "Приблизительная общая стоимость стейкинга", + "ESTIMATED_EARNINGS_VALUE": "Приблизительная стоимость доходов", + "READ_BEFORE_AGREE_AND_EARN": "Прочитайте перед согласием и заработком", + "AGREEMENT_INTRODUCTION_1": "Заблокировать средства и участвовать в стейкинге действительно может быть прибыльным способом заработка наград. Однако важно знать, что могут быть налагаемы штрафы за раннее снятие и возможно долгосрочные сроки блокировки. Поэтому крайне важно полностью понимать правила пула перед стейкингом. Нажав 'Я согласен и понимаю' ниже, вы признаете, что вы будете", + "AGREEMENT_INTRODUCTION_2": "внимательно читать условия в последующих шагах,", + "AGREEMENT_INTRODUCTION_3": "и вы соглашаетесь с потенциальными рисками и штрафами, связанными с ранним снятием. Продолжение с таким пониманием обеспечит более осознанный и безопасный опыт стейкинга.", + "BACK_BUTTON": "Назад", + "PROCEED_BUTTON": "Продолжить", + "I_UNDERSTAND_BUTTON": "Я понимаю", + "AMOUNT_TO_STAKE_LABEL": "Сумма для стейкинга", + "NEXT_BUTTON": "Далее", + "LOCKUP_DURATION_LABEL": "Срок блокировки", + "PENALTY_UPON_INITIAL_STAKE_PRINCIPLE": "Штраф при начальном принципе стейкинга", + "FORFEITURE_OF_EARNINGS_LABEL": "Утрата доходов", + "SLASHING_RULES_ENFORCED_LABEL": "! Правила сокращения применяются в этом пуле", + "SLASHING_RULES_NOTICE": "Имейте в виду, что отказ от средств до указанного срока может повлечь штраф, как указано в правилах сокращения, упомянутых выше. Прежде чем обязательно начать стейкинговый период, важно оценить свою финансовую стабильность, так как запуск процесса раннего снятия может привести к уменьшению общей стоимости вашего начального стейкинга.", + "UNSTAKE_ANYTIME_LABEL": "Этот пул позволяет вам снимать стейкинг в любое время без последствий.", + "FULL_DURATION_LOCK_LABEL": "Этот пул блокирует вас на весь срок. Пожалуйста, обязательно оцените свою финансовую устойчивость, прежде чем начать стейкинговый пул, так как снятие до окончания срока будет невозможным.", + "CHECK_STAKE_DETAILS_BUTTON": "Проверить детали стейкинга", + "STAKING_POOL_LABEL": "Пул стейкинга", + "ANNUAL_PERCENTAGE_YIELD_LABEL": "Годовая процентная доходность (APY)", + "PENALTY_UPON_INITIAL_STAKE_PRINCIPLE_LABEL": "Штраф при начальном принципе стейкинга", + "FORFEITURE_OF_EARNINGS_DETAILS_LABEL": "Утрата доходов", + "STAKE_AMOUNT_LABEL": "Сумма стейкинга", + "DISCLAIMER_NOTICE": "Отказ от ответственности: обратите внимание, что суммы, оцениваемые в долларах США свыше 1 000 долларов, потребуют завершения идентификации. Эта сумма включает в себя доходы, и платформа оставляет за собой право запросить дополнительную информацию о пользователе.", + "SETTLEMENT_NOTICE": "Расчет: после снятия будет применен 24-часовой период расчета.", + "CONFIRM_BUTTON": "Подтвердить", + "STAKE_RULES_NOTICE": "Как только вы начнете стейкинг, вы обязуетесь соблюдать правила пула.", + "I_UNDERSTAND_AGAIN_BUTTON": "Я понимаю еще раз", + "CONGRATULATIONS_NOTICE": "Поздравляем!", + "EARNINGS_START_NOTICE": "Ваш стейкинг начнет приносить доход.", + "REVIEW_PROGRESS_LABEL": "Вы можете просмотреть прогресс своего стейкинга на странице 'Активные стейки'.", + "TIME_REMAINING_LABEL": "Оставшееся время", + "PENALTY_UPON_INITIAL_STAKE_PRINCIPLE_LABEL_2": "Штраф при начальном принципе стейкинга", + "FORFEITURE_OF_EARNINGS_LABEL_2": "Утрата доходов", + "AMOUNT_TO_RECEIVE_LABEL": "Сумма к получению", + "REQUIRES_24H_TO_SETTLE_NOTICE": "Требуется 24 часа для завершения.", + "SUCCESSFUL_UNSTAKE_NOTICE": "Вы успешно сняли стейкинг", + "VISIT_WALLET_BUTTON": "ПОСЕТИТЬ КОШЕЛЕК", + "CLOSE_BUTTON": "ЗАКРЫТЬ", + "ACTIVE_STAKES": "Активные ставки", + "STAKES_HISTORY": "История" + }, + "MOVE_XHT": { + "TITLE": "Переместить XHT", + "TEXT_1": "Чтобы сделать ставку XHT, вы должны сначала перевести XHT в свой собственный кошелек.", + "TEXT_2": "Адрес вашего текущего подключенного кошелька:", + "LABEL": "Адрес кошелька", + "TEXT_3": "Важно убедиться, что указанный выше адрес кошелька является безопасным. XHT будет перемещен на указанный выше адрес кошелька." + }, + "MOVE_AMOUNT": { + "TITLE": "Сумма ввода", + "PROMPT": "Введите сумму, которую вы хотите перевести.", + "BALANCE": "{0} баланс: {1}", + "LABEL": "Сумма для перемещения", + "FEE": "Комиссия за транзакцию: {0} {1}" + }, + "CURRENCY": "Валюта", + "TYPE": "Тип", + "TYPES_VALUES": { "market": "рынок", "limit": "ограничение" }, + "TYPES": { "MARKET": "рынок", "LIMIT": "ограничение" }, + "SIDE": "Сторона", + "SIDES_VALUES": { "buy": "купить", "sell": "продавать" }, + "SIDES_VERBS": { "buy": "купил", "sell": "продал" }, + "SIDES": { "BUY": "купить", "SELL": "продавать" }, + "DEFAULT_TOGGLE_OPTIONS": { "ON": "на", "OFF": "выключенный" }, + "SIZE": "Размер", + "PRICE": "Цена", + "AVERAGE": "Средний", + "FEE": "Платеж", + "FEES": "Сборы", + "TIME": "Время", + "MORE": "Более", + "VIEW": "Вид", + "STATUS": "Положение дел", + "AMOUNT": "Количество", + "COMPLETE": "Полный", + "PENDING": "В ожидании", + "REJECTED": "Отклоненный", + "ORDERBOOK": "Книга заказов", + "CANCEL": "Отмена", + "CANCEL_ALL": "Отменить все", + "ORDER_ENTRY": "порядок въезда", + "TRADE_HISTORY": "история", + "CHART": "ценовой график", + "ORDERS": "мои активные заказы", + "RECENT_TRADES": "мои последние сделки", + "ORDER_HISTORY": "История заказов", + "PUBLIC_SALES": "публичные продажи", + "REMAINING": "Оставшийся", + "FULLFILLED": "{0} % заполнено", + "FILLED": "Заполненный", + "LOWEST_PRICE": "Самая низкая цена ({0})", + "PHASE": "Фаза", + "INCOMING": "Входящий", + "PRICE_CURRENCY": "Цена", + "AMOUNT_SYMBOL": "Количество", + "ESTIMATED_PRICE": "Ориентировочная цена", + "ORDER_PRICE": "Стоимость заказа", + "NO_DATA": "Нет данных", + "CHART_TEXTS": { + "d": "Дата", + "o": "Открыть", + "h": "Высокий", + "l": "Низкий", + "c": "Закрывать", + "v": "Объем" + }, + "QUICK_TRADE": "Быстрая торговля", + "PRO_TRADE": "Про трейд", + "WALLET_TITLE": "Кошелек", + "CURRENCY_WALLET": { + "BACK": "Назад", + "WALLET_PAGE": "{0} на страницу кошелька", + "LEARN_MORE": "Узнать больше {0}", + "ABOUT": "о", + "TOTAL_BALANCE": "Общий баланс: {0} {1}", + "AVAILABLE_BALANCE": "Доступный баланс: {0} {1} {2}", + "PERCENTAGE_SHARE": "Процентная доля {0}", + "EARN_BY": "Заработайте за {0} {1}", + "STAKING": "ставка", + "TOOLTIP": "Баланс не находится в состоянии ожидания или не заблокирован в сделках (открытых ордерах)", + "WALLET_HAS_BALANCE_PERCENTAGE": "В настоящее время в вашем кошельке нет {0}.", + "WALLET_DEPOSIT": "Вы можете попробовать внести {0} или купить {1}." + }, + "LOGOUT": "Выйти", + "WITHDRAWALS_MIN_VALUE_ERROR": "Транзакция слишком мала для отправки. Попробуйте большую сумму.", + "WITHDRAWALS_MAX_VALUE_ERROR": "Транзакция слишком велика для отправки. Попробуйте меньшее количество.", + "WITHDRAWALS_LOWER_BALANCE": "Недостаточно баланса для продолжения. Для этой транзакции требуется {0}.", + "WITHDRAWALS_BTC_INVALID_ADDRESS": "Биткойн-адрес недействителен. Пожалуйста, внимательно проверьте и введите еще раз", + "WITHDRAWALS_ETH_INVALID_ADDRESS": "Адрес Ethereum недействителен. Пожалуйста, внимательно проверьте и введите еще раз", + "WITHDRAWALS_BUTTON_TEXT": "отозвать отзыв", + "WITHDRAWALS_FORM_NETWORK_LABEL": "Сеть", + "DEPOSIT_FORM_NETWORK_WARNING": "Убедитесь, что выбранная сеть совместима с сетью кошельков отправителя.", + "DEPOSIT_FORM_MIN_WARNING": "Принимаются только депозиты в размере {0} {1} или более. Любые депозиты ниже этого минимального порога приведут к безвозвратной потере средств.", + "DEPOSIT_FORM_TITLE_WARNING_DESTINATION_TAG": "Введите адрес и тег, которые необходимы для успешного внесения средств на ваш счет.", + "WITHDRAW_PAGE_DESTINATION_TAG_NONE": "Никто", + "WITHDRAW_PAGE_DESTINATION_TAG_MESSAGE": "Тег назначения: {0}", + "WITHDRAW_PAGE_NETWORK_TYPE_MESSAGE": "{0} тип сети адреса: {1}", + "WITHDRAWALS_FORM_NETWORK_WARNING": "Убедитесь, что выбранная сеть совместима с целевым кошельком.", + "WITHDRAWALS_FORM_FEE_WARNING": "{0} ({1}) является обязательным условием для изъятия этого актива.", + "WITHDRAWALS_FORM_DESTINATION_TAG_WARNING": "Проверьте, требуется ли для получения адреса тег. Также известен как памятка, цифровое удостоверение личности, метка и заметки.", + "WITHDRAWALS_FORM_NETWORK_PLACEHOLDER": "Выберите сеть", + "WITHDRAWALS_FORM_ADDRESS_LABEL": "Адрес назначения", + "WITHDRAWALS_FORM_ADDRESS_PLACEHOLDER": "Введите адрес", + "WITHDRAWALS_FORM_DESTINATION_TAG_LABEL": "Тег назначения (необязательно)", + "WITHDRAWALS_FORM_MEMO_LABEL": "Памятка (необязательно)", + "WITHDRAWALS_FORM_DESTINATION_TAG_PLACEHOLDER": "Введите тег назначения", + "WITHDRAWALS_FORM_AMOUNT_LABEL": "{0} сумма для вывода", + "WITHDRAWALS_FORM_AMOUNT_PLACEHOLDER": "Введите сумму {0}, которую вы хотите снять", + "WITHDRAWALS_FORM_FEE_COMMON_LABEL": "Комиссия на перевод", + "WITHDRAWALS_FORM_FEE_COMMON_LABEL_COIN": "Комиссия за транзакцию ({0})", + "WITHDRAWALS_FORM_FEE_PLACEHOLDER": "Введите сумму {0}, которую вы хотите использовать в качестве комиссии за транзакцию.", + "DEPOSIT_BANK_REFERENCE": "Добавьте этот код \"{0}\" к банковскому переводу, чтобы идентифицировать депозит.", + "QUOTE_SUCCESS_REVIEW_MESSAGE": "Вы успешно {0} {1} {2}", + "COUNTDOWN_ERROR_MESSAGE": "Обратный отсчет завершен", + "WITHDRAW_PAGE": { + "BANK_TO_WITHDRAW": "Банк для вывода", + "MESSAGE_ABOUT_SEND": "Вы собираетесь отправить", + "MESSAGE_BTC_WARNING": "Пожалуйста, убедитесь в правильности этого адреса, поскольку переводы {0} необратимы.", + "MESSAGE_FEE": "Комиссия за транзакцию в размере {0} ({1}) включена", + "MESSAGE_FEE_COIN": "Комиссия за транзакцию {0}", + "BASE_MESSAGE_1": "Вы можете вывести средства только на банковский счет на имя, совпадающее с именем, зарегистрированным в вашей учетной записи.", + "BASE_MESSAGE_2": "Минимальная сумма вывода", + "BASE_MESSAGE_3": "Максимальная сумма ежедневного вывода", + "CONFIRM_VIA_EMAIL": "Подтвердить по электронной почте", + "CONFIRM_VIA_EMAIL_1": "Мы отправили вам электронное письмо с подтверждением вывода средств.", + "CONFIRM_VIA_EMAIL_2": "Для завершения процесса вывода, пожалуйста, подтвердите", + "CONFIRM_VIA_EMAIL_3": "вывод на вашу электронную почту в течение 5 минут.", + "WITHDRAW_CONFIRM_SUCCESS_1": "Ваш запрос на вывод средств подтвержден. Он будет обработан в ближайшее время.", + "WITHDRAW_CONFIRM_SUCCESS_2": "Чтобы просмотреть статус вывода средств, посетите страницу истории вывода средств.", + "GO_WITHDRAWAL_HISTORY": "Перейти к истории вывода средств", + "WITHDRAWALS_FORM_ERROR_TITLE": "Неверная информация о переводе", + "WITHDRAWALS_FORM_ERROR": "Ваш перевод средств не удался. Для отправки средств на электронную почту необходимо, чтобы у пользователя была учетная запись на этой бирже. Проверьте правильность адреса электронной почты и повторите попытку.", + "WITHDRAWAL_CONFIRM_FINAL": "Окончательное подтверждение вывода", + "WITHDRAWAL_CONFIRM_WARNING": "Пожалуйста, проверьте ваш вывод ниже перед подтверждением.", + "WITHDRAWAL_CONFIRM_AMOUNT": "Сумма", + "WITHDRAWAL_CONFIRM_FEE": "Комиссия", + "WITHDRAWAL_CONFIRM_ADDRESS": "Адрес", + "WITHDRAWAL_CONFIRM_NETWORK": "Сеть", + "WITHDRAWAL_CANCEL_BUTTON": "ЗАКРЫТЬ", + "WITHDRAWAL_CONFIRM_BUTTON": "ПОДТВЕРДИТЬ ВЫВОД" + }, + "WALLET_BUTTON_BASE_DEPOSIT": "депозит", + "WALLET_BUTTON_BASE_WITHDRAW": "отзывать", + "WALLET_BUTTON_CRYPTOCURRENCY_DEPOSIT": "депозит", + "WALLET_BUTTON_CRYPTOCURRENCY_WITHDRAW": "отправлять", + "24H_MAX": "24-часовой максимум:", + "24H_MIN": "24 часа Низкий", + "24H_VAL": "24ч Том", + "AVAILABLE_BALANCE_TEXT": "Доступный баланс {0}: {1} {2} {3}", + "BALANCE_TEXT": "Баланс", + "CURRENCY_BALANCE_TEXT": "{0} Баланс", + "WALLET_ALL_ASSETS": "Все активы", + "WALLET_HIDE_ZERO_BALANCE": "Скрыть нулевой баланс", + "WALLET_ESTIMATED_TOTAL_BALANCE": "Расчетный общий баланс", + "WALLET_ASSETS_SEARCH_TXT": "Поиск", + "PAGINATOR_FORMAT": "{0} / {1}", + "ORDERBOOK_SELLERS": "Продавцы", + "ORDERBOOK_BUYERS": "Покупатели", + "ORDERBOOK_SPREAD": "распространение {0}", + "CALCULATE_MAX": "Макс", + "VERIFICATION_WARNING_TITLE": "Проверка ваших банковских реквизитов", + "VERIFICATION_WARNING_MESSAGE": "Перед снятием средств вам необходимо подтвердить свои банковские реквизиты.", + "ORDER_SPENT": "Потраченный", + "ORDER_RECEIVED": "Полученный", + "ORDER_SOLD": "Продал", + "ORDER_BOUGHT": "Купил", + "ORDER_AVERAGE_PRICE": "Средняя цена", + "ORDER_TITLE_CREATED": "Успешно создан лимит {0}", + "ORDER_TITLE_FULLY_FILLED": "Заказ {0} успешно выполнен", + "ORDER_TITLE_PARTIALLY_FILLED": "{0} заказ частично выполнен", + "ORDER_TITLE_TRADE_COMPLETE": "{0} {1} заказ выполнен успешно", + "LOGOUT_TITLE": "Вы вышли из системы", + "LOGOUT_ERROR_TOKEN_EXPIRED": "Срок действия вашего сеанса истек. Пожалуйста, войдите снова.", + "LOGOUT_ERROR_LOGIN_AGAIN": "Войти снова", + "LOGOUT_ERROR_INVALID_TOKEN": "Недействительный токен", + "LOGOUT_ERROR_INACTIVE": "Вы вышли из системы, потому что были неактивны", + "ORDER_ENTRY_BUTTON": "{0} {1}", + "ORDER_ENTRY_ADVANCED": "Передовой", + "QUICK_TRADE_OUT_OF_LIMITS": "Размер заказа выходит за пределы", + "QUICK_TRADE_TOKEN_USED": "Токен был использован", + "QUICK_TRADE_QUOTE_EXPIRED": "Срок действия предложения истек", + "QUICK_TRADE_QUOTE_INVALID": "Неверная цитата", + "QUICK_TRADE_QUOTE_CALCULATING_ERROR": "Ошибка расчета котировки", + "QUICK_TRADE_ORDER_CAN_NOT_BE_FILLED": "Проскальзывание для выбранной суммы слишком велико на рынке", + "QUICK_TRADE_ORDER_NOT_FILLED": "Заказ не заполнен", + "QUICK_TRADE_NO_BALANCE": "Недостаточно средств для выполнения заказа", + "QUICK_TRADE_SUCCESS": "Успех!", + "QUICK_TRADE_INSUFFICIENT_FUND": "Недостаточно средств", + "QUICK_TRADE_INSUFFICIENT_FUND_MESSAGE": "В вашем кошельке недостаточно средств для завершения этой транзакции.", + "QUICK_TRADE_BROKER_NOT_AVAILABLE_MESSAGE": "Брокерская внебиржевая сделка в настоящее время недоступна.", + "SUBMIT": "представлять на рассмотрение", + "RESUBMIT": "Отправить повторно", + "VERIFICATION_NOTIFICATION_SKIP_TITLE": "Недостающие документы!", + "VERIFICATION_NOTIFICATION_SKIP_TEXT": "Чтобы получить полный доступ к функциям вывода и депозита, вы должны предоставить документы, удостоверяющие личность, на странице вашей учетной записи.", + "VERIFICATION_NOTIFICATION_SUCCESS_TITLE": "Успех!", + "VERIFICATION_NOTIFICATION_SUCCESS_TEXT": "Вы получите уведомление по электронной почте, когда ваша информация будет обработана. Обработка обычно занимает 1-3 дня.", + "VERIFICATION_NOTIFICATION_BUTTON": "ПЕРЕЙТИ К ОБМЕНУ", + "ERROR_USER_ALREADY_VERIFIED": "Пользователь уже проверен", + "ERROR_INVALID_CARD_USER": "Информация о банке или карте указана неверно", + "ERROR_INVALID_CARD_NUMBER": "Не верный номер карты", + "ERROR_LOGIN_USER_NOT_VERIFIED": "Пользователь не проверен", + "ERROR_LOGIN_USER_NOT_ACTIVATED": "Пользователь не активирован", + "ERROR_LOGIN_INVALID_CREDENTIALS": "Неверные учетные данные", + "SMS_SENT_TO": "SMS отправлено на {0}", + "SMS_ERROR_SENT_TO": "Ошибка при отправке SMS на {0}. Пожалуйста, обновите страницу и повторите попытку.", + "WITHDRAW_NOTIFICATION_TRANSACTION_ID": "ID транзакции:", + "CHECK_ORDER": "Проверьте и подтвердите свой заказ", + "CHECK_ORDER_TYPE": "{0} {1}", + "CONFIRM_TEXT": "Подтверждать", + "INVALID_CAPTCHA": "неверная капча", + "NO_FEE": "Н/Д", + "SETTINGS_LANGUAGE_LABEL": "Языковые настройки (включая электронную почту)", + "SETTINGS_THEME_LABEL": "Тема пользовательского интерфейса", + "SETTING_BUTTON": "сохранять", + "VERIFICATION_NO_WITHDRAW_TITLE": "Снятие средств отключено", + "VERIFICATION_NO_WITHDRAW_MESSAGE": "Ваш аккаунт отключен для снятия средств", + "UP_TO_MARKET": "До рынка", + "VIEW_MY_FEES": "Посмотреть мои сборы", + "ABOUT_LINK": "О {0}", + "DEVELOPER_SECTION": { + "TITLE": "API-ключ", + "INFORMATION_TEXT": "API предоставляет такие функции, как получение баланса кошелька, управление заказами на покупку/продажу, запрос на снятие средств, а также рыночные данные, такие как последние сделки, книга заказов и тикер.", + "ERROR_INACTIVE_OTP": "Для генерации ключа API необходимо включить двухфакторную аутентификацию.", + "ENABLE_2FA": "Включить двухфакторную аутентификацию", + "WARNING_TEXT": "Не сообщайте свой ключ API другим лицам.", + "GENERATE_KEY": "Сгенерировать API-ключ", + "ACTIVE": "Активный", + "INACTIVE": "Неактивный", + "INVALID_LEVEL": "Вам необходимо повысить уровень подтверждения, чтобы получить доступ к этой функции." + }, + "DEVELOPERS_TOKEN": { + "API_KEY": "API-ключ", + "SECRET_KEY": "Секретный ключ", + "ACCESS": "Доступ", + "BASIC_ACCESS": "Базовый доступ", + "BASIC_ACCESS_PROMPT": "Выберите, к чему может получить доступ этот ключ API.", + "READING_ACCESS": "Чтение (балансы кошельков и т. д.)", + "TRADING_ACCESS": "Трейдинг", + "IP_ACCESS": "IP-доступ", + "IP_ACCESS_PROMPT": "Настройте, какой IP-адрес будет работать с этим ключом API.", + "ANY_IP_ADDRESS": "Любой IP-адрес", + "ONLY_TRUSTED_IPS": "Только проверенные IP", + "ADD_IP_PH": "Введите IP-адрес. Вы можете добавить несколько IP-адресов", + "ADD_IP": "Добавлять", + "ADVANCED_ACCESS": "Расширенный доступ", + "ADVANCED_ACCESS_PROMPT": "Требует активации доверенных IP-адресов.", + "WITHDRAWAL_ACCESS": "Снятие средств", + "SAVE": "Сохранять", + "BEWARE": "Осторожно, снятие средств сопряжено с определенными рисками!" + }, + "DEVELOPERS_TOKENS_POPUP": { + "GENERATE_TITLE": "Сгенерировать API-ключ", + "GENERATE_TEXT": "Пожалуйста, назовите свой ключ API и держите его в секрете после его создания. Вы не сможете восстановить его позже.", + "GENERATE": "Создать", + "DELETE_TITLE": "Удалить ключ API", + "DELETE_TEXT": "Удаление вашего ключа API необратимо, хотя вы можете создать новый ключ API в любое время. Вы хотите удалить свой ключ API?", + "DELETE": "УДАЛИТЬ", + "FORM_NAME_LABEL": "Имя", + "FORM_LABLE_PLACEHOLDER": "Имя для ключа API", + "API_KEY_LABEL": "API-ключ", + "SECRET_KEY_LABEL": "Секретный ключ", + "CREATED_TITLE": "Скопируйте свой ключ API", + "CREATED_TEXT_1": "Пожалуйста, скопируйте свой ключ API, так как в будущем он будет недоступен.", + "CREATED_TEXT_2": "Держите ключ в секрете." + }, + "DEVELOPERS_TOKENS_TABLE": { + "NAME": "Имя", + "API_KEY": "API-ключ", + "SECRET": "Секрет", + "CREATED": "Дата создания", + "REVOKE": "Отозвать", + "REVOKED": "Аннулировано", + "REVOKE_TOOLTIP": "Вы должны включить 2FA, чтобы отозвать токен" + }, + "CHAT": { + "READ_MORE": "Читать далее", + "SHOW_IMAGE": "Показать изображение", + "HIDE_IMAGE": "Скрыть изображение", + "CHAT_MESSAGE_BOX_PLACEHOLDER": "Сообщение", + "TROLLBOX": "Тролльбокс ({0})", + "SET_USERNAME": "УСТАНОВИТЬ ИМЯ ПОЛЬЗОВАТЕЛЯ В ЧАТ" + }, + "INVALID_USERNAME": "Имя пользователя должно содержать от 3 до 15 символов. Содержит только строчные буквы, цифры и подчеркивание", + "USERNAME_TAKEN": "Это имя пользователя уже занято. Пожалуйста, попробуйте другой.", + "USERNAME_LABEL": "Имя пользователя (используется для чата)", + "USERNAME_PLACEHOLDER": "Имя пользователя", + "TAB_USERNAME": "Имя пользователя", + "USERNAME_WARNING": "Имя пользователя можно изменить только один раз. Пожалуйста, убедитесь, что ваше имя пользователя желательно.", + "USERNAME_CANNOT_BE_CHANGED": "Имя пользователя не может быть изменено", + "UPGRADE_LEVEL": "Повысить уровень аккаунта", + "LEVELS": { + "LABEL_LEVEL": "Уровень", + "LABEL_LEVEL_1": "Один", + "LABEL_LEVEL_2": "Два", + "LABEL_LEVEL_3": "Три", + "LABEL_BASE_DEPOSIT": "Ежедневный депозит в евро", + "LABEL_BASE_WITHDRAWAL": "Ежедневный вывод евро", + "LABEL_BTC_DEPOSIT": "Ежедневный биткойн-депозит", + "LABEL_BTC_WITHDRAWAL": "Ежедневный вывод биткойнов", + "LABEL_ETH_DEPOSIT": "Ежедневный депозит Ethereum", + "LABEL_ETH_WITHDRAWAL": "Ежедневный вывод Эфириума", + "LABEL_PAIR_MAKER_FEE": "Комиссия производителя {0}", + "LABEL_PAIR_TAKER_FEE": "{0} Комиссия тейкера", + "UNLIMITED": "Неограниченный", + "BLOCKED": "Неполноценный" + }, + "WALLET_ADDRESS_TITLE": "Создать кошелек {0}", + "WALLET_ADDRESS_GENERATE": "Создать", + "WALLET_ADDRESS_MESSAGE": "Когда вы создаете кошелек, вы создаете депозитный адрес.", + "WALLET_ADDRESS_ERROR": "Ошибка при создании адреса, обновите и повторите попытку.", + "DEPOSIT_WITHDRAW": "Депозит/Снятие", + "GENERATE_WALLET": "Создать кошелек", + "TRADE_TAB_CHART": "Диаграмма", + "TRADE_TAB_TRADE": "Торговля", + "TRADE_TAB_ORDERS": "Заказы", + "TRADE_TAB_POSTS": "Объявления", + "WALLET_TAB_WALLET": "Кошелек", + "WALLET_TAB_TRANSACTIONS": "Транзакции", + "RECEIVE_CURRENCY": "Депозит {0}", + "SEND_CURRENCY": "Отправить {0}", + "COPY_ADDRESS": "Копировать адрес", + "SUCCESFUL_COPY": "Успешно скопировано!", + "QUICK_TRADE_MODE": "Режим быстрой торговли", + "JUST_NOW": "прямо сейчас", + "PAIR": "Пара", + "ZERO_ASSET": "У вас нет активов", + "DEPOSIT_ASSETS": "Депозитные активы", + "SEARCH_TXT": "Поиск", + "SEARCH_ASSETS": "Искать активы", + "TOTAL_ASSETS_VALUE": "Общая стоимость активов в {0}: {1}", + "SUMMARY": { + "TITLE": "Краткое содержание", + "URGENT_REQUIREMENTS": "Срочные требования", + "TRADING_VOLUME": "Объем торгов", + "ACCOUNT_ASSETS": "Активы аккаунта", + "ACCOUNT_DETAILS": "Детали учетной записи", + "VIEW_FEE_STRUCTURE": "Посмотреть структуру комиссий и лимиты", + "UPGRADE_ACCOUNT": "Обновить аккаунт", + "ACTIVE_2FA_SECURITY": "Активная двухфакторная аутентификация", + "ACCOUNT_ASSETS_TXT_1": "Отображается сводка всех ваших активов.", + "ACCOUNT_ASSETS_TXT_2": "Владение большим количеством активов дает вам право на повышение уровня учетной записи, которое включает в себя уникальный значок и более низкие торговые сборы.", + "ACCOUNT_DETAILS_TXT_1": "Тип вашей учетной записи определяет значок вашей учетной записи, торговую комиссию, депозиты и лимиты на снятие средств.", + "ACCOUNT_DETAILS_TXT_2": "Возраст вашего торгового счета, уровень активности и общая сумма активов на счете будут определять, имеет ли ваша учетная запись право на обновление.", + "ACCOUNT_DETAILS_TXT_3": "Поддержание уровня вашего счета требует постоянной торговли и поддержания определенного количества депонированных активов.", + "ACCOUNT_DETAILS_TXT_4": "Периодическое понижение уровня учетных записей будет происходить, если активность и активы не поддерживаются.", + "REQUIREMENTS": "Требования", + "ONE_REQUIREMENT": "Только одно требование:", + "REQUEST_ACCOUNT_UPGRADE": "Запросить обновление учетной записи", + "FEES_AND_LIMIT": "{0} Структура комиссий и лимитов", + "FEES_AND_LIMIT_TXT_1": "Стать криптотрейдером означает новое начало. Вооружившись смекалкой, волей и скоростью, только рискуя и торгуя, вы сможете обновить свой счет.", + "FEES_AND_LIMIT_TXT_2": "Каждая учетная запись имеет свои собственные комиссии и лимиты на ввод и вывод средств.", + "TRADING_FEE_STRUCTURE": "Структура торговых комиссий", + "DEPOSIT_AND_WITHDRAWAL_FEES": "Комиссия за ввод и вывод средств", + "WITHDRAWAL": "Снятие", + "DEPOSIT": "Депозит", + "TAKER": "Берущий", + "MAKER": "Создатель", + "DEPOSITS": "Депозиты", + "WITHDRAWALS": "Снятие средств", + "NOMINAL_TRADING_WITH_MONTH": "Номинальная торговая последняя {0}", + "LEVEL_OF_ACCOUNT": "Аккаунт {0} уровня", + "TITLE_OF_ACCOUNT": "{0} Аккаунт", + "LEVEL_TXT_DEFAULT": "Добавьте сюда описание вашего уровня", + "CURRENT_TXT": "Текущий", + "EMAIL_VERIFICATION": "Подтверждение по элетронной почте", + "DOCUMENTS": "Документы", + "HAP_TEXT": "Партнерская программа HollaEx (HAP) {0}", + "LOCK_AN_EXCHANGE": "Заблокировать биржу {0}", + "WALLET_SUBSCRIPTION_USERS": "Пользователи подписки на Сейф {0}", + "TRADE_OVER_XHT": "Торгуйте на сумму более {0} USDT", + "XHT_IN_WALLET": "{0} XHT в кошельке", + "TASKS": "Задания", + "INVITE_USER": "Приглашайте пользователей и получайте комиссионные от их торговли", + "DISCOUNT": "(скидка {0}% )", + "MY_FEES_LIMITS": " Мои комиссии и лимиты", + "MARKETS": "Рынки", + "CHANGE_24H": "24-часовая смена", + "VOLUME_24H": "24-часовой объем", + "VIEW_MORE_MARKETS": "Посмотреть больше рынков", + "VIEW_VERIFICATION": "просмотреть подтверждение", + "EARN_COMMISSION": "Зарабатывайте комиссионные", + "ID_VERIFICATION": "Проверка личности:", + "VOLUME_7D": "7D Объем" + }, + "REFERRAL_LINK": { + "TITLE": "Пригласи своего друга", + "INFO_TEXT": "Пригласите своих друзей, раздав эту ссылку, и получайте выгоду от регистрации других людей.", + "COPY_FIELD_LABEL": "Поделитесь ссылкой ниже с друзьями и зарабатывайте комиссионные:", + "REFERRED_USER_COUT": "Вы привлекли {0} пользователей", + "COPY_LINK_BUTTON": "КОПИРОВАТЬ РЕФЕРАЛЬНУЮ ССЫЛКУ", + "VIEW": "вид", + "TABLE_TITLE": "Все успешные рефералы", + "USER": "Пользователь/электронная почта", + "TIME": "Время регистрации" + }, + "NOT_LOGGEDIN": { + "TEXT_GENERAL": "Для просмотра необходимо авторизоваться", + "TXT_1": "Чтобы начать торговать, вам необходимо авторизоваться", + "TXT_2": "{0} или {1}", + "LOGIN_HERE": "войти здесь" + }, + "USER_LEVEL": "Уровень пользователя", + "LIMIT_AMOUNT": "Предельная сумма", + "FEE_AMOUNT": "Величина платежа", + "COINS": "Монеты", + "PAIRS": "Пары", + "NOTE_FOR_EDIT_COIN": "Примечание. Для добавления и удаления {0} см.{1}.", + "REFER_DOCS_LINK": "документы", + "EXPIRED_INFO_1": "Ваше испытание закончилось.", + "EXPIRED_INFO_2": "Обеспечьте свою биржу, чтобы активировать ее снова.", + "EXPIRED_BUTTON_TXT": "АКТИВИРОВАТЬ ОБМЕН", + "TRADE_POSTS": { "LEARN_MORE": "Узнать больше" }, + "OPEN_WALLET": "Открытый кошелек", + "CUMULATIVE_AMOUNT_SYMBOL": "Общий", + "POST_ONLY": "Опубликовать только", + "CLEAR": "Прозрачный", + "ORDER_TYPE": "Тип", + "ORDER_MODE": "Режим заказа", + "TRIGGER_CONDITIONS": "Условия срабатывания", + "TRANSACTION_STATUS": { "PENDING": "В ожидании", "REJECTED": "Отклоненный", "COMPLETED": "Завершенный" }, + "DEPOSIT_STATUS": { + "NEW": "Новый", + "SEARCH_FIELD_LABEL": "Вставьте идентификатор транзакции", + "CHECK_DEPOSIT_STATUS": "Проверить статус депозита", + "SEARCH_BLOCKCHAIN_FOR_DEPOSIT": "Поиск блокчейна для вашего депозита", + "STATUS_DESCRIPTION": "Вы можете проверить статус своего депозита, передав идентификатор транзакции (хэш) ниже.", + "TRANSACTION_ID": "Идентификатор транзакции (хэш)", + "SEARCH_SUCCESS": "Поиск завершен", + "ADDRESS_FIELD_LABEL": "Вставьте свой адрес", + "CURRENCY_FIELD_LABEL": "Выберите валюту", + "DESTINATION_TAG_LABEL": "Тег (необязательно)", + "MEMO_LABEL": "Памятка (необязательно)", + "DESTINATION_TAG_PLACEHOLDER": "Вставьте свой тег", + "MEMO_PLACEHOLDER": "Вставьте свою заметку" + }, + "CANCEL_ORDERS": { + "HEADING": "Отменить заказы", + "SUB_HEADING": "Отменить все заказы", + "INFO_1": "Это отменит ваши открытые ордера для рынка {0}.", + "INFO_2": "Вы уверены, что хотите отменить все открытые ордера?" + }, + "AMOUNT_IN": "Сумма в", + "LIMITS_BLOCK": { + "HEADER_ROW_DESCRIPTION": "Разрешение на ввод и вывод средств в течение 24 часов для всех активов ({0})", + "HEADER_ROW_TYPE": "Тип (все активы)", + "HEADER_ROW_AMOUNT": "Сумма за 24 часа ({0})" + }, + "MARKETS_TABLE": { + "TITLE": "Живые рынки", + "MARKETS": "Рынки", + "LAST_PRICE": "Последняя цена", + "CHANGE_24H": "Изменение (24 часа)", + "VOLUME_24h": "Объем (24 часа)", + "CHART_24H": "График (24 часа)", + "VIEW_MARKETS": "Посмотреть рынки", + "TRADING_SYMBOL": "Торговый символ", + "TYPE": "Тип", + "SOURCE": "ИСТОЧНИК", + "CHANGE_7D": "Изменение (7 дней)", + "CHANGE_1D": "Изменение (1 день)", + "CHART_7D": "График (7 дней)", + "ASSET": "Объект" + }, + "SUMMARY_MARKETS": { + "HOLLAEX": "Хотите перечислить свои цифровые активы? Начните свой собственный рынок с HollaEx {0} {1}", + "WHITE_LABEL": "белая этикетка", + "SOLUTION": "решение", + "VISIT_COIN_INFO_PAGE": "Посетите страницу информации о монете {0}", + "HERE": "здесь" + }, + "PAGE_UNDER_CONSTRUCTION": "Эта страница находится в стадии разработки. Пожалуйста, вернитесь к этой странице в ближайшее время.", + "UNDEFINED_ERROR_TITLE": "Вы столкнулись с неизвестной ошибкой", + "UNDEFINED_ERROR": "Ух ты! Произошла неизвестная ошибка. Это может быть проблема с подключением или ряд других вещей. Вы можете повторить попытку позже или попробовать обновить.", + "POST_ONLY_TOOLTIP": "Ордера только на размещение выполняются только как лимитные ордера.", + "REFRESH": "Обновить", + "FEE_REDUCTION": "Снижение комиссии", + "FEE_REDUCTION_DESCRIPTION": "*к вашему аккаунту применяется скидка на комиссию. Снижение применяется к торговым сборам в зависимости от вашей учетной записи.", + "CHANGE_PASSWORD_FAILED": "смена пароля не удалась", + "MARKET_OPTIONS": { "LIST": "Список", "CARD": "Карта" }, + "ALL": "Все", + "ASSET_TXT": "Объект", + "ONE_DAY": "1 день", + "ONE_WEEK": "1 неделя", + "MONTH": "{0} месяц", + "START_DATE": "Дата начала", + "END_DATE": "Дата окончания", + "REGULAR": "Обычный", + "STOPS": "Остановки", + "VIEW_ALL": "посмотреть все", + "TRIGGER_PRICE": "Триггерная цена", + "SPEND_AMOUNT": "Сумма расходов", + "ESTIMATE_RECEIVE_AMOUNT": "Расчетная сумма получения", + "TOOLS": { + "ORDERBOOK": "Книга заказов", + "CHART": "Диаграмма", + "PUBLIC_SALES": "Публичные продажи", + "ORDER_ENTRY": "Порядок въезда", + "RECENT_TRADES": "Недавние сделки", + "OPEN_ORDERS": "Открытые ордера", + "WALLET": "Кошелек", + "DEPTH_CHART": "График глубины", + "COMING_SOON": "вскоре" + }, + "RESET_LAYOUT": "Сбросить раскладку", + "WALLET_BALANCE_LOADING": "Загрузка баланса...", + "LOADING": "Загрузка...", + "CONNECT_VIA_DESKTOP": { + "TITLE": "Подключиться через рабочий стол", + "SUBTITLE": "Ставки DeFi через мобильные устройства в настоящее время не поддерживаются.", + "TEXT": "Для подключения кошелька используйте настольный/портативный компьютер." + }, + "ORDER_HISTORY_CLOSED": "Закрыто", + "FIAT": { + "UNVERIFIED": { + "TITLE": "Полная проверка", + "TEXT": "Чтобы сделать {0}, вам необходимо пройти проверку, которая включает проверку ваших банковских реквизитов. Нажмите кнопку «Продолжить» ниже.", + "DEPOSIT": "депозит", + "WITHDRAWAL": "снятие" + }, + "REVIEW_DEPOSIT": { + "TITLE": "Проверьте и подтвердите данные депозита", + "SUBTITLE": "Пожалуйста, проверьте информацию о депозите ниже, чтобы убедиться, что все верно.", + "FORMAT": "{0} {1}", + "AMOUNT": "сумма для перечисления", + "FEE": "Комиссия за депозит", + "TRANSACTION_ID": "ID транзакции", + "NOTE": "Во избежание задержек убедитесь, что сумма вашего депозита, примечание и идентификатор транзакции совпадают с указанными выше данными. Если возникнут проблемы, обратитесь в службу поддержки.", + "BACK": "Назад", + "PROCEED": "Продолжить" + } + }, + "DEPOSIT_FEE_NOTE": "Обратите внимание, что общая сумма, внесенная на ваш счет, будет равна сумме за вычетом комиссии за депозит.", + "AMOUNT_LABEL": "Введите сумму депозита (должна совпадать с фактической суммой)", + "TRANSACTION_ID_LABEL": "Введите идентификатор транзакции депозита", + "FEE_LABEL": "Комиссия за депозит", + "AMOUNT_FORMAT": "{0} {1}", + "PENDING_DEPOSIT_TITLE": "Ожидание депозита", + "PENDING_DEPOSIT_TEXT_1": "Ваш депозит находится в очереди на проверку и будет оставаться в состоянии ожидания до тех пор, пока он не будет очищен.", + "PENDING_DEPOSIT_TEXT_2": "Обычно депозиты очищаются в течение 24 часов, но может занять до 48 часов, после того как сумма депозита за вычетом комиссии будет зачислена на ваш баланс.", + "DEPOSIT_HOME_NOTE": "Чтобы внести депозит, пожалуйста, введите сумму, которую вы будете вносить со своего банковского счета. Во избежание задержек убедитесь, что введенная ниже сумма соответствует фактической сумме, которую вы отправите или отправили с вашего банковского счета.", + "DEPOSIT_TXID_NOTE": "Чтобы избежать задержек с вашим депозитом, не забудьте ввести свое уникальное примечание, отображаемое выше, при внесении банковского депозита в примечание или сообщение о вашей транзакции. Убедитесь, что указанная ниже сумма соответствует сумме, которую вы фактически вносите в свой банк, и что вы указали идентификатор транзакции, предоставленный вашим банком после ее совершения.", + "DEPOSIT_BANK_TEXT": "Используйте банковские реквизиты ниже, чтобы сделать депозит.", + "MIN_DEPOSIT": "Минимальный депозит", + "MAX_DEPOSIT": "Максимальный депозит", + "BACK": "Назад", + "DONE": "Сделанный", + "PENDING_WITHDRAWAL_TITLE": "Ожидается вывод средств", + "PENDING_WITHDRAWAL_TEXT_1": "Ваш вывод сейчас находится в очереди на проверку и будет оставаться в состоянии ожидания до тех пор, пока не будет очищен.", + "DEPOSIT_AMOUNT_MIN_VALIDATION": "Транзакция слишком мала для отправки. Попробуйте большую сумму.", + "DEPOSIT_AMOUNT_MAX_VALIDATION": "Транзакция слишком велика для отправки. Попробуйте меньшее количество.", + "ACCOUNT_NAME": "Имя учетной записи", + "ACCOUNT_NUMBER": "номер счета", + "BANK_NAME": "название банка", + "VERIFY_BANK_WITHDRAW": "Чтобы вывести средства, вам необходимо пройти верификацию, которая включает проверку ваших банковских реквизитов. Нажмите кнопку «Продолжить» ниже.", + "VERIFICATION_TITLE": "Полная проверка", + "WITHDRAW_NOTE": "Обратите внимание: вы можете вывести средства только на счет, открытый на ваше имя.", + "USER_PAYMENT": { "TITLE": "Платежи" }, + "QUOTE_CONFIRMATION_MSG_TEXT_1": "Пожалуйста, проверьте свой заказ и подтвердите его ниже.", + "QUOTE_CONFIRMATION_MSG_TEXT_2": "Сумма к получению является приблизительной и не включает комиссию за торговлю.", + "ORDER_EXPIRED_MSG": "Срок действия заказа истек. Пожалуйста, обновите!", + "WITHDRAWALS_FORM_METHOD": "Метод", + "WITHDRAWALS_FORM_ADDRESS_EXCHANGE": "Обмен электронной почтой пользователя", + "WITHDRAWALS_FORM_EXCHANGE_PLACEHOLDER": "Введите адрес электронной почты пользователя на этой бирже", + "WITHDRAWALS_FORM_MAIL_INFO": "Введите адрес электронной почты учетной записи пользователя HollaEx, который находится на этой бирже, и переведите его бесплатно.", + "DUST": { + "TITLE": "Кошелек тряпка", + "LINK": "Кошелек тряпка", + "TOOLTIP": "Конвертируйте крошечные остатки кошелька (пыль) в криптовалюту", + "BACK_PLACEHOLDER": "{0} {1}", + "BACK_TO_WALLET": "в мой кошелек", + "BACK": "Назад", + "ESTIMATED_TOTAL": "Расчетное общее количество пыли", + "CONVERT_ALL": "конвертировать", + "NO_DUST": "Никаких пылевых активов!", + "SECTION": { + "TITLE": "Преобразовать бумажную пыль", + "TEXT_1": "Преобразуйте все низкие остатки кошелька, которые содержат значение {0} {1} или меньше.", + "TEXT_2": "Все разговоры будут урегулированы в {0} ({1})." + }, + "CONFIRMATION": { + "TITLE": "Преобразование пыли", + "SUBTITLE": "Преобразуемые активы", + "BACK": "назад", + "CONFIRM": "подтверждать", + "GET": "Ты получишь", + "FEE": "Плата за конвертацию", + "PLEASE": "Пожалуйста, обрати внимание", + "NOTE": "Преобразовывать пыль в {0} можно каждые 24 часа. Будут преобразованы только балансы со значением менее {1} {2}." + }, + "SUCCESSFUL": { + "TITLE": "Преобразование пыли успешно!", + "TEXT": "Вы получили", + "VIEW_HISTORY": "Посмотреть историю", + "CLOSE": "закрывать" + } + }, + "ASSET_INFO_PAGE": "Страница информации об объекте", + "MARKET_ROW": { "NATIVE": "Родной", "NO_NATIVE": "Нет родного источника" }, + "DIGITAL_ASSETS": { + "DIGITAL_ASSETS_TITLE": "Цифровые активы", + "GO_BACK": "Возвращаться", + "ASSETS_INFO": "Ниже представлены доступные активы на платформе.", + "ASSETS_INFO_DETAIL": "Вы можете щелкнуть актив в списке ниже, чтобы узнать больше.", + "QUICK_TRADE": "БЫСТРАЯ ТОРГОВЛЯ", + "MARKETS": "РЫНКИ", + "WALLET": "КОШЕЛЕК", + "PRICE_SOURCE": "Источник цены", + "PRO_TRADE": "РЫНКИ", + "ORDERBOOK": "Книга заказов", + "NETWORK": "Сетевой брокер внебиржевой", + "BROKER": "Местный брокер внебиржевой", + "BROKERAGE": "NA – (брокерские услуги)" + }, + "HOLLAEX_TOKEN": { + "REMOVE_FAVOURITES": "Удалить из избранного", + "ADD_FAVOURITES": "Добавить в избранное", + "GO_BACK": " Возвращаться", + "BALANCE": "Баланс:", + "OPEN_WALLET": "(Открыть кошелек)", + "ABOUT": "О", + "VIEW": "Посмотреть больше", + "COMING_SOON": "Вскоре...", + "NO_DESCRIPTION": "Этот цифровой актив в настоящее время не имеет описания.", + "STAKE": "Ставка", + "MORE": "Более", + "INFO": "Информация", + "TRADE": "ТОРГОВЛЯ", + "LIST": "Хотите перечислить свои цифровые активы?", + "WEBSITE": "Веб-сайт", + "EXPLORER": "Исследователь", + "QUICK_TRADE": "КОНВЕРТИРОВАТЬ {0}", + "PRO_TRADE": "ПОСМОТРЕТЬ РЫНОК {0}", + "PRICES": { + "TITLE": "Цены", + "QUICK_TRADE": "Быстрая торговля", + "SUBTITLE": "Чтобы просмотреть цены покупки и продажи для {0}, посетите {1}.", + "FOOTER": "Торговые комиссии маркет-тейкера применяются ко всем сделкам, совершаемым через Quick Trade." + } + }, + "ACCORDIAN": { + "ACCORDIAN_ASSETS": "Все активы кошелька", + "ACCORDIAN_INFO": "СТРАНИЦА ИНФОРМАЦИИ ОБ АКТИВАХ", + "ACCORDIAN_HISTORY": "ИСТОРИЯ" + }, + "ASYNC_LINK": { + "TITLE": "Страница не открывается автоматически?", + "TEXT": "Ваш браузер может блокировать страницу. Вместо этого попробуйте скопировать и вставить ссылку ниже:" + }, + "TERMS_OF_SERVICES": { "TO_GET_ACCESS": "Чтобы получить доступ, обратитесь к" }, + "FEES_AND_LIMITS": { + "LINK": "просмотреть мои сборы и лимиты", + "COIN_PAGE_LINK": "Торговые сборы и лимиты", + "BACK": { "PLACEHOLDER": "{0} {1}", "BACK": "Возвращаться", "TO": "резюмировать" }, + "TITLE": "Комиссии и лимиты", + "SEARCH_PLACEHOLDER": "Поиск...", + "TABS": { + "TRADING_FEES": { + "TITLE": "Торговые сборы", + "TABLE": { + "TITLE": "Торговые сборы на рынке", + "SUBTITLE": "Ниже приведены правила торговых комиссий для отдельных рынков.", + "HEADER": { + "MARKET": "Рынок", + "MAKER": "Торговые сборы Maker (лимитные ордера)", + "TAKER": "Торговые сборы тейкера (рыночные ордера)" + } + } + }, + "WITHDRAWAL_FEES": { + "TITLE": "Комиссия за снятие денег", + "TABLE": { + "TITLE": "Комиссия удерживается при выводе средств", + "SUBTITLE": "Ниже приведены все комиссии при выводе актива.", + "NOT_ALLOWED": "Не допускается", + "HEADER": { + "CURRENCY": "Валюта/актив", + "DEPOSIT": "Комиссия за депозит", + "WITHDRAWAL": "Комиссия за снятие средств" + } + } + }, + "WITHDRAWAL_LIMITS": { + "TITLE": "Лимиты на вывод", + "TABLE_1": { + "TITLE": "Независимый лимит вывода", + "SUBTITLE": "Активы, которые отделены от правила коллективного лимита, которые следуют собственному независимому правилу вывода средств в течение 24 часов:", + "HEADER": { + "CURRENCY": "Валюта/актив", + "LIMIT": "24-часовой лимит", + "LIMIT_2": "Ежемесячный лимит" + }, + "LIMIT_TEXT": "(Активы, которые имеют общую сумму лимита снятия)", + "LIMIT_TEXT_2": "(Ограничение основано только на собственной валюте)", + "VALUED": "(Ценится)", + "NO_INDEPENDENT": "Для этого уровня не существует независимого ограничения.", + "LIMIT_TEXT_3": "(Все активы имеют общую сумму дневного лимита снятия средств, выраженную в {0})", + "LIMIT_TEXT_4": "(Все активы имеют общий ежемесячный лимит снятия средств, выраженный в {0})" + }, + "TABLE_2": { + "TITLE": "Коллективный общий стандартный лимит на снятие средств", + "SUBTITLE": "Ниже приведены активы, для которых действует одно и то же стандартное правило суммы вывода средств за 24 часа:" + }, + "TABLE_3": { + "TITLE": "Коллективные ограничения", + "SUBTITLE": "Комбинированный дневной и ежемесячный лимит вывода для группы монет" + } + } + } + }, + "STATUS_TEXT": { + "INCOMPLETE": "неполный", + "PENDING": "в ожидании", + "REJECTED": "отклоненный", + "VERIFIED": "проверено" + }, + "LOGINS_HISTORY": { + "TAB": "История входа", + "CONTENT": { + "TITLE": "Запись попыток входа", + "SUBTITLE": "Историческая запись всех неудачных попыток входа в вашу учетную запись и их потенциальное происхождение.", + "DOWNLOAD_HISTORY": "СКАЧАТЬ_ИСТОРИЯ", + "FILTER": { + "STATUS": "Положение дел", + "ALL": "Все", + "FAILED": "Неуспешный", + "SUCCESS": "Успех" + }, + "TABLE": { + "HEADER": { + "LAST_SEEN": { + "TITLE": "Последнее посещение", + "DESCRIPTION": "(Самый недавний в начале)" + }, + "FAILED_ATTEMPTS": "Неудачные попытки", + "COUNTRY": "Страна", + "IP_ADDRESS": "Айпи адрес" + }, + "CELL": { + "FAILED_LOGIN": "Неудачный вход: {0}x", + "SUCCESS": "Успех", + "DEVICE": "Устройство" + } + } + } + }, + "SESSIONS": { + "TAB": "Сессии", + "CONTENT": { + "TITLE": "Активная сессия", + "SUBTITLE": { + "TEXT": "Ниже приведены активные сеансы вашей учетной записи. Чтобы завершить активный сеанс, нажмите {0}, и он выйдет из этого сеанса.", + "LINK": "Отозвать" + }, + "DOWNLOAD": "Скачать", + "TABLE": { + "HEADER": { + "LAST_SEEN": { + "TITLE": "Последнее посещение", + "DESCRIPTION": "(Самый недавний в начале)" + }, + "SESSION_STARTED": "Сеанс начался", + "SESSION_EXPIRY": "Срок действия сеанса", + "STATUS": "Положение дел" + }, + "CELL": { + "ACTIVE": "Активный", + "COUNTRY": "Страна", + "IP_ADDRESS": "Айпи адрес", + "DEVICE": "Устройство", + "LOGIN_TIME": "Время успешного входа" + } + }, + "NOTIFICATION": "Сессия отменена/выход из системы", + "MODAL": { + "TITLE": "Отменить сеанс", + "PROMPT": "Вы уверены, что хотите отозвать и выйти из этой сессии?", + "BACK": "НАЗАД", + "CONFIRM": "ПОДТВЕРЖДАТЬ" + } + } + }, + "TRADE_TOOLS": "Инструменты", + "CANCEL_WITHDRAWAL_ADDRESS": "Адрес вывода", + "SECOND": "второй", + "CANT_FIND_MARKETS": "Невозможно найти рынки для этого актива.", + "TRY_VISITING_ASSETS": "Попробуйте посетить {0}.", + "ASSETS_PAGE": "Страница ресурсов", + "NETWORK": "Сеть", + "CATEGORY": "Категория", + "ASSETS": "Ресурсы", + "QUICK_TRADE_QUOTE_EXPIRED_FIRST_LINE": "Срок действия ценового предложения истек.", + "QUICK_TRADE_QUOTE_EXPIRED_SECOND_LINE": "Нажмите «реквотировать», чтобы получить новую цену.", + "QUICK_TRADE_QUOTE_EXPIRED_BUTTON": "РЕКВОТ", + "ZERO_ASSET_2": "обнаружен. Делать", + "ZERO_ASSET_3": "депозит ниже.", + "QUOTE_CONFIRMATION_EXPIRY_MSG": "Срок действия ценового предложения истекает через {0} {1}.", + "QUOTE_CONFIRMATION_EXPIRED_MSG_TEXT_1": "Срок действия цены истек.", + "QUOTE_CONFIRMATION_EXPIRED_MSG_TEXT_2": "Вернитесь, чтобы получить новое ценовое предложение.", + "RISKY_TRADE_DISCLAIMER": { + "MSG_1": "Пожалуйста, убедитесь, что вы действительно хотите торговать {0} ({1}), поскольку этот актив может нести высокий риск", + "MSG_2": "крайняя волатильность", + "MSG_3": "Установите флажок ниже, чтобы подтвердить, что вы понимаете, что стоимость этого актива неопределенна и не гарантирована.", + "UNDERSTAND_RISK_MSG": "Да, я понимаю риски" + }, + "PROFIT_LOSS": { + "BACK": "Назад", + "BACK_TO_WALLET": "в кошелек", + "WALLET_PERFORMANCE_TITLE": "Баланс производительности", + "WALLET_PERFORMANCE_DESCRIPTION": "Динамика баланса вашего кошелька с течением времени и структура активов. Нажмите на даты ниже, чтобы увидеть эффективность вашего кошелька в этот день.", + "PL_7_DAY": "Прибыли и убытки за 7 дней", + "PL_DAYS": "Дневной прибылей и убытков", + "EST_TOTAL_BALANCE": "Восток. Итоговый баланс", + "WEEK": "неделя", + "MONTH": "месяц", + "MONTHS": "месяцы", + "WALLET_BALANCE": "Разбивка баланса кошелька", + "WALLET_BALANCE_DESCRIPTION_1": "Ниже представлена разбивка кошелька по", + "WALLET_BALANCE_DESCRIPTION_2": "Нажмите на диаграмму выше, чтобы обновить таблицу ниже.", + "DATE_SELECT": "Выберите дату", + "ASSET_NAME": "Имя актива", + "BALANCE_AMOUNT": "Сумма остатка", + "VALUE": "Ценить", + "PERFORMANCE_TREND": "Тенденция производительности за 7 дней", + "CUSTOM_DATE_DESCRIPTION": "Выберите конкретные даты, чтобы получить исторические данные (разница в датах не может превышать 3 месяцев)", + "SELECT_START_DATE": "Выберите дату начала", + "SELECT_END_DATE": "Выберите дату окончания", + "CUSTOM": "Обычай", + "BACK_CUSTOM": "НАЗАД", + "PROOCED_CUSTOM": "ПРОЦЕДУРА", + "VIEW_MORE": "Посмотреть больше {0}", + "SHOW_ASSET_BREAKDOWN": "Показать разбивку активов", + "PL_SUMMARY": "Сводка о прибылях и убытках", + "BALANCE_HISTORY": "История баланса" + }, + "ASSET_INFO": "Информация об активе", + "TAKER_FEES_APPLIED": "Взимаются комиссии тейкера" +} \ No newline at end of file diff --git a/web/src/config/lang/tr.json b/web/src/config/lang/tr.json index 16c0800862..e9ae15ef79 100644 --- a/web/src/config/lang/tr.json +++ b/web/src/config/lang/tr.json @@ -193,7 +193,9 @@ "ERROR_INVALID": "Hatalı OTP Kodu", "OTP_FORM_INFO": "Devam etmek için 6 haneli kodunuzu girin", "OTP_FORM_SUBNOTE_LINE_1": "Kodunuz aynı zamanda iki faktörlü kimlik doğrulayıcı (2FA) veya OTP kodu olarak da bilinir.", - "OTP_FORM_SUBNOTE_LINE_2": "Kodunuzu kaybettiyseniz lütfen destek ekibiyle iletişime geçin." + "OTP_FORM_SUBNOTE_LINE_2": "Kodunuzu kaybettiyseniz lütfen destek ekibiyle iletişime geçin.", + "OTP_FORM_SUBNOTE_LINE_3": "(To add a new account to Google Authenticator, click the '+' button. You can either scan the provided QR code in the previous step or manually enter the key to add your account to the app.)", + "INPUT_TEXT": "Input the 6-digit code from your phone's Google Authenticator app:" }, "EMAIL_CODE_FORM": { "TITLE": "Güvenlik kodlarınızı giriniz", @@ -565,7 +567,8 @@ "INTERNAL": "dahili", "BLOCKCHAIN": "blok zinciri", "STAKE": "hisse", - "REFERRAL": "Referans" + "REFERRAL": "Referans", + "QUICK_TRADE_TOOLTIP": "Dönüştürme sistemi aracılığıyla yürütülen sipariş" }, "ACCOUNT_SECURITY": { "TITLE_TEXT": "Hesabınızın güvenlik seviyesini arttırın. İki Faktörlü Doğrulama, Şifre, API anahtarları ve diğer güvenlik fonksiyonları.", @@ -582,15 +585,46 @@ "MESSAGE_3": "Eğer kodu taramakta sorun yaşarsanız, aşağıdaki kodu cihazınıza kendiniz girebilirsiniz.", "MESSAGE_4": "Telefonunuzu kaybetmeniz ya da değiştirmeniz durumunda, 2FA doğrulamanızı tekrar yaratabilmeniz için, bu kodu lütfen saklayınız.", "MESSAGE_5": "Manual", - "WARNING": "İki-Faktörlü doğrulamayı (2FA) aktiflemenizi şiddetle öneriyoruz. Bu şekilde fonlarınızın güvenliğini önemli ölçüde arttırabilirsiniz.", + "WARNING": "Biz {0} {1}", "ENABLE": "İki-Faktörlü Doğrulamayı Aktifle", "DISABLE": "İki-Faktörlü Doğrulamayı Kapat", - "INPUT": "Lütfen OTP kodunuzu giriniz" + "INPUT": "Lütfen OTP kodunuzu giriniz", + "STRONGLY_RECOMMEND": "şiddetle tavsiye ederim", + "WARNING_CONTENT": "hesabınızın güvenliğini artırmak ve paranızı korumak için iki faktörlü kimlik doğrulamayı etkinleştirmeniz" }, "FORM": { "PLACEHOLDER": "Google Authenticator tarafından sağlanan OTP kodunuzu giriniz.", "BUTTON": "2FA AKTİFLE" - } + }, + "DOWNLOAD_APP": "Download and install", + "GOOGLE_AUTHENTICATOR": "Google Authenticator:", + "DOWNLOAD_FROM": "Download from", + "GOOGLE_PLAY": "GooglePlay", + "APPLE": "App Store", + "APP_INFO": "Google Authenticator will generate random codes that are necessary for sensitive actions, such as logging in and withdrawing funds.", + "DONT_UNINSTALL": "Lütfen uygulamayı kaldırmayın.", + "INFO_UNINSTALL": "Telefonunuzun kazara kaybolması veya istemeden kaldırılması durumunda, hesabınıza yeniden erişim sağlamada gecikmeler yaşayabilirsiniz. Bu gibi durumlarda yardım için destek ekibimizle iletişime geçmeniz gerekecektir.", + "BACK": "GERİ", + "PROCEED": "İLERLEMEK", + "NEXT": "Sonraki", + "NOTE": "Not: {0} {1}", + "MANUEL_DESCRIPTION": "Lütfen önceki adımdaki manuel 2FA tohumunu girin.", + "MANUEL_DESCRIPTION_2": "Lütfen yukarıdaki manuel doğrulamayı dikkatlice yazıp saklayın.", + "MANUEL_DESCRIPTION_3": "Hesap kurtarma için kullanılabilecek anahtar.", + "MANUEL_KEY": "Manuel 2FA tohumu", + "MANUEL_PLACEHOLDER": "2FA tohumunuzu girin", + "MANUEL_WARNING": "(Bunu bir yere yazdığınızdan ve ileride kullanmak üzere güvenli bir şekilde sakladığınızdan emin olun.)", + "MANUEL_ERROR_1": "Lütfen 2FA tohum kılavuzunu girin", + "MANUEL_ERROR_2": "Lütfen önceki adımdaki manuel 2FA tohumunu girin", + "ACCOUNT_SECURED": "Hesap Güvence Altında", + "2FA_ENABLED": "2FA ETKİN", + "2FA_DISABLED": "2FA DEVRE DIŞI", + "2FA_CONTENT_ONE": "{0} {1} etkinleştiriliyor.", + "2FA_CONTENT_TWO": "(İki Faktörlü Kimlik Doğrulama), paranızı korumak için ekstra bir koruma katmanı ekler. Etkinleştirildiğinde, her giriş yaptığınızda veya varlıkları çektiğinizde telefonunuzdan bir doğrulama kodu girmeniz istenecektir. Bu özellik hesabınızın güvenliğini önemli ölçüde artırır", + "2FA_CONTENT_THREE": "(İki Faktörlü Kimlik Doğrulama), paranızı korumak için ekstra bir koruma katmanı ekler", + "2FA_CONTENT_FOUR": "2FA'yı devre dışı bırakmak için 2FA (OTP) kodunuzu girmeniz gerekecektir.", + "INPUT_SIX_DIGIT_CODE": "Telefonunuzun Google Authenticator uygulamasından 6 haneli kodu girin:", + "INPUT_CONTENT": "(Google Authenticator'a yeni bir hesap eklemek için '+' düğmesini tıklayın. Önceki adımda verilen QR kodunu tarayabilir veya hesabınızı uygulamaya eklemek için anahtarı manuel olarak girebilirsiniz.)" }, "CHANGE_PASSWORD": { "TITLE": "Şifre", @@ -1560,7 +1594,12 @@ "SELECT_END_DATE": "Bitiş tarihini seçin", "CUSTOM": "Gelenek", "BACK_CUSTOM": "GERİ", - "PROOCED_CUSTOM": "PROOCELENMİŞ" + "PROOCED_CUSTOM": "PROOCELENMİŞ", + "PL_DAYS": "Gün Kâr-Zararı", + "VIEW_MORE": "Daha Fazlasını Görüntüle {0}", + "SHOW_ASSET_BREAKDOWN": "Varlık Dökümünü Göster", + "PL_SUMMARY": "Kâr ve Zarar Özeti", + "BALANCE_HISTORY": "Bakiye Geçmişi" }, "ASSET_INFO": "Varlık bilgisi", "TAKER_FEES_APPLIED": "Alıcı ücretleri uygulanır", @@ -1603,7 +1642,8 @@ }, "LIST": "Dijital varlıklarınızı listelemek mi istiyorsunuz?", "WEBSITE": "İnternet sitesi", - "EXPLORER": "Kaşif" + "EXPLORER": "Kaşif", + "PRO_TRADE": "{0} PAZARINI GÖRÜNTÜLE" }, "ACCORDIAN": { "ACCORDIAN_ASSETS": "Tüm cüzdan varlıkları", @@ -1735,5 +1775,6 @@ "CONFIRM": "ONAYLAMAK" } } - } + }, + "CANCEL_WITHDRAWAL_ADDRESS": "Withdrawal Address" } \ No newline at end of file diff --git a/web/src/config/lang/ur.json b/web/src/config/lang/ur.json index eac3620ebd..c40d7462b5 100644 --- a/web/src/config/lang/ur.json +++ b/web/src/config/lang/ur.json @@ -169,7 +169,9 @@ "OTP_PLACEHOLDER": "تصدیقی کوڈ درج کریں۔", "OTP_TITLE": "تصدیق کنندہ کوڈ", "OTP_BUTTON": "جمع کرائیں", - "ERROR_INVALID": "غلط او ٹی پی کوڈ" + "ERROR_INVALID": "غلط او ٹی پی کوڈ", + "OTP_FORM_SUBNOTE_LINE_3": "(To add a new account to Google Authenticator, click the '+' button. You can either scan the provided QR code in the previous step or manually enter the key to add your account to the app.)", + "INPUT_TEXT": "Input the 6-digit code from your phone's Google Authenticator app:" }, "EMAIL_CODE_FORM": { "TITLE": "اپنے سیکیورٹی کوڈز داخل کریں۔", @@ -582,7 +584,8 @@ "INTERNAL": "اندرونی", "BLOCKCHAIN": "بلاکچین", "STAKE": "داؤ", - "REFERRAL": "حوالہ" + "REFERRAL": "حوالہ", + "QUICK_TRADE_TOOLTIP": "Order executed through convert system" }, "ACCOUNT_SECURITY": { "TITLE_TEXT": "اپنے اکاؤنٹ کے لیے سیکیورٹی کی ترتیبات کو ایڈجسٹ کریں۔ دو عنصر کی توثیق، پاس ورڈ، اے پی ای کیز اور سیکیورٹی سے متعلق دیگر افعال سے۔", @@ -605,15 +608,46 @@ "MESSAGE_3": "اگر آپ کو اسے اسکین کرنے میں دشواری پیش آتی ہے، تو آپ دستی طور پر نیچے کا کوڈ درج کر سکتے ہیں۔", "MESSAGE_4": "اگر آپ مستقبل میں اپنا موبائل فون تبدیل کرتے ہیں یا کھو دیتے ہیں تو آپ کو اس کوڈ کو محفوظ طریقے سے اپنے دو فیکٹر توثیقی کوڈ کو بحال کرنے کے لیے محفوظ کرنا چاہیے۔", "MESSAGE_5": "مینول", - "WARNING": "ہم آپ کو دو فیکٹر توثیقی کوڈ کی انتہائی سفارش کرتے ہیں۔ ایسا کرنے سے آپ کے فنڈز کی سیکیورٹی میں بہت اضافہ ہوگا۔", + "WARNING": "ہم {0} {1}", "ENABLE": "دو فیکٹر توثیقی کوڈ کو فعال کریں۔", "DISABLE": "دو فیکٹر توثیقی کوڈ کو غیر فعال کریں۔", - "INPUT": "براہ کرم اپنا او ٹی پی درج کریں۔" + "INPUT": "براہ کرم اپنا او ٹی پی درج کریں۔", + "STRONGLY_RECOMMEND": "سختی سے سفارش کرتے ہیں", + "WARNING_CONTENT": "کہ آپ اپنے اکاؤنٹ کی سیکیورٹی کو بڑھانے اور اپنے فنڈز کی حفاظت کے لیے دو عنصری تصدیق کو فعال کرتے ہیں۔" }, "FORM": { "PLACEHOLDER": "گوگل تصدیق کنندہ کے ذریعے فراہم کردہ اپنا او ٹی پی درج کریں۔", "BUTTON": "دو فیکٹر توثیقی کوڈ کو فعال کریں۔ " - } + }, + "DOWNLOAD_APP": "Download and install", + "GOOGLE_AUTHENTICATOR": "Google Authenticator:", + "DOWNLOAD_FROM": "Download from", + "GOOGLE_PLAY": "GooglePlay", + "APPLE": "App Store", + "APP_INFO": "Google Authenticator will generate random codes that are necessary for sensitive actions, such as logging in and withdrawing funds.", + "DONT_UNINSTALL": "براہ کرم ایپ کو ان انسٹال نہ کریں۔", + "INFO_UNINSTALL": "فون کے حادثاتی نقصان یا غیر ارادی طور پر ان انسٹال ہونے کی صورت میں، آپ کو اپنے اکاؤنٹ تک دوبارہ رسائی حاصل کرنے میں تاخیر کا سامنا کرنا پڑ سکتا ہے۔ ایسے معاملات میں، آپ کو مدد کے لیے ہماری سپورٹ ٹیم سے رابطہ کرنے کی ضرورت ہوگی۔", + "BACK": "پیچھے", + "PROCEED": "آگے بڑھو", + "NEXT": "اگلے", + "NOTE": "نوٹ: {0} {1}", + "MANUEL_DESCRIPTION": "براہ کرم پچھلے مرحلے سے دستی 2FA بیج درج کریں۔", + "MANUEL_DESCRIPTION_2": "براہ کرم احتیاط سے مندرجہ بالا دستی توثیق کو لکھ کر محفوظ کریں۔", + "MANUEL_DESCRIPTION_3": "کلید کیونکہ اسے اکاؤنٹ کی بازیابی کے لیے استعمال کیا جا سکتا ہے۔", + "MANUEL_KEY": "دستی 2FA بیج", + "MANUEL_PLACEHOLDER": "اپنا 2FA بیج درج کریں۔", + "MANUEL_WARNING": "(یقینی بنائیں کہ آپ نے اسے لکھ لیا ہے اور اسے مستقبل کے استعمال کے لیے محفوظ طریقے سے محفوظ کر لیا ہے۔)", + "MANUEL_ERROR_1": "براہ کرم دستی 2FA بیج درج کریں۔", + "MANUEL_ERROR_2": "براہ کرم پچھلے مرحلے سے دستی 2FA بیج داخل کریں۔", + "ACCOUNT_SECURED": "اکاؤنٹ محفوظ", + "2FA_ENABLED": "2FA فعال", + "2FA_DISABLED": "2FA غیر فعال", + "2FA_CONTENT_ONE": "{0} {1} کو چالو کیا جا رہا ہے۔", + "2FA_CONTENT_TWO": "(Two-factor Authentication) آپ کے فنڈز کی حفاظت کے لیے تحفظ کی ایک اضافی پرت کا اضافہ کرتا ہے۔ ایک بار فعال ہونے کے بعد، آپ کو ہر بار لاگ ان کرنے یا اثاثے نکالنے پر اپنے فون سے ایک تصدیقی کوڈ درج کرنے کی ضرورت ہوگی۔ یہ فیچر نمایاں طور پر آپ کے اکاؤنٹ کی سیکیورٹی کو بڑھاتا ہے۔", + "2FA_CONTENT_THREE": "(Two-factor Authentication) آپ کے فنڈز کی حفاظت کے لیے تحفظ کی ایک اضافی پرت کا اضافہ کرتا ہے۔", + "2FA_CONTENT_FOUR": "2FA کو غیر فعال کرنے کے لیے آپ کو اپنا 2FA (OTP) کوڈ درج کرنا ہوگا۔", + "INPUT_SIX_DIGIT_CODE": "اپنے فون کی Google Authenticator ایپ سے 6 ہندسوں کا کوڈ داخل کریں:", + "INPUT_CONTENT": "(Google Authenticator میں نیا اکاؤنٹ شامل کرنے کے لیے، '+' بٹن پر کلک کریں۔ آپ پچھلے مرحلے میں فراہم کردہ QR کوڈ کو اسکین کر سکتے ہیں یا ایپ میں اپنا اکاؤنٹ شامل کرنے کے لیے دستی طور پر کلید درج کر سکتے ہیں۔)" }, "CHANGE_PASSWORD": { "TITLE": "پاس ورڈ", @@ -1520,7 +1554,12 @@ "SELECT_END_DATE": "اختتامی تاریخ منتخب کریں۔", "CUSTOM": "اپنی مرضی کے مطابق", "BACK_CUSTOM": "پیچھے", - "PROOCED_CUSTOM": "آگے بڑھایا" + "PROOCED_CUSTOM": "آگے بڑھایا", + "PL_DAYS": "دن P&L", + "VIEW_MORE": "مزید دیکھیں {0}", + "SHOW_ASSET_BREAKDOWN": "اثاثوں کی خرابی دکھائیں۔", + "PL_SUMMARY": "P&L کا خلاصہ", + "BALANCE_HISTORY": "توازن کی تاریخ" }, "ASSET_INFO": "اثاثہ کی معلومات", "TAKER_FEES_APPLIED": "لینے والے کی فیس لاگو ہوتی ہے۔", @@ -1563,7 +1602,8 @@ }, "LIST": "اپنے ڈیجیٹل اثاثوں کی فہرست بنانا چاہتے ہیں؟", "WEBSITE": "ویب سائٹ", - "EXPLORER": "ایکسپلورر" + "EXPLORER": "ایکسپلورر", + "PRO_TRADE": "{0} مارکیٹ دیکھیں" }, "ACCORDIAN": { "ACCORDIAN_ASSETS": "تمام بٹوے کے اثاثے۔", @@ -1695,5 +1735,6 @@ "CONFIRM": "تصدیق کریں۔" } } - } + }, + "CANCEL_WITHDRAWAL_ADDRESS": "واپسی کا پتہ" } \ No newline at end of file diff --git a/web/src/config/lang/vi.json b/web/src/config/lang/vi.json index a9e4276cf0..e4b310b021 100644 --- a/web/src/config/lang/vi.json +++ b/web/src/config/lang/vi.json @@ -231,7 +231,9 @@ "ERROR_INVALID": "Mã OTP không hợp lệ", "OTP_FORM_INFO": "Nhập mã gồm 6 chữ số của bạn để tiếp tục", "OTP_FORM_SUBNOTE_LINE_1": "Mã của bạn còn được gọi là mã xác thực hai yếu tố (2FA) hoặc mã OTP.", - "OTP_FORM_SUBNOTE_LINE_2": "Nếu bạn bị mất mã, vui lòng liên hệ với bộ phận hỗ trợ." + "OTP_FORM_SUBNOTE_LINE_2": "Nếu bạn bị mất mã, vui lòng liên hệ với bộ phận hỗ trợ.", + "OTP_FORM_SUBNOTE_LINE_3": "(Để thêm tài khoản mới vào Google Authenticator, hãy nhấp vào nút '+'. Bạn có thể quét mã QR được cung cấp ở bước trước hoặc nhập khóa theo cách thủ công để thêm tài khoản của bạn vào ứng dụng.)", + "INPUT_TEXT": "Nhập mã gồm 6 chữ số từ ứng dụng Google Authenticator trên điện thoại của bạn:" }, "QUICK_TRADE_COMPONENT": { "TITLE": "Giao dịch nhanh", @@ -627,7 +629,8 @@ "INTERNAL": "nội bộ", "BLOCKCHAIN": "chuỗi khối", "STAKE": "cổ phần", - "REFERRAL": "giới thiệu" + "REFERRAL": "giới thiệu", + "QUICK_TRADE_TOOLTIP": "Lệnh được thực hiện thông qua hệ thống chuyển đổi" }, "ACCOUNT_SECURITY": { "TITLE_TEXT": "Điều chỉnh các cài đặt bảo mật cho tài khoản của quý khách. Có thể cài đặt Xác thực 2 yếu tố, mật khẩu, khóa API và các chức năng bảo mật khác.", @@ -648,7 +651,7 @@ "MESSAGE_4": "Quý khách có thể bảo quản mã này một cách an toàn để thiết lập lại 2FA trong trường hợp thay đổi hoặc bị mất điện thoại.", "MESSAGE_5": "Thủ công", "INPUT": "Vui lòng nhập mã (OTP) một lần", - "WARNING": "Chúng tôi khuyến khích quý khách cài đặt Xác thực 2 yếu tố (2FA). Điều này sẽ giúp nâng cao độ bảo mật cho tài khoản của quý khách.", + "WARNING": "Chúng tôi {0} {1}", "ENABLE": "Bật Xác thực 2 yếu tố", "DISABLE": "Tắt Xác thực 2 yếu Tối", "SECRET_1": "Enter yor secret key", @@ -657,12 +660,43 @@ "INPUT_1": "Secret Key", "TITLE_2": "Nhập mật khẩu một lần (OTP)", "MESSAGE_6": "Vui lòng nhập mật khẩu một lần bao gồm 6 ký tự xuống phía dưới.", - "INPUT_2": "Mật khẩu một lần (OTP)" + "INPUT_2": "Mật khẩu một lần (OTP)", + "STRONGLY_RECOMMEND": "khuyến khích mạnh mẽ", + "WARNING_CONTENT": "rằng bạn kích hoạt xác thực hai yếu tố để tăng cường tính bảo mật cho tài khoản và bảo vệ tiền của bạn" }, "FORM": { "PLACEHOLDER": "Vui lòng nhập OTP được cung cấp bởi Google Authenticator.", "BUTTON": "Bật 2FA" - } + }, + "DOWNLOAD_APP": "Tải xuống và cài đặt", + "GOOGLE_AUTHENTICATOR": "Trình xác thực của Google:", + "DOWNLOAD_FROM": "Tải xuống từ", + "GOOGLE_PLAY": "GooglePlay", + "APPLE": "Cửa hàng ứng dụng", + "APP_INFO": "Google Authenticator sẽ tạo mã ngẫu nhiên cần thiết cho các hành động nhạy cảm, chẳng hạn như đăng nhập và rút tiền.", + "DONT_UNINSTALL": "Vui lòng không gỡ cài đặt ứng dụng.", + "INFO_UNINSTALL": "Trong trường hợp vô tình làm mất điện thoại hoặc vô tình gỡ cài đặt, bạn có thể gặp phải sự chậm trễ trong việc lấy lại quyền truy cập vào tài khoản của mình. Trong những trường hợp như vậy, bạn sẽ cần liên hệ với nhóm hỗ trợ của chúng tôi để được hỗ trợ.", + "BACK": "MẶT SAU", + "PROCEED": "TIẾP TỤC", + "NEXT": "Kế tiếp", + "NOTE": "Lưu ý: {0} {1}", + "MANUEL_DESCRIPTION": "Vui lòng nhập hạt giống 2FA thủ công từ bước trước.", + "MANUEL_DESCRIPTION_2": "Vui lòng viết cẩn thận và lưu trữ xác minh thủ công ở trên", + "MANUEL_DESCRIPTION_3": "key vì nó có thể được sử dụng để khôi phục tài khoản.", + "MANUEL_KEY": "Hướng dẫn sử dụng hạt giống 2FA", + "MANUEL_PLACEHOLDER": "Nhập hạt giống 2FA của bạn", + "MANUEL_WARNING": "(Hãy chắc chắn rằng bạn đã viết nó ra và lưu trữ an toàn để sử dụng sau này.)", + "MANUEL_ERROR_1": "Vui lòng nhập hạt giống 2FA thủ công", + "MANUEL_ERROR_2": "Vui lòng nhập hạt giống 2FA thủ công từ bước trước", + "ACCOUNT_SECURED": "Tài khoản được bảo mật", + "2FA_ENABLED": "BẬT 2FA", + "2FA_DISABLED": "2FA bị TẮT", + "2FA_CONTENT_ONE": "Đang kích hoạt {0} {1} .", + "2FA_CONTENT_TWO": "(Xác thực hai yếu tố) bổ sung thêm một lớp bảo vệ để bảo vệ tiền của bạn. Sau khi được bật, bạn sẽ được yêu cầu nhập mã xác minh từ điện thoại của mình mỗi lần đăng nhập hoặc rút tài sản. Tính năng này tăng cường đáng kể tính bảo mật cho tài khoản của bạn", + "2FA_CONTENT_THREE": "(Xác thực hai yếu tố) bổ sung thêm một lớp bảo vệ để bảo vệ tiền của bạn", + "2FA_CONTENT_FOUR": "Để tắt 2FA, bạn cần nhập mã 2FA (OTP) của mình", + "INPUT_SIX_DIGIT_CODE": "Nhập mã gồm 6 chữ số từ ứng dụng Google Authenticator trên điện thoại của bạn:", + "INPUT_CONTENT": "(Để thêm tài khoản mới vào Google Authenticator, hãy nhấp vào nút '+'. Bạn có thể quét mã QR được cung cấp ở bước trước hoặc nhập khóa theo cách thủ công để thêm tài khoản của bạn vào ứng dụng.)" }, "CHANGE_PASSWORD": { "TITLE": "Thay đổi mật khẩu", @@ -1709,7 +1743,8 @@ "QUICK_TRADE": "Giao dịch nhanh", "SUBTITLE": "Để xem giá mua và bán của {0}, hãy truy cập {1}", "FOOTER": "Phí giao dịch của người thực hiện thị trường được áp dụng cho tất cả các giao dịch được thực hiện thông qua Giao dịch nhanh." - } + }, + "PRO_TRADE": "XEM THỊ TRƯỜNG {0}" }, "ACCORDIAN": { "ACCORDIAN_ASSETS": "Tất cả tài sản ví", @@ -1892,8 +1927,14 @@ "SELECT_END_DATE": "Chọn ngày kết thúc", "CUSTOM": "Phong tục", "BACK_CUSTOM": "MẶT SAU", - "PROOCED_CUSTOM": "ĐƯỢC TIẾN HÀNH" + "PROOCED_CUSTOM": "ĐƯỢC TIẾN HÀNH", + "PL_DAYS": "Lãi & Lỗ trong ngày", + "VIEW_MORE": "Xem thêm {0}", + "SHOW_ASSET_BREAKDOWN": "Hiển thị phân tích tài sản", + "PL_SUMMARY": "Tóm tắt P&L", + "BALANCE_HISTORY": "Lịch sử số dư" }, "ASSET_INFO": "Thông tin tài sản", - "TAKER_FEES_APPLIED": "Phí Taker được áp dụng" + "TAKER_FEES_APPLIED": "Phí Taker được áp dụng", + "CANCEL_WITHDRAWAL_ADDRESS": "Địa chỉ rút tiền" } \ No newline at end of file diff --git a/web/src/config/lang/zh.json b/web/src/config/lang/zh.json index 2279c9e8c2..3ee1db874e 100644 --- a/web/src/config/lang/zh.json +++ b/web/src/config/lang/zh.json @@ -218,7 +218,9 @@ "ERROR_INVALID": "无效的OTP码", "OTP_FORM_INFO": "输入您的 6 位数代码以继续", "OTP_FORM_SUBNOTE_LINE_1": "您的代码也称为双因素身份验证器 (2FA) 或 OTP 代码。", - "OTP_FORM_SUBNOTE_LINE_2": "如果您丢失了代码,请联系支持人员。" + "OTP_FORM_SUBNOTE_LINE_2": "如果您丢失了代码,请联系支持人员。", + "OTP_FORM_SUBNOTE_LINE_3": "(要向 Google Authenticator 添加新帐户,请点击“+”按钮。您可以扫描上一步中提供的二维码,也可以手动输入密钥将您的帐户添加到应用程序中。)", + "INPUT_TEXT": "输入手机 Google Authenticator 应用中收到的 6 位数代码:" }, "QUICK_TRADE_COMPONENT": { "TITLE": "快速交易", @@ -594,7 +596,8 @@ "INTERNAL": "内部的", "BLOCKCHAIN": "区块链", "STAKE": "赌注", - "REFERRAL": "转介" + "REFERRAL": "转介", + "QUICK_TRADE_TOOLTIP": "订单通过转换系统执行" }, "ACCOUNT_SECURITY": { "TITLE_TEXT": "调整账户的安全设置,谷歌验证、密码、API密钥等安全相关功能。", @@ -612,7 +615,7 @@ "MESSAGE_4": "你可以安全地保存该QR码,以便以后更换或丢失手机时恢复2FA。", "MESSAGE_5": "指南", "INPUT": "请在下方输入谷歌验证码", - "WARNING": "强烈建议你设置谷歌验证(2FA),这将大幅提高资金的安全性。 ", + "WARNING": "我们{0} {1}", "ENABLE": "启用谷歌验证(2FA)", "DISABLE": "禁用谷歌验证(2FA)", "SECRET_1": "Enter yor secret key", @@ -621,9 +624,40 @@ "INPUT_1": "Secret Key", "TITLE_2": "Enter One-Time Password (OTP)", "MESSAGE_6": "Please enter your 6-digit one-time password below.", - "INPUT_2": "One-Time Password (OTP)" + "INPUT_2": "One-Time Password (OTP)", + "STRONGLY_RECOMMEND": "强烈推荐", + "WARNING_CONTENT": "启用双重身份验证以增强帐户安全性并保护您的资金" }, - "FORM": { "PLACEHOLDER": "请输入6位谷歌验证码", "BUTTON": "启用2FA" } + "FORM": { "PLACEHOLDER": "请输入6位谷歌验证码", "BUTTON": "启用2FA" }, + "DOWNLOAD_APP": "下载并安装", + "GOOGLE_AUTHENTICATOR": "Google 身份验证器:", + "DOWNLOAD_FROM": "下载自", + "GOOGLE_PLAY": "谷歌播放", + "APPLE": "应用商店", + "APP_INFO": "Google Authenticator 将生成敏感操作(例如登录和提取资金)所必需的随机代码。", + "DONT_UNINSTALL": "请不要卸载该应用程序。", + "INFO_UNINSTALL": "如果手机意外丢失或无意卸载,您可能会在重新访问帐户时遇到延迟。在这种情况下,您需要联系我们的支持团队寻求帮助。", + "BACK": "后退", + "PROCEED": "继续", + "NEXT": "下一个", + "NOTE": "注意:{0} {1}", + "MANUEL_DESCRIPTION": "请输入上一步中的手动 2FA 种子。", + "MANUEL_DESCRIPTION_2": "请认真记录并保存上述手动验证", + "MANUEL_DESCRIPTION_3": "密钥,因为它可以用于帐户恢复。", + "MANUEL_KEY": "手动 2FA 种子", + "MANUEL_PLACEHOLDER": "输入您的 2FA 种子", + "MANUEL_WARNING": "(请确保您已将其写下来并安全存储以供将来使用。)", + "MANUEL_ERROR_1": "请输入手动 2FA 种子", + "MANUEL_ERROR_2": "请输入上一步中的手动 2FA 种子", + "ACCOUNT_SECURED": "账户安全", + "2FA_ENABLED": "已启用 2FA", + "2FA_DISABLED": "2FA 已禁用", + "2FA_CONTENT_ONE": "正在激活 {0} {1} 。", + "2FA_CONTENT_TWO": "(双重身份验证)增加了一层额外的保护来保护您的资金。启用后,每次登录或提取资产时,您都需要输入手机验证码。此功能大大增强了您帐户的安全性", + "2FA_CONTENT_THREE": "(双重身份验证)增加了一层额外的保护来保护你的资金", + "2FA_CONTENT_FOUR": "要禁用 2FA,您需要输入 2FA (OTP) 代码", + "INPUT_SIX_DIGIT_CODE": "输入手机 Google Authenticator 应用中收到的 6 位数代码:", + "INPUT_CONTENT": "(要向 Google Authenticator 添加新帐户,请点击“+”按钮。您可以扫描上一步中提供的二维码,也可以手动输入密钥将您的帐户添加到应用程序中。)" }, "CHANGE_PASSWORD": { "TITLE": "更改密码", @@ -1644,7 +1678,12 @@ "SELECT_END_DATE": "选择结束日期", "CUSTOM": "风俗", "BACK_CUSTOM": "后退", - "PROOCED_CUSTOM": "已处理" + "PROOCED_CUSTOM": "已处理", + "PL_DAYS": "当日盈亏", + "VIEW_MORE": "查看更多 {0}", + "SHOW_ASSET_BREAKDOWN": "显示资产明细", + "PL_SUMMARY": "损益摘要", + "BALANCE_HISTORY": "余额历史记录" }, "ASSET_INFO": "资产信息", "TAKER_FEES_APPLIED": "收取接受者费用", @@ -1687,7 +1726,8 @@ }, "LIST": "想列出您的数字资产吗?", "WEBSITE": "网站", - "EXPLORER": "探险家" + "EXPLORER": "探险家", + "PRO_TRADE": "查看{0}市场" }, "ACCORDIAN": { "ACCORDIAN_ASSETS": "所有钱包资产", "ACCORDIAN_INFO": "资产信息页面", "ACCORDIAN_HISTORY": "历史" }, "ASYNC_LINK": { "TITLE": "页面没有自动打开?", "TEXT": "您的浏览器可能阻止该页面。尝试复制并粘贴下面的链接:" }, @@ -1777,5 +1817,6 @@ "NOTIFICATION": "会话已撤销/注销", "MODAL": { "TITLE": "撤销会话", "PROMPT": "您确定要撤销并注销此会话吗?", "BACK": "后退", "CONFIRM": "确认" } } - } + }, + "CANCEL_WITHDRAWAL_ADDRESS": "提现地址" } \ No newline at end of file diff --git a/web/src/config/menu.js b/web/src/config/menu.js index 9d951040bd..d7e3c2aa7a 100644 --- a/web/src/config/menu.js +++ b/web/src/config/menu.js @@ -1,3 +1,5 @@ +import { isMobile } from 'react-device-detect'; + export const MENU_ITEMS = { top: [ { @@ -16,48 +18,34 @@ export const MENU_ITEMS = { '/verification', '/settings', ], - icon_id: 'ACCOUNT_LINE', + icon_id: `${isMobile ? 'FOOTER_ACCOUNT_LINE' : 'ACCOUNT_LINE'}`, string_id: 'ACCOUNT_TEXT', hide_from_appbar: true, hide_from_sidebar: true, hide_from_menulist: true, hide_from_bottom_nav: false, - }, - { - id: 'assets', - path: '/assets', - icon_id: 'FOOTERBAR_ASSETS_TRADE', - string_id: 'ASSETS', - hide_from_sidebar: true, - hide_from_appbar: false, - hide_from_menulist: true, - hide_from_bottom_nav: false, - }, + } ], features: [ - { - id: 'trade_tab', - path: '/trade', - icon_id: 'SIDEBAR_TRADING_ACTIVE', - string_id: 'ACCOUNTS.TAB_TRADE', - hide_from_sidebar: true, - hide_from_bottom_nav: false, - }, { id: 'pro_trade', path: '/markets', - icon_id: 'SIDEBAR_TRADING_ACTIVE', - string_id: 'PRO_TRADE', + icon_id: `${ + isMobile ? 'FOOTER_TRADING_ACTIVE' : 'SIDEBAR_TRADING_ACTIVE' + }`, + string_id: 'SUMMARY.MARKETS', hide_from_sidebar: true, hide_from_bottom_nav: false, + hide_from_menulist: true }, { id: 'quick_trade', path: 'quick-trade', - icon_id: 'QUICK_TRADE_TAB_ACTIVE', - string_id: 'QUICK_TRADE', + icon_id: 'FOOTER_QUICK_ACTIVE', + string_id: 'CONVERT', hide_from_sidebar: true, hide_from_bottom_nav: false, + hide_from_menulist: true }, { id: 'stake_page', @@ -88,15 +76,16 @@ export const MENU_ITEMS = { middle: [ { path: '/wallet', - icon_id: 'TAB_WALLET', + icon_id: `${isMobile ? 'WALLET_FOOTER' : 'TAB_WALLET'}`, string_id: 'ACCOUNTS.TAB_WALLET', + hide_from_appbar: false }, { path: '/transactions', icon_id: 'TAB_HISTORY', string_id: 'ACCOUNTS.TAB_HISTORY', hide_from_appbar: true, - hide_from_bottom_nav: false, + hide_from_bottom_nav: true, }, { path: '/security', @@ -112,6 +101,18 @@ export const MENU_ITEMS = { hide_from_appbar: true, hide_from_bottom_nav: true, }, + { + id: 'prices', + path: '/prices', + icon_id: 'FOOTERBAR_ASSETS_TRADE', + string_id: 'HOLLAEX_TOKEN.PRICES.TITLE', + hide_from_sidebar: true, + hide_from_appbar: false, + hide_from_menulist: true, + hide_from_bottom_nav: false, + }, + ], + bottom: [ { path: '/settings', icon_id: 'TAB_SETTING', @@ -119,8 +120,6 @@ export const MENU_ITEMS = { hide_from_appbar: true, hide_from_bottom_nav: true, }, - ], - bottom: [ { path: 'help', icon_id: 'SIDEBAR_HELP', @@ -136,4 +135,4 @@ export const MENU_ITEMS = { hide_from_bottom_nav: true, }, ], -}; +}; \ No newline at end of file diff --git a/web/src/containers/Admin/Fiat/FiatFees.js b/web/src/containers/Admin/Fiat/FiatFees.js index b1ef2170c6..30b28ccef4 100644 --- a/web/src/containers/Admin/Fiat/FiatFees.js +++ b/web/src/containers/Admin/Fiat/FiatFees.js @@ -61,6 +61,30 @@ const FiatFees = ({ coins }) => { return
{data?.deposit_fee || '-'}
; }, }, + { + title: 'Minimum amount', + dataIndex: 'min', + key: 'min', + render: (user_id, data) => { + return
{data?.min || '-'}
; + }, + }, + { + title: 'Maximum amount', + dataIndex: 'max', + key: 'max', + render: (user_id, data) => { + return
{data?.max || '-'}
; + }, + }, + { + title: 'Increment Unit', + dataIndex: 'increment_unit', + key: 'increment_unit', + render: (user_id, data) => { + return
{data?.increment_unit || '-'}
; + }, + }, { title: 'Edit', dataIndex: 'edit', @@ -115,6 +139,10 @@ const FiatFees = ({ coins }) => { fee_markup: null, }), fullname: coin.fullname, + min: data?.[coin.symbol]?.min || coin.min, + max: data?.[coin.symbol]?.max || coin.max, + increment_unit: + data?.[coin.symbol]?.increment_unit || coin.increment_unit, }; } } @@ -146,8 +174,9 @@ const FiatFees = ({ coins }) => { return (
- Below, You can add/edit fees for fiats available in your exchange, this - will override the default fees set to fiats by default + Below, You can add/edit fees and other attributes for fiats available in + your exchange, this will override the default fees set to fiats by + default
@@ -259,6 +288,48 @@ const FiatFees = ({ coins }) => { }} />
+
+
Minimum Allowable Amount
+ { + setSelectedCoin({ + ...selectedCoin, + min: e.target.value, + }); + }} + /> +
+
+
Maximum Allowable Amount
+ { + setSelectedCoin({ + ...selectedCoin, + max: e.target.value, + }); + }} + /> +
+
+
Increment Unit
+ { + setSelectedCoin({ + ...selectedCoin, + increment_unit: e.target.value, + }); + }} + /> +
{ ); } + if (selectedCoin.min) { + selectedCoin.min = Number(selectedCoin.min); + } + + if (selectedCoin.max) { + selectedCoin.max = Number(selectedCoin.max); + } + + if (selectedCoin.increment_unit) { + selectedCoin.increment_unit = Number( + selectedCoin.increment_unit + ); + } + await updateConstants({ kit: { fiat_fees: { @@ -306,6 +391,9 @@ const FiatFees = ({ coins }) => { symbol: selectedCoin.symbol, withdrawal_fee: selectedCoin.withdrawal_fee, deposit_fee: selectedCoin.deposit_fee, + min: selectedCoin.min, + max: selectedCoin.max, + increment_unit: selectedCoin.increment_unit, }, }, }, diff --git a/web/src/containers/Admin/Fiat/index.js b/web/src/containers/Admin/Fiat/index.js index 464ec424be..5bc7d76bbd 100644 --- a/web/src/containers/Admin/Fiat/index.js +++ b/web/src/containers/Admin/Fiat/index.js @@ -129,7 +129,7 @@ const Fiatmarkets = ({ setIsLoading={setIsLoading} /> - + {/* diff --git a/web/src/containers/Admin/General/General.js b/web/src/containers/Admin/General/General.js index 3f47fe1f1b..67623bd8ba 100644 --- a/web/src/containers/Admin/General/General.js +++ b/web/src/containers/Admin/General/General.js @@ -45,7 +45,6 @@ const ThemeForm = AdminHocForm('ThemeForm'); const NativeCurrencyForm = AdminHocForm('NativeCurrencyForm'); const HelpDeskForm = AdminHocForm('HelpDeskForm'); const APIDocLinkForm = AdminHocForm('APIDocLinkForm'); -const CaptchaForm = AdminHocForm('CaptchaForm'); const CountryForm = AdminHocForm('CountryForm'); class GeneralContent extends Component { @@ -62,7 +61,6 @@ class GeneralContent extends Component { initialEmailValues: {}, initialLinkValues: {}, initialEmailVerificationValues: {}, - initialCaptchaValues: {}, pendingPublishIcons: {}, showDisableSignUpsConfirmation: false, isSignUpActive: true, @@ -179,8 +177,7 @@ class GeneralContent extends Component { let initialEmailVerificationValues = { ...this.state.initialEmailVerificationValues, }; - let initialCaptchaValues = { ...this.state.initialCaptchaValues }; - const { kit = {}, secrets = { smtp: {}, captcha: {}, emails: {} } } = + const { kit = {}, secrets = { smtp: {}, emails: {} } } = this.state.constants || {}; const { api_name, @@ -188,7 +185,6 @@ class GeneralContent extends Component { links = {}, new_user_is_activated: isSignUpActive, email_verification_required, - captcha = {}, } = kit; initialNameValues = { ...initialNameValues, api_name }; initialLanguageValues = { @@ -207,12 +203,6 @@ class GeneralContent extends Component { email_verification_required, }; - initialCaptchaValues = { - ...initialCaptchaValues, - ...captcha, - ...secrets.captcha, - }; - const { configuration = {} } = this.state.initialEmailValues || {}; const initialEmailValues = { configuration: { ...configuration, ...secrets.emails, ...secrets.smtp }, @@ -229,7 +219,6 @@ class GeneralContent extends Component { initialLinkValues, isSignUpActive, initialEmailVerificationValues, - initialCaptchaValues, showDisableSignUpsConfirmation: false, }); }; @@ -476,27 +465,6 @@ class GeneralContent extends Component { }); }; - handleSubmitCaptcha = ({ site_key, secret_key }) => { - const formValues = { - kit: { - captcha: { - site_key, - }, - }, - ...(!secret_key.includes('*') - ? { - secrets: { - captcha: { - secret_key, - }, - }, - } - : {}), - }; - - this.handleSubmitGeneral(formValues); - }; - handleSubmitSignUps = (new_user_is_activated) => { if (this.state.isSignUpActive !== new_user_is_activated) { this.setState({ isDisableSave: true }); @@ -771,7 +739,6 @@ class GeneralContent extends Component { initialCountryValues, initialLinkValues, initialEmailVerificationValues, - initialCaptchaValues, loading, isSignUpActive, showDisableSignUpsConfirmation, @@ -1183,29 +1150,6 @@ class GeneralContent extends Component {
-
-
reCAPTCHA
-
- Make spammers go away with{' '} - - Google reCAPTCHA v3 - - . Leave the field blank to remove recaptcha. -
- -
-
Operator roles
diff --git a/web/src/containers/Admin/QuickTradesList/index.js b/web/src/containers/Admin/QuickTradesList/index.js index 39b2fe3dfd..46315d4540 100644 --- a/web/src/containers/Admin/QuickTradesList/index.js +++ b/web/src/containers/Admin/QuickTradesList/index.js @@ -80,6 +80,17 @@ const QuickTradesList = ({ pairs, coins, userId, getThisExchangeOrder }) => { }; const handleSearch = _debounce(searchUser, 1000); + + const onHandleDisable = () => { + if ( + (orderPayload && orderPayload.maker_fee) || + (orderPayload && orderPayload.taker_fee) + ) { + return false; + } + return true; + }; + return (
{displayCreateOrder && ( @@ -392,6 +403,7 @@ const QuickTradesList = ({ pairs, coins, userId, getThisExchangeOrder }) => { height: 35, }} type="default" + disabled={onHandleDisable()} > Create Trade diff --git a/web/src/containers/Admin/Settings/Utils.js b/web/src/containers/Admin/Settings/Utils.js index 1a4a7f97a3..fc5a0ee28f 100644 --- a/web/src/containers/Admin/Settings/Utils.js +++ b/web/src/containers/Admin/Settings/Utils.js @@ -161,17 +161,7 @@ export const generateAdminSettings = (key) => { label: 'Allowed domains', placeholder: 'Allowed domains', tokenSeparators: [',', ' ', ' '], - }, - site_key: { - type: 'input', - label: 'Captcha site key (Google ReCaptcha V3)', - placeholder: 'Captcha site key (Google ReCaptcha V3)', - }, - secret_key: { - type: 'input', - label: 'Captcha secret key (Google ReCaptcha V3)', - placeholder: 'Captcha secret key (Google ReCaptcha V3)', - }, + } }; } else if (key === 'email') { return { diff --git a/web/src/containers/Admin/Settings/index.js b/web/src/containers/Admin/Settings/index.js index 9fae33556b..a14b526143 100644 --- a/web/src/containers/Admin/Settings/index.js +++ b/web/src/containers/Admin/Settings/index.js @@ -83,9 +83,8 @@ class Settings extends Component { description, defaults = {}, emails = {}, - secrets = { smtp: {}, captcha: {} }, + secrets = { smtp: {} }, accounts = {}, - captcha = {}, links = {}, color = {}, } = this.state.constants || {}; @@ -116,10 +115,7 @@ class Settings extends Component { description, }; - let initialSecurityValues = { - ...captcha, - ...secrets.captcha, - }; + let initialSecurityValues = {}; if (secrets.allowed_domains) { initialSecurityValues.allowed_domains = typeof secrets.allowed_domains === 'string' @@ -227,21 +223,6 @@ class Settings extends Component { formValues = {}; Object.keys(formProps).forEach((val) => { if ( - val === 'site_key' && - initialSecurityValues[val] !== formProps[val] - ) { - if (!formValues.captcha) formValues.captcha = {}; - formValues.captcha[val] = formProps[val]; - } else if (val === 'secret_key') { - if ( - initialSecurityValues[val] !== formProps[val] && - !formProps[val].includes('*') - ) { - if (!formValues.secrets || !formValues.secrets.captcha) - formValues.secrets = { captcha: {} }; - formValues.secrets.captcha[val] = formProps[val]; - } - } else if ( (val === 'allowed_domains' || val === 'admin_whitelist') && initialSecurityValues[val] !== formProps[val] ) { diff --git a/web/src/containers/Admin/SetupWizard/Review.js b/web/src/containers/Admin/SetupWizard/Review.js index 56ad304973..328bfdacbe 100644 --- a/web/src/containers/Admin/SetupWizard/Review.js +++ b/web/src/containers/Admin/SetupWizard/Review.js @@ -132,24 +132,6 @@ const ExchangeReview = ({ : ''}
- {/*
-
Site key (Google reCAPTHA V3):
-
- {kit.captcha && kit.captcha.site_key !== 'null' - ? kit.captcha.site_key - : ''} -
-
-
-
- Secret key (Google reCAPTHA V3):{' '} -
-
- {secrets.captcha && secrets.captcha.secret_key !== 'null' - ? secrets.captcha.secret_key - : ''} -
-
*/}
diff --git a/web/src/containers/App/selector.js b/web/src/containers/App/selector.js index 04ccb3d7fc..83968c1960 100644 --- a/web/src/containers/App/selector.js +++ b/web/src/containers/App/selector.js @@ -2,17 +2,34 @@ import { isMobile } from 'react-device-detect'; import { createSelector } from 'reselect'; import { MENU_ITEMS } from 'config/menu'; import { STAKING_INDEX_COIN, isStakingAvailable } from 'config/contracts'; +import { MarketsSelector } from 'containers/Trade/utils'; const getConstants = (state) => state.app.constants; const getRemoteRoutes = (state) => state.app.remoteRoutes; const getContracts = (state) => state.app.contracts; const getToken = (state) => state.auth.token; +const getFavourites = (state) => state.app.favourites; +const getMarkets = (state) => MarketsSelector(state); export const menuItemsSelector = createSelector( - [getConstants, getRemoteRoutes, getContracts, getToken], - (constants = {}, remoteRoutes = [], contracts = {}, token) => { + [ + getConstants, + getRemoteRoutes, + getContracts, + getToken, + getFavourites, + getMarkets, + ], + ( + constants = {}, + remoteRoutes = [], + contracts = {}, + token, + getFavourites = {}, + getMarkets = {} + ) => { const { features = {} } = constants; - let featureItems = MENU_ITEMS.features + const featureItems = MENU_ITEMS.features .filter( ({ id }) => id !== 'stake_page' || @@ -44,44 +61,17 @@ export const menuItemsSelector = createSelector( item.hide_from_menulist = false; item.hide_from_sidebar = false; } - - if ( - id === 'trade_tab' && - features.quick_trade && - features.pro_trade - ) { - item.hide_from_bottom_nav = false; + if (id === 'pro_trade') { + if (getFavourites && getFavourites.length) { + item.path = `/trade/${getFavourites[0]}`; + } else { + item.path = `/trade/${getMarkets[0]?.key}`; + } } return item; } ); - if (features.quick_trade && features.pro_trade && isMobile) { - featureItems.push({ - id: 'pro_quick_trades', - path: 'trades', - icon_id: 'PRO_QUICK_TRADES_ICON', - string_id: 'PRO_QUICK_TRADES', - hide_from_appbar: true, - hide_from_sidebar: true, - hide_from_menulist: true, - hide_from_bottom_nav: false, - }); - const updatedItems = featureItems.filter( - ({ string_id }) => - string_id !== 'QUICK_TRADE' && string_id !== 'PRO_TRADE' - ); - featureItems = updatedItems; - } - - if (!features.quick_trade && !features.pro_trade && isMobile) { - const updatedItems = featureItems.filter( - ({ string_id }) => - string_id !== 'QUICK_TRADE' && string_id !== 'PRO_TRADE' - ); - featureItems = updatedItems; - } - const menuItems = isMobile ? remoteRoutes && remoteRoutes.length ? [ @@ -99,8 +89,8 @@ export const menuItemsSelector = createSelector( ] : [ ...MENU_ITEMS.top, - ...featureItems, ...MENU_ITEMS.middle, + ...featureItems, ...remoteRoutes, ...(token ? MENU_ITEMS.bottom : []), ]; diff --git a/web/src/containers/AuthContainer/index.js b/web/src/containers/AuthContainer/index.js index 3715bf6d7a..4d43a00055 100644 --- a/web/src/containers/AuthContainer/index.js +++ b/web/src/containers/AuthContainer/index.js @@ -86,7 +86,7 @@ class AuthContainer extends Component { const { activeLanguage, children, - constants = { captcha: {} }, + constants = {}, icons: ICONS = {}, activeNotification, } = this.props; diff --git a/web/src/containers/CoinPage/_CoinPage.scss b/web/src/containers/CoinPage/_CoinPage.scss index b6a88a1cdb..962d5fd9d9 100644 --- a/web/src/containers/CoinPage/_CoinPage.scss +++ b/web/src/containers/CoinPage/_CoinPage.scss @@ -213,7 +213,7 @@ .trade_tabs-container { margin: 0 !important; - width: 100% !important; + width: auto !important; } } @@ -230,7 +230,7 @@ .fullname { color: $colors-deactivate; - font-size: 0.65rem; + font-size: 1rem; white-space: nowrap; } diff --git a/web/src/containers/CoinPage/index.js b/web/src/containers/CoinPage/index.js index ed763bc217..6b5caa65b0 100644 --- a/web/src/containers/CoinPage/index.js +++ b/web/src/containers/CoinPage/index.js @@ -33,28 +33,28 @@ const CoinPage = ({ addToFavourites, removeFromFavourites, quicktradePairs, - markets + markets, }) => { const { params: { token: currentCoin }, } = router; const currentCoinUpper = currentCoin?.toUpperCase(); - - const currentQuicktradePair = - Object.keys(quicktradePairs).find((pair) => - pair.split('-').includes(currentCoin) - ); - const market = markets.find( - ({ symbol }) => currentCoin === symbol + const currentQuicktradePair = Object.keys(quicktradePairs).find((pair) => + pair.split('-').includes(currentCoin) ); - + + const market = markets.find(({ symbol }) => currentCoin === symbol); + const isBroker = currentQuicktradePair && - [TYPES.NETWORK, TYPES.BROKER].includes(quicktradePairs[currentQuicktradePair].type); + [TYPES.NETWORK, TYPES.BROKER].includes( + quicktradePairs[currentQuicktradePair].type + ); - const isNetwork = quicktradePairs[currentQuicktradePair]?.type === TYPES.NETWORK; + const isNetwork = + quicktradePairs[currentQuicktradePair]?.type === TYPES.NETWORK; const [data, setData] = useState([]); const [chartData, setChartData] = useState({}); @@ -62,13 +62,13 @@ const CoinPage = ({ useEffect(() => { handleMarket(); - const assetValues = Object.keys(coins).map(( - val) => coins[val].code).toLocaleString(); + const assetValues = Object.keys(coins) + .map((val) => coins[val].code) + .toLocaleString(); - getMiniCharts(assetValues) - .then((chartValues) =>{ - setChartData(chartValues); - }); + getMiniCharts(assetValues).then((chartValues) => { + setChartData(chartValues); + }); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -78,8 +78,7 @@ const CoinPage = ({ }, [data, chartData]); const handleOptions = () => { - const selectedPair = currentCoin+'-usdt'; - + const selectedPair = currentCoin + '-usdt'; const ChartData = { ...chartData[selectedPair], @@ -150,9 +149,7 @@ const CoinPage = ({
-
- -
+
{STRINGS['HOLLAEX_TOKEN.BALANCE']} {' '} - {formatCurrency(available_balance[`${currentCoin}_available`])}{' '} + {formatCurrency( + available_balance[`${currentCoin}_available`] + )}{' '} {currentCoinUpper}{' '} @@ -228,7 +227,9 @@ const CoinPage = ({
-
{ {!loading ? (
browserHistory.push(`/assets/coin/${symbol}`)} + onClick={() => browserHistory.push(`/prices/coin/${symbol}`)} >
{fullname}
diff --git a/web/src/containers/DigitalAssets/components/AssetsWrapper.js b/web/src/containers/DigitalAssets/components/AssetsWrapper.js index 661c26f6b0..60e911c593 100644 --- a/web/src/containers/DigitalAssets/components/AssetsWrapper.js +++ b/web/src/containers/DigitalAssets/components/AssetsWrapper.js @@ -51,7 +51,7 @@ class AssetsWrapper extends Component { const lastPrice = price[price.length - 1]; const priceDifference = lastPrice - firstPrice; const priceDifferencePercent = formatPercentage( - priceDifference / firstPrice * 100 + (priceDifference / firstPrice) * 100 ); const formattedNumber = (val) => formatToCurrency(val, 0, val < 1 && countDecimals(val) > 8); @@ -239,6 +239,7 @@ class AssetsWrapper extends Component { placeHolder={`${STRINGS['SEARCH_ASSETS']}...`} handleSearch={this.handleTabSearch} showCross + isFocus={true} />
diff --git a/web/src/containers/FeesAndLimits/DepositAndWithdrawalFees.js b/web/src/containers/FeesAndLimits/DepositAndWithdrawalFees.js index 9e65229942..c5296143bd 100644 --- a/web/src/containers/FeesAndLimits/DepositAndWithdrawalFees.js +++ b/web/src/containers/FeesAndLimits/DepositAndWithdrawalFees.js @@ -3,6 +3,7 @@ import { Coin, EditWrapper } from 'components'; import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; import { getNetworkNameByKey } from 'utils/wallet'; +import BigNumber from 'bignumber.js'; const renderRow = ( icon_id, @@ -28,16 +29,37 @@ const renderRow = ( ); }; -const getFeeText = (data, level) => { +const getFeeText = (data, level, type, coin_customizations, coins) => { const { symbol, value } = data; - const fee = value; + let fee = value; + + if (type === 'withdrawal') { + const feeMarkup = coin_customizations?.[symbol]?.fee_markup; + if (feeMarkup) { + const incrementUnit = coins?.[symbol]?.increment_unit; + const decimalPoint = new BigNumber(incrementUnit).dp(); + const roundedMarkup = new BigNumber(feeMarkup) + .decimalPlaces(decimalPoint) + .toNumber(); + + fee = new BigNumber(fee || 0).plus(roundedMarkup || 0).toNumber(); + } + } const text = `${fee} ${symbol?.toUpperCase()}`; return text; }; -const getRows = (level, coins, icons, search, strings) => { +const getRows = ( + level, + coins, + icons, + search, + strings, + coin_customizations, + fiat_fees +) => { const rowData = []; Object.entries(coins) .filter(([key]) => !search || (search && key.includes(search))) @@ -51,6 +73,7 @@ const getRows = (level, coins, icons, search, strings) => { symbol, network: networks, allow_withdrawal, + type, } = coin; if (withdrawal_fees) { Object.keys(withdrawal_fees).forEach((network, n_index) => { @@ -59,11 +82,23 @@ const getRows = (level, coins, icons, search, strings) => { withdrawal_fees_data['symbol'] = symbol; } const withdrawal_text = allow_withdrawal - ? getFeeText(withdrawal_fees_data, level) + ? getFeeText( + withdrawal_fees_data, + level, + 'withdrawal', + coin_customizations, + coins + ) : strings['FEES_AND_LIMITS.TABS.WITHDRAWAL_FEES.TABLE.NOT_ALLOWED']; const deposit_text = deposit_fees && deposit_fees[network] - ? getFeeText(deposit_fees[network], level) + ? getFeeText( + deposit_fees[network], + level, + 'deposit', + coin_customizations, + coins + ) : 'N/A'; const index = `${c_index}_${n_index}`; const display_text = @@ -83,8 +118,15 @@ const getRows = (level, coins, icons, search, strings) => { ); }); } else { + let customFee; + if (type === 'fiat') { + customFee = fiat_fees?.[symbol]?.withdrawal_fee; + } + const withdrawal_text = allow_withdrawal - ? `${withdrawal_fee} ${display_name}` + ? `${ + type === 'fiat' ? customFee || withdrawal_fee : withdrawal_fee + } ${display_name}` : strings['FEES_AND_LIMITS.TABS.WITHDRAWAL_FEES.TABLE.NOT_ALLOWED']; rowData.push( renderRow( @@ -101,7 +143,14 @@ const getRows = (level, coins, icons, search, strings) => { return rowData; }; -const DepositAndWithdrawalFees = ({ coins, level, icons, search }) => { +const DepositAndWithdrawalFees = ({ + coins, + level, + icons, + search, + coin_customizations, + fiat_fees, +}) => { return (
@@ -138,7 +187,17 @@ const DepositAndWithdrawalFees = ({ coins, level, icons, search }) => { - {getRows(level, coins, icons, search, STRINGS)} + + {getRows( + level, + coins, + icons, + search, + STRINGS, + coin_customizations, + fiat_fees + )} +
); diff --git a/web/src/containers/FeesAndLimits/LimitsBlock.js b/web/src/containers/FeesAndLimits/LimitsBlock.js index a22c0c19e9..361be47e5d 100644 --- a/web/src/containers/FeesAndLimits/LimitsBlock.js +++ b/web/src/containers/FeesAndLimits/LimitsBlock.js @@ -70,10 +70,21 @@ const getAccumulatedCoinList = (limits, coins, tier) => { return accumulatedCoins; }; -const getRows = (coins, level, tiers, ICONS, transaction_limits, type) => { - const individualLimits = transaction_limits.filter( - (limit) => - limit.limit_currency !== 'default' && limit.tier === Number(level) +const getRows = ( + coins, + level, + tiers, + ICONS, + transaction_limits, + type, + search +) => { + const individualLimits = transaction_limits.filter((limit) => + search + ? limit.limit_currency !== 'default' && + limit.tier === Number(level) && + limit.currency.toString().includes(search && search) + : limit.limit_currency !== 'default' && limit.tier === Number(level) ); return ( @@ -88,7 +99,7 @@ const getRows = (coins, level, tiers, ICONS, transaction_limits, type) => {
- +
{ return type === 'individual' ? (
@@ -288,7 +300,15 @@ const LimitsBlock = ({ - {getRows(coins, level, tiers, icons, transaction_limits, type)} + {getRows( + coins, + level, + tiers, + icons, + transaction_limits, + type, + search + )}
diff --git a/web/src/containers/FeesAndLimits/TradingFees.js b/web/src/containers/FeesAndLimits/TradingFees.js index 119b38b6b8..1e09851c15 100644 --- a/web/src/containers/FeesAndLimits/TradingFees.js +++ b/web/src/containers/FeesAndLimits/TradingFees.js @@ -97,6 +97,7 @@ const TradingFees = ({ } textType="title bold text-align-left" iconPath={ICONS['FEES_AND_LIMITS_TRADING_FEES']} + className="fees-limits-title" />
diff --git a/web/src/containers/FeesAndLimits/WithdrawalFees.js b/web/src/containers/FeesAndLimits/WithdrawalFees.js index cf4a84e364..b6ea4b7fad 100644 --- a/web/src/containers/FeesAndLimits/WithdrawalFees.js +++ b/web/src/containers/FeesAndLimits/WithdrawalFees.js @@ -19,6 +19,8 @@ const WithdrawalFees = ({ icons: ICONS, search, setSearch, + coin_customizations, + fiat_fees, }) => { const accountData = config_level[selectedLevel] || {}; const description = @@ -99,6 +101,7 @@ const WithdrawalFees = ({ } textType="title bold text-align-left" iconPath={ICONS['FEES_AND_LIMITS_WITHDRAWAL_FEES']} + className="fees-limits-title" />
@@ -131,6 +134,8 @@ const WithdrawalFees = ({ coins={coins} level={selectedLevel} search={search} + coin_customizations={coin_customizations} + fiat_fees={fiat_fees} />
@@ -145,6 +150,8 @@ const mapStateToProps = (state) => { coins: state.app.coins, pairs: state.app.pairs, config_level: state.app.config_level, + coin_customizations: state.app.constants.coin_customizations, + fiat_fees: state.app.constants.fiat_fees, options: Object.entries(state.app.config_level).map(([key, { name }]) => ({ value: key, label: name, diff --git a/web/src/containers/FeesAndLimits/WithdrawalLimits.js b/web/src/containers/FeesAndLimits/WithdrawalLimits.js index de5e75e9ca..9adaaab55f 100644 --- a/web/src/containers/FeesAndLimits/WithdrawalLimits.js +++ b/web/src/containers/FeesAndLimits/WithdrawalLimits.js @@ -1,6 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; -import { Select } from 'antd'; +import { Select, Input } from 'antd'; +import { SearchOutlined } from '@ant-design/icons'; import classnames from 'classnames'; import { isMobile } from 'react-device-detect'; import { Image, IconTitle, EditWrapper } from 'components'; @@ -17,6 +18,8 @@ const WithdrawalLimits = ({ setSelectedLevel, options, icons: ICONS, + search, + setSearch, }) => { const accountData = config_level[selectedLevel] || {}; const description = @@ -87,16 +90,35 @@ const WithdrawalLimits = ({ } textType="title bold text-align-left" iconPath={ICONS['FEES_AND_LIMITS_WITHDRAWAL_LIMITS']} + className="fees-limits-title" /> -
+
+
+
+ + { + STRINGS[ + 'FEES_AND_LIMITS.TABS.WITHDRAWAL_LIMITS.TABLE_2.SUBTITLE' + ] + } + +
+
- - { - STRINGS[ - 'FEES_AND_LIMITS.TABS.WITHDRAWAL_LIMITS.TABLE_2.SUBTITLE' - ] + } + placeholder={ + STRINGS['FEES_AND_LIMITS.SEARCH_PLACEHOLDER'] } - + value={search} + onChange={({ target: { value } }) => setSearch(value)} + style={{ + width: 200, + }} + bordered={false} + className="kit-divider" + />
@@ -107,6 +129,7 @@ const WithdrawalLimits = ({ tiers={config_level} transaction_limits={transaction_limits} type={'individual'} + search={search} />
@@ -120,6 +143,7 @@ const WithdrawalLimits = ({ } textType="title bold text-align-left" iconPath={ICONS['FEES_AND_LIMITS_WITHDRAWAL_LIMITS']} + className="fees-limits-title" />
diff --git a/web/src/containers/FeesAndLimits/index.js b/web/src/containers/FeesAndLimits/index.js index 497b778d90..16d5b003fe 100644 --- a/web/src/containers/FeesAndLimits/index.js +++ b/web/src/containers/FeesAndLimits/index.js @@ -19,7 +19,12 @@ import WithdrawalFees from './WithdrawalFees'; import WithdrawalLimits from './WithdrawalLimits'; import { isLoggedIn } from 'utils/token'; -const Index = ({ config_level, verification_level, router }) => { +const Index = ({ + config_level, + verification_level, + router, + selectedAccount, +}) => { const [selectedLevel, setSelectedLevel] = useState( isLoggedIn() ? verification_level?.toString() : Object.keys(config_level)[0] ); @@ -82,6 +87,10 @@ const Index = ({ config_level, verification_level, router }) => { updateTabs(); }, [selectedLevel, config_level, search]); + useEffect(() => { + setSelectedLevel(selectedAccount); + }, [selectedAccount]); + const renderContent = (tabs, activeTab) => tabs[activeTab] && tabs[activeTab].content ? ( tabs[activeTab].content @@ -89,7 +98,7 @@ const Index = ({ config_level, verification_level, router }) => {
); - return config_level && Object.keys(config_level).length && selectedLevel ? ( + return config_level && Object.keys(config_level).length ? (
{!isMobile && ( { const mapStateToProps = (state) => { const { - app: { config_level }, + app: { config_level, selectedAccount }, } = state; return { verification_level: state.user.verification_level, config_level, + selectedAccount, }; }; diff --git a/web/src/containers/Home/index.js b/web/src/containers/Home/index.js index d888ec0742..f72795df4f 100644 --- a/web/src/containers/Home/index.js +++ b/web/src/containers/Home/index.js @@ -175,8 +175,6 @@ class Home extends Component { const { constants: { features: { quick_trade = false } = {} } = {}, isReady, - pair, - // sections, } = this.props; const sectionsNumber = Object.entries(this.state.sectionData) @@ -191,7 +189,7 @@ class Home extends Component { minHeight: this.calculateMinHeight(sectionsNumber), }} onClickDemo={ - pair ? this.goTo(`trade/${pair}`) : this.goTo('markets') + this.goTo('accounts') } onClickTrade={this.goTo('signup')} /> diff --git a/web/src/containers/Init/Login.js b/web/src/containers/Init/Login.js index ae674e4005..0701b9053e 100644 --- a/web/src/containers/Init/Login.js +++ b/web/src/containers/Init/Login.js @@ -13,7 +13,6 @@ import { email, } from '../../components/AdminForm/validations'; import { STATIC_ICONS } from 'config/icons'; -import { getLanguage } from '../../utils/string'; import { getExchangeInitialized } from '../../utils/initialize'; import { isAdmin } from '../../utils/token'; @@ -54,9 +53,6 @@ const Login = (props) => { } else { errMsg = error.message; } - setTimeout(() => { - props.change('LOGIN_FORM', 'captcha', ''); - }, 5000); message.error(errMsg); }); } @@ -98,12 +94,6 @@ const Login = (props) => { maxLength: "6", onInput: maxLengthCheck }, - captcha: { - type: 'captcha', - language: getLanguage(), - theme: props.theme, - validate: [validateRequired], - }, }} onSubmit={handleSubmit} buttonText={'Proceed'} diff --git a/web/src/containers/Login/LoginForm.js b/web/src/containers/Login/LoginForm.js index da9d91c5fe..52cb4842ad 100644 --- a/web/src/containers/Login/LoginForm.js +++ b/web/src/containers/Login/LoginForm.js @@ -7,7 +7,6 @@ import { normalizeEmail, } from 'components/Form/validations'; import { AuthForm } from 'components'; -import { getLanguage } from 'utils/string'; import STRINGS from 'config/localizedStrings'; export const FORM_NAME = 'LoginForm'; @@ -28,13 +27,7 @@ const Form = (props) => { fullWidth: true, label: STRINGS['FORM_FIELDS.PASSWORD_LABEL'], placeholder: STRINGS['FORM_FIELDS.PASSWORD_PLACEHOLDER'], - }, - captcha: { - type: 'captcha', - language: getLanguage(), - theme: props.theme, - validate: [required], - }, + } }; return ( diff --git a/web/src/containers/Login/index.js b/web/src/containers/Login/index.js index 24a9ecac69..10b5e607de 100644 --- a/web/src/containers/Login/index.js +++ b/web/src/containers/Login/index.js @@ -11,7 +11,7 @@ import { storeLoginResult, setLogoutMessage, } from 'actions/authAction'; -import LoginForm, { FORM_NAME } from './LoginForm'; +import LoginForm from './LoginForm'; import { Dialog, OtpForm, IconTitle, Notification } from 'components'; import { NOTIFICATIONS } from 'actions/appActions'; import { errorHandler } from 'components/OtpForm/utils'; @@ -135,9 +135,6 @@ class Login extends Component { : err.message; let error = {}; - errorTimeOut = setTimeout(() => { - this.props.change(FORM_NAME, 'captcha', ''); - }, 5000); if (_error.toLowerCase().indexOf('otp') > -1) { this.setState({ values, otpDialogIsOpen: true }); @@ -145,8 +142,6 @@ class Login extends Component { } else { if (_error === 'User is not activated') { error._error = STRINGS['VALIDATIONS.FROZEN_ACCOUNT']; - } else if (_error.indexOf('captcha') > -1) { - error._error = STRINGS['VALIDATIONS.CAPTCHA']; } else { error._error = _error; } diff --git a/web/src/containers/QuickTrade/_QuickTrade.scss b/web/src/containers/QuickTrade/_QuickTrade.scss index cb4b223c6e..f941452864 100644 --- a/web/src/containers/QuickTrade/_QuickTrade.scss +++ b/web/src/containers/QuickTrade/_QuickTrade.scss @@ -131,11 +131,11 @@ } .extreme-volatility-msg { - color:#FFAA00; + color: #ffaa00; } .disclaimer-checkbox span { - color: #fff; + color: $colors-main-black; opacity: 0.7; } diff --git a/web/src/containers/QuickTrade/components/Details.js b/web/src/containers/QuickTrade/components/Details.js index fa55c759f5..4dce63af65 100644 --- a/web/src/containers/QuickTrade/components/Details.js +++ b/web/src/containers/QuickTrade/components/Details.js @@ -112,6 +112,10 @@ const Details = ({ const handleClick = () => { if (!isNetwork && !brokerUsed) { router.push(`/trade/${pair}`); + } else if (isNetwork && brokerUsed) { + router.push(`/quick-trade/${pair}`); + } else { + router.push(`/prices/coin/${pair.split('-')[0]}`); } }; @@ -168,8 +172,8 @@ const Details = ({
@@ -190,7 +194,7 @@ const Details = ({
-
+
{STRINGS['MARKETS_TABLE.LAST_PRICE']} @@ -226,7 +230,7 @@ const Details = ({
-
+
{ @@ -275,7 +279,7 @@ const Details = ({
{STRINGS['ASSET_INFO']}
{getLink( - `/assets/coin/${pairBase}`, + `/prices/coin/${pairBase}`, STRINGS.formatString( STRINGS['QUICK_TRADE_COMPONENT.COIN_INFORMATION'], coins[pairBase].display_name diff --git a/web/src/containers/QuickTrade/components/ReviewOrder.js b/web/src/containers/QuickTrade/components/ReviewOrder.js index 50c8b41864..0f293e3036 100644 --- a/web/src/containers/QuickTrade/components/ReviewOrder.js +++ b/web/src/containers/QuickTrade/components/ReviewOrder.js @@ -6,7 +6,6 @@ import classnames from 'classnames'; import { Button } from 'components'; import { Progress } from 'antd'; import { ClockCircleOutlined } from '@ant-design/icons'; -import { RiskyTrade } from './RiskyTrade'; const ReviewOrder = ({ onCloseDialog, @@ -27,21 +26,6 @@ const ReviewOrder = ({ moment(expiry).diff(moment(time), 'seconds') ); - const getShowCoinRisky = () => { - const {is_risky, code} = coins[selectedTarget]; - - if(is_risky) { - const localRiskyItems = localStorage.getItem('riskyItems'); - const riskyItems = localRiskyItems ? JSON.parse(localRiskyItems) : {}; - const isNotWarn = !riskyItems[code]; - return isNotWarn; - } - - return false; - } - - const [showRisky, setShowRisky] = useState(getShowCoinRisky()); - const [isExpired, setIsExpired] = useState(timeToExpiry <= 0); useEffect(() => { @@ -58,80 +42,78 @@ const ReviewOrder = ({ return (
- {showRisky ? : - ( -
-
-
{STRINGS['CONFIRM_TEXT']}
-
- {STRINGS['QUOTE_CONFIRMATION_MSG_TEXT_1']} -
-
- {STRINGS['QUOTE_CONFIRMATION_MSG_TEXT_2']} -
-
-
- -
- {isExpired ? ( -
-

{STRINGS['QUOTE_CONFIRMATION_EXPIRED_MSG_TEXT_1']}

-

{STRINGS['QUOTE_CONFIRMATION_EXPIRED_MSG_TEXT_2']}

-
- ) : ( - STRINGS.formatString( - STRINGS['QUOTE_CONFIRMATION_EXPIRY_MSG'], - timeToExpiry, - timeToExpiry > 1 ? STRINGS['SECONDS'] : STRINGS['SECOND'] - ) - )} -
+
+
+
{STRINGS['CONFIRM_TEXT']}
+
+ {STRINGS['QUOTE_CONFIRMATION_MSG_TEXT_1']}
-
- +
+ {STRINGS['QUOTE_CONFIRMATION_MSG_TEXT_2']}
- - +
+ +
+ {isExpired ? ( +
+

{STRINGS['QUOTE_CONFIRMATION_EXPIRED_MSG_TEXT_1']}

+

{STRINGS['QUOTE_CONFIRMATION_EXPIRED_MSG_TEXT_2']}

+
+ ) : ( + STRINGS.formatString( + STRINGS['QUOTE_CONFIRMATION_EXPIRY_MSG'], + timeToExpiry, + timeToExpiry > 1 ? STRINGS['SECONDS'] : STRINGS['SECOND'] + ) + )}
-
-
- )} +
+ +
+
+ + +
+
+
+
+
); }; diff --git a/web/src/containers/QuickTrade/components/RiskyTrade.js b/web/src/containers/QuickTrade/components/RiskyTrade.js deleted file mode 100644 index dcc23e0442..0000000000 --- a/web/src/containers/QuickTrade/components/RiskyTrade.js +++ /dev/null @@ -1,79 +0,0 @@ -import React, { useState } from 'react'; -import { Button, Coin } from 'components'; -import { Checkbox } from 'antd'; - -import STRINGS from 'config/localizedStrings'; -import classNames from 'classnames'; - -export const RiskyTrade = ({ setShowRisky, coinData, onCloseDialog }) => { - const [enableProceedBtn, setEnableProceedBtn] = useState(false); - const { icon_id, fullname, display_name, code } = coinData; - - const toggleRisk = (e) => { - setEnableProceedBtn(e.target.checked); - }; - - const handleProceedClick = () => { - localstoreRiskyCoin(); - setShowRisky(false); - }; - - const localstoreRiskyCoin = () => { - const localRiskyItems = localStorage.getItem('riskyItems'); - const riskyItems = localRiskyItems ? JSON.parse(localRiskyItems) : {}; - riskyItems[code] = true; - localStorage.setItem('riskyItems', JSON.stringify(riskyItems)); - }; - - return ( -
-
-
- -
-
- {STRINGS['USER_SETTINGS.RISKY_TRADE_DETECTED']} -
-

- {STRINGS.formatString( - STRINGS['RISKY_TRADE_DISCLAIMER.MSG_1'], - fullname, - display_name - )} -   - - {STRINGS['RISKY_TRADE_DISCLAIMER.MSG_2']} - -

-

- {STRINGS['RISKY_TRADE_DISCLAIMER.MSG_3']} -

-
- - {STRINGS['RISKY_TRADE_DISCLAIMER.UNDERSTAND_RISK_MSG']} - -
-
-
-
-
- ); -}; diff --git a/web/src/containers/QuickTrade/components/_Details.scss b/web/src/containers/QuickTrade/components/_Details.scss index ecc4081432..6c3b6e10aa 100644 --- a/web/src/containers/QuickTrade/components/_Details.scss +++ b/web/src/containers/QuickTrade/components/_Details.scss @@ -51,8 +51,7 @@ $colors-white: white; } .trade_tabs-container { margin: 0; - padding-left: 80px; - width: 100% !important; + width: auto !important; } .main-coin-wrapper { diff --git a/web/src/containers/RequestResetPassword/ResetPasswordForm.js b/web/src/containers/RequestResetPassword/ResetPasswordForm.js index a53f158141..7f64678783 100644 --- a/web/src/containers/RequestResetPassword/ResetPasswordForm.js +++ b/web/src/containers/RequestResetPassword/ResetPasswordForm.js @@ -4,11 +4,9 @@ import { requiredWithCustomMessage, email, normalizeEmail, - required, } from 'components/Form/validations'; import { AuthForm } from 'components'; import STRINGS from 'config/localizedStrings'; -import { getLanguage } from 'utils/string'; export const generateFormFields = (theme) => ({ email: { @@ -21,13 +19,7 @@ export const generateFormFields = (theme) => ({ fullWidth: true, label: STRINGS['FORM_FIELDS.EMAIL_LABEL'], placeholder: STRINGS['FORM_FIELDS.EMAIL_PLACEHOLDER'], - }, - captcha: { - type: 'captcha', - language: getLanguage(), - theme: theme, - validate: [required], - }, + } }); const Form = (props) => ( diff --git a/web/src/containers/RequestResetPassword/index.js b/web/src/containers/RequestResetPassword/index.js index 5fccfcc83c..0cf0e80733 100644 --- a/web/src/containers/RequestResetPassword/index.js +++ b/web/src/containers/RequestResetPassword/index.js @@ -67,9 +67,6 @@ class RequestResetPassword extends Component { } else { errors._error = error.message; } - errorTimeOut = setTimeout(() => { - this.props.change('ResetPasswordForm', 'captcha', ''); - }, 5000); throw new SubmissionError(errors); } }); diff --git a/web/src/containers/Signup/SignupForm.js b/web/src/containers/Signup/SignupForm.js index 19545627f1..e1bb0cd573 100644 --- a/web/src/containers/Signup/SignupForm.js +++ b/web/src/containers/Signup/SignupForm.js @@ -60,12 +60,7 @@ export const generateFormFields = (strings, theme, links = {}) => ({ text={strings['SIGN_UP.TERMS.policy']} /> ), - }, - captcha: { - type: 'captcha', - theme, - validate: [required], - }, + } }); const validate = (values) => { diff --git a/web/src/containers/Signup/index.js b/web/src/containers/Signup/index.js index 1caa05e16a..b7b8e9d9ac 100644 --- a/web/src/containers/Signup/index.js +++ b/web/src/containers/Signup/index.js @@ -114,10 +114,6 @@ class Signup extends Component { }) .catch((error) => { const errors = {}; - errorTimeOut = setTimeout(() => { - this.props.change(FORM_NAME, 'captcha', ''); - }, 5000); - if (error.response && error.response.status === 409) { errors.email = STRINGS['VALIDATIONS.USER_EXIST']; } else if (error.response) { diff --git a/web/src/containers/Summary/components/Markets.js b/web/src/containers/Summary/components/Markets.js index a95bf6848f..4e1257a377 100644 --- a/web/src/containers/Summary/components/Markets.js +++ b/web/src/containers/Summary/components/Markets.js @@ -141,7 +141,7 @@ class Markets extends Component { {STRINGS.formatString( STRINGS['SUMMARY_MARKETS.VISIT_COIN_INFO_PAGE'], - + {STRINGS['SUMMARY_MARKETS.HERE']} )} diff --git a/web/src/containers/Summary/index.js b/web/src/containers/Summary/index.js index a18b40c936..325e5c6a44 100644 --- a/web/src/containers/Summary/index.js +++ b/web/src/containers/Summary/index.js @@ -19,6 +19,7 @@ import { logoutconfirm, setNotification, NOTIFICATIONS, + setSelectedAccount, } from 'actions/appActions'; import { BASE_CURRENCY, @@ -88,6 +89,7 @@ class Summary extends Component { onAccountTypeChange = (type) => { this.setState({ selectedAccount: type }); + this.props.setSelectedAccount(type); }; onUpgradeAccount = () => { @@ -102,11 +104,13 @@ class Summary extends Component { currentTradingAccount, selectedAccount: user.verification_level, }); + this.props.setSelectedAccount(user.verification_level); } else if (!isLoggedIn()) { const { config_level } = this.props; this.setState({ selectedAccount: Object.keys(config_level)[0] || 0, }); + this.props.setSelectedAccount(Object.keys(config_level)[0] || 0); } }; @@ -339,6 +343,7 @@ const mapDispatchToProps = (dispatch) => ({ setNotification: bindActionCreators(setNotification, dispatch), getUserReferrals: bindActionCreators(getUserReferrals, dispatch), openContactForm: bindActionCreators(openContactForm, dispatch), + setSelectedAccount: bindActionCreators(setSelectedAccount, dispatch), }); export default connect( diff --git a/web/src/containers/Trade/Chart.js b/web/src/containers/Trade/Chart.js index 048eaef2b6..6a1abd1389 100644 --- a/web/src/containers/Trade/Chart.js +++ b/web/src/containers/Trade/Chart.js @@ -327,6 +327,7 @@ class TVChartContainer extends React.PureComponent { tvWidget.chart().createStudy('Moving Average', false, false, [7]); tvWidget.chart().createStudy('Moving Average', false, false, [25]); tvWidget.chart().createStudy('Volume', false, false); + // tvWidget.chart().createStudy('Keltner Channels', false, false, [{}, 20, 2]); // tvWidget.chart().createStudy('MACD', false, false, [14, 30, 'close', 9]) } diff --git a/web/src/containers/Trade/MobileTrade.js b/web/src/containers/Trade/MobileTrade.js index 86b6cf861b..9b381a84c6 100644 --- a/web/src/containers/Trade/MobileTrade.js +++ b/web/src/containers/Trade/MobileTrade.js @@ -12,7 +12,6 @@ const MobileTrade = ({ balance, onSubmitOrder, openCheckOrder, - onRiskyTrade, settings, orderbookProps, symbol, @@ -41,7 +40,6 @@ const MobileTrade = ({ { + return ( +
+ {STRINGS[label]} +
+ onHandleClick('caretUp', label)} /> + onHandleClick('caretDown', label)} /> +
+
+ ); +}; + const generateHeaders = ( pairs = {}, onCancel, onCancelAll, ICONS, - activeOrdersMarket + activeOrdersMarket, + orders, + onHandleClick ) => [ { stringId: 'PAIR', @@ -64,14 +79,14 @@ const generateHeaders = ( // }, // }, !isMobile && { - label: STRINGS['TIME'], + label: rendercaret('TIME', onHandleClick), key: 'created_At', renderCell: ({ created_at = '' }, key, index) => { return {getFormatTimestamp(created_at)}; }, }, { - label: STRINGS['PRICE'], + label: rendercaret('PRICE', onHandleClick), key: 'price', renderCell: ({ price = 0, symbol }, key, index) => { let pairData = pairs[symbol] || {}; @@ -194,6 +209,25 @@ const ActiveOrders = ({ activeOrdersMarket, pageSize, }) => { + const [filteredOrders, setFilteredOrders] = useState([...orders]); + + useEffect(() => { + setFilteredOrders(orders); + }, [orders]); + + const onHandleClick = (type, label) => { + const filteredData = filteredOrders.sort((a, b) => { + if (label === 'TIME') { + return type === 'caretUp' + ? new Date(a.created_at) - new Date(b.created_at) + : new Date(b.created_at) - new Date(a.created_at); + } else { + return type === 'caretUp' ? a.price - b.price : b.price - a.price; + } + }); + setFilteredOrders((prev) => [...prev, ...filteredData]); + }; + return (
{ - if (risk.popup_warning && isRiskyOrder) { - order['order_portfolio_percentage'] = risk.order_portfolio_percentage; - onRiskyTrade(order, () => { - submit(FORM_NAME); - }); - } else { - submit(FORM_NAME); - } - }); - } else if (risk.popup_warning && isRiskyOrder) { - order['order_portfolio_percentage'] = risk.order_portfolio_percentage; - onRiskyTrade(order, () => { submit(FORM_NAME); }); } else { @@ -727,7 +694,6 @@ const mapStateToProps = (state) => { bids, marketPrice, order_entry_data: state.orderbook.order_entry_data, - totalAsset: state.asset.totalAsset, oraclePrices: state.asset.oraclePrices, estimatedPrice, }; diff --git a/web/src/containers/Trade/components/OrderEntryReview.js b/web/src/containers/Trade/components/OrderEntryReview.js index 4cb5e341c3..fffb03b953 100644 --- a/web/src/containers/Trade/components/OrderEntryReview.js +++ b/web/src/containers/Trade/components/OrderEntryReview.js @@ -84,7 +84,7 @@ const Review = ({
- + {STRINGS.formatString(STRINGS['ABOUT_LINK'], symbol)} diff --git a/web/src/containers/Trade/index.js b/web/src/containers/Trade/index.js index 4a1bdd67b5..03405bd5db 100644 --- a/web/src/containers/Trade/index.js +++ b/web/src/containers/Trade/index.js @@ -21,7 +21,6 @@ import { changePair, setNotification, NOTIFICATIONS, - RISKY_ORDER, setTradeTab, } from 'actions/appActions'; import { NORMAL_CLOSURE_CODE, isIntentionalClosure } from 'utils/webSocket'; @@ -314,16 +313,6 @@ class Trade extends PureComponent { }); }; - onRiskyTrade = (order, onConfirm) => { - const { setNotification, fees, pairData } = this.props; - setNotification(RISKY_ORDER, { - order, - onConfirm, - fees, - pairData, - }); - }; - onPriceClick = (price) => { this.props.change(FORM_NAME, 'price', price); playBackgroundAudioNotification( @@ -613,7 +602,6 @@ class Trade extends PureComponent { focusOnSizeInput={this.focusOnSizeInput} submitOrder={this.onSubmitOrder} openCheckOrder={this.openCheckOrder} - onRiskyTrade={this.onRiskyTrade} symbol={symbol} balance={balance} fees={fees} @@ -767,7 +755,6 @@ class Trade extends PureComponent { settings={settings} orderbookReady={orderbookReady} openCheckOrder={this.openCheckOrder} - onRiskyTrade={this.onRiskyTrade} onSubmitOrder={this.onSubmitOrder} pair={pair} setPriceRef={this.setPriceRef} diff --git a/web/src/containers/TradeTabs/components/MarketRow.js b/web/src/containers/TradeTabs/components/MarketRow.js index c058b9b94d..4a2e78109e 100644 --- a/web/src/containers/TradeTabs/components/MarketRow.js +++ b/web/src/containers/TradeTabs/components/MarketRow.js @@ -105,7 +105,10 @@ class MarketRow extends Component { {ticker.volume > 0 && (
- {volume_native_text} + {volume_native_text.split(' ')[0]} + + + {volume_native_text.split(' ')[1]}
)} diff --git a/web/src/containers/TradeTabs/index.js b/web/src/containers/TradeTabs/index.js index 05a57b6786..e2ad95e16b 100644 --- a/web/src/containers/TradeTabs/index.js +++ b/web/src/containers/TradeTabs/index.js @@ -175,14 +175,14 @@ class AddTradeTab extends Component { {!isMobile && (
- {STRINGS['ASSET_INFO_PAGE']} + {STRINGS['ASSET_INFO_PAGE']} {constants && constants.features && constants.features.quick_trade ? ( - {STRINGS['QUICK_TRADE']} + {STRINGS['DIGITAL_ASSETS.QUICK_TRADE']} ) : null} diff --git a/web/src/containers/TransactionsHistory/index.js b/web/src/containers/TransactionsHistory/index.js index 0751e8cfcb..1c69fc4dc2 100644 --- a/web/src/containers/TransactionsHistory/index.js +++ b/web/src/containers/TransactionsHistory/index.js @@ -48,9 +48,7 @@ import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; import { STATIC_ICONS } from 'config/icons'; import { Image } from 'hollaex-web-lib'; -import { - quicktradePairSelector, -} from 'containers/QuickTrade/components/utils'; +import { quicktradePairSelector } from 'containers/QuickTrade/components/utils'; const GROUP_CLASSES = [...FLEX_CENTER_CLASSES, 'flex-column']; const transactionTabs = ['trades', 'orders', 'deposits', 'withdrawals']; @@ -156,7 +154,8 @@ class TransactionsHistory extends Component { } if ( JSON.stringify(nextProps.pairs) !== JSON.stringify(pairs) || - JSON.stringify(nextProps.quicktradePairs) !== JSON.stringify(quicktradePairs) || + JSON.stringify(nextProps.quicktradePairs) !== + JSON.stringify(quicktradePairs) || JSON.stringify(nextProps.coins) !== JSON.stringify(coins) ) { this.generateFilters(); @@ -702,6 +701,7 @@ class TransactionsHistory extends Component { onCloseDialog={onCloseDialog} shouldCloseOnOverlayClick={true} showCloseText={false} + className="cancel-withdraw-pop-up" >
-
+
{STRINGS['CANCEL_WITHDRAWAL_POPUP_CONFIRM']}
diff --git a/web/src/containers/TransactionsHistory/selectors.js b/web/src/containers/TransactionsHistory/selectors.js index dd809ba357..70b627d6cf 100644 --- a/web/src/containers/TransactionsHistory/selectors.js +++ b/web/src/containers/TransactionsHistory/selectors.js @@ -9,7 +9,7 @@ const getWithdrawals = (state) => state.wallet.withdrawals; const modifyTradesAndOrders = (history, coins) => { const data = history.data.map((record, index) => { - const { symbol: pair, fee_coin } = record; + const { symbol: pair, fee_coin, quick } = record; const [pair_base, pair_2] = pair.split('-'); const { display_name: pair_base_display, icon_id } = coins[pair_base] || DEFAULT_COIN_DATA; @@ -17,6 +17,7 @@ const modifyTradesAndOrders = (history, coins) => { const { display_name: fee_coin_display } = coins[fee_coin || pair_base] || DEFAULT_COIN_DATA; const display_name = `${pair_base_display}-${pair_2_display}`; + return { ...record, display_name, @@ -25,6 +26,7 @@ const modifyTradesAndOrders = (history, coins) => { fee_coin_display, icon_id, history_id: index, + quick, }; }); diff --git a/web/src/containers/TransactionsHistory/utils.js b/web/src/containers/TransactionsHistory/utils.js index 61ae397648..4084d1e463 100644 --- a/web/src/containers/TransactionsHistory/utils.js +++ b/web/src/containers/TransactionsHistory/utils.js @@ -3,8 +3,9 @@ import { InfoCircleTwoTone, PlusSquareOutlined, MinusSquareOutlined, + ThunderboltFilled, } from '@ant-design/icons'; -import { notification } from 'antd'; +import { notification, Tooltip } from 'antd'; import classnames from 'classnames'; import mathjs from 'mathjs'; import { isMobile } from 'react-device-detect'; @@ -424,12 +425,22 @@ export const generateTradeHeaders = ( key: 'pair', exportToCsv: ({ display_name }) => display_name, className: 'sticky-col', - renderCell: ({ display_name, icon_id }, key, index) => { + renderCell: ({ display_name, icon_id, quick }, key, index) => { return (
{display_name}
+
+ {quick && ( + + + + )} +
); diff --git a/web/src/containers/UserSecurity/OTP.js b/web/src/containers/UserSecurity/OTP.js index 676259fc5a..c9e0d37664 100644 --- a/web/src/containers/UserSecurity/OTP.js +++ b/web/src/containers/UserSecurity/OTP.js @@ -1,18 +1,48 @@ -import React from 'react'; -import { CheckboxButton, IconTitle, EditWrapper } from 'components'; +import React, { useState, useEffect } from 'react'; import QRCode from 'qrcode.react'; -import OTPForm from './OTPForm'; +import { Button } from 'antd'; +import { CloseCircleOutlined } from '@ant-design/icons'; +import { CheckboxButton, IconTitle, EditWrapper } from 'components'; import STRINGS from 'config/localizedStrings'; import { Image } from 'hollaex-web-lib'; +import { RenderBtn, RenderBackBtn } from './utils_logins'; +import { STATIC_ICONS } from 'config/icons'; -export const renderOTPForm = ( +const OtpFormSteps = ({ secret, email, activateOTP, constants = {}, - ICONS -) => { + ICONS, + closeDialog, + handleOTPCheckbox, + handleUpdateOtp, + selectedStep, +}) => { + const [step, setStep] = useState(0); + const [otpValue, setOtpValue] = useState(); + const [errorMsg, setErrorMsg] = useState(''); const app_name = constants.api_name.replace(' ', '').trim() || ''; + + useEffect(() => { + if (selectedStep) { + setStep(selectedStep); + handleUpdateOtp(false); + } + }, [selectedStep, handleUpdateOtp]); + + useEffect(() => { + if (otpValue) { + setErrorMsg(''); + } + }, [otpValue]); + + const handleOnBlur = () => { + if (!otpValue) { + setErrorMsg(STRINGS['ACCOUNT_SECURITY.OTP.MANUEL_ERROR_1']); + } + }; + return (
-
-
- {string}} - > - {STRINGS['ACCOUNT_SECURITY.OTP.CONTENT.MESSAGE_1']} - -
-
- - {STRINGS['ACCOUNT_SECURITY.OTP.CONTENT.MESSAGE_2']} - + {step === 0 && ( +
+
+
+
+ + {STRINGS['ACCOUNT_SECURITY.OTP.DOWNLOAD_APP']} + +
+ app-store +
+ + {STRINGS['ACCOUNT_SECURITY.OTP.GOOGLE_AUTHENTICATOR']} + +
+
+
+
+ window.open( + 'https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en&gl=US', + '_blank' + ) + } + > +
+ google-play +
+
+ + {STRINGS['ACCOUNT_SECURITY.OTP.DOWNLOAD_FROM']} + +
+
+ + {STRINGS['ACCOUNT_SECURITY.OTP.GOOGLE_PLAY']} + +
+
+
+
+
+ window.open( + 'https://apps.apple.com/us/app/google-authenticator/id388497605', + '_blank' + ) + } + > +
+ app-store +
+
+ + {STRINGS['ACCOUNT_SECURITY.OTP.DOWNLOAD_FROM']} + +
+
+ + {STRINGS['ACCOUNT_SECURITY.OTP.APPLE']} + +
+
+
+
+
+
+ + {STRINGS['ACCOUNT_SECURITY.OTP.APP_INFO']} + +
+
+
+
+
+ + {STRINGS['ACCOUNT_SECURITY.OTP.DONT_UNINSTALL']} + +
+
+ + {STRINGS['ACCOUNT_SECURITY.OTP.INFO_UNINSTALL']} + +
+
-
-
- +
+
+ {string}} + > + {STRINGS['ACCOUNT_SECURITY.OTP.CONTENT.MESSAGE_1']} + +
+
+
+ +
+
+
+
+
+
+ {string}} + > + {STRINGS['ACCOUNT_SECURITY.OTP.CONTENT.MESSAGE_5']} + +
+
{secret}
+
+ + {STRINGS.formatString( + STRINGS['ACCOUNT_SECURITY.OTP.NOTE'], + STRINGS['ACCOUNT_SECURITY.OTP.MANUEL_DESCRIPTION_2'], + STRINGS['ACCOUNT_SECURITY.OTP.MANUEL_DESCRIPTION_3'] + )} + +
+
+
+
-
-
-
- {string}} - > - {STRINGS['ACCOUNT_SECURITY.OTP.CONTENT.MESSAGE_5']} - -
-
- - {STRINGS['ACCOUNT_SECURITY.OTP.CONTENT.MESSAGE_3']} - -
- - {STRINGS['ACCOUNT_SECURITY.OTP.CONTENT.MESSAGE_4']} - -
-
{secret}
-
-
-
- {string}} - > - {STRINGS['ACCOUNT_SECURITY.OTP.CONTENT.INPUT']} - + )} + + {step === 2 && ( +
+
+ + {STRINGS['ACCOUNT_SECURITY.OTP.MANUEL_DESCRIPTION']} + +
+
+ + {STRINGS['ACCOUNT_SECURITY.OTP.MANUEL_KEY']} + +
+ { + setOtpValue(e.target.value); + }} + required + onBlur={handleOnBlur} + /> + {errorMsg &&
{errorMsg}
} +
+ + {STRINGS['ACCOUNT_SECURITY.OTP.MANUEL_WARNING']} + +
+ +
+ + +
- -
+ )}
); }; +export const renderOTPForm = ( + secret, + email, + activateOTP, + constants = {}, + ICONS, + closeDialog, + handleOTPCheckbox = () => {}, + handleUpdateOtp = () => {}, + selectedStep +) => { + return ( + + ); +}; + export const OTP = ({ requestOTP, data = {}, @@ -89,12 +309,74 @@ export const OTP = ({ icons = {}, }) => (
-
- {!otp_enabled && ( -
- - {STRINGS['ACCOUNT_SECURITY.OTP.CONTENT.WARNING']} - +
+ {!otp_enabled ? ( +
+
+ + {STRINGS.formatString( + STRINGS['ACCOUNT_SECURITY.OTP.CONTENT.WARNING'], + + {STRINGS['ACCOUNT_SECURITY.OTP.CONTENT.STRONGLY_RECOMMEND']} + , + STRINGS['ACCOUNT_SECURITY.OTP.CONTENT.WARNING_CONTENT'] + )} + +
+
+ + + + {STRINGS['ACCOUNT_SECURITY.OTP.2FA_DISABLED']} + + + + + {STRINGS.formatString( + STRINGS['ACCOUNT_SECURITY.OTP.2FA_CONTENT_ONE'], + + {STRINGS['ACCOUNT_SECURITY.OTP.TITLE']} + , + STRINGS['ACCOUNT_SECURITY.OTP.2FA_CONTENT_TWO'] + )} + + +
+
+ ) : ( +
+ {otp_enabled && ( + + )} + + + {STRINGS['ACCOUNT_SECURITY.OTP.2FA_DISABLED']} + + + + + + {STRINGS.formatString( + STRINGS['ACCOUNT_SECURITY.OTP.2FA_CONTENT_ONE'], + + {STRINGS['ACCOUNT_SECURITY.OTP.TITLE']} + , + STRINGS['ACCOUNT_SECURITY.OTP.2FA_CONTENT_THREE'] + )} + + + + + {STRINGS['ACCOUNT_SECURITY.OTP.2FA_CONTENT_FOUR']} + + +
)}
@@ -115,15 +397,6 @@ export const OTP = ({ > {children} - {otp_enabled && ( - - )}
); diff --git a/web/src/containers/UserSecurity/_UserSecurity.scss b/web/src/containers/UserSecurity/_UserSecurity.scss index 1f25a53672..8d2071b2b4 100644 --- a/web/src/containers/UserSecurity/_UserSecurity.scss +++ b/web/src/containers/UserSecurity/_UserSecurity.scss @@ -1,14 +1,15 @@ -.user_security-wrapper { +.user_security-wrapper, +.user_security-wrapper-disabled { display: flex; flex-direction: column; justify-content: center; align-items: center; - margin-top: 30px; padding: 30px; background-color: $app-sidebar-background; - > *:not(:last-child) { - margin-bottom: 2rem; - } +} + +.user_security-wrapper-disabled { + margin-top: 3%; } .OTP_Success { @@ -26,8 +27,128 @@ min-width: 30rem; width: 30rem; + .step-one-title-wrapper { + font-family: 'Open Sans'; + } + + .step-one-pop-up { + .store-wrapper { + cursor: pointer; + padding: 2%; + width: 30%; + color: #000000; + font-size: 10px; + border-radius: 10px; + border: 1px solid $colors-main-border; + background-color: #f5f5f7; + .store-icons { + width: 25%; + } + } + .download-content-title { + font-weight: bold; + margin: 2% 0; + } + .store-icons-wrapper { + display: flex; + justify-content: space-evenly; + margin-bottom: 5%; + .play-store-field { + cursor: pointer; + padding: 2%; + width: 100%; + height: 50%; + color: $colors-main-black; + font-size: 12px; + border-radius: 10px; + background-color: $colors-main-black; + } + } + .app-info { + margin-bottom: 5%; + } + .uninstall-info { + color: #ffbb00; + font-weight: bold; + } + } + + .step-one-pop-up, + .step-two-pop-up { + .note-txt { + color: #e59b07; + } + .otp-border, + .otp-border-footer { + margin-bottom: 5%; + border-bottom: 1px solid $colors-main-border; + width: 100%; + } + .otp-border-footer { + width: 25%; + } + .btn-wrapper { + display: flex; + justify-content: space-between; + margin-top: 8%; + .back-btn, + .proceed-btn { + background-color: $link; + border: none; + width: 45%; + height: 40px; + text-align: center; + color: $colors-main-black; + } + } + } + + .step-two-pop-up { + .btn-wrapper { + margin-top: 5% !important; + } + } + + .step-three-pop-up { + .verfication-field { + background-color: $app-sidebar-background; + border: none; + border-bottom: 1px solid $colors-black; + border-image: initial; + width: 100%; + margin-top: 5px; + } + .warning-text { + color: $colors-black; + margin-top: 15%; + } + .btn-wrapper { + display: flex; + justify-content: space-between; + margin-top: 8%; + } + } + + .step-three-pop-up, + .step-four-pop-up { + .back-btn, + .proceed-btn { + background-color: $link; + border: none; + width: 45%; + height: 40px; + text-align: center; + color: $colors-main-black; + } + } + + .step-four-pop-up { + .back-btn { + width: 100%; + } + } + .otp_form-section-wrapper { - border-top: 1px solid $colors-main-border; padding: 0.75rem 0; .otp_form-section-content { @@ -67,7 +188,7 @@ font-size: $font-size-subhead2; } .icon_title-svg { - @include size(6.5rem); + @include size(5.5rem); svg { @include size(6.5rem); } @@ -240,6 +361,20 @@ $padding-input: 4rem; } } +.otp-field-wrapper { + margin-top: 3%; + .warning_text { + padding: 10px; + text-align: center; + background-color: $colors-notifications-red; + color: $colors-main-black; + .improtant-text { + font-weight: bold; + text-decoration: underline; + } + } +} + .layout-mobile { .user_security-wrapper { .warning_text { @@ -464,3 +599,10 @@ $padding-input: 4rem; } } } + +.errorField { + border-color: $colors-notifications-red !important; +} +.errorText { + color: $colors-notifications-red !important; +} diff --git a/web/src/containers/UserSecurity/index.js b/web/src/containers/UserSecurity/index.js index e2b8eb3a85..8c7242ba0e 100644 --- a/web/src/containers/UserSecurity/index.js +++ b/web/src/containers/UserSecurity/index.js @@ -4,7 +4,7 @@ import { bindActionCreators } from 'redux'; import { SubmissionError } from 'redux-form'; import { isMobile } from 'react-device-detect'; import { message } from 'antd'; -import { openContactForm } from 'actions/appActions'; +import { openContactForm, setSelectedStep } from 'actions/appActions'; import { resetPassword, otpRequest, @@ -62,6 +62,8 @@ class UserSecurity extends Component { freeze: false, error: '', updatedPassword: {}, + isEnableOtpForm: false, + otpFormStep: 0, }; componentDidMount() { @@ -480,7 +482,7 @@ class UserSecurity extends Component { onSubmitCancelOTP = (values) => { const { icons: ICONS } = this.props; const { otp_enabled } = this.props.user; - const { updatedPassword } = this.state; + const { updatedPassword, isEnableOtpForm } = this.state; const body = { ...updatedPassword, ...values, @@ -515,18 +517,34 @@ class UserSecurity extends Component { throw new SubmissionError({ _error }); }); } else { - return otpRevoke({ code: values.otp_code }) - .then(() => { - this.props.otpSetActivated(false); - this.setState({ - dialogIsOpen: true, - iconId: 'OTP_DEACTIVATED', - icon: ICONS['OTP_DEACTIVATED'], - modalText: STRINGS['ACCOUNT_SECURITY.OTP.DIALOG.REVOKE'], - stringId: 'ACCOUNT_SECURITY.OTP.DIALOG.REVOKE', - }); - }) - .catch(errorHandler); + if (isEnableOtpForm) { + return otpActivate({ code: values.otp_code }) + .then(() => { + this.props.otpSetActivated(true); + this.setState({ + dialogIsOpen: true, + iconId: 'OTP_ACTIVE', + icon: ICONS['OTP_ACTIVE'], + modalText: STRINGS['ACCOUNT_SECURITY.OTP.DIALOG.SUCCESS'], + stringId: 'ACCOUNT_SECURITY.OTP.DIALOG.SUCCESS', + isEnableOtpForm: false, + }); + }) + .catch(errorHandler); + } else { + return otpRevoke({ code: values.otp_code }) + .then(() => { + this.props.otpSetActivated(false); + this.setState({ + dialogIsOpen: true, + iconId: 'OTP_DEACTIVATED', + icon: ICONS['OTP_DEACTIVATED'], + modalText: STRINGS['ACCOUNT_SECURITY.OTP.DIALOG.REVOKE'], + stringId: 'ACCOUNT_SECURITY.OTP.DIALOG.REVOKE', + }); + }) + .catch(errorHandler); + } } }; @@ -544,7 +562,9 @@ class UserSecurity extends Component { iconId: '', icon: '', updatedPassword: {}, + isEnableOtpForm: false, }); + this.props.setSelectedStep(0); }; // onSubmitotp = (values) => { @@ -553,6 +573,10 @@ class UserSecurity extends Component { // }; + handleUpdateOtp = (val) => { + this.setState({ isEnableOtpForm: val }); + }; + renderModalContent = ( { requested, activated, secret, error }, otp_enabled, @@ -561,7 +585,7 @@ class UserSecurity extends Component { constants, icons ) => { - const { stringId, icon, iconId } = this.state; + const { stringId, icon, iconId, isEnableOtpForm } = this.state; if (error) { return ( @@ -571,15 +595,25 @@ class UserSecurity extends Component { success={false} /> ); - } else if (otp_enabled && !modalText) { - return ; + } else if ((otp_enabled && !modalText) || isEnableOtpForm) { + return ( + + ); } else if (requested && !activated) { + const { selectedStep } = this.props; return renderOTPForm( secret, email, this.onSubmitActivateOtp, constants, - icons + icons, + this.onCloseDialog, + this.handleOTPCheckbox, + this.handleUpdateOtp, + selectedStep ); } else { return ( @@ -595,6 +629,11 @@ class UserSecurity extends Component { } }; + onHandleEnableBack = (val) => { + this.props.setSelectedStep(val); + this.setState({ isEnableOtpForm: false }); + }; + render() { const { icons: ICONS, @@ -605,7 +644,14 @@ class UserSecurity extends Component { if (isLoggedIn() && verification_level === 0) { return ; } - const { dialogIsOpen, modalText, activeTab, tabs, freeze } = this.state; + const { + dialogIsOpen, + modalText, + activeTab, + tabs, + freeze, + isEnableOtpForm, + } = this.state; //const { onCloseDialog } = this; if (freeze === true) { @@ -676,6 +722,8 @@ class UserSecurity extends Component { label="security-modal" onCloseDialog={this.onCloseDialog} showCloseText={!(otp.error || modalText)} + isEnableOtpForm={isEnableOtpForm} + onHandleEnableBack={this.onHandleEnableBack} > {dialogIsOpen && !otp.requesting ? ( this.renderModalContent( @@ -722,6 +770,7 @@ const mapStateToProps = (state) => ({ state, ...Object.keys(generateFormValues()) ), + selectedStep: state.app.selectedStep, }); const mapDispatchToProps = (dispatch) => ({ @@ -729,6 +778,7 @@ const mapDispatchToProps = (dispatch) => ({ requestOTP: () => dispatch(otpRequest()), otpSetActivated: (active) => dispatch(otpSetActivated(active)), openContactForm: bindActionCreators(openContactForm, dispatch), + setSelectedStep: bindActionCreators(setSelectedStep, dispatch), }); export default connect( diff --git a/web/src/containers/UserSecurity/utils_logins.js b/web/src/containers/UserSecurity/utils_logins.js index 59f501f975..2447a869d9 100644 --- a/web/src/containers/UserSecurity/utils_logins.js +++ b/web/src/containers/UserSecurity/utils_logins.js @@ -1,6 +1,8 @@ import React from 'react'; +import { Button } from 'antd'; import { getFormatTimestamp } from 'utils/utils'; import STRINGS from 'config/localizedStrings'; +import { EditWrapper } from 'components'; export const generateLogins = () => { return [ @@ -20,3 +22,45 @@ export const generateLogins = () => { }, ]; }; + +export const RenderBtn = ({ closeDialog, setStep, step, goBack, label }) => { + return ( +
+ + +
+ ); +}; + +export const RenderBackBtn = ({ setStep, setErrorMsg, setOtpValue, step }) => { + return ( + + ); +}; diff --git a/web/src/containers/UserSettings/RiskForm.js b/web/src/containers/UserSettings/RiskForm.js deleted file mode 100644 index 69c15dbe05..0000000000 --- a/web/src/containers/UserSettings/RiskForm.js +++ /dev/null @@ -1,210 +0,0 @@ -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { reduxForm, formValueSelector } from 'redux-form'; -import classnames from 'classnames'; -import { Table, Button, IconTitle } from 'components'; -import renderFields from 'components/Form/factoryFields'; -import STRINGS from 'config/localizedStrings'; -import { formatBaseAmount } from 'utils/currency'; -import { BASE_CURRENCY, DEFAULT_COIN_DATA } from 'config/constants'; -import { EditWrapper } from 'components'; - -const FORM_NAME = 'WarningForm'; -const selector = formValueSelector(FORM_NAME); - -export const generateHeaders = (onAdjustPortfolio) => { - return [ - { - stringId: 'USER_SETTINGS.RISK_MANAGEMENT.PORTFOLIO', - label: STRINGS['USER_SETTINGS.RISK_MANAGEMENT.PORTFOLIO'], - key: 'percentage', - renderCell: ({ id, percentage, status }, key, index) => ( - - - {percentage.portfolioPercentage} - {}} - > - - {STRINGS['USER_SETTINGS.RISK_MANAGEMENT.ADJUST']} - - - - - ), - }, - { - stringId: 'USER_SETTINGS.RISK_MANAGEMENT.VALUE_ASSET', - label: STRINGS['USER_SETTINGS.RISK_MANAGEMENT.VALUE_ASSET'], - key: 'assetValue', - renderCell: ({ id, assetValue, status }, key, index) => ( - - - {' '} - {assetValue.percentPrice} - - - ), - }, - { - stringId: 'USER_SETTINGS.RISK_MANAGEMENT.ACTIVATE_RISK_MANAGEMENT', - label: STRINGS['USER_SETTINGS.RISK_MANAGEMENT.ACTIVATE_RISK_MANAGEMENT'], - key: 'adjust', - className: 'text-right', - renderCell: ({ id, adjust }, key, index) => ( - -
- {renderFields(adjust)} -
- - ), - }, - ]; -}; - -export const generateWarningFormValues = (DEFAULT_TOGGLE_OPTIONS) => ({ - popup_warning: { - type: 'toggle', - stringId: 'USER_SETTINGS.RISK_MANAGEMENT.WARNING_POP_UP', - label: STRINGS['USER_SETTINGS.RISK_MANAGEMENT.WARNING_POP_UP'], - // className: 'toggle-wrapper', - toggleOnly: true, - options: DEFAULT_TOGGLE_OPTIONS, - }, -}); - -class Form extends Component { - componentDidUpdate(prevProps) { - if ( - JSON.stringify(this.props.initialValues) !== - JSON.stringify(prevProps.initialValues) - ) { - this.props.initialize(this.props.initialValues); - } - } - render() { - const { - onAdjustPortfolio, - totalAssets, - initialValues = {}, - handleSubmit, - submitting, - pristine, - // error, - valid, - formFields, - coins, - ICONS, - popup_warning, - } = this.props; - - const percentPrice = - (totalAssets / 100) * initialValues.order_portfolio_percentage; - const { fullname, display_name } = - coins[BASE_CURRENCY] || DEFAULT_COIN_DATA; - const assetData = [ - { - id: 1, - percentage: { - portfolioPercentage: initialValues.order_portfolio_percentage - ? `${initialValues.order_portfolio_percentage}%` - : '', - popupWarning: initialValues.popup_warning, - }, - assetValue: { - percentPrice: percentPrice - ? `${formatBaseAmount(percentPrice)} ${display_name}` - : 0, - popupWarning: initialValues.popup_warning, - }, - adjust: formFields, - warning: initialValues.popup_warning, - status: popup_warning, - }, - ]; - const sections = [ - { - stringId: - 'USER_SETTINGS.CREATE_ORDER_WARING,USER_SETTINGS.RISK_MANAGEMENT.INFO_TEXT,USER_SETTINGS.RISK_MANAGEMENT.INFO_TEXT_1', - title: STRINGS['USER_SETTINGS.CREATE_ORDER_WARING'], - content: ( -
-

{children}

} - > - {STRINGS['USER_SETTINGS.RISK_MANAGEMENT.INFO_TEXT']} -
-

{children}

} - > - {STRINGS.formatString( - STRINGS['USER_SETTINGS.RISK_MANAGEMENT.INFO_TEXT_1'], - fullname, - totalAssets - )} -
- - - ), - isOpen: true, - }, - ]; - return ( -
-
- {/* */} -
- - {sections.map((data, index) => { - return
{data.content}
; - })} -
-
- -
- -
- ); - } -} - -const RiskForm = reduxForm({ - form: FORM_NAME, - enableReinitialize: true, -})(Form); - -const mapStateToForm = (state) => ({ - popup_warning: selector(state, 'popup_warning'), -}); - -const RiskFormValues = connect(mapStateToForm)(RiskForm); - -export default RiskFormValues; diff --git a/web/src/containers/UserSettings/index.js b/web/src/containers/UserSettings/index.js index 3ad08daf73..9d093bf307 100644 --- a/web/src/containers/UserSettings/index.js +++ b/web/src/containers/UserSettings/index.js @@ -38,7 +38,6 @@ import NotificationForm, { generateNotificationFormValues, } from './NotificationForm'; import AudioCueForm, { generateAudioCueFormValues } from './AudioForm'; -import RiskForm, { generateWarningFormValues } from './RiskForm'; import { isLoggedIn } from 'utils/token'; import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; @@ -78,16 +77,11 @@ class UserSettings extends Component { window.location.search.includes('audioCue') ) { this.setState({ activeTab: 3 }); - } else if ( - window.location.search && - window.location.search.includes('manageRisk') - ) { - this.setState({ activeTab: 4 }); } else if ( window.location.search && window.location.search.includes('account') ) { - this.setState({ activeTab: 5 }); + this.setState({ activeTab: 4 }); } this.openCurrentTab(); } @@ -146,8 +140,6 @@ class UserSettings extends Component { } else if (this.state.activeTab === 3) { currentTab = 'audioCue'; } else if (this.state.activeTab === 4) { - currentTab = 'manageRisk'; - } else if (this.state.activeTab === 5) { currentTab = 'account'; } this.props.router.push(`/settings?${currentTab}`); @@ -167,7 +159,6 @@ class UserSettings extends Component { const { constants = {}, icons: ICONS, - totalAsset, themeOptions, } = this.props; const formValues = generateFormValues({ @@ -187,7 +178,6 @@ class UserSettings extends Component { DEFAULT_TOGGLE_OPTIONS ); const audioFormValues = generateAudioCueFormValues(DEFAULT_TOGGLE_OPTIONS); - const warningFormValues = generateWarningFormValues(DEFAULT_TOGGLE_OPTIONS); let audioFormInitialValues = { all: true, @@ -314,35 +304,6 @@ class UserSettings extends Component { /> ), }, - { - title: isMobile ? ( - - ) : ( - // - - {STRINGS['USER_SETTINGS.TITLE_MANAGE_RISK']} - - ), - content: ( - this.onSubmitSettings(formProps, 'risk')} - formFields={warningFormValues} - initialValues={settings.risk} - ICONS={ICONS} - /> - ), - }, { title: isMobile ? ( ({ price: state.orderbook.price, //orders: state.order.activeOrders, constants: state.app.constants, - totalAsset: state.asset.totalAsset, features: state.app.features, }); diff --git a/web/src/containers/Wallet/AssetsBlock.js b/web/src/containers/Wallet/AssetsBlock.js index 3ce09432b3..98fd41f932 100644 --- a/web/src/containers/Wallet/AssetsBlock.js +++ b/web/src/containers/Wallet/AssetsBlock.js @@ -416,6 +416,10 @@ const AssetsBlock = ({ balance_history_config?.active && Number(userPL?.['7d']?.total || 0) !== 0 && (
{ + handleBalanceHistory(true); + }} + style={{ cursor: 'pointer' }} className={ Number(userPL?.['7d']?.total || 0) === 0 ? 'profitNeutral' @@ -466,7 +470,7 @@ const AssetsBlock = ({ {!isUpgrade && balance_history_config?.active && historyData.length > 1 ? ( -
+
{' '}
+
handleBalanceHistory(true)} + > + + {STRINGS.formatString( + STRINGS['PROFIT_LOSS.VIEW_MORE'], + '>' + )} + +
) : ( !plLoading && @@ -517,6 +532,7 @@ const AssetsBlock = ({ placeHolder={`${STRINGS['WALLET_ASSETS_SEARCH_TXT']}...`} handleSearch={handleSearch} showCross + isFocus={true} />
@@ -706,7 +722,7 @@ const AssetsBlock = ({ />
- {!isMobile && ( + {
- )} + } {/* {hasEarn && ( - + - - - - - ); +
+ +
{coin.display_name}
+
+ + + + + + + ); + } + return null; })} ); @@ -516,13 +529,19 @@ const ProfitLossSection = ({ return sourceAmount; }; + + const getPeriod = (day) => { + if (day === 7) { + return '7d'; + } else if (day === 30) { + return '1m'; + } else return '3m'; + }; return ( -
+
{customDateModal()} +
-
-
-
- - {STRINGS['PROFIT_LOSS.WALLET_PERFORMANCE_TITLE']} - -
-
- - {STRINGS['PROFIT_LOSS.WALLET_PERFORMANCE_DESCRIPTION']} - -
-
-
-
- - {STRINGS['PROFIT_LOSS.EST_TOTAL_BALANCE']} - {' '} - {moment(latestBalance?.created_at).format('DD/MMM/YYYY')} -
-
- {balance_history_config?.currency?.toUpperCase() || 'USDT'}{' '} - {getSourceDecimals( - balance_history_config?.currency || 'usdt', - latestBalance?.total - ) - ?.toString() - .replace(/\B(?=(\d{3})+(?!\d))/g, ',') || '0'} -
+ +
0 - ? 'profitPositive' - : 'profitNegative' - } - > - - {STRINGS['PROFIT_LOSS.PL_7_DAY']} - {' '} - {Number(userPL?.['7d']?.total || 0) > 0 ? '+' : ' '} - {''} - {getSourceDecimals( - balance_history_config?.currency || 'usdt', - userPL?.['7d']?.total - ) - ?.toString() - .replace(/\B(?=(\d{3})+(?!\d))/g, ',') || '0'} - {userPL?.['7d']?.totalPercentage - ? ` (${userPL?.['7d']?.totalPercentage}%) ` - : ' '} - {balance_history_config?.currency?.toUpperCase() || 'USDT'} -
-
-
- -
- - - - -
- -
- -
- - {currentBalance && ( - <> -
-
-
- - {STRINGS['PROFIT_LOSS.WALLET_BALANCE']} - -
-
- - {STRINGS['PROFIT_LOSS.WALLET_BALANCE_DESCRIPTION_1']} - {' '} - {moment(currentBalance?.created_at).format('DD/MMM/YYYY')}. - - {STRINGS['PROFIT_LOSS.WALLET_BALANCE_DESCRIPTION_2']} - -
-
-
+
- - {STRINGS['PROFIT_LOSS.EST_TOTAL_BALANCE']} - {' '} - {moment(currentBalance?.created_at).format('DD/MMM/YYYY')} -
-
- {balance_history_config?.currency?.toUpperCase() || 'USDT'}{' '} - {getSourceDecimals( - balance_history_config?.currency || 'usdt', - currentBalance?.total - ) - ?.toString() - .replace(/\B(?=(\d{3})+(?!\d))/g, ',') || '0'} +
+ + {STRINGS['PROFIT_LOSS.WALLET_PERFORMANCE_TITLE']} + +
+
+ + {STRINGS['PROFIT_LOSS.WALLET_PERFORMANCE_DESCRIPTION']} + +
- - {STRINGS['PROFIT_LOSS.DATE_SELECT']} - - : + + {STRINGS['PROFIT_LOSS.EST_TOTAL_BALANCE']} + {' '} + {moment(latestBalance?.created_at).format('DD/MMM/YYYY')} +
+
+ {balance_history_config?.currency?.toUpperCase() || 'USDT'}{' '} + {getSourceDecimals( + balance_history_config?.currency || 'usdt', + latestBalance?.total + ) + ?.toString() + .replace(/\B(?=(\d{3})+(?!\d))/g, ',') || '0'}
- { - return ( - current && - (current < - moment( - balance_history_config?.date_enabled, - 'YYYY-MM-DD' - ) || - current.isAfter(moment())) - ); - }} - style={{ - width: '12.5em', - fontSize: '1em', - }} - onChange={(date, dateString) => { - if (!dateString) return; - setSelectedCustomDate(date); - fetchBalanceHistory({ - start_date: moment(dateString) - .startOf('day') - .toISOString(), - end_date: moment(dateString).endOf('day').toISOString(), - limit: 1, - }) - .then((response) => { - let balance = response?.data?.[0]; - - if (balance) setCurrentBalance(balance); - else { - message.error('Balance not found'); - } - }) - .catch((error) => { - message.error('Something went wrong'); - }); - }} - format={'YYYY/MM/DD'} - /> + +
0 + ? 'profitPositive' + : 'profitNegative' + } + > + {' '} + {currentDay + ' '} + + {STRINGS['PROFIT_LOSS.PL_DAYS']} + {' '} + {Number(userPL?.[getPeriod(currentDay)]?.total || 0) > 0 + ? '+' + : ' '} + {''} + {getSourceDecimals( + balance_history_config?.currency || 'usdt', + userPL?.[getPeriod(currentDay)]?.total + ) + ?.toString() + .replace(/\B(?=(\d{3})+(?!\d))/g, ',') || '0'} + {userPL?.[getPeriod(currentDay)]?.totalPercentage + ? ` (${ + userPL?.[getPeriod(currentDay)]?.totalPercentage + }%) ` + : ' '} + {balance_history_config?.currency?.toUpperCase() || + 'USDT'} +
+
-
-
-
{markets.length > 1 ? ( )} {STRINGS['CURRENCY_WALLET.ABOUT']} {currency.toUpperCase()} @@ -336,7 +336,7 @@ class Wallet extends Component { > {currency.toUpperCase()} , - + here )} diff --git a/web/src/containers/Wallet/HeaderSection.js b/web/src/containers/Wallet/HeaderSection.js index 960e589b78..676203cdf2 100644 --- a/web/src/containers/Wallet/HeaderSection.js +++ b/web/src/containers/Wallet/HeaderSection.js @@ -12,7 +12,7 @@ const HeaderSection = ({ icons: ICONS }) => {
- + {STRINGS['ACCORDIAN.ACCORDIAN_INFO']} @@ -20,7 +20,7 @@ const HeaderSection = ({ icons: ICONS }) => {
- + {STRINGS['ACCORDIAN.ACCORDIAN_HISTORY']} diff --git a/web/src/containers/Wallet/ProfitLossSection.js b/web/src/containers/Wallet/ProfitLossSection.js index 8133397b64..4ab0fc77b5 100644 --- a/web/src/containers/Wallet/ProfitLossSection.js +++ b/web/src/containers/Wallet/ProfitLossSection.js @@ -6,14 +6,14 @@ import HighchartsReact from 'highcharts-react-official'; // eslint-disable-next-line import { Coin, EditWrapper } from 'components'; import { Link } from 'react-router'; -import { Button, Spin, DatePicker, message, Modal } from 'antd'; +import { Button, Spin, DatePicker, message, Modal, Tabs } from 'antd'; import { fetchBalanceHistory, fetchPlHistory } from './actions'; import BigNumber from 'bignumber.js'; import moment from 'moment'; import STRINGS from 'config/localizedStrings'; import { CloseOutlined } from '@ant-design/icons'; import './_ProfitLoss.scss'; - +const TabPane = Tabs.TabPane; const ProfitLossSection = ({ coins, balance_history_config, @@ -49,6 +49,7 @@ const ProfitLossSection = ({ const [graphData, setGraphData] = useState([]); const [customDate, setCustomDate] = useState(false); const [customDateValues, setCustomDateValues] = useState(); + const [loadingPnl, setLoadingPnl] = useState(false); const options = { chart: { @@ -65,7 +66,9 @@ const ProfitLossSection = ({ labels: { formatter: (item) => { const color = - graphData?.[current || 0]?.[0] === item.value ? '#5D63FF' : 'white'; + graphData?.[current || 0]?.[0] === item.value + ? '#5D63FF' + : 'date-text'; const fontWeight = graphData?.[current || 0]?.[0] === item.value ? 'bold' : 'normal'; return `${item.value}`; @@ -120,8 +123,10 @@ const ProfitLossSection = ({ firstRender.current = false; } else { setIsLoading(true); + setLoadingPnl(true); fetchPlHistory().then((res) => { setUserPL(res); + setLoadingPnl(false); }); requestHistory(queryFilters.page, queryFilters.limit); } @@ -132,8 +137,10 @@ const ProfitLossSection = ({ if (firstRender.current) { firstRender.current = false; } else { - fetchPlHistory().then((res) => { + setLoadingPnl(true); + fetchPlHistory({ period: currentDay }).then((res) => { setUserPL(res); + setLoadingPnl(false); }); requestHistory(queryFilters.page, queryFilters.limit); } @@ -282,42 +289,48 @@ const ProfitLossSection = ({ .decimalPlaces(decimalPointNative) .toNumber(); - return ( -
- -
0) { + return ( +
+ - -
{coin.display_name}
- - -
- {sourceAmount} {coin.symbol.toUpperCase()} - - = {sourceAmountNative}{' '} - {balance_history_config?.currency?.toUpperCase() || 'USDT'} -
+ {sourceAmount} {coin.symbol.toUpperCase()} + + = {sourceAmountNative}{' '} + {balance_history_config?.currency?.toUpperCase() || 'USDT'} +
- - - - - - - - - {getRows(coins)} - -
- - {STRINGS['PROFIT_LOSS.ASSET_NAME']} - - - - {STRINGS['PROFIT_LOSS.BALANCE_AMOUNT']} +
+ + + + {/* */} +
+ +
+ +
+ + + {currentBalance && ( + +
+
+
+
+ + {STRINGS['PROFIT_LOSS.WALLET_BALANCE']} -
- - {STRINGS['PROFIT_LOSS.VALUE']} + +
+ + {STRINGS['PROFIT_LOSS.WALLET_BALANCE_DESCRIPTION_1']} + {' '} + {moment(currentBalance?.created_at).format('DD/MMM/YYYY')} + . + + {STRINGS['PROFIT_LOSS.WALLET_BALANCE_DESCRIPTION_2']} -
-
- - )} +
+
+
+
+ + {STRINGS['PROFIT_LOSS.EST_TOTAL_BALANCE']} + {' '} + {moment(currentBalance?.created_at).format('DD/MMM/YYYY')} +
+
+ {balance_history_config?.currency?.toUpperCase() || + 'USDT'}{' '} + {getSourceDecimals( + balance_history_config?.currency || 'usdt', + currentBalance?.total + ) + ?.toString() + .replace(/\B(?=(\d{3})+(?!\d))/g, ',') || '0'} +
+
+
+ + {STRINGS['PROFIT_LOSS.DATE_SELECT']} + + : +
+ { + return ( + current && + (current < + moment( + balance_history_config?.date_enabled, + 'YYYY-MM-DD' + ) || + current.isAfter(moment())) + ); + }} + style={{ + width: '12.5em', + fontSize: '1em', + }} + onChange={(date, dateString) => { + if (!dateString) return; + setSelectedCustomDate(date); + fetchBalanceHistory({ + start_date: moment(dateString) + .startOf('day') + .toISOString(), + end_date: moment(dateString) + .endOf('day') + .toISOString(), + limit: 1, + }) + .then((response) => { + let balance = response?.data?.[0]; + + if (balance) setCurrentBalance(balance); + else { + message.error('Balance not found'); + } + }) + .catch((error) => { + message.error('Something went wrong'); + }); + }} + format={'YYYY/MM/DD'} + /> +
+
+
+ +
+ + + + + + + + + + {getRows(coins)} + +
+ + {STRINGS['PROFIT_LOSS.ASSET_NAME']} + + + + {STRINGS['PROFIT_LOSS.BALANCE_AMOUNT']} + + + + {STRINGS['PROFIT_LOSS.VALUE']} + +
+
+
+ + )} +
); diff --git a/web/src/containers/Wallet/_ProfitLoss.scss b/web/src/containers/Wallet/_ProfitLoss.scss index b199b59b7a..088cdabaa0 100644 --- a/web/src/containers/Wallet/_ProfitLoss.scss +++ b/web/src/containers/Wallet/_ProfitLoss.scss @@ -42,6 +42,32 @@ color: '#ccc'; } +.profit-loss-wrapper { + .view-more-content { + display: none; + cursor: pointer; + } + .highChartColor { + opacity: 0.5; + } +} + +.profit-loss-wrapper:hover { + transform: translateY(-5%); + transition-timing-function: ease-in; + transition: 0.2s; + .highChartColor { + opacity: unset; + } + .view-more-content { + display: block; + text-align: end; + } + .view-more-content :first-child:first-child { + text-decoration: underline; + } +} + .profit_block-table { width: 100%; // font-size: $font-size-subtext; @@ -93,4 +119,7 @@ min-width: 150px !important; direction: ltr; } + .date-text { + color: var(--labels_important-active-labels-text-graphics); + } } diff --git a/web/src/containers/Wallet/_Wallet.scss b/web/src/containers/Wallet/_Wallet.scss index e1a4dc92d0..844e5837df 100644 --- a/web/src/containers/Wallet/_Wallet.scss +++ b/web/src/containers/Wallet/_Wallet.scss @@ -603,7 +603,7 @@ $header-margin: 2rem; width: 300px !important; } .currency-wallet-wrapper { - width: 60%; + width: 80%; margin-left: auto; margin-right: auto; font-family: $raleway--font-family; diff --git a/web/src/containers/Wallet/components/CurrencySlider.js b/web/src/containers/Wallet/components/CurrencySlider.js index ab3501a6e9..34130ff106 100644 --- a/web/src/containers/Wallet/components/CurrencySlider.js +++ b/web/src/containers/Wallet/components/CurrencySlider.js @@ -286,6 +286,9 @@ class CurrencySlider extends Component {
{ + this.props.handleBalanceHistory(true); + }} className={ Number(this.state.userPL?.['7d']?.total || 0) === 0 ? 'profitNeutral' @@ -298,6 +301,7 @@ class CurrencySlider extends Component { display: 'flex', justifyContent: 'center', fontSize: '1.5rem', + cursor: 'pointer', }} > diff --git a/web/src/containers/Withdraw/Fiat/FormUtils.js b/web/src/containers/Withdraw/Fiat/FormUtils.js index bd4ded35e2..9976a6be15 100644 --- a/web/src/containers/Withdraw/Fiat/FormUtils.js +++ b/web/src/containers/Withdraw/Fiat/FormUtils.js @@ -170,15 +170,6 @@ export const generateFormValues = ( }; } - fields.captcha = { - type: 'captcha', - language, - theme, - validate: [required], - strings: STRINGS, - constants, - }; - return fields; }; diff --git a/web/src/containers/Withdraw/Fiat/WithdrawalForm.js b/web/src/containers/Withdraw/Fiat/WithdrawalForm.js index 2503327c90..249f9bfe0f 100644 --- a/web/src/containers/Withdraw/Fiat/WithdrawalForm.js +++ b/web/src/containers/Withdraw/Fiat/WithdrawalForm.js @@ -6,8 +6,7 @@ import { formValueSelector, reset, SubmissionError, - stopSubmit, - change, + stopSubmit } from 'redux-form'; import renderFields from 'components/Form/factoryFields'; import ReviewModalContent from './ReviewModalContent'; @@ -120,9 +119,6 @@ class Index extends Component { : err.message, ...err.errors, }; - errorTimeOut = setTimeout(() => { - this.props.dispatch(change(FORM_NAME, 'captcha', '')); - }, 5000); this.props.onSubmitFail(err.errors || err, this.props.dispatch); this.onCloseDialog(); this.props.dispatch(stopSubmit(FORM_NAME, error)); @@ -179,9 +175,6 @@ class Index extends Component { : err.message, ...err.errors, }; - errorTimeOut = setTimeout(() => { - this.props.dispatch(change(FORM_NAME, 'captcha', '')); - }, 5000); this.props.onSubmitFail(err.errors, this.props.dispatch); this.onCloseDialog(); this.props.dispatch(stopSubmit(FORM_NAME, error)); @@ -193,9 +186,6 @@ class Index extends Component { ? err.response.data.message : err.message, }; - errorTimeOut = setTimeout(() => { - this.props.dispatch(change(FORM_NAME, 'captcha', '')); - }, 5000); this.props.onSubmitFail(error, this.props.dispatch); this.onCloseDialog(); this.props.dispatch(stopSubmit(FORM_NAME, error)); @@ -371,7 +361,7 @@ const FiatWithdrawalForm = reduxForm({ })(Index); const mapStateToProps = (state) => ({ - data: selector(state, 'bank', 'amount', 'fee', 'captcha'), + data: selector(state, 'bank', 'amount', 'fee'), fiat_fees: state.app.constants.fiat_fees, }); diff --git a/web/src/containers/Withdraw/form.js b/web/src/containers/Withdraw/form.js index ec51f8fa09..71a9f5e793 100644 --- a/web/src/containers/Withdraw/form.js +++ b/web/src/containers/Withdraw/form.js @@ -5,8 +5,7 @@ import { formValueSelector, reset, SubmissionError, - stopSubmit, - change, + stopSubmit } from 'redux-form'; import math from 'mathjs'; import { Button, Dialog, OtpForm, Loader, SmartTarget } from 'components'; @@ -147,9 +146,6 @@ class Form extends Component { }) .catch((err) => { const error = { _error: err.message, ...err.errors }; - errorTimeOut = setTimeout(() => { - this.props.dispatch(change(FORM_NAME, 'captcha', '')); - }, 5000); this.props.onSubmitFail(err.errors || err, this.props.dispatch); this.onCloseDialog(); this.props.dispatch(stopSubmit(FORM_NAME, error)); @@ -183,9 +179,6 @@ class Form extends Component { if (err instanceof SubmissionError) { if (err.errors && !err.errors.otp_code) { const error = { _error: err.message, ...err.errors }; - errorTimeOut = setTimeout(() => { - this.props.dispatch(change(FORM_NAME, 'captcha', '')); - }, 5000); this.props.onSubmitFail(err.errors, this.props.dispatch); this.onCloseDialog(); this.props.dispatch(stopSubmit(FORM_NAME, error)); @@ -193,9 +186,6 @@ class Form extends Component { throw err; } else { const error = { _error: err.message }; - errorTimeOut = setTimeout(() => { - this.props.dispatch(change(FORM_NAME, 'captcha', '')); - }, 5000); this.props.onSubmitFail(error, this.props.dispatch); this.onCloseDialog(); this.props.dispatch(stopSubmit(FORM_NAME, error)); @@ -340,7 +330,6 @@ const mapStateToForm = (state) => ({ 'address', 'destination_tag', 'amount', - 'captcha', 'fee', 'fee_coin', 'email', diff --git a/web/src/containers/Withdraw/formUtils.js b/web/src/containers/Withdraw/formUtils.js index 3c1cd8ad1a..ed54afe795 100644 --- a/web/src/containers/Withdraw/formUtils.js +++ b/web/src/containers/Withdraw/formUtils.js @@ -11,7 +11,6 @@ import { import STRINGS from 'config/localizedStrings'; import { STATIC_ICONS } from 'config/icons'; import { DEFAULT_COIN_DATA } from 'config/constants'; -import { getLanguage } from 'utils/string'; import { getTheme } from 'utils/theme'; import { toFixed } from 'utils/currency'; import { getDecimals } from 'utils/utils'; @@ -60,7 +59,10 @@ export const generateInitialValues = ( const roundedMarkup = new BigNumber(feeMarkup) .decimalPlaces(decimalPoint) .toNumber(); - initialValues.fee += roundedMarkup; + + initialValues.fee = new BigNumber(initialValues.fee || 0) + .plus(roundedMarkup || 0) + .toNumber(); } initialValues.destination_tag = ''; @@ -378,12 +380,5 @@ export const generateFormValues = ( }; } - fields.captcha = { - type: 'captcha', - language: getLanguage(), - theme: theme, - validate: [required], - }; - return fields; }; diff --git a/web/src/index.css b/web/src/index.css index 774f4098bf..95f225663a 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -845,16 +845,17 @@ table th { .direction_rtl .user-limits .user-limits-table .table-icon { padding-left: 0.5rem; } -.user_security-wrapper { +.user_security-wrapper, +.user_security-wrapper-disabled { display: flex; flex-direction: column; justify-content: center; align-items: center; - margin-top: 30px; padding: 30px; background-color: var(--base_wallet-sidebar-and-popup); } - .user_security-wrapper > *:not(:last-child) { - margin-bottom: 2rem; } + +.user_security-wrapper-disabled { + margin-top: 3%; } .OTP_Success { width: 30px; @@ -868,8 +869,97 @@ table th { flex-direction: column; min-width: 30rem; width: 30rem; } + .otp_form-wrapper .step-one-title-wrapper { + font-family: 'Open Sans'; } + .otp_form-wrapper .step-one-pop-up .store-wrapper { + cursor: pointer; + padding: 2%; + width: 30%; + color: #000000; + font-size: 10px; + border-radius: 10px; + border: 1px solid var(--calculated_important-border); + background-color: #f5f5f7; } + .otp_form-wrapper .step-one-pop-up .store-wrapper .store-icons { + width: 25%; } + .otp_form-wrapper .step-one-pop-up .download-content-title { + font-weight: bold; + margin: 2% 0; } + .otp_form-wrapper .step-one-pop-up .store-icons-wrapper { + display: flex; + justify-content: space-evenly; + margin-bottom: 5%; } + .otp_form-wrapper .step-one-pop-up .store-icons-wrapper .play-store-field { + cursor: pointer; + padding: 2%; + width: 100%; + height: 50%; + color: var(--labels_important-active-labels-text-graphics); + font-size: 12px; + border-radius: 10px; + background-color: var(--labels_important-active-labels-text-graphics); } + .otp_form-wrapper .step-one-pop-up .app-info { + margin-bottom: 5%; } + .otp_form-wrapper .step-one-pop-up .uninstall-info { + color: #ffbb00; + font-weight: bold; } + .otp_form-wrapper .step-one-pop-up .note-txt, + .otp_form-wrapper .step-two-pop-up .note-txt { + color: #e59b07; } + .otp_form-wrapper .step-one-pop-up .otp-border, + .otp_form-wrapper .step-one-pop-up .otp-border-footer, + .otp_form-wrapper .step-two-pop-up .otp-border, + .otp_form-wrapper .step-two-pop-up .otp-border-footer { + margin-bottom: 5%; + border-bottom: 1px solid var(--calculated_important-border); + width: 100%; } + .otp_form-wrapper .step-one-pop-up .otp-border-footer, + .otp_form-wrapper .step-two-pop-up .otp-border-footer { + width: 25%; } + .otp_form-wrapper .step-one-pop-up .btn-wrapper, + .otp_form-wrapper .step-two-pop-up .btn-wrapper { + display: flex; + justify-content: space-between; + margin-top: 8%; } + .otp_form-wrapper .step-one-pop-up .btn-wrapper .back-btn, + .otp_form-wrapper .step-one-pop-up .btn-wrapper .proceed-btn, + .otp_form-wrapper .step-two-pop-up .btn-wrapper .back-btn, + .otp_form-wrapper .step-two-pop-up .btn-wrapper .proceed-btn { + background-color: var(--specials_buttons-links-and-highlights); + border: none; + width: 45%; + height: 40px; + text-align: center; + color: var(--labels_important-active-labels-text-graphics); } + .otp_form-wrapper .step-two-pop-up .btn-wrapper { + margin-top: 5% !important; } + .otp_form-wrapper .step-three-pop-up .verfication-field { + background-color: var(--base_wallet-sidebar-and-popup); + border: none; + border-bottom: 1px solid var(--labels_secondary-inactive-label-text-graphics); + border-image: initial; + width: 100%; + margin-top: 5px; } + .otp_form-wrapper .step-three-pop-up .warning-text { + color: var(--labels_secondary-inactive-label-text-graphics); + margin-top: 15%; } + .otp_form-wrapper .step-three-pop-up .btn-wrapper { + display: flex; + justify-content: space-between; + margin-top: 8%; } + .otp_form-wrapper .step-three-pop-up .back-btn, + .otp_form-wrapper .step-three-pop-up .proceed-btn, + .otp_form-wrapper .step-four-pop-up .back-btn, + .otp_form-wrapper .step-four-pop-up .proceed-btn { + background-color: var(--specials_buttons-links-and-highlights); + border: none; + width: 45%; + height: 40px; + text-align: center; + color: var(--labels_important-active-labels-text-graphics); } + .otp_form-wrapper .step-four-pop-up .back-btn { + width: 100%; } .otp_form-wrapper .otp_form-section-wrapper { - border-top: 1px solid var(--calculated_important-border); padding: 0.75rem 0; } .otp_form-wrapper .otp_form-section-wrapper .otp_form-section-content { margin: 1rem 0; } @@ -895,8 +985,8 @@ table th { .otp_form-wrapper .icon_title-wrapper .icon_title-text.title { font-size: 2rem; } .otp_form-wrapper .icon_title-wrapper .icon_title-svg { - width: 6.5rem; - height: 6.5rem; } + width: 5.5rem; + height: 5.5rem; } .otp_form-wrapper .icon_title-wrapper .icon_title-svg svg { width: 6.5rem; height: 6.5rem; } @@ -1007,6 +1097,17 @@ table th { .user-security-container .checkbutton-input-wrapper { margin-top: 0px !important; } +.otp-field-wrapper { + margin-top: 3%; } + .otp-field-wrapper .warning_text { + padding: 10px; + text-align: center; + background-color: var(--specials_notifications-alerts-warnings); + color: var(--labels_important-active-labels-text-graphics); } + .otp-field-wrapper .warning_text .improtant-text { + font-weight: bold; + text-decoration: underline; } + .layout-mobile .user_security-wrapper .warning_text { font-size: 13px !important; } @@ -1156,6 +1257,12 @@ table th { height: 3rem; width: 3rem; } +.errorField { + border-color: var(--specials_notifications-alerts-warnings) !important; } + +.errorText { + color: var(--specials_notifications-alerts-warnings) !important; } + .signup_success-wrapper { text-align: center; } .signup_success-wrapper .signup_success-content { @@ -1954,7 +2061,7 @@ table th { width: 300px !important; } .currency-wallet-wrapper { - width: 60%; + width: 80%; margin-left: auto; margin-right: auto; font-family: "Raleway"; } @@ -3548,8 +3655,7 @@ table th { font-size: 1.8rem; } .trade-details-wrapper .trade-details-content .trade_tabs-container { margin: 0; - padding-left: 80px; - width: 100% !important; } + width: auto !important; } .trade-details-wrapper .trade-details-content .main-coin-wrapper { opacity: 0.3; } .trade-details-wrapper .trade-details-content .fullname { @@ -3714,10 +3820,10 @@ table th { margin: 2dvh auto; } .risky-trade-disclaimer .extreme-volatility-msg { - color: #FFAA00; } + color: #ffaa00; } .risky-trade-disclaimer .disclaimer-checkbox span { - color: #fff; + color: var(--labels_important-active-labels-text-graphics); opacity: 0.7; } .risky-trade-disclaimer .disclaimer-checkbox.disclaimer-checkbox-selected span { @@ -5936,8 +6042,7 @@ table th { font-size: 1.8rem; } .trade-details-wrapper .trade-details-content .trade_tabs-container { margin: 0; - padding-left: 80px; - width: 100% !important; } + width: auto !important; } .trade-details-wrapper .trade-details-content .main-coin-wrapper { opacity: 0.3; } .trade-details-wrapper .trade-details-content .fullname { @@ -6134,7 +6239,7 @@ table th { z-index: -1; } .hollaex-token-wrapper .token-wrapper .hollaex-container .trade-details-wrapper .trade-details-content .trade_tabs-container { margin: 0 !important; - width: 100% !important; } + width: auto !important; } .hollaex-token-wrapper .token-wrapper .hollaex-container .trade-details-wrapper .f-size-16 { font-size: 16px; color: var(--labels_secondary-inactive-label-text-graphics); @@ -6144,7 +6249,7 @@ table th { font-weight: bold; } .hollaex-token-wrapper .token-wrapper .hollaex-container .trade-details-wrapper .fullname { color: var(--labels_secondary-inactive-label-text-graphics); - font-size: 0.65rem; + font-size: 1rem; white-space: nowrap; } .hollaex-token-wrapper .token-wrapper .hollaex-container .trade-details-wrapper .sub-title { color: var(--labels_secondary-inactive-label-text-graphics); @@ -6225,6 +6330,8 @@ table th { width: 100%; padding: 0.5rem 0.5rem 0.8rem 0.5rem; justify-content: space-around; } + .sidebar-bottom-wrapper .sidebar-bottom-button-account .sidebar-bottom-icon svg g .assets-icon-tab { + opacity: 0.5; } .sidebar-bottom-wrapper .sidebar-bottom-button { justify-content: space-between; flex-direction: column; @@ -6237,13 +6344,13 @@ table th { .sidebar-bottom-wrapper .sidebar-bottom-button .sidebar-bottom-icon svg .quick-trade-tab-active, .sidebar-bottom-wrapper .sidebar-bottom-button .sidebar-bottom-icon svg .assets-icon-tab, .sidebar-bottom-wrapper .sidebar-bottom-button .sidebar-bottom-icon svg .onramp-icon { - fill: var(--calculated_base_top-bar-navigation_text-inactive); } + fill: var(--labels_secondary-inactive-label-text-graphics); } .sidebar-bottom-wrapper .sidebar-bottom-button .sidebar-bottom-icon svg .tab-wallet0, .sidebar-bottom-wrapper .sidebar-bottom-button .sidebar-bottom-icon svg .trade-active-2, .sidebar-bottom-wrapper .sidebar-bottom-button .sidebar-bottom-icon svg .chat-0, .sidebar-bottom-wrapper .sidebar-bottom-button .sidebar-bottom-icon svg .quick-trade-tab-active, .sidebar-bottom-wrapper .sidebar-bottom-button .sidebar-bottom-icon svg .assets-icon-tab { - stroke: var(--calculated_base_top-bar-navigation_text-inactive); } + stroke: var(--labels_secondary-inactive-label-text-graphics); } .sidebar-bottom-wrapper .sidebar-bottom-button .sidebar-bottom-icon svg .tab-wallet0, .sidebar-bottom-wrapper .sidebar-bottom-button .sidebar-bottom-icon svg .quick-trade-tab-active { fill: transparent; } @@ -6260,7 +6367,7 @@ table th { .sidebar-bottom-wrapper .sidebar-bottom-button.active .sidebar-bottom-icon svg .assets-icon-tab { stroke: var(--calculated_base_top-bar-navigation_text); } .sidebar-bottom-wrapper .sidebar-bottom-button .bottom-bar-text { - font-size: 10px; + font-size: 9px; text-align: center; margin-top: -17px; font-weight: lighter; @@ -6326,61 +6433,10 @@ table th { .sidebar-bottom-button-trade .edit-wrapper__container { height: 100%; } -@media screen and (max-width: 375px) { - .mobile-trade-wrapper .pro-trade-footer-icon { - height: 50%; - margin-top: 20% !important; } - .mobile-trade-wrapper .quick-trade-footer-icon { - margin-top: 20% !important; } - .mobile-trade-wrapper .quick-trade-field, - .mobile-trade-wrapper .pro-trade-field { - width: 65px !important; - height: 65px !important; } - .sidebar-bottom-button-trade { - height: 130%; - width: 20% !important; } } - -@media screen and (max-width: 450px) and (min-width: 375px) { - .mobile-trade-wrapper { - width: 50%; } - .mobile-trade-wrapper .pro-trade-footer-icon { - height: 50%; - margin-top: 18% !important; } - .mobile-trade-wrapper .quick-trade-footer-icon { - height: 40%; - margin-top: 20% !important; } - .mobile-trade-wrapper .quick-trade-field, - .mobile-trade-wrapper .pro-trade-field { - width: 75px !important; - height: 75px !important; } - .sidebar-bottom-button-trade { - height: 135%; - width: 18% !important; } } - -@media screen and (max-width: 550px) and (min-width: 450px) { - .mobile-trade-wrapper { - width: 50%; } - .mobile-trade-wrapper .quick-trade-field, - .mobile-trade-wrapper .pro-trade-field { - width: 85px !important; - height: 85px !important; } - .mobile-trade-wrapper .pro-trade-footer-icon { - height: 40%; - margin-top: 25% !important; } - .mobile-trade-wrapper .quick-trade-footer-icon { - height: 35%; - margin-top: 25% !important; } } - @media screen and (max-height: 600px) { .sidebar-bottom-icon svg { stroke: none !important; } } -@media screen and (max-width: 768px) { - .mobile-trade-wrapper .quick-trade-field, - .mobile-trade-wrapper .pro-trade-field { - width: 100px; - height: 100px; } } - .tab_controller-title { display: flex; align-items: center; @@ -7049,6 +7105,8 @@ table th { .market-bar .selector-trigger.narrow { min-width: 14rem; max-width: 14rem; } + .market-bar .fav-price-label { + color: var(--labels_important-active-labels-text-graphics) !important; } .market-bar .app_bar-currency-txt { font-weight: 600; } .market-bar .app-price-diff-down { @@ -8436,13 +8494,17 @@ table th { border: 1px solid var(--labels_secondary-inactive-label-text-graphics); background: var(--labels_secondary-inactive-label-text-graphics); } .mainContainer .active { - border: 1px solid var(--labels_secondary-inactive-label-text-graphics) !important; } + border: 2px solid var(--labels_secondary-inactive-label-text-graphics) !important; } .mainContainer .error { border: 1px solid var(--specials_notifications-alerts-warnings) !important; } - .mainContainer .group { + .mainContainer .leftGroup, + .mainContainer .rightGroup { padding: 1rem; } - .mainContainer .group input { + .mainContainer .leftGroup input, + .mainContainer .rightGroup input { margin: 0 0.25rem; } + .mainContainer .leftGroup .error:first-child { + border: 2px solid var(--specials_notifications-alerts-warnings) !important; } .input_icon { margin-right: 0.25rem; @@ -8839,6 +8901,10 @@ table th { .dialog-content.bottom { padding-bottom: 2.5rem; } +.cancel-withdraw-pop-up .ReactModal__Content { + width: 25% !important; + padding: 0rem 2.5rem 2.5rem 2.5rem !important; } + .ReactModal__Content { padding: 2.5rem !important; top: auto !important; @@ -8864,6 +8930,11 @@ table th { .success_display-wrapper > * { flex: 0; } +.success_disable-display-wrapper, +.success_active-display-wrapper { + min-width: 20rem; + min-height: 14rem; } + .success_display-content { height: auto; flex: 1; } @@ -9337,12 +9408,24 @@ table th { background-size: contain; background-position: center; background-repeat: no-repeat; } + .icon_title-wrapper .step-one-title-wrapper { + margin-top: unset; } .direction_ltr .icon_title-wrapper { font-family: "Raleway"; } -.fees_limits .icon_title-text.title { - font-size: 1.5rem !important; } +.fees_limits .fees-limits-title { + font-size: 1.5rem !important; + text-transform: capitalize; + padding-top: 0.25rem !important; } + +.cancel-widrawal-pop-up .icon_title-text.title { + font-size: 2rem !important; + text-transform: capitalize; + padding-top: 0.25rem !important; } + +.cancel-widrawal-pop-up:last-child { + margin-top: 2rem !important; } .layout-mobile .icon_title-wrapper .auth_logo-wrapper svg { height: 8rem !important; @@ -9635,7 +9718,8 @@ table th { font-size: 8px !important; } } .quick-trade-select-wrapper .input-group__coin-icons-wrap { - display: flex; } + display: flex; + pointer-events: none; } .input-group__container { border-radius: 4px; @@ -9817,18 +9901,22 @@ table th { top: 50% !important; } .quick_trade-container .active-input { color: var(--labels_important-active-labels-text-graphics) !important; } - .quick_trade-container .input-group__input, .quick_trade-container .active-input { + .quick_trade-container .input-group__input, + .quick_trade-container .active-input { text-align: right; color: var(--labels_secondary-inactive-label-text-graphics); font-size: 2.1rem; margin-left: 1rem; height: 40px; } - .quick_trade-container .input-group__input:focus, .quick_trade-container .input-group__input:active, .quick_trade-container .active-input:focus, .quick_trade-container .active-input:active { + .quick_trade-container .input-group__input:focus, .quick_trade-container .input-group__input:active, + .quick_trade-container .active-input:focus, + .quick_trade-container .active-input:active { background-color: var(--base_secondary-navigation-bar) !important; box-shadow: inset 1px 1px 3px #00000029; border-radius: 5px; border-bottom: 1px solid #ffffff; } - .quick_trade-container .input-group__input:hover, .quick_trade-container .active-input:hover { + .quick_trade-container .input-group__input:hover, + .quick_trade-container .active-input:hover { color: var(--labels_important-active-labels-text-graphics); } .quick_trade-container .field-error-content { display: block; } @@ -11215,6 +11303,12 @@ table th { .price-change .glance-up.entering:before, .price-change .glance-up.exiting:before { border-color: var(--calculated_trading_buying-related-text); } +.price-change .markets-drop-down { + display: flex; } + +.price-change .markets-drop-down.down:before { + margin: 10% 0 0 0; } + .hollaex-token-wrapper { font-family: 'Open Sans' !important; } diff --git a/web/src/index.js b/web/src/index.js index 5dd81916e7..59a68336fe 100644 --- a/web/src/index.js +++ b/web/src/index.js @@ -120,7 +120,6 @@ const getConfigs = async () => { features: { home_page = false } = {}, injected_values = [], injected_html = {}, - captcha = {}, defaults = {}, } = kitData; @@ -245,7 +244,6 @@ const getConfigs = async () => { const appConfigs = merge({}, defaultConfig, remoteConfigs, { coin_icons, - captcha, valid_languages, defaults, }); diff --git a/web/src/reducers/appReducer.js b/web/src/reducers/appReducer.js index d907b1938d..11fabc46c7 100644 --- a/web/src/reducers/appReducer.js +++ b/web/src/reducers/appReducer.js @@ -58,6 +58,8 @@ import { SET_EXPLORE_PLUGINS, OVERWRITE_CURRENCY_NAMES, SET_TRANSACTION_LIMITS, + SET_SELECTED_ACCOUNT, + SET_SELECTED_STEP, } from 'actions/appActions'; import { THEME_DEFAULT } from 'config/constants'; import { getLanguage } from 'utils/string'; @@ -160,6 +162,8 @@ const INITIAL_STATE = { is_descending: true, }, default_digital_assets_sort: DIGITAL_ASSETS_SORT.CHANGE, + selectedAccount: 1, + selectedStep: 0, }; const reducer = (state = INITIAL_STATE, { type, payload = {} }) => { @@ -705,6 +709,16 @@ const reducer = (state = INITIAL_STATE, { type, payload = {} }) => { pinned_assets: payload.pinned_assets, default_digital_assets_sort: payload.default_digital_assets_sort, }; + case SET_SELECTED_ACCOUNT: + return { + ...state, + selectedAccount: payload.selectedAccount, + }; + case SET_SELECTED_STEP: + return { + ...state, + selectedStep: payload.selectedStep, + }; default: return state; } diff --git a/web/src/routes.js b/web/src/routes.js index e47c1b0c03..f8dc69172a 100644 --- a/web/src/routes.js +++ b/web/src/routes.js @@ -396,7 +396,7 @@ export const generateRoutes = (routes = []) => { name="Fees and limits" component={FeesAndLimits} /> - + @@ -439,7 +439,7 @@ export const generateRoutes = (routes = []) => { component={QuickTrade} /> diff --git a/web/src/utils/wallet.js b/web/src/utils/wallet.js index b3e8e26734..5276596e27 100644 --- a/web/src/utils/wallet.js +++ b/web/src/utils/wallet.js @@ -38,6 +38,8 @@ export const getNetworkNameByKey = (network) => { return 'Solana'; case 'xlm': return 'Stellar'; + case 'ftm': + return 'Fantom'; default: return network.toUpperCase(); }