diff --git a/app/ResolveRoute.js b/app/ResolveRoute.js
index fd9ec96..baed8c3 100644
--- a/app/ResolveRoute.js
+++ b/app/ResolveRoute.js
@@ -1,14 +1,15 @@
 export const routeRegex = {
     UserProfile1: /^\/(@[\w\.\d-]+)\/?$/,
-    UserProfile2: /^\/(@[\w\.\d-]+)\/(transfers|assets|create-asset|invites|curation-rewards|author-rewards|donates-from|donates-to|filled-orders|permissions|created|password|witness|settings)\/??(?:&?[^=&]*=[^=&]*)*$/,
+    UserProfile2: /^\/(@[\w\.\d-]+)\/(transfers|assets|create-asset|invites|curation-rewards|author-rewards|donates-from|donates-to|nft|nft-collections|filled-orders|permissions|created|password|witness|settings)\/??(?:&?[^=&]*=[^=&]*)*$/,
     UserProfile3: /^\/(@[\w\.\d-]+)\/[\w\.\d-]+/,
     UserAssetEndPoints: /^\/(@[\w\.\d-]+)\/assets\/([\w\d.-]+)\/(update|transfer)$/,
-    UserEndPoints: /^(transfers|assets|create-asset|invites|curation-rewards|author-rewards|donates-from|donates-to|filled-orders|permissions|created|password|witness|settings)$/,
+    UserEndPoints: /^(transfers|assets|create-asset|invites|curation-rewards|author-rewards|donates-from|donates-to|nft|nft-collections|filled-orders|permissions|created|password|witness|settings)$/,
     WorkerSort: /^\/workers\/([\w\d\-]+)\/?($|\?)/,
     WorkerSearchByAuthor: /^\/workers\/([\w\d\-]+)\/(\@[\w\d.-]+)\/?($|\?)/,
     WorkerRequest: /^\/workers\/([\w\d\-]+)\/(\@[\w\d.-]+)\/([\w\d-]+)\/?($|\?)/,
     MarketPair: /^\/market\/([\w\d\.]+)\/([\w\d.]+)\/?($|\?)/,
     ConvertPair: /^\/convert\/([\w\d\.]+)\/([\w\d.]+)\/?($|\?)/,
+    NFTMarket: /^\/nft-market\/([\w\d\.]+)\/?($|\?)/,
     UserJson: /^\/(@[\w\.\d-]+)(\.json)$/,
     UserNameJson: /^.*(?=(\.json))/
 };
@@ -89,5 +90,12 @@ export default function resolveRoute(path)
     if (match) { 
         return {page: 'ConvertAssetsPage', params: match.slice(1)}
     }
+    if (path === '/nft-market') {
+        return {page: 'NFTMarketPage'}
+    }
+    match = path.match(routeRegex.NFTMarket);
+    if (match) { 
+        return {page: 'NFTMarketPage', params: match.slice(1)}
+    }
     return {page: 'NotFound'};
 }
