diff --git a/scss/ionic.app.scss b/scss/ionic.app.scss
index fc658014a1d50288d450890bc806da9722695f34..12e8b4d6aae00043aba50438fd949ba9ba9bb108 100644
--- a/scss/ionic.app.scss
+++ b/scss/ionic.app.scss
@@ -831,7 +831,6 @@ $screen-menu:                     845px;
   text-align: right !important;
 }
 
-
 .lookupForm .list .item-avatar {
   min-height: 80px !important;
 
diff --git a/www/i18n/locale-en.json b/www/i18n/locale-en.json
index e138a02279face8e5a815053f11c77d142513d44..559838b0eab34f6b5abe5562cc98cede4928f103 100644
--- a/www/i18n/locale-en.json
+++ b/www/i18n/locale-en.json
@@ -42,6 +42,7 @@
     "UNIVERSAL_DIVIDEND": "Universal dividend",
     "UD": "UD",
     "DATE_PATTERN": "MM/DD/YYYY HH:mm",
+    "DATE_SHORT_PATTERN": "MM/DD/YYYY",
     "EMPTY_PARENTHESIS": "(empty)",
     "UID": "Pseudonym",
     "ENABLE": "Enable",
@@ -112,7 +113,7 @@
     "REMEMBER_ME": "Remember me",
     "PLUGINS_SETTINGS": "Extensions",
     "BTN_RESET": "Restore default values",
-    "EXPERT_MODE": "Enable expert mode<span class=\"hidden-xs\"> (Display more details on peers)</span>",
+    "EXPERT_MODE": "Enable expert mode<span class=\"hidden-xs\">(display more details)</span>",
     "POPUP_NODE": {
       "TITLE" : "Duniter Node",
       "HELP" : "Set the address of the node to use:"
@@ -157,9 +158,12 @@
     "SEARCH_HELP": "Search (member or public key)",
     "REGISTERED_SINCE": "Registered since ",
     "REGISTERED_SINCE_BLOCK": "Registered since block #",
-    "NO_CERTIFICATIONS": "No certification",
+    "NO_CERTIFICATION": "No received certification",
+    "NO_GIVEN_CERTIFICATION": "No given certification",
     "NOT_MEMBER_PARENTHESIS": "(not member)",
     "EXPIRE_IN": "Expires",
+    "NOT_WRITTEN_EXPIRE_IN": "Deadline<br/>treatment",
+    "EXPIRED": "Expired",
     "PSEUDO": "Pseudonym",
     "SIGNED_ON_BLOCK": "Certify on block #{{block}}",
     "WRITTEN_ON_BLOCK": "Written on block #{{block}}",
@@ -197,6 +201,7 @@
       "TITLE": "{{uid}} - Certifications sent",
       "SUMMARY": "Sent certifications",
       "LIST": "Détails of sent certifications",
+      "PENDING_LIST": "Pending certifications",
       "SENT": "Sent certifications",
       "SENT_BY": "Certifications sent by {{uid}}"
     }
@@ -331,7 +336,10 @@
     "IDENTITY_INVALID_BLOCK_HASH": "This membership application is no longer valid (because it references a block that network nodes are cancelled): the person must renew its application for membership <b>before</b> being certified.",
     "IDENTITY_SANDBOX_FULL": "Could not register, because node's sandbox is full.<br/><br/>Please retry later or choose another Duniter node (in <b>Settings</b>).",
     "WOT_PENDING_INVALID_BLOCK_HASH": "Membership not valid.",
-    "WALLET_INVALID_BLOCK_HASH": "Your membership application is no longer valid (because it references a block that network nodes are cancelled).<br/>You must <a ng-click=\"doQuickFix('renew')\">renew your application for membership</a> to fix this issue."
+    "WALLET_INVALID_BLOCK_HASH": "Your membership application is no longer valid (because it references a block that network nodes are cancelled).<br/>You must <a ng-click=\"doQuickFix('renew')\">renew your application for membership</a> to fix this issue.",
+    "IDENTITY_ALREADY_CERTIFY": "You have <b>already certified</b> that identity.<br/><br/>Your certificate is still valid (expires {{expiresIn|formatDuration}}).",
+    "IDENTITY_ALREADY_CERTIFY_PENDING": "You have <b>already certified</b> that identity.<br/><br/>Your certification is still pending (Deadline for treatment {{expiresIn|formatDuration}}).",
+    "UNABLE_TO_CERTIFY_TITLE": "Unable to certify"
   },
   "INFO": {
     "POPUP_TITLE": "Information",
diff --git a/www/i18n/locale-fr-FR.json b/www/i18n/locale-fr-FR.json
index 10520b5d6eebb1271a5f6f23e1c00f730c894264..480f437449319162ea990540ecc695a763eed597 100644
--- a/www/i18n/locale-fr-FR.json
+++ b/www/i18n/locale-fr-FR.json
@@ -42,6 +42,7 @@
     "UNIVERSAL_DIVIDEND": "Dividende universel",
     "UD": "DU",
     "DATE_PATTERN": "DD/MM/YYYY HH:mm",
+    "DATE_SHORT_PATTERN": "DD/MM/YYYY",
     "EMPTY_PARENTHESIS": "(vide)",
     "UID": "Pseudonyme",
     "ENABLE": "Activé",
@@ -112,7 +113,7 @@
     "REMEMBER_ME": "Se souvenir de moi",
     "PLUGINS_SETTINGS": "Extensions",
     "BTN_RESET": "Restaurer les valeurs par défaut",
-    "EXPERT_MODE": "Activer le mode expert<span class=\"hidden-xs\"> (Informations détaillées sur les noeuds)</span>",
+    "EXPERT_MODE": "Activer le mode expert<span class=\"hidden-xs\">(affichage plus détaillé)</span>",
     "POPUP_NODE": {
       "TITLE" : "Noeud Duniter",
       "HELP" : "Saisissez l'adresse du noeud que vous voulez utiliser :"
@@ -157,12 +158,15 @@
     "SEARCH_HELP": "Recherche (pseudo ou clé publique)",
     "REGISTERED_SINCE": "Inscription",
     "REGISTERED_SINCE_BLOCK": "Inscrit au block #",
-    "NO_CERTIFICATIONS": "Aucune certification",
+    "NO_CERTIFICATION": "Aucune certification validée",
+    "NO_GIVEN_CERTIFICATION": "Aucune certification émise",
     "NOT_MEMBER_PARENTHESIS": "(non membre)",
     "EXPIRE_IN": "Expiration",
+    "NOT_WRITTEN_EXPIRE_IN": "Date limite<br/>de traitement",
+    "EXPIRED": "Expiré",
     "PSEUDO": "Pseudonyme",
-    "SIGNED_ON_BLOCK": "Certifié au bloc #{{block}}",
-    "WRITTEN_ON_BLOCK": "Ecrit au bloc #{{block}}",
+    "SIGNED_ON_BLOCK": "Emise au bloc #{{block}}",
+    "WRITTEN_ON_BLOCK": "Ecrite au bloc #{{block}}",
     "GENERAL_DIVIDER": "Informations générales",
     "TECHNICAL_DIVIDER": "Informations techniques",
     "BTN_CERTIFY": "Certifier",
@@ -197,6 +201,7 @@
       "TITLE": "{{uid}} - Certifications émises",
       "SUMMARY": "Certifications émises",
       "LIST": "Détail des certifications émises",
+      "PENDING_LIST": "Certifications en attente de traitement",
       "SENT": "Certifications émises",
       "SENT_BY": "Certifications émises par {{uid}}"
     }
@@ -331,7 +336,10 @@
     "IDENTITY_INVALID_BLOCK_HASH": "Cette demande d'adhésion n'est plus valide (car elle référence un bloc que les noeuds du réseau ont annulé) : cette personne doit renouveller sa demande d'adhésion <b>avant</b> d'être certifiée.",
     "IDENTITY_SANDBOX_FULL": "Le noeud Duniter utilisé par Cesium ne peut plus recevoir de nouvelles identités, car sa file d'attente est pleine.<br/><br/>Veuillez réessayer ultérieurement ou changer de noeud (via le menu <b>Paramètres</b>).",
     "WOT_PENDING_INVALID_BLOCK_HASH": "Adhésion non valide.",
-    "WALLET_INVALID_BLOCK_HASH": "Votre demande d'adhésion n'est plus valide (car elle référence un bloc que les noeuds du réseau ont annulé).<br/>Vous devez <a ng-click=\"doQuickFix('renew')\">envoyer une nouvelle demande</a> pour résoudre ce problème."
+    "WALLET_INVALID_BLOCK_HASH": "Votre demande d'adhésion n'est plus valide (car elle référence un bloc que les noeuds du réseau ont annulé).<br/>Vous devez <a ng-click=\"doQuickFix('renew')\">envoyer une nouvelle demande</a> pour résoudre ce problème.",
+    "IDENTITY_ALREADY_CERTIFY": "Vous avez <b>déjà certifié</b> cette identité.<br/><br/>Cette certification est encore valide (expiration {{expiresIn|formatDuration}}).",
+    "IDENTITY_ALREADY_CERTIFY_PENDING": "Vous avez <b>déjà certifié</b> cette identité.<br/><br/>Cette certification est en attente de traitement (date limite de traitement {{expiresIn|formatDuration}}).",
+    "UNABLE_TO_CERTIFY_TITLE": "Certification impossible"
   },
   "INFO": {
     "POPUP_TITLE": "Information",
diff --git a/www/index.html b/www/index.html
index 88c4c89b44a8a9a72b02b7f7bfcb618d86303dd1..93f33d393f7fc22c4abcb4fe7ca02a11c6967aee 100644
--- a/www/index.html
+++ b/www/index.html
@@ -56,6 +56,7 @@
       <script src="dist/dist_js/app/services/network-services.js"></script>
       <script src="dist/dist_js/app/services/crypto-services.js"></script>
       <script src="dist/dist_js/app/services/utils-services.js"></script>
+      <script src="dist/dist_js/app/services/cache-services.js"></script>
       <script src="dist/dist_js/app/services/modal-services.js"></script>
       <script src="dist/dist_js/app/services/http-services.js"></script>
       <script src="dist/dist_js/app/services/storage-services.js"></script>
diff --git a/www/js/app.js b/www/js/app.js
index f837e916c3e8f258a38156d69372454d412c6a7c..9ecd29621760c13863b32bee71a54642c25b702d 100644
--- a/www/js/app.js
+++ b/www/js/app.js
@@ -53,6 +53,12 @@ angular.module('cesium', ['ionic', 'ionic-material', 'ngMessages', 'pascalprecht
     };
   })
 
