diff --git a/Sources/App/DependencyRegistrator.swift b/Sources/App/DependencyRegistrator.swift
index 211cc7c4d9a65ba097a7e4e0f445d160555d126a..1217667eec9b64be80a4d8fd39e9c28b17c19651 100644
--- a/Sources/App/DependencyRegistrator.swift
+++ b/Sources/App/DependencyRegistrator.swift
@@ -29,6 +29,7 @@ import ChatFeature
 import MenuFeature
 import TermsFeature
 import BackupFeature
+import DrawerFeature
 import SearchFeature
 import LaunchFeature
 import RestoreFeature
@@ -62,8 +63,8 @@ struct DependencyRegistrator {
 
   static func registerForMock() {
     container.register(XXLogger.noop)
+    container.register(VersionCheck.mock)
     container.register(CrashReporter.noop)
-    container.register(VersionChecker.mock)
     container.register(ReportingStatus.mock())
     container.register(SendReport.mock())
     container.register(MockNetworkMonitor() as NetworkMonitoring)
@@ -86,8 +87,8 @@ struct DependencyRegistrator {
 
     container.register(KeyObjectStore.userDefaults)
     container.register(XXLogger.live())
+    container.register(VersionCheck.live)
     container.register(CrashReporter.live)
-    container.register(VersionChecker.live())
     container.register(ReportingStatus.live())
     container.register(SendReport.live)
 
@@ -110,6 +111,8 @@ struct DependencyRegistrator {
       PopToNavigator(),
       SetStackNavigator(),
 
+      OpenUpNavigator(),
+
       PresentOnboardingStartNavigator(
         screen: OnboardingStartController.init,
         navigationController: { navController }
@@ -161,6 +164,10 @@ struct DependencyRegistrator {
       PresentOnboardingCodeNavigator(
         screen: OnboardingCodeController.init(_:_:_:),
         navigationController: { navController }
+      ),
+      PresentDrawerNavigator(
+        screen: DrawerController.init(_:),
+        navigationController: { navController }
       )
       //        searchFactory: SearchContainerController.init,
       //        restoreListFactory: RestoreListController.init,
diff --git a/Sources/BackupFeature/Controllers/BackupConfigController.swift b/Sources/BackupFeature/Controllers/BackupConfigController.swift
index 18848479c0193b3f62b2a00e5fc10dead562c015..9fd0b2735bab16831a77ee46d13c186b07c65b0a 100644
--- a/Sources/BackupFeature/Controllers/BackupConfigController.swift
+++ b/Sources/BackupFeature/Controllers/BackupConfigController.swift
@@ -231,7 +231,7 @@ final class BackupConfigController: UIViewController {
             spacingAfter: 40
         )
 
-        let drawer = DrawerController(with: [
+        let drawer = DrawerController([
             DrawerText(
                 font: Fonts.Mulish.extraBold.font(size: 28.0),
                 text: Localized.Backup.Config.infrastructure,
@@ -290,7 +290,7 @@ final class BackupConfigController: UIViewController {
             spacingAfter: 40
         )
 
-        let drawer = DrawerController(with: [
+        let drawer = DrawerController([
             DrawerText(
                 font: Fonts.Mulish.extraBold.font(size: 28.0),
                 text: Localized.Backup.Config.frequency(serviceName),
diff --git a/Sources/ChatFeature/Controllers/GroupChatController.swift b/Sources/ChatFeature/Controllers/GroupChatController.swift
index 31a65eaffd081f19e397da2ace17604ec8c3a9a2..e101c03535310fcdbfab685901633919921e6bb6 100644
--- a/Sources/ChatFeature/Controllers/GroupChatController.swift
+++ b/Sources/ChatFeature/Controllers/GroupChatController.swift
@@ -264,7 +264,7 @@ public final class GroupChatController: UIViewController {
       style: .brandColored
     ))
 
-    let drawer = DrawerController(with: [text, button])
+    let drawer = DrawerController([text, button])
 
     button.action
       .receive(on: DispatchQueue.main)
diff --git a/Sources/ChatFeature/Controllers/SingleChatController.swift b/Sources/ChatFeature/Controllers/SingleChatController.swift
index 708b9d5043d22ad2df980e19a346c42ba17a0ac2..9f2f7f8227bbf3696cedc3916b1ea1c6f582ab12 100644
--- a/Sources/ChatFeature/Controllers/SingleChatController.swift
+++ b/Sources/ChatFeature/Controllers/SingleChatController.swift
@@ -373,7 +373,7 @@ public final class SingleChatController: UIViewController {
       style: .brandColored
     ))
 
-    let drawer = DrawerController(with: [text, button])
+    let drawer = DrawerController([text, button])
 
     button.action
       .receive(on: DispatchQueue.main)
@@ -406,7 +406,7 @@ public final class SingleChatController: UIViewController {
     cancelButton.setStyle(.seeThrough)
     cancelButton.setTitle(Localized.Chat.Clear.cancel, for: .normal)
 
-    let drawer = DrawerController(with: [
+    let drawer = DrawerController([
       DrawerImage(
         image: Asset.drawerNegative.image
       ),
diff --git a/Sources/ChatListFeature/Controller/ChatListTableController.swift b/Sources/ChatListFeature/Controller/ChatListTableController.swift
index b67c29da977c59eeb16428f251481ef8c0b2674b..ed8bd0e9bcf323ee00ba46155ad2e5fc417ac626 100644
--- a/Sources/ChatListFeature/Controller/ChatListTableController.swift
+++ b/Sources/ChatListFeature/Controller/ChatListTableController.swift
@@ -174,7 +174,7 @@ extension ChatListTableController {
 
         let actionButton = DrawerCapsuleButton(model: .init(title: actionTitle, style: .red))
 
-        let drawer = DrawerController(with: [
+        let drawer = DrawerController([
             DrawerText(
                 font: Fonts.Mulish.bold.font(size: 26.0),
                 text: title,
diff --git a/Sources/ContactFeature/Controllers/ContactController.swift b/Sources/ContactFeature/Controllers/ContactController.swift
index 0fbb22e4b0620b4f99c4ecd656d1e2fc09d10977..87a3f073ad467c00a5e3797cdcc190cb5cd52443 100644
--- a/Sources/ContactFeature/Controllers/ContactController.swift
+++ b/Sources/ContactFeature/Controllers/ContactController.swift
@@ -277,7 +277,7 @@ public final class ContactController: UIViewController {
     cancelButton.setStyle(.seeThrough)
     cancelButton.setTitle(Localized.Contact.Clear.cancel, for: .normal)
 
-    let drawer = DrawerController(with: [
+    let drawer = DrawerController([
       DrawerImage(
         image: Asset.drawerNegative.image
       ),
@@ -360,7 +360,7 @@ extension ContactController {
       title: Localized.Settings.InfoDrawer.action
     )
 
-    let drawer = DrawerController(with: [
+    let drawer = DrawerController([
       DrawerText(
         font: Fonts.Mulish.bold.font(size: 26.0),
         text: title,
@@ -397,7 +397,7 @@ extension ContactController {
       style: .red
     ))
 
-    let drawer = DrawerController(with: [
+    let drawer = DrawerController([
       DrawerText(
         font: Fonts.Mulish.bold.font(size: 26.0),
         text: Localized.Contact.Delete.Drawer.title,
diff --git a/Sources/DrawerFeature/DrawerController.swift b/Sources/DrawerFeature/DrawerController.swift
index de613530cd8fc92b150c1d56af1a110ad767c0eb..a51fe87a53aa1f385e85460234dfa7ae92f913c3 100644
--- a/Sources/DrawerFeature/DrawerController.swift
+++ b/Sources/DrawerFeature/DrawerController.swift
@@ -6,8 +6,8 @@ public final class DrawerController: UIViewController {
     private let content: [DrawerItem]
     public var cancellables = Set<AnyCancellable>()
 
-    public init(with content: [DrawerItem]) {
-        self.content = content
+    public init(_ items: [DrawerItem]) {
+        self.content = items
         super.init(nibName: nil, bundle: nil)
 
         let views = content.map { $0.makeView() }
diff --git a/Sources/LaunchFeature/LaunchController.swift b/Sources/LaunchFeature/LaunchController.swift
index fbcd6044ff3888c8d3ceb1109f6b3f13ab3aa756..2eb224ca240c4dbb8e1f55c0ba4003c8d28eeb1b 100644
--- a/Sources/LaunchFeature/LaunchController.swift
+++ b/Sources/LaunchFeature/LaunchController.swift
@@ -3,17 +3,18 @@ import Shared
 import Combine
 import Navigation
 import PushFeature
+import XXNavigation
+import DrawerFeature
 import DependencyInjection
 
 public final class LaunchController: UIViewController {
   @Dependency var navigator: Navigator
 
-  // TO REMOVE:
-  public var pendingPushRoute: PushRouter.Route?
-
   private let viewModel = LaunchViewModel()
   private lazy var screenView = LaunchView()
+  public var pendingPushRoute: PushRouter.Route?
   private var cancellables = Set<AnyCancellable>()
+  private var drawerCancellables = Set<AnyCancellable>()
 
   public override func viewDidAppear(_ animated: Bool) {
     super.viewDidAppear(animated)
@@ -38,75 +39,118 @@ public final class LaunchController: UIViewController {
       UIColor(red: 63/255, green: 186/255, blue: 253/255, alpha: 1).cgColor,
       UIColor(red: 98/255, green: 163/255, blue: 255/255, alpha: 1).cgColor
     ]
-
     gradient.frame = screenView.bounds
     gradient.startPoint = CGPoint(x: 1, y: 0)
     gradient.endPoint = CGPoint(x: 0, y: 1)
     screenView.layer.insertSublayer(gradient, at: 0)
   }
 
-  private func offerUpdate(model: Update) {
-    let drawerView = UIView()
-    drawerView.backgroundColor = Asset.neutralSecondary.color
-    drawerView.layer.cornerRadius = 5
-
-    let vStack = UIStackView()
-    vStack.axis = .vertical
-    vStack.spacing = 10
-    drawerView.addSubview(vStack)
+  public override func viewDidLoad() {
+    super.viewDidLoad()
+
+    viewModel
+      .statePublisher
+      .receive(on: DispatchQueue.main)
+      .sink { [unowned self] in
+        guard $0.shouldPushChats == false else {
+          guard $0.shouldShowTerms == false else {
+            navigator.perform(PresentTermsAndConditions(popAllowed: false))
+            return
+          }
+          if let route = pendingPushRoute {
+            hasPendingPushRoute(route)
+            return
+          }
+          navigator.perform(PresentChatList())
+          return
+        }
+        guard $0.shouldPushOnboarding == false else {
+          navigator.perform(PresentOnboardingStart())
+          return
+        }
+        if let update = $0.shouldOfferUpdate {
+          offerUpdate(model: update)
+        }
+      }.store(in: &cancellables)
+  }
 
-    vStack.snp.makeConstraints {
-      $0.top.equalToSuperview().offset(18)
-      $0.left.equalToSuperview().offset(18)
-      $0.right.equalToSuperview().offset(-18)
-      $0.bottom.equalToSuperview().offset(-18)
+  private func hasPendingPushRoute(_ route: PushRouter.Route) {
+    switch route {
+    case .requests:
+      navigator.perform(PresentRequests())
+    case .search(username: let username):
+      navigator.perform(PresentSearch(searching: username))
+    case .groupChat(id: let groupId):
+      if let info = viewModel.getGroupInfoWith(groupId: groupId) {
+        navigator.perform(PresentGroupChat(model: info))
+        return
+      }
+      navigator.perform(PresentChatList())
+    case .contactChat(id: let userId):
+      if let model = viewModel.getContactWith(userId: userId) {
+        navigator.perform(PresentChat(contact: model))
+        return
+      }
+      navigator.perform(PresentChatList())
     }
+  }
 
-    let title = UILabel()
-    title.text = "App Update"
-    title.textAlignment = .center
-    title.textColor = Asset.neutralDark.color
-
-    let body = UILabel()
-    body.numberOfLines = 0
-    body.textAlignment = .center
-    body.textColor = Asset.neutralDark.color
-
-    let update = CapsuleButton()
-    update.publisher(for: .touchUpInside)
-      .sink { UIApplication.shared.open(.init(string: model.urlString)!, options: [:]) }
-      .store(in: &cancellables)
-
-    vStack.addArrangedSubview(title)
-    vStack.addArrangedSubview(body)
-    vStack.addArrangedSubview(update)
-
-    body.text = model.content
-    update.set(
-      style: model.actionStyle,
+  private func offerUpdate(model: LaunchViewModel.UpdateModel) {
+    let updateButton = CapsuleButton()
+    updateButton.set(
+      style: .brandColored,
       title: model.positiveActionTitle
     )
+    let notNowButton = CapsuleButton()
+    if let negativeTitle = model.negativeActionTitle {
+      notNowButton.set(
+        style: .red,
+        title: negativeTitle
+      )
+    }
+    updateButton
+      .publisher(for: .touchUpInside)
+      .receive(on: DispatchQueue.main)
+      .sink { [unowned self] in
+        navigator.perform(DismissModal(from: self)) {
+          self.drawerCancellables.removeAll()
+          UIApplication.shared.open(.init(string: model.urlString)!)
+        }
+      }.store(in: &drawerCancellables)
+
+    notNowButton
+      .publisher(for: .touchUpInside)
+      .receive(on: DispatchQueue.main)
+      .sink { [unowned self] in
+        navigator.perform(DismissModal(from: self)) {
+          self.viewModel.didRefuseUpdating()
+        }
+      }.store(in: &drawerCancellables)
+
+    var actions: [UIView] = [updateButton]
+    if model.negativeActionTitle != nil {
+      actions.append(notNowButton)
+    }
 
-    //    if let negativeTitle = model.negativeActionTitle {
-    //      let negativeButton = CapsuleButton()
-    //      negativeButton.set(style: .simplestColoredRed, title: negativeTitle)
-    //
-    //      negativeButton.publisher(for: .touchUpInside)
-    //        .sink { [unowned self] in
-    //          blocker.hideWindow()
-    //          viewModel.continueWithInitialization()
-    //        }.store(in: &cancellables)
-    //
-    //      vStack.addArrangedSubview(negativeButton)
-    //    }
-    //
-    //    blocker.window?.addSubview(drawerView)
-    //    drawerView.snp.makeConstraints {
-    //      $0.left.equalToSuperview().offset(18)
-    //      $0.center.equalToSuperview()
-    //      $0.right.equalToSuperview().offset(-18)
-    //    }
-    //
-    //    blocker.showWindow()
+    navigator.perform(PresentDrawer(items: [
+      DrawerText(
+        font: Fonts.Mulish.bold.font(size: 26.0),
+        text: "App Update",
+        color: Asset.neutralActive.color,
+        alignment: .center,
+        spacingAfter: 19
+      ),
+      DrawerText(
+        font: Fonts.Mulish.regular.font(size: 16.0),
+        text: model.content,
+        color: Asset.neutralBody.color,
+        alignment: .center,
+        spacingAfter: 19
+      ),
+      DrawerStack(
+        axis: .vertical,
+        views: actions
+      )
+    ], dismissable: false))
   }
 }
diff --git a/Sources/LaunchFeature/LaunchViewModel+Banned.swift b/Sources/LaunchFeature/LaunchViewModel+Banned.swift
index d62c2c3d70fc34379fe00cca21dbf84e11cc79d4..1449fb675e7e190cc257785b55f43517f39ac518 100644
--- a/Sources/LaunchFeature/LaunchViewModel+Banned.swift
+++ b/Sources/LaunchFeature/LaunchViewModel+Banned.swift
@@ -43,7 +43,6 @@ extension LaunchViewModel {
           DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
             self.updateBannedList(completion: completion)
           }
-
         case .success(_):
           completion()
         }
diff --git a/Sources/LaunchFeature/LaunchViewModel+Database.swift b/Sources/LaunchFeature/LaunchViewModel+Database.swift
index 62fa45aafb2c7d73cc7efa2a0fb834938e889f8d..15a88e815fe9df6bc52f5adc15622e1a90ea4cc0 100644
--- a/Sources/LaunchFeature/LaunchViewModel+Database.swift
+++ b/Sources/LaunchFeature/LaunchViewModel+Database.swift
@@ -47,22 +47,20 @@ extension LaunchViewModel {
     _ = try? database.bulkUpdateContacts(.init(authStatus: [.verificationInProgress]), .init(authStatus: .verificationFailed))
   }
 
+  func getContactWith(userId: Data) -> XXModels.Contact? {
+    let query = Contact.Query(
+      id: [userId],
+      isBlocked: reportingStatus.isEnabled() ? false : nil,
+      isBanned: reportingStatus.isEnabled() ? false : nil
+    )
 
-  //  func getContactWith(userId: Data) -> XXModels.Contact? {
-  //    let query = Contact.Query(
-  //      id: [userId],
-  //      isBlocked: reportingStatus.isEnabled() ? false : nil,
-  //      isBanned: reportingStatus.isEnabled() ? false : nil
-  //    )
-  //
-  //    guard let database: Database = try? DependencyInjection.Container.shared.resolve(),
-  //          let contact = try? database.fetchContacts(query).first else {
-  //      return nil
-  //    }
-  //
-  //    return contact
-  //  }
+    guard let database: Database = try? DependencyInjection.Container.shared.resolve(),
+          let contact = try? database.fetchContacts(query).first else {
+      return nil
+    }
 
+    return contact
+  }
 
   func getGroupInfoWith(groupId: Data) -> GroupInfo? {
     let query = GroupInfo.Query(groupId: groupId)
diff --git a/Sources/LaunchFeature/LaunchViewModel+Errors.swift b/Sources/LaunchFeature/LaunchViewModel+Errors.swift
new file mode 100644
index 0000000000000000000000000000000000000000..399667657243523475a2857e9838451a0198d587
--- /dev/null
+++ b/Sources/LaunchFeature/LaunchViewModel+Errors.swift
@@ -0,0 +1,42 @@
+import XXClient
+import Foundation
+
+extension LaunchViewModel {
+  func updateErrors(
+    completion: @escaping (Result<Void, Error>) -> Void
+  ) {
+    let url = "https://git.xx.network/elixxir/client-error-database/-/raw/main/clientErrors.json"
+    downloadErrors(from: url) {
+      switch $0 {
+      case .success(let string):
+        do {
+          try UpdateCommonErrors.live(jsonFile: string)
+          completion(.success(()))
+        } catch {
+          completion(.failure(error))
+        }
+      case .failure(let error):
+        completion(.failure(error))
+      }
+    }
+  }
+
+  func downloadErrors(
+    from urlString: String,
+    completion: @escaping (Result<String, Error>) -> Void
+  ) {
+    URLSession.shared.dataTask(with: URL(string: urlString)!) { data, _, error in
+      if let error {
+        completion(.failure(error))
+        return
+      }
+      guard let data else {
+        fatalError("No errors or data when downloading \(urlString)")
+      }
+      guard let string = String(data: data, encoding: .utf8) else {
+        fatalError("Impossible to decode error json")
+      }
+      completion(.success(string))
+    }.resume()
+  }
+}
diff --git a/Sources/LaunchFeature/LaunchViewModel+Messenger.swift b/Sources/LaunchFeature/LaunchViewModel+Messenger.swift
index f5ed8ee1af24b5fe4793f2b337fe979da126debe..2971fe40e65127804228af2c8b0a06df90ea3311 100644
--- a/Sources/LaunchFeature/LaunchViewModel+Messenger.swift
+++ b/Sources/LaunchFeature/LaunchViewModel+Messenger.swift
@@ -1,3 +1,4 @@
+import Shared
 import XXClient
 import XXModels
 import XXLogger
@@ -393,4 +394,52 @@ extension LaunchViewModel {
     DependencyInjection.Container.shared.register(manager)
     try! manager.setStatus(dummyTrafficOn)
   }
+
+  func setupMessenger() throws {
+    setupLogWriter()
+    setupAuthCallback()
+    setupBackupCallback()
+    setupMessageCallback()
+
+    if messenger.isLoaded() == false {
+      if messenger.isCreated() == false {
+        try messenger.create()
+      }
+
+      try messenger.load()
+    }
+
+    try messenger.start()
+
+    if messenger.isConnected() == false {
+      try messenger.connect()
+      try messenger.listenForMessages()
+    }
+
+    try generateGroupManager()
+    try generateTrafficManager()
+    try generateTransferManager()
+    listenToNetworkUpdates()
+
+    if messenger.isLoggedIn() == false {
+      if try messenger.isRegistered() {
+        try messenger.logIn()
+        hudController.dismiss()
+        stateSubject.value.shouldPushChats = true
+      } else {
+        try? sftpManager.unlink()
+        try? dropboxManager.unlink()
+        hudController.dismiss()
+        stateSubject.value.shouldPushOnboarding = true
+      }
+    } else {
+      hudController.dismiss()
+      stateSubject.value.shouldPushChats = true
+    }
+    if !messenger.isBackupRunning() {
+      try? messenger.resumeBackup()
+    }
+    // TODO: Biometric auth
+
+  }
 }
diff --git a/Sources/LaunchFeature/LaunchViewModel+VersionCheck.swift b/Sources/LaunchFeature/LaunchViewModel+VersionCheck.swift
deleted file mode 100644
index 4496c75f3f4198ce391e1a5621d0de54127c6184..0000000000000000000000000000000000000000
--- a/Sources/LaunchFeature/LaunchViewModel+VersionCheck.swift
+++ /dev/null
@@ -1,33 +0,0 @@
-import Shared
-import VersionChecking
-
-extension LaunchViewModel {
-  func versionFailed(error: Error) {
-    hudController.show(.init(
-      title: Localized.Launch.Version.failed,
-      content: error.localizedDescription
-    ))
-  }
-
-  func versionUpdateRequired(_ info: DappVersionInformation) {
-    hudController.dismiss()
-    routeSubject.send(.update(Update(
-      content: info.minimumMessage,
-      urlString: info.appUrl,
-      positiveActionTitle: Localized.Launch.Version.Required.positive,
-      negativeActionTitle: nil,
-      actionStyle: .brandColored
-    )))
-  }
-
-  func versionUpdateRecommended(_ info: DappVersionInformation) {
-    hudController.dismiss()
-    routeSubject.send(.update(Update(
-      content: Localized.Launch.Version.Recommended.title,
-      urlString: info.appUrl,
-      positiveActionTitle: Localized.Launch.Version.Recommended.positive,
-      negativeActionTitle: Localized.Launch.Version.Recommended.negative,
-      actionStyle: .simplestColoredRed
-    )))
-  }
-}
diff --git a/Sources/LaunchFeature/LaunchViewModel.swift b/Sources/LaunchFeature/LaunchViewModel.swift
index 3639e1d1d8a7a31c755f20350dc83469446a40f6..74d77143687839378a29cc4677c7666dddced9d5 100644
--- a/Sources/LaunchFeature/LaunchViewModel.swift
+++ b/Sources/LaunchFeature/LaunchViewModel.swift
@@ -2,49 +2,43 @@ import Shared
 import Combine
 import Defaults
 import XXModels
-import XXLogger
 import Keychain
+import XXClient
+import CloudFiles
 import Foundation
 import Permissions
 import BackupFeature
+import NetworkMonitor
 import VersionChecking
 import ReportingFeature
 import CombineSchedulers
+import CloudFilesDropbox
+import XXMessengerClient
 import DependencyInjection
 
-import XXClient
-import struct XXClient.FileTransfer
 import class XXClient.Cancellable
 
-import XXDatabase
-import XXLegacyDatabaseMigrator
-import XXMessengerClient
-import NetworkMonitor
-
-import CloudFiles
-import CloudFilesSFTP
-import CloudFilesDropbox
-
-struct Update {
-  let content: String
-  let urlString: String
-  let positiveActionTitle: String
-  let negativeActionTitle: String?
-  let actionStyle: CapsuleButtonStyle
-}
+final class LaunchViewModel {
+  struct UpdateModel {
+    let content: String
+    let urlString: String
+    let positiveActionTitle: String
+    let negativeActionTitle: String?
+    let actionStyle: CapsuleButtonStyle
+  }
 
-enum LaunchRoute {
-  case chats
-  case update(Update)
-  case onboarding
-}
+  struct ViewState {
+    var shouldShowTerms = false
+    var shouldPushChats = false
+    var shouldOfferUpdate: UpdateModel?
+    var shouldPushOnboarding = false
+  }
 
-final class LaunchViewModel {
   @Dependency var database: Database
   @Dependency var messenger: Messenger
+  @Dependency var versionCheck: VersionCheck
   @Dependency var hudController: HUDController
   @Dependency var backupService: BackupService
-  @Dependency var versionChecker: VersionChecker
   @Dependency var fetchBannedList: FetchBannedList
   @Dependency var reportingStatus: ReportingStatus
   @Dependency var toastController: ToastController
@@ -63,125 +57,108 @@ final class LaunchViewModel {
   var networkCallbacksCancellable: Cancellable?
   var messageListenerCallbacksCancellable: Cancellable?
 
-  var routePublisher: AnyPublisher<LaunchRoute, Never> {
-    routeSubject.eraseToAnyPublisher()
+  var statePublisher: AnyPublisher<ViewState, Never> {
+    stateSubject.eraseToAnyPublisher()
   }
 
   private var scheduler: AnySchedulerOf<DispatchQueue> = {
     DispatchQueue.global().eraseToAnyScheduler()
   }()
 
-  private let dropboxManager = CloudFilesManager.dropbox(
+  let dropboxManager = CloudFilesManager.dropbox(
     appKey: "ppx0de5f16p9aq2",
     path: "/backup/backup.xxm"
   )
 
-  private let sftpManager = CloudFilesManager.sftp(
+  let sftpManager = CloudFilesManager.sftp(
     host: "",
     username: "",
     password: "",
     fileName: ""
   )
 
-  var cancellables = Set<AnyCancellable>()
-  let routeSubject = PassthroughSubject<LaunchRoute, Never>()
+  let stateSubject = CurrentValueSubject <ViewState, Never>(.init())
 
   func viewDidAppear() {
     scheduler.schedule(after: .init(.now() + 1)) { [weak self] in
       guard let self else { return }
-      self.hudController.show()
-      self.versionChecker()
-        .sink { [unowned self] in
-          switch $0 {
-          case .upToDate:
-            self.updateBannedList {
-              self.updateErrors {
-                self.continueWithInitialization()
-              }
-            }
-          case .failure(let error):
-            self.versionFailed(error: error)
-          case .updateRequired(let info):
-            self.versionUpdateRequired(info)
-          case .updateRecommended(let info):
-            self.versionUpdateRecommended(info)
-          }
-        }.store(in: &self.cancellables)
+      self.startLaunch()
     }
   }
 
-  func continueWithInitialization() {
-    do {
-      try self.setupDatabase()
-
-      setupLogWriter()
-      setupAuthCallback()
-      setupBackupCallback()
-      setupMessageCallback()
-
-      if messenger.isLoaded() == false {
-        if messenger.isCreated() == false {
-          try messenger.create()
-        }
-
-        try messenger.load()
-      }
-
-      try messenger.start()
-
-      if messenger.isConnected() == false {
-        try messenger.connect()
-        try messenger.listenForMessages()
+  private func startLaunch() {
+    if !didAcceptTerms {
+      stateSubject.value.shouldShowTerms = true
+    }
+    hudController.show()
+    versionCheck.verify { [weak self] in
+      guard let self else { return }
+      switch $0 {
+      case .upToDate:
+        self.didVerifyVersion()
+      case .failure(let error):
+        self.hudController.show(.init(
+          title: Localized.Launch.Version.failed,
+          content: error.localizedDescription
+        ))
+      case .outdated(let info):
+        self.hudController.dismiss()
+        let isRequired = info.isRequired ?? false
+
+        let content = isRequired ?
+        info.minimumMessage :
+        Localized.Launch.Version.Recommended.title
+
+        let positiveActionTitle = isRequired ?
+        Localized.Launch.Version.Required.positive :
+        Localized.Launch.Version.Recommended.positive
+
+        self.stateSubject.value.shouldOfferUpdate = .init(
+          content: content,
+          urlString: info.appUrl,
+          positiveActionTitle: positiveActionTitle,
+          negativeActionTitle: isRequired ? nil : Localized.Launch.Version.Recommended.negative,
+          actionStyle: isRequired ? .brandColored : .simplestColoredRed
+        )
       }
+    }
+  }
 
-      try generateGroupManager()
-      try generateTrafficManager()
-      try generateTransferManager()
-      listenToNetworkUpdates()
+  func didRefuseUpdating() {
+    hudController.show()
+    didVerifyVersion()
+  }
 
-      if messenger.isLoggedIn() == false {
-        if try messenger.isRegistered() {
-          try messenger.logIn()
-          hudController.dismiss()
-          routeSubject.send(.chats)
-        } else {
-          try? sftpManager.unlink()
-          try? dropboxManager.unlink()
-          hudController.dismiss()
-          routeSubject.send(.onboarding)
+  private func didVerifyVersion() {
+    updateBannedList { [weak self] in
+      guard let self else { return }
+      self.updateErrors {
+        switch $0 {
+        case .success:
+          self.didFinishAsyncWork()
+        case .failure(let error):
+          self.hudController.show(.init(error: error))
         }
-      } else {
-        hudController.dismiss()
-        routeSubject.send(.chats)
       }
-      if !messenger.isBackupRunning() {
-        try? messenger.resumeBackup()
-      }
-      // TODO: Biometric auth
+    }
+  }
 
+  private func didFinishAsyncWork() {
+    do {
+      try setupDatabase()
+      try setupMessenger()
     } catch {
       let xxError = CreateUserFriendlyErrorMessage.live(error.localizedDescription)
       hudController.show(.init(content: xxError))
     }
   }
 
-  private func cleanUp() {
-    // try? cMixManager.remove()
-    // try? keychainHandler.clear()
-  }
-
-  private func presentOnboardingFlow() {
-    hudController.dismiss()
-    routeSubject.send(.onboarding)
-  }
-
   private func checkBiometrics(completion: @escaping (Result<Bool, Error>) -> Void) {
     if permissionHandler.isBiometricsAvailable && isBiometricsOn {
       permissionHandler.requestBiometrics {
         switch $0 {
         case .success(let granted):
           completion(.success(granted))
-
         case .failure(let error):
           completion(.failure(error))
         }
@@ -190,76 +167,4 @@ final class LaunchViewModel {
       completion(.success(true))
     }
   }
-
-  private func updateErrors(completion: @escaping () -> Void) {
-    let errorsURLString = "https://git.xx.network/elixxir/client-error-database/-/raw/main/clientErrors.json"
-
-    URLSession.shared.dataTask(with: URL(string: errorsURLString)!) { [weak self] data, _, error in
-      guard let self else { return }
-
-      guard error == nil else {
-        print(">>> Issue when trying to download errors json: \(error!.localizedDescription)")
-        self.updateErrors(completion: completion)
-        return
-      }
-
-      guard let data = data, let json = String(data: data, encoding: .utf8) else {
-        print(">>> Issue when trying to unwrap errors json")
-        return
-      }
-
-      do {
-        try UpdateCommonErrors.live(jsonFile: json)
-        completion()
-      } catch {
-        print(">>> Issue when trying to update common errors: \(error.localizedDescription)")
-      }
-    }.resume()
-  }
 }
-
-//    viewModel.routePublisher
-//      .receive(on: DispatchQueue.main)
-//      .sink { [unowned self] in
-//        switch $0 {
-//        case .chats:
-//          guard didAcceptTerms == true else {
-//            navigator.perform(PresentTermsAndConditions(popAllowed: false))
-//            return
-//          }
-//
-//          if let pushRoute = pendingPushRoute {
-//            switch pushRoute {
-//            case .requests:
-//              navigator.perform(PresentRequests())
-//
-//            case .search(username: let username):
-//              navigator.perform(PresentSearch(searching: username))
-//
-//            case .groupChat(id: let groupId):
-//              if let info = viewModel.getGroupInfoWith(groupId: groupId) {
-//                navigator.perform(PresentGroupChat(model: info))
-//                return
-//              }
-//              navigator.perform(PresentChatList())
-//
-//            case .contactChat(id: let userId):
-//              if let model = viewModel.getContactWith(userId: userId) {
-//                navigator.perform(PresentChat(contact: model))
-//                return
-//              }
-//              navigator.perform(PresentChatList())
-//            }
-//
-//            return
-//          }
-//
-//          navigator.perform(PresentChatList())
-//
-//        case .onboarding:
-//          navigator.perform(PresentOnboardingStart())
-//
-//        case .update(let model):
-//          offerUpdate(model: model)
-//        }
-//      }.store(in: &cancellables)
diff --git a/Sources/MenuFeature/Controllers/MenuController.swift b/Sources/MenuFeature/Controllers/MenuController.swift
index 22cee48831683ba0ba47619f640dfd6990b2310c..dbd587248752db7580356c9f40a79541110867f3 100644
--- a/Sources/MenuFeature/Controllers/MenuController.swift
+++ b/Sources/MenuFeature/Controllers/MenuController.swift
@@ -201,7 +201,7 @@ public final class MenuController: UIViewController {
       style: .red
     ))
 
-    let drawer = DrawerController(with: [
+    let drawer = DrawerController([
       DrawerText(
         font: Fonts.Mulish.bold.font(size: 26.0),
         text: title,
diff --git a/Sources/OnboardingFeature/Controllers/OnboardingCodeController.swift b/Sources/OnboardingFeature/Controllers/OnboardingCodeController.swift
index b8fe915aee1e3df24665104a1c99db4e745e71a1..82044ac6f26b271bc9977519260ed1370b3a8d6e 100644
--- a/Sources/OnboardingFeature/Controllers/OnboardingCodeController.swift
+++ b/Sources/OnboardingFeature/Controllers/OnboardingCodeController.swift
@@ -144,7 +144,7 @@ public final class OnboardingCodeController: UIViewController {
     let actionButton = CapsuleButton()
     actionButton.set(style: .seeThrough, title: Localized.Settings.InfoDrawer.action)
 
-    let drawer = DrawerController(with: [
+    let drawer = DrawerController([
       DrawerText(
         font: Fonts.Mulish.bold.font(size: 26.0),
         text: title,
@@ -175,6 +175,6 @@ public final class OnboardingCodeController: UIViewController {
         }
       }.store(in: &drawerCancellables)
 
-    navigator.perform(PresentDrawer())
+//    navigator.perform(PresentDrawer())
   }
 }
diff --git a/Sources/OnboardingFeature/Controllers/OnboardingEmailController.swift b/Sources/OnboardingFeature/Controllers/OnboardingEmailController.swift
index b6554d3dad3252d2e22fda105c0fc614fd45c9e3..3844b24cd957422f2736e76db95e529e13fbbc07 100644
--- a/Sources/OnboardingFeature/Controllers/OnboardingEmailController.swift
+++ b/Sources/OnboardingFeature/Controllers/OnboardingEmailController.swift
@@ -115,44 +115,42 @@ public final class OnboardingEmailController: UIViewController {
       title: Localized.Settings.InfoDrawer.action
     )
 
-    let drawer = DrawerController(with: [
-      DrawerText(
-        font: Fonts.Mulish.bold.font(size: 26.0),
-        text: title,
-        color: Asset.neutralActive.color,
-        alignment: .left,
-        spacingAfter: 19
-      ),
-      DrawerLinkText(
-        text: subtitle,
-        urlString: urlString,
-        spacingAfter: 37
-      ),
-      DrawerStack(views: [
-        actionButton,
-        FlexibleSpace()
-      ])
-    ])
+//    navigator.perform(PresentDrawer([
+//      DrawerText(
+//        font: Fonts.Mulish.bold.font(size: 26.0),
+//        text: title,
+//        color: Asset.neutralActive.color,
+//        alignment: .left,
+//        spacingAfter: 19
+//      ),
+//      DrawerLinkText(
+//        text: subtitle,
+//        urlString: urlString,
+//        spacingAfter: 37
+//      ),
+//      DrawerStack(views: [
+//        actionButton,
+//        FlexibleSpace()
+//      ])
+//    ]))
 
     actionButton.publisher(for: .touchUpInside)
       .receive(on: DispatchQueue.main)
       .sink {
-        drawer.dismiss(animated: true) { [weak self] in
-          guard let self = self else { return }
-          self.drawerCancellables.removeAll()
-        }
+        //        drawer.dismiss(animated: true) { [weak self] in
+        //          guard let self = self else { return }
+        //          self.drawerCancellables.removeAll()
+        //      }
       }.store(in: &drawerCancellables)
-
-    navigator.perform(PresentDrawer())
   }
 }
 
-//        coordinator.toEmailConfirmation(with: $0, from: self) { controller in
-//          let successModel = OnboardingSuccessModel(
-//            title: Localized.Onboarding.Success.Email.title,
-//            subtitle: nil,
-//            nextController: self.coordinator.toPhone(from:)
-//          )
-//
-//          self.coordinator.toSuccess(with: successModel, from: controller)
-//        }
+  //        coordinator.toEmailConfirmation(with: $0, from: self) { controller in
+  //          let successModel = OnboardingSuccessModel(
+  //            title: Localized.Onboarding.Success.Email.title,
+  //            subtitle: nil,
+  //            nextController: self.coordinator.toPhone(from:)
+  //          )
+  //
+  //          self.coordinator.toSuccess(with: successModel, from: controller)
+  //        }
diff --git a/Sources/OnboardingFeature/Controllers/OnboardingPhoneController.swift b/Sources/OnboardingFeature/Controllers/OnboardingPhoneController.swift
index dc9df4ea436f0e6cfa10f24449f4daa060b5d1b8..1e64773787bb8bb46eaebb12ae86a5134a6c8166 100644
--- a/Sources/OnboardingFeature/Controllers/OnboardingPhoneController.swift
+++ b/Sources/OnboardingFeature/Controllers/OnboardingPhoneController.swift
@@ -133,7 +133,7 @@ public final class OnboardingPhoneController: UIViewController {
       title: Localized.Settings.InfoDrawer.action
     )
 
-    let drawer = DrawerController(with: [
+    let drawer = DrawerController([
       DrawerText(
         font: Fonts.Mulish.bold.font(size: 26.0),
         text: title,
@@ -161,7 +161,7 @@ public final class OnboardingPhoneController: UIViewController {
         }
       }.store(in: &drawerCancellables)
 
-    navigator.perform(PresentDrawer())
+//    navigator.perform(PresentDrawer())
   }
 }
 
diff --git a/Sources/OnboardingFeature/Controllers/OnboardingUsernameController.swift b/Sources/OnboardingFeature/Controllers/OnboardingUsernameController.swift
index 5939dab891e93f9031990eaca31da3ee8a86c7f9..7edb740b5feebeb134d655e4fdfc76d90d9739b5 100644
--- a/Sources/OnboardingFeature/Controllers/OnboardingUsernameController.swift
+++ b/Sources/OnboardingFeature/Controllers/OnboardingUsernameController.swift
@@ -116,7 +116,7 @@ public final class OnboardingUsernameController: UIViewController {
       title: Localized.Settings.InfoDrawer.action
     )
 
-    let drawer = DrawerController(with: [
+    let drawer = DrawerController([
       DrawerText(
         font: Fonts.Mulish.bold.font(size: 26.0),
         text: title,
@@ -144,6 +144,6 @@ public final class OnboardingUsernameController: UIViewController {
         }
       }.store(in: &drawerCancellables)
 
-    navigator.perform(PresentDrawer())
+//    navigator.perform(PresentDrawer())
   }
 }
diff --git a/Sources/OnboardingFeature/Controllers/OnboardingWelcomeController.swift b/Sources/OnboardingFeature/Controllers/OnboardingWelcomeController.swift
index c979f8121e6f02758d1fbc8ec37f7a64b96fc5d3..69033a19f3555a189f36d0094fceebdcc34ab600 100644
--- a/Sources/OnboardingFeature/Controllers/OnboardingWelcomeController.swift
+++ b/Sources/OnboardingFeature/Controllers/OnboardingWelcomeController.swift
@@ -59,40 +59,40 @@ public final class OnboardingWelcomeController: UIViewController {
     subtitle: String,
     urlString: String = ""
   ) {
-    let actionButton = CapsuleButton()
-    actionButton.set(
-      style: .seeThrough,
-      title: Localized.Settings.InfoDrawer.action
-    )
-
-    let drawer = DrawerController(with: [
-      DrawerText(
-        font: Fonts.Mulish.bold.font(size: 26.0),
-        text: title,
-        color: Asset.neutralActive.color,
-        alignment: .left,
-        spacingAfter: 19
-      ),
-      DrawerLinkText(
-        text: subtitle,
-        urlString: urlString,
-        spacingAfter: 37
-      ),
-      DrawerStack(views: [
-        actionButton,
-        FlexibleSpace()
-      ])
-    ])
-
-    actionButton.publisher(for: .touchUpInside)
-      .receive(on: DispatchQueue.main)
-      .sink {
-        drawer.dismiss(animated: true) { [weak self] in
-          guard let self = self else { return }
-          self.drawerCancellables.removeAll()
-        }
-      }.store(in: &drawerCancellables)
-
-    navigator.perform(PresentDrawer())
+//    let actionButton = CapsuleButton()
+//    actionButton.set(
+//      style: .seeThrough,
+//      title: Localized.Settings.InfoDrawer.action
+//    )
+//
+//    let drawer = DrawerController([
+//      DrawerText(
+//        font: Fonts.Mulish.bold.font(size: 26.0),
+//        text: title,
+//        color: Asset.neutralActive.color,
+//        alignment: .left,
+//        spacingAfter: 19
+//      ),
+//      DrawerLinkText(
+//        text: subtitle,
+//        urlString: urlString,
+//        spacingAfter: 37
+//      ),
+//      DrawerStack(views: [
+//        actionButton,
+//        FlexibleSpace()
+//      ])
+//    ])
+//
+//    actionButton.publisher(for: .touchUpInside)
+//      .receive(on: DispatchQueue.main)
+//      .sink {
+//        drawer.dismiss(animated: true) { [weak self] in
+//          guard let self = self else { return }
+//          self.drawerCancellables.removeAll()
+//        }
+//      }.store(in: &drawerCancellables)
+//
+//    navigator.perform(PresentDrawer())
   }
 }
diff --git a/Sources/ProfileFeature/Controllers/ProfileController.swift b/Sources/ProfileFeature/Controllers/ProfileController.swift
index 787f83f6e0d429262780485b11cef70dae17a917..58a74cd0a6cca98ed8c29fa4520abc223e7466a0 100644
--- a/Sources/ProfileFeature/Controllers/ProfileController.swift
+++ b/Sources/ProfileFeature/Controllers/ProfileController.swift
@@ -151,7 +151,7 @@ public final class ProfileController: UIViewController {
       style: .red
     ))
 
-    let drawer = DrawerController(with: [
+    let drawer = DrawerController([
       DrawerText(
         font: Fonts.Mulish.bold.font(size: 26.0),
         text: title,
diff --git a/Sources/ReportingFeature/MakeReportDrawer.swift b/Sources/ReportingFeature/MakeReportDrawer.swift
index b8b4aa394481d3e292ef236711b1f31f82704d3e..bd102a6ee2e046b03c787d250f8a0573d25af7c3 100644
--- a/Sources/ReportingFeature/MakeReportDrawer.swift
+++ b/Sources/ReportingFeature/MakeReportDrawer.swift
@@ -34,7 +34,7 @@ extension MakeReportDrawer {
         reportButton.setStyle(.red)
         reportButton.setTitle(Localized.Chat.Report.action, for: .normal)
 
-        let drawer = DrawerController(with: [
+        let drawer = DrawerController([
             DrawerImage(
                 image: Asset.drawerNegative.image
             ),
diff --git a/Sources/RequestsFeature/Controllers/RequestsReceivedController.swift b/Sources/RequestsFeature/Controllers/RequestsReceivedController.swift
index feec2f2873d90a2fbbbaa492e03e4c22a4ac5567..279f29b627f8a072609868fd3686c6138d435e68 100644
--- a/Sources/RequestsFeature/Controllers/RequestsReceivedController.swift
+++ b/Sources/RequestsFeature/Controllers/RequestsReceivedController.swift
@@ -166,7 +166,7 @@ extension RequestsReceivedController {
             drawerLaterButton
         ])
 
-        let drawer = DrawerController(with: items)
+        let drawer = DrawerController(items)
 
         drawerSendButton.action
             .receive(on: DispatchQueue.main)
@@ -240,7 +240,7 @@ extension RequestsReceivedController {
             drawerLaterButton
         ])
 
-        let drawer = DrawerController(with: items)
+        let drawer = DrawerController(items)
 
         drawerSendButton.action
             .receive(on: DispatchQueue.main)
@@ -344,7 +344,7 @@ extension RequestsReceivedController {
 
         items.append(contentsOf: [drawerAcceptButton, drawerHideButton])
 
-        let drawer = DrawerController(with: items)
+        let drawer = DrawerController(items)
 
         drawerAcceptButton.action
             .receive(on: DispatchQueue.main)
@@ -471,7 +471,7 @@ extension RequestsReceivedController {
 
         items.append(contentsOf: [drawerAcceptButton, drawerHideButton])
 
-        let drawer = DrawerController(with: items)
+        let drawer = DrawerController(items)
 
         var nickname: String?
         var allowsSave = true
@@ -551,7 +551,7 @@ extension RequestsReceivedController {
 
         items.append(drawerDoneButton)
 
-        let drawer = DrawerController(with: items)
+        let drawer = DrawerController(items)
 
         drawerDoneButton.action
             .receive(on: DispatchQueue.main)
diff --git a/Sources/RestoreFeature/Controllers/RestoreController.swift b/Sources/RestoreFeature/Controllers/RestoreController.swift
index 12bc3a7d244e147e3fc7433a1eaa036db828db01..f88ead1da0327ede349a9e1fdf3a4b0d5eb0b13a 100644
--- a/Sources/RestoreFeature/Controllers/RestoreController.swift
+++ b/Sources/RestoreFeature/Controllers/RestoreController.swift
@@ -105,7 +105,7 @@ extension RestoreController {
             style: .brandColored
         ))
 
-        let drawer = DrawerController(with: [
+        let drawer = DrawerController([
             DrawerText(
                 font: Fonts.Mulish.bold.font(size: 26.0),
                 text: Localized.AccountRestore.Warning.title,
diff --git a/Sources/RestoreFeature/Controllers/RestoreListController.swift b/Sources/RestoreFeature/Controllers/RestoreListController.swift
index 026bdbd0fe809898df7ead2282729f44732f1e47..6e85c99fb155a90394836d095964fa2dbc1e49b4 100644
--- a/Sources/RestoreFeature/Controllers/RestoreListController.swift
+++ b/Sources/RestoreFeature/Controllers/RestoreListController.swift
@@ -97,7 +97,7 @@ extension RestoreListController {
       style: .brandColored
     ))
 
-    let drawer = DrawerController(with: [
+    let drawer = DrawerController([
       DrawerText(
         font: Fonts.Mulish.bold.font(size: 26.0),
         text: Localized.AccountRestore.Warning.title,
diff --git a/Sources/ScanFeature/Controllers/ScanContainerController.swift b/Sources/ScanFeature/Controllers/ScanContainerController.swift
index 883f95fdb92a27d2b4f860ec2356f450a7ceeaa5..a85c06a797e1013275d261d2cbc51bdbf298db4f 100644
--- a/Sources/ScanFeature/Controllers/ScanContainerController.swift
+++ b/Sources/ScanFeature/Controllers/ScanContainerController.swift
@@ -115,7 +115,7 @@ public final class ScanContainerController: UIViewController {
       title: Localized.Settings.InfoDrawer.action
     )
 
-    let drawer = DrawerController(with: [
+    let drawer = DrawerController([
       DrawerText(
         font: Fonts.Mulish.bold.font(size: 26.0),
         text: title,
diff --git a/Sources/SearchFeature/Controllers/SearchContainerController.swift b/Sources/SearchFeature/Controllers/SearchContainerController.swift
index 77f2a535feca6a5dacafca6e9632e3f0396d26e4..a78f5993fb3a7381f0f337884a8e03a878324bac 100644
--- a/Sources/SearchFeature/Controllers/SearchContainerController.swift
+++ b/Sources/SearchFeature/Controllers/SearchContainerController.swift
@@ -137,7 +137,7 @@ extension SearchContainerController {
       title: Localized.ChatList.Traffic.negative
     )
 
-    let drawer = DrawerController(with: [
+    let drawer = DrawerController([
       DrawerText(
         font: Fonts.Mulish.bold.font(size: 26.0),
         text: Localized.ChatList.Traffic.title,
diff --git a/Sources/SearchFeature/Controllers/SearchLeftController.swift b/Sources/SearchFeature/Controllers/SearchLeftController.swift
index 7eb72535120e8f30f86258365e4ca86d422572e4..bc6b1fbd5f78c9fc6a62b705f11bdf929a6e1faf 100644
--- a/Sources/SearchFeature/Controllers/SearchLeftController.swift
+++ b/Sources/SearchFeature/Controllers/SearchLeftController.swift
@@ -208,7 +208,7 @@ final class SearchLeftController: UIViewController {
             title: Localized.Ud.Placeholder.Drawer.action
         )
 
-        let drawer = DrawerController(with: [
+        let drawer = DrawerController([
             DrawerText(
                 font: Fonts.Mulish.bold.font(size: 26.0),
                 text: Localized.Ud.Placeholder.Drawer.title,
@@ -282,7 +282,7 @@ final class SearchLeftController: UIViewController {
 
         items.append(drawerSaveButton)
 
-        let drawer = DrawerController(with: items)
+        let drawer = DrawerController(items)
         var nickname: String?
         var allowsSave = true
 
@@ -402,7 +402,7 @@ final class SearchLeftController: UIViewController {
         )
 
         items.append(contentsOf: [drawerSendButton, drawerCancelButton])
-        let drawer = DrawerController(with: items)
+        let drawer = DrawerController(items)
 
         drawerSendButton.action
             .receive(on: DispatchQueue.main)
diff --git a/Sources/SettingsFeature/Controllers/AccountDeleteController.swift b/Sources/SettingsFeature/Controllers/AccountDeleteController.swift
index 01ad5ad59576f8654f8ed66cf9250e42dbfc4f5d..31674910b97735d82456859454c56c4325c9c18d 100644
--- a/Sources/SettingsFeature/Controllers/AccountDeleteController.swift
+++ b/Sources/SettingsFeature/Controllers/AccountDeleteController.swift
@@ -77,7 +77,7 @@ public final class AccountDeleteController: UIViewController {
             title: Localized.Settings.InfoDrawer.action
         )
 
-        let drawer = DrawerController(with: [
+        let drawer = DrawerController([
             DrawerText(
                 font: Fonts.Mulish.bold.font(size: 26.0),
                 text: title,
diff --git a/Sources/SettingsFeature/Controllers/SettingsController.swift b/Sources/SettingsFeature/Controllers/SettingsController.swift
index 74ca0b94de5d3d367874e3474d3260b331608875..5cce068e5dfceae71439bc1c7f9e4a3047548275 100644
--- a/Sources/SettingsFeature/Controllers/SettingsController.swift
+++ b/Sources/SettingsFeature/Controllers/SettingsController.swift
@@ -198,7 +198,7 @@ public final class SettingsController: UIViewController {
     cancelButton.setStyle(.seeThrough)
     cancelButton.setTitle(Localized.ChatList.Dashboard.cancel, for: .normal)
 
-    let drawer = DrawerController(with: [
+    let drawer = DrawerController([
       DrawerImage(
         image: Asset.drawerNegative.image
       ),
@@ -259,7 +259,7 @@ extension SettingsController {
       title: Localized.Settings.InfoDrawer.action
     )
 
-    let drawer = DrawerController(with: [
+    let drawer = DrawerController([
       DrawerText(
         font: Fonts.Mulish.bold.font(size: 26.0),
         text: title,
diff --git a/Sources/VersionChecking/BackendVersionInformation.swift b/Sources/VersionChecking/BackendVersionInformation.swift
new file mode 100644
index 0000000000000000000000000000000000000000..bb52e089d69a5bdc73672d70b936d0afe941c341
--- /dev/null
+++ b/Sources/VersionChecking/BackendVersionInformation.swift
@@ -0,0 +1,7 @@
+struct BackendVersionInformation: Codable {
+  var info: DappVersionInformation
+
+  private enum CodingKeys: String, CodingKey {
+    case info = "dapp-id"
+  }
+}
diff --git a/Sources/VersionChecking/DappVersionInformation.swift b/Sources/VersionChecking/DappVersionInformation.swift
new file mode 100644
index 0000000000000000000000000000000000000000..c6d49f32bf3f211ea669cf003a9cc663c86adc76
--- /dev/null
+++ b/Sources/VersionChecking/DappVersionInformation.swift
@@ -0,0 +1,14 @@
+public struct DappVersionInformation: Codable {
+  public var appUrl: String
+  public var minimum: String
+  public var isRequired: Bool?
+  public var recommended: String
+  public var minimumMessage: String
+
+  private enum CodingKeys: String, CodingKey {
+    case appUrl = "new_ios_app_url"
+    case minimum = "new_ios_min_version"
+    case recommended = "new_ios_recommended_version"
+    case minimumMessage = "new_minimum_popup_msg"
+  }
+}
diff --git a/Sources/VersionChecking/VersionChecking.swift b/Sources/VersionChecking/VersionChecking.swift
index bf50d90c877a65858940a4bdad70b1101e1dc1d0..820a2aac66a5db65577ab4e39109c33256e26e7a 100644
--- a/Sources/VersionChecking/VersionChecking.swift
+++ b/Sources/VersionChecking/VersionChecking.swift
@@ -1,109 +1,53 @@
 import Combine
 import Foundation
 
-public enum VersionInfo {
+public typealias VersionCompletion = (VersionCheck.Requirement) -> Void
+
+public struct VersionCheck {
+  public enum Requirement {
     case upToDate
     case failure(Error)
-    case updateRequired(DappVersionInformation)
-    case updateRecommended(DappVersionInformation)
-}
-
-public struct VersionDataFetcher {
-    var run: () -> AnyPublisher<DappVersionInformation, Error>
-
-    public init(run: @escaping () -> AnyPublisher<DappVersionInformation, Error>) {
-        self.run = run
-    }
-
-    public func callAsFunction() -> AnyPublisher<DappVersionInformation, Error> { run() }
-}
-
-public struct VersionChecker {
-    var run: () -> AnyPublisher<VersionInfo, Never>
-
-    public init(run: @escaping () -> AnyPublisher<VersionInfo, Never>) {
-        self.run = run
-    }
-
-    public func callAsFunction() -> AnyPublisher<VersionInfo, Never> { run() }
-}
-
-public extension VersionChecker {
+    case outdated(DappVersionInformation)
+  }
 
-    static let mock: Self = .init { Just(.upToDate).eraseToAnyPublisher() }
-
-    static func live(
-        fetchVersion: VersionDataFetcher = .live(),
-        bundleVersion: @escaping () -> String = { Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String }
-    ) -> Self {
-        .init {
-            fetchVersion()
-                .map { dappInfo -> VersionInfo in
-                    let version = bundleVersion()
-                    if version >= dappInfo.recommended {
-                        return .upToDate
-                    } else if version >= dappInfo.minimum {
-                        return .updateRecommended(dappInfo)
-                    } else {
-                        return .updateRequired(dappInfo)
-                    }
-                }
-                .catch { Just(VersionInfo.failure($0)) }
-                .eraseToAnyPublisher()
-        }
-    }
+  public var verify: (@escaping VersionCompletion) -> Void
 }
 
-public extension VersionDataFetcher {
-    static func mock() -> Self {
-        .init {
-            Just(DappVersionInformation(
-                appUrl: "https://testflight.apple.com/join/L1Rj0so3",
-                minimum: "1.0",
-                recommended: "1.0",
-                minimumMessage: "This app version is not supported anymore, please update to the latest version to keep enjoying our app"
-            ))
-                .setFailureType(to: Error.self)
-                .eraseToAnyPublisher()
-        }
-    }
-
-    static func live() -> Self {
-        .init {
-            let request = URLRequest(
-                url: URL(string: "https://elixxir-bins.s3-us-west-1.amazonaws.com/client/dapps/appdb.json")!,
-                cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
-                timeoutInterval: 5
-            )
-
-            return URLSession.shared
-                .dataTaskPublisher(for: request)
-                .map(\.data)
-                .decode(type: BackendVersionInformation.self, decoder: JSONDecoder())
-                .map(\.info)
-                .eraseToAnyPublisher()
+public extension VersionCheck {
+  static let mock: Self = .init { $0(.outdated(.init(
+    appUrl: "https://testflight.apple.com/join/L1Rj0so3",
+    minimum: "2.0",
+    isRequired: false,
+    recommended: "5.0",
+    minimumMessage: "This app version is not supported anymore, please update to the latest version to keep enjoying our app"
+  ))) }
+
+  static let live: Self = .init { completion in
+    let request = URLRequest(
+      url: URL(string: "https://elixxir-bins.s3-us-west-1.amazonaws.com/client/dapps/appdb.json")!,
+      cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
+      timeoutInterval: 5
+    )
+    URLSession.shared.dataTask(with: request) { data, _, error in
+      if let error {
+        completion(.failure(error))
+        return
+      }
+      guard let data else {
+        fatalError("No data for version checking")
+      }
+      guard var model = try? JSONDecoder().decode(BackendVersionInformation.self, from: data) else {
+        fatalError()
+      }
+      let bundleVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String
+      if bundleVersion >= model.info.recommended {
+        completion(.upToDate)
+      } else {
+        if bundleVersion < model.info.minimum {
+          model.info.isRequired = true
         }
-    }
-}
-
-public struct DappVersionInformation: Codable {
-    public let appUrl: String
-    public let minimum: String
-    public let recommended: String
-    public let minimumMessage: String
-
-    private enum CodingKeys: String, CodingKey {
-        case appUrl = "new_ios_app_url"
-        case minimum = "new_ios_min_version"
-        case recommended = "new_ios_recommended_version"
-        case minimumMessage = "new_minimum_popup_msg"
-    }
-}
-
-private struct BackendVersionInformation: Codable {
-    let info: DappVersionInformation
-
-    private enum CodingKeys: String, CodingKey {
-        case info = "dapp-id"
-    }
+        completion(.outdated(model.info))
+      }
+    }.resume()
+  }
 }
diff --git a/Sources/XXNavigation/Actions/PresentDrawer.swift b/Sources/XXNavigation/Actions/PresentDrawer.swift
index 44e85b9584e45614c46225fb83c302d130e52e1a..01e878b3ec02bd211213f9e1fb2f73f95a0de0bc 100644
--- a/Sources/XXNavigation/Actions/PresentDrawer.swift
+++ b/Sources/XXNavigation/Actions/PresentDrawer.swift
@@ -1,9 +1,18 @@
 import Navigation
+import DrawerFeature
 
 public struct PresentDrawer: Navigation.Action {
+  public var items: [DrawerItem]
   public var animated: Bool = true
+  public var dismissable: Bool = true
 
-  public init(animated: Bool = true) {
+  public init(
+    items: [DrawerItem],
+    animated: Bool = true,
+    dismissable: Bool = true
+  ) {
+    self.items = items
     self.animated = animated
+    self.dismissable = dismissable
   }
 }
diff --git a/Sources/XXNavigation/CustomActions/OpenUp.swift b/Sources/XXNavigation/CustomActions/OpenUp.swift
new file mode 100644
index 0000000000000000000000000000000000000000..6aea3325084a82f7405f6e106087cbb3b8ff7f6c
--- /dev/null
+++ b/Sources/XXNavigation/CustomActions/OpenUp.swift
@@ -0,0 +1,201 @@
+import UIKit
+import Navigation
+
+/// Open up view controller on provided parent view controller
+public struct OpenUp: Action {
+  /// - Parameters:
+  ///   - viewController: View controller to present
+  ///   - parent: Parent view controller from which presentation should happen
+  ///   - animated: Animate the transition
+  ///   - dismissable: Dismissable upon background touch flag
+  public init(
+    _ viewController: UIViewController,
+    from parent: UIViewController,
+    animated: Bool = true,
+    dismissable: Bool = true
+  ) {
+    self.viewController = viewController
+    self.parent = parent
+    self.animated = animated
+    self.dismissable = dismissable
+  }
+
+  /// View controller to present
+  public var viewController: UIViewController
+
+  /// Parent view controller from which presentation should happen
+  public var parent: UIViewController
+
+  /// Animate the transition
+  public var animated: Bool
+
+  /// Dismissable upon background touch flag
+  public var dismissable: Bool
+}
+
+/// Performs `OpenUp` action
+public struct OpenUpNavigator: TypedNavigator {
+  let transitioningDelegate = BottomPresenter()
+
+  public init() {}
+
+  public func perform(_ action: OpenUp, completion: @escaping () -> Void) {
+    transitioningDelegate.isDismissableOnBackgroundTouch = action.dismissable
+    action.viewController.transitioningDelegate = transitioningDelegate
+    action.viewController.modalPresentationStyle = .overFullScreen
+
+    action.parent.present(
+      action.viewController,
+      animated: action.animated,
+      completion: completion
+    )
+  }
+}
+
+final class BottomPresenter: NSObject, UIViewControllerTransitioningDelegate {
+  var isDismissableOnBackgroundTouch: Bool = true
+  private var transition: BottomTransition?
+
+  public func animationController(
+    forPresented presented: UIViewController,
+    presenting: UIViewController,
+    source: UIViewController
+  ) -> UIViewControllerAnimatedTransitioning? {
+    transition = BottomTransition(isDismissableOnBackgroundTouch) { [weak self] in
+      self?.transition = nil
+    }
+
+    return transition
+  }
+
+  public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
+    transition?.direction = .dismiss
+    return transition
+  }
+}
+
+import Combine
+import SnapKit
+final class BottomTransition: NSObject, UIViewControllerAnimatedTransitioning {
+  enum Direction {
+    case present
+    case dismiss
+  }
+
+  let isDismissableOnBackground: Bool
+  var direction: Direction = .present
+  private let onDismissal: (() -> Void)?
+  private weak var darkOverlayView: UIControl?
+  private weak var topConstraint: Constraint?
+  private weak var bottomConstraint: Constraint?
+  private var cancellables = Set<AnyCancellable>()
+
+  private var presentedConstraints: [NSLayoutConstraint] = []
+  private var dismissedConstraints: [NSLayoutConstraint] = []
+
+  init(
+    _ isDismissableOnBackground: Bool = true,
+    onDismissal: (() -> Void)?
+  ) {
+    self.onDismissal = onDismissal
+    self.isDismissableOnBackground = isDismissableOnBackground
+    super.init()
+  }
+
+  func transitionDuration(using context: UIViewControllerContextTransitioning?) -> TimeInterval { 0.5 }
+
+  func animateTransition(using context: UIViewControllerContextTransitioning) {
+    switch direction {
+    case .present:
+      present(using: context)
+    case .dismiss:
+      dismiss(using: context)
+    }
+  }
+
+  private func present(using context: UIViewControllerContextTransitioning) {
+    guard let presentingController = context.viewController(forKey: .from),
+          let presentedView = context.view(forKey: .to) else {
+      context.completeTransition(false)
+      return
+    }
+
+    let darkOverlayView = UIControl()
+    self.darkOverlayView = darkOverlayView
+
+    darkOverlayView.alpha = 0.0
+    darkOverlayView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
+    context.containerView.addSubview(darkOverlayView)
+    darkOverlayView.frame = context.containerView.bounds
+
+    darkOverlayView
+      .publisher(for: .touchUpInside)
+      .sink { [weak presentingController] _ in
+        guard self.isDismissableOnBackground else { return }
+        presentingController?.dismiss(animated: true)
+      }.store(in: &cancellables)
+
+    context.containerView.addSubview(presentedView)
+    presentedView.translatesAutoresizingMaskIntoConstraints = false
+
+    presentedConstraints = [
+      presentedView.leftAnchor.constraint(equalTo: context.containerView.leftAnchor),
+      presentedView.rightAnchor.constraint(equalTo: context.containerView.rightAnchor),
+      presentedView.bottomAnchor.constraint(equalTo: context.containerView.bottomAnchor),
+      presentedView.topAnchor.constraint(
+        greaterThanOrEqualTo: context.containerView.safeAreaLayoutGuide.topAnchor,
+        constant: 60
+      )
+    ]
+
+    dismissedConstraints = [
+      presentedView.leftAnchor.constraint(equalTo: context.containerView.leftAnchor),
+      presentedView.rightAnchor.constraint(equalTo: context.containerView.rightAnchor),
+      presentedView.topAnchor.constraint(equalTo: context.containerView.bottomAnchor)
+    ]
+
+    NSLayoutConstraint.activate(dismissedConstraints)
+
+    context.containerView.setNeedsLayout()
+    context.containerView.layoutIfNeeded()
+
+    NSLayoutConstraint.deactivate(dismissedConstraints)
+    NSLayoutConstraint.activate(presentedConstraints)
+
+    UIView.animate(
+      withDuration: transitionDuration(using: context),
+      delay: 0,
+      usingSpringWithDamping: 1,
+      initialSpringVelocity: 0,
+      options: .curveEaseInOut,
+      animations: {
+        darkOverlayView.alpha = 1.0
+        context.containerView.setNeedsLayout()
+        context.containerView.layoutIfNeeded()
+      },
+      completion: { _ in
+        context.completeTransition(true)
+      })
+  }
+
+  private func dismiss(using context: UIViewControllerContextTransitioning) {
+    NSLayoutConstraint.deactivate(presentedConstraints)
+    NSLayoutConstraint.activate(dismissedConstraints)
+
+    UIView.animate(
+      withDuration: transitionDuration(using: context),
+      delay: 0,
+      usingSpringWithDamping: 1,
+      initialSpringVelocity: 0,
+      options: .curveEaseInOut,
+      animations: { [weak darkOverlayView] in
+        darkOverlayView?.alpha = 0.0
+        context.containerView.setNeedsLayout()
+        context.containerView.layoutIfNeeded()
+      },
+      completion: { [weak self] _ in
+        context.completeTransition(true)
+        self?.onDismissal?()
+      })
+  }
+}
diff --git a/Sources/XXNavigation/Navigators/PresentDrawerNavigator.swift b/Sources/XXNavigation/Navigators/PresentDrawerNavigator.swift
index 8b137891791fe96927ad78e64b0aad7bded08bdc..aec7278800720dbb2471ac9786f09b465d50adde 100644
--- a/Sources/XXNavigation/Navigators/PresentDrawerNavigator.swift
+++ b/Sources/XXNavigation/Navigators/PresentDrawerNavigator.swift
@@ -1 +1,30 @@
+import UIKit
+import Navigation
+import DrawerFeature
+import DependencyInjection
 
+public struct PresentDrawerNavigator: TypedNavigator {
+  @Dependency var navigator: Navigator
+  var screen: ([DrawerItem]) -> UIViewController
+  var navigationController: () -> UINavigationController
+
+  public func perform(_ action: PresentDrawer, completion: @escaping () -> Void) {
+    if let topViewController = navigationController().topViewController {
+      let openUpAction = OpenUp(
+        screen(action.items),
+        from: topViewController,
+        animated: action.animated,
+        dismissable: action.dismissable
+      )
+      navigator.perform(openUpAction, completion: completion)
+    }
+  }
+
+  public init(
+    screen: @escaping ([DrawerItem]) -> UIViewController,
+    navigationController: @escaping () -> UINavigationController
+  ) {
+    self.screen = screen
+    self.navigationController = navigationController
+  }
+}