diff --git a/app/RootRoute.js b/app/RootRoute.js
index bd2cad2..0d6f3e7 100644
--- a/app/RootRoute.js
+++ b/app/RootRoute.js
@@ -33,6 +33,8 @@ export default {
             cb(null, [require('@pages/UserProfile')]);
         } else if (route.page === 'ConvertAssetsPage') {
             cb(null, [require('@pages/ConvertAssetsPage')]);
+        } else if (route.page === 'NFTMarketPage') {
+            cb(null, [require('@pages/NFTMarketPage')]);
         } else if (route.page === 'Market') {
             cb(null, [require('@pages/MarketLoader')]);
        } else if (route.page === 'Rating') {
diff --git a/app/assets/images/nft.png b/app/assets/images/nft.png
new file mode 100644
index 0000000..db3321a
Binary files /dev/null and b/app/assets/images/nft.png differ
diff --git a/app/components/all.scss b/app/components/all.scss
index d85eb42..f6c7ffb 100644
--- a/app/components/all.scss
+++ b/app/components/all.scss
@@ -39,6 +39,7 @@
 @import "./elements/market/MarketPair";
 @import "./elements/market/OrderForm";
 @import "./elements/market/TickerPriceStat";
+@import "./elements/nft/NFTSmallIcon";
 @import "./elements/workers/WorkerRequestVoting";
 
 // dialogs
@@ -67,6 +68,12 @@
 @import "./modules/Powerdown.scss";
 @import "./modules/QuickBuy.scss";
 @import "./modules/Modals";
+@import "./modules/nft/NFTCollections";
+@import "./modules/nft/CreateNFTCollection";
+@import "./modules/nft/IssueNFTToken";
+@import "./modules/nft/NFTTokens";
+@import "./modules/nft/NFTTokenTransfer";
+@import "./modules/nft/NFTTokenDetails";
 
 // pages
 @import "./pages/Exchanges";
diff --git a/app/components/elements/DropdownMenu.jsx b/app/components/elements/DropdownMenu.jsx
index 521bf75..2173600 100644
--- a/app/components/elements/DropdownMenu.jsx
+++ b/app/components/elements/DropdownMenu.jsx
@@ -68,7 +68,7 @@ export default class DropdownMenu extends React.Component {
     }
 
     render() {
-        const {el, items, selected, children, className, title, href, noArrow} = this.props;
+        const {el, items, selected, children, className, title, href, onClick, noArrow} = this.props;
         const hasDropdown = items.length > 0
 
         let entry = children || <span key='label'>
@@ -76,7 +76,7 @@ export default class DropdownMenu extends React.Component {
                 {hasDropdown && !noArrow && <Icon name="dropdown-arrow" />}
             </span>
 
-        if(hasDropdown) entry = <a key="entry" href={href || '#'} onClick={this.toggle}>{entry}</a>
+        if(hasDropdown) entry = <a key="entry" href={href || '#'} onClick={onClick ? (e) => { onClick(e); this.toggle(e) } : this.toggle}>{entry}</a>
 
         const menu = <VerticalMenu key="menu" title={title} items={items} hideValue={selected} className="VerticalMenu" />;
         const cls = 'DropdownMenu' + (this.state.shown ? ' show' : '') + (className ? ` ${className}` : '')
diff --git a/app/components/elements/nft/NFTSmallIcon.jsx b/app/components/elements/nft/NFTSmallIcon.jsx
new file mode 100644
index 0000000..c3dba89
--- /dev/null
+++ b/app/components/elements/nft/NFTSmallIcon.jsx
@@ -0,0 +1,12 @@
+import React, { Component, } from 'react'
+
+class NFTSmallIcon extends Component {
+    render() {
+        const { image, ...rest } = this.props
+
+        return <a className='NFTSmallIcon' 
+            style={{ backgroundImage: `url(${image})` }} href={image} target='_blank' rel='nofollow noreferrer' {...rest}></a>
+    }
+}
+
+export default NFTSmallIcon
diff --git a/app/components/elements/nft/NFTSmallIcon.scss b/app/components/elements/nft/NFTSmallIcon.scss
new file mode 100644
index 0000000..0ee5941
--- /dev/null
+++ b/app/components/elements/nft/NFTSmallIcon.scss
@@ -0,0 +1,11 @@
+.NFTSmallIcon {
+    background-size: cover;
+    background-repeat: no-repeat;
+    background-position: 50% 50%;
+    border-radius: 50%;
+
+    width: 3rem;
+    height: 3rem;
+    display: inline-block;
+    vertical-align: top;
+}
diff --git a/app/components/modules/Header.jsx b/app/components/modules/Header.jsx
index 12bfd41..585a4ba 100644
--- a/app/components/modules/Header.jsx
+++ b/app/components/modules/Header.jsx
@@ -91,23 +91,25 @@ class Header extends React.Component {
             const name = acct_meta ? normalizeProfile(acct_meta.toJS()).name : null;
             const user_title = name ? `${name} (@${user_name})` : user_name;
             page_title = user_title;
-            if(route.params[1] === "curation-rewards"){
+            if (route.params[1] === "curation-rewards"){
                 page_title = tt('header_jsx.curation_rewards_by') + " " + user_title;
-            }
-            if(route.params[1] === "author-rewards"){
+            } else if (route.params[1] === "author-rewards"){
                 page_title = tt('header_jsx.author_rewards_by') + " " + user_title;
-            }
-            if(route.params[1] === "donates-from"){
+            } else if (route.params[1] === "donates-from"){
                 page_title = tt('header_jsx.donates_from') + " " + user_title;
-            }
-            if(route.params[1] === "donates-to"){
+            } else if (route.params[1] === "donates-to"){
                 page_title = tt('header_jsx.donates_to') + " " + user_title;
-            }
-            if(route.params[1] === "recent-replies"){
+            } else if (route.params[1] === "recent-replies"){
                 page_title = tt('header_jsx.replies_to') + " " + user_title;
+            } else if (route.params[1] === "nft"){
+                page_title = tt('header_jsx.nft_tokens') + " " + user_title
+            } else if (route.params[1] === "nft-collections"){
+                page_title = tt('header_jsx.nft_collections') + " " + user_title
             }
         } else if (route.page === 'ConvertAssetsPage') {
             page_title = tt('g.convert_assets')
+        } else if (route.page === `NFTMarketPage`){
+            page_title = tt('header_jsx.nft_market')
         } else {
             page_name = ''; //page_title = route.page.replace( /([a-z])([A-Z])/g, '$1 $2' ).toLowerCase();
         }
diff --git a/app/components/modules/nft/CreateNFTCollection.jsx b/app/components/modules/nft/CreateNFTCollection.jsx
new file mode 100644
index 0000000..3432068
--- /dev/null
+++ b/app/components/modules/nft/CreateNFTCollection.jsx
@@ -0,0 +1,398 @@
+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 Expandable from 'app/components/elements/Expandable'
+import LoadingIndicator from 'app/components/elements/LoadingIndicator'
+import transaction from 'app/redux/Transaction'
+
+const UINT32_MAX = '4294967295'
+
+class CreateNFTCollection extends Component {
+    state = {
+        collection: {
+            name: '',
+            title: '',
+            json_metadata: '{}',
+            max_token_count: UINT32_MAX,
+            infinity: true
+        }
+    }
+
+    validate = (values) => {
+        const errors = {}
+        const { title, name } = values
+        if (!title.length) {
+            errors.title = tt('g.required')
+        }
+        if (name.length < 3) {
+            errors.name = tt('assets_jsx.symbol_too_short')
+        } else {
+            const parts = name.split('.')
+            if (parts[0] == 'GOLOS' || parts[0] == 'GBG' || parts[0] == 'GESTS') {
+                errors.name = tt('assets_jsx.top_symbol_not_your')
+            } else if (parts.length == 2 && parts[1].length < 3) {
+                errors.name = tt('assets_jsx.subsymbol_too_short')
+            }
+        }
+        let meta = values.json_metadata
+        try {
+            meta = JSON.parse(meta)
+            if (!meta || Array.isArray(meta)) throw new Error('JSON is array')
+        } catch (err) {
+            console.error('json_metadata', err)
+            //errors.json_metadata = tt('create_nft_collection_jsx.json_wrong')
+            meta = null
+        }
+        if (meta) {
+            const noFields = []
+            if (!('title' in meta)) noFields.push('title')
+            if (values.image && !('image' in meta)) noFields.push('image')
+            if (values.description && !('description' in meta)) noFields.push('description')
+            if (noFields.length) {
+                errors.json_metadata = tt('create_nft_collection_jsx.json_no_fields')
+                errors.json_metadata += noFields.join(', ') + '. '
+            }
+        }
+        ++this.validationTime
+        return errors
+    }
+
+    setSubmitting = (submitting) => {
+        this.setState({ submitting })
+    }
+
+    _onSubmit = async (values) => {
+        this.setSubmitting(true)
+        this.setState({
+            errorMessage: ''
+        })
+        const { currentUser } = this.props
+        const username = currentUser.get('username')
+        await this.props.createCollection(values.name, values.json_metadata, values.max_token_count, currentUser, () => {
+            this.props.fetchState()
+            this.props.onClose()
+            this.setSubmitting(false)
+        }, (err) => {
+            console.error(err)
+            this.setSubmitting(false)
+            this.setState({
+                errorMessage: err.toString()
+            })
+        })
+    }
+
+    _renderSubmittingIndicator = () => {
+        const { submitting } = this.state
+
+        return submitting ? <span className='submitter'>
+            <LoadingIndicator type='circle' />
+        </span> : null
+    }
+
+    onNameChange = (e, values, setFieldValue) => {
+        let newName = ''
+        let hasDot
+        for (let i = 0; i < e.target.value.length; ++i) {
+            const c = e.target.value[i]
+            if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && c !== '.') {
+                continue
+            }
+            if (c == '.') {
+                if (i < 3 || hasDot) {
+                    continue
+                }
+                hasDot = true
+            }
+            newName += c.toUpperCase()
+        }
+        setFieldValue('name', newName)
+    }
+
+    updateJSONMetadata = (values, setFieldValue, currentVals, force = false) => {
+        let { json_metadata } = values
+        try {
+            json_metadata = JSON.parse(json_metadata)
+            if (json_metadata === null || Array.isArray(json_metadata)) {
+                json_metadata = {}
+            }
+        } catch (err) {
+            console.error('updateJSONMetadata', err)
+            if (!force) {
+                return
+            }
+            json_metadata = {}
+        }
+
+        const title = currentVals.title || values.title
+        json_metadata.title = title || ''
+
+        const description = currentVals.description || values.description
+        if (description) {
+            json_metadata.description = description
+        }
+
+        const image = currentVals.image || values.image
+        if (image) {
+            json_metadata.image = image
+        }
+
+        setFieldValue('json_metadata', JSON.stringify(json_metadata, null, 2))
+    }
+
+    onTitleChange = (e, values, setFieldValue) => {
+        const title = e.target.value
+        setFieldValue('title', title)
+        this.updateJSONMetadata(values, setFieldValue, { title })
+    }
+
+    onDescriptionChange = (e, values, setFieldValue) => {
+        const description = e.target.value
+        setFieldValue('description', description)
+        this.updateJSONMetadata(values, setFieldValue, { description })
+    }
+
+    onImageChange = (e, values, setFieldValue) => {
+        const image = e.target.value
+        setFieldValue('image', image)
+        this.updateJSONMetadata(values, setFieldValue, { image })
+    }
+
+    onImageBlur = (e) => {
+        e.preventDefault()
+        this.setState({
+            showImage: true
+        })
+    }
+
+    onMaxTokenCountChange = (e, setFieldValue) => {
+        let maxTokenCount = ''
+        for (let i = 0; i < e.target.value.length; ++i) {
+            const c = e.target.value[i]
+            if (c < '0' || c > '9') {
+                continue
+            }
+            maxTokenCount += c
+        }
+        if (maxTokenCount === UINT32_MAX) {
+            setFieldValue('infinity', true)
+        }
+        setFieldValue('max_token_count', maxTokenCount)
+    }
+
+    onInfinityChange = (e, values, setFieldValue) => {
+        if (!values.infinity) {
+            setFieldValue('max_token_count', UINT32_MAX)
+            setFieldValue('infinity', !values.infinity)
+        } else {
+            setFieldValue('max_token_count', '')
+            setFieldValue('infinity', !values.infinity)
+        }
+    }
+
+    restoreJson = (e, values, setFieldValue) => {
+        e.preventDefault()
+        this.updateJSONMetadata(values, setFieldValue, {}, true)
+    }
+
+    fixJson = (e, values, setFieldValue) => {
+        e.preventDefault()
+        this.updateJSONMetadata(values, setFieldValue, {})
+    }
+
+    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()   
+        }
+    }
+
+    render() {
+        const { onClose, } = this.props;
+        const { submitting, showImage, errorMessage, hideErrors } = this.state
+
+        return (<div className='CreateNFTCollection'>
+            <CloseButton onClick={onClose} />
+            <h4>
+                {tt('create_nft_collection_jsx.title')}
+            </h4>
+            <Formik
+                initialValues={this.state.collection}
+                enableReinitialize={true}
+                validate={this.validate}
+                validateOnMount={true}
+                onSubmit={this._onSubmit}
+            >
+            {({
+                handleSubmit, isValid, values, errors, touched, setFieldValue, handleChange,
+            }) => {
+                return (
+            <Form onMouseUp={this.onMouseUp}>
+                <div className='row'>
+                    <div className='column small-5'>
+                        {tt('create_nft_collection_jsx.name')}
+                        <div className='input-group' style={{marginBottom: 5}}>
+                            <Field name='name' type='text' maxLength='14' autoFocus={true} 
+                                onChange={(e) => this.onNameChange(e, values, setFieldValue)} />
+                        </div>
+                    </div>
+                    <div className='column small-7 padding-left'>
+                        {tt('create_nft_collection_jsx.coll_title') + '*'}
+                        <div className='input-group' style={{marginBottom: 5}}>
+                            <Field name='title' type='text'
+                                onChange={(e) => this.onTitleChange(e, values, setFieldValue)} />
+                        </div>
+                        {!errors.name && <ErrorMessage name='title' component='div' className='error' />}
+                    </div>
+                    {!hideErrors && <ErrorMessage name='name' component='div' className='error' />}
+                </div>
+                <div>
+                    {tt('create_nft_collection_jsx.coll_descr')}
+                    {' '}
+                    {tt('create_nft_collection_jsx.not_required')}
+                </div>
+                <div className='row'>
+                    <div className='column small-12'>
+                        <div className='input-group' style={{marginBottom: 5}}>
+                            <Field name='description' type='text'
+                                onChange={(e) => this.onDescriptionChange(e, values, setFieldValue)} />
+                        </div>
+                        <ErrorMessage name='description' component='div' className='error' />
+                    </div>
+                </div>
+                <div>
+                    {tt('create_nft_collection_jsx.image')}
+                    {' '}
+                    {tt('create_nft_collection_jsx.not_required')}
+                </div>
+                <div className='row'>
+                    <div className='column small-12'>
+                        <div className='input-group' style={{marginBottom: 5}}>
+                            <Field name='image' type='text' onBlur={this.onImageBlur}
+                                onChange={(e) => this.onImageChange(e, values, setFieldValue)} />
+                            <img src={values.image || 'empty'} className='image-preview' style={{ visibility: (showImage && values.image) ? 'visible' : 'hidden' }} />
+                        </div>
+                        <ErrorMessage name='image' component='div' className='error' />
+                    </div>
+                </div>
+                <div className='row'>
+                    <Expandable title={tt('create_nft_collection_jsx.json_metadata')}>
+                        <div className='column small-12'>
+                            <div className='input-group' style={{marginBottom: 5}}>
+                                <Field name='json_metadata' type='text' as='textarea' className='json_metadata' />
+                            </div>
+                        </div>
+                    </Expandable>
+                </div>
+                <div className='row'>
+                    <div className='column small-12'>
+                        <ErrorMessage name='json_metadata' component='span' className='error json-error' />
+                        {(touched.json_metadata && errors.json_metadata) ?
+                            (errors.json_metadata === tt('create_nft_collection_jsx.json_wrong') ?
+                                <a href='#' onClick={(e) => this.restoreJson(e, values, setFieldValue)}>{tt('create_nft_collection_jsx.restore_json')}</a> :
+                                <a href='#' onClick={(e) => this.fixJson(e, values, setFieldValue)}>{tt('create_nft_collection_jsx.json_fix')}</a>)
+                        : null}
+                    </div>
+                </div>
+                <div>
+                    {tt('create_nft_collection_jsx.token_count')}
+                </div>
+                <div className='row'>
+                    <div className='column small-6'>
+                        <div className='input-group' style={{marginBottom: 5}}>
+                            <Field name='max_token_count' disabled={values.max_token_count === UINT32_MAX}
+                                type='text' value={values.max_token_count !== UINT32_MAX ?
+                                    values.max_token_count : ''}
+                                onChange={e => this.onMaxTokenCountChange(e, setFieldValue)}
+                            />
+                        </div>
+                    </div>
+                    <div className='column small-6 padding-left'>
+                        <div className='input-group' style={{marginBottom: 5, marginTop: '8px'}}>
+                            <label>
+                                <Field name='infinity' type='checkbox'
+                                    onChange={(e) => this.onInfinityChange(e, values, setFieldValue)} />
+                                {tt('create_nft_collection_jsx.infinity')}
+                            </label>
+                        </div>
+                    </div>
+                </div>
+                {(errorMessage && errorMessage !== 'Canceled') ? <div className='row'>
+                    <div className='column small-12'>
+                        <div className='error' style={{marginBottom:'0px'}}>{errorMessage}</div>
+                    </div>
+                </div> : null}
+                <div className='row' style={{ marginTop: '0.5rem' }}>
+                    <div className='column small-8'>
+                        <button type='submit' disabled={!isValid || submitting} className='button'>
+                            {tt('create_nft_collection_jsx.create')}
+                        </button>
+                        <button type='button' disabled={submitting} className='button hollow'
+                                onMouseDown={this.onCancelMouseDown} onMouseUp={this.onCancelMouseUp}>
+                            {tt('g.cancel')}
+                        </button>
+                        {this._renderSubmittingIndicator()}
+                    </div>
+                </div>
+            </Form>
+            )}}</Formik>
+        </div>)
+    }
+}
+
+export default connect(
+    // mapStateToProps
+    (state, ownProps) => {
+        const {locationBeforeTransitions: {pathname}} = state.routing;
+        let currentUser = ownProps.currentUser || state.user.getIn(['current']) 
+        if (!currentUser) {
+            const currentUserNameFromRoute = pathname.split(`/`)[1].substring(1);
+            currentUser = Map({username: currentUserNameFromRoute});
+        }
+        const currentAccount = currentUser && state.global.getIn(['accounts', currentUser.get('username')]);
+        return { ...ownProps, currentUser, currentAccount, };
+    },
+
+    dispatch => ({
+        createCollection: (
+            name, json_metadata, max_token_count, currentUser, successCallback, errorCallback
+        ) => {
+            const username = currentUser.get('username')
+            let json = JSON.parse(json_metadata)
+            json = JSON.stringify(json)
+            const operation = {
+                creator: username,
+                name,
+                json_metadata: json,
+                max_token_count: parseInt(max_token_count)
+            }
+
+            dispatch(transaction.actions.broadcastOperation({
+                type: 'nft_collection',
+                username,
+                operation,
+                successCallback,
+                errorCallback
+            }))
+        }
+    })
+)(CreateNFTCollection)
diff --git a/app/components/modules/nft/CreateNFTCollection.scss b/app/components/modules/nft/CreateNFTCollection.scss
new file mode 100644
index 0000000..3db9e28
--- /dev/null
+++ b/app/components/modules/nft/CreateNFTCollection.scss
@@ -0,0 +1,34 @@
+.CreateNFTCollection {
+    .column {
+        padding-left: 0rem;
+        padding-right: 0rem;
+    }
+    .padding-left {
+        padding-left: 0.75rem;
+    }
+    .image-preview {
+        max-width: 150px;
+        height: 40px;
+        margin-left: 0.75rem;
+        border: none;
+    }
+    .json_metadata {
+        min-width: 100%;
+        min-height: 120px;
+        font-family: monospace;
+    }
+    .json-error {
+        margin-bottom: 0px;
+    }
+    .Expandable {
+        margin-bottom: 0px;
+        padding-bottom: 0px;
+        width: 100%;
+        .Expander {
+            margin-bottom: 0px;
+            h5 {
+                font-size: 1rem;
+            }
+        }
+    }
+}
diff --git a/app/components/modules/nft/IssueNFTToken.jsx b/app/components/modules/nft/IssueNFTToken.jsx
new file mode 100644
index 0000000..1115534
--- /dev/null
+++ b/app/components/modules/nft/IssueNFTToken.jsx
@@ -0,0 +1,358 @@
+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 { Asset, validateAccountName } from 'golos-lib-js/lib/utils'
+
+import Expandable from 'app/components/elements/Expandable'
+import LoadingIndicator from 'app/components/elements/LoadingIndicator'
+import transaction from 'app/redux/Transaction'
+
+class IssueNFTToken extends Component {
+    state = {
+        token: {
+            name: '',
+            to: '',
+            title: '',
+            json_metadata: '{}',
+        }
+    }
+
+    initTo = (currentUser) => {
+        if (!currentUser) return
+        const username = currentUser.get('username')
+        this.setState({
+            token: {
+                ...this.state.token,
+                to: username
+            }
+        })
+    }
+
+    componentDidMount() {
+        this.initTo(this.props.currentUser)
+    }
+
+    componentDidUpdate(prevProps) {
+        const { currentUser } = this.props
+        if (currentUser && !prevProps.currentUser) {
+            this.initTo(currentUser)
+        }
+    }
+
+    validate = (values) => {
+        const errors = {}
+        const { title, to } = values
+        if (!title.length) {
+            errors.title = tt('g.required')
+        }
+        const accNameError = validateAccountName(values.to)
+        if (accNameError.error) {
+            errors.to = tt('account_name.' + accNameError.error)
+        }
+        let meta = values.json_metadata
+        try {
+            meta = JSON.parse(meta)
+            if (Array.isArray(meta)) throw new Error('JSON is array')
+        } catch (err) {
+            console.error('json_metadata', err)
+            errors.json_metadata = tt('create_nft_collection_jsx.json_wrong')
+            meta = null
+        }
+        if (meta) {
+            const noFields = []
+            if (!('title' in meta)) noFields.push('title')
+            if (values.image && !('image' in meta)) noFields.push('image')
+            if (values.description && !('description' in meta)) noFields.push('description')
+            if (noFields.length) {
+                errors.json_metadata = tt('create_nft_collection_jsx.json_no_fields')
+                errors.json_metadata += noFields.join(', ') + '. '
+            }
+        }
+        return errors
+    }
+
+    setSubmitting = (submitting) => {
+        this.setState({ submitting })
+    }
+
+    _onSubmit = async (values) => {
+        this.setSubmitting(true)
+        this.setState({
+            errorMessage: ''
+        })
+        const { currentUser, issueName } = this.props
+        const username = currentUser.get('username')
+        await this.props.issueToken(issueName, values.to, values.json_metadata, currentUser, () => {
+            this.props.fetchState()
+            this.props.onClose()
+            this.setSubmitting(false)
+        }, (err) => {
+            console.error(err)
+            this.setSubmitting(false)
+            this.setState({
+                errorMessage: err.toString()
+            })
+        })
+    }
+
+    _renderSubmittingIndicator = () => {
+        const { submitting } = this.state
+
+        return submitting ? <span className='submitter'>
+            <LoadingIndicator type='circle' />
+        </span> : null
+    }
+
+    updateJSONMetadata = (values, setFieldValue, currentVals, force = false) => {
+        let { json_metadata } = values
+        try {
+            json_metadata = JSON.parse(json_metadata)
+            if (Array.isArray(json_metadata)) {
+                json_metadata = {}
+            }
+        } catch (err) {
+            console.error('updateJSONMetadata', err)
+            if (!force) {
+                return
+            }
+            json_metadata = {}
+        }
+
+        const title = currentVals.title || values.title
+        json_metadata.title = title || ''
+
+        const description = currentVals.description || values.description
+        if (description) {
+            json_metadata.description = description
+        }
+
+        const image = currentVals.image || values.image
+        if (image) {
+            json_metadata.image = image
+        }
+
+        setFieldValue('json_metadata', JSON.stringify(json_metadata, null, 2))
+    }
+
+    onTitleChange = (e, values, setFieldValue) => {
+        const title = e.target.value
+        setFieldValue('title', title)
+        this.updateJSONMetadata(values, setFieldValue, { title })
+    }
+
+    onDescriptionChange = (e, values, setFieldValue) => {
+        const description = e.target.value
+        setFieldValue('description', description)
+        this.updateJSONMetadata(values, setFieldValue, { description })
+    }
+
+    onImageChange = (e, values, setFieldValue) => {
+        const image = e.target.value
+        setFieldValue('image', image)
+        this.updateJSONMetadata(values, setFieldValue, { image })
+    }
+
+    onImageBlur = (e) => {
+        e.preventDefault()
+        this.setState({
+            showImage: true
+        })
+    }
+
+    restoreJson = (e, values, setFieldValue) => {
+        e.preventDefault()
+        this.updateJSONMetadata(values, setFieldValue, {}, true)
+    }
+
+    fixJson = (e, values, setFieldValue) => {
+        e.preventDefault()
+        this.updateJSONMetadata(values, setFieldValue, {})
+    }
+
+    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()   
+        }
+    }
+
+    render() {
+        const { issueName, issueNum, cprops, onClose, } = this.props;
+        const { submitting, showImage, errorMessage } = this.state
+
+        let cost = null
+        if (cprops) {
+            cost = <div className='row'>
+                {tt('issue_nft_token_jsx.issue_cost')}&nbsp;
+                <b>{Asset(cprops.get('nft_issue_cost')).floatString}</b>
+                .
+            </div>
+        }
+
+        return (<div className='IssueNFTToken'>
+            <CloseButton onClick={onClose} />
+            <h4>
+                {tt('issue_nft_token_jsx.title') + ' (' + issueName + ', #' + issueNum + ')'}
+            </h4>
+            <Formik
+                initialValues={this.state.token}
+                enableReinitialize={true}
+                validate={this.validate}
+                validateOnMount={true}
+                onSubmit={this._onSubmit}
+            >
+            {({
+                handleSubmit, isValid, values, errors, touched, setFieldValue, handleChange,
+            }) => {
+                return (
+            <Form onMouseUp={this.onMouseUp}>
+                <div className='row'>
+                    <div className='column small-12'>
+                        {tt('create_nft_collection_jsx.coll_title') + '*'}
+                        <div className='input-group' style={{marginBottom: 5}}>
+                            <Field name='title' type='text' autoFocus
+                                onChange={(e) => this.onTitleChange(e, values, setFieldValue)} />
+                        </div>
+                        <ErrorMessage name='title' component='div' className='error' />
+                    </div>
+                </div>
+                <div>
+                    {tt('assets_jsx.transfer_new_owner')}
+                </div>
+                <div className='row'>
+                    <div className='column small-12'>
+                        <div className='input-group' style={{marginBottom: 5}}>
+                            <Field name='to' type='text' />
+                        </div>
+                        <ErrorMessage name='to' component='div' className='error' />
+                    </div>
+                </div>
+                <div>
+                    {tt('create_nft_collection_jsx.coll_descr')}
+                    {' '}
+                    {tt('create_nft_collection_jsx.not_required')}
+                </div>
+                <div className='row'>
+                    <div className='column small-12'>
+                        <div className='input-group' style={{marginBottom: 5}}>
+                            <Field name='description' type='text'
+                                onChange={(e) => this.onDescriptionChange(e, values, setFieldValue)} />
+                        </div>
+                        <ErrorMessage name='description' component='div' className='error' />
+                    </div>
+                </div>
+                <div>
+                    {tt('create_nft_collection_jsx.image')}
+                    {' '}
+                    {tt('create_nft_collection_jsx.not_required')}
+                </div>
+                <div className='row'>
+                    <div className='column small-12'>
+                        <div className='input-group' style={{marginBottom: 5}}>
+                            <Field name='image' type='text' onBlur={this.onImageBlur}
+                                onChange={(e) => this.onImageChange(e, values, setFieldValue)} />
+                            <img src={values.image || 'empty'} className='image-preview' style={{ visibility: (showImage && values.image) ? 'visible' : 'hidden' }} />
+                        </div>
+                        <ErrorMessage name='image' component='div' className='error' />
+                    </div>
+                </div>
+                <div className='row'>
+                    <Expandable title={tt('create_nft_collection_jsx.json_metadata')}>
+                        <div className='column small-12'>
+                            <div className='input-group' style={{marginBottom: 5}}>
+                                <Field name='json_metadata' type='text' as='textarea' className='json_metadata' />
+                            </div>
+                        </div>
+                    </Expandable>
+                </div>
+                <div className='row'>
+                    <div className='column small-12'>
+                        <ErrorMessage name='json_metadata' component='span' className='error json-error' />
+                        {(touched.json_metadata && errors.json_metadata) ?
+                            (errors.json_metadata === tt('create_nft_collection_jsx.json_wrong') ?
+                                <a href='#' onClick={(e) => this.restoreJson(e, values, setFieldValue)}>{tt('create_nft_collection_jsx.restore_json')}</a> :
+                                <a href='#' onClick={(e) => this.fixJson(e, values, setFieldValue)}>{tt('create_nft_collection_jsx.json_fix')}</a>)
+                        : null}
+                    </div>
+                </div>
+                {cost}
+                {(errorMessage && errorMessage !== 'Canceled') ? <div className='row'>
+                    <div className='column small-12'>
+                        <div className='error' style={{marginBottom:'0px'}}>{errorMessage}</div>
+                    </div>
+                </div> : null}
+                <div className='row' style={{ marginTop: '0.5rem' }}>
+                    <div className='column small-8'>
+                        <button type='submit' disabled={!isValid || submitting} className='button'>
+                            {tt('transfer_jsx.issue')}
+                        </button>
+                        <button type='button' disabled={submitting} className='button hollow'
+                                onMouseDown={this.onCancelMouseDown} onMouseUp={this.onCancelMouseUp}>
+                            {tt('g.cancel')}
+                        </button>
+                        {this._renderSubmittingIndicator()}
+                    </div>
+                </div>
+            </Form>
+            )}}</Formik>
+        </div>)
+    }
+}
+
+export default connect(
+    // mapStateToProps
+    (state, ownProps) => {
+        const {locationBeforeTransitions: {pathname}} = state.routing;
+        let currentUser = ownProps.currentUser || state.user.getIn(['current']) 
+        if (!currentUser) {
+            const currentUserNameFromRoute = pathname.split(`/`)[1].substring(1);
+            currentUser = Map({username: currentUserNameFromRoute});
+        }
+        const currentAccount = currentUser && state.global.getIn(['accounts', currentUser.get('username')]);
+        const cprops = state.global.get('cprops')
+        return { ...ownProps, currentUser, currentAccount, cprops, }
+    },
+
+    dispatch => ({
+        issueToken: (
+            name, to, json_metadata, currentUser, successCallback, errorCallback
+        ) => {
+            const username = currentUser.get('username')
+            let json = JSON.parse(json_metadata)
+            json = JSON.stringify(json)
+            const operation = {
+                creator: username,
+                name,
+                to,
+                json_metadata: json,
+            }
+
+            dispatch(transaction.actions.broadcastOperation({
+                type: 'nft_issue',
+                username,
+                operation,
+                successCallback,
+                errorCallback
+            }))
+        }
+    })
+)(IssueNFTToken)
diff --git a/app/components/modules/nft/IssueNFTToken.scss b/app/components/modules/nft/IssueNFTToken.scss
new file mode 100644
index 0000000..ff33515
--- /dev/null
+++ b/app/components/modules/nft/IssueNFTToken.scss
@@ -0,0 +1,31 @@
+.IssueNFTToken {
+    .column {
+        padding-left: 0rem;
+        padding-right: 0rem;
+    }
+    .image-preview {
+        max-width: 150px;
+        height: 40px;
+        margin-left: 0.75rem;
+        border: none;
+    }
+    .json_metadata {
+        min-width: 100%;
+        min-height: 120px;
+        font-family: monospace;
+    }
+    .json-error {
+        margin-bottom: 0px;
+    }
+    .Expandable {
+        margin-bottom: 0px;
+        padding-bottom: 0px;
+        width: 100%;
+        .Expander {
+            margin-bottom: 0px;
+            h5 {
+                font-size: 1rem;
+            }
+        }
+    }
+}
diff --git a/app/components/modules/nft/NFTCollections.jsx b/app/components/modules/nft/NFTCollections.jsx
new file mode 100644
index 0000000..36199a7
--- /dev/null
+++ b/app/components/modules/nft/NFTCollections.jsx
@@ -0,0 +1,208 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import {connect} from 'react-redux';
+import { Link } from 'react-router';
+import tt from 'counterpart';
+import { Asset } from 'golos-lib-js/lib/utils';
+import Reveal from 'react-foundation-components/lib/global/reveal';
+
+import Icon from 'app/components/elements/Icon';
+import LoadingIndicator from 'app/components/elements/LoadingIndicator'
+import NFTSmallIcon from 'app/components/elements/nft/NFTSmallIcon'
+import CreateNFTCollection from 'app/components/modules/nft/CreateNFTCollection'
+import IssueNFTToken from 'app/components/modules/nft/IssueNFTToken'
+import g from 'app/redux/GlobalReducer'
+import user from 'app/redux/User'
+import transaction from 'app/redux/Transaction'
+import { getAssetMeta } from 'app/utils/market/utils'
+
+class NFTCollections extends Component {
+    state = {}
+
+    constructor() {
+        super()
+    }
+
+    showCreate = (e) => {
+        e.preventDefault()
+        this.setState({
+            showCreate: true,
+        })
+    }
+
+    hideCreate = () => {
+        this.setState({
+            showCreate: false,
+        })
+    }
+
+    showIssue = (e) => {
+        e.preventDefault()
+        this.setState({
+            showIssue: true,
+        })
+    }
+
+    hideIssue = () => {
+        this.setState({
+            showIssue: false,
+        })
+    }
+
+    render() {
+        const { account, isMyAccount, nft_collections, nft_assets, fetchState } = this.props
+        const accountName = account.get('name')
+
+        const collections = nft_collections ? nft_collections.toJS() : null
+        const assets = nft_assets ? nft_assets.toJS() : null
+
+        let items
+        if (!collections) {
+            items = <LoadingIndicator type='circle' />
+        } else if (!collections.length) {
+            if (isMyAccount) {
+                items = <span>{tt('nft_collections_jsx.not_yet')}</span>
+            } else {
+                items = <span>{tt('nft_collections_jsx.not_yet2') + accountName + tt('nft_collections_jsx.not_yet3')}</span>
+            }
+        } else {
+            items = []
+            let count = 0
+            for (const collection of collections) {
+                const { name, token_count, json_metadata, image, market_volume, last_buy_price } = collection
+
+                let data
+                if (json_metadata) {
+                    data = JSON.parse(json_metadata)
+                }
+                data = data || {} // node allows to use '', null, object, or array
+
+                const issueIt = (e) => {
+                    e.preventDefault()
+                    this.setState({
+                        showIssue: true,
+                        issueName: name,
+                        issueNum: token_count + 1
+                    })
+                }
+
+                const deleteIt = async (e) => {
+                    e.preventDefault()
+
+                    await this.props.deleteCollection(name, accountName, () => {
+                        this.props.fetchState()
+                    }, (err) => {
+                        console.error(err)
+                    })
+                }
+
+                const price = Asset(last_buy_price)
+                const asset = assets[price.symbol]
+                let imageUrl
+                if (asset) {
+                    imageUrl = getAssetMeta(asset).image_url
+                }
+
+                items.push(<tr key={name} className={count % 2 == 0 ? '' : 'zebra'}>
+                    <td title={data.title}>
+                        <NFTSmallIcon image={image} />
+                    </td>
+                    <td title={data.title}>
+                        {name}
+                    </td>
+                    <td>
+                        {tt('nft_tokens_jsx.token_count', { count: token_count })}
+                        {isMyAccount ? <button className='button hollow small' onClick={issueIt}>
+                            {tt('transfer_jsx.issue')}
+                        </button> : null}
+                    </td>
+                    <td className='market-stats'>
+                        <div title={tt('nft_collections_jsx.volume_hint')}>
+                            {tt('rating_jsx.volume') + ' ' + parseFloat(market_volume).toFixed(3)}
+                        </div>
+                        <div title={tt('nft_collections_jsx.price_hint')}>
+                            {imageUrl && <img className='price-icon' src={imageUrl} alt={''} />}
+                            <span className='price-val'>{price.amountFloat}</span>
+                        </div>
+                    </td>
+                    <td>
+                        {isMyAccount ? <button disabled={!!token_count} title={token_count ? tt('nft_collections_jsx.tokens_exist') : null} className='button hollow small alert' onClick={deleteIt}>
+                            {tt('g.delete')}
+                        </button> : null}
+                    </td>
+                </tr>)
+
+                ++count
+            }
+
+            items = <table><tbody>
+                {items}
+            </tbody></table>
+        }
+
+        const { showCreate, showIssue, issueName, issueNum } = this.state
+
+        return (<div className='NFTCollections'>
+            <div className="row">
+                <div className="column small-12">
+                    <h4 className="Assets__header">{tt('g.nft_collections')}</h4>
+                    {isMyAccount && <a href='#' onClick={this.showCreate} className="button hollow float-right">
+                        {tt('nft_collections_jsx.create')}
+                    </a>}
+                </div>
+            </div>
+            <div className="row">
+                <div className="column small-12">
+                    {items}
+                </div>
+            </div>
+
+            <Reveal show={showCreate} onHide={this.hideCreate} revealStyle={{ width: '450px' }}>
+                <CreateNFTCollection
+                    onClose={this.hideCreate}
+                    fetchState={fetchState}
+                />
+            </Reveal>
+
+            <Reveal show={showIssue} onHide={this.hideIssue} revealStyle={{ width: '450px' }}>
+                <IssueNFTToken
+                    onClose={this.hideIssue}
+                    issueName={issueName}
+                    issueNum={issueNum}
+                    fetchState={fetchState}
+                />
+            </Reveal>
+        </div>)
+    }
+}
+
+export default connect(
+    (state, ownProps) => {
+        return {...ownProps,
+            nft_collections: state.global.get('nft_collections'),
+            nft_assets: state.global.get('nft_assets')
+        }
+    },
+    dispatch => ({
+        fetchState: () => {
+            const pathname = window.location.pathname
+            dispatch({type: 'FETCH_STATE', payload: {pathname}})
+        },
+        deleteCollection: (
+            name, username, successCallback, errorCallback
+        ) => {
+            const operation = {
+                creator: username,
+                name,
+            }
+
+            dispatch(transaction.actions.broadcastOperation({
+                type: 'nft_collection_delete',
+                username,
+                operation,
+                successCallback,
+                errorCallback
+            }))
+        }
+    })
+)(NFTCollections)
diff --git a/app/components/modules/nft/NFTCollections.scss b/app/components/modules/nft/NFTCollections.scss
new file mode 100644
index 0000000..0074e6c
--- /dev/null
+++ b/app/components/modules/nft/NFTCollections.scss
@@ -0,0 +1,20 @@
+.NFTCollections {
+    .market-stats {
+        font-size: 90%;
+    }
+    .price-icon {
+        width: 20px;
+        height: 20px;
+        margin-right: 0.25rem;
+    }
+    .price-val { 
+        display: inline-block;
+        vertical-align: middle;
+    }
+
+    .button {
+        margin-left: 0.5rem;
+        margin-right: 0rem;
+        margin-bottom: 0rem;
+    }
+}
diff --git a/app/components/modules/nft/NFTTokenDetails.jsx b/app/components/modules/nft/NFTTokenDetails.jsx
new file mode 100644
index 0000000..f1c78a4
--- /dev/null
+++ b/app/components/modules/nft/NFTTokenDetails.jsx
@@ -0,0 +1,108 @@
+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 { Asset } from 'golos-lib-js/lib/utils'
+
+import { getAssetMeta } from 'app/utils/market/utils'
+import transaction from 'app/redux/Transaction'
+
+class NFTTokenDetails extends Component {
+    state = {
+    }
+
+    onBurnClick = async (e) => {
+        const { nft_tokens, tokenIdx, currentUser } = this.props
+        const token = nft_tokens.toJS()[tokenIdx]
+        const { token_id } = token
+        await this.props.burnToken(token_id, currentUser, () => {
+            this.props.fetchState()
+            this.props.onClose()
+        }, (err) => {
+            if (!err || err.toString() === 'Canceled') return
+            console.error(err)
+            alert(err.toString())
+        })
+    }
+
+    onTransferClick = (e) => {
+        const { tokenIdx } = this.props
+        this.props.onClose()
+        this.props.showTransfer(e, tokenIdx)
+    }
+
+    render() {
+        const { nft_tokens, nft_assets, tokenIdx, onClose, } = this.props
+
+        const token = nft_tokens.toJS()[tokenIdx]
+
+        const assets = nft_assets.toJS()
+
+        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
+
+        let last_price
+        const last_buy_price = Asset('1.005 GOLOS')// Asset(token.last_buy_price)
+        if (last_buy_price.amount > 0) {
+            const asset = assets[last_buy_price.symbol]
+            let imageUrl
+            if (asset) {
+                imageUrl = getAssetMeta(asset).image_url
+            }
+            last_price = <span title={last_buy_price.floatString}>
+                {imageUrl && <img className='price-icon' src={imageUrl} alt={''} />}
+                {last_buy_price.amountFloat}
+            </span>
+        }
+
+        return <div className='NFTTokenDetails'>
+            <div>
+                <div style={{ float: 'left', paddingRight: '0.75rem' }}>
+                    <a href={data.image} target='_blank' rel='noreferrer nofollow'>
+                        <img src={data.image} />
+                    </a>
+                </div>
+                <div style={{ width: '100%' }}>
+                    <CloseButton onClick={onClose} />
+                    <h4 style={{ marginBottom: '0.1rem'}}>
+                        {data.title}
+                    </h4>
+                    <span className='secondary'><a href='#'>{token.name}</a></span>
+                    {data.description ? <div style={{ marginTop: '0.7rem', marginBottom: '0.75rem' }}>
+                        {data.description}
+                    </div> : null}
+                    <div>
+                        {last_price}
+                        <button className='button slim hollow alert float-right' onClick={this.onBurnClick}>
+                            {tt('g.burn')}
+                        </button>
+                        <button className='button slim hollow float-right' onClick={this.onTransferClick}>
+                            {tt('g.transfer')}
+                        </button>
+                        <button className='button slim float-right'>
+                            {tt('g.sell')}
+                        </button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    }
+}
+
+export default connect(
+    // mapStateToProps
+    (state, ownProps) => {
+        return { ...ownProps,
+            nft_tokens: state.global.get('nft_tokens'),
+            nft_assets: state.global.get('nft_assets')
+        }
+    },
+
+    dispatch => ({
+    })
+)(NFTTokenDetails)
diff --git a/app/components/modules/nft/NFTTokenDetails.scss b/app/components/modules/nft/NFTTokenDetails.scss
new file mode 100644
index 0000000..bf49dbf
--- /dev/null
+++ b/app/components/modules/nft/NFTTokenDetails.scss
@@ -0,0 +1,26 @@
+.NFTTokenDetails {
+	img {
+		max-width: 320px;
+		max-height: 320px;
+	}
+
+    .price-icon {
+        width: 20px;
+        height: 20px;
+        margin-right: 0.25rem;
+    }
+
+    .button {
+        margin: 0px !important;
+        margin-left: 5px !important;
+    }
+    .button:hover {
+        background-color: #016aad !important;
+    }
+    .button.hollow {
+        border: 0px;
+    }
+    .button.hollow:hover {
+        background-color: transparent !important;
+    }
+}
diff --git a/app/components/modules/nft/NFTTokenTransfer.jsx b/app/components/modules/nft/NFTTokenTransfer.jsx
new file mode 100644
index 0000000..629030c
--- /dev/null
+++ b/app/components/modules/nft/NFTTokenTransfer.jsx
@@ -0,0 +1,187 @@
+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 { validateAccountName } from 'golos-lib-js/lib/utils'
+
+import LoadingIndicator from 'app/components/elements/LoadingIndicator'
+import NFTSmallIcon from 'app/components/elements/nft/NFTSmallIcon'
+import transaction from 'app/redux/Transaction'
+
+class NFTTokenTransfer extends Component {
+    state = {
+        token: {
+            to: ''
+        }
+    }
+
+    validate = (values) => {
+        const errors = {}
+        const { to } = values
+        const accNameError = validateAccountName(values.to)
+        if (accNameError.error) {
+            errors.to = tt('account_name.' + accNameError.error)
+        }
+        return errors
+    }
+
+    setSubmitting = (submitting) => {
+        this.setState({ submitting })
+    }
+
+    _onSubmit = async (values) => {
+        this.setSubmitting(true)
+        this.setState({
+            errorMessage: ''
+        })
+
+        const { nft_tokens, tokenIdx, currentUser, onClose, } = this.props
+        const token = nft_tokens.toJS()[tokenIdx]
+        const { token_id } = token
+
+        const username = currentUser.get('username')
+
+        await this.props.transferToken(token_id, values.to, currentUser, () => {
+            this.props.fetchState()
+            this.props.onClose()
+            this.setSubmitting(false)
+        }, (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()   
+        }
+    }
+
+    _renderSubmittingIndicator = () => {
+        const { submitting } = this.state
+
+        return submitting ? <span className='submitter'>
+            <LoadingIndicator type='circle' />
+        </span> : null
+    }
+
+    render() {
+        const { nft_tokens, tokenIdx, onClose, } = this.props
+
+        const token = nft_tokens.toJS()[tokenIdx]
+
+        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  } = this.state
+
+        return <div className='NFTTokenTransfer'>
+            <CloseButton onClick={onClose} />
+            <h4>{tt('g.transfer')}</h4>
+            <div style={{ marginBottom: '0.5rem' }}>
+                <NFTSmallIcon image={image} />
+                <span style={{ display: 'inline-block', marginTop: '13px', marginLeft: '0.5rem' }}>{data.title}</span>
+            </div>
+            <Formik
+                initialValues={this.state.token}
+                enableReinitialize={true}
+                validate={this.validate}
+                validateOnMount={true}
+                onSubmit={this._onSubmit}
+            >
+            {({
+                handleSubmit, isValid, values, errors, touched, setFieldValue, handleChange,
+            }) => {
+                return (
+            <Form onMouseUp={this.onMouseUp}>
+                <div>
+                    {tt('assets_jsx.transfer_new_owner')}
+                </div>
+                <div className='row'>
+                    <div className='column small-12'>
+                        <div className='input-group' style={{marginBottom: 5}}>
+                            <Field name='to' type='text' autoFocus />
+                        </div>
+                        <ErrorMessage name='to' component='div' className='error' />
+                    </div>
+                </div>
+                {(errorMessage && errorMessage !== 'Canceled') ? <div className='row'>
+                    <div className='column small-12'>
+                        <div className='error' style={{marginBottom:'0px'}}>{errorMessage}</div>
+                    </div>
+                </div> : null}
+                <div className='row' style={{ marginTop: '0.5rem' }}>
+                    <div className='column small-8'>
+                        <button type='submit' disabled={!isValid || submitting} className='button'>
+                            {tt('g.transfer')}
+                        </button>
+                        <button type='button' disabled={submitting} className='button hollow'
+                                onMouseDown={this.onCancelMouseDown} onMouseUp={this.onCancelMouseUp}>
+                            {tt('g.cancel')}
+                        </button>
+                        {this._renderSubmittingIndicator()}
+                    </div>
+                </div>
+            </Form>
+            )}}</Formik>
+        </div>
+    }
+}
+
+export default connect(
+    // mapStateToProps
+    (state, ownProps) => {
+        return { ...ownProps,
+            nft_tokens: state.global.get('nft_tokens'),
+        }
+    },
+
+    dispatch => ({
+        transferToken: (
+            token_id, to, currentUser, successCallback, errorCallback
+        ) => {
+            const username = currentUser.get('username')
+            const operation = {
+                from: username,
+                to,
+                token_id,
+                memo: ''
+            }
+
+            dispatch(transaction.actions.broadcastOperation({
+                type: 'nft_transfer',
+                username,
+                operation,
+                successCallback,
+                errorCallback
+            }))
+        }
+    })
+)(NFTTokenTransfer)
diff --git a/app/components/modules/nft/NFTTokenTransfer.scss b/app/components/modules/nft/NFTTokenTransfer.scss
new file mode 100644
index 0000000..f371653
--- /dev/null
+++ b/app/components/modules/nft/NFTTokenTransfer.scss
@@ -0,0 +1,9 @@
+.NFTTokenTransfer {
+    .column {
+        padding-left: 0rem;
+        padding-right: 0rem;
+    }
+    .error {
+        margin-bottom: 0px;
+    }
+}
diff --git a/app/components/modules/nft/NFTTokens.jsx b/app/components/modules/nft/NFTTokens.jsx
new file mode 100644
index 0000000..898f174
--- /dev/null
+++ b/app/components/modules/nft/NFTTokens.jsx
@@ -0,0 +1,232 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import {connect} from 'react-redux'
+import { Link } from 'react-router'
+import tt from 'counterpart'
+import { Asset } from 'golos-lib-js/lib/utils'
+import { Map } from 'immutable'
+import Reveal from 'react-foundation-components/lib/global/reveal'
+
+import DropdownMenu from 'app/components/elements/DropdownMenu'
+import Icon from 'app/components/elements/Icon'
+import LoadingIndicator from 'app/components/elements/LoadingIndicator'
+import NFTTokenDetails from 'app/components/modules/nft/NFTTokenDetails'
+import NFTTokenTransfer from 'app/components/modules/nft/NFTTokenTransfer'
+import g from 'app/redux/GlobalReducer'
+import user from 'app/redux/User'
+import transaction from 'app/redux/Transaction'
+import { getAssetMeta } from 'app/utils/market/utils'
+
+class NFTTokens extends Component {
+    state = {}
+
+    constructor() {
+        super()
+    }
+
+    showTransfer = (e, tokenIdx) => {
+        e.preventDefault()
+        this.setState({
+            showTransfer: true,
+            tokenIdx,
+        })
+    }
+
+    hideTransfer = () => {
+        this.setState({
+            showTransfer: false,
+        })
+    }
+
+    burnIt = async (e, tokenIdx) => {
+        e.preventDefault()
+        const { nft_tokens, currentUser } = this.props
+        const token = nft_tokens.toJS()[tokenIdx]
+        const { token_id } = token
+        await this.props.burnToken(token_id, currentUser, () => {
+            this.props.fetchState()
+        }, (err) => {
+            if (!err || err.toString() === 'Canceled') return
+            console.error(err)
+            alert(err.toString())
+        })
+    }
+
+    showDetails = (e, tokenIdx) => {
+        e.preventDefault()
+        this.setState({
+            showDetails: true,
+            tokenIdx,
+        })
+    }
+
+    hideDetails = () => {
+        this.setState({
+            showDetails: false,
+        })
+    }
+
+    onClick = (e, tokenIdx) => {
+        e.preventDefault()
+        for (let node = e.target; node && node != e.currentTarget; node = node.parentNode) {
+            if (node.onclick) {
+                return
+            }
+        }
+        this.showDetails(e, tokenIdx)
+    }
+
+    render() {
+        const { currentUser, account, isMyAccount, nft_tokens, nft_assets, fetchState } = this.props
+        const accountName = account.get('name')
+
+        const tokens = nft_tokens ? nft_tokens.toJS() : null
+        const assets = nft_assets ? nft_assets.toJS() : null
+
+        let items = []
+        if (!tokens) {
+            items = <LoadingIndicator type='circle' />
+        } else if (!tokens.length) {
+            if (isMyAccount) {
+                items = <span>{tt('nft_tokens_jsx.not_yet')}</span>
+            } else {
+                items = <span>{tt('nft_tokens_jsx.not_yet2') + accountName + tt('nft_tokens_jsx.not_yet3')}</span>
+            }
+        } else {
+            for (let i = 0; i < tokens.length; ++i) {
+                const token = tokens[i]
+                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
+
+                let last_price
+                const last_buy_price = Asset(token.last_buy_price)
+                if (last_buy_price.amount > 0) {
+                    const asset = assets[last_buy_price.symbol]
+                    let imageUrl
+                    if (asset) {
+                        imageUrl = getAssetMeta(asset).image_url
+                    }
+                    last_price = <span title={last_buy_price.floatString}>
+                        {imageUrl && <img className='price-icon' src={imageUrl} alt={''} />}
+                        {last_buy_price.amountFloat}
+                    </span>
+                }
+
+                const kebabItems = [
+                    { link: '#', onClick: e => {
+                        this.burnIt(e, i)
+                    }, value: tt('g.burn') },
+                    { link: '#', onClick: e => {
+                        this.showDetails(e, i)
+                    }, value: tt('g.more_hint') },
+                ]
+
+                if (last_price) {
+                    kebabItems.unshift({ link: '#', onClick: e => {
+                        this.showTransfer(e, i)
+                    }, value: tt('g.transfer') })
+                }
+
+                items.push(<div className='Token' onClick={e => this.onClick(e, i)}>
+                    <img className='token-image' src={image} alt='' title={data.title} />
+                    <div>
+                        <h5 className='token-title'>{data.title}</h5>
+                        <span className='token-coll secondary'><a href='#'>{token.name}</a></span>
+                        <div>
+                            {last_price}
+                            <DropdownMenu className='float-right' el='div' items={kebabItems}>
+                                <Icon name='new/more' size='0_95x' />
+                            </DropdownMenu>
+                            {!last_price ? <button className='button slim hollow float-right' onClick={e => this.showTransfer(e, i)}>{tt('g.transfer')}</button> : null}
+                            <button className='button slim float-right'>{tt('g.sell')}</button>
+                        </div>
+                    </div>
+                </div>)
+            }
+        }
+
+        const { showTransfer, showDetails, tokenIdx } = this.state
+
+        return (<div className='NFTTokens'>
+            <div className="row">
+                <div className="column small-12">
+                    <h4 className="Assets__header">{tt('g.nft_tokens')}</h4>
+                    <Link to={`/nft-market`} className="button float-right">
+                        {tt('g.buy')}
+                    </Link>
+                </div>
+            </div>
+            <div className="row">
+                <div className="column small-12">
+                    {items}
+                </div>
+            </div>
+
+            <Reveal show={showTransfer} onHide={this.hideTransfer} revealStyle={{ width: '450px' }}>
+                <NFTTokenTransfer
+                    currentUser={currentUser}
+                    onClose={this.hideTransfer}
+                    tokenIdx={tokenIdx}
+                    fetchState={fetchState}
+                />
+            </Reveal>
+
+            <Reveal show={showDetails} onHide={this.hideDetails} revealStyle={{ width: '700px' }}>
+                <NFTTokenDetails
+                    currentUser={currentUser}
+                    showTransfer={(e) => this.showTransfer(e, tokenIdx)}
+                    burnToken={this.props.burnToken}
+                    onClose={this.hideDetails}
+                    tokenIdx={tokenIdx}
+                    fetchState={fetchState}
+                />
+            </Reveal>
+        </div>)
+    }
+}
+
+export default connect(
+    (state, ownProps) => {
+        const {locationBeforeTransitions: {pathname}} = state.routing;
+        let currentUser = ownProps.currentUser || state.user.getIn(['current']) 
+        if (!currentUser) {
+            const currentUserNameFromRoute = pathname.split(`/`)[1].substring(1);
+            currentUser = Map({username: currentUserNameFromRoute});
+        }
+        return {...ownProps, currentUser,
+            nft_tokens: state.global.get('nft_tokens'),
+            nft_assets: state.global.get('nft_assets')
+        }
+    },
+    dispatch => ({
+        fetchState: () => {
+            const pathname = window.location.pathname
+            dispatch({type: 'FETCH_STATE', payload: {pathname}})
+        },
+        burnToken: (
+            token_id, currentUser, successCallback, errorCallback
+        ) => {
+            const username = currentUser.get('username')
+            const operation = {
+                from: username,
+                to: 'null',
+                token_id,
+                memo: ''
+            }
+
+            dispatch(transaction.actions.broadcastOperation({
+                type: 'nft_transfer',
+                confirm: tt('g.are_you_sure'),
+                username,
+                operation,
+                successCallback,
+                errorCallback
+            }))
+        }
+    })
+)(NFTTokens)
diff --git a/app/components/modules/nft/NFTTokens.scss b/app/components/modules/nft/NFTTokens.scss
new file mode 100644
index 0000000..b9bc7be
--- /dev/null
+++ b/app/components/modules/nft/NFTTokens.scss
@@ -0,0 +1,48 @@
+.NFTTokens {
+    .Token {
+        display: inline-block;
+        border: 1px solid rgba(128,128,128,0.45);
+        border-radius: 5px;
+        margin-right: 1rem;
+        margin-bottom: 1em;
+        padding: 0.5rem;
+        cursor: pointer;
+
+        .token-image {
+            width: 200px;
+            height: 200px;
+        }
+        .price-icon {
+            width: 20px;
+            height: 20px;
+            margin-right: 0.25rem;
+        }
+
+        .token-title {
+            margin-top: 0.25rem;
+            margin-bottom: 0.1rem;
+        }
+        .token-coll {
+            display: inline-block;
+            margin-bottom: 0.35rem;
+        }
+
+        .button {
+            margin: 0px !important;
+            margin-left: 5px !important;
+        }
+        .button:hover {
+            background-color: #016aad;
+        }
+        .button.hollow {
+            border: 0px;
+        }
+        .button.hollow:hover {
+            background-color: transparent;
+        }
+    }
+    .Token:hover {
+        background-color: rgba(208,208,208,0.45);
+        border: 1px solid rgba(128,128,128,0.45);
+    }
+}
diff --git a/app/components/pages/NFTMarketPage.jsx b/app/components/pages/NFTMarketPage.jsx
new file mode 100644
index 0000000..06d0a87
--- /dev/null
+++ b/app/components/pages/NFTMarketPage.jsx
@@ -0,0 +1,25 @@
+import React from 'react'
+import { connect, } from 'react-redux'
+import tt from 'counterpart'
+
+class NFTMarketPage extends React.Component {
+    render() {
+        const { currentAccount, routeParams } = this.props
+    }
+}
+
+module.exports = {
+    path: '/nft-market(/:sym)',
+    component: connect(
+        (state, ownProps) => {
+            const currentUser = state.user.getIn(['current'])
+            const currentAccount = currentUser && state.global.getIn(['accounts', currentUser.get('username')])
+
+            return {
+                currentAccount,
+            }
+        },
+        dispatch => ({
+        })
+    )(NFTMarketPage),
+}
diff --git a/app/components/pages/UserProfile.jsx b/app/components/pages/UserProfile.jsx
index 14c99c1..282516d 100644
--- a/app/components/pages/UserProfile.jsx
+++ b/app/components/pages/UserProfile.jsx
@@ -14,6 +14,8 @@ import CreateAsset from 'app/components/modules/uia/CreateAsset';
 import Assets from 'app/components/modules/uia/Assets';
 import UpdateAsset from 'app/components/modules/uia/UpdateAsset';
 import TransferAsset from 'app/components/modules/uia/TransferAsset';
