diff --git a/angular.json b/angular.json
index ae1dc865367ff75c2bfd702eba2844a73cdde45e..aff02da09db3a24586474ff7c4fd24ffc9f5a605 100644
--- a/angular.json
+++ b/angular.json
@@ -33,7 +33,8 @@
               "bn.js",
               "ip-regexp",
               "eventemitter3",
-              "qrious"
+              "qrious",
+              "localforage-cordovasqlitedriver"
             ],
             "assets": [
               {
@@ -78,6 +79,18 @@
                   "with": "src/environments/environment.prod.ts"
                 }
               ],
+              "budgets": [
+                {
+                  "type": "initial",
+                  "maximumWarning": "6mb",
+                  "maximumError": "8mb"
+                },
+                {
+                  "type": "anyComponentStyle",
+                  "maximumWarning": "6kb",
+                  "maximumError": "10kb"
+                }
+              ],
               "optimization": true,
               "outputHashing": "all",
               "sourceMap": false,
@@ -85,14 +98,7 @@
               "aot": true,
               "extractLicenses": true,
               "vendorChunk": false,
-              "buildOptimizer": true,
-              "budgets": [
-                {
-                  "type": "initial",
-                  "maximumWarning": "2mb",
-                  "maximumError": "5mb"
-                }
-              ]
+              "buildOptimizer": true
             },
             "ci": {
               "progress": false
@@ -107,17 +113,12 @@
           "configurations": {
             "production": {
               "browserTarget": "app:build:production"
-            },
+            }
             "ci": {
+              "progress": false
             }
           }
         },
