diff --git a/app/components/all.scss b/app/components/all.scss index f5a86f0..3a81d25 100644 --- a/app/components/all.scss +++ b/app/components/all.scss @@ -73,8 +73,9 @@ @import "./modules/nft/NFTCollections"; @import "./modules/nft/CreateNFTCollection"; @import "./modules/nft/IssueNFTToken"; +@import "./modules/nft/NFTAuction"; @import "./modules/nft/NFTTokens"; -@import "./modules/nft/NFTPlaceBet"; +@import "./modules/nft/NFTPlaceOfferBet"; @import "./modules/nft/NFTTokenSell"; @import "./modules/nft/NFTTokenTransfer"; diff --git a/app/components/elements/TimeExactWrapper.jsx b/app/components/elements/TimeExactWrapper.jsx new file mode 100644 index 0000000..fb7a045 --- /dev/null +++ b/app/components/elements/TimeExactWrapper.jsx @@ -0,0 +1,62 @@ +/* eslint react/prop-types: 0 */ +import React from 'react'; +import tt from 'counterpart' + +import Tooltip from 'app/components/elements/Tooltip'; + +const SECOND = 1000 +const MINUTE = 60*SECOND +const HOUR = 60*MINUTE +const DAY = 24*HOUR + +function formatTimeExact(dt, maxDepth = 2) { + const msec = +dt + const now = Date.now() + + const formatMsec = (ms, depth = 1, prev = null) => { + if (depth > maxDepth) return '' + if (ms >= DAY) { + const days = Math.floor(ms / DAY) + const remainder = ms % DAY + const res = days ? days + tt('time_exact_wrapper_jsx.days') : '' + return res + formatMsec(remainder, ++depth, DAY) + } else if (ms >= HOUR && (!prev || prev == DAY)) { + const hours = Math.floor(ms / HOUR) + const remainder = ms % HOUR + const res = hours ? hours + tt('time_exact_wrapper_jsx.hours') : '' + return res + formatMsec(remainder, ++depth, HOUR) + } else if (ms >= MINUTE && (!prev || prev == HOUR)) { + const minutes = Math.floor(ms / MINUTE) + const remainder = ms % MINUTE + const res = minutes ? minutes + tt('time_exact_wrapper_jsx.minutes') : '' + return res + formatMsec(remainder, ++depth, MINUTE) + } else if (!prev || prev == MINUTE) { + const secs = Math.floor(ms / SECOND) + return secs ? secs + tt('time_exact_wrapper_jsx.secs') : '' + } else { + return '' + } + } + + const deltaSign = now - msec + const delta = Math.abs(deltaSign) + + const result = formatMsec(delta) + return { + result, + ago: deltaSign > 0 + } +} + +export default class TimeExactWrapper extends React.Component { + render() { + let {date, className} = this.props + if (date && /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d$/.test(date)) { + date = date + 'Z' // Firefox really wants this Z (Zulu) + } + const dt = new Date(date) + return + {formatTimeExact(dt).result} + + } +} diff --git a/app/components/modules/nft/NFTAuction.jsx b/app/components/modules/nft/NFTAuction.jsx new file mode 100644 index 0000000..05c5b4e --- /dev/null +++ b/app/components/modules/nft/NFTAuction.jsx @@ -0,0 +1,248 @@ +import React, { Component, } from 'react' +import tt from 'counterpart' +import { connect, } from 'react-redux' +import CloseButton from 'react-foundation-components/lib/global/close-button' +import { Formik, Form, Field, ErrorMessage, } from 'formik' +import { api } from 'golos-lib-js' +import { validateAccountName, Asset, AssetEditor } from 'golos-lib-js/lib/utils' + +import AmountField from 'app/components/elements/forms/AmountField' +import AmountAssetField from 'app/components/elements/forms/AmountAssetField' +import LoadingIndicator from 'app/components/elements/LoadingIndicator' +import NFTSmallIcon from 'app/components/elements/nft/NFTSmallIcon' +import transaction from 'app/redux/Transaction' + +const isDev = () => { + return process.env.NODE_ENV === 'development' +} + +class NFTAuction extends Component { + state = { + auction: { + min_price: AssetEditor('0.000 GOLOS'), + expiration: isDev() ? 15 : 7 + } + } + + async componentDidMount() { + const isHidden = (sym) => { + return ($STM_Config.hidden_assets && $STM_Config.hidden_assets[sym]) + } + try { + const assets = {} + const data = await api.getAssetsAsync('', [], '', 5000, 'by_symbol_name', { system: true }) + for (const asset of data) { + asset.supply = Asset(asset.supply) + assets[asset.supply.symbol] = asset + } + this.setState({ + assets + }) + } catch (err) { + console.error(err) + } + } + + validate = (values) => { + const errors = {} + const { min_price } = values + if (min_price.asset.eq(0)) { + errors.min_price = tt('nft_token_sell_jsx.fill_price') + } + return errors + } + + setSubmitting = (submitting) => { + this.setState({ submitting }) + } + + getToken = () => { + const { nft_tokens, tokenIdx } = this.props + if (tokenIdx !== undefined) { + return nft_tokens.toJS().data[tokenIdx] + } + return this.props.token + } + + _onSubmit = async (values) => { + this.setSubmitting(true) + this.setState({ + errorMessage: '' + }) + + const { currentUser, onClose, } = this.props + const token = this.getToken() + const { token_id } = token + + const username = currentUser.get('username') + + let expirationSec = parseInt(values.expiration) + if (!isDev()) { + expirationSec *= 3600*24 + } + let gprops + try { + gprops = await api.getDynamicGlobalPropertiesAsync() + } catch (err) { + console.error(err) + alert('Error - blockchain unavailable') + return + } + let expiration = new Date(gprops.time) + expiration.setSeconds(expiration.getSeconds() + expirationSec) + + await this.props.auction(token_id, values.min_price.asset, expiration, username, () => { + this.props.onClose() + this.setSubmitting(false) + this.doNotRender = true + this.props.refetch() + }, (err) => { + console.error(err) + this.setSubmitting(false) + this.setState({ + errorMessage: err.toString() + }) + }) + } + + onCancelMouseDown = (e) => { + e.preventDefault() + this.setState({ + cancelMouseDown: true + }) + } + + onCancelMouseUp = (e) => { + e.preventDefault() + if (this.state.cancelMouseDown) { + this.props.onClose() + this.setState({ + cancelMouseDown: false + }) + } + } + + onMouseUp = (e) => { + /*e.preventDefault() + if (this.state.cancelMouseDown) { + this.props.onClose() + }*/ + } + + onAssetChange = (e, asset) => { + this.setState({ + currentBalance: asset.supply.clone() + }) + } + + _renderSubmittingIndicator = () => { + const { submitting } = this.state + + return submitting ? + + : null + } + + render() { + const { assets } = this.state + + if (this.doNotRender || !assets) { + return + } + + const { onClose, } = this.props + + const token = this.getToken() + + const { json_metadata, image } = token + + let data + if (json_metadata) { + data = JSON.parse(json_metadata) + } + data = data || {} // node allows to use '', null, object, or array + + const { errorMessage, submitting, currentBalance, } = this.state + + return
+ +

{tt('nft_tokens_jsx.start_auction')}

+
+ + {data.title} +
+ + {({ + handleSubmit, isValid, values, errors, touched, setFieldValue, handleChange, + }) => { + return ( +
+
+ {tt('nft_tokens_jsx.min_price')} +
+
+
+
+ + + + +
+ {errors.min_price &&
{errors.min_price}
} +
+
+
+ {tt('nft_tokens_jsx.auction_expiration')} +
+
+
+
+ + + {isDev() ? tt('nft_tokens_jsx.auction_expiration_dev') : tt('nft_tokens_jsx.auction_expiration2')} + +
+ {errors.expiration &&
{errors.expiration}
} +
+
+ {(errorMessage && errorMessage !== 'Canceled') ?
+
+
{errorMessage}
+
+
: null} +
+
+ + + {this._renderSubmittingIndicator()} +
+
+
+ )}}
+
+ } +} + +export default connect( + // mapStateToProps + (state, ownProps) => { + return { ...ownProps, + nft_tokens: state.global.get('nft_tokens'), + } + }, + + dispatch => ({ + }) +)(NFTAuction) diff --git a/app/components/modules/nft/NFTPlaceBet.scss b/app/components/modules/nft/NFTAuction.scss similarity index 89% rename from app/components/modules/nft/NFTPlaceBet.scss rename to app/components/modules/nft/NFTAuction.scss index feff4b1..5480972 100644 --- a/app/components/modules/nft/NFTPlaceBet.scss +++ b/app/components/modules/nft/NFTAuction.scss @@ -1,4 +1,4 @@ -.NFTPlaceBet { +.NFTAuction { .column { padding-left: 0rem; padding-right: 0rem; diff --git a/app/components/modules/nft/NFTPlaceBet.jsx b/app/components/modules/nft/NFTPlaceOfferBet.jsx similarity index 71% rename from app/components/modules/nft/NFTPlaceBet.jsx rename to app/components/modules/nft/NFTPlaceOfferBet.jsx index 4ec5a90..a54e3c4 100644 --- a/app/components/modules/nft/NFTPlaceBet.jsx +++ b/app/components/modules/nft/NFTPlaceOfferBet.jsx @@ -15,7 +15,7 @@ import transaction from 'app/redux/Transaction' import { generateOrderID } from 'app/utils/market/utils' import session from 'app/utils/session' -class NFTPlaceBet extends Component { +class NFTPlaceOfferBet extends Component { state = { order: { price: AssetEditor('0.000 GOLOS') @@ -28,30 +28,51 @@ class NFTPlaceBet extends Component { } try { let assets = {} - let currentBalance + let currentBalance, price const username = session.load().currentName if (username) { - let bals = await api.getAccountsBalances([username], { system: true }) - bals = bals[0] - if (bals['GOLOS']) { - assets['GOLOS'] = { supply: Asset(bals['GOLOS'].balance) } - } - if (bals['GBG']) { - assets['GBG'] = { supply: Asset(bals['GBG'].balance) } - } - for (const [sym, obj] of Object.entries(bals)) { - if (!isHidden(sym) && sym !== 'GOLOS' && sym !== 'GBG') { - assets[sym] = { supply: Asset(obj.balance) } + if (this.isBet()) { + const { minPrice } = this.props + const { symbol } = minPrice + let bals = await api.getAccountsBalances([username], { symbols: [symbol] }) + bals = bals[0] + let supply + if (bals[symbol]) { + supply = Asset(bals[symbol].balance) + } else { + supply = minPrice.clone() + supply.amount = '0' + } + assets[symbol] = { supply } + currentBalance = supply.clone() + } else { + let bals = await api.getAccountsBalances([username], { system: true }) + bals = bals[0] + if (bals['GOLOS']) { + assets['GOLOS'] = { supply: Asset(bals['GOLOS'].balance) } + } + if (bals['GBG']) { + assets['GBG'] = { supply: Asset(bals['GBG'].balance) } + } + for (const [sym, obj] of Object.entries(bals)) { + if (!isHidden(sym) && sym !== 'GOLOS' && sym !== 'GBG') { + assets[sym] = { supply: Asset(obj.balance) } + } + } + for (const [sym, obj] of Object.entries(assets)) { + currentBalance = obj.supply.clone() + break } } - for (const [sym, obj] of Object.entries(assets)) { - currentBalance = obj.supply.clone() - break - } + price = currentBalance.clone() + price.amount = '0' } this.setState({ assets, - currentBalance + currentBalance, + order: { + price: AssetEditor(price) + } }) } catch (err) { console.error(err) @@ -66,6 +87,13 @@ class NFTPlaceBet extends Component { errors.price = tt('nft_token_sell_jsx.fill_price') } else if (currentBalance && price.asset.gt(currentBalance)) { errors.price = tt('g.invalid_amount') + } else { + const { minPrice } = this.props + if (minPrice && minPrice.gt(0)) { + if (price.asset.lt(minPrice)) { + errors.price = tt('nft_tokens_jsx.min_price') + ' ' + minPrice.floatString + } + } } return errors } @@ -94,7 +122,8 @@ class NFTPlaceBet extends Component { const username = currentUser.get('username') - await this.props.placeBet(token_id, values.price, name, username, () => { + const order_id = this.isBet() ? 0 : generateOrderID() + await this.props.placeBet(token_id, values.price, name, order_id, username, () => { this.props.onClose() this.setSubmitting(false) this.doNotRender = true @@ -146,6 +175,11 @@ class NFTPlaceBet extends Component { : null } + isBet = () => { + const { minPrice } = this.props + return !!minPrice + } + render() { const { assets } = this.state @@ -167,9 +201,11 @@ class NFTPlaceBet extends Component { const { errorMessage, submitting, currentBalance, } = this.state - return
+ const title = this.isBet() ? tt('nft_tokens_jsx.place_bet') : tt('nft_tokens_jsx.place_offer') + + return
-

{tt('nft_tokens_jsx.place_bet')}

+

{title}

{data.title} @@ -194,8 +230,9 @@ class NFTPlaceBet extends Component {
- + {this.isBet() ? + {currentBalance.symbol} : }
{errors.price &&
{errors.price}
} @@ -208,9 +245,9 @@ class NFTPlaceBet extends Component {
: null}
-
+
) } - bets = + offers =
{rows}
} else { return null } - const opened = this.isBets() + const opened = this.isOffers() - bets = - {bets} + offers = + {offers} } + return offers + } + + _renderBets = (token, isMy) => { + let bets + + const betObjs = this.state.bets + if (betObjs && betObjs.length) { + const rows = [] + for (let i = 0; i < betObjs.length; ++i) { + const bet = betObjs[i] + rows.push( + {Asset(bet.price).floatString} + + ) + } + offers = + {rows} +
+ } else { + return null + } + + bets = + {bets} + + return bets } + _popupVisibleRef = el => { + this._popupVisible = el + } + + _onAwayClick = e => { + if (!this._popupVisible || !this._popupVisible.contains(e.target)) { + setTimeout(() => { + if (!this._unmount) { + this.setState({ + showSellOptions: false, + }); + } + }, 50); + } + }; + + onSellClick = e => { + e.preventDefault() + this.setState({ + showSellOptions: !this.state.showSellOptions + }, + () => { + const { showSellOptions } = this.state; + + if (showSellOptions && !this._onAwayClickListen) { + window.addEventListener('mousedown', this._onAwayClick) + this._onAwayClickListen = true + } + }) + } + render() { if (!this.props.nft_token_loaded) { return @@ -353,17 +460,18 @@ class NFTTokenPage extends Component { const description = data.description || '' - const isMy = currentUser && currentUser.get('username') === token.owner + const username = currentUser && currentUser.get('username') + const isMy = username === token.owner - let my_bet = token.my_bet ? Asset(token.my_bet.price) : null - if (my_bet) { - const asset = assets[my_bet.symbol] + let my_offer = token.my_offer ? Asset(token.my_offer.price) : null + if (my_offer) { + const asset = assets[my_offer.symbol] let imageUrl if (asset) { imageUrl = getAssetMeta(asset).image_url } const cancelBet = (e) => { - this.props.cancelBet(e, token.my_bet, () => { + this.props.cancelBet(e, token.my_offer, () => { this.props.fetchState() }, (err) => { if (!err || err.toString() === 'Canceled') return @@ -371,15 +479,50 @@ class NFTTokenPage extends Component { alert(err.toString()) }) } - my_bet =
+ my_offer =
{imageUrl && {''}} - {tt('nft_tokens_jsx.you_bet_is') + my_bet.floatString} + {tt('nft_tokens_jsx.you_bet_is') + my_offer.floatString}
} + const { auction_min_price, is_auction } = token + + const sellItems = [ + {link: '#', label: tt('nft_tokens_jsx.sell_fix_price'), value: 'sell_fix_price', + onClick: this.showSell }, + ] + + let auction + if (!is_auction) { + sellItems.push( + {link: '#', label: tt('nft_tokens_jsx.start_auction'), value: 'start_auction', + onClick: this.showAuction }) + } else { + const cancelAuction = (e) => { + this.props.auction(token.token_id, Asset(0, 3, 'GOLOS'), new Date(0), username, () => { + this.props.fetchState() + }, (err) => { + if (!err || err.toString() === 'Canceled') return + console.error(err) + alert(err.toString()) + }) + } + auction =
+ + + {isMy ? : } +
+ } + + const { showSellOptions } = this.state + return
@@ -416,36 +559,43 @@ class NFTTokenPage extends Component { {this._renderOps(ops)} + {this._renderOffers(token, isMy)} {this._renderBets(token, isMy)} {!description ?
: null} {isMy ?
- {last_price} + {last_price || auction} {selling && } - {!selling && } - {!selling && } + {showSellOptions && + + } +
+ {!selling && !is_auction && } - {!selling && }
:
- {last_price} + {last_price || auction} {selling && } - {!my_bet && } {!selling && msgsHost() && {tt('nft_token_page_jsx.msg')}}
} - {my_bet} + {my_offer} + {last_price ? auction : null}
@@ -468,12 +618,23 @@ class NFTTokenPage extends Component { /> - - + + + + +
@@ -576,14 +737,14 @@ module.exports = { errorCallback })) }, - sellToken: (bet, currentUser, successCallback, errorCallback) => { + sellToken: (offer, currentUser, successCallback, errorCallback) => { const username = currentUser.get('username') const operation = { seller: username, - token_id: bet.token_id, - buyer: bet.owner, - order_id: bet.order_id, - price: bet.price + token_id: offer.token_id, + buyer: offer.owner, + order_id: offer.order_id, + price: offer.price } dispatch(transaction.actions.broadcastOperation({ @@ -593,6 +754,24 @@ module.exports = { successCallback, errorCallback })) + }, + auction: ( + token_id, min_price, expiration, username, successCallback, errorCallback + ) => { + const operation = { + owner: username, + token_id, + min_price: min_price.toString(), + expiration: expiration.toISOString().split('.')[0] + } + + dispatch(transaction.actions.broadcastOperation({ + type: 'nft_auction', + username, + operation, + successCallback, + errorCallback + })) } }) )(NFTTokenPage) diff --git a/app/components/pages/nft/NFTTokenPage.scss b/app/components/pages/nft/NFTTokenPage.scss index 0f6e488..e65a0f4 100644 --- a/app/components/pages/nft/NFTTokenPage.scss +++ b/app/components/pages/nft/NFTTokenPage.scss @@ -22,6 +22,9 @@ margin-right: 2px; } } + .my_auction { + padding-left: 5px; + } img { max-width: 320px; @@ -56,4 +59,19 @@ margin-top: -4px !important; margin-left: 10px !important; } + + .sell-button { + position: relative; + display: inline-block; + } + .Hint__content { + padding: 0px; + min-width: 50px; + .Hint__inner { + .VerticalMenu { + white-space: nowrap; + width: auto; + } + } + } } diff --git a/app/locales/en.json b/app/locales/en.json index 7e094fc..fd6f076 100644 --- a/app/locales/en.json +++ b/app/locales/en.json @@ -28,6 +28,12 @@ "quick_convert": "Quick Convert", "open_market": "Go To Market (DEX)" }, + "time_exact_wrapper_jsx": { + "days": " d ", + "hours": " h", + "minutes": " m ", + "secs": " s " + }, "g": { "golos_fest": "Golos.id News", "APP_NAME": "Golos", @@ -788,7 +794,16 @@ "buy_confirm2": " for ", "your_token": "Your token", "owner": "Owner", - "bets": "Bets" + "bets": "Bets", + "sell_fix_price": "Via fix price", + "start_auction": "Start auction", + "stop_auction": "Остановить аукцион", + "start": "Start", + "min_price": "Min bet:", + "auction_expiration": "Auction will end after:", + "auction_expiration2": "days", + "auction_expiration_dev": "secs", + "auction_hint": "User proposals will be canceled." }, "all_nft_page_jsx": { "title": "All NFT on the Golos", diff --git a/app/locales/ru-RU.json b/app/locales/ru-RU.json index 93ec882..bb29b05 100644 --- a/app/locales/ru-RU.json +++ b/app/locales/ru-RU.json @@ -143,6 +143,12 @@ "quick_convert": "Быстрый обмен", "open_market": "Перейти к бирже" }, + "time_exact_wrapper_jsx": { + "days": " дн. ", + "hours": " ч ", + "minutes": " мин ", + "secs": " сек " + }, "g": { "golos_fest": "Новости", "APP_NAME": "Голос", @@ -1133,16 +1139,28 @@ "cancel": "Отменить", "cancel_hint": "Отменить продажу", "place_bet": "Сделать ставку", + "place_offer": "Предложить цену", "selling_for": "Продается за ", "you_bet_is": "Ваша ставка - ", "has_bets": "Ставки", + "has_offers": "Предложения", "buy": "Купить", "buy2": "Купить за ", "buy_confirm": "Вы уверены, что хотите купить ", "buy_confirm2": " за ", "your_token": "Ваш токен", "owner": "Владелец", - "bets": "Ставки" + "offers": "Предложения", + "bets": "Ставки", + "sell_fix_price": "По фиксированной цене", + "start_auction": "Запустить аукцион", + "stop_auction": "Остановить аукцион", + "start": "Запустить", + "min_price": "Минимальная ставка:", + "auction_expiration": "Окончание аукциона - через:", + "auction_expiration2": "дней", + "auction_expiration_dev": "секунд", + "auction_hint": "Предложение о покупке, сделанные пользователями самостоятельно, будут отменены." }, "all_nft_page_jsx": { "title": "Все NFT на Golos", @@ -1163,6 +1181,8 @@ "bought": " купил токен", "placed_bet": " сделал ставку", "canceled_bet": " отменил ставку", + "placed_offer": " предложил цену", + "canceled_offer": " отменил предложение", "on": " на ", "owner_is": "Владелец - ", "burnt": "Этот NFT-токен существовал когда-то.", diff --git a/app/redux/FetchDataSaga.js b/app/redux/FetchDataSaga.js index 7eb9a54..0472320 100644 --- a/app/redux/FetchDataSaga.js +++ b/app/redux/FetchDataSaga.js @@ -15,6 +15,13 @@ import { getAllPairs } from 'app/utils/market/utils' import { parseNFTImage, NFTImageStub } from 'app/utils/NFTUtils' import session from 'app/utils/session' +function* markAuctions(tokens) { + for (const token of tokens) { + token.auction_min_price = Asset(token.auction_min_price) + token.is_auction = token.auction_min_price.gt(0) + } +} + function* fillNftCollectionImages(nft_collections) { const noImgColls = {} for (let i = 0; i < nft_collections.length; ++i) { @@ -361,6 +368,7 @@ export function* fetchState(location_change_action) { select_token_ids: [parts[1]], state: 'any' })) + yield markAuctions(state.nft_token) state.nft_token = state.nft_token[0] state.nft_token_loaded = true diff --git a/package.json b/package.json index 84e59a3..070711e 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "foundation-sites": "^6.4.3", "fs-extra": "^10.0.1", "git-rev-sync": "^3.0.2", - "golos-lib-js": "^0.9.60", + "golos-lib-js": "^0.9.65", "history": "^2.0.0-rc2", "immutable": "^3.8.2", "intl": "^1.2.5", diff --git a/yarn.lock b/yarn.lock index 3b85059..d9c6677 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2971,9 +2971,9 @@ core-js@^2.4.0, core-js@^2.5.0: integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== core-js@^3.17.3: - version "3.33.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.33.0.tgz#70366dbf737134761edb017990cf5ce6c6369c40" - integrity sha512-HoZr92+ZjFEKar5HS6MC776gYslNOKHt75mEBKWKnPeFDpZ6nH5OeF3S6HFT1mUAUZKrzkez05VboaX8myjSuw== + version "3.35.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.35.1.tgz#9c28f8b7ccee482796f8590cc8d15739eaaf980c" + integrity sha512-IgdsbxNyMskrTFxa9lWHyMwAJU5gXOPP+1yO+K59d50VLVAIDAbs7gIv705KzALModfK3ZrSZTPNpC0PQgIZuw== core-js@^3.19.1, core-js@^3.6.0, core-js@^3.8.3: version "3.25.0" @@ -4200,10 +4200,10 @@ globule@^1.0.0: lodash "^4.17.21" minimatch "~3.0.2" -golos-lib-js@^0.9.60: - version "0.9.60" - resolved "https://registry.yarnpkg.com/golos-lib-js/-/golos-lib-js-0.9.60.tgz#abf7e88499954312c817ebc38532e8e8d0451cbd" - integrity sha512-9UlH8OLTZj70godojecYTHIJ/6X+YW80zDVTYEq4qGDjZFlIAyczqu4UHlwZIxOtlZoyFMFtxMonWEXkw3nnPg== +golos-lib-js@^0.9.65: + version "0.9.65" + resolved "https://registry.yarnpkg.com/golos-lib-js/-/golos-lib-js-0.9.65.tgz#dc2e579701fab138d0ffdd48a267c4b8d295adfc" + integrity sha512-uOe7gBn982UQHBRPbUJ6lC9YJlnFhHMJMkZDfVfr0tFKiCjy2hnjDFaG/w/0QvSMQaLCay2MlrB15KImuxvuHQ== dependencies: abort-controller "^3.0.0" assert "^2.0.0" @@ -8532,9 +8532,9 @@ ws@^5.2.0: async-limiter "~1.0.0" ws@^8.2.3: - version "8.14.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" - integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== + version "8.16.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== ws@^8.4.2: version "8.11.0"