+import NFTCollections from 'app/components/modules/nft/NFTCollections'
+import NFTTokens from 'app/components/modules/nft/NFTTokens'
 import Invites from 'app/components/elements/Invites';
 import PasswordReset from 'app/components/elements/PasswordReset';
 import UserWallet from 'app/components/modules/UserWallet';
@@ -246,8 +248,15 @@ export default class UserProfile extends React.Component {
                 <br />
                 <CreateAsset account={accountImm} />
                 </div>;
-        }
-        else if( section === 'curation-rewards' ) {
+        } else if( section === 'nft-collections' ) {
+            tab_content = <div>
+                <NFTCollections account={accountImm} isMyAccount={isMyAccount} />
+                </div>
+        } else if( section === 'nft' ) {
+            tab_content = <div>
+                <NFTTokens account={accountImm} isMyAccount={isMyAccount} />
+                </div>
+        } else if( section === 'curation-rewards' ) {
             rewardsClass = 'active';
             tab_content = <CurationRewards
                 account={account}
@@ -357,6 +366,11 @@ export default class UserProfile extends React.Component {
             {link: `/@${accountname}/curation-rewards`, label: tt('g.curation_rewards'), value: tt('g.curation_rewards')}
         ];
 
+        let nftMenu = [
+            {link: `/@${accountname}/nft`, label: tt('g.nft_tokens'), value: tt('g.nft_tokens'),},
+            {link: `/@${accountname}/nft-collections`, label: tt('g.nft_collections'), value: tt('g.nft_collections')},
+        ];
+
         let permissionsMenu = [
             {link: `/@${accountname}/permissions`, label: tt('g.keys'), value: tt('g.keys')},
             {link: `/@${accountname}/password`, label: tt('g.reset_password'), value: tt('g.reset_password')}
@@ -378,6 +392,19 @@ export default class UserProfile extends React.Component {
                     <Link className='UserProfile__menu-item' to={`/@${accountname}/assets`} activeClassName='active'>
                         {tt('g.assets')}
                     </Link>
+                    <div>
+                        <LinkWithDropdown
+                            closeOnClickOutside
+                            dropdownPosition='bottom'
+                            dropdownAlignment={this.state.linksAlign}
+                            dropdownContent={<VerticalMenu items={nftMenu} />}
+                            >
+                            <a className={`${rewardsClass} UserProfile__menu-item`} ref={this._onLinkRef}>
+                                {tt('g.nft')}
+                                <Icon name='dropdown-center' />
+                            </a>
+                        </LinkWithDropdown>
+                    </div>
                     {isMyAccount ? <Link className='UserProfile__menu-item' to={`/@${accountname}/filled-orders`} activeClassName='active'>
                         {tt('navigation.market2')} <NotifiCounter fields="fill_order" />
                     </Link> : null}
diff --git a/app/locales/en.json b/app/locales/en.json
index d0d9054..929f09d 100644
--- a/app/locales/en.json
+++ b/app/locales/en.json
@@ -58,6 +58,7 @@
     "bid": "Bid",
     "blog": "Blog",
     "browse": "Browse",
+    "burn": "Burn",
     "buy": "Buy",
     "quick_buy": "Quick Buy",
     "buy_VESTING_TOKEN": "Buy %(VESTING_TOKEN)s",
@@ -133,6 +134,9 @@
     "reset_password": "Password reset",
     "invites": "Invite checks",
     "assets": "UIA assets",
+    "nft": "NFT",
+    "nft_tokens": "NFT-tokens",
+    "nft_collections": "NFT-collections",
     "help_wallet": "Wallet functions",
     "phone": "phone",
     "post": "Post",    
@@ -511,6 +515,16 @@
       "other": "%(count)s posts"
     }
   },
+  "account_name": {
+    "account_name_should_be_shorter": "Account name should be shorter.",
+    "account_name_should_be_longer": "Account name should be longer.",
+    "account_name_should_not_be_empty": "Account name should not be empty.",
+    "each_account_segment_should_start_with_a_letter": "Each account name segment should start with a letter.",
+    "each_account_segment_should_have_only_letters_digits_or_dashes": "Each account name segment should have only letters, digits or dashes.",
+    "each_account_segment_should_have_only_one_dash_in_a_row": "Each account name segment should have only one dash in a row.",
+    "each_account_segment_should_end_with_a_letter_or_digit": "Each account name segment should end with a letter or digit.",
+    "each_account_segment_should_be_longer": "Each account name segment should be longer."
+  },
   "authorrewards_jsx": {
     "estimated_author_rewards_last_week": "Estimated author rewards last week",
     "author_rewards_history": "Author Rewards History"
@@ -715,6 +729,47 @@
     "no_way_error": "If you set at leas one of these fields, you should also set at least 1 withdrawal method or Details field.",
     "no_to_error": "If you set withdraw via transfer with memo, please also set, to which account to transfer."
   },
+  "nft_collections_jsx": {
+    "not_yet": "You haven't any own NFT-token collections.",
+    "not_yet2": "",
+    "not_yet3": " haven't any own NFT-token collections.",
+    "create": "Create collection",
+    "name_exists": "Collection with such name is already created by another author",
+    "tokens_exist": "Cannot delete collection which has tokens",
+    "volume_hint": "Market volume for all time",
+    "price_hint": "Last buy price"
+  },
+  "create_nft_collection_jsx": {
+    "title": "Create NFT collection",
+    "name": "Token name",
+    "coll_title": "Title",
+    "coll_descr": "Description",
+    "image": "Image URL",
+    "json_metadata": "JSON metadata",
+    "json_wrong": "Wrong JSON. ",
+    "not_required": "(not required)",
+    "token_count": "Max token count",
+    "infinity": "Infinity",
+    "create": "Create",
+    "restore_json": "Restore default",
+    "json_no_fields": "In JSON missing fields: ",
+    "json_fix": "Add missing fields"
+  },
+  "issue_nft_token_jsx": {
+    "title": "Issue NFT",
+    "issue_cost": "The issue cost is",
+    "max_token_count": "Max token count in collection is already reached."
+  },
+  "nft_tokens_jsx": {
+    "not_yet": "You have no NFT-tokens.",
+    "not_yet2": "",
+    "not_yet3": " has no NFT-tokens.",
+    "token_count": {
+      "zero": "0 tokens",
+      "one": "1 token",
+      "other": "%(count)s tokens"
+    }
+  },
   "invites_jsx": {
     "create_invite": "Create new invite check",
     "create_invite_info": "Cheques (invite codes) are a universal tool for transferring of GOLOS tokens to other people outside the blockchain. There are two ways to redeem the code: transfer its balance to your account or register a new account using it.",
@@ -918,6 +973,9 @@
     "author_rewards_by": "Author rewards by",
     "donates_from": "Donates from",
     "donates_to": "Donates to",
+    "nft_tokens": "NFT-tokens",
+    "nft_collections": "NFT-collections, created by",
+    "nft_market": "NFT-market",
     "replies_to": "Replies to",
     "comments_by": "Comments by"
   },
diff --git a/app/locales/ru-RU.json b/app/locales/ru-RU.json
index d4a29b1..de4809b 100644
--- a/app/locales/ru-RU.json
+++ b/app/locales/ru-RU.json
@@ -12,6 +12,16 @@
     "TIP_TOKEN": "TIP-баланс",
     "CLAIM_TOKEN": "Накопительный баланс"
   },
