diff --git a/package.json b/package.json
index 03f37c0f..4374ead7 100644
--- a/package.json
+++ b/package.json
@@ -44,7 +44,7 @@
     "start": "craco start",
     "build": "craco build",
     "test": "craco test",
-    "eject": "craco eject"
+    "ts-check": "tsc -p tsconfig.json --noEmit"
   },
   "eslintConfig": {
     "extends": [
diff --git a/src/redux/auth/api.ts b/src/redux/auth/api.ts
index 18cb240c..25f38508 100644
--- a/src/redux/auth/api.ts
+++ b/src/redux/auth/api.ts
@@ -1,55 +1,46 @@
-import { api, configWithToken, errorMiddleware, resultMiddleware } from '~/utils/api';
+import { api, cleanResult, errorMiddleware, resultMiddleware } from '~/utils/api';
 import { API } from '~/constants/api';
-import { INotification, IResultWithStatus } from '~/redux/types';
-import { userLoginTransform } from '~/redux/auth/transforms';
-import { ISocialAccount, IUser } from './types';
+import { IResultWithStatus } from '~/redux/types';
+import {
+  ApiAttachSocialRequest,
+  ApiAttachSocialResult,
+  ApiAuthGetUpdatesRequest,
+  ApiAuthGetUpdatesResult,
+  ApiAuthGetUserProfileRequest,
+  ApiAuthGetUserProfileResult,
+  ApiAuthGetUserResult,
+  ApiCheckRestoreCodeRequest,
+  ApiCheckRestoreCodeResult,
+  ApiDropSocialRequest,
+  ApiDropSocialResult,
+  ApiGetSocialsResult,
+  ApiLoginWithSocialRequest,
+  ApiLoginWithSocialResult,
+  ApiRestoreCodeRequest,
+  ApiRestoreCodeResult,
+  ApiUpdateUserRequest,
+  ApiUpdateUserResult,
+  ApiUserLoginRequest,
+  ApiUserLoginResult,
+} from './types';
 
-export const apiUserLogin = ({
-  username,
-  password,
-}: {
-  username: string;
-  password: string;
-}): Promise<IResultWithStatus<{ token: string; status?: number }>> =>
+export const apiUserLogin = ({ username, password }: ApiUserLoginRequest) =>
   api
-    .post(API.USER.LOGIN, { username, password })
-    .then(resultMiddleware)
-    .catch(errorMiddleware)
-    .then(userLoginTransform);
+    .post<ApiUserLoginResult>(API.USER.LOGIN, { username, password })
+    .then(cleanResult);
 
-export const apiAuthGetUser = ({ access }): Promise<IResultWithStatus<{ user: IUser }>> =>
-  api
-    .get(API.USER.ME, configWithToken(access))
-    .then(resultMiddleware)
-    .catch(errorMiddleware);
+export const apiAuthGetUser = () => api.get<ApiAuthGetUserResult>(API.USER.ME).then(cleanResult);
 
-export const apiAuthGetUserProfile = ({
-  access,
-  username,
-}): Promise<IResultWithStatus<{ user: IUser }>> =>
-  api
-    .get(API.USER.PROFILE(username), configWithToken(access))
-    .then(resultMiddleware)
-    .catch(errorMiddleware);
+export const apiAuthGetUserProfile = ({ username }: ApiAuthGetUserProfileRequest) =>
+  api.get<ApiAuthGetUserProfileResult>(API.USER.PROFILE(username)).then(cleanResult);
 
-export const apiAuthGetUpdates = ({
-  access,
-  exclude_dialogs,
-  last,
-}): Promise<IResultWithStatus<{
-  notifications: INotification[];
-  boris: { commented_at: string };
-}>> =>
+export const apiAuthGetUpdates = ({ exclude_dialogs, last }: ApiAuthGetUpdatesRequest) =>
   api
-    .get(API.USER.GET_UPDATES, configWithToken(access, { params: { exclude_dialogs, last } }))
-    .then(resultMiddleware)
-    .catch(errorMiddleware);
+    .get<ApiAuthGetUpdatesResult>(API.USER.GET_UPDATES, { params: { exclude_dialogs, last } })
+    .then(cleanResult);
 
-export const apiUpdateUser = ({ access, user }): Promise<IResultWithStatus<{ user: IUser }>> =>
-  api
-    .patch(API.USER.ME, user, configWithToken(access))
-    .then(resultMiddleware)
-    .catch(errorMiddleware);
+export const apiUpdateUser = ({ user }: ApiUpdateUserRequest) =>
+  api.patch<ApiUpdateUserResult>(API.USER.ME, user).then(cleanResult);
 
 export const apiRequestRestoreCode = ({ field }): Promise<IResultWithStatus<{}>> =>
   api
@@ -57,75 +48,26 @@ export const apiRequestRestoreCode = ({ field }): Promise<IResultWithStatus<{}>>
     .then(resultMiddleware)
     .catch(errorMiddleware);
 
-export const apiCheckRestoreCode = ({ code }): Promise<IResultWithStatus<{}>> =>
-  api
-    .get(API.USER.REQUEST_CODE(code))
-    .then(resultMiddleware)
-    .catch(errorMiddleware);
+export const apiCheckRestoreCode = ({ code }: ApiCheckRestoreCodeRequest) =>
+  api.get<ApiCheckRestoreCodeResult>(API.USER.REQUEST_CODE(code)).then(cleanResult);
 
-export const apiRestoreCode = ({ code, password }): Promise<IResultWithStatus<{}>> =>
+export const apiRestoreCode = ({ code, password }: ApiRestoreCodeRequest) =>
   api
-    .post(API.USER.REQUEST_CODE(code), { password })
-    .then(resultMiddleware)
-    .catch(errorMiddleware);
+    .post<ApiRestoreCodeResult>(API.USER.REQUEST_CODE(code), { password })
+    .then(cleanResult);
 
-export const apiGetSocials = ({
-  access,
-}: {
-  access: string;
-}): Promise<IResultWithStatus<{
-  accounts: ISocialAccount[];
-}>> =>
-  api
-    .get(API.USER.GET_SOCIALS, configWithToken(access))
-    .then(resultMiddleware)
-    .catch(errorMiddleware);
+export const apiGetSocials = () =>
+  api.get<ApiGetSocialsResult>(API.USER.GET_SOCIALS).then(cleanResult);
 
-export const apiDropSocial = ({
-  access,
-  id,
-  provider,
-}: {
-  access: string;
-  id: string;
-  provider: string;
-}): Promise<IResultWithStatus<{
-  accounts: ISocialAccount[];
-}>> =>
-  api
-    .delete(API.USER.DROP_SOCIAL(provider, id), configWithToken(access))
-    .then(resultMiddleware)
-    .catch(errorMiddleware);
+export const apiDropSocial = ({ id, provider }: ApiDropSocialRequest) =>
+  api.delete<ApiDropSocialResult>(API.USER.DROP_SOCIAL(provider, id)).then(cleanResult);
 
-export const apiAttachSocial = ({
-  access,
-  token,
-}: {
-  access: string;
-  token: string;
-}): Promise<IResultWithStatus<{
-  account: ISocialAccount;
-}>> =>
+export const apiAttachSocial = ({ token }: ApiAttachSocialRequest) =>
   api
-    .post(API.USER.ATTACH_SOCIAL, { token }, configWithToken(access))
-    .then(resultMiddleware)
-    .catch(errorMiddleware);
+    .post<ApiAttachSocialResult>(API.USER.ATTACH_SOCIAL, { token })
+    .then(cleanResult);
 
-export const apiLoginWithSocial = ({
-  token,
-  username,
-  password,
-}: {
-  token: string;
-  username?: string;
-  password?: string;
-}): Promise<IResultWithStatus<{
-  token: string;
-  error: string;
-  errors: Record<string, string>;
-  needs_register: boolean;
-}>> =>
+export const apiLoginWithSocial = ({ token, username, password }: ApiLoginWithSocialRequest) =>
   api
-    .post(API.USER.LOGIN_WITH_SOCIAL, { token, username, password })
-    .then(resultMiddleware)
-    .catch(errorMiddleware);
+    .post<ApiLoginWithSocialResult>(API.USER.LOGIN_WITH_SOCIAL, { token, username, password })
+    .then(cleanResult);
diff --git a/src/redux/auth/constants.ts b/src/redux/auth/constants.ts
index 5c66e845..d2959fdc 100644
--- a/src/redux/auth/constants.ts
+++ b/src/redux/auth/constants.ts
@@ -53,26 +53,26 @@ export const USER_ROLES = {
 };
 
 export const EMPTY_TOKEN: IToken = {
-  access: null,
-  refresh: null,
+  access: '',
+  refresh: '',
 };
 
 export const EMPTY_USER: IUser = {
-  id: null,
+  id: 0,
   role: USER_ROLES.GUEST,
-  email: null,
-  name: null,
-  username: null,
-  photo: null,
-  cover: null,
+  email: '',
+  name: '',
+  username: '',
+  photo: undefined,
+  cover: undefined,
   is_activated: false,
   is_user: false,
-  fullname: null,
-  description: null,
+  fullname: '',
+  description: '',
 
-  last_seen: null,
-  last_seen_messages: null,
-  last_seen_boris: null,
+  last_seen: '',
+  last_seen_messages: '',
+  last_seen_boris: '',
 };
 
 export interface IApiUser {
diff --git a/src/redux/auth/index.ts b/src/redux/auth/index.ts
index 008d581f..357cb37e 100644
--- a/src/redux/auth/index.ts
+++ b/src/redux/auth/index.ts
@@ -8,17 +8,17 @@ const HANDLERS = {
 };
 
 const INITIAL_STATE: IAuthState = {
-  token: null,
+  token: '',
   user: { ...EMPTY_USER },
 
   updates: {
-    last: null,
+    last: '',
     notifications: [],
-    boris_commented_at: null,
+    boris_commented_at: '',
   },
 
   login: {
-    error: null,
+    error: '',
     is_loading: false,
     is_registering: true,
   },
@@ -27,7 +27,7 @@ const INITIAL_STATE: IAuthState = {
     tab: 'profile',
     is_loading: true,
 
-    user: null,
+    user: undefined,
     patch_errors: {},
 
     socials: {
@@ -39,20 +39,19 @@ const INITIAL_STATE: IAuthState = {
 
   restore: {
     code: '',
-    user: null,
+    user: undefined,
     is_loading: false,
     is_succesfull: false,
-    error: null,
+    error: '',
   },
 
   register_social: {
     errors: {
-      username: 'and this',
-      password: 'dislike this',
+      username: '',
+      password: '',
     },
-    error: 'dont like this one',
-    token:
-      'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJEYXRhIjp7IlByb3ZpZGVyIjoiZ29vZ2xlIiwiSWQiOiJma2F0dXJvdkBpY2Vyb2NrZGV2LmNvbSIsIkVtYWlsIjoiZmthdHVyb3ZAaWNlcm9ja2Rldi5jb20iLCJUb2tlbiI6InlhMjkuYTBBZkg2U01EeXFGdlRaTExXckhsQm1QdGZIOFNIVGQteWlSYTFKSXNmVXluY2F6MTZ5UGhjRmxydTlDMWFtTEg0aHlHRzNIRkhrVGU0SXFUS09hVVBEREdqR2JQRVFJbGpPME9UbUp2T2RrdEtWNDVoUGpJcTB1cHVLc003UWJLSm1oRWhkMEFVa3YyejVHWlNSMjhaM2VOZVdwTEVYSGV0MW1yNyIsIkZldGNoZWQiOnsiUHJvdmlkZXIiOiJnb29nbGUiLCJJZCI6OTIyMzM3MjAzNjg1NDc3NTgwNywiTmFtZSI6IkZlZG9yIEthdHVyb3YiLCJQaG90byI6Imh0dHBzOi8vbGg2Lmdvb2dsZXVzZXJjb250ZW50LmNvbS8ta1VMYXh0VV9jZTAvQUFBQUFBQUFBQUkvQUFBQUFBQUFBQUEvQU1adXVjbkEycTFReU1WLUN0RUtBclRhQzgydE52NTM2QS9waG90by5qcGcifX0sIlR5cGUiOiJvYXV0aF9jbGFpbSJ9.r1MY994BC_g4qRDoDoyNmwLs0qRzBLx6_Ez-3mHQtwg',
+    error: '',
+    token: '',
     is_loading: false,
   },
 };
diff --git a/src/redux/auth/sagas.ts b/src/redux/auth/sagas.ts
index 89c75b15..71af4744 100644
--- a/src/redux/auth/sagas.ts
+++ b/src/redux/auth/sagas.ts
@@ -1,5 +1,5 @@
 import { call, delay, put, select, takeEvery, takeLatest } from 'redux-saga/effects';
-import { AUTH_USER_ACTIONS, EMPTY_USER, USER_ERRORS, USER_ROLES } from '~/redux/auth/constants';
+import { AUTH_USER_ACTIONS, EMPTY_USER, USER_ROLES } from '~/redux/auth/constants';
 import {
   authAttachSocial,
   authDropSocial,
@@ -48,49 +48,37 @@ import {
   selectAuthRestore,
   selectAuthUpdates,
   selectAuthUser,
-  selectToken,
 } from './selectors';
-import { IResultWithStatus, OAUTH_EVENT_TYPES, Unwrap } from '../types';
-import { IAuthState, IUser } from './types';
+import { OAUTH_EVENT_TYPES, Unwrap } from '../types';
+import { IAuthState } from './types';
 import { REHYDRATE, RehydrateAction } from 'redux-persist';
 import { selectModal } from '~/redux/modal/selectors';
 import { IModalState } from '~/redux/modal';
 import { DIALOGS } from '~/redux/modal/constants';
 import { ERRORS } from '~/constants/errors';
 import { messagesSet } from '~/redux/messages/actions';
+import { SagaIterator } from 'redux-saga';
 
-export function* reqWrapper(requestAction, props = {}): ReturnType<typeof requestAction> {
-  const access = yield select(selectToken);
-
-  const result = yield call(requestAction, { access, ...props });
-
-  if (result && result.status === 401) {
-    return { error: USER_ERRORS.UNAUTHORIZED, data: {} };
-  }
-
-  return result;
+function* setTokenSaga({ token }: ReturnType<typeof authSetToken>) {
+  localStorage.setItem('token', token);
 }
 
 function* sendLoginRequestSaga({ username, password }: ReturnType<typeof userSendLoginRequest>) {
   if (!username || !password) return;
 
-  const {
-    error,
-    data: { token, user },
-  }: IResultWithStatus<{ token: string; user: IUser }> = yield call(apiUserLogin, {
-    username,
-    password,
-  });
+  try {
+    const { token, user }: Unwrap<typeof apiUserLogin> = yield call(apiUserLogin, {
+      username,
+      password,
+    });
 
-  if (error) {
-    yield put(userSetLoginError(error));
-    return;
+    yield put(authSetToken(token));
+    yield put(authSetUser({ ...user, is_user: true }));
+    yield put(authLoggedIn());
+    yield put(modalSetShown(false));
+  } catch (e) {
+    yield put(userSetLoginError(e));
   }
-
-  yield put(authSetToken(token));
-  yield put(authSetUser({ ...user, is_user: true }));
-  yield put(authLoggedIn());
-  yield put(modalSetShown(false));
 }
 
 function* refreshUser() {
@@ -98,23 +86,18 @@ function* refreshUser() {
 
   if (!token) return;
 
-  const {
-    error,
-    data: { user },
-  }: IResultWithStatus<{ user: IUser }> = yield call(reqWrapper, apiAuthGetUser);
+  try {
+    const { user }: Unwrap<typeof apiAuthGetUser> = yield call(apiAuthGetUser);
 
-  if (error) {
+    yield put(authSetUser({ ...user, is_user: true }));
+  } catch (e) {
     yield put(
       authSetUser({
         ...EMPTY_USER,
         is_user: false,
       })
     );
-
-    return;
   }
-
-  yield put(authSetUser({ ...user, is_user: true }));
 }
 
 function* checkUserSaga({ key }: RehydrateAction) {
@@ -126,44 +109,43 @@ function* gotPostMessageSaga({ token }: ReturnType<typeof gotAuthPostMessage>) {
   yield put(authSetToken(token));
   yield call(refreshUser);
 
-  const { is_shown, dialog }: IModalState = yield select(selectModal);
+  const { is_shown, dialog }: ReturnType<typeof selectModal> = yield select(selectModal);
 
   if (is_shown && dialog === DIALOGS.LOGIN) yield put(modalSetShown(false));
 }
 
 function* logoutSaga() {
-  yield put(authSetToken(null));
+  yield put(authSetToken(''));
   yield put(authSetUser({ ...EMPTY_USER }));
   yield put(
     authSetUpdates({
-      last: null,
+      last: '',
       notifications: [],
     })
   );
 }
 
-function* loadProfile({ username }: ReturnType<typeof authLoadProfile>) {
+function* loadProfile({ username }: ReturnType<typeof authLoadProfile>): SagaIterator<boolean> {
   yield put(authSetProfile({ is_loading: true }));
 
-  const {
-    error,
-    data: { user },
-  } = yield call(reqWrapper, apiAuthGetUserProfile, { username });
+  try {
+    const { user }: Unwrap<typeof apiAuthGetUserProfile> = yield call(apiAuthGetUserProfile, {
+      username,
+    });
 
-  if (error || !user) {
+    yield put(authSetProfile({ is_loading: false, user }));
+    yield put(messagesSet({ messages: [] }));
+    return true;
+  } catch (error) {
     return false;
   }
-
-  yield put(authSetProfile({ is_loading: false, user }));
-  yield put(messagesSet({ messages: [] }));
-  return true;
 }
 
 function* openProfile({ username, tab = 'profile' }: ReturnType<typeof authOpenProfile>) {
   yield put(modalShowDialog(DIALOGS.PROFILE));
   yield put(authSetProfile({ tab }));
 
-  const success: boolean = yield call(loadProfile, authLoadProfile(username));
+  const success: Unwrap<typeof loadProfile> = yield call(loadProfile, authLoadProfile(username));
 
   if (!success) {
     return yield put(modalSetShown(false));
@@ -171,42 +153,39 @@ function* openProfile({ username, tab = 'profile' }: ReturnType<typeof authOpenP
 }
 
 function* getUpdates() {
-  const user: ReturnType<typeof selectAuthUser> = yield select(selectAuthUser);
+  try {
+    const user: ReturnType<typeof selectAuthUser> = yield select(selectAuthUser);
 
-  if (!user || !user.is_user || user.role === USER_ROLES.GUEST || !user.id) return;
+    if (!user || !user.is_user || user.role === USER_ROLES.GUEST || !user.id) return;
 
-  const modal: IModalState = yield select(selectModal);
-  const profile: IAuthState['profile'] = yield select(selectAuthProfile);
-  const { last, boris_commented_at }: IAuthState['updates'] = yield select(selectAuthUpdates);
-  const exclude_dialogs =
-    modal.is_shown && modal.dialog === DIALOGS.PROFILE && profile.user.id ? profile.user.id : null;
+    const modal: ReturnType<typeof selectModal> = yield select(selectModal);
+    const profile: ReturnType<typeof selectAuthProfile> = yield select(selectAuthProfile);
+    const { last, boris_commented_at }: IAuthState['updates'] = yield select(selectAuthUpdates);
+    const exclude_dialogs =
+      modal.is_shown && modal.dialog === DIALOGS.PROFILE && profile.user?.id ? profile.user.id : 0;
 
-  const { error, data }: Unwrap<ReturnType<typeof apiAuthGetUpdates>> = yield call(
-    reqWrapper,
-    apiAuthGetUpdates,
-    { exclude_dialogs, last: last || user.last_seen_messages }
-  );
+    const data: Unwrap<typeof apiAuthGetUpdates> = yield call(apiAuthGetUpdates, {
+      exclude_dialogs,
+      last: last || user.last_seen_messages,
+    });
 
-  if (error || !data) {
-    return;
-  }
+    if (data.notifications && data.notifications.length) {
+      yield put(
+        authSetUpdates({
+          last: data.notifications[0].created_at,
+          notifications: data.notifications,
+        })
+      );
+    }
 
-  if (data.notifications && data.notifications.length) {
-    yield put(
-      authSetUpdates({
-        last: data.notifications[0].created_at,
-        notifications: data.notifications,
-      })
-    );
-  }
-
-  if (data.boris && data.boris.commented_at && boris_commented_at !== data.boris.commented_at) {
-    yield put(
-      authSetUpdates({
-        boris_commented_at: data.boris.commented_at,
-      })
-    );
-  }
+    if (data.boris && data.boris.commented_at && boris_commented_at !== data.boris.commented_at) {
+      yield put(
+        authSetUpdates({
+          boris_commented_at: data.boris.commented_at,
+        })
+      );
+    }
+  } catch (error) {}
 }
 
 function* startPollingSaga() {
@@ -219,148 +198,136 @@ function* startPollingSaga() {
 function* setLastSeenMessages({ last_seen_messages }: ReturnType<typeof authSetLastSeenMessages>) {
   if (!Date.parse(last_seen_messages)) return;
 
-  yield call(reqWrapper, apiUpdateUser, { user: { last_seen_messages } });
+  yield call(apiUpdateUser, { user: { last_seen_messages } });
 }
 
-function* patchUser({ user }: ReturnType<typeof authPatchUser>) {
-  const me = yield select(selectAuthUser);
+function* patchUser(payload: ReturnType<typeof authPatchUser>) {
+  const me: ReturnType<typeof selectAuthUser> = yield select(selectAuthUser);
 
-  const { error, data } = yield call(reqWrapper, apiUpdateUser, { user });
+  try {
+    const { user, errors }: Unwrap<typeof apiUpdateUser> = yield call(apiUpdateUser, {
+      user: payload.user,
+    });
 
-  if (error || !data.user || data.errors) {
-    return yield put(authSetProfile({ patch_errors: data.errors }));
+    if (errors && Object.keys(errors).length) {
+      yield put(authSetProfile({ patch_errors: errors }));
+      return;
+    }
+
+    yield put(authSetUser({ ...me, ...user }));
+    yield put(authSetProfile({ user: { ...me, ...user }, tab: 'profile' }));
+  } catch (error) {
+    return;
   }
-
-  yield put(authSetUser({ ...me, ...data.user }));
-  yield put(authSetProfile({ user: { ...me, ...data.user }, tab: 'profile' }));
 }
 
 function* requestRestoreCode({ field }: ReturnType<typeof authRequestRestoreCode>) {
   if (!field) return;
 
-  yield put(authSetRestore({ error: null, is_loading: true }));
-  const { error, data } = yield call(apiRequestRestoreCode, { field });
+  try {
+    yield put(authSetRestore({ error: '', is_loading: true }));
+    yield call(apiRequestRestoreCode, {
+      field,
+    });
 
-  if (data.error || error) {
-    return yield put(authSetRestore({ is_loading: false, error: data.error || error }));
+    yield put(authSetRestore({ is_loading: false, is_succesfull: true }));
+  } catch (error) {
+    return yield put(authSetRestore({ is_loading: false, error }));
   }
-
-  yield put(authSetRestore({ is_loading: false, is_succesfull: true }));
 }
 
 function* showRestoreModal({ code }: ReturnType<typeof authShowRestoreModal>) {
-  if (!code && !code.length) {
-    return yield put(authSetRestore({ error: ERRORS.CODE_IS_INVALID, is_loading: false }));
+  try {
+    if (!code && !code.length) {
+      return yield put(authSetRestore({ error: ERRORS.CODE_IS_INVALID, is_loading: false }));
+    }
+
+    yield put(authSetRestore({ user: undefined, is_loading: true }));
+
+    const data: Unwrap<typeof apiCheckRestoreCode> = yield call(apiCheckRestoreCode, { code });
+
+    yield put(authSetRestore({ user: data.user, code, is_loading: false }));
+    yield put(modalShowDialog(DIALOGS.RESTORE_PASSWORD));
+  } catch (error) {
+    yield put(authSetRestore({ is_loading: false, error: error || ERRORS.CODE_IS_INVALID }));
+    yield put(modalShowDialog(DIALOGS.RESTORE_PASSWORD));
   }
-
-  yield put(authSetRestore({ user: null, is_loading: true }));
-
-  const { error, data } = yield call(apiCheckRestoreCode, { code });
-
-  if (data.error || error || !data.user) {
-    yield put(
-      authSetRestore({ is_loading: false, error: data.error || error || ERRORS.CODE_IS_INVALID })
-    );
-
-    return yield put(modalShowDialog(DIALOGS.RESTORE_PASSWORD));
-  }
-
-  yield put(authSetRestore({ user: data.user, code, is_loading: false }));
-  yield put(modalShowDialog(DIALOGS.RESTORE_PASSWORD));
 }
 
 function* restorePassword({ password }: ReturnType<typeof authRestorePassword>) {
-  if (!password) return;
+  try {
+    if (!password) return;
 
-  yield put(authSetRestore({ is_loading: true }));
-  const { code } = yield select(selectAuthRestore);
+    yield put(authSetRestore({ is_loading: true }));
+    const { code } = yield select(selectAuthRestore);
 
-  if (!code) {
-    return yield put(authSetRestore({ error: ERRORS.CODE_IS_INVALID, is_loading: false }));
+    if (!code) {
+      return yield put(authSetRestore({ error: ERRORS.CODE_IS_INVALID, is_loading: false }));
+    }
+
+    const data: Unwrap<typeof apiRestoreCode> = yield call(apiRestoreCode, { code, password });
+
+    yield put(authSetToken(data.token));
+    yield put(authSetUser(data.user));
+
+    yield put(authSetRestore({ is_loading: false, is_succesfull: true, error: '' }));
+
+    yield call(refreshUser);
+  } catch (error) {
+    return yield put(authSetRestore({ is_loading: false, error: error || ERRORS.CODE_IS_INVALID }));
   }
-
-  const { error, data } = yield call(apiRestoreCode, { code, password });
-
-  if (data.error || error || !data.user || !data.token) {
-    return yield put(
-      authSetRestore({ is_loading: false, error: data.error || error || ERRORS.CODE_IS_INVALID })
-    );
-  }
-
-  yield put(authSetToken(data.token));
-  yield put(authSetUser(data.user));
-
-  yield put(authSetRestore({ is_loading: false, is_succesfull: true, error: null }));
-
-  yield call(refreshUser);
 }
 
 function* getSocials() {
-  yield put(authSetSocials({ is_loading: true, error: '' }));
-
   try {
-    const { data, error }: Unwrap<ReturnType<typeof apiGetSocials>> = yield call(
-      reqWrapper,
-      apiGetSocials,
-      {}
-    );
-
-    if (error) {
-      throw new Error(error);
-    }
-
-    yield put(authSetSocials({ is_loading: false, accounts: data.accounts, error: '' }));
-  } catch (e) {
-    yield put(authSetSocials({ is_loading: false, error: e.toString() }));
+    yield put(authSetSocials({ is_loading: true, error: '' }));
+    const data: Unwrap<typeof apiGetSocials> = yield call(apiGetSocials);
+    yield put(authSetSocials({ accounts: data.accounts }));
+  } catch (error) {
+    yield put(authSetSocials({ error }));
+  } finally {
+    yield put(authSetSocials({ is_loading: false }));
   }
 }
 
+// TODO: start from here
 function* dropSocial({ provider, id }: ReturnType<typeof authDropSocial>) {
   try {
     yield put(authSetSocials({ error: '' }));
-    const { error }: Unwrap<ReturnType<typeof apiDropSocial>> = yield call(
-      reqWrapper,
-      apiDropSocial,
-      { id, provider }
-    );
-
-    if (error) {
-      throw new Error(error);
-    }
+    yield call(apiDropSocial, {
+      id,
+      provider,
+    });
 
     yield call(getSocials);
-  } catch (e) {
-    yield put(authSetSocials({ error: e.message }));
+  } catch (error) {
+    yield put(authSetSocials({ error }));
   }
 }
 
 function* attachSocial({ token }: ReturnType<typeof authAttachSocial>) {
-  if (!token) return;
-
   try {
+    if (!token) return;
+
     yield put(authSetSocials({ error: '', is_loading: true }));
 
-    const { data, error }: Unwrap<ReturnType<typeof apiAttachSocial>> = yield call(
-      reqWrapper,
-      apiAttachSocial,
-      { token }
-    );
-
-    if (error) {
-      throw new Error(error);
-    }
+    const data: Unwrap<typeof apiAttachSocial> = yield call(apiAttachSocial, {
+      token,
+    });
 
     const {
       socials: { accounts },
     }: ReturnType<typeof selectAuthProfile> = yield select(selectAuthProfile);
 
     if (accounts.some(it => it.id === data.account.id && it.provider === data.account.provider)) {
-      yield put(authSetSocials({ is_loading: false }));
-    } else {
-      yield put(authSetSocials({ is_loading: false, accounts: [...accounts, data.account] }));
+      return;
     }
+
+    yield put(authSetSocials({ accounts: [...accounts, data.account] }));
   } catch (e) {
-    yield put(authSetSocials({ is_loading: false, error: e.message }));
+    yield put(authSetSocials({ error: e.message }));
+  } finally {
+    yield put(authSetSocials({ is_loading: false }));
   }
 }
 
@@ -368,10 +335,9 @@ function* loginWithSocial({ token }: ReturnType<typeof authLoginWithSocial>) {
   try {
     yield put(userSetLoginError(''));
 
-    const {
-      data,
-      error,
-    }: Unwrap<ReturnType<typeof apiLoginWithSocial>> = yield call(apiLoginWithSocial, { token });
+    const data: Unwrap<typeof apiLoginWithSocial> = yield call(apiLoginWithSocial, {
+      token,
+    });
 
     // Backend asks us for account registration
     if (data?.needs_register) {
@@ -380,18 +346,14 @@ function* loginWithSocial({ token }: ReturnType<typeof authLoginWithSocial>) {
       return;
     }
 
-    if (error) {
-      throw new Error(error);
-    }
-
     if (data.token) {
       yield put(authSetToken(data.token));
       yield call(refreshUser);
       yield put(modalSetShown(false));
       return;
     }
-  } catch (e) {
-    yield put(userSetLoginError(e.message));
+  } catch (error) {
+    yield put(userSetLoginError(error));
   }
 }
 
@@ -414,23 +376,19 @@ function* authRegisterSocial({ username, password }: ReturnType<typeof authSendR
   try {
     yield put(authSetRegisterSocial({ error: '' }));
 
-    const { token }: Unwrap<ReturnType<typeof selectAuthRegisterSocial>> = yield select(
+    const { token }: Unwrap<typeof selectAuthRegisterSocial> = yield select(
       selectAuthRegisterSocial
     );
 
-    const { data, error }: Unwrap<ReturnType<typeof apiLoginWithSocial>> = yield call(
-      apiLoginWithSocial,
-      {
-        token,
-        username,
-        password,
-      }
-    );
+    const data: Unwrap<typeof apiLoginWithSocial> = yield call(apiLoginWithSocial, {
+      token,
+      username,
+      password,
+    });
 
     if (data?.errors) {
       yield put(authSetRegisterSocialErrors(data.errors));
-    } else if (data?.error) {
-      throw new Error(error);
+      return;
     }
 
     if (data.token) {
@@ -449,6 +407,7 @@ function* authSaga() {
   yield takeLatest([REHYDRATE, AUTH_USER_ACTIONS.LOGGED_IN], startPollingSaga);
 
   yield takeLatest(AUTH_USER_ACTIONS.LOGOUT, logoutSaga);
+  yield takeLatest(AUTH_USER_ACTIONS.SET_TOKEN, setTokenSaga);
   yield takeLatest(AUTH_USER_ACTIONS.SEND_LOGIN_REQUEST, sendLoginRequestSaga);
   yield takeLatest(AUTH_USER_ACTIONS.GOT_AUTH_POST_MESSAGE, gotPostMessageSaga);
   yield takeLatest(AUTH_USER_ACTIONS.OPEN_PROFILE, openProfile);
diff --git a/src/redux/auth/selectors.ts b/src/redux/auth/selectors.ts
index aa3aa475..6f6ed43b 100644
--- a/src/redux/auth/selectors.ts
+++ b/src/redux/auth/selectors.ts
@@ -5,7 +5,7 @@ export const selectUser = (state: IState) => state.auth.user;
 export const selectToken = (state: IState) => state.auth.token;
 export const selectAuthLogin = (state: IState) => state.auth.login;
 export const selectAuthProfile = (state: IState) => state.auth.profile;
-export const selectAuthProfileUsername = (state: IState) => state.auth.profile.user.username;
+export const selectAuthProfileUsername = (state: IState) => state.auth.profile.user?.username;
 export const selectAuthUser = (state: IState) => state.auth.user;
 export const selectAuthUpdates = (state: IState) => state.auth.updates;
 export const selectAuthRestore = (state: IState) => state.auth.restore;
diff --git a/src/redux/auth/transforms.ts b/src/redux/auth/transforms.ts
index 513b07fe..69c68fc8 100644
--- a/src/redux/auth/transforms.ts
+++ b/src/redux/auth/transforms.ts
@@ -1,13 +1,18 @@
 import { IResultWithStatus } from '~/redux/types';
 import { HTTP_RESPONSES } from '~/utils/api';
 
-export const userLoginTransform = ({ status, data, error }: IResultWithStatus<any>): IResultWithStatus<any> => {
+export const userLoginTransform = ({
+  status,
+  data,
+  error,
+}: IResultWithStatus<any>): IResultWithStatus<any> => {
   switch (true) {
-    case (status === HTTP_RESPONSES.UNAUTHORIZED || !data.token) && status !== HTTP_RESPONSES.CONNECTION_REFUSED:
+    case (status === HTTP_RESPONSES.UNAUTHORIZED || !data.token) &&
+      status !== HTTP_RESPONSES.CONNECTION_REFUSED:
       return { status, data, error: 'Пользователь не найден' };
 
     case status === 200:
-      return { status, data, error: null };
+      return { status, data, error: '' };
 
     default:
       return { status, data, error: error || 'Неизвестная ошибка' };
diff --git a/src/redux/auth/types.ts b/src/redux/auth/types.ts
index 52d417d4..55d7ae82 100644
--- a/src/redux/auth/types.ts
+++ b/src/redux/auth/types.ts
@@ -1,4 +1,4 @@
-import { IFile, INotification } from '../types';
+import { IFile, INotification, IResultWithStatus } from '../types';
 
 export interface IToken {
   access: string;
@@ -10,8 +10,8 @@ export interface IUser {
   username: string;
   email: string;
   role: string;
-  photo: IFile;
-  cover: IFile;
+  photo?: IFile;
+  cover?: IFile;
   name: string;
   fullname: string;
   description: string;
@@ -53,7 +53,7 @@ export type IAuthState = Readonly<{
     tab: 'profile' | 'messages' | 'settings';
     is_loading: boolean;
 
-    user: IUser;
+    user?: IUser;
     patch_errors: Record<string, string>;
 
     socials: {
@@ -65,7 +65,7 @@ export type IAuthState = Readonly<{
 
   restore: {
     code: string;
-    user: Pick<IUser, 'username' | 'photo'>;
+    user?: Pick<IUser, 'username' | 'photo'>;
     is_loading: boolean;
     is_succesfull: boolean;
     error: string;
@@ -81,3 +81,52 @@ export type IAuthState = Readonly<{
     is_loading: boolean;
   };
 }>;
+
+export type ApiWithTokenRequest = { access: string };
+
+export type ApiUserLoginRequest = Record<'username' | 'password', string>;
+export type ApiUserLoginResult = { token: string; user: IUser };
+
+export type ApiAuthGetUserRequest = {};
+export type ApiAuthGetUserResult = { user: IUser };
+
+export type ApiUpdateUserRequest = { user: Partial<IUser> };
+export type ApiUpdateUserResult = { user: IUser; errors: Record<Partial<keyof IUser>, string> };
+
+export type ApiAuthGetUserProfileRequest = { username: string };
+export type ApiAuthGetUserProfileResult = { user: IUser };
+
+export type ApiAuthGetUpdatesRequest = {
+  exclude_dialogs: number;
+  last: string;
+};
+export type ApiAuthGetUpdatesResult = {
+  notifications: INotification[];
+  boris: { commented_at: string };
+};
+
+export type ApiCheckRestoreCodeRequest = { code: string };
+export type ApiCheckRestoreCodeResult = { user: IUser };
+
+export type ApiRestoreCodeRequest = { code: string; password: string };
+export type ApiRestoreCodeResult = { token: string; user: IUser };
+
+export type ApiGetSocialsResult = { accounts: ISocialAccount[] };
+
+export type ApiDropSocialRequest = { id: string; provider: string };
+export type ApiDropSocialResult = { accounts: ISocialAccount[] };
+
+export type ApiAttachSocialRequest = { token: string };
+export type ApiAttachSocialResult = { account: ISocialAccount };
+
+export type ApiLoginWithSocialRequest = {
+  token: string;
+  username?: string;
+  password?: string;
+};
+
+export type ApiLoginWithSocialResult = {
+  token: string;
+  errors: Record<string, string>;
+  needs_register: boolean;
+};
diff --git a/src/redux/boris/sagas.ts b/src/redux/boris/sagas.ts
index 1f92b6a3..cacb77f4 100644
--- a/src/redux/boris/sagas.ts
+++ b/src/redux/boris/sagas.ts
@@ -8,10 +8,8 @@ function* loadStats() {
   yield put(borisSetStats({ is_loading: true }));
 
   try {
-    const git: Unwrap<ReturnType<typeof getBorisGitStats>> = yield call(getBorisGitStats);
-    const backend: Unwrap<ReturnType<typeof getBorisBackendStats>> = yield call(
-      getBorisBackendStats
-    );
+    const git: Unwrap<typeof getBorisGitStats> = yield call(getBorisGitStats);
+    const backend: Unwrap<typeof getBorisBackendStats> = yield call(getBorisBackendStats);
 
     yield put(borisSetStats({ git, backend: backend.data, is_loading: false }));
   } catch (e) {
diff --git a/src/redux/flow/sagas.ts b/src/redux/flow/sagas.ts
index 0929999b..d8e5ec36 100644
--- a/src/redux/flow/sagas.ts
+++ b/src/redux/flow/sagas.ts
@@ -14,7 +14,7 @@ import {
 } from './actions';
 import { IResultWithStatus, INode, Unwrap } from '../types';
 import { selectFlowNodes, selectFlow } from './selectors';
-import { reqWrapper } from '../auth/sagas';
+import { wrap } from '../auth/sagas';
 import { postCellView, getSearchResults } from './api';
 import { IFlowState } from './reducer';
 import { uniq } from 'ramda';
@@ -47,7 +47,7 @@ function* onGetFlow() {
     recent: IFlowState['recent'];
     updated: IFlowState['updated'];
     valid: INode['id'][];
-  }> = yield call(reqWrapper, getNodeDiff, {
+  }> = yield call(wrap, getNodeDiff, {
     start: new Date().toISOString(),
     end: new Date().toISOString(),
     with_heroes: true,
@@ -71,7 +71,7 @@ function* onSetCellView({ id, flow }: ReturnType<typeof flowSetCellView>) {
   const nodes = yield select(selectFlowNodes);
   yield put(flowSetNodes(nodes.map(node => (node.id === id ? { ...node, flow } : node))));
 
-  const { data, error } = yield call(reqWrapper, postCellView, { id, flow });
+  const { data, error } = yield call(wrap, postCellView, { id, flow });
 
   // TODO: error handling
 }
@@ -83,7 +83,7 @@ function* getMore() {
   const start = nodes && nodes[0] && nodes[0].created_at;
   const end = nodes && nodes[nodes.length - 1] && nodes[nodes.length - 1].created_at;
 
-  const { error, data } = yield call(reqWrapper, getNodeDiff, {
+  const { error, data } = yield call(wrap, getNodeDiff, {
     start,
     end,
     with_heroes: false,
@@ -124,13 +124,9 @@ function* changeSearch({ search }: ReturnType<typeof flowChangeSearch>) {
 
   yield delay(500);
 
-  const { data, error }: Unwrap<ReturnType<typeof getSearchResults>> = yield call(
-    reqWrapper,
-    getSearchResults,
-    {
-      ...search,
-    }
-  );
+  const { data, error }: Unwrap<typeof getSearchResults> = yield call(wrap, getSearchResults, {
+    ...search,
+  });
 
   if (error) {
     yield put(flowSetSearch({ is_loading: false, results: [], total: 0 }));
@@ -155,11 +151,8 @@ function* loadMoreSearch() {
 
   const { search }: ReturnType<typeof selectFlow> = yield select(selectFlow);
 
-  const {
-    result,
-    delay,
-  }: { result: Unwrap<ReturnType<typeof getSearchResults>>; delay: any } = yield race({
-    result: call(reqWrapper, getSearchResults, {
+  const { result, delay }: { result: Unwrap<typeof getSearchResults>; delay: any } = yield race({
+    result: call(wrap, getSearchResults, {
       ...search,
       skip: search.results.length,
     }),
diff --git a/src/redux/messages/index.ts b/src/redux/messages/index.ts
index c868715b..914eb0e3 100644
--- a/src/redux/messages/index.ts
+++ b/src/redux/messages/index.ts
@@ -12,7 +12,7 @@ export interface IMessagesState {
 const INITIAL_STATE: IMessagesState = {
   is_loading_messages: true,
   is_sending_messages: false,
-  error: null,
+  error: '',
   messages: [],
 };
 
diff --git a/src/redux/messages/sagas.ts b/src/redux/messages/sagas.ts
index c45bc6ee..cbce096d 100644
--- a/src/redux/messages/sagas.ts
+++ b/src/redux/messages/sagas.ts
@@ -12,7 +12,7 @@ import {
 } from '~/redux/messages/api';
 import { ERRORS } from '~/constants/errors';
 import { IMessageNotification, Unwrap } from '~/redux/types';
-import { reqWrapper } from '~/redux/auth/sagas';
+import { wrap } from '~/redux/auth/sagas';
 import {
   messagesDeleteMessage,
   messagesGetMessages,
@@ -39,11 +39,8 @@ function* getMessages({ username }: ReturnType<typeof messagesGetMessages>) {
     })
   );
 
-  const {
-    error,
-    data,
-  }: Unwrap<ReturnType<typeof apiMessagesGetUserMessages>> = yield call(
-    reqWrapper,
+  const { error, data }: Unwrap<typeof apiMessagesGetUserMessages> = yield call(
+    wrap,
     apiMessagesGetUserMessages,
     { username }
   );
@@ -82,8 +79,8 @@ function* sendMessage({ message, onSuccess }: ReturnType<typeof messagesSendMess
 
   yield put(messagesSet({ is_sending_messages: true, error: null }));
 
-  const { error, data }: Unwrap<ReturnType<typeof apiMessagesSendMessage>> = yield call(
-    reqWrapper,
+  const { error, data }: Unwrap<typeof apiMessagesSendMessage> = yield call(
+    wrap,
     apiMessagesSendMessage,
     {
       username,
@@ -138,8 +135,8 @@ function* deleteMessage({ id, is_locked }: ReturnType<typeof messagesDeleteMessa
 
   yield put(messagesSet({ is_sending_messages: true, error: null }));
 
-  const { error, data }: Unwrap<ReturnType<typeof apiMessagesDeleteMessage>> = yield call(
-    reqWrapper,
+  const { error, data }: Unwrap<typeof apiMessagesDeleteMessage> = yield call(
+    wrap,
     apiMessagesDeleteMessage,
     {
       username,
@@ -187,11 +184,8 @@ function* refreshMessages({}: ReturnType<typeof messagesRefreshMessages>) {
 
   const after = messages.length > 0 ? messages[0].created_at : undefined;
 
-  const {
-    data,
-    error,
-  }: Unwrap<ReturnType<typeof apiMessagesGetUserMessages>> = yield call(
-    reqWrapper,
+  const { data, error }: Unwrap<typeof apiMessagesGetUserMessages> = yield call(
+    wrap,
     apiMessagesGetUserMessages,
     { username, after }
   );
diff --git a/src/redux/modal/index.ts b/src/redux/modal/index.ts
index 7b9dfaee..95aa0cb0 100644
--- a/src/redux/modal/index.ts
+++ b/src/redux/modal/index.ts
@@ -14,7 +14,7 @@ export interface IModalState {
 
 const INITIAL_STATE: IModalState = {
   is_shown: false,
-  dialog: null,
+  dialog: '',
   photoswipe: {
     images: [],
     index: 0,
diff --git a/src/redux/node/sagas.ts b/src/redux/node/sagas.ts
index 95f43082..53c85f8e 100644
--- a/src/redux/node/sagas.ts
+++ b/src/redux/node/sagas.ts
@@ -2,7 +2,13 @@ import { all, call, delay, put, select, takeLatest, takeLeading } from 'redux-sa
 import { push } from 'connected-react-router';
 import { omit } from 'ramda';
 
-import { COMMENTS_DISPLAY, EMPTY_COMMENT, EMPTY_NODE, NODE_ACTIONS, NODE_EDITOR_DATA } from './constants';
+import {
+  COMMENTS_DISPLAY,
+  EMPTY_COMMENT,
+  EMPTY_NODE,
+  NODE_ACTIONS,
+  NODE_EDITOR_DATA,
+} from './constants';
 import {
   nodeCancelCommentEdit,
   nodeCreate,
@@ -39,7 +45,7 @@ import {
   postNodeStar,
   updateNodeTags,
 } from './api';
-import { reqWrapper } from '../auth/sagas';
+import { wrap } from '../auth/sagas';
 import { flowSetNodes, flowSetUpdated } from '../flow/actions';
 import { ERRORS } from '~/constants/errors';
 import { modalSetShown, modalShowDialog } from '../modal/actions';
@@ -77,7 +83,7 @@ function* onNodeSave({ node }: ReturnType<typeof nodeSave>) {
   const {
     error,
     data: { errors, node: result },
-  } = yield call(reqWrapper, postNode, { node });
+  } = yield call(wrap, postNode, { node });
 
   if (errors && Object.values(errors).length > 0) {
     return yield put(nodeSetSaveErrors(errors));
@@ -117,15 +123,11 @@ function* onNodeLoadMoreComments() {
     comments,
   }: ReturnType<typeof selectNode> = yield select(selectNode);
 
-  const { data, error }: Unwrap<ReturnType<typeof getNodeComments>> = yield call(
-    reqWrapper,
-    getNodeComments,
-    {
-      id,
-      take: COMMENTS_DISPLAY,
-      skip: comments.length,
-    }
-  );
+  const { data, error }: Unwrap<typeof getNodeComments> = yield call(wrap, getNodeComments, {
+    id,
+    take: COMMENTS_DISPLAY,
+    skip: comments.length,
+  });
 
   const current: ReturnType<typeof selectNode> = yield select(selectNode);
 
@@ -147,7 +149,7 @@ function* onNodeLoad({ id, order = 'ASC' }: ReturnType<typeof nodeLoadNode>) {
 
   const {
     data: { node, error },
-  } = yield call(reqWrapper, getNode, { id });
+  } = yield call(wrap, getNode, { id });
 
   if (error || !node || !node.id) {
     yield put(push(URLS.ERRORS.NOT_FOUND));
@@ -166,8 +168,8 @@ function* onNodeLoad({ id, order = 'ASC' }: ReturnType<typeof nodeLoadNode>) {
       data: { related },
     },
   } = yield all({
-    comments: call(reqWrapper, getNodeComments, { id, take: COMMENTS_DISPLAY, skip: 0 }),
-    related: call(reqWrapper, getNodeRelated, { id }),
+    comments: call(wrap, getNodeComments, { id, take: COMMENTS_DISPLAY, skip: 0 }),
+    related: call(wrap, getNodeRelated, { id }),
   });
 
   yield put(
@@ -190,14 +192,10 @@ function* onNodeLoad({ id, order = 'ASC' }: ReturnType<typeof nodeLoadNode>) {
 }
 
 function* onPostComment({ nodeId, comment, callback }: ReturnType<typeof nodePostLocalComment>) {
-  const { data, error }: Unwrap<ReturnType<typeof postNodeComment>> = yield call(
-    reqWrapper,
-    postNodeComment,
-    {
-      data: comment,
-      id: nodeId,
-    }
-  );
+  const { data, error }: Unwrap<typeof postNodeComment> = yield call(wrap, postNodeComment, {
+    data: comment,
+    id: nodeId,
+  });
 
   if (error || !data.comment) {
     return callback(error);
@@ -237,7 +235,7 @@ function* onUpdateTags({ id, tags }: ReturnType<typeof nodeUpdateTags>) {
 
   const {
     data: { node },
-  }: IResultWithStatus<{ node: INode }> = yield call(reqWrapper, updateNodeTags, { id, tags });
+  }: IResultWithStatus<{ node: INode }> = yield call(wrap, updateNodeTags, { id, tags });
 
   const { current } = yield select(selectNode);
 
@@ -259,7 +257,7 @@ function* onEditSaga({ id }: ReturnType<typeof nodeEdit>) {
   const {
     data: { node },
     error,
-  } = yield call(reqWrapper, getNode, { id });
+  } = yield call(wrap, getNode, { id });
 
   if (error || !node || !node.type || !NODE_EDITOR_DIALOGS[node.type])
     return yield put(modalSetShown(false));
@@ -282,7 +280,7 @@ function* onLikeSaga({ id }: ReturnType<typeof nodeLike>) {
     like_count: is_liked ? Math.max(like_count - 1, 0) : like_count + 1,
   });
 
-  const { data, error } = yield call(reqWrapper, postNodeLike, { id });
+  const { data, error } = yield call(wrap, postNodeLike, { id });
 
   if (!error || data.is_liked === !is_liked) return; // ok and matches
 
@@ -297,7 +295,7 @@ function* onStarSaga({ id }: ReturnType<typeof nodeLike>) {
 
   yield call(updateNodeEverywhere, { ...current, is_heroic: !is_heroic });
 
-  const { data, error } = yield call(reqWrapper, postNodeStar, { id });
+  const { data, error } = yield call(wrap, postNodeStar, { id });
 
   if (!error || data.is_heroic === !is_heroic) return; // ok and matches
 
@@ -315,7 +313,7 @@ function* onLockSaga({ id, is_locked }: ReturnType<typeof nodeLock>) {
     deleted_at: is_locked ? new Date().toISOString() : null,
   });
 
-  const { error } = yield call(reqWrapper, postNodeLock, { id, is_locked });
+  const { error } = yield call(wrap, postNodeLock, { id, is_locked });
 
   if (error) return yield call(updateNodeEverywhere, { ...current, deleted_at });
 }
@@ -333,7 +331,7 @@ function* onLockCommentSaga({ id, is_locked }: ReturnType<typeof nodeLockComment
     )
   );
 
-  yield call(reqWrapper, postNodeLockComment, { current: current.id, id, is_locked });
+  yield call(wrap, postNodeLockComment, { current: current.id, id, is_locked });
 }
 
 function* onEditCommentSaga({ id }: ReturnType<typeof nodeEditComment>) {
diff --git a/src/redux/player/sagas.ts b/src/redux/player/sagas.ts
index 36e4b8b4..d4a199d8 100644
--- a/src/redux/player/sagas.ts
+++ b/src/redux/player/sagas.ts
@@ -57,7 +57,7 @@ function* getYoutubeInfo() {
     }
 
     if (ticker || ids.length > 25) {
-      const result: Unwrap<ReturnType<typeof getEmbedYoutube>> = yield call(getEmbedYoutube, ids);
+      const result: Unwrap<typeof getEmbedYoutube> = yield call(getEmbedYoutube, ids);
 
       if (!result.error && result.data.items && Object.keys(result.data.items).length) {
         const { youtubes }: ReturnType<typeof selectPlayer> = yield select(selectPlayer);
diff --git a/src/redux/store.ts b/src/redux/store.ts
index a26248eb..8890775e 100644
--- a/src/redux/store.ts
+++ b/src/redux/store.ts
@@ -26,7 +26,7 @@ import playerSaga from '~/redux/player/sagas';
 import modal, { IModalState } from '~/redux/modal';
 import { modalSaga } from './modal/sagas';
 
-import { authOpenProfile, gotAuthPostMessage } from './auth/actions';
+import { authLogout, authOpenProfile, gotAuthPostMessage } from './auth/actions';
 
 import boris, { IBorisState } from './boris/reducer';
 import borisSaga from './boris/sagas';
@@ -36,6 +36,9 @@ import messagesSaga from './messages/sagas';
 
 import tag, { ITagState } from './tag';
 import tagSaga from './tag/sagas';
+import { AxiosError } from 'axios';
+import { api } from '~/utils/api';
+import { assocPath } from 'ramda';
 
 const authPersistConfig: PersistConfig = {
   key: 'auth',
@@ -116,5 +119,28 @@ export function configureStore(): {
 
   const persistor = persistStore(store);
 
+  // Pass token to axios
+  api.interceptors.request.use(options => {
+    const token = store.getState().auth.token;
+
+    if (!token) {
+      return options;
+    }
+
+    return assocPath(['headers', 'authorization'], `Bearer ${token}`, options);
+  });
+
+  // Logout on 401
+  api.interceptors.response.use(undefined, (error: AxiosError<{ message: string }>) => {
+    if (error.response?.status === 401) {
+      store.dispatch(authLogout());
+    }
+
+    console.log('Вот что случилось на сервере:', error);
+    throw new Error(
+      error?.response?.data?.message || error?.message || error?.response?.statusText
+    );
+  });
+
   return { store, persistor };
 }
diff --git a/src/redux/tag/sagas.ts b/src/redux/tag/sagas.ts
index ade289bd..00bcdf35 100644
--- a/src/redux/tag/sagas.ts
+++ b/src/redux/tag/sagas.ts
@@ -1,7 +1,12 @@
 import { TAG_ACTIONS } from '~/redux/tag/constants';
 import { call, delay, put, select, takeLatest } from 'redux-saga/effects';
-import { tagLoadAutocomplete, tagLoadNodes, tagSetAutocomplete, tagSetNodes, } from '~/redux/tag/actions';
-import { reqWrapper } from '~/redux/auth/sagas';
+import {
+  tagLoadAutocomplete,
+  tagLoadNodes,
+  tagSetAutocomplete,
+  tagSetNodes,
+} from '~/redux/tag/actions';
+import { wrap } from '~/redux/auth/sagas';
 import { selectTagNodes } from '~/redux/tag/selectors';
 import { getTagAutocomplete, getTagNodes } from '~/redux/tag/api';
 import { Unwrap } from '~/redux/types';
@@ -11,11 +16,11 @@ function* loadTagNodes({ tag }: ReturnType<typeof tagLoadNodes>) {
 
   try {
     const { list }: ReturnType<typeof selectTagNodes> = yield select(selectTagNodes);
-    const { data, error }: Unwrap<ReturnType<typeof getTagNodes>> = yield call(
-      reqWrapper,
-      getTagNodes,
-      { tag, limit: 18, offset: list.length }
-    );
+    const { data, error }: Unwrap<typeof getTagNodes> = yield call(wrap, getTagNodes, {
+      tag,
+      limit: 18,
+      offset: list.length,
+    });
 
     if (error) throw new Error(error);
 
@@ -33,8 +38,8 @@ function* loadAutocomplete({ search, exclude }: ReturnType<typeof tagLoadAutocom
     yield put(tagSetAutocomplete({ isLoading: true }));
     yield delay(100);
 
-    const { data, error }: Unwrap<ReturnType<typeof getTagAutocomplete>> = yield call(
-      reqWrapper,
+    const { data, error }: Unwrap<typeof getTagAutocomplete> = yield call(
+      wrap,
       getTagAutocomplete,
       { search, exclude }
     );
diff --git a/src/redux/types.ts b/src/redux/types.ts
index d38db98c..9155b1ff 100644
--- a/src/redux/types.ts
+++ b/src/redux/types.ts
@@ -192,7 +192,13 @@ export type INodeNotification = {
 
 export type INotification = IMessageNotification | ICommentNotification;
 
-export type Unwrap<T> = T extends Promise<infer U> ? U : T;
+export type Unwrap<T> = T extends (...args: any) => Promise<any>
+  ? T extends (...args: any) => Promise<infer U>
+    ? U
+    : T
+  : T extends () => Iterator<any, infer U, any>
+  ? U
+  : any;
 
 export interface IEmbed {
   provider: string;
diff --git a/src/redux/uploads/sagas.ts b/src/redux/uploads/sagas.ts
index b24ada1c..e832d16f 100644
--- a/src/redux/uploads/sagas.ts
+++ b/src/redux/uploads/sagas.ts
@@ -8,7 +8,7 @@ import {
   uploadDropStatus,
   uploadAddFile,
 } from './actions';
-import { reqWrapper } from '../auth/sagas';
+import { wrap } from '../auth/sagas';
 import { createUploader, uploadGetThumb } from '~/utils/uploader';
 import { HTTP_RESPONSES } from '~/utils/api';
 import { IFileWithUUID, IFile, IUploadProgressHandler } from '../types';
@@ -20,7 +20,7 @@ function* uploadCall({
   type,
   onProgress,
 }: IFileWithUUID & { onProgress: IUploadProgressHandler }) {
-  return yield call(reqWrapper, postUploadFile, {
+  return yield call(wrap, postUploadFile, {
     file,
     temp_id,
     type,
diff --git a/src/utils/api/index.ts b/src/utils/api/index.ts
index e7268343..dc68cdee 100644
--- a/src/utils/api/index.ts
+++ b/src/utils/api/index.ts
@@ -1,4 +1,4 @@
-import axios, { AxiosRequestConfig } from 'axios';
+import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
 import { push } from 'connected-react-router';
 import { API } from '~/constants/api';
 import { store } from '~/redux/store';
@@ -50,3 +50,5 @@ export const configWithToken = (
   ...config,
   headers: { ...(config.headers || {}), Authorization: `Bearer ${access}` },
 });
+
+export const cleanResult = <T extends any>(response: AxiosResponse<T>): T => response?.data;
diff --git a/src/utils/validators.ts b/src/utils/validators.ts
index 8c50b790..0691546f 100644
--- a/src/utils/validators.ts
+++ b/src/utils/validators.ts
@@ -3,13 +3,13 @@ import { IMAGE_MIME_TYPES } from '~/utils/uploader';
 
 const isValidEmail = (email: string): boolean =>
   !!email &&
-  String(email) &&
+  !!String(email) &&
   !!String(email).match(
     /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/
   );
 
 const isLikeEmail = (email: string): boolean =>
-  !!email && String(email) && !!String(email).match(/^([^\@]+)@([^\@]+)\.([^\@]+)$$/);
+  !!email && !!String(email) && !!String(email).match(/^([^\@]+)@([^\@]+)\.([^\@]+)$$/);
 
 const isNonEmpty = (value: string): boolean => !!value && value.trim().length > 0;
 const isLikePhone = isNonEmpty;
diff --git a/tsconfig.json b/tsconfig.json
index d0a4b9ad..6583d3c1 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -11,7 +11,7 @@
     "skipLibCheck": true,
     "esModuleInterop": true,
     "allowSyntheticDefaultImports": true,
-    "strict": false,
+    "strict": true,
     "forceConsistentCasingInFileNames": true,
     "noFallthroughCasesInSwitch": true,
     "module": "esnext",