-        "extract-i18n": {
-          "builder": "@angular-devkit/build-angular:extract-i18n",
-          "options": {
-            "browserTarget": "app:build"
-          }
-        },
         "test": {
           "builder": "@angular-devkit/build-angular:karma",
           "options": {
diff --git a/package.json b/package.json
index 16fe5ad37069f8dd8c6a73ecf5bff1d3452ac625..af6c3d8e0031e00da6786b2e8313b1dddccc333d 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,10 @@
   "scripts": {
     "ng": "ng",
     "start": "ng serve",
-    "build": "ng build",
+    "start.android": "ionic capacitor run android -l --external",
+    "build": "ng build --configuration production",
+    "build.android": "ionic capacitor build android --configuration production",
+    "sync.android": "npx cap sync android",
     "test": "ng test",
     "lint": "ng lint",
     "e2e": "ng e2e",
@@ -30,20 +33,21 @@
     "@angular/platform-browser-dynamic": "^14.2.1",
     "@angular/router": "^14.2.1",
     "@capacitor-community/barcode-scanner": "3.0.0",
-    "@capacitor-community/sqlite": "^4.0.1",
-    "@capacitor/android": "4.1.0",
-    "@capacitor/app": "4.0.1",
-    "@capacitor/clipboard": "^4.0.1",
-    "@capacitor/core": "4.1.0",
-    "@capacitor/haptics": "4.0.1",
-    "@capacitor/keyboard": "4.0.1",
-    "@capacitor/splash-screen": "4.0.1",
-    "@capacitor/status-bar": "4.0.1",
+    "@capacitor/android": "~4.2.0",
+    "@capacitor/app": "~4.0.1",
+    "@capacitor/browser": "~4.0.1",
+    "@capacitor/camera": "~4.1.1",
+    "@capacitor/clipboard": "~4.0.1",
+    "@capacitor/core": "~4.1.0",
+    "@capacitor/haptics": "~4.0.1",
+    "@capacitor/keyboard": "~4.0.1",
+    "@capacitor/splash-screen": "~4.0.1",
+    "@capacitor/status-bar": "~4.0.1",
     "@ionic/angular": "^6.2.6",
+    "@ionic/pwa-elements": "^3.1.1",
     "@ionic/storage-angular": "^3.0.6",
     "@ngx-translate/core": "^14.0.0",
     "@ngx-translate/http-loader": "^7.0.0",
-    "@ionic/pwa-elements": "~3.1.1",
     "@polkadot/api": "^9.2.4",
     "@polkadot/keyring": "^10.1.6",
     "@polkadot/networks": "^10.1.6",
@@ -54,6 +58,8 @@
     "angular2-qrcode": "^2.0.3",
     "crypto-browserify": "^3.12.0",
     "jdenticon": "^3.1.1",
+    "localforage": "~1.10.0",
+    "localforage-cordovasqlitedriver": "~1.8.0",
     "moment": "^2.29.4",
     "moment-timezone": "^0.5.37",
     "ngx-jdenticon": "^1.0.4",
@@ -63,11 +69,6 @@
     "zone.js": "~0.11.8"
   },
   "devDependencies": {
-    "ngx-color-picker": "^12.0.1",
-    "ngx-jdenticon": "^1.0.4",
-    "ngx-markdown": "^14.0.1",
-    "ngx-material-timepicker": "5.5.3",
-    "ngx-quicklink": "^0.3.0",
     "@angular-devkit/build-angular": "^14.2.2",
     "@angular-eslint/builder": "~13.5.0",
     "@angular-eslint/eslint-plugin": "~13.5.0",
@@ -78,13 +79,13 @@
     "@angular/compiler-cli": "^14.2.1",
     "@angular/language-service": "^14.2.1",
     "@capacitor/cli": "4.1.0",
-    "@ionic/cli": "^6.20.1",
     "@ionic/angular-toolkit": "^6.1.0",
+    "@ionic/cli": "^6.20.1",
     "@polkadot/typegen": "^9.2.4",
     "@polkadot/types": "^9.2.4",
     "@types/jasmine": "~4.0.3",
     "@types/jasminewd2": "~2.0.10",
-    "@types/node": "^12.20.55",
+    "@types/node": "^14.18.28",
     "@typescript-eslint/eslint-plugin": "4.33.0",
     "@typescript-eslint/parser": "4.33.0",
     "eslint": "^7.32.0",
@@ -99,8 +100,13 @@
     "karma-coverage-istanbul-reporter": "~3.0.3",
     "karma-jasmine": "~5.1.0",
     "karma-jasmine-html-reporter": "^2.0.0",
+    "ngx-color-picker": "^12.0.1",
+    "ngx-jdenticon": "^1.0.4",
+    "ngx-markdown": "^14.0.1",
+    "ngx-material-timepicker": "5.5.3",
+    "ngx-quicklink": "^0.3.0",
     "protractor": "~7.0.0",
-    "ts-node": "^8.6.2",
+    "ts-node": "^8.10.2",
     "typescript": "~4.6.4"
   },
   "engines": {
diff --git a/scripts/get-definitions.sh b/scripts/get-definitions.sh
index 4fa84af48dc3f38c67b199d862e34f1af5905ff9..52b41e87c6894f2ad8e0d2728f1d160e506d083d 100755
--- a/scripts/get-definitions.sh
+++ b/scripts/get-definitions.sh
@@ -1,3 +1,5 @@
 #!/bin/bash
 
-curl -H "Content-Type: application/json" -d '{"id":"1", "jsonrpc":"2.0", "method": "state_getMetadata", "params":[]}' http://localhost:9933 > ../src/interfaces/duniter-types.json
+NODE=http://localhost:9933
+
+curl -H "Content-Type: application/json" -d '{"id":"1", "jsonrpc":"2.0", "method": "state_getMetadata", "params":[]}' ${NODE} > ../src/interfaces/types.json
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 93b19d50ee8e532d1e778449d156bbac7c103bb3..3685daf745cbc27dd12b5a54ee807849ea20ee9a 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -2,9 +2,10 @@
   <ion-split-pane contentId="main-content">
     <ion-menu contentId="main-content" type="overlay">
       <ion-content>
-        <ion-list id="inbox-list">
-          <ion-list-header [innerHTML]="appName"></ion-list-header>
-          <ion-note>profile name</ion-note>
+        <ion-list id="main-menu-list">
+          <ion-list-header [innerHTML]="'COMMON.APP_NAME'|translate"></ion-list-header>
+
+          <ion-note></ion-note>
 
           <ion-menu-toggle auto-hide="false" *ngFor="let p of appPages; let i = index">
             <ion-item routerDirection="root" *ngIf="p.url"
@@ -13,7 +14,7 @@
               <ion-label [color]="p.color">{{ p.title | translate }}</ion-label>
             </ion-item>
             <ion-item routerDirection="root" *ngIf="p.handle && p.enable()"
-                      (click)="p.handle()" lines="none" detail="false" routerLinkActive="selected">
+                      (click)="p.handle($event)" lines="none" detail="false" routerLinkActive="selected">
               <ion-icon slot="start" [color]="p.color" [ios]="p.icon + '-outline'" [md]="p.icon + '-sharp'"></ion-icon>
               <ion-label [color]="p.color">{{ p.title | translate }}</ion-label>
             </ion-item>
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
index a893dff7bf27fb35a829b4722afe45cb3d063032..38e7fad34e398b3a884fc1ac74d1c28e70d20af6 100644
--- a/src/app/app.component.scss
+++ b/src/app/app.component.scss
@@ -43,11 +43,11 @@ ion-menu.md ion-note {
   padding-left: 10px;
 }
 
-ion-menu.md ion-list#inbox-list {
+ion-menu.md ion-list#main-menu-list {
   border-bottom: 1px solid var(--ion-color-step-150, #d7d8da);
 }
 
-ion-menu.md ion-list#inbox-list ion-list-header {
+ion-menu.md ion-list#main-menu-list ion-list-header {
   font-size: 22px;
   font-weight: 600;
 
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 7094125d21af9bdefca802b594d5ea5c9f7ba5ae..45a0e0cecc327348ac6dfb1e546fbd1142653829 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -3,6 +3,8 @@ import {PlatformService} from "./shared/services/platform.service";
 import {environment} from "@environments/environment";
 import {AccountService} from "@app/wallet/account.service";
 import {Router} from "@angular/router";
+import {App} from "@capacitor/app";
+import {isNotNilOrBlank} from "@app/shared/functions";
 
 @Component({
   selector: 'app-root',
@@ -11,7 +13,7 @@ import {Router} from "@angular/router";
 })
 export class AppComponent {
 
-  appName = environment.name;
+  appName = 'COMMON.APP_NAME';
   appPages = [
 
     { title: 'MENU.HOME', url: '/home', icon: 'home' },
@@ -24,10 +26,10 @@ export class AppComponent {
 
     { title: 'MENU.SETTINGS', url: '/settings', icon: 'settings' },
 
-    { title: 'HOME.BTN_CHANGE_ACCOUNT', icon: 'log-out', color: 'danger',
+    { title: 'COMMON.BTN_LOGOUT', icon: 'log-out', color: 'danger',
 
       handle: (event) => this.logout(event),
-      enable: () => this.accountService.isLogin
+      enable: () => this.accountService.isLogin && this.platform.mobile
     },
   ];
 
@@ -41,9 +43,13 @@ export class AppComponent {
     var now = Date.now();
     console.info('[app] Starting...');
 
+    // Start all stuff (services, plugins, etc.)
     await this.platform.start();
 
     console.info(`[app] Starting [OK] in ${Date.now()-now}ms`);
+
+    // Detecting deep link
+    await this.detectDeepLink();
   }
 
   async logout(event) {
@@ -52,4 +58,28 @@ export class AppComponent {
       replaceUrl: true
     });
   }
+
+  async detectDeepLink(){
+    try {
+      const {url} = await App.getLaunchUrl();
+      if (isNotNilOrBlank(url)) {
+
+        const slashIndex = url.indexOf('/');
+        if (slashIndex !== -1) {
+          const relativeUrl = url.substring(slashIndex+1);
+          console.info('[app] Detected a deep link: ' + relativeUrl);
+
+          // TODO: call the router ?
+          await this.router.navigateByUrl(relativeUrl);
+        }
+        else {
+          console.warn(`[app] Detected a INVALID deep link: ${url} - missing slash`);
+        }
+      }
+    }
+    catch(err) {
+      console.error(`[platform] Cannot get launch URL: ${err.message||err}\n${err?.originalStack || JSON.stringify(err)}`);
+      // Continue
+    }
+  }
 }
diff --git a/src/app/auth/auth.controller.ts b/src/app/auth/auth.controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5bcd6e7fa1897c7909845bc9cdd0c62f7d371782
--- /dev/null
+++ b/src/app/auth/auth.controller.ts
@@ -0,0 +1,141 @@
+import {
+  ActionSheetButton,
+  ActionSheetController,
+  ActionSheetOptions,
+  IonModal,
+  ModalController,
+  PopoverController
+} from "@ionic/angular";
+import {Injectable} from "@angular/core";
+import {PlatformService} from "@app/shared/services/platform.service";
+import {PopoverOptions} from "@ionic/core";
+import {ListItem, ListPopover, ListPopoverOptions} from "@app/shared/popover/list.popover";
+import {TranslateService} from "@ngx-translate/core";
+import {AuthModal, AuthModalOptions} from "@app/auth/auth.modal";
+import {Router} from "@angular/router";
+import {RegisterModal, RegisterModalOptions} from "@app/register/register.modal";
+
+export declare type LoginMethodType = 'v1' | 'v2' | 'keyfile-v1';
+export const LoginMethods: ListItem[] = [
+  {value: 'v1', label: 'Compte Duniter v1'},
+  {value: 'v2', label: 'Phrase de restauration'},
+  {value: 'keyfile-v1', label: 'Fichier de clef Duniter v1', disabled: true}
+];
+
+@Injectable()
+export class AuthController {
+
+  private _mobile = this.platform.mobile;
+
+  protected actionSheetOptions: Partial<ActionSheetOptions> = {
+    backdropDismiss: true,
+    cssClass: 'select-login-action-sheet'
+  };
+  protected popoverOptions: Partial<PopoverOptions> = {
+    backdropDismiss: true,
+    cssClass: 'select-login-popover',
+    reference: 'event'
+  };
+
+  constructor(
+    private platform: PlatformService,
+    private translate: TranslateService,
+    private popoverCtrl: PopoverController,
+    private actionSheetCtrl: ActionSheetController,
+    private modalCtrl: ModalController,
+    private router: Router
+  ) {
+  }
+
+  async login(event, opts?: {
+    loginMethod?: LoginMethodType,
+    auth?: boolean,
+    redirectToWalletPage?: boolean
+  }) {
+
+    let loginMethod = opts?.loginMethod;
+
+    // Ask login method
+    if (!loginMethod) {
+
+      // ...using popover
+      if (!this._mobile) {
+        const popover = await this.popoverCtrl.create(<PopoverOptions>{
+          event,
+          backdropDismiss: true,
+          component: ListPopover,
+          componentProps: <ListPopoverOptions>{
+            title: 'LOGIN.METHOD_POPOVER_TITLE',
+            items: LoginMethods
+          }
+        })
+        await popover.present(event);
+        const {data} = await popover.onWillDismiss();
+        loginMethod = data;
+      }
+      else {
+        const actionSheet = await this.actionSheetCtrl.create({
+          ...this.actionSheetOptions,
+          header: this.translate.instant('LOGIN.METHOD_POPOVER_TITLE'),
+          buttons: LoginMethods.map(method => {
+            return <ActionSheetButton>{
+              id: method.value,
+              data: method.value,
+              text: this.translate.instant(method.label)
+            }
+          })
+        });
+        await actionSheet.present();
+        const {data} = await actionSheet.onWillDismiss();
+        loginMethod = data;
+      }
+    }
+    if (!loginMethod) return undefined; // User cancelled
+
+    console.info('[auth] Selected login method: ' + loginMethod);
+
+    let modal: HTMLIonModalElement;
+    switch (loginMethod) {
+      case 'v1':
+        modal = await this.modalCtrl.create({
+          component: AuthModal,
+          componentProps: <AuthModalOptions>{
+            auth: opts?.auth,
+            scrollY: false // TODO remove this !
+          }
+        });
+        break;
+      default:
+        console.warn('[home] Unknown login method: ' + loginMethod);
+    }
+    if (!modal) return; // User cancelled of method not found
+
+    await modal.present();
+    const {data} = await modal.onWillDismiss();
+
+    if (data?.address && opts?.redirectToWalletPage === true) {
+      setTimeout(() => this.router.navigate(['/wallet', data.address]));
+    }
+
+    return data;
+  }
+
+  async register(opts?: { redirectToWalletPage?: boolean; }) {
+    const modal = await this.modalCtrl.create({
+      component: RegisterModal,
+      componentProps: <RegisterModalOptions>{
+        scrollY: false // TODO remove this !
+      }
+    });
+
+    await modal.present();
+
+    const {data} = await modal.onWillDismiss();
+
+    if (data?.address && opts.redirectToWalletPage === true) {
+      setTimeout(() => this.router.navigate(['/wallet', data.address]));
+    }
+
+    return data;
+  }
+}
diff --git a/src/app/auth/auth.form.ts b/src/app/auth/auth.form.ts
index ec03debab67fd4e7f6eecdc6da08f17f25489bfd..fa85892ed137fee8974d635f745fb82fe152aa0f 100644
--- a/src/app/auth/auth.form.ts
+++ b/src/app/auth/auth.form.ts
@@ -7,7 +7,7 @@ import {AppForm} from "@app/shared/form.class";
 import {AuthData} from "@app/auth/auth.model";
 import {SettingsService} from "@app/settings/settings.service";
 import {NetworkService} from "@app/network/network.service";
-import {environment} from "@duniter/core-types/environments/environment";
+import {environment} from "@environments/environment";
 import {FormUtils} from "@app/shared/forms";
 
 
@@ -64,7 +64,7 @@ export class AuthForm extends AppForm<AuthData> implements OnInit {
     this.onCancel.emit();
   }
 
-  async doSubmit(event?: UIEvent) {
+  async doSubmit(event?: Event) {
     if (event) {
       event.preventDefault();
       event.stopPropagation();
diff --git a/src/app/auth/auth.modal.html b/src/app/auth/auth.modal.html
index 55f2ea09ca66e5ec382178acda30b0ed4098b10e..1c8b70f8517e487ddb2f69c6697375e6fa462aba 100644
--- a/src/app/auth/auth.modal.html
+++ b/src/app/auth/auth.modal.html
@@ -6,7 +6,7 @@
       </ion-button>
     </ion-buttons>
 
-    <ion-title [innerHTML]="auth?'AUTH.TITLE': 'LOGIN.TITLE'|translate">
+    <ion-title [innerHTML]="title|translate">
     </ion-title>
 
     <ion-buttons slot="end">
@@ -20,7 +20,7 @@
   </ion-toolbar>
 </ion-header>
 
-<ion-content style="height: 100%">
+<ion-content style="height: 100%" [scrollY]="scrollY">
   <app-auth-form #form
                  (onSubmit)="doSubmit($event)"
                  (onCancel)="cancel()">
diff --git a/src/app/auth/auth.modal.ts b/src/app/auth/auth.modal.ts
index 31cf3c46be3dc0d95fabfcfdd4f73f23e6fb68cd..088c0d8965bc3e075aa894786202b0be07c3f8dc 100644
--- a/src/app/auth/auth.modal.ts
+++ b/src/app/auth/auth.modal.ts
@@ -5,15 +5,19 @@ import {AuthForm} from './auth.form';
 import {firstNotNilPromise} from '@app/shared/observables';
 import {AuthData} from "@app/auth/auth.model";
 
+export interface AuthModalOptions {
+  auth?: boolean;
+  scrollY?: boolean;
+  title?: string;
+}
 @Component({
   selector: 'app-auth-modal',
   templateUrl: 'auth.modal.html',
   styleUrls: ['./auth.modal.scss'],
   changeDetection: ChangeDetectionStrategy.OnPush
 })
-export class AuthModal implements OnInit {
+export class AuthModal implements OnInit, AuthModalOptions {
 
-  title: string = null;
   get loading() {
     return this.form?.loading;
   }
@@ -22,7 +26,9 @@ export class AuthModal implements OnInit {
     return this.form?.mobile;
   }
 
-  @Input() auth = false;
+  @Input() auth = false; // false for login, true for auth
+  @Input() scrollY = false;
+  @Input() title: string = null;
 
   @ViewChild('form', { static: true }) private form: AuthForm;
 
@@ -34,7 +40,7 @@ export class AuthModal implements OnInit {
 
   ngOnInit() {
 
-    this.title = this.auth ? 'AUTH.TITLE' : 'LOGIN.TITLE';
+    this.title = this.title || (this.auth ? 'AUTH.TITLE' : 'LOGIN.TITLE');
 
     this.form.markAsReady({emitEvent: false});
     this.form.markAsLoaded();
diff --git a/src/app/auth/auth.module.ts b/src/app/auth/auth.module.ts
index ffa85109d7267836759ed97330b6e5ecda79efd9..a37e4a98b80452148e262da4a9e0458bb4ab590b 100644
--- a/src/app/auth/auth.module.ts
+++ b/src/app/auth/auth.module.ts
@@ -7,19 +7,31 @@ import {AuthForm} from "./auth.form";
 import {AuthModal} from "./auth.modal";
 import {AppSharedModule} from "@app/shared/shared.module";
 import {TranslateModule} from "@ngx-translate/core";
+import {AuthController} from "@app/auth/auth.controller";
+import {AppRegisterModule} from "@app/register/register.module";
 
 @NgModule({
-    imports: [
-      CommonModule,
-      FormsModule,
-      ReactiveFormsModule,
-      IonicModule,
-      AppSharedModule,
-      TranslateModule
-    ],
-    exports: [
-        AuthForm, AuthModal
-    ],
-    declarations: [AuthForm, AuthModal]
+  imports: [
+    CommonModule,
+    FormsModule,
+    ReactiveFormsModule,
+    IonicModule,
+    TranslateModule,
+
+    // App modules
+    AppSharedModule,
+    AppRegisterModule
+  ],
+  declarations: [
+    AuthForm, AuthModal
+  ],
+  exports: [
+    AuthForm,
+    AuthModal,
+    TranslateModule
+  ],
+  providers: [
+    AuthController
+  ]
 })
 export class AppAuthModule {}
diff --git a/src/app/home/home.page.html b/src/app/home/home.page.html
index 75f792e4692f59f6a2b21cdfe04c139e3409bb23..7d658f00b731ffdbda65ea1b5271de70a67b2953 100644
--- a/src/app/home/home.page.html
+++ b/src/app/home/home.page.html
@@ -90,41 +90,6 @@
   </div>
 </ion-content>
 
-<ion-popover #loginMethodPopover>
-  <ng-template>
-    <ion-content>
-      <ion-list>
-        <ion-item *ngFor="let item of loginMethods"
-                  [disabled]="item.disabled"
-                  (click)="loginMethodPopover.dismiss(item.value)"
-                  tappable>
-          {{item.label|translate}}
-        </ion-item>
-      </ion-list>
-    </ion-content>
-  </ng-template>
-</ion-popover>
-
-<ion-modal
-  #loginModal
-  [backdropDismiss]="false">
-  <ng-template>
-    <ion-content scrollY="false">
-      <app-auth-modal></app-auth-modal>
-    </ion-content>
-  </ng-template>
-</ion-modal>
-
-<ion-modal
-  #registerModal
-  [backdropDismiss]="false">
-  <ng-template>
-    <ion-content scrollY="false">
-      <app-register-modal></app-register-modal>
-    </ion-content>
-  </ng-template>
-</ion-modal>
-
 <ng-template #localeButton let-buttonColor>
 
   <!-- locale button -->
@@ -140,7 +105,7 @@
       <ion-content>
         <ion-list>
           <ion-item *ngFor="let item of locales"
-                    (click)="changeLocale(item.key) || popover.dismiss()"
+                    (click)="changeLocale(item.key) && popover.dismiss()"
                     tappable>
             <ion-label>{{item.value}}</ion-label>
             <ion-icon slot="end" name="checkmark" *ngIf="data.locale===item.key"></ion-icon>
diff --git a/src/app/home/home.page.ts b/src/app/home/home.page.ts
index 9a37e0b57ba5eab060d7344229de4cb87a6efca9..6cf6c3b7a3cecfef202e9794155d0d11671908a8 100644
--- a/src/app/home/home.page.ts
+++ b/src/app/home/home.page.ts
@@ -1,29 +1,13 @@
-import {Component, Inject, Injector, OnInit, ViewChild} from '@angular/core';
-import {SettingsService} from "@app/settings/settings.service";
+import {Component, Inject, Injector, OnInit} from '@angular/core';
 import {APP_LOCALES, LocaleConfig, Settings} from "@app/settings/settings.model";
 import {BasePage} from "@app/shared/pages/base.page";
 import {NetworkService} from "@app/network/network.service";
-import {AbbreviatePipe} from "@app/shared/pipes/string.pipes";
 import {AccountService} from "@app/wallet/account.service";
 import {Account} from "@app/wallet/account.model";
 import {fadeInAnimation} from "@app/shared/animations";
-import {AuthModal} from "@app/auth/auth.modal";
-import {RegisterModal} from "@app/register/register.modal";
-import {
-  ActionSheetButton,
-  ActionSheetController,
-  ActionSheetOptions,
-  IonModal,
-  IonPopover,
-  PopoverOptions
-} from "@ionic/angular";
 import {Router} from "@angular/router";
+import {AuthController} from "@app/auth/auth.controller";
 
-export interface LoginMethod {
-  value: string;
-  label: string;
-  disabled?: boolean;
-}
 @Component({
   selector: 'app-home',
   templateUrl: './home.page.html',
@@ -32,22 +16,6 @@ export interface LoginMethod {
 })
 export class HomePage extends BasePage<Settings> implements OnInit {
 
-
-  protected actionSheetOptions: Partial<ActionSheetOptions> = {
-    backdropDismiss: true,
-    cssClass: 'select-login-action-sheet'
-  };
-  protected popoverOptions: Partial<PopoverOptions> = {
-    backdropDismiss: true,
-    cssClass: 'select-login-popover',
-    reference: 'event'
-  };
-  protected loginMethods: LoginMethod[] = [
-    {value: 'v1', label: 'Compte Duniter v1'},
-    {value: 'v2', label: 'Phrase de restauration'},
-    {value: 'keyfile-v1', label: 'Fichier de clef Duniter v1', disabled: true}
-  ];
-
   currency: string = null;
   defaultAccount: Account = null;
 
@@ -55,16 +23,12 @@ export class HomePage extends BasePage<Settings> implements OnInit {
     return this.accountService.isLogin
   }
 
-  @ViewChild('loginModal') loginModal: IonModal;
-  @ViewChild('registerModal') registerModal: IonModal;
-  @ViewChild('loginMethodPopover') loginMethodPopover: IonPopover;
-
   constructor(
     injector: Injector,
     public networkService: NetworkService,
     public accountService: AccountService,
+    public authController: AuthController,
     public router: Router,
-    public actionSheetCtrl: ActionSheetController,
     @Inject(APP_LOCALES) public locales: LocaleConfig[]
   ) {
     super(injector, {name: 'home'})
@@ -89,62 +53,25 @@ export class HomePage extends BasePage<Settings> implements OnInit {
   }
 
 
-  changeLocale(locale: string) {
+  changeLocale(locale: string): boolean  {
     this.settings.patchValue({locale});
     this.data.locale = locale;
     this.markForCheck();
+    return true;
   }
 
-  async login(event) {
-
-    let loginMethod: string;
-    if (!this.mobile) {
-      await this.loginMethodPopover.present(event);
-      const {data} = await this.loginMethodPopover.onWillDismiss();
-      loginMethod = data;
-    }
-    else {
-      const actionSheet = await this.actionSheetCtrl.create({
-        ...this.actionSheetOptions,
-        header: this.translate.instant('Select login method'),
-        buttons: this.loginMethods.map(method => {
-          return <ActionSheetButton>{
-            data: method.value,
-            text: this.translate.instant(method.label),
-            id: method.value
-          }
-        })
-      });
-      await actionSheet.present();
-      const {data} = await actionSheet.onWillDismiss();
-      loginMethod = data;
-    }
-    if (!loginMethod) return;
-    console.info('[home] Selected login method: ' + loginMethod);
-
-    let modal: IonModal;
-    switch (loginMethod) {
-      case 'v1':
-        modal = this.loginModal;
-        break;
-      default:
-        console.warn('[home] Unknown login method: ' + loginMethod);
-    }
-    if (!modal) return; // User cancelled of method not found
-
-    await modal.present();
-    const {data} = await modal.onWillDismiss();
+  async login(event: UIEvent) {
+    const data = await this.authController.login(event, {
+      auth: true
+    });
     if (data?.address) {
       this.defaultAccount = data;
       setTimeout(() => this.router.navigate(['/wallet', data.address]));
     }
   }
 
-  async register(event) {
-    await this.registerModal.present();
-
-    const {data} = await this.registerModal.onWillDismiss();
-
+  async register() {
+    const data = await this.authController.register();
     if (data?.address) {
       this.defaultAccount = data;
       setTimeout(() => this.router.navigate(['/wallet', data.address]));
diff --git a/src/app/network/network.service.ts b/src/app/network/network.service.ts
index 84acf88ea69216fc73d6534c45f463a67b7e8859..59eba32d8a2407b9870152ee15b8bf6d0bc7525c 100644
--- a/src/app/network/network.service.ts
+++ b/src/app/network/network.service.ts
@@ -5,7 +5,6 @@ import {Peer, Peers} from "./peer.model";
 import {StartableService} from "@app/shared/services/startable-service.class";
 import {abbreviate} from "@app/shared/currencies";
 import {Currency} from "@app/network/currency.model";
-//import * as definitions from '@duniter/core-types/interfaces'
 
 const WELL_KNOWN_CURRENCIES = Object.freeze({
   'Ğdev': <Partial<Currency>>{
diff --git a/src/app/register/register.modal.html b/src/app/register/register.modal.html
index 645a8b136fc248d206f0b669aeb6f1ec1a3e0e01..f719fc3495e31305cda63833cf86ade7d8a66b6e 100644
--- a/src/app/register/register.modal.html
+++ b/src/app/register/register.modal.html
@@ -26,12 +26,12 @@
   </ion-toolbar>
 </ion-header>
 
-<ion-content >
+<ion-content [scrollY]="scrollY">
+
   <app-register-form (onSubmit)="doSubmit()" (onCancel)="cancel()"
                      [class]="mobile ? '': 'has-footer'"
                      #form>
 
-
     <ion-row *ngIf="form.debug" codeConfirmation>
       <ion-text color="primary"  class="ion-padding">
         <small>loading: {{loading}}<br/>
diff --git a/src/app/register/register.modal.ts b/src/app/register/register.modal.ts
index 963efb4db1ed3af5fea0a8b653dd67a322d21368..319bd833b72858ec22b62d66332c3b845a7c7cc5 100644
--- a/src/app/register/register.modal.ts
+++ b/src/app/register/register.modal.ts
@@ -1,21 +1,21 @@
-import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, ViewChild} from '@angular/core';
+import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, ViewChild} from '@angular/core';
 import {ModalController} from '@ionic/angular';
 import {REGISTER_FORM_SLIDES, RegisterForm} from "@app/register/register.form";
 import {AccountService} from "@app/wallet/account.service";
 import {FormUtils} from "@app/shared/forms";
 import {RegisterData} from "@app/register/register.model";
 import {environment} from "@environments/environment";
-
+export interface RegisterModalOptions {
+  scrollY?: boolean;
+}
 @Component({
   selector: 'app-register-modal',
   templateUrl: 'register.modal.html',
   styleUrls: ['./register.modal.scss'],
   changeDetection: ChangeDetectionStrategy.OnPush
 })
-export class RegisterModal implements OnInit{
-
+export class RegisterModal implements OnInit, RegisterModalOptions {
 
-  @ViewChild('form', { static: true }) private form: RegisterForm;
 
   get loading() {
     return this.form.loading;
@@ -25,6 +25,9 @@ export class RegisterModal implements OnInit{
     return this.form.mobile;
   }
 
+  @Input() scrollY = false;
+  @ViewChild('form', { static: true }) private form: RegisterForm;
+
   constructor(
     private accountService: AccountService,
     public viewCtrl: ModalController,
diff --git a/src/app/register/register.module.ts b/src/app/register/register.module.ts
index e7ceb82c397ae47fc88da4af48d8f1132313e563..9c68966f32f2ec0e19f52544b11f5707948bbcb9 100644
--- a/src/app/register/register.module.ts
+++ b/src/app/register/register.module.ts
@@ -10,17 +10,22 @@ import {AppSharedModule} from "@app/shared/shared.module";
 import {AppUnlockModule} from "@app/unlock/unlock.module";
 
 @NgModule({
-    imports: [
-      CommonModule,
-      FormsModule,
-      IonicModule,
-      TranslateModule,
-      AppSharedModule,
-      AppUnlockModule
-    ],
-    exports: [
-        RegisterForm, RegisterModal
-    ],
-    declarations: [RegisterForm, RegisterModal]
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    TranslateModule,
+    AppSharedModule,
+    AppUnlockModule
+  ],
+  declarations: [
+    RegisterForm,
+    RegisterModal
+  ],
+  exports: [
+    RegisterForm,
+    RegisterModal,
+    TranslateModule
+  ]
 })
 export class AppRegisterModule {}
diff --git a/src/app/shared/colors/colors.utils.ts b/src/app/shared/colors/colors.utils.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fda2b2391423077235d7886e39b21c2f9fa6e971
--- /dev/null
+++ b/src/app/shared/colors/colors.utils.ts
@@ -0,0 +1,86 @@
+export type PredefinedColors =
+  | 'primary'
+  | 'secondary'
+  | 'tertiary'
+  | 'success'
+  | 'warning'
+  | 'danger'
+  | 'light'
+  | 'medium'
+  | 'dark';
+
+
+export function rgbToHex(r: number, g: number, b: number): string {
+  return '#' + componentToHex(r) + componentToHex(g) + componentToHex(b);
+}
+
+export function rgbArrayToHex(rgb: number[]): string {
+  return '#' + componentToHex(rgb[0]) + componentToHex(rgb[1]) + componentToHex(rgb[2]);
+}
+
+export function hexToRgbArray(hex: string): number[] {
+  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+  return result ? [
+    parseInt(result[1], 16),
+    parseInt(result[2], 16),
+    parseInt(result[3], 16)
+  ] : null;
+}
+
+export function hexToRgb(hex: string): {r: number; g: number; b: number} {
+  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+  return result ? {
+    r: parseInt(result[1], 16),
+    g: parseInt(result[2], 16),
+    b: parseInt(result[3], 16)
+  } : null;
+}
+
+function componentToHex(c: number): string {
+  const hex = c.toString(16);
+  return hex.length === 1 ? ('0' + hex) : hex;
+}
+
+// See mix in file ionic.functions.color.scss
+export function mixHex(color1: string, color2: string, weight?: number) {
+
+  weight = weight ? (weight / 100) : 0.5;
+
+  const rgb1 = hexToRgbArray(color1);
+  if (!rgb1) throw Error('Invalid hex color:' + color1);
+
+  const rgb2 = hexToRgbArray(color2);
+  if (!rgb2) throw Error('Invalid hex color:' + color2);
+
+  const rgbAverage = rgb1.map((v, index) => Math.round((v * weight + rgb2[index] * (1 - weight))) );
+  return rgbArrayToHex(rgbAverage);
+}
+
+// 12% darker version of the base color (mix with black)
+export function getColorShade(color: string) {
+  return mixHex('#000000', color, 12);
+}
+
+// 10% lighter version of the base color (mix with white)
+export function getColorTint(color: string) {
+  return mixHex('#ffffff', color, 10);
+}
+
+/**
+ *
+ * @param color
+ * @param bw if true, will use black or white color, instead of the exact inverse
+ */
+export function getColorContrast(color: string, bw?: boolean) {
+  const rgb = hexToRgbArray(color);
+  if (!rgb) throw Error('Invalid hex color:' + color);
+
+  if (bw === true) {
+    // http://stackoverflow.com/a/3943023/112731
+    return (rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114) > 186
+      ? '#000000'
+      : '#FFFFFF';
+  }
+
+  return rgbArrayToHex(rgb.map(v => 255 - v));
+}
diff --git a/src/app/shared/colors/graph-colors.ts b/src/app/shared/colors/graph-colors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e0cf365cf8b6b018c5960fd45ffe1ff062cefe98
--- /dev/null
+++ b/src/app/shared/colors/graph-colors.ts
@@ -0,0 +1,339 @@
+import {PredefinedColors} from '@ionic/core';
+import {isNil, isNotNil} from '../functions';
+
+export declare type ColorName = PredefinedColors |
+  'white'
+  | 'red'
+  | 'green'
+  | 'blue';
+
+
+/**
+ * Define here theme colors
+ */
+const rgbArrayMap = {
+  white: [255, 255, 255],
+  primary: [20, 67, 145], // ok
+  secondary: [117, 196, 253], // ok
+  tertiary: [91, 94, 244], // ok
+  danger: [245, 61, 61], // ok
+  light: [244, 245, 248], // ok
+  medium: [152, 154, 162], // ok
+  dark: [34, 36, 40], // ok
+  red: [255, 0, 0],
+  green: [0, 255, 0],
+  blue: [0, 0, 255]
+};
+
+// Fill a map of Color objects
+const colorsMap: { [key: string]: Color } = {};
+
+
+/**
+ * Useful class for color conversion
+ */
+// @dynamic
+export class Color {
+
+  // Helper method, to retrieve a color
+  static get(name: ColorName): Color {
+    return colorsMap[name] as Color;
+  }
+
+  static parseRgba(rgba: string): Color|null{
+      if (!rgba || (!rgba.startsWith('rgb(') && !rgba.startsWith('rgba('))) return null;
+
+      // Parse parts
+      const parts = rgba
+        .replace('rgb(', '')
+        .replace('rgba(', '')
+        .replace(')', '')
+        .split(',');
+
+      if (parts.length !== 3 && parts.length !== 4) return null;
+
+      return new Color([+parts[0], +parts[1], +parts[2]],
+        parts.length === 4 && +parts[3] || 1,
+        'custom');
+  }
+
+  static transparent = function() {
+    return new Color([0,0,0], 0, 'translucent');
+  };
+
+  constructor(
+    private _rgbArray: number[],
+    private _opacity?: number,
+    private _name: string = 'custom'
+  ) {
+  }
+  get name(): string {
+    return this._name;
+  }
+  get opacity(): number{
+    return isNotNil(this._opacity) ? this._opacity : 1;
+  }
+  get rgb(): number[] {
+    return this._rgbArray;
+  }
+
+  get r(): number {
+    return this._rgbArray[0];
+  }
+
+  get g(): number {
+    return this._rgbArray[1];
+  }
+
+  get b(): number {
+    return this._rgbArray[2];
+  }
+
+  rgba(opacity?: number): string {
+    opacity = isNotNil(opacity) ? opacity : this._opacity;
+    if (isNil(opacity) || opacity < 0 || opacity > 1) {
+      return 'rgb(' + this._rgbArray.join(',') + ')';
+    }
+    return 'rgba(' + this._rgbArray.join(',') + ',' + opacity + ')';
+  }
+
+}
+
+export declare interface ColorGradientOptions {
+  opacity?: number;
+  startColor?: number[];
+  mainColor?: number[];
+  mainColorIndex?: number;
+  endColor?: number[];
+  format?: 'rgb'|'hex'|'array';
+}
+
+export declare interface ColorScaleOptions extends ColorGradientOptions{
+  min?: number;
+  max?: number;
+  upperMax?: boolean;
+}
+
+export declare interface ColorScaleLegendItem {
+  label: string;
+  color: string;
+}
+
+export declare interface ColorScaleLegend {
+  items: ColorScaleLegendItem[];
+}
+
+/**
+ * Helper class for colors scale
+ */
+// @dynamic
+export class ColorScale {
+
+  static custom = (count: number, options?: ColorScaleOptions) => {
+    options = options || {};
+    return new ColorScale(
+      linearColorGradientWithIntermediate(count, {
+        opacity: options.opacity,
+        startColor: options.startColor || undefined,
+        mainColor: options.mainColor || undefined,
+        mainColorIndex: isNotNil(options.mainColorIndex) ? options.mainColorIndex : undefined,
+        endColor: options.endColor || undefined,
+        format: options.format
+      }) as string[],
+      options
+    );
+  };
+
+  static default() {
+    return ColorScale.custom(25);
+  }
+
+  /**
+   * Create a array with the given color
+   **/
+  static fix(length?: number, colorName?: ColorName): any[] {
+    return Array.apply(null, Array(length || 25))
+      .map(String.prototype.valueOf, Color.get(colorName || 'primary').rgba(0.5));
+  }
+
+  private _min: number;
+  private _max: number;
+  private _rangeSize: number;
+  private _legendItems: ColorScaleLegendItem[];
+
+  get min(): number {
+    return this._min;
+  }
+  get max(): number {
+    return this._max;
+  }
+
+  constructor(private colorArray: string[], options?: ColorScaleOptions) {
+    options = options || {};
+    // reserved last colors for value > max
+    const nbIntervalBeforeUpper = !options.upperMax ? colorArray.length : (colorArray.length - 1);
+    this._min = options.min || 0;
+    this._max = options.max || nbIntervalBeforeUpper;
+    this._rangeSize = Math.round((this._max - this._min) / nbIntervalBeforeUpper);
+    this._legendItems = this.computeLegend();
+  }
+
+  get legend(): ColorScaleLegend {
+    return {
+      items: this._legendItems
+    };
+  }
+
+  getValueColor(value: number): string {
+    const index = Math.floor(value * (this.colorArray.length - 1) / this._max);
+    return this.colorArray[index];
+  }
+
+  protected computeLegend(): ColorScaleLegendItem[] {
+    return this.colorArray.map((color, index) => {
+      const start = index * this._rangeSize;
+      const end = start + this._rangeSize;
+      return {
+        color,
+        label: (end < this._max) ? `${start.toLocaleString()} - ${end.toLocaleString()}` : ` >= ${start}`
+      };
+    });
+  }
+}
+
+
+// Fill colorsMap
+Object.getOwnPropertyNames(rgbArrayMap)
+  .forEach((key) => {
+    colorsMap[key] = new Color(rgbArrayMap[key], 1, key);
+  });
+
+// Internal function
+function state2side(state) {
+  switch (state) {
+    case 0:
+      return 0;
+    case 1:
+      return -1;
+    case 2:
+      return 0;
+    case 3:
+      return 1;
+  }
+}
+
+
+const SCALE_OPTIONS_DEFAULT = {
+  startColor: rgbArrayMap.red,
+  startStates: [0, 2, 3], // R=keep, V=keep, B=increase
+  startStepsFn: (defaultStateSize: number) => [
+      Math.round((rgbArrayMap.red[0] - 50) / defaultStateSize),
+      Math.round((255 - rgbArrayMap.red[1]) / defaultStateSize),
+      Math.round((255 - rgbArrayMap.red[2]) / defaultStateSize)
+    ]
+};
+
+/**
+ * Internal function, that create a colors scale, using iteration
+ *
+ * @param count
+ * @param opacity
+ * @param startColor
+ * @param startState
+ * @returns
+ */
+function linearColorGradientWithIntermediate(count: number,
+                                             options?: ColorGradientOptions): any[] {
+  options = options || {};
+
+  // From [0,1]
+  options.opacity = (options.opacity > 0 && options.opacity < 1) ? options.opacity : 1;
+  options.startColor = options.startColor || [255, 255, 190]; // default start = creme
+  options.mainColorIndex = options.mainColorIndex && options.mainColorIndex < count - 1 ? options.mainColorIndex : count - 1;
+  options.endColor = options.endColor || [255, 0, 0]; // default main = red
+  options.format = options.format || 'rgb';
+
+  if (!options.mainColor) {
+    return linearColorGradient(count, {
+      opacity: options.opacity,
+      startColor: options.startColor,
+      endColor: options.endColor,
+      format: options.format
+    });
+  }
+
+  else {
+    // Step 1: startColor -> mainColor
+    const result = linearColorGradient(options.mainColorIndex + 1, {
+      opacity: options.opacity,
+      startColor: options.startColor,
+      endColor: options.mainColor,
+      format: options.format
+    });
+
+    // Step 2: mainColor -> endColor
+    if (options.mainColorIndex < count - 1) {
+      return result.concat(
+        linearColorGradient(count - options.mainColorIndex, {
+          opacity: options.opacity,
+          startColor: options.mainColor,
+          endColor: options.endColor,
+          format: options.format
+        }));
+    }
+    else {
+      return result;
+    }
+  }
+}
+
+function linearColorGradient(count: number,
+                             options?: ColorGradientOptions): any[] {
+
+  options = options || {};
+
+  // From [0,1]
+  options.opacity = (options.opacity > 0 && options.opacity < 1) ? options.opacity : 1;
+  options.startColor = options.startColor || [255, 255, 255]; // default start = white
+  options.endColor = options.endColor || [255, 0, 0]; // default end = red
+  options.format = options.format || 'rgb';
+
+  const result: number[][] = [];
+  const color = options.startColor.slice(); // copy the start color
+  const delta = [
+    Math.round((options.endColor[0] - options.startColor[0]) / count),
+    Math.round((options.endColor[1] - options.startColor[1]) / count),
+    Math.round((options.endColor[2] - options.startColor[2]) / count)
+  ];
+
+  for (let i = 0; i < count - 1; i++) {
+    for (let j = 0; j < 3; j++) {
+      color[j] += delta[j];
+    }
+    result.push(color.slice());
+  }
+
+  // Force last color = end color
+  result.push(options.endColor.slice());
+
+  // Output as array
+  if (options.format === 'array') {
+    return result;
+  }
+
+  // Output as rgb(r,g,b) string
+  if (options.format === 'rgb') {
+    if (options.opacity >= 1) {
+      return result.map(color => 'rgb(' + color.join(',') + ')');
+    } else {
+      return result.map(color => 'rgba(' + color.concat(options.opacity).join(',') + ')');
+    }
+  }
+
+  // Output as hex
+  // TODO
+  // return result.map(color => {
+  //   return "rgb(" + color.join(',') + ")";
+  // });
+  return result;
+}
diff --git a/src/app/shared/currencies.ts b/src/app/shared/currencies.ts
index 4a8108a01335150fc036ad92176aee1d037cc475..95a20bcde18d869ba3af1f9232660351dc5045e6 100644
--- a/src/app/shared/currencies.ts
+++ b/src/app/shared/currencies.ts
@@ -27,7 +27,7 @@ export function abbreviate(currency: string): string {
   return currency;
 }
 
-export function formatAddress(value: string, withChecksum?: boolean ): string {
+export function formatAddress(value: string): string {
   if (!value) return '';
   if (value.length < 12) return '?';
   return value.substring(0,6) + '\u2026' + value.substring(value.length - 6);
diff --git a/src/app/shared/pages/base.page.ts b/src/app/shared/pages/base.page.ts
index 1c1b7858580bd6c43dad73cdb1e8708d7bbbecf2..0b1c3f4ceea9c340c54243acec704202038ce033 100644
--- a/src/app/shared/pages/base.page.ts
+++ b/src/app/shared/pages/base.page.ts
@@ -5,7 +5,7 @@ import {changeCaseToUnderscore, isNotNilOrBlank} from "@app/shared/functions";
 import {environment} from "@environments/environment";
 import {waitIdle} from "@app/shared/forms";
 import {WaitForOptions} from "@app/shared/observables";
-import {ToastController, ToastOptions} from "@ionic/angular";
+import {IonRouterOutlet, ToastController, ToastOptions} from "@ionic/angular";
 import {TranslateService} from "@ngx-translate/core";
 import {Subscription} from "rxjs";
 
@@ -26,6 +26,7 @@ export abstract class BasePage<
 
   protected translate: TranslateService;
   protected settings: SettingsService;
+  protected readonly routerOutlet: IonRouterOutlet;
   protected readonly activatedRoute: ActivatedRoute;
   protected toastController: ToastController;
   protected readonly _debug = !environment.production;
@@ -49,6 +50,7 @@ export abstract class BasePage<
     this._cd = injector.get(ChangeDetectorRef);
     this.settings = injector.get(SettingsService);
     this.translate = injector.get(TranslateService);
+    this.routerOutlet = injector.get(IonRouterOutlet);
     this.activatedRoute = injector.get(ActivatedRoute);
     this.toastController = injector.get(ToastController);
     this.mobile = this.settings.mobile;
@@ -167,4 +169,5 @@ export abstract class BasePage<
   protected unregisterSubscription(sub: Subscription) {
     this._subscription?.remove(sub);
   }
+
 }
diff --git a/src/app/shared/pipes/account.pipes.ts b/src/app/shared/pipes/account.pipes.ts
index a6b2591c1753d13df726cd73daf40d62d21f6a45..9e45744f2e9b91d79a02b587c9b3c1b8b8248b5f 100644
--- a/src/app/shared/pipes/account.pipes.ts
+++ b/src/app/shared/pipes/account.pipes.ts
@@ -1,34 +1,126 @@
-import {Pipe, PipeTransform} from '@angular/core';
+import {ChangeDetectorRef, Directive, Pipe, PipeTransform} from '@angular/core';
 import {NumberFormatPipe} from "@app/shared/pipes/number-format.pipe";
 import {NetworkService} from "@app/network/network.service";
-import {Account, AccountUtils} from "@app/wallet/account.model";
-import {isNotNilOrBlank} from "@app/shared/functions";
+import {Account, AccountData, AccountUtils} from "@app/wallet/account.model";
+import {equals, getPropertyByPath, isNotNilOrBlank} from "@app/shared/functions";
 import {AddressFormatPipe} from "@app/shared/pipes/address.pipes";
+import {Subscription} from "rxjs";
+import {formatAddress} from "@app/shared/currencies";
+
+// @dynamic
+/**
+ * A common pipe, that will subscribe to all account changes, to refresh its value
+ */
+export abstract class AccountAbstractPipe<T = any, O = any> implements PipeTransform {
+
+  private value: T = null;
+  private _lastAccount: Partial<Account> | null = null;
+  private _lastOptions: O = null;
+  private _changesSubscription: Subscription = null;
+
+  protected constructor(private _ref: ChangeDetectorRef) {
+  }
+
+  transform(account: Partial<Account>, opts: O): T {
+    if (!account || (!account.data && !account.dataSubject)) {
+      this._dispose();
+      return undefined;
+    }
+
+    // if we ask another time for the same account and opts, return the last value
+    if (account === this._lastAccount && equals(opts, this._lastOptions)) {
+      return this.value;
+    }
+
+    // store the query, in case it changes
+    this._lastAccount = account;
+
+    // store the params, in case they change
+    this._lastOptions = opts;
+
+    // set the value
+    this._updateValue(account, opts);
+
+    // if there is a subscription to onLangChange, clean it
+    this._dispose();
+
+    // subscribe to onTranslationChange event, in case the translations change
+    if (!this._changesSubscription && account.dataSubject) {
+      this._changesSubscription = account.dataSubject.subscribe((status) => {
+        this.value = this._transform(account, opts);
+        this._ref.markForCheck();
+      });
+    }
+    return this.value;
+  }
+
+  ngOnDestroy(): void {
+    this._dispose();
+  }
+
+  private _updateValue(account: Partial<Account>, opts?: O) {
+    this.value = this._transform(account, opts);
+    this._ref.markForCheck();
+  }
+
+  protected abstract _transform(account: Partial<Account>, opts?: O): T;
+
+  /**
+   * Clean any existing subscription to change events
+   */
+  private _dispose(): void {
+    this._changesSubscription?.unsubscribe();
+    this._changesSubscription = undefined;
+  }
+}
+
+export declare type AccountPropertyPipeOptions<T> = string | {key?: string; defaultValue?: T};
 
 @Pipe({
-  name: 'balance'
+  name: 'accountProperty',
+  pure: false
 })
-export class AccountBalancePipe implements PipeTransform {
+export class AccountPropertyPipe<T = any, O extends AccountPropertyPipeOptions<T> = AccountPropertyPipeOptions<T>> extends AccountAbstractPipe<T, O> {
+
+  constructor(_ref: ChangeDetectorRef) {
+    super(_ref);
+  }
 
-  delegate = new NumberFormatPipe();
+  protected _transform(account: Partial<Account>, opts?: O): T {
+    return getPropertyByPath(account,
+      // Path
+      opts && (typeof opts === 'string' ? opts : opts.key),
+      // Default value
+      opts && (opts as any).defaultValue);
+  }
+}
 
-  constructor(private networkService: NetworkService) {
+@Pipe({
+  name: 'balance',
+  pure: false
+})
+export class AccountBalancePipe extends AccountAbstractPipe<number, void> {
+
+  constructor(_ref: ChangeDetectorRef) {
+    super(_ref);
   }
 
-  transform(account: Partial<Account>, opts?: Intl.NumberFormatOptions & {fixedDecimals?: number}): number | undefined {
-    if (!account?.data) return undefined;
+  protected _transform(account: Partial<Account>): number {
     return AccountUtils.getBalance(account);
   }
 }
 
 @Pipe({
-  name: 'accountName'
+  name: 'accountName',
+  pure: false
 })
-export class AccountNamePipe implements PipeTransform {
+export class AccountNamePipe extends AccountAbstractPipe<string, void> {
 
-  private addressFormatter = new AddressFormatPipe();
+  constructor(_ref: ChangeDetectorRef) {
+    super(_ref);
+  }
 
-  transform(account: Partial<Account>): string {
-    return account?.meta?.name || this.addressFormatter.transform(account?.address, true);
+  protected _transform(account: Partial<Account>): string {
+    return account?.meta?.name || formatAddress(account?.address);
   }
 }
diff --git a/src/app/shared/pipes/address.pipes.ts b/src/app/shared/pipes/address.pipes.ts
index 4e64eeef080c06619780663dca00c21346db579c..a1de75333f9d3a09ae963dce2630e8360c0fbe2d 100644
--- a/src/app/shared/pipes/address.pipes.ts
+++ b/src/app/shared/pipes/address.pipes.ts
@@ -6,7 +6,7 @@ import {formatAddress} from "@app/shared/currencies";
 })
 export class AddressFormatPipe implements PipeTransform {
 
-  transform(value: string, withChecksum?: boolean ): string {
-    return formatAddress(value, withChecksum);
+  transform(value: string): string {
+    return formatAddress(value);
   }
 }
diff --git a/src/app/shared/pipes/pipes.module.ts b/src/app/shared/pipes/pipes.module.ts
index 81e39fac7105921f2988ab1f3097112cd75c2843..9d28b2abc4d62ae406db49d1cd5ff3e2b95d0596 100644
--- a/src/app/shared/pipes/pipes.module.ts
+++ b/src/app/shared/pipes/pipes.module.ts
@@ -34,7 +34,7 @@ import {FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetPipe, For
 import {PropertyGetPipe} from './property.pipes';
 import {AmountFormatPipe} from "@app/shared/pipes/amount.pipe";
 import {AddressFormatPipe} from "@app/shared/pipes/address.pipes";
-import {AccountBalancePipe, AccountNamePipe} from "@app/shared/pipes/account.pipes";
+import {AccountBalancePipe, AccountNamePipe, AccountPropertyPipe} from "@app/shared/pipes/account.pipes";
 
 @NgModule({
   imports: [
@@ -81,6 +81,7 @@ import {AccountBalancePipe, AccountNamePipe} from "@app/shared/pipes/account.pip
     AddressFormatPipe,
     AbbreviatePipe,
     // Account pipes
+    AccountPropertyPipe,
     AccountBalancePipe,
     AccountNamePipe
   ],
@@ -123,6 +124,7 @@ import {AccountBalancePipe, AccountNamePipe} from "@app/shared/pipes/account.pip
     AddressFormatPipe,
     AbbreviatePipe,
     // Account pipes
+    AccountPropertyPipe,
     AccountBalancePipe,
     AccountNamePipe
   ]
diff --git a/src/app/shared/popover/list.popover.html b/src/app/shared/popover/list.popover.html
new file mode 100644
index 0000000000000000000000000000000000000000..1cfcef6f7773016639950b19da0f9b9fc4c85a5c
--- /dev/null
+++ b/src/app/shared/popover/list.popover.html
@@ -0,0 +1,11 @@
+<ion-content>
+  <ion-list>
+    <ion-list-header *ngIf="title">{{title|translate}}</ion-list-header>
+    <ion-item *ngFor="let item of items"
+              [disabled]="item.disabled"
+              (click)="click(item.value)"
+              tappable>
+      {{item.label|translate}}
+    </ion-item>
+  </ion-list>
+</ion-content>
diff --git a/src/app/shared/popover/list.popover.module.ts b/src/app/shared/popover/list.popover.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9aac0ec264ac389b5788e98f10df54985c9d35e4
--- /dev/null
+++ b/src/app/shared/popover/list.popover.module.ts
@@ -0,0 +1,22 @@
+import {NgModule} from '@angular/core';
+import {CommonModule} from '@angular/common';
+
+import {IonicModule} from '@ionic/angular';
+import {TranslateModule} from "@ngx-translate/core";
+import {ListPopover} from "./list.popover";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    IonicModule,
+    TranslateModule
+  ],
+  declarations: [
+    ListPopover
+  ],
+  exports: [
+    ListPopover
+  ]
+
+})
+export class ListPopoverModule {}
diff --git a/src/app/shared/popover/list.popover.scss b/src/app/shared/popover/list.popover.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/popover/list.popover.ts b/src/app/shared/popover/list.popover.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9367becf37cb9c3768b27c1c048074ca2d853353
--- /dev/null
+++ b/src/app/shared/popover/list.popover.ts
@@ -0,0 +1,32 @@
+import {Component, Input} from "@angular/core";
+import {PopoverController} from "@ionic/angular";
+
+export interface ListItem {
+  value: string;
+  label: string;
+  disabled?: boolean;
+}
+
+export interface ListPopoverOptions {
+  title?: string;
+  items: ListItem[];
+}
+
+@Component({
+  selector: 'app-list-popover',
+  templateUrl: './list.popover.html',
+  styleUrls: ['./list.popover.scss']
+})
+export class ListPopover {
+
+  @Input() title: string = null;
+  @Input() items: ListItem[] = null;
+
+  constructor(protected popoverCtrl: PopoverController) {
+
+  }
+
+  click(value: string){
+    this.popoverCtrl.dismiss(value);
+  }
+}
diff --git a/src/app/shared/services/platform.service.ts b/src/app/shared/services/platform.service.ts
index 4d38cf0bef6591d1a6142dc888f54955987be2a3..3298fca7e6695f60113f8d191275c6969a9e7552 100644
--- a/src/app/shared/services/platform.service.ts
+++ b/src/app/shared/services/platform.service.ts
@@ -7,8 +7,9 @@ import {StorageService} from "@app/shared/services/storage/storage.service";
 import {environment} from "@environments/environment.prod";
 import {TranslateService} from "@ngx-translate/core";
 import * as momentImported from 'moment';
-import {Subject} from "rxjs";
-import {Settings} from "@app/settings/settings.model";
+import {StatusBar} from "@capacitor/status-bar";
+import {Keyboard} from "@capacitor/keyboard";
+
 const moment = momentImported;
 
 @Injectable({
@@ -18,7 +19,8 @@ export class PlatformService extends StartableService {
 
   private _mobile: boolean = null;
   private _touchUi: boolean = null;
-
+  private _cordova: boolean = null;
+  private _capacitor: boolean = null;
 
   get mobile(): boolean {
     return this._mobile != null ? this._mobile : this.ionicPlatform.is('mobile');
@@ -29,6 +31,14 @@ export class PlatformService extends StartableService {
       (this.mobile || this.ionicPlatform.is('tablet') || this.ionicPlatform.is('phablet'));
   }
 
+  get capacitor(): boolean {
+    return this._capacitor != null ? this._capacitor : this.ionicPlatform.is('capacitor');
+  }
+
+  get cordova(): boolean {
+    return this._cordova != null ? this._cordova : this.ionicPlatform.is('cordova');
+  }
+
   constructor(
     protected ionicPlatform: Platform,
     protected translate: TranslateService,
@@ -45,6 +55,11 @@ export class PlatformService extends StartableService {
 
     this._mobile = this.mobile;
     this._touchUi = this.touchUi;
+    this._cordova = this.cordova;
+    this._capacitor = this.capacitor;
+
+    // Configure Capacitor plugins
+    await this.configureCapacitorPlugins();
 
     // Configure translation
     await this.configureTranslate();
@@ -56,6 +71,24 @@ export class PlatformService extends StartableService {
     ]);
   }
 
+  protected async configureCapacitorPlugins() {
+    if (!this._capacitor) return; // Skip
+
+    console.info('[platform] Configuring Cordova plugins...');
+
+    let plugin: string;
+    try {
+      plugin = 'StatusBar';
+      await StatusBar.setOverlaysWebView({overlay: false});
+
+      plugin = 'Keyboard';
+      await Keyboard.setAccessoryBarVisible({isVisible: false});
+    }
+    catch(err) {
+      console.error(`[platform] Error while configuring ${plugin} plugin: ${err?.originalStack || JSON.stringify(err)}`);
+    }
+  }
+
 
   protected configureTranslate() {
     console.info('[platform] Configuring i18n ...');
diff --git a/src/app/shared/services/plugins.ts b/src/app/shared/services/plugins.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c9c76b2d5d35a002aff8af8de5c76aae62e41c58
--- /dev/null
+++ b/src/app/shared/services/plugins.ts
@@ -0,0 +1,4 @@
+export const CapacitorPlugins = Object.freeze({
+  Camera: 'Camera',
+  BarcodeScanner: 'BarcodeScanner'
+});
diff --git a/src/app/shared/services/storage/storage.service.ts b/src/app/shared/services/storage/storage.service.ts
index f2f043844f31cd3a9d31bd54d89f7f71c1b0bfad..ef2608bf6675a05c68e4fe29fe230274d548aa14 100644
--- a/src/app/shared/services/storage/storage.service.ts
+++ b/src/app/shared/services/storage/storage.service.ts
@@ -1,12 +1,12 @@
-import {Injectable} from '@angular/core';
+import {ENVIRONMENT_INITIALIZER, Injectable} from '@angular/core';
 import {Storage} from '@ionic/storage-angular';
 import {StartableService} from "@app/shared/services/startable-service.class";
 import {IStorage} from "@app/shared/services/storage/storage.utils";
 import {Platform} from '@ionic/angular';
+import {environment} from "@environments/environment";
+import cordovaSQLiteDriver from 'localforage-cordovasqlitedriver';
 
-@Injectable({
-  providedIn: 'root'
-})
+@Injectable({providedIn: 'root'})
 export class StorageService extends StartableService<Storage>
   implements IStorage<Storage> {
 
@@ -21,10 +21,21 @@ export class StorageService extends StartableService<Storage>
   }
 
   protected async ngOnStart(): Promise<Storage> {
-    await this.platform.ready();
-    const storage = await this.storage.create();
-    //console.info(`[storage-service] Started using driver=${storage?.driver}`);
-    return storage;
+    try {
+      console.debug(`[storage-service] Starting... {driverOrder: ${environment.storage?.driverOrder}}`);
+
+      // Define Cordova SQLLite driver
+      await this.storage.defineDriver(cordovaSQLiteDriver);
+
+      // Create the storage instance
+      const storage = await this.storage.create();
+
+      console.info(`[storage-service] Started using driver: ${storage?.driver}`);
+      return storage;
+    }
+    catch (err) {
+      console.error('[storage-service] Cannot create storage: ' + (err?.message || err), err);
+    }
   }
 
   async set(key: string, value: any) {
diff --git a/src/app/shared/services/storage/storage.utils.ts b/src/app/shared/services/storage/storage.utils.ts
index 085ab55faa48f830976ca1bcdc9187128bf9a14c..749ae87852ac77d7f16647bb10400dc6bd2639c9 100644
--- a/src/app/shared/services/storage/storage.utils.ts
+++ b/src/app/shared/services/storage/storage.utils.ts
@@ -2,6 +2,7 @@ import {InjectionToken} from "@angular/core";
 import {Drivers} from "@ionic/storage";
 import * as LocalForage from "localforage";
 
+import * as CordovaSQLiteDriver from 'localforage-cordovasqlitedriver';
 
 export interface IStorage<T = any> {
   readonly driver: string;
@@ -15,7 +16,7 @@ export interface IStorage<T = any> {
 }
 
 export const StorageDrivers = {
-  //SQLLite: CordovaSQLiteDriver._driver,
+  SQLLite: CordovaSQLiteDriver._driver,
   SecureStorage: Drivers.SecureStorage,
   WebSQL: LocalForage.WEBSQL,
   IndexedDB: Drivers.IndexedDB,
diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts
index 9043ba7f671e07ace2ad75627a4c051effec9299..06bc3af22dafaed6aa0188e517c06bed154c3404 100644
--- a/src/app/shared/shared.module.ts
+++ b/src/app/shared/shared.module.ts
@@ -5,8 +5,8 @@ import {FormsModule, ReactiveFormsModule} from '@angular/forms';
 import {IonicModule} from '@ionic/angular';
 import {TranslateModule} from "@ngx-translate/core";
 import {SharedPipesModule} from "@app/shared/pipes/pipes.module";
-import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
 import {QRCodeModule} from "angular2-qrcode";
+import {ListPopoverModule} from "@app/shared/popover/list.popover.module";
 
 @NgModule({
   imports: [
@@ -17,8 +17,9 @@ import {QRCodeModule} from "angular2-qrcode";
     TranslateModule,
     QRCodeModule,
 
-    // App modules
-    SharedPipesModule
+    // Sub modules
+    SharedPipesModule,
+    ListPopoverModule
   ],
   exports: [
     CommonModule,
@@ -28,8 +29,9 @@ import {QRCodeModule} from "angular2-qrcode";
     TranslateModule,
     QRCodeModule,
 
-    // App modules
-    SharedPipesModule
+    // Sub modules
+    SharedPipesModule,
+    ListPopoverModule
   ]
 })
 export class AppSharedModule {}
diff --git a/src/app/transfer/transfer.page.html b/src/app/transfer/transfer.page.html
index a9fdbfeae1989e340301a8a914d5d4f7cef841f0..ec60681b2d4353370a2262c5cec6d6446c17bc97 100644
--- a/src/app/transfer/transfer.page.html
+++ b/src/app/transfer/transfer.page.html
@@ -26,19 +26,22 @@
       <!-- TO -->
       <ion-item>
         <ion-label color="medium" translate>TRANSFER.TO</ion-label>
-        <ion-textarea *ngIf="data|async|isNotEmptyArray; else inputSkeleton"
+        <ion-textarea *ngIf="data|async|isNotEmptyArray; else inputSkeleton" #fromInput
                    [rows]="mobile ? 2 : 1"
-                   class="ion-text-wrap ion-text-end"
-                   [(ngModel)]="recipient.address" (ionFocus)="wotModal.present()">
+                   class="ion-text-wrap ion-padding-start"
+                   [(ngModel)]="recipient.address"
+                   (ionFocus)="focusFrom($event, fromInput)">
         </ion-textarea>
-        <ion-button slot="end" fill="clear" id="open-modal-trigger"
-                    [class.cdk-visually-hidden]="loading || mobile"
+        <ion-button slot="end" fill="clear"
+                    (click)="showWotModal($event, 0.75)"
                     [disabled]="loading"
                     [title]="'COMMON.BTN_SEARCH'|translate">
           <ion-icon slot="icon-only" name="search"></ion-icon>
         </ion-button>
+
+        <!-- Scan QR code-->
         <ion-button slot="end" fill="clear" color="dark"
-                    *ngIf="_capacitor"
+                    *ngIf="_enableScan"
                     (click)="scanRecipient($event)"
                     [disabled]="loading">
           <ion-icon slot="icon-only" name="qr-code"></ion-icon>
@@ -107,20 +110,22 @@
 
   <ion-modal
     #wotModal
-    trigger="open-modal-trigger"
-    [initialBreakpoint]="0.25"
+    [initialBreakpoint]="_initialWotModalBreakpoint"
     [breakpoints]="[0.25, 0.5, 0.75]"
-    [backdropDismiss]="false"
+    [backdropDismiss]="true"
     [backdropBreakpoint]="0.5"
-    [keepContentsMounted]="true"
   >
     <ng-template>
       <app-wot-lookup
-        [showToolbar]="false"
+        showToolbar="true"
+        toolbarColor="secondary"
         [showSearchBar]="true"
         [showItemActions]="false"
-        (itemClick)="setRecipient($event) || wotModal.dismiss()"
+        (itemClick)="setRecipient($event) && hideWotModal()"
         (searchClick)="wotModal.setCurrentBreakpoint(0.75)">
+
+        <ion-button toolbar-end fill="clear" (click)="hideWotModal($event)" translate>COMMON.BTN_CLOSE</ion-button>
+
       </app-wot-lookup>
     </ng-template>
   </ion-modal>
diff --git a/src/app/transfer/transfer.page.ts b/src/app/transfer/transfer.page.ts
index 15e151ed2ed402a6b4cbfe61089b9ddb79fb0a09..c685257c6716f4a0e38fe5c1258df1d4204c4df7 100644
--- a/src/app/transfer/transfer.page.ts
+++ b/src/app/transfer/transfer.page.ts
@@ -10,7 +10,7 @@ import {
 import {AccountService} from "../wallet/account.service";
 import {BasePage} from "@app/shared/pages/base.page";
 import {Account} from "@app/wallet/account.model";
-import {ActionSheetOptions, IonModal, Platform, PopoverOptions} from "@ionic/angular";
+import {ActionSheetOptions, IonModal, IonRouterOutlet, IonTextarea, Platform, PopoverOptions} from "@ionic/angular";
 import {BehaviorSubject, firstValueFrom, Observable} from "rxjs";
 import {isNotEmptyArray, isNotNilOrBlank} from "@app/shared/functions";
 import {filter} from "rxjs/operators";
@@ -20,6 +20,8 @@ import {Currency} from "@app/network/currency.model";
 import {Router} from "@angular/router";
 import {BarcodeScanner} from "@capacitor-community/barcode-scanner";
 import {BarcodeScannerWeb} from "@capacitor-community/barcode-scanner/dist/esm/web";
+import {Capacitor} from "@capacitor/core";
+import {CapacitorPlugins} from "@app/shared/services/plugins";
 
 @Component({
   selector: 'app-transfer',
@@ -34,7 +36,7 @@ export class TransferPage extends BasePage<Observable<Account[]>> implements OnI
   recipient: Account = {address: null, meta: null};
   amount: number;
   fee: number;
-  protected _capacitor: boolean;
+  protected _enableScan: boolean = false;
 
   protected actionSheetOptions: Partial<ActionSheetOptions> = {
     cssClass: 'select-account-action-sheet'
@@ -79,22 +81,16 @@ export class TransferPage extends BasePage<Observable<Account[]>> implements OnI
     protected cd: ChangeDetectorRef
   ) {
     super(injector, {name: 'transfer', loadDueTime: 250});
-
   }
 
   ngOnInit() {
     super.ngOnInit();
+  }
+
+  ionViewWillLeave() {
 
     // Hide modal when leave page
-    this.registerSubscription(
-      this.router.events
-        .pipe(filter(
-          (value, index) => {
-            console.log(value);
-            return true;
-          }
-        )).subscribe()
-    )
+    this.hideWotModal();
   }
 
   async ngOnDestroy() {
@@ -103,14 +99,62 @@ export class TransferPage extends BasePage<Observable<Account[]>> implements OnI
     await this.qrCodeModal?.dismiss();
   }
 
+  protected _autoOpenWotModal = true;
+
+  protected async focusFrom(event: UIEvent, textarea?: IonTextarea) {
+
+    if (this._autoOpenWotModal) {
+      await this.showWotModal(event);
+
+      if (textarea) {
+        const el = await textarea.getInputElement();
+        setTimeout( () => el.focus(), 250);
+      }
+    }
+
+  }
+
+  protected _initialWotModalBreakpoint = 0.25
+
+  protected async showWotModal(event: UIEvent, breakpoint?: number) {
+    breakpoint = breakpoint || 0.25;
+
+    this._initialWotModalBreakpoint = breakpoint;
+
+    if (!this.wotModal.isCmpOpen) {
+      await this.wotModal.present();
+    }
+
+    // Set breakpoint
+    if (breakpoint > 0.25){
+      const currentBreakpoint = await this.wotModal.getCurrentBreakpoint();
+      if (breakpoint > currentBreakpoint) {
+        await this.wotModal.setCurrentBreakpoint(breakpoint);
+      }
+    }
+  }
+
+  protected hideWotModal(event?: UIEvent) {
+    if (this.wotModal && this.wotModal.isCmpOpen) {
+      this.wotModal.dismiss();
+      this._autoOpenWotModal = false;
+    }
+  }
+
   protected async ngOnLoad(): Promise<Observable<Account[]>> {
+
+    this._enableScan = this.ionicPlatform.is('capacitor') && Capacitor.isPluginAvailable(CapacitorPlugins.BarcodeScanner);
+
     await this.accountService.ready();
 
     const subject = new BehaviorSubject<Account[]>(null);
     this.registerSubscription(
       this.accountService.watchAll({positiveBalanceFirst: true})
         .pipe(filter(isNotEmptyArray))
-        .subscribe((value) => subject.next(value))
+        .subscribe((value) => {
+          subject.next(value);
+          if (this.loaded) this.cd.markForCheck();
+        })
     );
 
     const accounts = await firstValueFrom(subject);
@@ -133,12 +177,11 @@ export class TransferPage extends BasePage<Observable<Account[]>> implements OnI
 
     this.fee = (this.networkService.currency?.fees.tx || 0) / Math.pow(10, this.networkService.currency?.decimals || 0);
 
-    this._capacitor = this.ionicPlatform.is('capacitor');
 
     return subject;
   }
 
-  setRecipient(recipient: string|Account) {
+  setRecipient(recipient: string|Account): boolean {
     if (typeof recipient === 'object') {
       this.recipient = recipient;
     }
@@ -146,6 +189,7 @@ export class TransferPage extends BasePage<Observable<Account[]>> implements OnI
       this.recipient = {address: recipient, meta: null};
     }
     this.markForCheck();
+    return true;
   }
 
   cancel(event?: UIEvent) {
@@ -165,6 +209,10 @@ export class TransferPage extends BasePage<Observable<Account[]>> implements OnI
       await this.showToast({message: 'INFO.TRANSFER_SENT'});
 
       this.reset();
+
+      if (this.routerOutlet.canGoBack()) {
+        await this.routerOutlet.pop();
+      }
     }
     catch (err) {
       this.setError(err);
@@ -173,7 +221,7 @@ export class TransferPage extends BasePage<Observable<Account[]>> implements OnI
   }
 
   async scanRecipient(event: UIEvent) {
-    if (!this._capacitor) return; // SKip
+    if (!this._enableScan) return; // SKip
 
     await BarcodeScanner.hideBackground(); // make background of WebView transparent
 
diff --git a/src/app/wallet/account.model.ts b/src/app/wallet/account.model.ts
index cb89529c8b0aa39a1a199ecef5841a4564bc604d..682d6d38e3f9a4959a921e8921329203972a560f 100644
--- a/src/app/wallet/account.model.ts
+++ b/src/app/wallet/account.model.ts
@@ -1,4 +1,5 @@
 import {KeypairType} from "@polkadot/util-crypto/types";
+import {Subject} from "rxjs";
 
 export interface Account {
   address: string;
@@ -8,6 +9,7 @@ export interface Account {
   meta: AccountMeta;
 
   data?: AccountData;
+  dataSubject?: Subject<AccountData>;
 }
 export interface AccountMeta {
   name: string;
diff --git a/src/app/wallet/account.service.ts b/src/app/wallet/account.service.ts
index 3871f7a5b3bcf6ae99398c62b9a11869a495670e..8a056e8e080e9cb4ed2a58a26f17c834358d8d84 100644
--- a/src/app/wallet/account.service.ts
+++ b/src/app/wallet/account.service.ts
@@ -1,7 +1,7 @@
 import {Inject, Injectable} from "@angular/core";
 import {NetworkService} from "../network/network.service";
 import {ApiPromise} from "@polkadot/api";
-import {Account, AccountMeta, AccountUtils} from "./account.model";
+import {Account, AccountData, AccountMeta, AccountUtils} from "./account.model";
 import {StartableService} from "@app/shared/services/startable-service.class";
 import {AuthData} from "@app/auth/auth.model";
 import {keyring} from "@polkadot/ui-keyring";
@@ -26,7 +26,7 @@ import {
   firstValueFrom,
   from,
   map,
-  Observable,
+  Observable, Subject,
   Subscription,
   switchMap,
   timer
@@ -132,7 +132,8 @@ export class AccountService extends StartableService {
           meta: {
             name: ka.meta.name,
             genesisHash: ka.meta.genesisHash
-          }
+          },
+          dataSubject: new Subject<AccountData>()
         }
       });
 
@@ -492,7 +493,7 @@ export class AccountService extends StartableService {
 
     try {
       const now = Date.now();
-      let loaded = false;
+      let changed = false;
 
       // Load balance (free + reserved)
       if (opts.withBalance === true && (isNil(account.data?.free) || opts.reload === true)) {
@@ -502,7 +503,7 @@ export class AccountService extends StartableService {
           ...account.data,
           ...JSON.parse(data.toString())
         };
-        loaded = true;
+        changed = true;
       }
 
       // Load TX
@@ -513,7 +514,10 @@ export class AccountService extends StartableService {
         //somethingLoaded = true;
       }
 
-      if (loaded) {
+      // Emit change event
+      if (changed) {
+        account.dataSubject = account.dataSubject || new Subject();
+        account.dataSubject.next(account.data);
         console.debug(`${this._logPrefix} Loading ${formatAddress(account.address)} data [OK] in ${Date.now()-now}ms`, account.data);
       }
 
diff --git a/src/app/wallet/wallet.page.html b/src/app/wallet/wallet.page.html
index 7e2f43d829f769df6ce7e4cefa52a090d96d5eeb..024a1a11146013a33b34799dc22db36d579a594e 100644
--- a/src/app/wallet/wallet.page.html
+++ b/src/app/wallet/wallet.page.html
@@ -4,10 +4,9 @@
       <ion-menu-button></ion-menu-button>
     </ion-buttons>
     <ion-title translate>ACCOUNT.TITLE</ion-title>
-
-    <ng-container *ngIf="$account|async; let accounts">
-      <ion-select slot="end"
-                  *ngIf="accounts|isNotEmptyArray"
+    <ion-buttons slot="end">
+      <ion-select *ngIf="$account|async; let accounts"
+                  [class.cdk-visually-hidden]="accounts|isEmptyArray"
                   [(ngModel)]="data"
                   [interface]="mobile ? 'action-sheet' : 'popover'"
                   [okText]="'COMMON.BTN_OK'|translate"
@@ -16,8 +15,13 @@
                            [value]="account">
           {{account|accountName}}
         </ion-select-option>
+
+        <ion-select-option (click)="addNewWallet($event)"
+                           [value]="null"
+                           translate>ACCOUNT.WALLET_LIST.BTN_NEW</ion-select-option>
       </ion-select>
-    </ng-container>
+    </ion-buttons>
+
   </ion-toolbar>
   <ion-progress-bar type="indeterminate" *ngIf="loading"></ion-progress-bar>
 </ion-header>
@@ -31,9 +35,9 @@
           <ion-icon slot="icon-only" name="qr-code"></ion-icon>
         </ion-button>
       </ion-buttons>
-      <ion-label slot="end" *ngIf="loaded" class="ion-text-end ion-margin-end">
-        <b>{{ balance | amountFormat }}</b>
-      </ion-label>
+      <ion-title slot="end" *ngIf="loaded" class="balance">
+        {{ data|balance|amountFormat }}
+      </ion-title>
     </ion-toolbar>
   </ion-header>
 
diff --git a/src/app/wallet/wallet.page.scss b/src/app/wallet/wallet.page.scss
index 8b137891791fe96927ad78e64b0aad7bded08bdc..e91c444f25c3053d3258bd164187683305806d98 100644
--- a/src/app/wallet/wallet.page.scss
+++ b/src/app/wallet/wallet.page.scss
@@ -1 +1,6 @@
 
+ion-toolbar {
+  ion-buttons[slot="end"] {
+    padding-inline-end: var(--ion-padding);
+  }
+}
diff --git a/src/app/wallet/wallet.page.ts b/src/app/wallet/wallet.page.ts
index e6de42b2ba154651ab62131f59b39279a690a134..a71b014168f10deca589c33949fa0833e0f1c587 100644
--- a/src/app/wallet/wallet.page.ts
+++ b/src/app/wallet/wallet.page.ts
@@ -22,21 +22,11 @@ export class WalletPage extends BasePage<Account> implements OnInit, AfterViewCh
 
   $account = new BehaviorSubject<Account[]>(null);
 
-  get loaded(): boolean {
-    return !this.loading;
-  }
-
-  get balance(): number {
-    if (!this.data?.data) return undefined;
-    return (this.data.data.free || 0) + (this.data.data.reserved || 0);
-  }
-
   get account(): Account {
     return this.data;
   }
 
   @ViewChild('authModal') authModal: IonModal;
-
   @ViewChild('qrCodeModal') qrCodeModal: IonModal;
 
   constructor(
@@ -117,6 +107,10 @@ export class WalletPage extends BasePage<Account> implements OnInit, AfterViewCh
     this.qrCodeModal.present();
   }
 
+  addNewWallet(event: UIEvent) {
+
+  }
+
   async openAuthModal(): Promise<Account|null> {
     if (!this.authModal.isOpen) {
       await this.authModal.present();
diff --git a/src/app/wot/wot-details.page.html b/src/app/wot/wot-details.page.html
index 153114a1465f32f29d75b4b8488fcde8273a78ae..4c61f824af81db0c4a492e672abc8b10eb51ddc2 100644
--- a/src/app/wot/wot-details.page.html
+++ b/src/app/wot/wot-details.page.html
@@ -22,7 +22,7 @@
         <ion-avatar slot="start" *ngIf="data?.meta?.avatar">
           <ion-img [src]="data.meta.avatar"></ion-img>
         </ion-avatar>
-        <ion-title size="large" *ngIf="loaded; else loadingText">
+        <ion-title size="large" *ngIf="loaded; else loadingText" class="balance">
           {{ data|balance|amountFormat }}
         </ion-title>
         <ng-template #loadingText>
diff --git a/src/app/wot/wot-lookup.page.html b/src/app/wot/wot-lookup.page.html
index f17c45f88d9dd0db80f536cbd84d62582476101e..985d3071dfbeb009c163d776a438f1ba23b6446b 100644
--- a/src/app/wot/wot-lookup.page.html
+++ b/src/app/wot/wot-lookup.page.html
@@ -1,13 +1,16 @@
 <ion-header [translucent]="true" *ngIf="showToolbar">
-  <ion-toolbar color="primary">
+  <ion-toolbar [color]="toolbarColor">
     <ion-buttons slot="start">
       <ion-menu-button></ion-menu-button>
     </ion-buttons>
     <ion-title translate>MENU.WOT</ion-title>
+    <ion-buttons slot="end">
+      <ng-content select="[toolbar-end]"></ng-content>
+    </ion-buttons>
   </ion-toolbar>
 </ion-header>
 
-<ion-content [fullscreen]="true">
+<ion-content [fullscreen]="showSearchBar">
   <ion-header collapse="condense" *ngIf="showToolbar">
     <ion-toolbar>
       <ion-title size="large" translate>MENU.WOT</ion-title>
diff --git a/src/app/wot/wot-lookup.page.ts b/src/app/wot/wot-lookup.page.ts
index 1304a9c8a9fdd942c8f7739c4045ccd9aa6f8829..0c02edcbb1fd2fd7bb2dab5fd777ccc505647b5d 100644
--- a/src/app/wot/wot-lookup.page.ts
+++ b/src/app/wot/wot-lookup.page.ts
@@ -6,6 +6,7 @@ import {Router} from "@angular/router";
 import {WotService} from "@app/wot/wot.service";
 import {WotSearchFilter} from "@app/wot/wot.model";
 import {toBoolean} from "@app/shared/functions";
+import {PredefinedColors} from "@app/shared/colors/colors.utils";
 
 @Component({
   selector: 'app-wot-lookup',
@@ -22,6 +23,7 @@ export class WotLookupPage extends BasePage<Account[]> implements OnInit {
   @Output() itemClick = new EventEmitter<Account>();
 
   @Input() showItemActions: boolean;
+  @Input() toolbarColor: PredefinedColors = 'primary';
 
   constructor(injector: Injector,
               private router: Router,
@@ -80,7 +82,7 @@ export class WotLookupPage extends BasePage<Account[]> implements OnInit {
     super.markAsLoading();
   }
 
-  searchChanged(event: CustomEvent<any>, value: string) {
+  searchChanged(event: Event, value: string) {
     if (!event || event.defaultPrevented) return;
     event.preventDefault();
     event.stopPropagation();
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index a7bdf553bf0ecb2006a035a40291c2168894b94a..7e2b18563fd844c83c6f7e93fc8779e9a1a0cd28 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -438,7 +438,7 @@
     "ASSOCIATED_PUBKEY": "Public key :",
     "BTN_METHODS": "Other methods",
     "BTN_METHODS_DOTS": "Change method...",
-    "METHOD_POPOVER_TITLE": "Methods",
+    "METHOD_POPOVER_TITLE": "Connection methods",
     "MEMORIZE_AUTH_FILE": "Memorize this keychain during the navigation session",
     "SCRYPT_PARAMETERS": "Paramètres (Scrypt) :",
     "AUTO_LOGOUT": {
diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json
index 24cb1c6f8f60d3dd3a71840c98a0d382d1f47078..1e7c46c0711da04e460437365baa397129945033 100644
--- a/src/assets/i18n/fr.json
+++ b/src/assets/i18n/fr.json
@@ -1,6 +1,6 @@
 {
   "COMMON": {
-    "APP_NAME": "Cesium",
+    "APP_NAME": "Cesium²",
     "APP_VERSION": "v{{version}}",
     "APP_BUILD": "date : {{build}}",
     "PUBKEY": "Clé publique",
@@ -438,7 +438,7 @@
     "ASSOCIATED_PUBKEY": "Clé publique du trousseau :",
     "BTN_METHODS": "Autres méthodes",
     "BTN_METHODS_DOTS": "Changer de méthode...",
-    "METHOD_POPOVER_TITLE": "Méthodes",
+    "METHOD_POPOVER_TITLE": "Méthodes de connexion :",
     "MEMORIZE_AUTH_FILE": "Mémoriser ce trousseau le temps de la session de navigation",
     "SCRYPT_PARAMETERS": "Paramètres (Scrypt) :",
     "AUTO_LOGOUT": {
diff --git a/src/polyfills.ts b/src/polyfills.ts
index 27f5e432a014fa4fa7b8ba93744eab9dfea054e3..e97a8b57e4ca9903b86bf06b2350e7f46d983875 100644
--- a/src/polyfills.ts
+++ b/src/polyfills.ts
@@ -53,6 +53,7 @@ import 'zone.js/dist/zone';  // Included with Angular CLI.
 /***************************************************************************************************
  * APPLICATION IMPORTS
  */
+// Polkadot API augment
 import '@polkadot/api-augment'
 (window as any).global = window;
 
diff --git a/src/theme/_cesium.scss b/src/theme/_cesium.scss
index 4eb8f3541b6839e0d8ab0a4a519d469f7ca0d5a3..393cbbe968f18aac47c59f8c18cd84fa3af54e79 100644
--- a/src/theme/_cesium.scss
+++ b/src/theme/_cesium.scss
@@ -32,3 +32,11 @@ qr-code {
 ion-toolbar ion-menu-button {
   color: var(--ion-color-base-contrast);
 }
+
+ion-toolbar {
+  user-select: none;
+
+  ion-title.balance {
+    font-weight: bold;
+  }
+}
diff --git a/tsconfig.json b/tsconfig.json
index 268903a5b2f26aaa18569674b41291a658d6ad4b..d403b27907d58c898a13d29de8c7e9e02be4029c 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -13,7 +13,7 @@
     "incremental": true,
     "target": "es2020",
     "module": "es2020",
-    "lib": ["es2018", "dom"],
+    "lib": ["es2020", "dom"],
     "allowSyntheticDefaultImports": true,
     "typeRoots": [
       "node_modules/@types"
@@ -28,7 +28,7 @@
       "rxjs/*": ["node_modules/rxjs/*"],
 
       // Local deps        // Package name
-      "@duniter/core-types/*": ["src/*"],
+      "@duniter/types": ["src/interfaces/types-lookup.ts"],
 
       // here we replace the @polkadot/api augmentation with our own, generated from chain
       "@polkadot/api/augment": ["src/interfaces/augment-api.ts"],
@@ -50,6 +50,6 @@
     "enableI18nLegacyMessageIdFormat": false,
     "strictInjectionParameters": true,
     "strictInputAccessModifiers": true,
-    "strictTemplates": true
+    "strictTemplates": false
   }
 }
diff --git a/yarn.lock b/yarn.lock
index 76d1d5abe78d16b0e5aea3b00bbabf5f6b4af943..78bbb45842f5b74325bc147af26a558c7c74b55a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1313,23 +1313,26 @@
   resolved "https://registry.npmjs.org/@capacitor-community/barcode-scanner/-/barcode-scanner-3.0.0.tgz"
   integrity sha512-Jzr4phHyDS+C2ZE+eoVsuq15oFvFLVtAJmmegIjwVBcb3CxgRdkt/kdVurXgVKMQ6mJ6StYI7hALeMu2vkyC6A==
 
-"@capacitor-community/sqlite@^4.0.1":
-  version "4.0.1"
-  resolved "https://registry.npmjs.org/@capacitor-community/sqlite/-/sqlite-4.0.1.tgz"
-  integrity sha512-bKdIUfDm+fXyZm2WZWWceFZjv8Zn0bRUI2lhaCRKJNqFk3jTI7z4qGTB2zqsKcijVhYF18j21pzXqJc3Ybb9xA==
-  dependencies:
-    jeep-sqlite "^1.5.5"
-
-"@capacitor/android@4.1.0":
-  version "4.1.0"
-  resolved "https://registry.npmjs.org/@capacitor/android/-/android-4.1.0.tgz"
-  integrity sha512-aYHvpYVlS6WC+bG9jJfwqgHMxTw3e8f3taNnl/y9qCjglmMmtFcZWFAVLlOleVK4Q7olSirqjx37f0ppvxRTLg==
+"@capacitor/android@~4.2.0":
+  version "4.2.0"
+  resolved "https://registry.npmjs.org/@capacitor/android/-/android-4.2.0.tgz#f06539694adbaf189cf38cb039f079bde4f7422b"
+  integrity sha512-LWZhM31DoQuNSW8ZGslJ8gZfOAZS2A5TLq30HP1bn0OQTJGvOFIizQysraVRSOOq5FRptykf2nZWu6WEwoKMlA==
 
-"@capacitor/app@4.0.1":
+"@capacitor/app@~4.0.1":
   version "4.0.1"
-  resolved "https://registry.npmjs.org/@capacitor/app/-/app-4.0.1.tgz"
+  resolved "https://registry.npmjs.org/@capacitor/app/-/app-4.0.1.tgz#05c60541c427ef30f4762d8f786e70d0ed32fe01"
   integrity sha512-frPft9TMJL70jWq5jmxwGSBddZVvsWxX5Agj2i19WJerk37aTgljB05HRr/YLg6mF1G/NIXmmFJZDY8MEgirDg==
 
+"@capacitor/browser@~4.0.1":
+  version "4.0.1"
+  resolved "https://registry.npmjs.org/@capacitor/browser/-/browser-4.0.1.tgz#0a725fea27d706940af8dbcc362b6d14bf20bfd2"
+  integrity sha512-tPOA/eYDJdDeMFd+A3I8teuGfPP0GEqhWtMzH8z5r2tW40iJ/mL8qH5NgVLMZ3uvs7gN2LfQTCMNJvl0F/WdBw==
+
+"@capacitor/camera@~4.1.1":
+  version "4.1.1"
+  resolved "https://registry.npmjs.org/@capacitor/camera/-/camera-4.1.1.tgz#a361b3595c378666614e3bd3384d158b85e4d7c3"
+  integrity sha512-L/1KLg4IRCAUmwhmu5jIic4U2OLAHFSW5GoExFU9yR8iCJo1SBGSiay2TpU3PpgmJHRtazO6pxKUCyAmDExkhw==
+
 "@capacitor/cli@4.1.0":
   version "4.1.0"
   resolved "https://registry.npmjs.org/@capacitor/cli/-/cli-4.1.0.tgz"
@@ -1353,36 +1356,36 @@
     tslib "^2.4.0"
     xml2js "^0.4.23"
 
-"@capacitor/clipboard@^4.0.1":
+"@capacitor/clipboard@~4.0.1":
   version "4.0.1"
-  resolved "https://registry.npmjs.org/@capacitor/clipboard/-/clipboard-4.0.1.tgz"
+  resolved "https://registry.npmjs.org/@capacitor/clipboard/-/clipboard-4.0.1.tgz#b96ae5563583d12e510745fff03aa23050284948"
   integrity sha512-DO5fC6ax5Tm/4K77NjxRLu/bdyvO6FDCK38w05CE4LHvi3RF4LTM8EgnmIrEGKxwwbH5VloTeca9Cu6bsMXPiA==
 
-"@capacitor/core@4.1.0":
+"@capacitor/core@~4.1.0":
   version "4.1.0"
   resolved "https://registry.npmjs.org/@capacitor/core/-/core-4.1.0.tgz#4f5b80cd9cbf65bfc5a203082399fa794ffc5f45"
   integrity sha512-bMYFnC5l5n11D/kLTz9TOOJfUH+nQqYRwzG7h5h4AXIm8aFxH5ZY3iFlDZd7R4xcyrlnfxzfoooSH9trPjRBLA==
   dependencies:
     tslib "^2.1.0"
 
-"@capacitor/haptics@4.0.1":
+"@capacitor/haptics@~4.0.1":
   version "4.0.1"
-  resolved "https://registry.npmjs.org/@capacitor/haptics/-/haptics-4.0.1.tgz"
+  resolved "https://registry.npmjs.org/@capacitor/haptics/-/haptics-4.0.1.tgz#8113c757f9bce3cd6618f6aadda47e7aa7a92069"
   integrity sha512-ZLVoLdK1Md/xIRRrDOnrLCKGRg8UENY1ShpKcysPN1z1MgcpvB/9Nbqczm3RH24zyo3MP/7homkqMzUGxPBlwQ==
 
-"@capacitor/keyboard@4.0.1":
+"@capacitor/keyboard@~4.0.1":
   version "4.0.1"
-  resolved "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-4.0.1.tgz"
+  resolved "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-4.0.1.tgz#36cd5f8aa2ef87a722f318e525d1bda12e7811d1"
   integrity sha512-JZVci2v9jAKH0sIoNNZzmw/cWGXWf+KneLt0yDr/6YSs/2/tfuH10yOrUOhgrKFkR+fFj/rddTClQXUQ8Rqcrg==
 
-"@capacitor/splash-screen@4.0.1":
+"@capacitor/splash-screen@~4.0.1":
   version "4.0.1"
-  resolved "https://registry.npmjs.org/@capacitor/splash-screen/-/splash-screen-4.0.1.tgz"
+  resolved "https://registry.npmjs.org/@capacitor/splash-screen/-/splash-screen-4.0.1.tgz#5017a33bade5509e075f010c6381190e6ca6c5ba"
   integrity sha512-7hklUx69aZDonuLP1R5X4ZTGgZLwX8RTj9/3U1905Kz/XflcT7Rhighbad+uZBaOU+L/8Vm6Y3RlR3rFj4ELVA==
 
-"@capacitor/status-bar@4.0.1":
+"@capacitor/status-bar@~4.0.1":
   version "4.0.1"
-  resolved "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-4.0.1.tgz"
+  resolved "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-4.0.1.tgz#d1320add5f4ef383394b94beae45a0c5af7250e1"
   integrity sha512-BmEyOf3m/hAf8bO4hCX0m5gpQPSgd05mGYl+9E841WUZeJjcYlmiG/EBshAUb2uGCVtyNaG36yPXB0r0Ypg+rw==
 
 "@colors/colors@1.5.0":
@@ -1653,7 +1656,7 @@
     ionicons "^6.0.3"
     tslib "^2.1.0"
 
-"@ionic/pwa-elements@~3.1.1":
+"@ionic/pwa-elements@^3.1.1":
   version "3.1.1"
   resolved "https://registry.npmjs.org/@ionic/pwa-elements/-/pwa-elements-3.1.1.tgz#ae1a97260886545b3285364974674576f2b090a7"
   integrity sha512-BoEX1pSrKn5dP4VQwaf8DweAmT4ynmpW+9f6oj/W1xLe54bmuXcUY07ziuZX1DLu50xHSYcO5qJ/dbYC/w6wgA==
@@ -2507,11 +2510,6 @@
   resolved "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz"
   integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==
 
-"@types/emscripten@*":
-  version "1.39.6"
-  resolved "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.6.tgz"
-  integrity sha512-H90aoynNhhkQP6DRweEjJp5vfUVdIj7tdPLsu7pq89vODD/lcugKfZOsfgwpvM6XUewEp2N5dCg1Uf3Qe55Dcg==
-
 "@types/eslint-scope@^3.7.3":
   version "3.7.3"
   resolved "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz"
@@ -2616,11 +2614,16 @@
     "@types/node" "*"
     form-data "^3.0.0"
 
-"@types/node@*", "@types/node@>=10.0.0", "@types/node@^12.20.55":
+"@types/node@*", "@types/node@>=10.0.0":
   version "12.20.55"
   resolved "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz"
   integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==
 
+"@types/node@^14.18.28":
+  version "14.18.28"
+  resolved "https://registry.npmjs.org/@types/node/-/node-14.18.28.tgz#ddb82da2fff476a8e827e8773c84c19d9c235278"
+  integrity sha512-CK2fnrQlIgKlCV3N2kM+Gznb5USlwA1KFX3rJVHmgVk6NJxFPuQ86pAcvKnu37IA4BGlSRz7sEE1lHL1aLZ/eQ==
+
 "@types/parse-json@^4.0.0":
   version "4.0.0"
   resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz"
@@ -2686,14 +2689,6 @@
   dependencies:
     "@types/node" "*"
 
-"@types/sql.js@^1.4.3":
-  version "1.4.3"
-  resolved "https://registry.npmjs.org/@types/sql.js/-/sql.js-1.4.3.tgz"
-  integrity sha512-3bz1LJIiJtKMEL8tYf7c9Nrb1lYcFeWQkE8vhWvobE29ZzizW79DtoTjqx1bR82DS2Ch2K30nOwNhuLclZ1vYg==
-  dependencies:
-    "@types/emscripten" "*"
-    "@types/node" "*"
-
 "@types/websocket@^1.0.5":
   version "1.0.5"
   resolved "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.5.tgz"
@@ -7129,17 +7124,6 @@ jdenticon@^3.1.1:
   dependencies:
     canvas-renderer "~2.2.0"
 
-jeep-sqlite@^1.5.5:
-  version "1.5.7"
-  resolved "https://registry.npmjs.org/jeep-sqlite/-/jeep-sqlite-1.5.7.tgz"
-  integrity sha512-/lxBCs1uw3cvsdZizhxw8Kn/7SX3Xcxel7ifRiokyDekDqH9fpcdBh+W1lCq+u895bYL+l9XoqceT4zqUI5zvA==
-  dependencies:
-    "@stencil/core" "^2.17.4"
-    "@types/sql.js" "^1.4.3"
-    jszip "^3.7.1"
-    localforage "^1.10.0"
-    sql.js "^1.7.0"
-
 jest-worker@^27.4.5:
   version "27.5.1"
   resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz"
@@ -7282,16 +7266,6 @@ jszip@^3.1.3:
     readable-stream "~2.3.6"
     set-immediate-shim "~1.0.1"
 
-jszip@^3.7.1:
-  version "3.10.1"
-  resolved "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz"
-  integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==
-  dependencies:
-    lie "~3.3.0"
-    pako "~1.0.2"
-    readable-stream "~2.3.6"
-    setimmediate "^1.0.5"
-
 karma-chrome-launcher@~3.1.1:
   version "3.1.1"
   resolved "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz"
@@ -7497,7 +7471,14 @@ loader-utils@^2.0.0:
     emojis-list "^3.0.0"
     json5 "^2.1.2"
 
-localforage@^1.10.0, localforage@^1.9.0:
+localforage-cordovasqlitedriver@~1.8.0:
+  version "1.8.0"
+  resolved "https://registry.npmjs.org/localforage-cordovasqlitedriver/-/localforage-cordovasqlitedriver-1.8.0.tgz#13a3613650b01e0899aaf19336ed4655cc0bf77f"
+  integrity sha512-AeYiVPURow8gPAGHNOiGMS9rlgv81wUuQLtnyCP6Eh1mq+IsqNl9fwAOP+RiTi6aO/Wfy3TTWiW2WtbTdJaUnQ==
+  dependencies:
+    localforage ">=1.5.0"
+
+localforage@>=1.5.0, localforage@^1.9.0, localforage@~1.10.0:
   version "1.10.0"
   resolved "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz"
   integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==
@@ -9859,11 +9840,6 @@ set-immediate-shim@~1.0.1:
   resolved "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz"
   integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=
 
-setimmediate@^1.0.5:
-  version "1.0.5"
-  resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz"
-  integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==
-
 setprototypeof@1.1.0:
   version "1.1.0"
   resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz"
@@ -10128,11 +10104,6 @@ sprintf-js@~1.0.2:
   resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
   integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
 
-sql.js@^1.7.0:
-  version "1.7.0"
-  resolved "https://registry.npmjs.org/sql.js/-/sql.js-1.7.0.tgz"
-  integrity sha512-qAfft3xkSgHqmmfNugWTp/59PsqIw8gbeao5TZmpmzQQsAJ49de3iDDKuxVixidYs6dkHNksY8m27v2dZNn2jw==
-
 ssh-config@^1.1.1:
   version "1.1.6"
   resolved "https://registry.npmjs.org/ssh-config/-/ssh-config-1.1.6.tgz#c6ce2d7f85f395178c9e47c448d62b8adf9b2523"
@@ -10526,9 +10497,9 @@ tree-kill@1.2.2, tree-kill@^1.2.2:
   resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz"
   integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
 
-ts-node@^8.6.2:
+ts-node@^8.10.2:
   version "8.10.2"
-  resolved "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz"
+  resolved "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d"
   integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==
   dependencies:
     arg "^4.1.0"
@@ -10783,7 +10754,7 @@ uuid@8.3.2, uuid@^8.3.2:
 
 uuid@^3.3.2:
   version "3.4.0"
-  resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz"
+  resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
   integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
 
 v8-compile-cache@2.3.0, v8-compile-cache@^2.0.3:
@@ -11025,7 +10996,7 @@ which-module@^2.0.0:
 
 which@^1.2.1:
   version "1.3.1"
-  resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz"
+  resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
   integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
   dependencies:
     isexe "^2.0.0"
@@ -11068,7 +11039,7 @@ wordwrap@^1.0.0:
 
 wrap-ansi@^6.2.0:
   version "6.2.0"
-  resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz"
+  resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
   integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
   dependencies:
     ansi-styles "^4.0.0"