+  .filter('formatDateShort', function($rootScope) {
+    return function(input) {
+      return input ? moment(parseInt(input)*1000).local().format($rootScope.dateShortPattern || 'YYYY-MM-DD') : '';
+    };
+  })
+
   .filter('formatFromNow', function() {
     return function(input) {
       return input ? moment(parseInt(input)*1000).startOf('minute').fromNow() : '';
@@ -261,9 +267,16 @@ angular.module('cesium', ['ionic', 'ionic-material', 'ngMessages', 'pascalprecht
     }
 
     // Set date pattern (see 'formatDate' filter)
-    $translate('COMMON.DATE_PATTERN')
-      .then(function(datePattern) {
-        $rootScope.datePattern = datePattern || 'YYYY-MM-DD HH:mm';
+    $translate(['COMMON.DATE_PATTERN', 'COMMON.DATE_SHORT_PATTERN'])
+      .then(function(translations) {
+        $rootScope.datePattern = translations['COMMON.DATE_PATTERN'];
+        if ($rootScope.datePattern == 'COMMON.DATE_PATTERN') {
+          $rootScope.datePattern = 'YYYY-MM-DD HH:mm';
+        }
+        $rootScope.dateShortPattern = translations['COMMON.DATE_SHORT_PATTERN'];
+        if ($rootScope.dateShortPattern == 'COMMON.DATE_SHORT_PATTERN') {
+          $rootScope.dateShortPattern = 'YYYY-MM-DD';
+        }
       });
 
   };
diff --git a/www/js/controllers/app-controllers.js b/www/js/controllers/app-controllers.js
index 6ec64b209f04a1c0f959e2cc8a006621ffd7a618..d55e8000b7014e97a9f9cec8f771df758ae898f8 100644
--- a/www/js/controllers/app-controllers.js
+++ b/www/js/controllers/app-controllers.js
@@ -71,16 +71,16 @@ function PluginExtensionPointController($scope, PluginService) {
  * Abstract controller (inherited by other controllers)
  */
 function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $timeout, $ionicHistory, $controller,
-                       UIUtils, BMA, Wallet, Device, Modals, csSettings, csConfig
+                       UIUtils, BMA, csWallet, Device, Modals, csSettings, csConfig
   ) {
   'ngInject';
 
   $scope.search = {};
-  $rootScope.walletData = Wallet.data;
+  $rootScope.walletData = csWallet.data;
   $rootScope.settings = csSettings.data;
   $rootScope.config = csConfig;
   $rootScope.device = Device;
-  $rootScope.login = Wallet.isLogin();
+  $rootScope.login = csWallet.isLogin();
 
   ////////////////////////////////////////
   // Show view
@@ -131,7 +131,7 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
 
   $scope.createHelptipScope = function(isTour) {
     if (!isTour && ($rootScope.tour || !$rootScope.settings.helptip.enable)) {
-      return; // avoid other helptip to be launched (e.g. wallet)
+      return; // avoid other helptip to be launched (e.g. csWallet)
     }
     // Create a new scope for the tour controller
     var helptipScope = $scope.$new();
@@ -140,7 +140,7 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
   };
 
   $scope.startHelpTour = function() {
-    $rootScope.tour = true; // to avoid other helptip to be launched (e.g. wallet)
+    $rootScope.tour = true; // to avoid other helptip to be launched (e.g. csWallet)
     var helptipScope = $scope.createHelptipScope(true);
     return helptipScope.startHelpTour()
     .then(function() {
@@ -185,7 +185,7 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
 
     // Warn if wallet has been never used - see #167
     var _showConfirmIfUnused = function() {
-      var showAlert = !csConfig.initPhase && Wallet.isNeverUsed() && (!csSettings.data.wallet || csSettings.data.wallet.alertIfUnusedWallet);
+      var showAlert = !csConfig.initPhase && csWallet.isNeverUsed() && (!csSettings.data.wallet || csSettings.data.wallet.alertIfUnusedWallet);
       if (!showAlert) return;
       UIUtils.alert.confirm('CONFIRM.LOGIN_UNUSED_WALLET',
         'CONFIRM.LOGIN_UNUSED_WALLET_TITLE', {
@@ -204,12 +204,12 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
     };
 
     return $q(function(resolve, reject){
-      if (!Wallet.isLogin()) {
+      if (!csWallet.isLogin()) {
         return $scope.showLoginModal()
         .then(function(walletData) {
           if (walletData) {
             $rootScope.viewFirstEnter = false;
-            Wallet.loadData()
+            csWallet.loadData()
             .then(function(walletData){
               _showConfirmIfUnused();
               $rootScope.walletData = walletData;
@@ -229,8 +229,8 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
           }
         });
       }
-      else if (!Wallet.data.loaded) {
-        return Wallet.loadData()
+      else if (!csWallet.data.loaded) {
+        return csWallet.loadData()
         .then(function(walletData){
           _showConfirmIfUnused();
           $rootScope.walletData = walletData;
@@ -246,7 +246,7 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
         });
       }
       else {
-        resolve(Wallet.data);
+        resolve(csWallet.data);
       }
     });
   };
@@ -260,7 +260,7 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
   $scope.loginAndGo = function(state) {
     $scope.closeProfilePopover();
 
-    if (!Wallet.isLogin()) {
+    if (!csWallet.isLogin()) {
       return $scope.showLoginModal()
       .then(function(walletData){
         UIUtils.loading.hide(10);
@@ -285,7 +285,7 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
         csSettings.data.useLocalStorage = csSettings.data.rememberMe ? true : csSettings.data.useLocalStorage;
         csSettings.store();
       }
-      return Wallet.login(formData.username, formData.password);
+      return csWallet.login(formData.username, formData.password);
     })
     .then(function(walletData){
       if (walletData) {
@@ -307,7 +307,7 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
       return;
     }
     UIUtils.loading.show();
-    return Wallet.logout()
+    return csWallet.logout()
     .then(function() {
       $ionicSideMenuDelegate.toggleLeft();
       $ionicHistory.clearHistory();
@@ -321,17 +321,17 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
   };
 
   // add listener on wallet event
-  Wallet.api.data.on.login($scope, function(walletData, resolve) {
+  csWallet.api.data.on.login($scope, function(walletData, resolve) {
     $rootScope.login = true;
     if (resolve) resolve();
   });
-  Wallet.api.data.on.logout($scope, function() {
+  csWallet.api.data.on.logout($scope, function() {
     $rootScope.login = false;
   });
 
   // If connected and same pubkey
   $scope.isUserPubkey = function(pubkey) {
-    return Wallet.isUserPubkey(pubkey);
+    return csWallet.isUserPubkey(pubkey);
   };
 
   ////////////////////////////////////////
diff --git a/www/js/controllers/help-controllers.js b/www/js/controllers/help-controllers.js
index e672919a14e77b9a7bc3a8ed131da3c809bc66cc..9bda73f6e3211b869b16fe42d0f2720a695385ff 100644
--- a/www/js/controllers/help-controllers.js
+++ b/www/js/controllers/help-controllers.js
@@ -79,8 +79,8 @@ function HelpModalController($scope, $timeout, $anchorScroll, csSettings, parame
 /* ----------------------------
 *  Help Tip
 * ---------------------------- */
-function HelpTipController($scope, $rootScope, $state, $window, $ionicSideMenuDelegate, $timeout, $q, $translate, $sce,
-                           UIUtils, csConfig, csSettings, csCurrency, Device, Wallet) {
+function HelpTipController($scope, $rootScope, $state, $window, $ionicSideMenuDelegate, $timeout, $q,
+                           UIUtils, csConfig, csSettings, csCurrency, Device, csWallet) {
 
   $scope.tour = false; // Is a tour or a helptip ?
 
@@ -183,7 +183,7 @@ function HelpTipController($scope, $rootScope, $state, $window, $ionicSideMenuDe
       // Wallet tour (if login)
       .then(function(next){
         if (!next) return false;
-        if (!Wallet.isLogin()) return true; // not login: continue
+        if (!csWallet.isLogin()) return true; // not login: continue
         return $scope.startWalletTour(0, true)
           .then(function(endIndex){
             if (!endIndex) return false;
@@ -196,7 +196,7 @@ function HelpTipController($scope, $rootScope, $state, $window, $ionicSideMenuDe
       // Wallet certifications tour
       .then(function(next){
         if (!next) return false;
-        if (!Wallet.isLogin()) return true; // not login: continue
+        if (!csWallet.isLogin()) return true; // not login: continue
         return $scope.startWalletCertTour(0, true)
           .then(function(endIndex){
             if (!endIndex) return false;
@@ -562,7 +562,7 @@ function HelpTipController($scope, $rootScope, $state, $window, $ionicSideMenuDe
    * @returns {*}
    */
   $scope.startWalletNoLoginTour = function(startIndex, hasNext) {
-    if (Wallet.isLogin()) return $scope.emptyPromise(true); // skip if login
+    if (csWallet.isLogin()) return $scope.emptyPromise(true); // skip if login
 
     var steps = [
       function () {
@@ -587,7 +587,7 @@ function HelpTipController($scope, $rootScope, $state, $window, $ionicSideMenuDe
    * @returns {*}
    */
   $scope.startWalletTour = function(startIndex, hasNext) {
-    if (!Wallet.isLogin()) return $scope.emptyPromise(true); // skip if not login
+    if (!csWallet.isLogin()) return $scope.emptyPromise(true); // skip if not login
 
     var contentParams;
 
@@ -678,7 +678,7 @@ function HelpTipController($scope, $rootScope, $state, $window, $ionicSideMenuDe
    * @returns {*}
    */
   $scope.startWalletCertTour = function(startIndex, hasNext) {
-    if (!Wallet.isLogin()) return $scope.emptyPromise(true);
+    if (!csWallet.isLogin()) return $scope.emptyPromise(true);
 
     var contentParams;
     var skipAll = false;
@@ -900,7 +900,7 @@ function HelpTipController($scope, $rootScope, $state, $window, $ionicSideMenuDe
     }
 
     // If login: redirect to wallet
-    if (Wallet.isLogin()) {
+    if (csWallet.isLogin()) {
       return $state.go('app.view_wallet')
         .then(function(){
           return $scope.showHelpTip('helptip-wallet-balance', {
diff --git a/www/js/controllers/join-controllers.js b/www/js/controllers/join-controllers.js
index ed5227d21733848c45018eebaeb7a74bae060dfd..0529a73b0f9b30e65ec07dd1cf16f81de0f77ff4 100644
--- a/www/js/controllers/join-controllers.js
+++ b/www/js/controllers/join-controllers.js
@@ -36,7 +36,7 @@ function JoinController($timeout, Modals) {
 }
 
 
-function JoinModalController($scope, $state, $timeout, UIUtils, CryptoUtils, Modals, Wallet, csCurrency, PluginService) {
+function JoinModalController($scope, $state,  UIUtils, CryptoUtils, Modals, csWallet, csCurrency) {
   'ngInject';
 
   $scope.formData = {
@@ -150,7 +150,7 @@ function JoinModalController($scope, $state, $timeout, UIUtils, CryptoUtils, Mod
 
     var onErrorLogout = function(message) {
       return function(err) {
-        Wallet.logout()
+        csWallet.logout()
         .then(function(){
           UIUtils.onError(message)(err);
         });
@@ -159,7 +159,7 @@ function JoinModalController($scope, $state, $timeout, UIUtils, CryptoUtils, Mod
 
     UIUtils.loading.show();
 
-    Wallet.login($scope.formData.username, $scope.formData.password)
+    csWallet.login($scope.formData.username, $scope.formData.password)
     .then(function() {
       if (!$scope.formData.isMember) {
         // Redirect to wallet
@@ -168,10 +168,10 @@ function JoinModalController($scope, $state, $timeout, UIUtils, CryptoUtils, Mod
       }
 
       // Send self
-      Wallet.self($scope.formData.pseudo, false/*do NOT load membership here*/)
+      csWallet.self($scope.formData.pseudo, false/*do NOT load membership here*/)
         .then(function() {
           // Send membership IN
-          Wallet.membership.inside()
+          csWallet.membership.inside()
           .then(function() {
 
             $scope.closeModal();
diff --git a/www/js/controllers/settings-controllers.js b/www/js/controllers/settings-controllers.js
index b5b632778d9df8d52c48cb36c6760620a7f92ec1..c0a070d3c57d73429f7d7c81eda2a0686b0ede2e 100644
--- a/www/js/controllers/settings-controllers.js
+++ b/www/js/controllers/settings-controllers.js
@@ -36,7 +36,7 @@ function SettingsController($scope, $q, $ionicPopup, $timeout, $translate, csHtt
   };
 
   $scope.load = function() {
-    $scope.loading = true; // to avoid the call of Wallet.store()
+    $scope.loading = true; // to avoid the call of csWallet.store()
 
     // Fill locales
     $scope.locales = angular.copy(UIUtils.locales);
diff --git a/www/js/controllers/transfer-controllers.js b/www/js/controllers/transfer-controllers.js
index 58164acad7df21952093be34a6b64e21f3f753ba..f119975afae7e0b197a00cef302b28c0f2f7b53a 100644
--- a/www/js/controllers/transfer-controllers.js
+++ b/www/js/controllers/transfer-controllers.js
@@ -44,10 +44,10 @@ angular.module('cesium.transfer.controllers', ['cesium.services', 'cesium.curren
   .controller('TransferModalCtrl', TransferModalController)
 ;
 
-function TransferController($scope, $rootScope, $state, BMA, Wallet, UIUtils, $timeout, Device, $ionicPopover, $translate, $filter, $q, Modals, $ionicHistory) {
+function TransferController($scope, $rootScope, $state, BMA, csWallet, UIUtils, $timeout, Device, $ionicPopover, $translate, $filter, $q, Modals, $ionicHistory) {
   'ngInject';
 
-  TransferModalController.call(this, $scope, $rootScope, $state, BMA, Wallet, UIUtils, $timeout, Device, $ionicPopover, $translate, $filter, $q, Modals, csSettings);
+  TransferModalController.call(this, $scope, $rootScope, $state, BMA, csWallet, UIUtils, $timeout, Device, $ionicPopover, $translate, $filter, $q, Modals, csSettings);
 
   $scope.$on('$ionicView.enter', function(e, state) {
     if (!!state.stateParams && !!state.stateParams.pubkey) {
@@ -80,7 +80,7 @@ function TransferController($scope, $rootScope, $state, BMA, Wallet, UIUtils, $t
   };
 }
 
-function TransferModalController($scope, $rootScope, $ionicPopover, $translate, $filter, $q, BMA, Wallet, UIUtils, Modals,
+function TransferModalController($scope, $rootScope, $ionicPopover, $translate, $filter, $q, BMA, csWallet, UIUtils, Modals,
                                  csSettings, parameters) {
   'ngInject';
 
@@ -181,7 +181,7 @@ function TransferModalController($scope, $rootScope, $ionicPopover, $translate,
                  amount.replace(new RegExp('[.,]'), '.');
       }
 
-      Wallet.transfer($scope.formData.destPub, amount, $scope.formData.comment, $scope.formData.useRelative)
+      csWallet.transfer($scope.formData.destPub, amount, $scope.formData.comment, $scope.formData.useRelative)
       .then(function() {
         UIUtils.loading.hide();
         $scope.closeModal(true);
diff --git a/www/js/controllers/wallet-controllers.js b/www/js/controllers/wallet-controllers.js
index 5c496867df65bd0538b7ea92e25a07dac374f58f..6f0aa4b69abca9025801b7d6d8598dfdf59d7e30 100644
--- a/www/js/controllers/wallet-controllers.js
+++ b/www/js/controllers/wallet-controllers.js
@@ -33,7 +33,7 @@ angular.module('cesium.wallet.controllers', ['cesium.services', 'cesium.currency
 ;
 
 function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state, $ionicHistory,
-                          UIUtils, Wallet, $translate, $ionicPopover, Modals, csSettings, BMA) {
+                          UIUtils, csWallet, $translate, $ionicPopover, Modals, csSettings, BMA) {
   'ngInject';
 
   $scope.convertedBalance = null;
@@ -159,7 +159,7 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state,
     .then(function(uid) {
       UIUtils.loading.show();
 
-      return Wallet.self(uid)
+      return csWallet.self(uid)
       .then(function() {
         $scope.updateView();
         UIUtils.loading.hide();
@@ -174,7 +174,7 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state,
   };
 
   $scope.doMembershipIn = function(retryCount) {
-    return Wallet.membership.inside()
+    return csWallet.membership.inside()
       .then(function() {
         $scope.updateView();
         UIUtils.loading.hide();
@@ -206,7 +206,7 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state,
       if (!$rootScope.walletData.blockUid || uid != $rootScope.walletData.uid) {
         $rootScope.walletData.blockUid = null;
         $rootScope.walletData.uid = uid;
-        Wallet.self(uid, false/*do NOT load membership here*/)
+        csWallet.self(uid, false/*do NOT load membership here*/)
         .then(function() {
           $scope.doMembershipIn();
         })
@@ -241,7 +241,7 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state,
     }
 
     UIUtils.loading.show();
-    return Wallet.membership.out()
+    return csWallet.membership.out()
       .then(function() {
         UIUtils.loading.hide();
         UIUtils.toast.show('INFO.MEMBERSHIP_OUT_SENT');
@@ -252,7 +252,7 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state,
   // Updating wallet data
   $scope.doUpdate = function() {
     UIUtils.loading.show();
-    return Wallet.refreshData()
+    return csWallet.refreshData()
     .then(function() {
       $scope.updateView();
       UIUtils.loading.hide();
@@ -316,6 +316,15 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state,
 
   /* -- popup / UI -- */
 
+  $scope.toggleShowDetails = function() {
+    // Update user settings
+    csSettings.data.wallet = csSettings.data.wallet || {};
+    csSettings.data.wallet.showPubkey = !$scope.showDetails;
+    csSettings.store();
+
+    $scope.setShowDetails(csSettings.data.wallet.showPubkey);
+  };
+
   $scope.setShowDetails = function(show) {
     $scope.showDetails = show;
     $scope.hideActionsPopover();
@@ -332,11 +341,6 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state,
         pubkeyElement.classList.toggle('in', true);
       }, 500);
     }
-
-    // Update user settings
-    csSettings.data.wallet = csSettings.data.wallet || {};
-    csSettings.data.wallet.showPubkey = show;
-    csSettings.store();
   };
 
   // Transfer
@@ -462,7 +466,7 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state,
       (Math.trunc(new Date().getTime() / 1000) - 2 * csSettings.data.walletHistoryTimeSecond);
 
     UIUtils.loading.show();
-    return Wallet.refreshData({tx: {enable: true,fromTime: fromTime}})
+    return csWallet.refreshData({tx: {enable: true,fromTime: fromTime}})
       .then(function() {
         $scope.updateView();
         UIUtils.loading.hide();
@@ -483,7 +487,7 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state,
 }
 
 
-function WalletTxErrorController($scope, $timeout, UIUtils, Wallet) {
+function WalletTxErrorController($scope, $timeout, UIUtils, csWallet) {
   'ngInject';
 
   $scope.$on('$ionicView.enter', function(e) {
@@ -508,7 +512,7 @@ function WalletTxErrorController($scope, $timeout, UIUtils, Wallet) {
   // Updating wallet data
   $scope.doUpdate = function() {
     UIUtils.loading.show();
-    Wallet.refreshData()
+    csWallet.refreshData()
     .then(function() {
       $scope.updateView();
       UIUtils.loading.hide();
diff --git a/www/js/controllers/wot-controllers.js b/www/js/controllers/wot-controllers.js
index 16425d02a373a4352f8f9c5bf87bb60010a4e6c8..74089bd8ca8e4492829f43b4554263b8e4f91588 100644
--- a/www/js/controllers/wot-controllers.js
+++ b/www/js/controllers/wot-controllers.js
@@ -84,7 +84,7 @@ angular.module('cesium.wot.controllers', ['cesium.services'])
 
 ;
 
-function WotLookupController($scope, BMA, $state, UIUtils, $timeout, csConfig, csSettings, Device, Wallet, WotService, $focus, $ionicPopover) {
+function WotLookupController($scope, BMA, $state, UIUtils, $timeout, csConfig, csSettings, Device, csWallet, csWot, $focus, $ionicPopover) {
   'ngInject';
 
   var defaultSearchLimit = 20;
@@ -143,7 +143,7 @@ function WotLookupController($scope, BMA, $state, UIUtils, $timeout, csConfig, c
     }
     else {
       $scope.search.type = 'text';
-      WotService.search(text)
+      csWot.search(text)
       .then(function(idties){
         if ($scope.search.type != 'text') return; // could have change
         if ($scope.search.text.trim() !== text) return; // search text has changed before received response
@@ -165,8 +165,8 @@ function WotLookupController($scope, BMA, $state, UIUtils, $timeout, csConfig, c
     $scope.search.type = 'newcomers';
     $scope.search.limit = (limit && limit > 0) ? limit : defaultSearchLimit;
     var searchFunction =  csConfig.initPhase ?
-      WotService.all :
-      WotService.newcomers;
+      csWot.all :
+      csWot.newcomers;
     return searchFunction($scope.search.limit).then(function(idties){
       if ($scope.search.type != 'newcomers') return; // could have change
       $scope.doDisplayResult(idties);
@@ -178,7 +178,7 @@ function WotLookupController($scope, BMA, $state, UIUtils, $timeout, csConfig, c
     $scope.search.loading = more ? false : true;
     $scope.search.type = 'pending';
     $scope.search.limit = (limit && limit > 0) ? limit : defaultSearchLimit;
-    return WotService.pending($scope.search.limit).then(function(idties){
+    return csWot.pending($scope.search.limit).then(function(idties){
       if ($scope.search.type != 'pending') return; // could have change
       $scope.doDisplayResult(idties);
     });
@@ -206,7 +206,7 @@ function WotLookupController($scope, BMA, $state, UIUtils, $timeout, csConfig, c
 
   $scope.select = function(identity) {
     // identity = self -> open the user wallet
-    if (Wallet.isUserPubkey(identity.pubkey)) {
+    if (csWallet.isUserPubkey(identity.pubkey)) {
       $state.go('app.view_wallet');
     }
     // Open identity view
@@ -313,10 +313,10 @@ function WotLookupController($scope, BMA, $state, UIUtils, $timeout, csConfig, c
 
 }
 
-function WotLookupModalController($scope, BMA, $state, UIUtils, $timeout, csConfig, csSettings, Device, Wallet, WotService, $focus, $ionicPopover){
+function WotLookupModalController($scope, BMA, $state, UIUtils, $timeout, csConfig, csSettings, Device, csWallet, csWot, $focus, $ionicPopover){
   'ngInject';
 
-  WotLookupController.call(this, $scope, BMA, $state, UIUtils, $timeout, csConfig, csSettings, Device, Wallet, WotService, $focus, $ionicPopover);
+  WotLookupController.call(this, $scope, BMA, $state, UIUtils, $timeout, csConfig, csSettings, Device, csWallet, csWot, $focus, $ionicPopover);
 
   $scope.search.loading = false;
   $scope.enableFilter = false;
@@ -343,7 +343,7 @@ function WotLookupModalController($scope, BMA, $state, UIUtils, $timeout, csConf
   // endRemoveIf(device)
 }
 
-function WotIdentityViewController($scope, $state, $timeout, UIUtils, WotService) {
+function WotIdentityViewController($scope, $state, $timeout, UIUtils, csWot) {
   'ngInject';
 
   $scope.formData = {};
@@ -353,11 +353,9 @@ function WotIdentityViewController($scope, $state, $timeout, UIUtils, WotService
     if (state.stateParams &&
       state.stateParams.pubkey &&
       state.stateParams.pubkey.trim().length > 0) {
-      if ($scope.loading) {
-        $scope.load(
-          state.stateParams.pubkey.trim(),
-          state.stateParams.uid ? state.stateParams.uid.trim() : null
-        );
+      if ($scope.loading) { // load once
+        $scope.load(state.stateParams.pubkey.trim(),
+                    true /*withCache*/);
       }
     }
     else {
@@ -366,8 +364,8 @@ function WotIdentityViewController($scope, $state, $timeout, UIUtils, WotService
     }
   });
 
-  $scope.load = function(pubkey) {
-    WotService.load(pubkey)
+  $scope.load = function(pubkey, withCache) {
+    csWot.load(pubkey, withCache)
     .then(function(identity){
       $scope.formData = identity;
       $scope.loading = false;
@@ -391,7 +389,7 @@ function WotIdentityViewController($scope, $state, $timeout, UIUtils, WotService
 
   $scope.showSharePopover = function(event) {
     var title = $scope.formData.name || $scope.formData.uid || $scope.formData.pubkey;
-    var url = $state.href('app.wot_view_identity', {pubkey: $scope.formData.pubkey, uid: $scope.formData.name || $scope.formData.uid}, {absolute: true});
+    var url = $state.href('app.wot_view_identity', {pubkey: $scope.formData.pubkey, uid: $scope.formData.uid}, {absolute: true});
     UIUtils.popover.share(event, {
       bindings: {
         url: url,
@@ -413,13 +411,13 @@ function WotIdentityViewController($scope, $state, $timeout, UIUtils, WotService
  * @param $timeout
  * @param $translate
  * @param csSettings
- * @param Wallet
+ * @param csWallet
  * @param UIUtils
- * @param WotService
+ * @param csWot
  * @param Modals
  * @constructor
  */
-function WotCertificationsViewController($scope, $rootScope, $state, $timeout, $translate, csConfig, csSettings, Wallet, UIUtils, WotService, Modals) {
+function WotCertificationsViewController($scope, $rootScope, $state, $timeout, $translate, csConfig, csSettings, csWallet, UIUtils, csWot, Modals) {
   'ngInject';
 
   $scope.loading = true;
@@ -432,14 +430,14 @@ function WotCertificationsViewController($scope, $rootScope, $state, $timeout, $
     if (state.stateParams && state.stateParams.pubkey &&
       state.stateParams.pubkey.trim().length >  0) {
       if ($scope.loading) {
-        $scope.load(state.stateParams.pubkey.trim());
+        $scope.load(state.stateParams.pubkey.trim(), true /*withCache*/);
       }
     }
 
     // Load from wallet pubkey
-    else if (Wallet.isLogin()){
+    else if (csWallet.isLogin()){
       if ($scope.loading) {
-        $scope.load(Wallet.data.pubkey);
+        $scope.load(csWallet.data.pubkey, true /*withCache*/);
       }
     }
 
@@ -451,13 +449,13 @@ function WotCertificationsViewController($scope, $rootScope, $state, $timeout, $
     }
   });
 
-  $scope.load = function(pubkey) {
-    return WotService.load(pubkey)
+  $scope.load = function(pubkey, withCache) {
+    return csWot.load(pubkey, withCache)
     .then(function(identity){
       $scope.formData = identity;
-      $scope.canCertify = $scope.formData.hasSelf && (!Wallet.isLogin() || (!Wallet.isUserPubkey(pubkey)));
-      $scope.canSelectAndCertify = $scope.formData.hasSelf && Wallet.isUserPubkey(pubkey);
-      $scope.alreadyCertified = $scope.canCertify ? !!_.findWhere(identity.certifications, { uid: Wallet.data.uid, valid: true }) : false;
+      $scope.canCertify = $scope.formData.hasSelf && (!csWallet.isLogin() || (!csWallet.isUserPubkey(pubkey)));
+      $scope.canSelectAndCertify = $scope.formData.hasSelf && csWallet.isUserPubkey(pubkey);
+      $scope.alreadyCertified = $scope.canCertify ? !!_.findWhere(identity.certifications, { uid: csWallet.data.uid, valid: true }) : false;
 
       $scope.loading = false;
 
@@ -467,7 +465,7 @@ function WotCertificationsViewController($scope, $rootScope, $state, $timeout, $
       $scope.motionGivenCertifications(900);
 
       // Show help tip
-      var isWallet = Wallet.isUserPubkey(pubkey);
+      var isWallet = csWallet.isUserPubkey(pubkey);
       $scope.showHelpTip(isWallet);
     });
   };
@@ -488,7 +486,7 @@ function WotCertificationsViewController($scope, $rootScope, $state, $timeout, $
           return;
         }
         UIUtils.loading.show();
-        Wallet.certify($scope.formData.uid,
+        csWallet.certify($scope.formData.uid,
                     $scope.formData.pubkey,
                     $scope.formData.timestamp,
                     $scope.formData.sig)
@@ -527,7 +525,7 @@ function WotCertificationsViewController($scope, $rootScope, $state, $timeout, $
 
         UIUtils.loading.show();
         // load selected identity
-        return WotService.load(idty.pubkey);
+        return csWot.load(idty.pubkey, false /*no cache*/);
       })
       .then(function(identity) {
         if (!identity) return; // cancelled
@@ -537,6 +535,26 @@ function WotCertificationsViewController($scope, $rootScope, $state, $timeout, $
           return;
         }
 
+        // Check not already certified
+        var previousCert = _.findWhere(identity.certifications, { uid: csWallet.data.uid, pending: false, valid: true});
+        if (previousCert) {
+          $translate('ERROR.IDENTITY_ALREADY_CERTIFY', previousCert)
+            .then(function(message) {
+              UIUtils.alert.error(message, 'ERROR.UNABLE_TO_CERTIFY_TITLE');
+            });
+          return;
+        }
+
+        // Check not pending certification
+        previousCert = _.findWhere(identity.certifications, { uid: csWallet.data.uid, pending: true, valid: true});
+        if (previousCert) {
+          $translate('ERROR.IDENTITY_ALREADY_CERTIFY_PENDING', previousCert)
+            .then(function(message) {
+              UIUtils.alert.error(message, 'ERROR.UNABLE_TO_CERTIFY_TITLE');
+            });
+          return;
+        }
+
         // Ask confirmation
         $translate('CONFIRM.CERTIFY_RULES_TITLE_UID', {uid: identity.uid})
           .then(function(confirmTitle) {
@@ -549,7 +567,7 @@ function WotCertificationsViewController($scope, $rootScope, $state, $timeout, $
             UIUtils.loading.show();
 
             // Send certification
-            Wallet.certify(identity.uid,
+            csWallet.certify(identity.uid,
               identity.pubkey,
               identity.timestamp,
               identity.sig)
@@ -566,7 +584,7 @@ function WotCertificationsViewController($scope, $rootScope, $state, $timeout, $
 
   // Updating wallet data
   $scope.doUpdate = function() {
-    $scope.load($scope.formData.pubkey);
+    $scope.load($scope.formData.pubkey, false /*no cache*/);
   };
 
   // Show received certifcations
diff --git a/www/js/services/bma-services.js b/www/js/services/bma-services.js
index 422d3fdfc2944eff35f3ff577576d46ad25d248b..7ab8d6db4e5b2afc4b0181b9fee370e4e2d3af60 100644
--- a/www/js/services/bma-services.js
+++ b/www/js/services/bma-services.js
@@ -2,7 +2,7 @@
 
 angular.module('cesium.bma.services', ['ngResource', 'cesium.http.services', 'cesium.settings.services'])
 
-.factory('BMA', function($q, csSettings, csHttp, $rootScope, $timeout) {
+.factory('BMA', function($q, csSettings, csHttp, csCache, $rootScope, $timeout) {
   'ngInject';
 
   function factory(host, port, cacheEnable) {
@@ -42,7 +42,7 @@ angular.module('cesium.bma.services', ['ngResource', 'cesium.http.services', 'ce
     function copy(otherNode) {
       if (!!this.instance) { // if main service impl
         var instance = this.instance; // keep factory
-        csHttp.cache.clearAll(); // clean cache for old node
+        csCache.clearAll(); // clean all caches
         angular.copy(otherNode, this);
         this.instance = instance;
       }
diff --git a/www/js/services/cache-services.js b/www/js/services/cache-services.js
new file mode 100644
index 0000000000000000000000000000000000000000..503a12dc320861ab2eca7711006c5552a1c1ed27
--- /dev/null
+++ b/www/js/services/cache-services.js
@@ -0,0 +1,91 @@
+angular.module('cesium.cache.services', ['ngResource', 'angular-cache'])
+
+.factory('csCache', function($http, csSettings, CacheFactory) {
+  'ngInject';
+
+  function factory() {
+
+    var
+      constants = {
+        LONG: 1 * 60  * 60 * 1000 /*5 min*/,
+        SHORT: csSettings.data.cacheTimeMs
+      },
+      cacheNames = [];
+    ;
+
+    function getOrCreateCache(prefix, maxAge, onExpire){
+      prefix = prefix || 'csCache-';
+      maxAge = maxAge || constants.cache.SHORT;
+      var cacheName = prefix + maxAge;
+      if (!onExpire) {
+        if (!cacheNames[cacheName]) {
+          cacheNames[cacheName] = true;
+        }
+        return CacheFactory.get(cacheName) ||
+          CacheFactory.createCache(cacheName, {
+            maxAge: maxAge,
+            deleteOnExpire: 'aggressive',
+            //cacheFlushInterval: 60 * 60 * 1000, //  clear itself every hour
+            recycleFreq: Math.max(maxAge - 1000, 5 * 60 * 1000 /*5min*/),
+            storageMode: 'memory'
+              // FIXME : enable this when cache is cleaning on rollback
+              //csSettings.data.useLocalStorage ? 'localStorage' : 'memory'
+          });
+      }
+      else {
+        var counter = 1;
+        while(CacheFactory.get(cacheName + counter)) {
+          counter++;
+        }
+        cacheName = cacheName + counter;
+        if (!cacheNames[cacheName]) {
+          cacheNames[cacheName] = true;
+        }
+        return CacheFactory.createCache(cacheName, {
+            maxAge: maxAge,
+            deleteOnExpire: 'aggressive',
+            //cacheFlushInterval: 60 * 60 * 1000, // This cache will clear itself every hour
+            recycleFreq: maxAge,
+            onExpire: onExpire,
+            storageMode: 'memory'
+              // FIXME : enable this when cache is cleaning on rollback
+              //csSettings.data.useLocalStorage ? 'localStorage' : 'memory'
+          });
+      }
+    }
+
+    function clearAllCaches() {
+      console.debug("[http] cleaning all caches");
+      _.forEach(_.keys(cacheNames), function(cacheName) {
+        var cache = CacheFactory.get(cacheName);
+        if (cache) {
+          cache.removeAll();
+        }
+      });
+    }
+
+    function clearFromPrefix(cachePrefix) {
+      _.forEach(_.keys(cacheNames), function(cacheName) {
+        if (cacheName.startsWith(cachePrefix)) {
+          var cache = CacheFactory.get(cacheNames);
+          if (cache) {
+            cache.removeAll();
+          }
+        }
+      });
+    }
+
+    return {
+      get: getOrCreateCache,
+      clear: clearFromPrefix,
+      clearAll: clearAllCaches,
+      constants: {
+        LONG : constants.LONG,
+        SHORT: constants.SHORT
+      }
+    };
+  }
+
+  return factory();
+})
+;
diff --git a/www/js/services/http-services.js b/www/js/services/http-services.js
index dd84f83155a2dbd182e8888302e120eff9a0c078..abd3fde6d67aff86bbaa5f6193aa07f1d02ec090 100644
--- a/www/js/services/http-services.js
+++ b/www/js/services/http-services.js
@@ -1,18 +1,13 @@
-angular.module('cesium.http.services', ['ngResource', 'angular-cache'])
+angular.module('cesium.http.services', ['ngResource', 'cesium.cache.services'])
 
-.factory('csHttp', function($http, $q, csSettings, CacheFactory) {
+.factory('csHttp', function($http, $q, csSettings, csCache, CacheFactory) {
   'ngInject';
 
   function factory(timeout) {
 
     var
       sockets = [],
-      constants = {
-        cache: {
-          LONG: 1 * 60  * 60 * 1000 /*5 min*/,
-          SHORT: csSettings.data.cacheTimeMs
-        }
-      }
+      cachePrefix = 'csHttp'
     ;
 
     if (!timeout) {
@@ -84,41 +79,9 @@ angular.module('cesium.http.services', ['ngResource', 'angular-cache'])
       };
     }
 
-    function getOrCreateCache(maxAge, onExpire){
-      var cacheName = 'csHttp-' + maxAge;
-      if (!onExpire) {
-        return CacheFactory.get(cacheName) ||
-          CacheFactory.createCache(cacheName, {
-            maxAge: maxAge,
-            deleteOnExpire: 'aggressive',
-            //cacheFlushInterval: 60 * 60 * 1000, //  clear itself every hour
-            recycleFreq: Math.max(maxAge - 1000, 5 * 60 * 1000 /*5min*/),
-            storageMode: 'memory'
-              // FIXME : enable this when cache is cleaning on rollback
-              //csSettings.data.useLocalStorage ? 'localStorage' : 'memory'
-          });
-      }
-      else {
-        var counter = 1;
-        while(CacheFactory.get(cacheName + counter)) {
-          counter++;
-        }
-        return CacheFactory.createCache(cacheName + counter, {
-            maxAge: maxAge,
-            deleteOnExpire: 'aggressive',
-            //cacheFlushInterval: 60 * 60 * 1000, // This cache will clear itself every hour
-            recycleFreq: maxAge,
-            onExpire: onExpire,
-            storageMode: 'memory'
-              // FIXME : enable this when cache is cleaning on rollback
-              //csSettings.data.useLocalStorage ? 'localStorage' : 'memory'
-          });
-      }
-    }
-
     function getResourceWithCache(host, port, path, maxAge, autoRefresh) {
       var url = getUrl(host, port, path);
-      maxAge = maxAge || constants.cache.LONG;
+      maxAge = maxAge || csCache.constants.LONG;
       console.debug('[http] will cache ['+url+'] ' + maxAge + 'ms' + (autoRefresh ? ' with auto-refresh' : ''));
 
       return function(params) {
@@ -127,7 +90,7 @@ angular.module('cesium.http.services', ['ngResource', 'angular-cache'])
             timeout: timeout
           };
           if (autoRefresh) { // redo the request if need
-            config.cache = getOrCreateCache(maxAge, function (key, value) {
+            config.cache = csCache.get(cachePrefix, maxAge, function (key, value) {
                 console.debug('[http] Refreshing cache for ['+key+'] ');
                 $http.get(key, config)
                   .success(function (data) {
@@ -136,7 +99,7 @@ angular.module('cesium.http.services', ['ngResource', 'angular-cache'])
               });
           }
           else {
-            config.cache = getOrCreateCache(maxAge);
+            config.cache = csCache.get(cachePrefix, maxAge);
           }
 
           prepare(url, params, config, function(url, config) {
@@ -152,18 +115,6 @@ angular.module('cesium.http.services', ['ngResource', 'angular-cache'])
       };
     }
 
-    function clearAllCache() {
-      console.debug("[http] cleaning all caches");
-      var cache = CacheFactory.get('csHttp-' + constants.cache.SHORT);
-      if (cache) {
-        cache.removeAll();
-      }
-      cache = CacheFactory.get('csHttp-' + constants.cache.LONG);
-      if (cache) {
-        cache.removeAll();
-      }
-    }
-
     function postResource(host, port, path) {
       var url = getUrl(host, port, path);
       return function(data, params) {
@@ -258,11 +209,7 @@ angular.module('cesium.http.services', ['ngResource', 'angular-cache'])
       uri: {
         parse: parseUri
       },
-      cache: {
-        LONG : constants.LONG,
-        SHORT: constants.SHORT,
-        clearAll: clearAllCache
-      }
+      cache: csCache.constants
     };
   }
 
diff --git a/www/js/services/network-services.js b/www/js/services/network-services.js
index e602fc93cbe11211cc6a966b1e04b03a1007a833..3461062d6215a61c32d1a1144bdb1bea25c76ba1 100644
--- a/www/js/services/network-services.js
+++ b/www/js/services/network-services.js
@@ -75,7 +75,7 @@ angular.module('cesium.network.services', ['ngResource', 'ngApi', 'cesium.bma.se
             $interval.cancel(interval);
             console.debug('[network] Finish : all peers found. Stopping new peers check.');
             // The peer lookup end, we can make a clean final report
-            sortPeers();
+            sortPeers(true/*update main buid*/);
           }
         }, 1000);
 
@@ -167,12 +167,12 @@ angular.module('cesium.network.services', ['ngResource', 'ngApi', 'cesium.bma.se
         newPeers = newPeers || data.newPeers;
         if (newPeers.length) {
           data.peers = data.peers.concat(newPeers.splice(0));
-          console.debug('[network] New peers found: sort and add them to result...');
+          console.debug('[network] New peers found: add them to result and sort...');
           sortPeers();
         }
       },
 
-      sortPeers = function() {
+      sortPeers = function(updateMainBuid) {
         // Count peer by current block uid
         var currents = {};
         _.forEach(data.peers, function(peer){
@@ -187,9 +187,8 @@ angular.module('cesium.network.services', ['ngResource', 'ngApi', 'cesium.bma.se
         var mainBlock = _.max(buids, function(obj) {
           return obj.count;
         });
-        data.mainBuid = mainBlock.buid;
         _.forEach(data.peers, function(peer){
-          peer.hasMainConsensusBlock = peer.buid == data.mainBuid;
+          peer.hasMainConsensusBlock = peer.buid == mainBlock.buid;
           peer.hasConsensusBlock = !peer.hasMainConsensusBlock && currents[peer.buid] > 1;
         });
         data.peers = _.uniq(data.peers, false, function(peer) {
@@ -203,6 +202,11 @@ angular.module('cesium.network.services', ['ngResource', 'ngApi', 'cesium.bma.se
           score += (-1       * (peer.uid ? peer.uid.charCodeAt(0) : 999)); // alphabetical order
           return -score;
         });
+        if (updateMainBuid) {
+          // TODO: raise a special event if changed ?
+          data.mainBuid = mainBlock.buid;
+          //api.data.raise.changed(data); // raise event
+        }
         api.data.raise.changed(data); // raise event
       },
 
@@ -311,6 +315,7 @@ angular.module('cesium.network.services', ['ngResource', 'ngApi', 'cesium.bma.se
 
     // Register extension points
     api.registerEvent('data', 'changed');
+    api.registerEvent('data', 'rollback');
 
     return {
       id: id,
diff --git a/www/js/services/wallet-services.js b/www/js/services/wallet-services.js
index dde9d87031dc258d812fe58f2cd1dd33b09743ee..2a33c7358e1abafaf63b963b9333d60820e4a667 100644
--- a/www/js/services/wallet-services.js
+++ b/www/js/services/wallet-services.js
@@ -3,10 +3,10 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
   'cesium.settings.services'])
 
 
-.factory('Wallet', function($q, $rootScope, $timeout, $translate, $filter, Api, localStorage, CryptoUtils, BMA, csSettings, csNetwork, Device) {
+.factory('csWallet', function($q, $rootScope, $timeout, $translate, $filter, Api, localStorage, CryptoUtils, BMA, csSettings) {
   'ngInject';
 
-  Wallet = function(id) {
+  factory = function(id) {
 
     var
     constants = {
@@ -483,7 +483,7 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
           data.tx.pendings = txPendings;
           data.tx.fromTime = fromTime;
           data.tx.toTime = data.tx.history.length ? data.tx.history[0].time /*=max(tx.time)*/: fromTime;
-          console.log('[wallet] TX history loaded in '+ (new Date().getTime()-now) +'ms');
+          console.debug('[wallet] TX history loaded in '+ (new Date().getTime()-now) +'ms');
           resolve();
         })
         .catch(function(err) {
@@ -1412,7 +1412,7 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
     };
   };
 
-  var service = Wallet('default');
+  var service = factory('default');
 
   // try to restore wallet
   csSettings.api.data.on.ready($rootScope, function() {
@@ -1422,6 +1422,6 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
       });
   });
 
-  service.instance = Wallet;
+  service.instance = factory;
   return service;
 });
diff --git a/www/js/services/wot-services.js b/www/js/services/wot-services.js
index 0da07c5e4902880fdcf5d69cd318696de022c032..606d3d1bcdc519e5074b56a48ad0c451fec9ea20 100644
--- a/www/js/services/wot-services.js
+++ b/www/js/services/wot-services.js
@@ -2,138 +2,188 @@
 angular.module('cesium.wot.services', ['ngResource', 'ngApi', 'cesium.bma.services', 'cesium.crypto.services', 'cesium.utils.services',
   'cesium.settings.services'])
 
-.factory('WotService', function($q, $timeout, BMA, Api, csSettings) {
+.factory('csWot', function($q, $timeout, BMA, Api, CacheFactory, csSettings, csCache) {
   'ngInject';
 
-  WotService = function(id) {
+  factory = function(id) {
 
     var
-    api = new Api(this, "WotService-" + id),
-
-    _sortAndLimitIdentities = function(idties, size) {
-      idties = _.sortBy(idties, function(idty){
-        var score = 1;
-        score += (1000000 * (idty.block));
-        score += (10      * (900 - idty.uid.toLowerCase().charCodeAt(0)));
-        return -score;
-      });
-      if (angular.isDefined(size) && idties.length > size) {
-        idties = idties.slice(0, size); // limit if more than expected size
-      }
-      return idties;
-    },
-
-    loadRequirements = function(pubkey, uid) {
-      return $q(function(resolve, reject) {
-        // Get requirements
-        BMA.wot.requirements({pubkey: pubkey})
-        .then(function(res){
-          if (!res.identities || res.identities.length === 0) {
-            resolve();
-            return;
-          }
-          if (res.identities.length > 1) {
-            res.identities = _.sortBy(res.identities, function(idty) {
-                  var score = 1;
-                  score += (100000000000 * ((uid && idty.uid === uid) ? 1 : 0));
-                  score += (1000000      * idty.membershipExpiresIn);
-                  score += (10           * idty.membershipPendingExpiresIn);
-                  return -score;
-                });
-          }
-          var requirements = res.identities[0];
-          // Add useful custom fields
-          requirements.hasSelf = true;
-          requirements.needMembership = (requirements.membershipExpiresIn === 0 &&
-                                              requirements.membershipPendingExpiresIn <= 0 );
-          requirements.needRenew = !requirements.needMembership && (requirements.membershipExpiresIn <= csSettings.data.timeWarningExpire &&
-                                        requirements.membershipPendingExpiresIn <= 0 );
-          requirements.canMembershipOut = (requirements.membershipExpiresIn > 0);
-          requirements.pendingMembership = (requirements.membershipPendingExpiresIn > 0);
-          requirements.certificationCount = (requirements.certifications) ? requirements.certifications.length : 0;
-          requirements.willExpireCertificationCount = requirements.certifications ? requirements.certifications.reduce(function(count, cert){
-            if (cert.expiresIn <= csSettings.data.timeWarningExpire) {
-              return count + 1;
-            }
-            return count;
-          }, 0) : 0;
-          requirements.isMember = !requirements.needMembership && !requirements.pendingMembership;
-          resolve(requirements);
-        })
-        .catch(function(err) {
-          // If not a member: continue
-          if (!!err &&
-              (err.ucode == BMA.errorCodes.NO_MATCHING_MEMBER ||
-               err.ucode == BMA.errorCodes.NO_IDTY_MATCHING_PUB_OR_UID)) {
-            resolve({
-              hasSelf: false,
-              needMembership: true,
-              canMembershipOut: false,
-              needRenew: false,
-              pendingMembership: false,
-              certificationCount: 0,
-              certifications: [],
-              needCertifications: false,
-              needCertificationCount: 0,
-              willNeedCertificationCount: 0
-            });
-          }
-          else {
-            reject(err);
-          }
+      api = new Api(this, "csWot-" + id),
+      identityCache = csCache.get('csWot-idty-', csCache.constants.SHORT),
+
+      _sortAndLimitIdentities = function(idties, size) {
+        idties = _.sortBy(idties, function(idty){
+          var score = 1;
+          score += (1000000 * (idty.block));
+          score += (10      * (900 - idty.uid.toLowerCase().charCodeAt(0)));
+          return -score;
         });
-      });
-    },
-
-    loadIdentity = function(pubkey, requirements, parameters) {
-      return $q(function(resolve, reject) {
-        BMA.wot.lookup({ search: pubkey })
-        .then(function(res){
-          var identities = res.results.reduce(function(idties, res) {
-            return idties.concat(res.uids.reduce(function(uids, idty) {
-              var blockUid = idty.meta.timestamp.split('-', 2);
-              return uids.concat({
-                uid: idty.uid,
-                pubkey: res.pubkey,
-                timestamp: idty.meta.timestamp,
-                number: parseInt(blockUid[0]),
-                hash: blockUid[1],
-                revoked: idty.revoked,
-                revokedSig: idty.revocation_sig,
-                sig: idty.self
+        if (angular.isDefined(size) && idties.length > size) {
+          idties = idties.slice(0, size); // limit if more than expected size
+        }
+        return idties;
+      },
+
+      loadRequirements = function(pubkey, uid) {
+        return $q(function(resolve, reject) {
+          // Get requirements
+          BMA.wot.requirements({pubkey: pubkey})
+          .then(function(res){
+            if (!res.identities || res.identities.length === 0) {
+              resolve();
+              return;
+            }
+            if (res.identities.length > 1) {
+              res.identities = _.sortBy(res.identities, function(idty) {
+                    var score = 1;
+                    score += (100000000000 * ((uid && idty.uid === uid) ? 1 : 0));
+                    score += (1000000      * idty.membershipExpiresIn);
+                    score += (10           * idty.membershipPendingExpiresIn);
+                    return -score;
+                  });
+            }
+            var requirements = res.identities[0];
+            // Add useful custom fields
+            requirements.hasSelf = true;
+            requirements.needMembership = (requirements.membershipExpiresIn === 0 &&
+                                                requirements.membershipPendingExpiresIn <= 0 );
+            requirements.needRenew = !requirements.needMembership && (requirements.membershipExpiresIn <= csSettings.data.timeWarningExpire &&
+                                          requirements.membershipPendingExpiresIn <= 0 );
+            requirements.canMembershipOut = (requirements.membershipExpiresIn > 0);
+            requirements.pendingMembership = (requirements.membershipPendingExpiresIn > 0);
+            requirements.certificationCount = (requirements.certifications) ? requirements.certifications.length : 0;
+            requirements.willExpireCertificationCount = requirements.certifications ? requirements.certifications.reduce(function(count, cert){
+              if (cert.expiresIn <= csSettings.data.timeWarningExpire) {
+                return count + 1;
+              }
+              return count;
+            }, 0) : 0;
+            requirements.isMember = !requirements.needMembership && !requirements.pendingMembership;
+            resolve(requirements);
+          })
+          .catch(function(err) {
+            // If not a member: continue
+            if (!!err &&
+                (err.ucode == BMA.errorCodes.NO_MATCHING_MEMBER ||
+                 err.ucode == BMA.errorCodes.NO_IDTY_MATCHING_PUB_OR_UID)) {
+              resolve({
+                hasSelf: false,
+                needMembership: true,
+                canMembershipOut: false,
+                needRenew: false,
+                pendingMembership: false,
+                certificationCount: 0,
+                certifications: [],
+                needCertifications: false,
+                needCertificationCount: 0,
+                willNeedCertificationCount: 0
               });
-            }, []));
-          }, []);
-          // Choose the more updated identity
-          var identity = identities.length == 1 ?
-            identities[0] :
-            _.sortBy(identities, 'number')[identities.length-1];
-
-          identity.hasSelf = !!(identity.uid && identity.timestamp && identity.sig);
-
-          // Retrieve certifications
-          var expiresInByPub = requirements.certifications.reduce(function(map, cert){
-            map[cert.from]=cert.expiresIn;
-            return map;
-          }, {});
-          var certPubkeys = [];
-          identity.hasPendingCertifications = false;
-          var certifications = !res.results ? [] : res.results.reduce(function(certs, res) {
-            return certs.concat(res.uids.reduce(function(certs, idty) {
-              return certs.concat(idty.others.reduce(function(certs, cert) {
-                var expiresIn = cert.isMember ? expiresInByPub[cert.pubkey] : null;
+            }
+            else {
+              reject(err);
+            }
+          });
+        });
+      },
+
+      loadIdentity = function(pubkey, requirements, parameters, medianTime) {
+        return $q(function(resolve, reject) {
+          BMA.wot.lookup({ search: pubkey })
+          .then(function(res){
+            var identities = res.results.reduce(function(idties, res) {
+              return idties.concat(res.uids.reduce(function(uids, idty) {
+                var blockUid = idty.meta.timestamp.split('-', 2);
+                return uids.concat({
+                  uid: idty.uid,
+                  pubkey: res.pubkey,
+                  timestamp: idty.meta.timestamp,
+                  number: parseInt(blockUid[0]),
+                  hash: blockUid[1],
+                  revoked: idty.revoked,
+                  revokedSig: idty.revocation_sig,
+                  sig: idty.self
+                });
+              }, []));
+            }, []);
+            // Choose the more updated identity
+            var identity = identities.length == 1 ?
+              identities[0] :
+              _.sortBy(identities, 'number')[identities.length-1];
+
+            identity.hasSelf = !!(identity.uid && identity.timestamp && identity.sig);
+
+            // Retrieve certifications
+            var expiresInByPub = requirements.certifications.reduce(function(map, cert){
+              map[cert.from]=cert.expiresIn;
+              return map;
+            }, {});
+            var certPubkeys = [];
+            var pendingCertBlocks = {};
+            identity.hasPendingCertifications = false;
+            var certifications = !res.results ? [] : res.results.reduce(function(certs, res) {
+              return certs.concat(res.uids.reduce(function(certs, idty) {
+                return certs.concat(idty.others.reduce(function(certs, cert) {
+                  var expiresIn = cert.isMember ? expiresInByPub[cert.pubkey] : null;
+                  var certTime = expiresIn ? (medianTime - parameters.sigValidity + expiresIn) : null;
+                  var result = {
+                    pubkey: cert.pubkey,
+                    uid: cert.uids[0],
+                    time: certTime,
+                    block: (cert.meta && cert.meta.block_number) ? cert.meta.block_number : 0,
+                    expiresIn: expiresIn,
+                    willExpire: (expiresIn && expiresIn <= csSettings.data.timeWarningExpire),
+                    pending: !expiresIn,
+                    isMember: cert.isMember
+                  };
+                  if (result.pending) {
+                    identity.hasPendingCertifications = true;
+                    if (!pendingCertBlocks[result.block]) {
+                      pendingCertBlocks[result.block] = [result];
+                    }
+                    else {
+                      pendingCertBlocks[result.block].push(result);
+                    }
+                  }
+                  if (!certPubkeys[cert.pubkey]) {
+                    certPubkeys[cert.pubkey] = result;
+                  }
+                  else { // if duplicated cert: keep the most recent
+                    if (result.block > certPubkeys[cert.pubkey].block) {
+                      certPubkeys[cert.pubkey] = result;
+                    }
+                    else {
+                      return certs; // skip this result
+                    }
+                  }
+                  return certs.concat(result);
+                }, certs));
+              }, certs));
+            }, []);
+            identity.certifications = _.sortBy(certifications, function(cert){
+              var score = 1;
+              score += (1000000000000 * (cert.expiresIn ? cert.expiresIn : 0));
+              score += (10000000      * (cert.isMember ? 1 : 0));
+              score += (10            * (cert.block ? cert.block : 0));
+              return -score;
+            });
+            identity.certificationCount = requirements.certificationCount;
+            identity.isMember = requirements.isMember;
+            delete requirements.certifications;
+            delete requirements.certificationCount;
+
+            // Store given certs
+            certPubkeys = [];
+            var givenCertifications = !res.results ? [] : res.results.reduce(function(certs, res) {
+              return certs.concat(res.signed.reduce(function(certs, cert) {
+                var blockUid = cert.meta ? cert.meta.timestamp.split('-', 2) : [null, null];
                 var result = {
                   pubkey: cert.pubkey,
-                  uid: cert.uids[0],
-                  block: (cert.meta && cert.meta.block_number) ? cert.meta.block_number : 0,
-                  expiresIn: expiresIn,
-                  willExpire: (expiresIn && expiresIn <= csSettings.data.timeWarningExpire),
-                  pending: !expiresIn,
-                  isMember: cert.isMember
+                  uid: cert.uid,
+                  block: blockUid[0],
+                  hash: blockUid[1],
+                  isMember: cert.isMember,
+                  wasMember: cert.wasMember
                 };
-                if (result.pending && !identity.hasPendingCertifications) {
-                  identity.hasPendingCertifications = true;
-                }
                 if (!certPubkeys[cert.pubkey]) {
                   certPubkeys[cert.pubkey] = result;
                 }
@@ -147,120 +197,113 @@ angular.module('cesium.wot.services', ['ngResource', 'ngApi', 'cesium.bma.servic
                 }
                 return certs.concat(result);
               }, certs));
-            }, certs));
-          }, []);
-          identity.certifications = _.sortBy(certifications, function(cert){
-            var score = 1;
-            score += (1000000000000 * (cert.expiresIn ? cert.expiresIn : 0));
-            score += (10000000      * (cert.isMember ? 1 : 0));
-            score += (10            * (cert.block ? cert.block : 0));
-            return -score;
-          });
-          identity.certificationCount = requirements.certificationCount;
-          identity.isMember = requirements.isMember;
-          delete requirements.certifications;
-          delete requirements.certificationCount;
-
-          // Store given certs
-          certPubkeys = [];
-          var givenCertifications = !res.results ? [] : res.results.reduce(function(certs, res) {
-            return certs.concat(res.signed.reduce(function(certs, cert) {
-              if (!certPubkeys[cert.pubkey]) { // skip duplicated certs
-                certPubkeys[cert.pubkey] = true;
-                var blockUid = cert.meta ? cert.meta.timestamp.split('-', 2) : [null, null];
-                return certs.concat({
-                  pubkey: cert.pubkey,
-                  uid: cert.uid,
-                  block: blockUid[0],
-                  hash: blockUid[1],
-                  isMember: cert.isMember,
-                  wasMember: cert.wasMember
+            }, []);
+            identity.temp = {
+              givenCertifications: givenCertifications // will be used in loadGivenCertifications()
+            };
+            identity.sigQty =  parameters.sigQty;
+            identity.sigStock =  parameters.sigStock;
+
+            var blocks = [identity.number].concat(_.keys(pendingCertBlocks));
+
+            // Retrieve block time, from number
+            return BMA.blockchain.blocks(_.uniq(blocks)).then(function(blocks){
+              _.forEach(blocks, function(block){
+                // Retrieve self time
+                if (identity.number && block.number === identity.number) {
+                  identity.sigDate = block.time;
+
+                  // Check if self has been done on a valid block
+                  if (!identity.isMember && identity.number !== 0 && identity.hash !== block.hash) {
+                    addEvent(identity, {type: 'error', message: 'ERROR.IDENTITY_INVALID_BLOCK_HASH'});
+                    console.debug("Invalid membership for uid={0}: block hash not match a real block (block cancelled)".format(identity.uid));
+                  }
+                }
+
+                // Set time of pending certs
+                _.forEach(pendingCertBlocks[block.number], function(cert) {
+                  cert.time = block.medianTime;
+                  cert.expiresIn = Math.max(0, cert.time + parameters.sigWindow - medianTime);
+                  cert.valid = (cert.expiresIn > 0);
                 });
+              });
+
+              resolve(identity);
+            })
+            .catch(function(err){
+              // Special case for currency init (root block not exists): use now
+              if (err && err.ucode == BMA.errorCodes.BLOCK_NOT_FOUND && identity.number === '0') {
+                identity.sigDate = Math.trunc(new Date().getTime() / 1000);
+                resolve(identity);
               }
-              return certs;
-            }, certs));
-          }, []);
-          identity.temp = {
-            givenCertifications: givenCertifications
-          };
-          identity.sigQty =  parameters.sigQty;
-          identity.sigStockMax =  parameters.sigStock;
-
-          // Retrieve registration date
-          return BMA.blockchain.block({block: identity.number})
-            .then(function(block) {
-            identity.sigDate = block.time;
-
-            // Check if self has been done on a valid block
-            if (!identity.isMember && identity.number !== 0 && identity.hash !== block.hash) {
-              addEvent(identity, {type: 'error', message: 'ERROR.IDENTITY_INVALID_BLOCK_HASH'});
-              console.debug("Invalid membership for uid={0}: block hash not match a real block (block cancelled)".format(identity.uid));
-            }
-            resolve(identity);
+              else {
+                reject(err);
+              }
+            });
           })
-          .catch(function(err){
-            // Special case for currency init (root block not exists): use now
-            if (err && err.ucode == BMA.errorCodes.BLOCK_NOT_FOUND && identity.number === '0') {
-              identity.sigDate = Math.trunc(new Date().getTime() / 1000);
+          .catch(function(err) {
+            if (!!err && err.ucode == BMA.errorCodes.NO_MATCHING_IDENTITY) { // Identity not found (if no self)
+              var identity = {
+                uid: null,
+                pubkey: pubkey,
+                hasSelf: false
+              };
               resolve(identity);
             }
             else {
               reject(err);
             }
           });
-        })
-        .catch(function(err) {
-          if (!!err && err.ucode == BMA.errorCodes.NO_MATCHING_IDENTITY) { // Identity not found (if no self)
-            var identity = {
-              uid: null,
-              pubkey: pubkey,
-              hasSelf: false
-            };
-            resolve(identity);
-          }
-          else {
-            reject(err);
-          }
         });
-      });
-    },
+      },
 
       loadGivenCertifications = function(pubkey, lookupGivenCertifications, parameters, medianTime) {
         return $q(function(resolve, reject) {
 
-          var lookupCertsMap = !lookupGivenCertifications ? {} : lookupGivenCertifications.reduce(function(map, cert){
-            map[cert.pubkey] = cert;
-            return map;
+          var lookupCertsMap = !lookupGivenCertifications ? {} : lookupGivenCertifications.reduce(function(res, cert){
+            res[cert.pubkey] = cert;
+            return res;
           }, {});
 
           BMA.wot.certifiedBy({ pubkey: pubkey })
             .then(function(res){
-              var sigStock = 0;
+              var givenCertificationCount = 0;
+
               var certifications = res.certifications.reduce(function(res, cert) {
                 var certTime = cert.cert_time ? cert.cert_time.medianTime : null;
-                var expiresIn = (cert.written === null || certTime === null) ? 0 : (certTime + parameters.sigValidity - medianTime);
+                var expiresIn = (!cert.written || !certTime) ? 0 : (certTime + parameters.sigValidity - medianTime);
                 expiresIn = (expiresIn < 0) ? 0 : expiresIn;
-                sigStock = (expiresIn > 0) ? sigStock+1 : sigStock;
+                givenCertificationCount = (expiresIn > 0) ? givenCertificationCount+1 : givenCertificationCount;
                 delete lookupCertsMap[cert.pubkey];
-                return res.concat({
-                  isMember: cert.isMember,
-                  wasMember: cert.wasMember,
-                  uid: cert.uid,
+                var result = {
                   pubkey: cert.pubkey,
+                  uid: cert.uid,
                   time: certTime,
+                  isMember: cert.isMember,
+                  wasMember: cert.wasMember,
                   expiresIn: expiresIn,
-                  valid: (expiresIn > 0),
+                  willExpire: (expiresIn && expiresIn <= csSettings.data.timeWarningExpire),
+                  pending: false,
                   block: (cert.written !== null) ? cert.written.number :
                     (cert.cert_time ? cert.cert_time.block : null)
-                });
+                };
+                return res.concat(result);
               }, []);
 
-              // Add missing certs found in lookup (e.g. not written certs)
-              certifications = _.keys(lookupCertsMap).reduce(function(res, pubkey){
-                var cert = lookupCertsMap[pubkey];
-                cert.valid = false;
-                return res.concat(cert);
-              }, certifications);
+              // Add pending certs (found in lookup - see loadIdentity())
+              var pendingCertBlocks = {};
+              var pendingCertifications = _.forEach(_.values(lookupCertsMap), function(cert){
+                cert.pending = true;
+                if (!pendingCertBlocks[cert.block]) {
+                  pendingCertBlocks[cert.block] = [cert];
+                }
+                else {
+                  pendingCertBlocks[cert.block].push(cert);
+                }
+              });
+
+              var hasPendingGivenCertifications = pendingCertifications.length > 0;
+              certifications = certifications.concat(pendingCertifications);
 
               certifications = _.sortBy(certifications, function(cert){
                 var score = 1;
@@ -270,15 +313,35 @@ angular.module('cesium.wot.services', ['ngResource', 'ngApi', 'cesium.bma.servic
                 return -score;
               });
 
-              resolve({
-                sigStock: sigStock,
+              var result = {
+                givenCertificationCount: givenCertificationCount,
+                hasPendingGivenCertifications: hasPendingGivenCertifications,
                 givenCertifications: certifications
-              });
+              };
+              var pendingCertBlockNumbers = _.keys(pendingCertBlocks);
+              if (!pendingCertBlockNumbers.length) {
+                resolve(result);
+              }
+              else {
+                // Add time to pending cert
+                BMA.blockchain.blocks(_.uniq(pendingCertBlockNumbers)).then(function(blocks){
+                  _.forEach(blocks, function(block){
+                    _.forEach(pendingCertBlocks[block.number], function(cert) {
+                      cert.time = block.medianTime;
+                      cert.expiresIn = Math.max(0, cert.time + parameters.sigWindow - medianTime);
+                      cert.valid = (cert.expiresIn > 0);
+                    });
+                  });
+
+                  resolve(result);
+                });
+              }
             })
             .catch(function(err) {
               if (!!err && err.ucode == BMA.errorCodes.NO_MATCHING_MEMBER) { // member not found
                 resolve({
-                  sigStock: 0,
+                  givenCertificationCount: 0,
+                  hasPendingGivenCertifications: false,
                   givenCertifications: []
                 });
               }
@@ -289,40 +352,47 @@ angular.module('cesium.wot.services', ['ngResource', 'ngApi', 'cesium.bma.servic
         });
       },
 
-    loadSources = function(pubkey) {
-      return $q(function(resolve, reject) {
-        // Get transactions
-        BMA.tx.sources({pubkey: pubkey})
-        .then(function(res){
-          var sources = [];
-          var sourcesIndexByKey = [];
-          var balance = 0;
-          if (!!res.sources && res.sources.length > 0) {
-            _.forEach(res.sources, function(src) {
-              var srcKey = src.type+':'+src.identifier+':'+src.noffset;
-              src.consumed = false;
-              balance += (src.base > 0) ? (src.amount * Math.pow(10, src.base)) : src.amount;
-              sources.push(src);
-              sourcesIndexByKey[srcKey] = sources.length -1 ;
+      loadSources = function(pubkey) {
+        return $q(function(resolve, reject) {
+          // Get transactions
+          BMA.tx.sources({pubkey: pubkey})
+          .then(function(res){
+            var sources = [];
+            var sourcesIndexByKey = [];
+            var balance = 0;
+            if (!!res.sources && res.sources.length > 0) {
+              _.forEach(res.sources, function(src) {
+                var srcKey = src.type+':'+src.identifier+':'+src.noffset;
+                src.consumed = false;
+                balance += (src.base > 0) ? (src.amount * Math.pow(10, src.base)) : src.amount;
+                sources.push(src);
+                sourcesIndexByKey[srcKey] = sources.length -1 ;
+              });
+            }
+            resolve({
+              sources: sources,
+              sourcesIndexByKey: sourcesIndexByKey,
+              balance: balance
             });
-          }
-          resolve({
-            sources: sources,
-            sourcesIndexByKey: sourcesIndexByKey,
-            balance: balance
+          })
+          .catch(function(err) {
+            reject(err);
           });
-        })
-        .catch(function(err) {
-          reject(err);
         });
-      });
-    },
+      },
 
-    loadData = function(pubkey, uid) {
+      loadData = function(pubkey, withCache, uid) {
         return $q(function(resolve, reject){
-          var data = {
-            pubkey: pubkey
-          };
+          // Check cached data
+          var data = withCache ? identityCache.get(pubkey) : null;
+          if (data) {
+            console.debug("[wot] Found cached identity " + pubkey.substring(0, 8));
+            resolve(data);
+            return;
+          }
+          console.debug("[wot] Loading identity " + pubkey.substring(0, 8));
+          var now = new Date().getTime();
+          data = {pubkey: pubkey};
 
           var parameters;
           var medianTime;
@@ -355,7 +425,7 @@ angular.module('cesium.wot.services', ['ngResource', 'ngApi', 'cesium.bma.servic
                   data.requirements = requirements;
 
                   // Get identity
-                  return loadIdentity(pubkey, requirements, parameters)
+                  return loadIdentity(pubkey, requirements, parameters, medianTime)
                     .then(function (identity) {
                       angular.merge(data, identity);
 
@@ -378,282 +448,284 @@ angular.module('cesium.wot.services', ['ngResource', 'ngApi', 'cesium.bma.servic
             ]);
           })
           .then(function() {
+            identityCache.put(pubkey, data); // add to cache
+            console.debug('[wallet] Identity '+ pubkey.substring(0, 8) +' loaded in '+ (new Date().getTime()-now) +'ms');
             resolve(data);
           })
           .catch(function(err) {
             reject(err);
           });
         });
-    },
+      },
 
-    search = function(text) {
-      return $q(function(resolve, reject) {
-        if (!text || text.trim() !== text) {
-          resolve();
-        }
-        return BMA.wot.lookup({ search: text })
-          .then(function(res){
-            var idtyKeys = [];
-            var idties = res.results.reduce(function(idties, res) {
-              return idties.concat(res.uids.reduce(function(uids, idty) {
-                var blocUid = idty.meta.timestamp.split('-', 2);
-                var idtyKey = idty.uid + '-' + res.pubkey;
-                if (!idtyKeys[idtyKey] && !idty.revoked) {
-                  idtyKeys[idtyKey] = true;
-                  return uids.concat({
-                    uid: idty.uid,
-                    pubkey: res.pubkey,
-                    number: blocUid[0],
-                    hash: blocUid[1]
-                  });
-                }
-                return uids;
-              }, []));
-            }, []);
+      search = function(text) {
+        return $q(function(resolve, reject) {
+          if (!text || text.trim() !== text) {
+            resolve();
+          }
+          return BMA.wot.lookup({ search: text })
+            .then(function(res){
+              var idtyKeys = [];
+              var idties = res.results.reduce(function(idties, res) {
+                return idties.concat(res.uids.reduce(function(uids, idty) {
+                  var blocUid = idty.meta.timestamp.split('-', 2);
+                  var idtyKey = idty.uid + '-' + res.pubkey;
+                  if (!idtyKeys[idtyKey] && !idty.revoked) {
+                    idtyKeys[idtyKey] = true;
+                    return uids.concat({
+                      uid: idty.uid,
+                      pubkey: res.pubkey,
+                      number: blocUid[0],
+                      hash: blocUid[1]
+                    });
+                  }
+                  return uids;
+                }, []));
+              }, []);
 
-            api.data.raisePromise.search(text, idties)
-            .then(function() {
-              resolve(idties);
-            });
-          })
-          .catch(function(err) {
-            if (err && err.ucode == BMA.errorCodes.NO_MATCHING_IDENTITY) {
-              var idties = [];
               api.data.raisePromise.search(text, idties)
               .then(function() {
                 resolve(idties);
               });
-            }
-            else {
-              reject(err);
-            }
-          });
-      });
-    },
-
-    getNewcomers = function(size) {
-      size = size || 20;
-      return BMA.blockchain.stats.newcomers()
-        .then(function(res) {
-          if (!res.result.blocks || !res.result.blocks.length) {
-            return null;
-          }
-          var blocks = _.sortBy(res.result.blocks, function(n){ return -n; });
-          return getNewcomersRecursive(blocks, 0, 5, size)
-            .then(function(idties){
-              if (!idties || !idties.length) {
-                return null;
+            })
+            .catch(function(err) {
+              if (err && err.ucode == BMA.errorCodes.NO_MATCHING_IDENTITY) {
+                var idties = [];
+                api.data.raisePromise.search(text, idties)
+                .then(function() {
+                  resolve(idties);
+                });
+              }
+              else {
+                reject(err);
               }
-              idties = _sortAndLimitIdentities(idties, size);
-              return $q(function(resolve, reject) {
-                api.data.raisePromise.search(null, idties)
-                  .then(function () {
-                    resolve(idties);
-                  })
-                  .catch(function(err){
-                    reject(err);
-                  });
-              });
             });
         });
-    },
-
-
-    getNewcomersRecursive = function(blocks, offset, size, maxResultSize) {
-      return $q(function(resolve, reject) {
-        var result = [];
-        var jobs = [];
-        _.each(blocks.slice(offset, offset+size), function(number) {
-          jobs.push(
-            BMA.blockchain.block({block: number})
-              .then(function(block){
-                if (!block || !block.joiners) return;
-                _.each(block.joiners, function(joiner){
-                  var parts = joiner.split(':');
-                  result.push({
-                    pubkey:parts[0],
-                    uid: parts[parts.length-1],
-                    memberDate: block.medianTime,
-                    block: block.number
-                  });
+      },
+
+      getNewcomers = function(size) {
+        size = size || 20;
+        return BMA.blockchain.stats.newcomers()
+          .then(function(res) {
+            if (!res.result.blocks || !res.result.blocks.length) {
+              return null;
+            }
+            var blocks = _.sortBy(res.result.blocks, function(n){ return -n; });
+            return getNewcomersRecursive(blocks, 0, 5, size)
+              .then(function(idties){
+                if (!idties || !idties.length) {
+                  return null;
+                }
+                idties = _sortAndLimitIdentities(idties, size);
+                return $q(function(resolve, reject) {
+                  api.data.raisePromise.search(null, idties)
+                    .then(function () {
+                      resolve(idties);
+                    })
+                    .catch(function(err){
+                      reject(err);
+                    });
                 });
-              })
-          );
-        });
+              });
+          });
+      },
 
-        $q.all(jobs)
-          .then(function() {
-            if (result.length < maxResultSize && offset < blocks.length - 1) {
-              $timeout(function() {
-                getNewcomersRecursive(blocks, offset+size, size, maxResultSize - result.length)
-                  .then(function(res) {
-                    resolve(result.concat(res));
-                  })
-                  .catch(function(err) {
-                    reject(err);
+
+      getNewcomersRecursive = function(blocks, offset, size, maxResultSize) {
+        return $q(function(resolve, reject) {
+          var result = [];
+          var jobs = [];
+          _.each(blocks.slice(offset, offset+size), function(number) {
+            jobs.push(
+              BMA.blockchain.block({block: number})
+                .then(function(block){
+                  if (!block || !block.joiners) return;
+                  _.each(block.joiners, function(joiner){
+                    var parts = joiner.split(':');
+                    result.push({
+                      pubkey:parts[0],
+                      uid: parts[parts.length-1],
+                      memberDate: block.medianTime,
+                      block: block.number
+                    });
                   });
-              }, 1000);
-            }
-            else {
-              resolve(result);
-            }
-          })
-          .catch(function(err){
-            if (err && err.ucode === BMA.errorCodes.HTTP_LIMITATION) {
-              resolve(result);
-            }
-            else {
-              reject(err);
-            }
+                })
+            );
           });
-      });
-    },
-
-    getPending = function(size) {
-      size = size || 20;
-      return BMA.wot.member.pending()
-        .then(function(res) {
-          if (!res.memberships || !res.memberships.length) {
-            return null;
-          }
-          var idtiesByBlock = {};
-          var idtiesByPubkey = {};
-          var idties = [];
-          _.forEach(res.memberships, function(ms){
-            if (ms.membership == 'IN') {
-              var idty = {
-                uid: ms.uid,
-                pubkey: ms.pubkey,
-                block: ms.blockNumber,
-                blockHash: ms.blockHash
-              };
-              var otherIdtySamePubkey = idtiesByPubkey[ms.pubkey];
-              if (otherIdtySamePubkey && idty.block > otherIdtySamePubkey.block) {
-                return; // skip
-              }
-              idtiesByPubkey[idty.pubkey] = idty;
-              if (!idtiesByBlock[idty.block]) {
-                idtiesByBlock[idty.block] = [idty];
+
+          $q.all(jobs)
+            .then(function() {
+              if (result.length < maxResultSize && offset < blocks.length - 1) {
+                $timeout(function() {
+                  getNewcomersRecursive(blocks, offset+size, size, maxResultSize - result.length)
+                    .then(function(res) {
+                      resolve(result.concat(res));
+                    })
+                    .catch(function(err) {
+                      reject(err);
+                    });
+                }, 1000);
               }
               else {
-                idtiesByBlock[idty.block].push(idty);
+                resolve(result);
               }
-
-              // Remove previous idty from maps
-              if (otherIdtySamePubkey) {
-                idtiesByBlock[otherIdtySamePubkey.block] = idtiesByBlock[otherIdtySamePubkey.block].reduce(function(res, aidty){
-                  if (aidty.pubkey == otherIdtySamePubkey.pubkey) return res; // if match idty to remove, to NOT add
-                  return (res||[]).concat(aidty);
-                }, null);
-                if (idtiesByBlock[otherIdtySamePubkey.block] === null) {
-                  delete idtiesByBlock[otherIdtySamePubkey.block];
-                }
-                return;
+            })
+            .catch(function(err){
+              if (err && err.ucode === BMA.errorCodes.HTTP_LIMITATION) {
+                resolve(result);
               }
               else {
-                idties.push(idty);
+                reject(err);
               }
-            }
-          });
-          idties = _sortAndLimitIdentities(idtiesByPubkey, size);
+            });
+        });
+      },
 
-          return BMA.blockchain.blocks(_.keys(idtiesByBlock))
-            .then(function(blocks) {
+      getPending = function(size) {
+        size = size || 20;
+        return BMA.wot.member.pending()
+          .then(function(res) {
+            if (!res.memberships || !res.memberships.length) {
+              return null;
+            }
+            var idtiesByBlock = {};
+            var idtiesByPubkey = {};
+            var idties = [];
+            _.forEach(res.memberships, function(ms){
+              if (ms.membership == 'IN') {
+                var idty = {
+                  uid: ms.uid,
+                  pubkey: ms.pubkey,
+                  block: ms.blockNumber,
+                  blockHash: ms.blockHash
+                };
+                var otherIdtySamePubkey = idtiesByPubkey[ms.pubkey];
+                if (otherIdtySamePubkey && idty.block > otherIdtySamePubkey.block) {
+                  return; // skip
+                }
+                idtiesByPubkey[idty.pubkey] = idty;
+                if (!idtiesByBlock[idty.block]) {
+                  idtiesByBlock[idty.block] = [idty];
+                }
+                else {
+                  idtiesByBlock[idty.block].push(idty);
+                }
 
-              _.forEach(blocks, function(block){
-                _.forEach(idtiesByBlock[block.number], function(idty) {
-                  idty.sigDate = block.medianTime;
-                  if (block.number !== 0 && idty.blockHash !== block.hash) {
-                    addEvent(idty, {type:'error', message: 'ERROR.WOT_PENDING_INVALID_BLOCK_HASH'});
-                    console.debug("Invalid membership for uid={0}: block hash not match a real block (block cancelled)".format(idty.uid));
+                // Remove previous idty from maps
+                if (otherIdtySamePubkey) {
+                  idtiesByBlock[otherIdtySamePubkey.block] = idtiesByBlock[otherIdtySamePubkey.block].reduce(function(res, aidty){
+                    if (aidty.pubkey == otherIdtySamePubkey.pubkey) return res; // if match idty to remove, to NOT add
+                    return (res||[]).concat(aidty);
+                  }, null);
+                  if (idtiesByBlock[otherIdtySamePubkey.block] === null) {
+                    delete idtiesByBlock[otherIdtySamePubkey.block];
                   }
-                });
-              });
-              return $q(function(resolve, reject) {
-                api.data.raisePromise.search(null, idties)
-                  .then(function () {
-                    resolve(idties);
-                  })
-                  .catch(function(err){
-                    reject(err);
-                  });
-              });
+                  return;
+                }
+                else {
+                  idties.push(idty);
+                }
+              }
             });
-        });
-    },
-
-    getAll = function() {
-      var letters = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','u','v','w','x','y','z'];
-      return getAllRecursive(letters, 0, BMA.constants.LIMIT_REQUEST_COUNT);
-    },
-
-    getAllRecursive = function(letters, offset, size) {
-      return $q(function(resolve, reject) {
-        var result = [];
-        var pubkeys = {};
-        var jobs = [];
-        _.each(letters.slice(offset, offset+size), function(letter) {
-          jobs.push(
-            search(letter)
-              .then(function(idties){
-                if (!idties || !idties.length) return;
-                result = idties.reduce(function(res, idty) {
-                  if (!pubkeys[idty.pubkey]) {
-                    pubkeys[idty.pubkey] = true;
-                    return res.concat(idty);
-                  }
-                  return res;
-                }, result);
-              })
-          );
-        });
+            idties = _sortAndLimitIdentities(idtiesByPubkey, size);
 
-        $q.all(jobs)
-          .then(function() {
-            if (offset < letters.length - 1) {
-              $timeout(function() {
-                getAllRecursive(letters, offset+size, size)
-                  .then(function(idties) {
-                    if (!idties || !idties.length) {
-                      resolve(result);
-                      return;
+            return BMA.blockchain.blocks(_.keys(idtiesByBlock))
+              .then(function(blocks) {
+
+                _.forEach(blocks, function(block){
+                  _.forEach(idtiesByBlock[block.number], function(idty) {
+                    idty.sigDate = block.medianTime;
+                    if (block.number !== 0 && idty.blockHash !== block.hash) {
+                      addEvent(idty, {type:'error', message: 'ERROR.WOT_PENDING_INVALID_BLOCK_HASH'});
+                      console.debug("Invalid membership for uid={0}: block hash not match a real block (block cancelled)".format(idty.uid));
                     }
-                    resolve(idties.reduce(function(res, idty) {
-                      if (!pubkeys[idty.pubkey]) {
-                        pubkeys[idty.pubkey] = true;
-                        return res.concat(idty);
-                      }
-                      return res;
-                    }, result));
-                  })
-                  .catch(function(err) {
-                    reject(err);
                   });
-              }, BMA.constants.LIMIT_REQUEST_DELAY);
-            }
-            else {
-              resolve(result);
-            }
-          })
-          .catch(function(err){
-            if (err && err.ucode === BMA.errorCodes.HTTP_LIMITATION) {
-              resolve(result);
-            }
-            else {
-              reject(err);
-            }
+                });
+                return $q(function(resolve, reject) {
+                  api.data.raisePromise.search(null, idties)
+                    .then(function () {
+                      resolve(idties);
+                    })
+                    .catch(function(err){
+                      reject(err);
+                    });
+                });
+              });
           });
-      });
-    },
-
-    addEvent = function(data, event) {
-      event = event || {};
-      event.type = event.type || 'info';
-      event.message = event.message || '';
-      event.messageParams = event.messageParams || {};
-      data.events = data.events || [];
-      data.events.push(event);
-    }
+      },
+
+      getAll = function() {
+        var letters = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','u','v','w','x','y','z'];
+        return getAllRecursive(letters, 0, BMA.constants.LIMIT_REQUEST_COUNT);
+      },
+
+      getAllRecursive = function(letters, offset, size) {
+        return $q(function(resolve, reject) {
+          var result = [];
+          var pubkeys = {};
+          var jobs = [];
+          _.each(letters.slice(offset, offset+size), function(letter) {
+            jobs.push(
+              search(letter)
+                .then(function(idties){
+                  if (!idties || !idties.length) return;
+                  result = idties.reduce(function(res, idty) {
+                    if (!pubkeys[idty.pubkey]) {
+                      pubkeys[idty.pubkey] = true;
+                      return res.concat(idty);
+                    }
+                    return res;
+                  }, result);
+                })
+            );
+          });
+
+          $q.all(jobs)
+            .then(function() {
+              if (offset < letters.length - 1) {
+                $timeout(function() {
+                  getAllRecursive(letters, offset+size, size)
+                    .then(function(idties) {
+                      if (!idties || !idties.length) {
+                        resolve(result);
+                        return;
+                      }
+                      resolve(idties.reduce(function(res, idty) {
+                        if (!pubkeys[idty.pubkey]) {
+                          pubkeys[idty.pubkey] = true;
+                          return res.concat(idty);
+                        }
+                        return res;
+                      }, result));
+                    })
+                    .catch(function(err) {
+                      reject(err);
+                    });
+                }, BMA.constants.LIMIT_REQUEST_DELAY);
+              }
+              else {
+                resolve(result);
+              }
+            })
+            .catch(function(err){
+              if (err && err.ucode === BMA.errorCodes.HTTP_LIMITATION) {
+                resolve(result);
+              }
+              else {
+                reject(err);
+              }
+            });
+        });
+      },
+
+      addEvent = function(data, event) {
+        event = event || {};
+        event.type = event.type || 'info';
+        event.message = event.message || '';
+        event.messageParams = event.messageParams || {};
+        data.events = data.events || [];
+        data.events.push(event);
+      }
     ;
 
     // Register extension points
@@ -672,8 +744,8 @@ angular.module('cesium.wot.services', ['ngResource', 'ngApi', 'cesium.bma.servic
     };
   };
 
-  var service = WotService('default');
+  var service = factory('default');
 
-  service.instance = WotService;
+  service.instance = factory;
   return service;
 });
diff --git a/www/plugins/es/i18n/locale-fr-FR.json b/www/plugins/es/i18n/locale-fr-FR.json
index a53b4702326fbae807327bec83de62dea3b976ed..bef80391654b276d33a7c0b7ba57793c9c0f8d1b 100644
--- a/www/plugins/es/i18n/locale-fr-FR.json
+++ b/www/plugins/es/i18n/locale-fr-FR.json
@@ -197,8 +197,8 @@
   },
   "PROFILE": {
     "UID": "Pseudonyme",
-    "TITLE": "Nom",
-    "TITLE_HELP": "Nom",
+    "TITLE": "Nom, Prénom",
+    "TITLE_HELP": "Nom, Prénom",
     "DESCRIPTION": "A propos de moi",
     "DESCRIPTION_HELP": "A propos de moi...",
     "ADDRESS": "Rue",
diff --git a/www/plugins/es/js/controllers/app-controllers.js b/www/plugins/es/js/controllers/app-controllers.js
index 8ae31e16f6bd0bbca50cd6f2dd469ccf0554405a..7e533b8c746c6eb7945660b8e96938cee4cf9cc0 100644
--- a/www/plugins/es/js/controllers/app-controllers.js
+++ b/www/plugins/es/js/controllers/app-controllers.js
@@ -113,11 +113,11 @@ function ESMenuExtendController($scope, $state, PluginService, csSettings,UIUtil
 /**
  * Control profile popover extension
  */
-function ESProfilePopoverExtendController($scope, $state, csSettings, Wallet) {
+function ESProfilePopoverExtendController($scope, $state, csSettings, csWallet) {
   'ngInject';
 
   $scope.updateView = function() {
-    $scope.enable = Wallet.isLogin() && (
+    $scope.enable = csWallet.isLogin() && (
         (csSettings.data.plugins && csSettings.data.plugins.es) ?
           csSettings.data.plugins.es.enable :
           !!csSettings.data.plugins.host);
@@ -127,12 +127,12 @@ function ESProfilePopoverExtendController($scope, $state, csSettings, Wallet) {
     $scope.updateView();
   });
 
-  Wallet.api.data.on.login($scope, function(walletData, resolve){
+  csWallet.api.data.on.login($scope, function(walletData, resolve){
     $scope.updateView();
     if (resolve) resolve();
   });
 
-  Wallet.api.data.on.logout($scope, function(){
+  csWallet.api.data.on.logout($scope, function(){
     $scope.updateView();
   });
 
diff --git a/www/plugins/es/js/controllers/common-controllers.js b/www/plugins/es/js/controllers/common-controllers.js
index 3b34327e317117527eef99b3e505675555750b08..05f7c8a27f05799cbd5b8c31d9ec977db021c4b3 100644
--- a/www/plugins/es/js/controllers/common-controllers.js
+++ b/www/plugins/es/js/controllers/common-controllers.js
@@ -114,7 +114,7 @@ function ESCategoryModalController($scope, UIUtils, $timeout, parameters) {
 
 
 
-function ESCommentsController($scope, $timeout, $filter, $state, Wallet, UIUtils, esHttp, DataService) {
+function ESCommentsController($scope, $timeout, $filter, $state, csWallet, UIUtils, esHttp, DataService) {
   'ngInject';
 
   $scope.maxCommentSize = 10;
@@ -219,7 +219,7 @@ function ESCommentsController($scope, $timeout, $filter, $state, Wallet, UIUtils
     if (!comment || !comment.id) {return;}
     $scope.comments.splice(index, 1);
 
-    DataService.record.comment.remove(comment.id, Wallet.data.keypair)
+    DataService.record.comment.remove(comment.id, csWallet.data.keypair)
     .catch(UIUtils.onError('esMarket.ERROR.FAILED_REMOVE_COMMENT'));
   };
 }
diff --git a/www/plugins/es/js/controllers/market-controllers.js b/www/plugins/es/js/controllers/market-controllers.js
index f95b0419b10cdae5f9ec6a9ef8dd2ae8454f09d9..35d26e586f91813ec4f40750fa46ed629264088f 100644
--- a/www/plugins/es/js/controllers/market-controllers.js
+++ b/www/plugins/es/js/controllers/market-controllers.js
@@ -417,7 +417,7 @@ function ESMarketLookupController($scope, $state, $focus, $timeout, $filter, $q,
 
 function ESMarketRecordViewController($scope, $anchorScroll, $ionicPopover, $state, $translate, $ionicHistory, $q,
                                       $timeout, $filter,
-                                      Wallet, esMarket, UIUtils,  esHttp, esUser, BMA, csSettings) {
+                                      csWallet, esMarket, UIUtils,  esHttp, esUser, BMA, csSettings) {
   'ngInject';
 
   $scope.formData = {};
@@ -428,7 +428,7 @@ function ESMarketRecordViewController($scope, $anchorScroll, $ionicPopover, $sta
   $scope.maxCommentSize = 10;
   $scope.loading = true;
 
-  ESCommentsController.call(this, $scope, $timeout, $filter, $state, Wallet, UIUtils, esHttp, esMarket);
+  ESCommentsController.call(this, $scope, $timeout, $filter, $state, csWallet, UIUtils, esHttp, esMarket);
 
   $scope.$on('$ionicView.enter', function (e, state) {
     if (state.stateParams && state.stateParams.id) { // Load by id
@@ -467,7 +467,7 @@ function ESMarketRecordViewController($scope, $anchorScroll, $ionicPopover, $sta
       if (hit._source.thumbnail) {
         $scope.thumbnail = UIUtils.image.fromAttachment(hit._source.thumbnail);
       }
-      $scope.canEdit = $scope.formData && Wallet.isUserPubkey($scope.formData.issuer);
+      $scope.canEdit = $scope.formData && csWallet.isUserPubkey($scope.formData.issuer);
       return esUser.profile.fillAvatars([{pubkey: $scope.formData.issuer}])
         .then(function(idties) {
           return idties[0];
diff --git a/www/plugins/es/js/controllers/message-controllers.js b/www/plugins/es/js/controllers/message-controllers.js
index 073571ea7539d20a9e1830728f3fc82886b39c1c..684422e5f450428aaa9ba3aff7c52e29c5bd0e10 100644
--- a/www/plugins/es/js/controllers/message-controllers.js
+++ b/www/plugins/es/js/controllers/message-controllers.js
@@ -185,10 +185,10 @@ function ESMessageInboxController($scope, $rootScope, $state, $timeout, $transla
 }
 
 
-function ESMessageComposeController($scope,  $ionicHistory, Modals, UIUtils, CryptoUtils, Wallet, esHttp, esMessage) {
+function ESMessageComposeController($scope,  $ionicHistory, Modals, UIUtils, CryptoUtils, csWallet, esHttp, esMessage) {
   'ngInject';
 
-  ESMessageComposeModalController.call(this, $scope, Modals, UIUtils, CryptoUtils, Wallet, esHttp, esMessage);
+  ESMessageComposeModalController.call(this, $scope, Modals, UIUtils, CryptoUtils, csWallet, esHttp, esMessage);
 
   $scope.$on('$ionicView.enter', function(e, state) {
     if (!!state.stateParams && !!state.stateParams.pubkey) {
@@ -227,7 +227,7 @@ function ESMessageComposeController($scope,  $ionicHistory, Modals, UIUtils, Cry
 
 }
 
-function ESMessageComposeModalController($scope, Modals, UIUtils, CryptoUtils, Wallet, esHttp, esMessage, parameters) {
+function ESMessageComposeModalController($scope, Modals, UIUtils, CryptoUtils, csWallet, esHttp, esMessage, parameters) {
   'ngInject';
 
   $scope.formData = {
@@ -257,7 +257,7 @@ function ESMessageComposeModalController($scope, Modals, UIUtils, CryptoUtils, W
 
     UIUtils.loading.show();
     var data = {
-      issuer: Wallet.data.pubkey,
+      issuer: csWallet.data.pubkey,
       recipient: $scope.formData.destPub,
       title: $scope.formData.title,
       content: $scope.formData.content,
@@ -265,7 +265,7 @@ function ESMessageComposeModalController($scope, Modals, UIUtils, CryptoUtils, W
       nonce: CryptoUtils.util.random_nonce()
     };
 
-    esMessage.send(data, Wallet.data.keypair)
+    esMessage.send(data, csWallet.data.keypair)
       .then(function(id) {
         $scope.id=id;
         UIUtils.loading.hide();
diff --git a/www/plugins/es/js/controllers/registry-controllers.js b/www/plugins/es/js/controllers/registry-controllers.js
index 52062db575f635955c19747b10a5774675b0dc3b..c458d3845bc91ccabe4c32f059e3fa80d5899b76 100644
--- a/www/plugins/es/js/controllers/registry-controllers.js
+++ b/www/plugins/es/js/controllers/registry-controllers.js
@@ -376,7 +376,7 @@ function ESRegistryLookupController($scope, $state, $focus, $timeout, esRegistry
 
 function ESRegistryRecordViewController($scope, $state, $q, $timeout, $ionicPopover, $ionicHistory, $translate,
                                         $anchorScroll, $filter,
-                                        Wallet, esRegistry, esUser, UIUtils, BMA, esHttp) {
+                                        csWallet, esRegistry, esUser, UIUtils, esHttp) {
   'ngInject';
 
   $scope.formData = {};
@@ -386,7 +386,7 @@ function ESRegistryRecordViewController($scope, $state, $q, $timeout, $ionicPopo
   $scope.canEdit = false;
   $scope.loading = true;
 
-  ESCommentsController.call(this, $scope,  $timeout, $filter, $state, Wallet, UIUtils, esHttp, esRegistry);
+  ESCommentsController.call(this, $scope,  $timeout, $filter, $state, csWallet, UIUtils, esHttp, esRegistry);
 
   $scope.$on('$ionicView.enter', function(e, state) {
     if (state.stateParams && state.stateParams.id) { // Load by id
@@ -416,7 +416,7 @@ function ESRegistryRecordViewController($scope, $state, $q, $timeout, $ionicPopo
           else {
             delete $scope.thumbnail;
           }
-          $scope.canEdit = Wallet.isUserPubkey($scope.formData.issuer);
+          $scope.canEdit = csWallet.isUserPubkey($scope.formData.issuer);
 
           // Load avatar and name (and uid)
           return esUser.profile.fillAvatars([{pubkey: $scope.formData.issuer}])
diff --git a/www/plugins/es/js/services/http-services.js b/www/plugins/es/js/services/http-services.js
index 9e82e99f53782a2627f759cddaaf36f513e0fb64..1a4a2ed46c1708e22ea6e4d3d6a5de4ed488a234 100644
--- a/www/plugins/es/js/services/http-services.js
+++ b/www/plugins/es/js/services/http-services.js
@@ -3,7 +3,7 @@ angular.module('cesium.es.http.services', ['ngResource', 'cesium.services', 'ces
 /**
  * Elastic Search Http
  */
-.factory('esHttp', function($q, CryptoUtils, csHttp, $rootScope, csConfig, csSettings, Wallet) {
+.factory('esHttp', function($q, CryptoUtils, csHttp, $rootScope, csConfig, csSettings, csWallet) {
   'ngInject';
 
   function factory() {
@@ -27,7 +27,7 @@ angular.module('cesium.es.http.services', ['ngResource', 'cesium.services', 'ces
 
       return function(record, params) {
         return $q(function(resolve, reject) {
-          if (!Wallet.isLogin()) {
+          if (!csWallet.isLogin()) {
             reject('Wallet must be login before sending record to ES node'); return;
           }
           var errorFct = function(err) {
@@ -67,7 +67,7 @@ angular.module('cesium.es.http.services', ['ngResource', 'cesium.services', 'ces
       var postHistoryDelete = csHttp.post(host, node, '/history/delete');
       return function(id) {
         return $q(function(resolve, reject) {
-          if (!Wallet.isLogin()) {
+          if (!csWallet.isLogin()) {
             reject('Wallet must be login before sending record to ES node'); return;
           }
           var errorFct = function(err) {
diff --git a/www/plugins/es/js/services/message-services.js b/www/plugins/es/js/services/message-services.js
index 5a7238f7fd74a3a646adf1932fc70e7d5eb28331..2351bb89135c1ce4d92c0a5bbc3107dfead27aa2 100644
--- a/www/plugins/es/js/services/message-services.js
+++ b/www/plugins/es/js/services/message-services.js
@@ -10,7 +10,7 @@ angular.module('cesium.es.message.services', ['ngResource', 'cesium.services', '
 
   })
 
-.factory('esMessage', function($q, $rootScope, csSettings, esHttp, CryptoUtils, esUser, Wallet, BMA) {
+.factory('esMessage', function($q, $rootScope, csSettings, esHttp, CryptoUtils, esUser, csWallet, BMA) {
   'ngInject';
 
   function factory(host, port) {
@@ -79,7 +79,7 @@ angular.module('cesium.es.message.services', ['ngResource', 'cesium.services', '
 
 
     function getBoxKeypair(keypair) {
-      keypair = keypair || (Wallet.isLogin() ? Wallet.data.keypair : keypair);
+      keypair = keypair || (csWallet.isLogin() ? csWallet.data.keypair : keypair);
       if (!keypair) {
         throw new Error('no keypair, and user not connected.');
       }
@@ -87,14 +87,14 @@ angular.module('cesium.es.message.services', ['ngResource', 'cesium.services', '
         return keypair;
       }
       var boxKeypair = CryptoUtils.box.keypair.fromSignKeypair(keypair);
-      Wallet.data.keypair.boxSk = boxKeypair.boxSk;
-      Wallet.data.keypair.boxPk = boxKeypair.boxPk;
+      csWallet.data.keypair.boxSk = boxKeypair.boxSk;
+      csWallet.data.keypair.boxPk = boxKeypair.boxPk;
       console.debug("[ES] Secret box keypair successfully computed");
-      return Wallet.data.keypair;
+      return csWallet.data.keypair;
     }
 
     function countNewMessages(pubkey, fromTime) {
-      pubkey = pubkey || (Wallet.isLogin() ? Wallet.data.pubkey : pubkey);
+      pubkey = pubkey || (csWallet.isLogin() ? csWallet.data.pubkey : pubkey);
       if (!pubkey) {
         throw new Error('no pubkey, and user not connected.');
       }
@@ -165,7 +165,7 @@ angular.module('cesium.es.message.services', ['ngResource', 'cesium.services', '
             return [];
           }
           else {
-            var walletPubkey = Wallet.isLogin() ? Wallet.data.pubkey : null;
+            var walletPubkey = csWallet.isLogin() ? csWallet.data.pubkey : null;
             var messages = res.hits.hits.reduce(function(result, hit) {
               var msg = hit._source;
               msg.id = hit._id;
@@ -177,8 +177,8 @@ angular.module('cesium.es.message.services', ['ngResource', 'cesium.services', '
             console.debug('[ES] [message] Loading ' + messages.length + ' messages');
 
             // Update message count
-            Wallet.data.messages = Wallet.data.messages || {};
-            Wallet.data.messages.count = messages.length;
+            csWallet.data.messages = csWallet.data.messages || {};
+            csWallet.data.messages.count = messages.length;
 
             return decryptMessages(messages, keypair)
               .then(function(){
@@ -195,7 +195,7 @@ angular.module('cesium.es.message.services', ['ngResource', 'cesium.services', '
             return;
           }
           else {
-            var walletPubkey = Wallet.isLogin() ? Wallet.data.pubkey : null;
+            var walletPubkey = csWallet.isLogin() ? csWallet.data.pubkey : null;
             var msg = hit._source;
             msg.id = hit._id;
             msg.pubkey = msg.issuer !== walletPubkey ? msg.issuer : msg.recipient;
@@ -268,8 +268,8 @@ angular.module('cesium.es.message.services', ['ngResource', 'cesium.services', '
       return esHttp.record.remove(host, port, 'message', 'record')(id)
         .then(function(res) {
           // update message count
-          Wallet.data.messages = Wallet.data.messages || {};
-          Wallet.data.messages.count = Wallet.data.messages.count > 0 ? Wallet.data.messages.count-1 : 0;
+          csWallet.data.messages = csWallet.data.messages || {};
+          csWallet.data.messages.count = csWallet.data.messages.count > 0 ? csWallet.data.messages.count-1 : 0;
           return res;
         });
     }
@@ -286,12 +286,12 @@ angular.module('cesium.es.message.services', ['ngResource', 'cesium.services', '
     function addListeners() {
       console.debug("[ES] Enable message extension");
 
-      // Extend Wallet.loadData() and WotService.loadData()
+      // Extend csWallet.loadData()
       listeners = [
-        Wallet.api.data.on.login($rootScope, onWalletLoad, this),
-        //Wallet.api.data.on.load($rootScope, onWalletLoad, this),
-        Wallet.api.data.on.init($rootScope, onWalletInit, this),
-        Wallet.api.data.on.reset($rootScope, onWalletReset, this),
+        csWallet.api.data.on.login($rootScope, onWalletLoad, this),
+        //csWallet.api.data.on.load($rootScope, onWalletLoad, this),
+        csWallet.api.data.on.init($rootScope, onWalletInit, this),
+        csWallet.api.data.on.reset($rootScope, onWalletReset, this),
       ];
     }
 
@@ -314,8 +314,8 @@ angular.module('cesium.es.message.services', ['ngResource', 'cesium.services', '
     // Listen for settings changed
     csSettings.api.data.on.changed($rootScope, function(){
       refreshListeners();
-      if (isEnable() && !Wallet.data.messages) {
-        onWalletLoad(Wallet.data);
+      if (isEnable() && !csWallet.data.messages) {
+        onWalletLoad(csWallet.data);
       }
     });
 
diff --git a/www/plugins/es/js/services/user-services.js b/www/plugins/es/js/services/user-services.js
index 7d44d48194e3c1a60b70f558c90750566ab80084..bdcb48d43ed01bfdeb77a797398c427bc51c17dd 100644
--- a/www/plugins/es/js/services/user-services.js
+++ b/www/plugins/es/js/services/user-services.js
@@ -10,7 +10,7 @@ angular.module('cesium.es.user.services', ['cesium.services', 'cesium.es.http.se
 
   })
 
-.factory('esUser', function($rootScope, $q, $timeout, esHttp, csConfig, csSettings, Wallet, WotService, UIUtils, BMA, CryptoUtils, Device) {
+.factory('esUser', function($rootScope, $q, $timeout, esHttp, csConfig, csSettings, csWallet, csWot, UIUtils, BMA, CryptoUtils, Device) {
   'ngInject';
 
   function factory(host, port) {
@@ -242,14 +242,14 @@ angular.module('cesium.es.user.services', ['cesium.services', 'cesium.es.http.se
 
       // Waiting to load crypto libs
       if (!CryptoUtils.isLoaded()) {
-        console.debug('[esUser] Waiting crypto lib loading...');
+        console.debug('[ES] [user] Waiting crypto lib loading...');
         $timeout(function() {
           onWalletLogin(data, resolve, reject);
         }, 200);
         return;
       }
 
-      console.debug('[esUser] Loading user settings from ES node...');
+      console.debug('[ES] [user] Loading user settings from ES node...');
 
       // Load settings
       esHttp.get(host, port, '/user/settings/:id')({id: data.pubkey})
@@ -261,7 +261,7 @@ angular.module('cesium.es.user.services', ['cesium.services', 'cesium.es.http.se
           var record = res._source;
           // Do not apply if same version
           if (record.time === csSettings.data.time) {
-            console.debug('[esUser] Local settings already up to date');
+            console.debug('[ES] [user] Local settings already up to date');
             resolve(data);
             return;
           }
@@ -275,13 +275,13 @@ angular.module('cesium.es.user.services', ['cesium.services', 'cesium.es.http.se
               angular.merge(csSettings.data, settings);
               restoringSettings = true;
               csSettings.store();
-              console.debug('[esUser] Successfully loaded user settings from ES node');
+              console.debug('[ES] [user] Successfully loaded user settings from ES node');
               resolve(data);
             });
         })
         .catch(function(err){
           if (err && err.ucode && err.ucode == 404) {
-            console.debug('[esUser] No user settings found in ES node...');
+            console.debug('[ES] [user] No user settings found in ES node...');
             resolve(data); // not found
           }
           else {
@@ -291,24 +291,24 @@ angular.module('cesium.es.user.services', ['cesium.services', 'cesium.es.http.se
     }
 
     function onSettingsChanged(data) {
-      if (!Wallet.isLogin()) return;
+      if (!csWallet.isLogin()) return;
 
       // Waiting to load crypto libs
       if (!CryptoUtils.isLoaded()) {
-        console.debug('[esUser] Waiting crypto lib loading...');
+        console.debug('[ES] [user] Waiting crypto lib loading...');
         $timeout(function() {
           onSettingsChanged(data);
         }, 200);
         return;
       }
 
-      console.debug('[esUser] Saving user settings to ES...');
+      console.debug('[ES] [user] Saving user settings to ES...');
 
-      var boxKeypair = CryptoUtils.box.keypair.fromSignKeypair(Wallet.data.keypair);
+      var boxKeypair = CryptoUtils.box.keypair.fromSignKeypair(csWallet.data.keypair);
       var nonce = CryptoUtils.util.random_nonce();
 
       var formData = {
-        issuer: Wallet.data.pubkey,
+        issuer: csWallet.data.pubkey,
         nonce: CryptoUtils.util.encode_base58(nonce),
         time: Math.trunc(new Date().getTime() / 1000)
       };
@@ -330,7 +330,7 @@ angular.module('cesium.es.user.services', ['cesium.services', 'cesium.es.http.se
           csSettings.data.time = formData.time;
           restoringSettings = true;
           csSettings.store();
-          console.debug('[esUser] User settings saved in ES');
+          console.debug('[ES] [user] User settings saved in ES');
         })
         .catch(function(err) {
           console.error(err);
@@ -340,7 +340,7 @@ angular.module('cesium.es.user.services', ['cesium.services', 'cesium.es.http.se
     }
 
     function removeListeners() {
-      console.debug("[esUser] Disable user extension");
+      console.debug("[ES] [user] Disable");
 
       _.forEach(listeners, function(remove){
         remove();
@@ -349,16 +349,16 @@ angular.module('cesium.es.user.services', ['cesium.services', 'cesium.es.http.se
     }
 
     function addListeners() {
-      console.debug("[ES] Enable user extension");
+      console.debug("[ES] [user] Enable");
 
-      // Extend Wallet.loadData() and WotService.loadData()
+      // Extend csWallet.loadData() and csWot.loadData()
       listeners = [
-        Wallet.api.data.on.load($rootScope, onWalletLoad, this),
-        Wallet.api.data.on.finishLoad($rootScope, onWalletFinishLoad, this),
-        Wallet.api.data.on.reset($rootScope, onWalletReset, this),
-        Wallet.api.data.on.login($rootScope, onWalletLogin, this),
-        WotService.api.data.on.load($rootScope, onWotLoad, this),
-        WotService.api.data.on.search($rootScope, onWotSearch, this),
+        csWallet.api.data.on.load($rootScope, onWalletLoad, this),
+        csWallet.api.data.on.finishLoad($rootScope, onWalletFinishLoad, this),
+        csWallet.api.data.on.reset($rootScope, onWalletReset, this),
+        csWallet.api.data.on.login($rootScope, onWalletLogin, this),
+        csWot.api.data.on.load($rootScope, onWotLoad, this),
+        csWot.api.data.on.search($rootScope, onWotSearch, this),
       ];
     }
 
@@ -391,7 +391,7 @@ angular.module('cesium.es.user.services', ['cesium.services', 'cesium.es.http.se
 
       if (!wasEnable && isEnable()) {
         return $q(function(resolve, reject){
-          onWalletLogin(Wallet.data, resolve, reject);
+          onWalletLogin(csWallet.data, resolve, reject);
         });
       }
       else {
diff --git a/www/templates/settings/settings.html b/www/templates/settings/settings.html
index 29a99a253a11ec12c5496be5b910c46db218e694..18273401f813a8fc1064d4f18d644fa58825a320 100644
--- a/www/templates/settings/settings.html
+++ b/www/templates/settings/settings.html
@@ -115,7 +115,7 @@
         </div>
         <span class="item-note dark">{{getServer()}}</ng-if></span>
       </div>
-      <div class="item item-toggle dark hidden-sm hidden-xs hidden-md">
+      <div class="item item-toggle dark hidden-xs hidden-sm">
         <div class="input-label" ng-bind-html="'SETTINGS.EXPERT_MODE' | translate"></div>
         <label class="toggle toggle-royal">
           <input type="checkbox" ng-model="formData.expertMode" >
diff --git a/www/templates/wallet/popover_actions.html b/www/templates/wallet/popover_actions.html
index ca3ea7eb7cd226b5e7ab2bac84a068b501a7df76..4818d68c007ab39784a0f55b432959c8cf05b912 100644
--- a/www/templates/wallet/popover_actions.html
+++ b/www/templates/wallet/popover_actions.html
@@ -6,7 +6,7 @@
     <div class="list item-text-wrap">
 
       <a class="item item-icon-left item-icon-right ink"
-         ng-click="setShowDetails(!showDetails)">
+         ng-click="toggleShowDetails()">
         <i class="icon ion-key"></i>
         {{'ACCOUNT.BTN_SHOW_DETAILS' | translate}}
         <i class="icon"
diff --git a/www/templates/wot/tabs/view_certifications.html b/www/templates/wot/tabs/view_certifications.html
index e1fd7a38c3530873d536095d24a0e21596e6d895..c61a8e87f2284b7f9670969de17baeb5ada017fd 100644
--- a/www/templates/wot/tabs/view_certifications.html
+++ b/www/templates/wot/tabs/view_certifications.html
@@ -11,8 +11,10 @@
             <span class="badge" ng-class="{'badge-balanced': formData.certificationCount >= formData.sigQty, 'badge-assertive': formData.certificationCount < formData.sigQty}">{{formData.certificationCount}}</span>
           </div>
 
+          <!-- pending certifications -->
           <span class="item item-divider" ng-if="formData.hasPendingCertifications">
             <span translate>WOT.CERTIFICATIONS.PENDING_LIST</span>
+            <div class="badge item-note" style="text-align: right !important;" translate>WOT.NOT_WRITTEN_EXPIRE_IN</div>
           </span>
 
           <a class="item ink"
@@ -30,7 +32,8 @@
               <h4 class="gray">
                 <i class="ion-key"></i>
                 {{::cert.pubkey | formatPubkey}}
-                <span class="gray"> | {{::'WOT.SIGNED_ON_BLOCK' | translate:cert}}</span>
+                <span class="gray"> | {{::cert.time|formatDate}}</span>
+                <span class="gray" ng-if="$root.settings.expertMode"> | {{::'WOT.SIGNED_ON_BLOCK' | translate:cert}}</span>
               </h4>
             </span>
             <span ng-if="!cert.isMember">
@@ -42,19 +45,31 @@
                 </span>
               </h3>
               <h5 class="assertive">{{'WOT.NOT_MEMBER_PARENTHESIS'|translate}}</h5>
-              <h4 class="gray"><i class="ion-key"></i>
-                {{::cert.pubkey | formatPubkey}} | {{'WOT.SIGNED_ON_BLOCK' | translate}} #{{::cert.block}}</h4>
+              <h4 class="gray">
+                <span ng-if="cert.uid">
+                  <i class="ion-key"></i>
+                  {{::cert.pubkey | formatPubkey}}
+                </span>
+                <span class="gray"> | {{::cert.time|formatDate}}</span>
+                <span class="gray" ng-if="$root.settings.expertMode"> | {{::'WOT.SIGNED_ON_BLOCK' | translate:cert}}</span>
+              </h4>
             </span>
+            <div class="badge badge-stable" ng-if="cert.expiresIn">
+              {{::cert.expiresIn | formatDuration}}
+            </div>
+            <div class="badge badge-assertive" ng-if="!cert.expiresIn">
+              {{::'WOT.EXPIRED' | translate}}
+            </div>
           </a>
 
+          <!-- valid certifications -->
           <span class="item item-divider">
             <span translate>WOT.CERTIFICATIONS.LIST</span>
             <div class="badge item-note" translate>WOT.EXPIRE_IN</div>
           </span>
 
-          <span class="item" ng-if="!loading && formData.certifications.length == 0">
-            <h3 translate>WOT.NO_CERTIFICATIONS</h3>
-          </span>
+          <span class="item gray" ng-if="!formData.certificationCount"
+                translate>WOT.NO_CERTIFICATION</span>
 
           <a class="item ink"
                 ng-repeat="cert in formData.certifications | filter: {pending: false}"
@@ -69,7 +84,8 @@
               <h4 class="gray">
                 <i class="ion-key"></i>
                 {{::cert.pubkey | formatPubkey}}
-                <span class="gray"> | {{::'WOT.WRITTEN_ON_BLOCK' | translate:cert}}</span>
+                <span class="gray"> | {{cert.time|formatDate}}</span>
+                <span class="gray" ng-if="$root.settings.expertMode"> | {{::'WOT.WRITTEN_ON_BLOCK' | translate:cert}}</span>
               </h4>
             </span>
             <span ng-if="!cert.isMember">
@@ -82,11 +98,21 @@
                 {{::cert.pubkey | formatPubkey}}
               </h3>
               <h5 class="assertive">{{'WOT.NOT_MEMBER_PARENTHESIS'|translate}}</h5>
-              <h4 ng-if="!cert.isMember" class="gray">{{'WOT.SIGNED_ON_BLOCK' | translate}} #{{::cert.block}}</h4>
+              <h4 class="gray">
+                <span ng-if="cert.uid">
+                  <i class="ion-key"></i>
+                  {{::cert.pubkey | formatPubkey}}
+                </span>
+                <span class="gray"> | {{cert.time|formatDate}}</span>
+                <span class="gray" ng-if="$root.settings.expertMode"> | {{'WOT.SIGNED_ON_BLOCK' | translate:cert}}</span>
+              </h4>
             </span>
-            <div class="badge item-note"
+            <div class="badge" ng-if="cert.expiresIn"
                  ng-class="{'badge-balanced': !cert.willExpire, 'badge-energized': cert.willExpire}">
               {{::cert.expiresIn | formatDuration}}
             </div>
+            <div class="badge badge-assertive" ng-if="!cert.expiresIn">
+              {{::'WOT.EXPIRED' | translate}}
+            </div>
           </a>
         </div>
diff --git a/www/templates/wot/tabs/view_given_certifications.html b/www/templates/wot/tabs/view_given_certifications.html
index 864edc42cf2d7d01dbc861e68933017efbccee95..bfd69948574ba776c6f79c3a1b3ee31a66f0e13d 100644
--- a/www/templates/wot/tabs/view_given_certifications.html
+++ b/www/templates/wot/tabs/view_given_certifications.html
@@ -10,24 +10,21 @@
             <i class="icon ion-ribbon-a"></i>
             <span translate>WOT.GIVEN_CERTIFICATIONS.SENT</span>
             <span class="badge"
-                  ng-class="{'badge-balanced': formData.sigStock < formData.sigStockMax, 'badge-assertive': formData.sigStock >= formData.sigStockMax}">{{formData.sigStock}} / {{formData.sigStockMax}}</span>
+                  ng-class="{'badge-balanced': formData.givenCertificationCount < formData.sigStock, 'badge-assertive': formData.givenCertificationCount >= formData.sigStock}">{{formData.givenCertificationCount}} / {{formData.sigStock}}</span>
           </div>
 
-          <span class="item item-divider">
-            <span translate>WOT.GIVEN_CERTIFICATIONS.LIST</span>
-            <div class="badge item-note" translate>WOT.EXPIRE_IN</div>
-          </span>
-
-          <span class="item" ng-if="!loading && formData.givenCertifications.length == 0">
-            <h3 translate>WOT.NO_CERTIFICATIONS</h3>
+          <!-- pending given certifications -->
+          <span class="item item-divider" ng-if="formData.hasPendingGivenCertifications">
+            <span translate>WOT.GIVEN_CERTIFICATIONS.PENDING_LIST</span>
+            <div class="badge item-note" style="text-align: right !important;" translate>WOT.NOT_WRITTEN_EXPIRE_IN</div>
           </span>
 
           <a class="item ink"
-                ng-repeat="cert in formData.givenCertifications"
-                ui-sref="app.wot_view_identity({pubkey:cert.pubkey, uid:cert.uid})">
+             ng-repeat="cert in formData.givenCertifications | filter:{pending:true}"
+             ui-sref="app.wot_view_identity({pubkey:cert.pubkey, uid:cert.uid})">
             <span ng-if="cert.isMember">
               <h3>
-                <i ng-if="!cert.valid" class="icon ion-clock"> </i>
+                <i class="icon ion-clock"> </i>
                 <span class="positive">
                   <i class="icon ion-person"> </i>
                   {{::cert.uid}}
@@ -36,8 +33,8 @@
               <h4 class="gray">
                 <i class="icon ion-key"></i>
                 {{::cert.pubkey | formatPubkey}}
-                <span class="gray" ng-if="cert.valid"> | {{'WOT.WRITTEN_ON_BLOCK' | translate}} #{{::cert.block}}</span>
-                <span class="gray" ng-if="!cert.valid"> | {{'WOT.SIGNED_ON_BLOCK' | translate}} #{{::cert.block}}</span>
+                <span class="gray"> | {{cert.time|formatDate}}</span>
+                <span class="gray" ng-if="$root.settings.expertMode"> | {{'WOT.SIGNED_ON_BLOCK' | translate:cert}}</span>
               </h4>
             </span>
             <span ng-if="!cert.isMember">
@@ -46,16 +43,81 @@
                   <i class="ion-person"> </i>
                   {{::cert.uid}}
                 </span>
-                <i class="ion-key"></i>
+                <span ng-if="!cert.uid">
+                  <i class="ion-key"> </i>
+                  {{::cert.pubkey | formatPubkey}}
+                </span>
+              </h3>
+              <h5 class="assertive">{{::'WOT.NOT_MEMBER_PARENTHESIS'|translate}}</h5>
+              <h4 class="gray">
+                <span ng-if="cert.uid">
+                  <i class="ion-key"> </i>
+                  {{::cert.pubkey | formatPubkey}}
+                </span>
+                <span class="gray"> | {{::cert.time|formatDate}}</span>
+                <span class="gray" ng-if="$root.settings.expertMode"> | {{::'WOT.SIGNED_ON_BLOCK' | translate:cert}}</span>
+              </h4>
+            </span>
+            <div class="badge badge-stable" ng-if="cert.expiresIn">
+              {{::cert.expiresIn | formatDuration}}
+            </div>
+            <div class="badge badge-assertive" ng-if="!cert.expiresIn">
+              {{::'WOT.EXPIRED' | translate}}
+            </div>
+          </a>
+
+          <!-- validated given certifications -->
+          <span class="item item-divider">
+            <span translate>WOT.GIVEN_CERTIFICATIONS.LIST</span>
+            <div class="badge item-note" translate>WOT.EXPIRE_IN</div>
+          </span>
+
+          <span class="item gray" ng-if="!formData.givenCertificationCount" translate>
+            WOT.NO_GIVEN_CERTIFICATION
+          </span>
+
+          <a class="item ink"
+                ng-repeat="cert in formData.givenCertifications | filter:{pending:false}"
+                ui-sref="app.wot_view_identity({pubkey:cert.pubkey, uid:cert.uid})">
+            <span ng-if="cert.isMember">
+              <h3 class="positive">
+                <i class="icon ion-person"> </i>
+                {{::cert.uid}}
+              </h3>
+              <h4 class="gray">
+                <i class="icon ion-key"></i>
                 {{::cert.pubkey | formatPubkey}}
+                <span class="gray"> | {{::cert.time|formatDate}}</span>
+                <span class="gray" ng-if="$root.settings.expertMode"> | {{::'WOT.WRITTEN_ON_BLOCK' | translate:cert}}</span>
+              </h4>
+            </span>
+            <span ng-if="!cert.isMember">
+              <h3 class="gray">
+                <span ng-if="cert.uid">
+                  <i class="ion-person"> </i>
+                  {{::cert.uid}}
+                </span>
+                <span ng-if="!cert.uid">
+                   <i class="ion-key"></i>
+                  {{::cert.pubkey | formatPubkey}}
+                </span>
               </h3>
-              <h5 class="assertive">{{'WOT.NOT_MEMBER_PARENTHESIS'|translate}}</h5>
-              <h4 ng-if="!cert.isMember" class="gray">{{'WOT.SIGNED_ON_BLOCK' | translate}} #{{::cert.block}}</h4>
+              <h5 class="assertive">{{::'WOT.NOT_MEMBER_PARENTHESIS'|translate}}</h5>
+              <h4 class="gray">
+                <span ng-if="cert.uid">
+                   <i class="ion-key"></i>
+                  {{::cert.pubkey | formatPubkey}}
+                </span>
+                <span class="gray"> | {{::cert.time|formatDate}}</span>
+                <span class="gray" ng-if="$root.settings.expertMode"> | {{::'WOT.WRITTEN_ON_BLOCK' | translate:cert}}</span>
+              </h4>
             </span>
-            <div class="badge item-note"
-                 ng-if="cert.valid"
+            <div class="badge" ng-if="cert.expiresIn"
                  ng-class="{'badge-balanced': !cert.willExpire, 'badge-energized': cert.willExpire}">
               {{::cert.expiresIn | formatDuration}}
             </div>
+            <div class="badge badge-assertive" ng-if="!cert.expiresIn">
+              {{::'WOT.EXPIRED' | translate}}
+            </div>
           </a>
         </div>