+  "account_name": {
+    "account_name_should_be_shorter": "Имя аккаунта должно быть короче.",
+    "account_name_should_be_longer": "Имя аккаунта должно быть длиннее.",
+    "account_name_should_not_be_empty": "Имя аккаунта не должно быть пустым.",
+    "each_account_segment_should_start_with_a_letter": "Каждый сегмент имени аккаунта должен начинаться с буквы.",
+    "each_account_segment_should_have_only_letters_digits_or_dashes": "Сегмент имени аккаунта может содержать только буквы, цифры и дефисы.",
+    "each_account_segment_should_have_only_one_dash_in_a_row": "Каждый сегмент имени аккаунта может содержать только один дефис.",
+    "each_account_segment_should_end_with_a_letter_or_digit": "Каждый сегмент имени аккаунта должен заканчиваться буквой или цифрой.",
+    "each_account_segment_should_be_longer": "Сегмент имени аккаунта должен быть длиннее."
+  },
   "authorrewards_jsx": {
     "estimated_author_rewards_last_week": "Оценочные авторские вознаграждения за неделю",
     "author_rewards_history": "История авторских наград"
@@ -164,6 +174,7 @@
     "blog": "Блог",
     "browse": "Посмотреть",
     "basic": "Основные",
+    "burn": "Сжечь",
     "buy": "Купить",
     "quick_buy": "Быстрая покупка",
     "buy_VESTING_TOKEN": "Купить %(VESTING_TOKEN2)s",
@@ -242,6 +253,9 @@
     "reset_password": "Сброс пароля",
     "invites": "Инвайт-чеки",
     "assets": "Активы UIA",
+    "nft": "NFT",
+    "nft_tokens": "Токены NFT",
+    "nft_collections": "Коллекции NFT",
     "help_wallet": "Функции кошелька",
     "phone": "телефон",
     "post": "Пост",
@@ -501,6 +515,9 @@
     "author_rewards_by": "Автор награжден",
     "donates_from": "Донаты, отправленные",
     "donates_to": "Донаты, полученные",
+    "nft_tokens": "NFT-токены",
+    "nft_collections": "NFT-коллекции, созданные",
+    "nft_market": "Биржа NFT",
     "replies_to": "Ответы на",
     "comments_by": "Комментарии"
   },
@@ -1053,6 +1070,47 @@
     "no_way_error": "Если вы заполнили одно из полей, то вы должны указать хотя бы один способ вывода или заполнить Дополнительно.",
     "no_to_error": "Если вы указали вывод через перевод с заметкой, то укажите, куда переводить."
   },
+  "nft_collections_jsx": {
+    "not_yet": "У вас нет своих собственных коллекций NFT-токенов.",
+    "not_yet2": "У ",
+    "not_yet3": " нет своих собственных коллекций NFT-токенов.",
+    "create": "Создать коллекцию",
+    "name_exists": "Коллекция с таким именем уже создана другим автором",
+    "tokens_exist": "Нельзя удалить коллекцию, в которой уже есть токены",
+    "volume_hint": "Объем торгов за все время",
+    "price_hint": "Последняя цена покупки"
+  },
+  "create_nft_collection_jsx": {
+    "title": "Создать коллекцию NFT",
+    "name": "Имя токена",
+    "coll_title": "Название",
+    "coll_descr": "Описание",
+    "image": "Ссылка на изображение",
+    "json_metadata": "JSON-метаданные",
+    "json_wrong": "Некорректный JSON. ",
+    "not_required": "(не обязательно)",
+    "token_count": "Количество токенов",
+    "infinity": "Бесконечное",
+    "create": "Создать",
+    "restore_json": "Вернуть по умолчанию",
+    "json_no_fields": "В JSON не хватает полей: ",
+    "json_fix": "Добавить недостающее"
+  },
+  "issue_nft_token_jsx": {
+    "title": "Выпустить NFT",
+    "issue_cost": "Будет списана плата за выпуск -",
+    "max_token_count": "Достигнуто максимальное кол-во токенов в коллекции."
+  },
+  "nft_tokens_jsx": {
+    "not_yet": "У вас пока нет NFT-токенов.",
+    "not_yet2": "У ",
+    "not_yet3": " пока нет NFT-токенов.",
+    "token_count": {
+      "zero": "0 токенов",
+      "one": "1 токен",
+      "other": "%(count)s токенов"
+    }
+  },
   "invites_jsx": {
     "create_invite": "Создание чека",
     "create_invite_info": "Чеки (инвайт-коды) — инструмент для передачи токенов другим людям вне блокчейна. Использовать чек можно двумя способами: перевести его баланс на аккаунт (форма для этого ниже) или зарегистрировать с его помощью новый аккаунт.",
diff --git a/app/redux/FetchDataSaga.js b/app/redux/FetchDataSaga.js
index 6c48c16..43bd1ad 100644
--- a/app/redux/FetchDataSaga.js
+++ b/app/redux/FetchDataSaga.js
@@ -1,6 +1,7 @@
 import { call, put, select, fork, cancelled, takeLatest, takeEvery } from 'redux-saga/effects';
 import cookie from "react-cookie";
 import {config, api} from 'golos-lib-js';
+import { Asset } from 'golos-lib-js/lib/utils'
 
 import { getPinnedPosts, getMutedInNew } from 'app/utils/NormalizeProfile';
 import { getBlockings, listBlockings } from 'app/redux/BlockingSaga'
@@ -11,6 +12,7 @@ import constants from './constants';
 import { reveseTag, getFilterTags } from 'app/utils/tags';
 import { CATEGORIES, SELECT_TAGS_KEY, DEBT_TOKEN_SHORT, LIQUID_TICKER } from 'app/client_config';
 import { getAllPairs } from 'app/utils/market/utils'
+import { parseNFTImage, NFTImageStub } from 'app/utils/NFTUtils'
 
 export function* fetchDataWatches () {
     yield fork(watchLocationChange);
@@ -124,6 +126,92 @@ export function* fetchState(location_change_action) {
                         state.cprops = yield call([api, api.getChainPropertiesAsync])
                     break
 
+                    case 'nft-collections':
+                        state.nft_collections = (yield call([api, api.getNftCollectionsAsync], {
+                            creator: uname,
+                            limit: 100,
+                            sort: 'by_created'
+                        }))
+
+                        try {
+                            const noImgColls = {}
+                            for (let i = 0; i < state.nft_collections.length; ++i) {
+                                const nco = state.nft_collections[i]
+
+                                nco.image = parseNFTImage(nco.json_metadata)
+                                if (!nco.image) {
+                                    noImgColls[nco.name] = i
+                                }
+                            }
+
+                            const noImgKeys = Object.keys(noImgColls)
+
+                            const tokens = (yield call([api, api.getNftTokensAsync], {
+                                select_collections: noImgKeys,
+                                collection_limit: 1,
+                                limit: 100,
+                                collections: false,
+                                orders: false,
+                            }))
+
+                            for (const token of tokens) {
+                                const idx = noImgColls[token.name]
+                                const nco = state.nft_collections[idx]
+                                nco.image = parseNFTImage(token.json_metadata)
+                            }
+
+                            const syms = new Set()
+
+                            for (const nco of state.nft_collections) {
+                                nco.image = nco.image || NFTImageStub()
+
+                                const price = Asset(nco.last_buy_price)
+                                syms.add(price.symbol)
+                            }
+
+                            state.nft_assets = {}
+                            if (syms.size) {
+                                const nft_assets = yield call([api, api.getAssets], '', [...syms])
+                                for (const a of nft_assets) {
+                                    const supply = Asset(a.supply)
+                                    state.nft_assets[supply.symbol] = a
+                                }
+                            }
+                        } catch (err) {
+                            console.error(err)
+                        }
+
+                        state.cprops = yield call([api, api.getChainPropertiesAsync])
+                    break
+
+                    case 'nft':
+                        state.nft_tokens = (yield call([api, api.getNftTokensAsync], {
+                            owner: uname
+                        }))
+
+                        try {
+                            const syms = new Set()
+
+                            for (const no of state.nft_tokens) {
+                                no.image = parseNFTImage(no.json_metadata) || NFTImageStub()
+
+                                const price = Asset(no.last_buy_price)
+                                syms.add(price.symbol)
+                            }
+
+                            state.nft_assets = {}
+                            if (syms.size) {
+                                const nft_assets = yield call([api, api.getAssets], '', [...syms])
+                                for (const a of nft_assets) {
+                                    const supply = Asset(a.supply)
+                                    state.nft_assets[supply.symbol] = a
+                                }
+                            }
+                        } catch (err) {
+                            console.error(err)
+                        }
+                    break
+
                     case 'invites':
                         state.cprops = yield call([api, api.getChainPropertiesAsync])
                     break
diff --git a/app/redux/GlobalReducer.js b/app/redux/GlobalReducer.js
index ce156bd..76c49dd 100644
--- a/app/redux/GlobalReducer.js
+++ b/app/redux/GlobalReducer.js
@@ -52,7 +52,12 @@ export default createModule({
                         );
                     }
                 }
-                let res = state.mergeDeep(payload)
+                let res = state
+                if (res.has('nft_collections'))
+                    res = res.delete('nft_collections')
+                if (res.has('nft_tokens'))
+                    res = res.delete('nft_tokens')
+                res = res.mergeDeep(payload)
                 return res
             },
         },
diff --git a/app/redux/Transaction_Error.js b/app/redux/Transaction_Error.js
index 0ea51cd..e05e861 100644
--- a/app/redux/Transaction_Error.js
+++ b/app/redux/Transaction_Error.js
@@ -8,6 +8,8 @@ export default function transactionErrorReducer(
     let errorStr = error.toString();
     let errorKey = 'Transaction broadcast error.';
 
+    let handled = false
+
     for (const [type] of operations) {
         switch (type) {
             case 'vote':
@@ -42,6 +44,21 @@ export default function transactionErrorReducer(
                     errorKey = errorStr = tt('invites_jsx.claim_wrong_secret_fatal');
                 }
                 break;
+            case 'nft_collection':
+                if (errorStr.includes('Object already exist')) {
+                    errorKey = errorStr = tt('nft_collections_jsx.name_exists')
+                    handled = true
+                }
+                break;
+            case 'nft_issue':
+                if (errorStr.includes('Account does not have sufficient funds')) {
+                    errorKey = errorStr = tt('transfer_jsx.insufficient_funds')
+                    handled = true
+                } else if (errorStr.includes('Cannot issue more tokens')) {
+                    errorKey = errorStr = tt('issue_nft_token_jsx.max_token_count')
+                    handled = true
+                }
+                break;
             case 'withdraw_vesting':
                 if (
                     errorStr.includes(
@@ -64,7 +81,6 @@ export default function transactionErrorReducer(
                 break;
         }
 
-        let handled = false
         if (errorStr.includes('You are blocked by user')) {
             errorKey = errorStr = tt('chain_errors.user_blocked_user')
             handled = true
diff --git a/app/utils/NFTUtils.js b/app/utils/NFTUtils.js
new file mode 100644
index 0000000..d5fb6bc
--- /dev/null
+++ b/app/utils/NFTUtils.js
@@ -0,0 +1,12 @@
+
+export function parseNFTImage(json_metadata) {
+    if (json_metadata) {
+        const meta = JSON.parse(json_metadata)
+        if (meta) return meta.image
+    }
+    return null
+}
+
+export function NFTImageStub() {
+	return require('app/assets/images/nft.png')
+}
diff --git a/package.json b/package.json
index 4aa7f8c..95faadd 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.53",
+    "golos-lib-js": "^0.9.56",
     "history": "^2.0.0-rc2",
     "immutable": "^3.8.2",
     "intl": "^1.2.5",
diff --git a/yarn.lock b/yarn.lock
index ddbc36f..ecd2f83 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2185,14 +2185,15 @@ assert@^1.4.1:
     util "0.10.3"
 
 assert@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/assert/-/assert-2.0.0.tgz#95fc1c616d48713510680f2eaf2d10dd22e02d32"
-  integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd"
+  integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==
   dependencies:
-    es6-object-assign "^1.1.0"
-    is-nan "^1.2.1"
-    object-is "^1.0.1"
-    util "^0.12.0"
+    call-bind "^1.0.2"
+    is-nan "^1.3.2"
+    object-is "^1.1.5"
+    object.assign "^4.1.4"
+    util "^0.12.5"
 
 assertion-error@^1.1.0:
   version "1.1.0"
@@ -2970,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.30.0"
-  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.30.0.tgz#64ac6f83bc7a49fd42807327051701d4b1478dea"
-  integrity sha512-hQotSSARoNh1mYPi9O2YaWeiq/cEB95kOrFb4NCrO4RIFt1qqNpKsaE+vy/L3oiqvND5cThqXzUU3r9F7Efztg==
+  version "3.32.2"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.32.2.tgz#172fb5949ef468f93b4be7841af6ab1f21992db7"
+  integrity sha512-pxXSw1mYZPDGvTQqEc5vgIb83jGQKFGYWY76z4a7weZXUolw3G+OvpZqSRcfYOoOVUQJYEPsWeQK8pKEnUtWxQ==
 
 core-js@^3.19.1, core-js@^3.6.0, core-js@^3.8.3:
   version "3.25.0"
@@ -3605,11 +3606,6 @@ es-to-primitive@^1.2.1:
     is-date-object "^1.0.1"
     is-symbol "^1.0.2"
 
-es6-object-assign@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
-  integrity sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==
-
 escalade@^3.1.1:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
@@ -4204,10 +4200,10 @@ globule@^1.0.0:
     lodash "^4.17.21"
     minimatch "~3.0.2"
 
-golos-lib-js@^0.9.53:
-  version "0.9.53"
-  resolved "https://registry.yarnpkg.com/golos-lib-js/-/golos-lib-js-0.9.53.tgz#8469e0cfb5b0183bb0b161279ceb059907e5251e"
-  integrity sha512-ipjFLKOXhhI31JuQzYsgZtZbFJQem15PRwU0OG+ocnQtUHB1CsU02GGJV0la3gL+K2KvHZdmKqd8XzGx8jgA4g==
+golos-lib-js@^0.9.56:
+  version "0.9.56"
+  resolved "https://registry.yarnpkg.com/golos-lib-js/-/golos-lib-js-0.9.56.tgz#3dfe8c0658fba2f50976ef49103ddc3f34109c19"
+  integrity sha512-h9ay0q2AuHiYL8aFXsCGoEFe6ojHt67FHMv8W6oWbqayl44JlRuuEysfE1MZQiiLwzBDFOO1SNMAtv5sE0bRcg==
   dependencies:
     abort-controller "^3.0.0"
     assert "^2.0.0"
@@ -4849,7 +4845,7 @@ is-lambda@^1.0.1:
   resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5"
   integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==
 
-is-nan@^1.2.1:
+is-nan@^1.3.2:
   version "1.3.2"
   resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d"
   integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==
@@ -6073,7 +6069,7 @@ object-inspect@^1.12.2:
   resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9"
   integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==
 
-object-is@^1.0.1:
+object-is@^1.0.1, object-is@^1.1.5:
   version "1.1.5"
   resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac"
   integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==
@@ -7242,11 +7238,16 @@ selfsigned@^2.1.1:
   dependencies:
     node-forge "^1"
 
-"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0, semver@^5.7.0:
+"semver@2 || 3 || 4 || 5", semver@^5.6.0, semver@^5.7.0:
   version "5.7.1"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
   integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
 
+semver@^5.5.0:
+  version "5.7.2"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
+  integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
+
 semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
   version "6.3.0"
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
@@ -8148,7 +8149,7 @@ util@0.10.3:
     safe-buffer "^5.1.2"
     which-typed-array "^1.1.2"
 
-util@^0.12.0:
+util@^0.12.5:
   version "0.12.5"
   resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc"
   integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==
@@ -8531,9 +8532,9 @@ ws@^5.2.0:
     async-limiter "~1.0.0"
 
 ws@^8.2.3:
-  version "8.13.0"
-  resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0"
-  integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
+  version "8.14.1"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.1.tgz#4b9586b4f70f9e6534c7bb1d3dc0baa8b8cf01e0"
+  integrity sha512-4OOseMUq8AzRBI/7SLMUwO+FEDnguetSk7KMb1sHwvF2w2Wv5Hoj0nlifx8vtGsftE/jWHojPy8sMMzYLJ2G/A==
 
 ws@^8.4.2:
   version "8.11.0"