diff --git a/www/i18n/locale-en.json b/www/i18n/locale-en.json
index 63858b1e6bef924225adb938ec8d43059f517216..6f7b9ebcdae950af9479655ce4f4b68f738ea87c 100644
--- a/www/i18n/locale-en.json
+++ b/www/i18n/locale-en.json
@@ -265,7 +265,6 @@
     "BTN_MEMBERSHIP_RENEW_DOTS": "Renew membership...",
     "BTN_MEMBERSHIP_OUT_DOTS": "Revoke membership...",
     "BTN_SEND_IDENTITY_DOTS": "Publish identity...",
-    "BTN_REVOKE": "Revoke<span class='hidden-xs hidden-sm'> definitely</span> this identity...",
     "BTN_SHOW_DETAILS": "Display technical data",
     "NEW": {
       "TITLE": "Registration",
@@ -295,6 +294,40 @@
     "POPUP_REGISTER": {
       "TITLE": "Enter a pseudonym",
       "HELP": "A pseudonym is needed to let other members find you."
+    },
+    "SECURITY":{
+      "TITLE": "Sign-in and security",
+      "SAVE_KEYS": "Save your login",
+      "DOWNLOAD_REVOKE": "Save a recocation file",
+      "REVOKE" : "Revoke this identity",
+      "DEFINITELY_REVOKE" : "Revoke definitely this identity.",
+      "REVOCATION": "Revocation ...",
+      "LEVEL": "Security level",
+      "HELP_LEVEL": "Choose <strong> at least {{nb}} questions </strong> :",
+      "LOW_LEVEL": "Low <span class=\"hidden-xs\">(2 questions minimum)</span>",
+      "MEDIUM_LEVEL": "Medium <span class=\"hidden-xs\">(4 questions minimum)</span>",
+      "STRONG_LEVEL": "Strong <span class=\"hidden-xs \">(6 questions minimum)</span>",
+      "ADD_QUESTION" : "Add custom question",
+      "BTN_RESET" : "Reset",
+      "QUESTION_1": "Comment s'appelait votre meilleur ami lorsque vous étiez adolescent ?",
+      "QUESTION_2": "What was the name of your first pet ?",
+      "QUESTION_3": "Quel est le premier plat que vous avez appris à cuisiner ?",
+      "QUESTION_4": "Quel est le premier film que vous avez vu au cinéma ?",
+      "QUESTION_5": "Où êtes-vous allé la première fois que vous avez pris l'avion ?",
+      "QUESTION_6": "Comment s'appelait votre instituteur préféré à l'école primaire ?",
+      "QUESTION_7": "Quel serait selon vous le métier idéal ?",
+      "QUESTION_8": "Quel est le livre pour enfants que vous préférez ?",
+      "QUESTION_9": "Quel était le modèle de votre premier véhicule ?",
+      "QUESTION_10": "What was your nickname when you were a child ?",
+      "QUESTION_11": "Quel était votre personnage ou acteur de cinéma préféré lorsque vous étiez étudiant ?",
+      "QUESTION_12": "Quel était votre chanteur ou groupe préféré lorsque vous étiez étudiant ?",
+      "QUESTION_13": "Dans quelle ville vos parents se sont-ils rencontrés ?",
+      "QUESTION_14": "Comment s'appelait votre premier patron ?",
+      "QUESTION_15": "Quel est le nom de la rue où vous avez grandi ?",
+      "QUESTION_16": "Quel est le nom de la première plage où vous vous êtes baigné ?",
+      "QUESTION_17": "Quel est le premier album que vous avez acheté ?",
+      "QUESTION_18": "What is the name of your favorite sport team ?",
+      "QUESTION_19": "What was your grand-father's job ?"
     }
   },
   "TRANSFER": {
diff --git a/www/i18n/locale-fr-FR.json b/www/i18n/locale-fr-FR.json
index 211ab141b1b8db62732af9c5905a9de9a96ee9fa..67e7a8af3580b6b6ade6bb5c3ac5af5427c51481 100644
--- a/www/i18n/locale-fr-FR.json
+++ b/www/i18n/locale-fr-FR.json
@@ -301,10 +301,36 @@
       "DOWNLOAD_REVOKE": "Sauvegarder un fichier de revocation",
       "REVOKE" : "Revoquer cette identité",
       "DEFINITELY_REVOKE" : "Revoquer définitivement cette identité",
-      "REVOCATION": "Révocation"
-
-    }
-  },
+      "REVOCATION": "Révocation ...",
+      "LEVEL": "Niveau de sécurité",
+      "HELP_LEVEL": "Pour générer un fichier de sauvegarde de vos identifiants, choisissez <strong> au moins {{nb}} questions :</strong>",
+      "LOW_LEVEL": "Faible <span class=\"hidden-xs\">(2 questions minimum)</span>",
+      "MEDIUM_LEVEL": "Moyen <span class=\"hidden-xs\">(4 questions minimum)</span>",
+      "STRONG_LEVEL": "Fort <span class=\"hidden-xs \">(6 questions minimum)</span>",
+      "ADD_QUESTION" : "Ajouter une question personnalisée ",
+      "BTN_RESET" : "Réinitialiser",
+      "BTN_CLEAN" : "vider",
+      "QUESTION_1": "Comment s'appelait votre meilleur ami lorsque vous étiez adolescent ?",
+      "QUESTION_2": "Comment s'appelait votre premier animal de compagnie ?",
+      "QUESTION_3": "Quel est le premier plat que vous avez appris à cuisiner ?",
+      "QUESTION_4": "Quel est le premier film que vous avez vu au cinéma ?",
+      "QUESTION_5": "Où êtes-vous allé la première fois que vous avez pris l'avion ?",
+      "QUESTION_6": "Comment s'appelait votre instituteur préféré à l'école primaire ?",
+      "QUESTION_7": "Quel serait selon vous le métier idéal ?",
+      "QUESTION_8": "Quel est le livre pour enfants que vous préférez ?",
+      "QUESTION_9": "Quel était le modèle de votre premier véhicule ?",
+      "QUESTION_10": "Quel était votre surnom lorsque vous étiez enfant ?",
+      "QUESTION_11": "Quel était votre personnage ou acteur de cinéma préféré lorsque vous étiez étudiant ?",
+      "QUESTION_12": "Quel était votre chanteur ou groupe préféré lorsque vous étiez étudiant ?",
+      "QUESTION_13": "Dans quelle ville vos parents se sont-ils rencontrés ?",
+      "QUESTION_14": "Comment s'appelait votre premier patron ?",
+      "QUESTION_15": "Quel est le nom de la rue où vous avez grandi ?",
+      "QUESTION_16": "Quel est le nom de la première plage où vous vous êtes baigné ?",
+      "QUESTION_17": "Quel est le premier album que vous avez acheté ?",
+      "QUESTION_18": "Quel est le nom de votre équipe de sport préférée ?",
+      "QUESTION_19": "Quel était le métier de votre grand-père ?"
+       }
+    },
   "TRANSFER": {
     "TITLE": "Virement",
     "SUB_TITLE": "Faire un virement",
@@ -384,7 +410,8 @@
     "LOAD_PENDING_FAILED": "Echec du chargement des inscriptions en attente.",
     "ONLY_MEMBER_CAN_EXECUTE_THIS_ACTION": "Vous devez <b>être membre</b> pour pouvoir effectuer cette action.",
     "ONLY_SELF_CAN_EXECUTE_THIS_ACTION": "Vous devez avoir <b>publié votre identité</b> pour pouvoir effectuer cette action.",
-    "REVOCATION_FAILED": "Echec de la révocation."
+    "REVOCATION_FAILED": "Echec de la révocation.",
+    "SALT_OR_PASSWORD_NOT_CONFIRMED": "Phrase de protection ou mot de passe incorrects"
   },
   "INFO": {
     "POPUP_TITLE": "Information",
diff --git a/www/js/controllers/settings-controllers.js b/www/js/controllers/settings-controllers.js
index 5391f095a7a83a11d0a0ac87922a204f5b4bc45b..dd8ea310d7dd8ffd18e08ea8ea642a9459d01844 100644
--- a/www/js/controllers/settings-controllers.js
+++ b/www/js/controllers/settings-controllers.js
@@ -29,6 +29,7 @@ function SettingsController($scope, $q, $ionicPopup, $timeout, $translate, csHtt
   $scope.loading = true;
   $scope.nodePopup = {};
 
+
   $scope.$on('$ionicView.enter', function() {
     $scope.load();
   });
diff --git a/www/js/controllers/wallet-controllers.js b/www/js/controllers/wallet-controllers.js
index 225a0e592a84930a7fb2076c6fe1184e43bb9568..88037fdeac0d093c6dab5b5f5ae17ea3d730898d 100644
--- a/www/js/controllers/wallet-controllers.js
+++ b/www/js/controllers/wallet-controllers.js
@@ -558,7 +558,7 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state,
   };
 }
 
-function WalletSecurityModalController($scope, $state,  UIUtils, Modals, csWallet){
+function WalletSecurityModalController($scope, $rootScope, UIUtils, csWallet, $translate, CryptoUtils, $timeout){
   $scope.slides = {
     slider: null,
     options: {
@@ -569,19 +569,154 @@ function WalletSecurityModalController($scope, $state,  UIUtils, Modals, csWalle
   };
   $scope.isLastSlide = false;
   $scope.smallscreen = UIUtils.screen.isSmall();
+  $scope.formData = {
+    addQuestion: '',
+    level: '4',
+    questions : []
+  };
+
+  for (var i = 1; i<20; i++){
+    $translate('ACCOUNT.SECURITY.QUESTION_' + i.toString())
+      .then(function(translation){
+        $scope.formData.questions.push({value: translation , checked: false});
+        })
+    };
+
+  $scope.restore = function(){
+    if ($scope.slides.slider.activeIndex === 2) {
+      $scope.formData = {
+        addQuestion: '',
+        level: '4',
+        questions: []
+      };
+      for (var i = 1; i < 20; i++) {
+        $translate('ACCOUNT.SECURITY.QUESTION_' + i.toString())
+          .then(function (translation) {
+            $scope.formData.questions.push({value: translation, checked: false});
+          })
+      }
+    }
+    if($scope.slides.slider.activeIndex === 3) {
+      _.each($scope.formData.questions, function(question){
+        question.answer = undefined;
+      })
+    }
+  };
+
+  $scope.addQuestion = function(){
+    if ($scope.formData.addQuestion != '') {
+      $scope.formData.questions.push({value: $scope.formData.addQuestion, checked: true});
+      $scope.formData.addQuestion = '';
+    }
+  };
+
+  $scope.submit = function(){
+    if (!$scope.isUserPubkey($scope.pubkey)){
+      if ($scope.computing = false){
+        UIUtils.alert.error('ERROR.SALT_OR_PASSWORD_NOT_CONFIRMED', 'ERROR.LOGIN_FAILED');
+        return;
+      }
+    }
+    if(!$scope['loginForm'].$valid){
+      return;
+    }
+
+    var file = {file : _.filter($scope.formData.questions, function(question){
+      return question.checked;
+    })};
+    var record = {
+      salt: $scope.formData.username,
+      pwd: $scope.formData.password,
+      questions: ''
+    };
+    _.each(file.file, function(question){
+      record.questions += question.value + '\n';
+      record.answer += question.answer;
+    })
+
+    return csWallet.getkeypairSaveId(record)
+      .then(function(record){
+        csWallet.downloadSaveId(record);
+      })
+
+  };
+
+  $scope.isUserPubkey = function(pubkey) {
+    return csWallet.isUserPubkey(pubkey);
+  };
+
+  $scope.isRequired = function(){
+    var questionChecked = _.filter($scope.formData.questions, function(question) {
+      return question.checked;
+    })
+    return questionChecked.length < $scope.formData.level;
+  };
+
+  $scope.formDataChanged = function() {
+    $scope.computing = true;
+    $scope.pubkey = '';
+    $timeout(function() {
+      var salt = $scope.formData.username;
+      var pwd = $scope.formData.password;
+      CryptoUtils.connect(salt, pwd).then(
+        function (keypair) {
+            $scope.pubkey = CryptoUtils.util.encode_base58(keypair.signPk);
+            $scope.computing = false;
+        }
+      )
+        .catch(function (err) {
+          $scope.pubkey = '';
+          UIUtils.loading.hide();
+          console.error('>>>>>>>', err);
+          UIUtils.alert.error('ERROR.CRYPTO_UNKNOWN_ERROR');
+          $scope.computing = false;
+        });
+    }, 500);
+
+
+  };
+  $scope.$watch('formData.username', $scope.formDataChanged, true);
+  $scope.$watch('formData.password', $scope.formDataChanged, true);
 
   $scope.slidePrev = function() {
+    if ($scope.slides.slider.activeIndex === 2){
+      $scope.slideTo(0);
+      $scope.isLastSlide = false;
+      return;
+    }
     $scope.slides.slider.unlockSwipes();
     $scope.slides.slider.slidePrev();
     $scope.slides.slider.lockSwipes();
     $scope.isLastSlide = false;
+
   };
 
   $scope.slideNext = function() {
     $scope.slides.slider.unlockSwipes();
     $scope.slides.slider.slideNext();
     $scope.slides.slider.lockSwipes();
-    $scope.isLastSlide = $scope.slides.slider.activeIndex === 5;
+    $scope.isLastSlide = $scope.slides.slider.activeIndex === 4;
+  };
+
+  $scope.slideTo = function(index) {
+    $scope.slides.slider.unlockSwipes();
+    $scope.slides.slider.slideTo(index);
+    $scope.slides.slider.lockSwipes();
+    $scope.isLastSlide = $scope.slides.slider.activeIndex === 4;
+  };
+
+  $scope.doNext = function(formName) {
+    if (!formName) {
+      formName = $scope.slides.slider.activeIndex === 2 ? 'questionsForm' :
+        ($scope.slides.slider.activeIndex === 3 ? 'answersForm' : formName);
+    }
+    if (formName) {
+      $scope[formName].$submitted = true;
+      if (!$scope[formName].$valid) {
+        return;
+      }
+      $scope.slideNext();
+    }
   };
 
 
diff --git a/www/js/services/wallet-services.js b/www/js/services/wallet-services.js
index e448aaab56c1dbec4b69e628fbbfd8e8280bc654..4c9081ecba55e361c6d1d02207127bed27732574 100644
--- a/www/js/services/wallet-services.js
+++ b/www/js/services/wallet-services.js
@@ -10,85 +10,85 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
   factory = function(id) {
 
     var
-    constants = {
-      STORAGE_KEY: 'CESIUM_DATA',
-      /* Need for compat with old currencies (test_net and sou) */
-      TX_VERSION:   csConfig.compatProtocol_0_80 ? 3 : BMA.constants.PROTOCOL_VERSION,
-      IDTY_VERSION: csConfig.compatProtocol_0_80 ? 2 : BMA.constants.PROTOCOL_VERSION,
-      MS_VERSION:   csConfig.compatProtocol_0_80 ? 2 : BMA.constants.PROTOCOL_VERSION,
-      CERT_VERSION: csConfig.compatProtocol_0_80 ? 2 : BMA.constants.PROTOCOL_VERSION,
-      REVOKE_VERSION: csConfig.compatProtocol_0_80 ? 2 : BMA.constants.PROTOCOL_VERSION
-    },
-    data = {},
-
-    api = new Api(this, 'csWallet-' + id),
-
-    resetData = function(init) {
-      data.pubkey= null;
-      data.keypair = {
+      constants = {
+        STORAGE_KEY: 'CESIUM_DATA',
+        /* Need for compat with old currencies (test_net and sou) */
+        TX_VERSION:   csConfig.compatProtocol_0_80 ? 3 : BMA.constants.PROTOCOL_VERSION,
+        IDTY_VERSION: csConfig.compatProtocol_0_80 ? 2 : BMA.constants.PROTOCOL_VERSION,
+        MS_VERSION:   csConfig.compatProtocol_0_80 ? 2 : BMA.constants.PROTOCOL_VERSION,
+        CERT_VERSION: csConfig.compatProtocol_0_80 ? 2 : BMA.constants.PROTOCOL_VERSION,
+        REVOKE_VERSION: csConfig.compatProtocol_0_80 ? 2 : BMA.constants.PROTOCOL_VERSION
+      },
+      data = {},
+
+      api = new Api(this, 'csWallet-' + id),
+
+      resetData = function(init) {
+        data.pubkey= null;
+        data.keypair = {
           signSk: null,
           signPk: null
         };
-      data.uid = null;
-      data.balance = 0;
-      data.sources = null;
-      data.sourcesIndexByKey = null;
-      data.currency= null;
-      data.parameters = null;
-      data.currentUD = null;
-      data.medianTime = null;
-      data.tx = data.tx || {};
-      data.tx.history = [];
-      data.tx.pendings = [];
-      data.tx.errors = [];
-      data.requirements = {};
-      data.blockUid = null;
-      data.sigDate = null;
-      data.isMember = false;
-      data.events = [];
-      data.loaded = false;
-      if (init) {
-        api.data.raise.init(data);
-      }
-      else {
-        if (!csSettings.data.useLocalStorage) {
-          csSettings.reset();
+        data.uid = null;
+        data.balance = 0;
+        data.sources = null;
+        data.sourcesIndexByKey = null;
+        data.currency= null;
+        data.parameters = null;
+        data.currentUD = null;
+        data.medianTime = null;
+        data.tx = data.tx || {};
+        data.tx.history = [];
+        data.tx.pendings = [];
+        data.tx.errors = [];
+        data.requirements = {};
+        data.blockUid = null;
+        data.sigDate = null;
+        data.isMember = false;
+        data.events = [];
+        data.loaded = false;
+        if (init) {
+          api.data.raise.init(data);
         }
-        api.data.raise.reset(data);
-      }
-    },
+        else {
+          if (!csSettings.data.useLocalStorage) {
+            csSettings.reset();
+          }
+          api.data.raise.reset(data);
+        }
+      },
 
-    reduceTxAndPush = function(txArray, result, processedTxMap, excludePending) {
-      if (!txArray || txArray.length === 0) {
-        return;
-      }
-      var txPendingsTimeByKey = excludePending ? [] : data.tx.pendings.reduce(function(res, tx) {
-        if (tx.time) {
-          res[tx.amount+':'+tx.hash] = tx.time;
+      reduceTxAndPush = function(txArray, result, processedTxMap, excludePending) {
+        if (!txArray || txArray.length === 0) {
+          return;
         }
-        return res;
-      }, []);
+        var txPendingsTimeByKey = excludePending ? [] : data.tx.pendings.reduce(function(res, tx) {
+            if (tx.time) {
+              res[tx.amount+':'+tx.hash] = tx.time;
+            }
+            return res;
+          }, []);
 
-      _.forEach(txArray, function(tx) {
-        if (!excludePending || tx.block_number !== null) {
-          var walletIsIssuer = false;
-          var otherIssuer = tx.issuers.reduce(function(issuer, res) {
+        _.forEach(txArray, function(tx) {
+          if (!excludePending || tx.block_number !== null) {
+            var walletIsIssuer = false;
+            var otherIssuer = tx.issuers.reduce(function(issuer, res) {
               walletIsIssuer = (res === data.pubkey) ? true : walletIsIssuer;
               return issuer + ((res !== data.pubkey) ? ', ' + res : '');
-          }, '');
-          if (otherIssuer.length > 0) {
-            otherIssuer = otherIssuer.substring(2);
-          }
-          var otherReceiver;
-          var outputBase;
-          var sources = [];
-          var amount = tx.outputs.reduce(function(sum, output, noffset) {
+            }, '');
+            if (otherIssuer.length > 0) {
+              otherIssuer = otherIssuer.substring(2);
+            }
+            var otherReceiver;
+            var outputBase;
+            var sources = [];
+            var amount = tx.outputs.reduce(function(sum, output, noffset) {
               var outputArray = output.split(':',3);
               outputBase = parseInt(outputArray[1]);
               var outputAmount = powBase(parseInt(outputArray[0]), outputBase);
               var outputCondArray = outputArray[2].split('(', 3);
               var outputPubkey = (outputCondArray.length == 2 && outputCondArray[0] == 'SIG') ?
-                   outputCondArray[1].substring(0,outputCondArray[1].length-1) : '';
+                outputCondArray[1].substring(0,outputCondArray[1].length-1) : '';
               if (outputPubkey == data.pubkey) { // output is for the wallet
                 if (!walletIsIssuer) {
                   return sum + outputAmount;
@@ -116,613 +116,613 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
               return sum;
             }, 0);
 
-          var pubkey = amount > 0 ? otherIssuer : otherReceiver;
-          var time = tx.time;
-          if (tx.block_number === null) {
-            time = tx.blockstampTime || txPendingsTimeByKey[amount + ':' + tx.hash];
-          }
-
-          // Avoid duplicated tx, or tx to him self
-          var txKey = amount + ':' + tx.hash + ':' + time;
-          if (!processedTxMap[txKey] && amount !== 0) {
-            processedTxMap[txKey] = true;
-            var newTx = {
-              time: time,
-              amount: amount,
-              pubkey: pubkey,
-              comment: tx.comment,
-              isUD: false,
-              hash: tx.hash,
-              locktime: tx.locktime,
-              block_number: tx.block_number
-            };
-            // If pending: store sources and inputs for a later use - see method processTransactionsAndSources()
-            if (walletIsIssuer && tx.block_number === null) {
-              newTx.inputs = tx.inputs;
-              newTx.sources = sources;
+            var pubkey = amount > 0 ? otherIssuer : otherReceiver;
+            var time = tx.time;
+            if (tx.block_number === null) {
+              time = tx.blockstampTime || txPendingsTimeByKey[amount + ':' + tx.hash];
             }
-            result.push(newTx);
-          }
-        }
-      });
-    },
-
-    resetSources = function(){
-      data.sources = [];
-      data.sourcesIndexByKey = {};
-    },
-
-    addSource = function(src, sources, sourcesIndexByKey) {
-      var srcKey = src.type+':'+src.identifier+':'+src.noffset;
-      if (angular.isUndefined(sourcesIndexByKey[srcKey])) {
-        sources.push(src);
-        sourcesIndexByKey[srcKey] = sources.length - 1;
-      }
-    },
 
-    addSources = function(sources) {
-      _(sources).forEach(function(src) {
-        addSource(src, data.sources, data.sourcesIndexByKey);
-      });
-    },
-
-    login = function(salt, password) {
-      return CryptoUtils.connect(salt, password)
-        .then(function(keypair) {
-          // Copy result to properties
-          data.pubkey = CryptoUtils.util.encode_base58(keypair.signPk);
-          data.keypair = keypair;
-
-          // Call extend api
-          return api.data.raisePromise.login(data);
-        })
-        // store if need
-        .then(function() {
-          if (csSettings.data.useLocalStorage) {
-            store();
+            // Avoid duplicated tx, or tx to him self
+            var txKey = amount + ':' + tx.hash + ':' + time;
+            if (!processedTxMap[txKey] && amount !== 0) {
+              processedTxMap[txKey] = true;
+              var newTx = {
+                time: time,
+                amount: amount,
+                pubkey: pubkey,
+                comment: tx.comment,
+                isUD: false,
+                hash: tx.hash,
+                locktime: tx.locktime,
+                block_number: tx.block_number
+              };
+              // If pending: store sources and inputs for a later use - see method processTransactionsAndSources()
+              if (walletIsIssuer && tx.block_number === null) {
+                newTx.inputs = tx.inputs;
+                newTx.sources = sources;
+              }
+              result.push(newTx);
+            }
           }
-          return data;
         });
-    },
+      },
 
-    logout = function() {
-      return $q(function(resolve, reject) {
+      resetSources = function(){
+        data.sources = [];
+        data.sourcesIndexByKey = {};
+      },
 
-        resetData(); // will reset keypair
-        store(); // store (if local storage enable)
+      addSource = function(src, sources, sourcesIndexByKey) {
+        var srcKey = src.type+':'+src.identifier+':'+src.noffset;
+        if (angular.isUndefined(sourcesIndexByKey[srcKey])) {
+          sources.push(src);
+          sourcesIndexByKey[srcKey] = sources.length - 1;
+        }
+      },
 
-        // Send logout event
-        api.data.raise.logout();
+      addSources = function(sources) {
+        _(sources).forEach(function(src) {
+          addSource(src, data.sources, data.sourcesIndexByKey);
+        });
+      },
 
-        resolve();
-      });
-    },
-
-    isLogin = function() {
-      return !!data.pubkey;
-    },
-
-    isNeverUsed = function() {
-      if (!data.loaded) return undefined; // undefined if not full loaded
-      return !data.pubkey ||
-        (!data.isMember &&
-        (!data.requirements || !data.requirements.pendingMembership) &&
-         !data.tx.history.length &&
-         !data.tx.pendings.length);
-    },
-
-    // If connected and same pubkey
-    isUserPubkey = function(pubkey) {
-      return isLogin() && data.pubkey === pubkey;
-    },
-
-    store = function() {
-      if (csSettings.data.useLocalStorage) {
-
-        if (isLogin() && csSettings.data.rememberMe) {
-          var dataToStore = {
-            keypair: data.keypair,
-            pubkey: data.pubkey
-          };
+      login = function(salt, password) {
+        return CryptoUtils.connect(salt, password)
+          .then(function(keypair) {
+            // Copy result to properties
+            data.pubkey = CryptoUtils.util.encode_base58(keypair.signPk);
+            data.keypair = keypair;
 
-          if (data.tx && data.tx.pendings && data.tx.pendings.length>0) {
-            var pendings = data.tx.pendings.reduce(function(res, tx){
-              return tx.time ? res.concat({
-                amount: tx.amount,
-                time: tx.time,
-                hash: tx.hash
-              }) : res;
-            }, []);
-            if (pendings.length) {
-              dataToStore.tx = {
-                pendings: pendings
-              };
+            // Call extend api
+            return api.data.raisePromise.login(data);
+          })
+          // store if need
+          .then(function() {
+            if (csSettings.data.useLocalStorage) {
+              store();
             }
-          }
+            return data;
+          });
+      },
 
-          localStorage.setObject(constants.STORAGE_KEY, dataToStore);
-        }
-        else {
-          localStorage.setObject(constants.STORAGE_KEY, null);
-        }
-      }
-      else {
-        localStorage.setObject(constants.STORAGE_KEY, null);
-      }
-    },
+      logout = function() {
+        return $q(function(resolve, reject) {
+
+          resetData(); // will reset keypair
+          store(); // store (if local storage enable)
+
+          // Send logout event
+          api.data.raise.logout();
 
-    restore = function() {
-      return $q(function(resolve, reject){
-        var dataStr = localStorage.get(constants.STORAGE_KEY);
-        if (!dataStr) {
           resolve();
-          return;
-        }
-        fromJson(dataStr, false)
-        .then(function(storedData){
-          if (storedData && storedData.keypair && storedData.pubkey) {
-            data.keypair = storedData.keypair;
-            data.pubkey = storedData.pubkey;
-            if (storedData.tx && storedData.tx.pendings) {
-              data.tx.pendings = storedData.tx.pendings;
-            }
-            data.loaded = false;
+        });
+      },
 
-            return $q.all([
-              // Call extend api
-              api.data.raisePromise.login(data),
+      isLogin = function() {
+        return !!data.pubkey;
+      },
 
-              // Load parameters
-              // This prevent timeout error, when loading a market record after a browser refresh (e.g. F5)
-              loadParameters(),
+      isNeverUsed = function() {
+        if (!data.loaded) return undefined; // undefined if not full loaded
+        return !data.pubkey ||
+          (!data.isMember &&
+          (!data.requirements || !data.requirements.pendingMembership) &&
+          !data.tx.history.length &&
+          !data.tx.pendings.length);
+      },
+
+      // If connected and same pubkey
+      isUserPubkey = function(pubkey) {
+        return isLogin() && data.pubkey === pubkey;
+      },
+
+      store = function() {
+        if (csSettings.data.useLocalStorage) {
+
+          if (isLogin() && csSettings.data.rememberMe) {
+            var dataToStore = {
+              keypair: data.keypair,
+              pubkey: data.pubkey
+            };
+
+            if (data.tx && data.tx.pendings && data.tx.pendings.length>0) {
+              var pendings = data.tx.pendings.reduce(function(res, tx){
+                return tx.time ? res.concat({
+                    amount: tx.amount,
+                    time: tx.time,
+                    hash: tx.hash
+                  }) : res;
+              }, []);
+              if (pendings.length) {
+                dataToStore.tx = {
+                  pendings: pendings
+                };
+              }
+            }
 
-              // Load current UD is need by features tour
-              loadCurrentUD()
-            ]);
+            localStorage.setObject(constants.STORAGE_KEY, dataToStore);
           }
           else {
-            // Load parameters
-            // This prevent timeout error, when loading a market record after a browser refresh (e.g. F5)
-            return loadParameters();
+            localStorage.setObject(constants.STORAGE_KEY, null);
           }
-        })
-        .then(function(){
-          resolve(data);
-        })
-        .catch(function(err){reject(err);});
-      });
-    },
-
-    getData = function() {
-      return data;
-    },
-
-    resetRequirements = function() {
-      data.requirements = {
-        needSelf: true,
-        needMembership: true,
-        canMembershipOut: false,
-        needRenew: false,
-        pendingMembership: false,
-        certificationCount: 0,
-        needCertifications: false,
-        needCertificationCount: 0,
-        willNeedCertificationCount: 0
-      };
-      data.blockUid = null;
-      data.isMember = false;
-      data.sigDate = null;
-      cleanEventsByContext('requirements');
-    },
-
-    loadRequirements = function() {
-      return $q(function(resolve, reject) {
-
-        // Clean existing events
-        cleanEventsByContext('requirements');
+        }
+        else {
+          localStorage.setObject(constants.STORAGE_KEY, null);
+        }
+      },
 
-        // Get requirements
-        BMA.wot.requirements({pubkey: data.pubkey})
-        .then(function(res){
-          if (!res.identities || res.identities.length === 0) {
-            resetRequirements();
+      restore = function() {
+        return $q(function(resolve, reject){
+          var dataStr = localStorage.get(constants.STORAGE_KEY);
+          if (!dataStr) {
             resolve();
             return;
           }
-          // Sort to select the best identity
-          if (res.identities.length > 1) {
-            // Select the best identity, by sorting using this order
-            //  - same wallet uid
-            //  - is member
-            //  - has a pending membership
-            //  - is not expired
-            //  - is not outdistanced
-            //  - if has certifications
-            //      max(count(certification)
-            //    else
-            //      max(membershipPendingExpiresIn) = must recent membership
-            res.identities = _.sortBy(res.identities, function(idty) {
-              var score = 0;
-              score += (10000000000 * ((data.uid && idty.uid === data.uid) ? 1 : 0));
-              score += (1000000000  * (idty.membershipExpiresIn > 0 ? 1 : 0));
-              score += (100000000   * (idty.membershipPendingExpiresIn > 0 ? 1 : 0));
-              score += (10000000    * (!idty.expired ? 1 : 0));
-              score += (1000000     * (!idty.outdistanced ? 1 : 0));
-              var certCount = !idty.expired && idty.certifications ? idty.certifications.length : 0;
-              score += (1         * (certCount ? certCount : 0));
-              score += (1         * (!certCount && idty.membershipPendingExpiresIn > 0 ? idty.membershipPendingExpiresIn/1000 : 0));
-              return -score;
-            });
-            console.debug('Found {0} identities. Will selected the best one'.format(res.identities.length));
-          }
+          fromJson(dataStr, false)
+            .then(function(storedData){
+              if (storedData && storedData.keypair && storedData.pubkey) {
+                data.keypair = storedData.keypair;
+                data.pubkey = storedData.pubkey;
+                if (storedData.tx && storedData.tx.pendings) {
+                  data.tx.pendings = storedData.tx.pendings;
+                }
+                data.loaded = false;
 
-          // Select the first identity
-          var idty = res.identities[0];
-
-          // Compute useful fields
-          idty.needSelf = false;
-          idty.needMembership = (idty.membershipExpiresIn <= 0 && idty.membershipPendingExpiresIn <= 0 );
-          idty.needRenew = (!idty.needMembership &&
-          idty.membershipExpiresIn <= csSettings.data.timeWarningExpireMembership && idty.membershipPendingExpiresIn <= 0);
-          idty.canMembershipOut = (idty.membershipExpiresIn > 0);
-          idty.pendingMembership = (idty.membershipExpiresIn <= 0 && idty.membershipPendingExpiresIn > 0);
-          idty.certificationCount = (idty.certifications) ? idty.certifications.length : 0;
-          idty.willExpireCertificationCount = idty.certifications ? idty.certifications.reduce(function(count, cert){
-            return count + (cert.expiresIn <= csSettings.data.timeWarningExpire ? 1 : 0);
-          }, 0) : 0;
-
-          data.requirements = idty;
-          data.uid = idty.uid;
-          data.blockUid = idty.meta.timestamp;
-          data.isMember = (idty.membershipExpiresIn > 0);
-
-          var blockParts = idty.meta.timestamp.split('-', 2);
-          var blockNumber = parseInt(blockParts[0]);
-          var blockHash = blockParts[1];
-          // Retrieve registration date
-          return BMA.blockchain.block({block: blockNumber})
-            .then(function(block) {
-              data.sigDate = block.medianTime;
+                return $q.all([
+                  // Call extend api
+                  api.data.raisePromise.login(data),
+
+                  // Load parameters
+                  // This prevent timeout error, when loading a market record after a browser refresh (e.g. F5)
+                  loadParameters(),
 
-              // Check if self has been done on a valid block
-              if (!data.isMember && blockNumber !== 0 && blockHash !== block.hash) {
-                addEvent({type: 'error', message: 'ERROR.WALLET_INVALID_BLOCK_HASH', context: 'requirements'});
-                console.debug("Invalid membership for uid={0}: block hash changed".format(data.uid));
+                  // Load current UD is need by features tour
+                  loadCurrentUD()
+                ]);
               }
-              // Check if self expired
-              else if (!data.isMember && data.requirements.expired) {
-                addEvent({type: 'error', message: 'ERROR.WALLET_IDENTITY_EXPIRED', context: 'requirements'});
-                console.debug("Identity expired for uid={0}.".format(data.uid));
+              else {
+                // Load parameters
+                // This prevent timeout error, when loading a market record after a browser refresh (e.g. F5)
+                return loadParameters();
               }
-              resolve();
             })
-            .catch(function(err){
-              // Special case for currency init (root block not exists): use now
-              if (err && err.ucode == BMA.errorCodes.BLOCK_NOT_FOUND && blockNumber === 0) {
-                data.sigDate = Math.trunc(new Date().getTime() / 1000);
+            .then(function(){
+              resolve(data);
+            })
+            .catch(function(err){reject(err);});
+        });
+      },
+
+      getData = function() {
+        return data;
+      },
+
+      resetRequirements = function() {
+        data.requirements = {
+          needSelf: true,
+          needMembership: true,
+          canMembershipOut: false,
+          needRenew: false,
+          pendingMembership: false,
+          certificationCount: 0,
+          needCertifications: false,
+          needCertificationCount: 0,
+          willNeedCertificationCount: 0
+        };
+        data.blockUid = null;
+        data.isMember = false;
+        data.sigDate = null;
+        cleanEventsByContext('requirements');
+      },
+
+      loadRequirements = function() {
+        return $q(function(resolve, reject) {
+
+          // Clean existing events
+          cleanEventsByContext('requirements');
+
+          // Get requirements
+          BMA.wot.requirements({pubkey: data.pubkey})
+            .then(function(res){
+              if (!res.identities || res.identities.length === 0) {
+                resetRequirements();
+                resolve();
+                return;
+              }
+              // Sort to select the best identity
+              if (res.identities.length > 1) {
+                // Select the best identity, by sorting using this order
+                //  - same wallet uid
+                //  - is member
+                //  - has a pending membership
+                //  - is not expired
+                //  - is not outdistanced
+                //  - if has certifications
+                //      max(count(certification)
+                //    else
+                //      max(membershipPendingExpiresIn) = must recent membership
+                res.identities = _.sortBy(res.identities, function(idty) {
+                  var score = 0;
+                  score += (10000000000 * ((data.uid && idty.uid === data.uid) ? 1 : 0));
+                  score += (1000000000  * (idty.membershipExpiresIn > 0 ? 1 : 0));
+                  score += (100000000   * (idty.membershipPendingExpiresIn > 0 ? 1 : 0));
+                  score += (10000000    * (!idty.expired ? 1 : 0));
+                  score += (1000000     * (!idty.outdistanced ? 1 : 0));
+                  var certCount = !idty.expired && idty.certifications ? idty.certifications.length : 0;
+                  score += (1         * (certCount ? certCount : 0));
+                  score += (1         * (!certCount && idty.membershipPendingExpiresIn > 0 ? idty.membershipPendingExpiresIn/1000 : 0));
+                  return -score;
+                });
+                console.debug('Found {0} identities. Will selected the best one'.format(res.identities.length));
+              }
+
+              // Select the first identity
+              var idty = res.identities[0];
+
+              // Compute useful fields
+              idty.needSelf = false;
+              idty.needMembership = (idty.membershipExpiresIn <= 0 && idty.membershipPendingExpiresIn <= 0 );
+              idty.needRenew = (!idty.needMembership &&
+              idty.membershipExpiresIn <= csSettings.data.timeWarningExpireMembership && idty.membershipPendingExpiresIn <= 0);
+              idty.canMembershipOut = (idty.membershipExpiresIn > 0);
+              idty.pendingMembership = (idty.membershipExpiresIn <= 0 && idty.membershipPendingExpiresIn > 0);
+              idty.certificationCount = (idty.certifications) ? idty.certifications.length : 0;
+              idty.willExpireCertificationCount = idty.certifications ? idty.certifications.reduce(function(count, cert){
+                  return count + (cert.expiresIn <= csSettings.data.timeWarningExpire ? 1 : 0);
+                }, 0) : 0;
+
+              data.requirements = idty;
+              data.uid = idty.uid;
+              data.blockUid = idty.meta.timestamp;
+              data.isMember = (idty.membershipExpiresIn > 0);
+
+              var blockParts = idty.meta.timestamp.split('-', 2);
+              var blockNumber = parseInt(blockParts[0]);
+              var blockHash = blockParts[1];
+              // Retrieve registration date
+              return BMA.blockchain.block({block: blockNumber})
+                .then(function(block) {
+                  data.sigDate = block.medianTime;
+
+                  // Check if self has been done on a valid block
+                  if (!data.isMember && blockNumber !== 0 && blockHash !== block.hash) {
+                    addEvent({type: 'error', message: 'ERROR.WALLET_INVALID_BLOCK_HASH', context: 'requirements'});
+                    console.debug("Invalid membership for uid={0}: block hash changed".format(data.uid));
+                  }
+                  // Check if self expired
+                  else if (!data.isMember && data.requirements.expired) {
+                    addEvent({type: 'error', message: 'ERROR.WALLET_IDENTITY_EXPIRED', context: 'requirements'});
+                    console.debug("Identity expired for uid={0}.".format(data.uid));
+                  }
+                  resolve();
+                })
+                .catch(function(err){
+                  // Special case for currency init (root block not exists): use now
+                  if (err && err.ucode == BMA.errorCodes.BLOCK_NOT_FOUND && blockNumber === 0) {
+                    data.sigDate = Math.trunc(new Date().getTime() / 1000);
+                    resolve();
+                  }
+                  else {
+                    reject(err);
+                  }
+                });
+            })
+            .catch(function(err) {
+              resetRequirements();
+              // 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();
               }
               else {
                 reject(err);
               }
             });
-        })
-        .catch(function(err) {
-          resetRequirements();
-          // 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();
-          }
-          else {
-            reject(err);
-          }
         });
-      });
-    },
-
-    loadSources = function() {
-      return $q(function(resolve, reject) {
-        // Get transactions
-        BMA.tx.sources({pubkey: data.pubkey})
-        .then(function(res){
-          resetSources();
-          var balance = 0;
-          if (res.sources) {
-            _.forEach(res.sources, function(src) {
-              src.consumed = false;
-              balance += powBase(src.amount, src.base);
+      },
+
+      loadSources = function() {
+        return $q(function(resolve, reject) {
+          // Get transactions
+          BMA.tx.sources({pubkey: data.pubkey})
+            .then(function(res){
+              resetSources();
+              var balance = 0;
+              if (res.sources) {
+                _.forEach(res.sources, function(src) {
+                  src.consumed = false;
+                  balance += powBase(src.amount, src.base);
+                });
+                addSources(res.sources);
+              }
+              data.balance = balance;
+              resolve();
+            })
+            .catch(function(err) {
+              resetSources();
+              reject(err);
             });
-            addSources(res.sources);
-          }
-          data.balance = balance;
-          resolve();
-        })
-        .catch(function(err) {
-          resetSources();
-          reject(err);
         });
-      });
-    },
-
-    loadTransactions = function(fromTime) {
-      return $q(function(resolve, reject) {
-        var txHistory = [];
-        var udHistory = [];
-        var txPendings = [];
-
-        var now = new Date().getTime();
-        var nowInSec = Math.trunc(now / 1000);
-        fromTime = fromTime || (nowInSec - csSettings.data.walletHistoryTimeSecond);
-        var processedTxMap = {};
-
-        var reduceTx = function(res){
-          reduceTxAndPush(res.history.sent, txHistory, processedTxMap, true/*exclude pending*/);
-          reduceTxAndPush(res.history.received, txHistory, processedTxMap, true/*exclude pending*/);
-          reduceTxAndPush(res.history.sending, txHistory, processedTxMap, true/*exclude pending*/);
-          reduceTxAndPush(res.history.pending, txPendings, processedTxMap, false/*include pending*/);
-        };
+      },
+
+      loadTransactions = function(fromTime) {
+        return $q(function(resolve, reject) {
+          var txHistory = [];
+          var udHistory = [];
+          var txPendings = [];
+
+          var now = new Date().getTime();
+          var nowInSec = Math.trunc(now / 1000);
+          fromTime = fromTime || (nowInSec - csSettings.data.walletHistoryTimeSecond);
+          var processedTxMap = {};
+
+          var reduceTx = function(res){
+            reduceTxAndPush(res.history.sent, txHistory, processedTxMap, true/*exclude pending*/);
+            reduceTxAndPush(res.history.received, txHistory, processedTxMap, true/*exclude pending*/);
+            reduceTxAndPush(res.history.sending, txHistory, processedTxMap, true/*exclude pending*/);
+            reduceTxAndPush(res.history.pending, txPendings, processedTxMap, false/*include pending*/);
+          };
 
-        var jobs = [
-          // get pendings history
-          BMA.tx.history.pending({pubkey: data.pubkey})
-            .then(reduceTx)
-        ];
-
-        // get TX history since
-        if (fromTime !== -1) {
-          var sliceTime = csSettings.data.walletHistorySliceSecond;
-          for(var i = fromTime - (fromTime % sliceTime); i - sliceTime < nowInSec; i += sliceTime)  {
-            jobs.push(BMA.tx.history.times({pubkey: data.pubkey, from: i, to: i+sliceTime-1})
+          var jobs = [
+            // get pendings history
+            BMA.tx.history.pending({pubkey: data.pubkey})
               .then(reduceTx)
-            );
-          }
+          ];
+
+          // get TX history since
+          if (fromTime !== -1) {
+            var sliceTime = csSettings.data.walletHistorySliceSecond;
+            for(var i = fromTime - (fromTime % sliceTime); i - sliceTime < nowInSec; i += sliceTime)  {
+              jobs.push(BMA.tx.history.times({pubkey: data.pubkey, from: i, to: i+sliceTime-1})
+                .then(reduceTx)
+              );
+            }
 
-          jobs.push(BMA.tx.history.timesNoCache({pubkey: data.pubkey, from: nowInSec - (nowInSec % sliceTime), to: nowInSec+999999999})
-            .then(reduceTx));
-        }
+            jobs.push(BMA.tx.history.timesNoCache({pubkey: data.pubkey, from: nowInSec - (nowInSec % sliceTime), to: nowInSec+999999999})
+              .then(reduceTx));
+          }
 
-        // get all TX
-        else {
-          jobs.push(BMA.tx.history.all({pubkey: data.pubkey})
-            .then(reduceTx)
-          );
-        }
+          // get all TX
+          else {
+            jobs.push(BMA.tx.history.all({pubkey: data.pubkey})
+              .then(reduceTx)
+            );
+          }
 
-        // get UD history
-        if (csSettings.data.showUDHistory) {
-          jobs.push(
-            BMA.ud.history({pubkey: data.pubkey})
-            .then(function(res){
-              udHistory = !res.history || !res.history.history ? [] :
-               res.history.history.reduce(function(res, ud){
-                 if (ud.time < fromTime) return res; // skip to old UD
-                 var amount = powBase(ud.amount, ud.base);
-                 return res.concat({
-                   time: ud.time,
-                   amount: amount,
-                   isUD: true,
-                   block_number: ud.block_number
-                 });
-               }, []);
-            }));
-        }
+          // get UD history
+          if (csSettings.data.showUDHistory) {
+            jobs.push(
+              BMA.ud.history({pubkey: data.pubkey})
+                .then(function(res){
+                  udHistory = !res.history || !res.history.history ? [] :
+                    res.history.history.reduce(function(res, ud){
+                      if (ud.time < fromTime) return res; // skip to old UD
+                      var amount = powBase(ud.amount, ud.base);
+                      return res.concat({
+                        time: ud.time,
+                        amount: amount,
+                        isUD: true,
+                        block_number: ud.block_number
+                      });
+                    }, []);
+                }));
+          }
 
-        // Execute jobs
-        $q.all(jobs)
-        .then(function(){
-          // sort by time desc
-          data.tx.history  = txHistory.concat(udHistory).sort(function(tx1, tx2) {
-             return (tx2.time - tx1.time);
-          });
-          data.tx.pendings = txPendings;
-          data.tx.fromTime = fromTime;
-          data.tx.toTime = data.tx.history.length ? data.tx.history[0].time /*=max(tx.time)*/: fromTime;
+          // Execute jobs
+          $q.all(jobs)
+            .then(function(){
+              // sort by time desc
+              data.tx.history  = txHistory.concat(udHistory).sort(function(tx1, tx2) {
+                return (tx2.time - tx1.time);
+              });
+              data.tx.pendings = txPendings;
+              data.tx.fromTime = fromTime;
+              data.tx.toTime = data.tx.history.length ? data.tx.history[0].time /*=max(tx.time)*/: fromTime;
 
-          // Call extend api
-          return api.data.raisePromise.loadTx(data.tx)
-            .then(function() {
-              console.debug('[wallet] TX history loaded in '+ (new Date().getTime()-now) +'ms');
-              resolve();
+              // Call extend api
+              return api.data.raisePromise.loadTx(data.tx)
+                .then(function() {
+                  console.debug('[wallet] TX history loaded in '+ (new Date().getTime()-now) +'ms');
+                  resolve();
+                });
+            })
+            .catch(function(err) {
+              data.tx.history = [];
+              data.tx.pendings = [];
+              data.tx.errors = [];
+              delete data.tx.fromTime;
+              delete data.tx.toTime;
+              reject(err);
             });
-        })
-        .catch(function(err) {
-          data.tx.history = [];
-          data.tx.pendings = [];
-          data.tx.errors = [];
-          delete data.tx.fromTime;
-          delete data.tx.toTime;
-          reject(err);
         });
-      });
-    },
+      },
 
-    processTransactionsAndSources = function() {
-      return BMA.wot.member.uids()
-        .then(function(uids){
-          var txPendings = [];
-          var txErrors = [];
-          var balance = data.balance;
+      processTransactionsAndSources = function() {
+        return BMA.wot.member.uids()
+          .then(function(uids){
+            var txPendings = [];
+            var txErrors = [];
+            var balance = data.balance;
 
-          // process TX history
-          _.forEach(data.tx.history, function(tx) {
-             tx.uid = uids[tx.pubkey] || null;
-          });
+            // process TX history
+            _.forEach(data.tx.history, function(tx) {
+              tx.uid = uids[tx.pubkey] || null;
+            });
 
-          var processPendingTx = function(tx) {
-            tx.uid = uids[tx.pubkey] || null;
+            var processPendingTx = function(tx) {
+              tx.uid = uids[tx.pubkey] || null;
 
-            var consumedSources = [];
-            var valid = true;
-            if (tx.amount > 0) { // do not check sources from received TX
-              valid = false;
-              // TODO get sources from the issuer ?
-            }
-            else {
-              _.forEach(tx.inputs, function(input) {
-                var inputKey = input.split(':').slice(2).join(':');
-                var srcIndex = data.sourcesIndexByKey[inputKey];
-                if (angular.isDefined(srcIndex)) {
-                  consumedSources.push(data.sources[srcIndex]);
-                }
-                else {
-                  valid = false;
-                  return false; // break
+              var consumedSources = [];
+              var valid = true;
+              if (tx.amount > 0) { // do not check sources from received TX
+                valid = false;
+                // TODO get sources from the issuer ?
+              }
+              else {
+                _.forEach(tx.inputs, function(input) {
+                  var inputKey = input.split(':').slice(2).join(':');
+                  var srcIndex = data.sourcesIndexByKey[inputKey];
+                  if (angular.isDefined(srcIndex)) {
+                    consumedSources.push(data.sources[srcIndex]);
+                  }
+                  else {
+                    valid = false;
+                    return false; // break
+                  }
+                });
+                if (tx.sources) { // add source output
+                  addSources(tx.sources);
                 }
-              });
-              if (tx.sources) { // add source output
-                addSources(tx.sources);
+                delete tx.sources;
+                delete tx.inputs;
               }
-              delete tx.sources;
-              delete tx.inputs;
-            }
-            if (valid) {
-              balance += tx.amount; // update balance
-              txPendings.push(tx);
-              _.forEach(consumedSources, function(src) {
-                src.consumed=true;
-              });
-            }
-            else {
-              txErrors.push(tx);
-            }
-          };
+              if (valid) {
+                balance += tx.amount; // update balance
+                txPendings.push(tx);
+                _.forEach(consumedSources, function(src) {
+                  src.consumed=true;
+                });
+              }
+              else {
+                txErrors.push(tx);
+              }
+            };
 
-          var txs = data.tx.pendings;
-          var retry = true;
-          while(txs && txs.length > 0) {
-            // process TX pendings
-            _.forEach(txs, processPendingTx);
-
-            // Retry once (TX could be chained and processed in a wrong order)
-            if (txErrors.length > 0 && txPendings.length > 0 && retry) {
-              txs = txErrors;
-              txErrors = [];
-              retry = false;
-            }
-            else {
-              txs = null;
+            var txs = data.tx.pendings;
+            var retry = true;
+            while(txs && txs.length > 0) {
+              // process TX pendings
+              _.forEach(txs, processPendingTx);
+
+              // Retry once (TX could be chained and processed in a wrong order)
+              if (txErrors.length > 0 && txPendings.length > 0 && retry) {
+                txs = txErrors;
+                txErrors = [];
+                retry = false;
+              }
+              else {
+                txs = null;
+              }
             }
-          }
 
-          data.tx.pendings = txPendings;
-          data.tx.errors = txErrors;
-          data.balance = balance;
-        });
-    },
+            data.tx.pendings = txPendings;
+            data.tx.errors = txErrors;
+            data.balance = balance;
+          });
+      },
 
-    loadParameters = function() {
-      return $q(function(resolve, reject) {
-        if (data.parameters && data.currency) {
-          resolve();
-          return;
-        }
-        BMA.blockchain.parameters()
-        .then(function(json){
-          data.currency = json.currency;
-          data.parameters = json;
-          if (data.currentUD == -1) data.currentUD = data.parameters.ud0;
-          resolve();
-        })
-        .catch(function(err) {
-          data.currency = null;
-          data.parameters = null;
-          reject(err);
-        });
-      });
-    },
-
-    loadCurrentUD = function() {
-      return BMA.blockchain.stats.ud()
-        .then(function(res){
-          // Special case for currency init
-          if (!res.result.blocks.length) {
-            data.currentUD = data.parameters ? data.parameters.ud0 : -1;
-            return data.currentUD ;
+      loadParameters = function() {
+        return $q(function(resolve, reject) {
+          if (data.parameters && data.currency) {
+            resolve();
+            return;
           }
-          else {
-            var lastBlockWithUD = res.result.blocks[res.result.blocks.length - 1];
-            return BMA.blockchain.block({ block: lastBlockWithUD })
-              .then(function(block){
-                data.currentUD = powBase(block.dividend, block.unitbase);
-                return data.currentUD;
-              })
-              .catch(function(err) {
-                data.currentUD = null;
-                throw err;
-              });
-            }
-        })
-        .catch(function(err) {
-          data.currentUD = null;
-          throw err;
+          BMA.blockchain.parameters()
+            .then(function(json){
+              data.currency = json.currency;
+              data.parameters = json;
+              if (data.currentUD == -1) data.currentUD = data.parameters.ud0;
+              resolve();
+            })
+            .catch(function(err) {
+              data.currency = null;
+              data.parameters = null;
+              reject(err);
+            });
         });
-    },
-
-    // Must be call after loadParameters() and loadRequirements()
-    finishLoadRequirements = function() {
-      data.requirements.needCertificationCount = (!data.requirements.needMembership && (data.requirements.certificationCount < data.parameters.sigQty)) ?
-          (data.parameters.sigQty - data.requirements.certificationCount) : 0;
-      data.requirements.willNeedCertificationCount = (!data.requirements.needMembership &&
-          data.requirements.needCertificationCount === 0 && (data.requirements.certificationCount - data.requirements.willExpireCertificationCount) < data.parameters.sigQty) ?
-          (data.parameters.sigQty - data.requirements.certificationCount + data.requirements.willExpireCertificationCount) : 0;
-      data.requirements.pendingCertificationCount = 0 ; // init to 0, because not loaded here (see wot-service.js)
-
-      // Add user events
-      if (data.requirements.pendingMembership) {
-        addEvent({type:'pending', message: 'ACCOUNT.WAITING_MEMBERSHIP', context: 'requirements'});
-      }
-      if (data.requirements.needCertificationCount > 0) {
-        addEvent({type:'warn', message: 'ACCOUNT.WAITING_CERTIFICATIONS', messageParams: data.requirements, context: 'requirements'});
-      }
-      if (data.requirements.willNeedCertificationCount > 0) {
-        addEvent({type:'warn', message: 'ACCOUNT.WILL_MISSING_CERTIFICATIONS', messageParams: data.requirements, context: 'requirements'});
-      }
-      if (data.requirements.needRenew) {
-        addEvent({type:'warn', message: 'ACCOUNT.WILL_NEED_RENEW_MEMBERSHIP', messageParams: data.requirements, context: 'requirements'});
-      }
-    },
+      },
 
-    loadSigStock = function() {
-      return $q(function(resolve, reject) {
-        // Get certified by, then count written certification
-        BMA.wot.certifiedBy({pubkey: data.pubkey})
+      loadCurrentUD = function() {
+        return BMA.blockchain.stats.ud()
           .then(function(res){
-            data.sigStock = !res.certifications ? 0 : res.certifications.reduce(function(res, cert) {
-              return cert.written === null ? res : res+1;
-            }, 0);
-            resolve();
-          })
-          .catch(function(err) {
-            if (!!err && err.ucode == BMA.errorCodes.NO_MATCHING_MEMBER) {
-              data.sigStock = 0;
-              resolve(); // not found
+            // Special case for currency init
+            if (!res.result.blocks.length) {
+              data.currentUD = data.parameters ? data.parameters.ud0 : -1;
+              return data.currentUD ;
             }
             else {
-              reject(err);
+              var lastBlockWithUD = res.result.blocks[res.result.blocks.length - 1];
+              return BMA.blockchain.block({ block: lastBlockWithUD })
+                .then(function(block){
+                  data.currentUD = powBase(block.dividend, block.unitbase);
+                  return data.currentUD;
+                })
+                .catch(function(err) {
+                  data.currentUD = null;
+                  throw err;
+                });
             }
+          })
+          .catch(function(err) {
+            data.currentUD = null;
+            throw err;
           });
-      });
-    },
+      },
 
-    loadData = function(options) {
+      // Must be call after loadParameters() and loadRequirements()
+      finishLoadRequirements = function() {
+        data.requirements.needCertificationCount = (!data.requirements.needMembership && (data.requirements.certificationCount < data.parameters.sigQty)) ?
+          (data.parameters.sigQty - data.requirements.certificationCount) : 0;
+        data.requirements.willNeedCertificationCount = (!data.requirements.needMembership &&
+        data.requirements.needCertificationCount === 0 && (data.requirements.certificationCount - data.requirements.willExpireCertificationCount) < data.parameters.sigQty) ?
+          (data.parameters.sigQty - data.requirements.certificationCount + data.requirements.willExpireCertificationCount) : 0;
+        data.requirements.pendingCertificationCount = 0 ; // init to 0, because not loaded here (see wot-service.js)
 
-      if (options && options.minData) {
-        return loadMinData(options);
-      }
+        // Add user events
+        if (data.requirements.pendingMembership) {
+          addEvent({type:'pending', message: 'ACCOUNT.WAITING_MEMBERSHIP', context: 'requirements'});
+        }
+        if (data.requirements.needCertificationCount > 0) {
+          addEvent({type:'warn', message: 'ACCOUNT.WAITING_CERTIFICATIONS', messageParams: data.requirements, context: 'requirements'});
+        }
+        if (data.requirements.willNeedCertificationCount > 0) {
+          addEvent({type:'warn', message: 'ACCOUNT.WILL_MISSING_CERTIFICATIONS', messageParams: data.requirements, context: 'requirements'});
+        }
+        if (data.requirements.needRenew) {
+          addEvent({type:'warn', message: 'ACCOUNT.WILL_NEED_RENEW_MEMBERSHIP', messageParams: data.requirements, context: 'requirements'});
+        }
+      },
 
-      if (options || data.loaded) {
-        return refreshData(options);
-      }
+      loadSigStock = function() {
+        return $q(function(resolve, reject) {
+          // Get certified by, then count written certification
+          BMA.wot.certifiedBy({pubkey: data.pubkey})
+            .then(function(res){
+              data.sigStock = !res.certifications ? 0 : res.certifications.reduce(function(res, cert) {
+                  return cert.written === null ? res : res+1;
+                }, 0);
+              resolve();
+            })
+            .catch(function(err) {
+              if (!!err && err.ucode == BMA.errorCodes.NO_MATCHING_MEMBER) {
+                data.sigStock = 0;
+                resolve(); // not found
+              }
+              else {
+                reject(err);
+              }
+            });
+        });
+      },
+
+      loadData = function(options) {
+
+        if (options && options.minData) {
+          return loadMinData(options);
+        }
+
+        if (options || data.loaded) {
+          return refreshData(options);
+        }
 
-      return loadFullData();
-    },
+        return loadFullData();
+      },
 
-    loadFullData = function() {
-      data.loaded = false;
+      loadFullData = function() {
+        data.loaded = false;
 
-      return $q.all([
+        return $q.all([
 
           // Get currency parameters
           loadParameters(),
@@ -749,576 +749,576 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
               console.error(err);
             })
         ])
-        .then(function() {
-          // Process transactions and sources
-          return processTransactionsAndSources();
-        })
-        .then(function() {
-          finishLoadRequirements(); // must be call after loadParameters() and loadRequirements()
-          return api.data.raisePromise.finishLoad(data)
-            .catch(function(err) {
-              console.error('Error while finishing wallet data load, on extension point. Try to continue');
-              console.error(err);
-            });
-        })
-        .then(function() {
-          data.loaded = true;
-          return data;
-        })
-        .catch(function(err) {
-          data.loaded = false;
-          throw err;
-        });
-    },
-
-    loadMinData = function(options) {
-      options = options || {};
-      options.parameters = angular.isDefined(options.parameters) ? options.parameters : !data.parameters; // do not load if already done
-      options.requirements = angular.isDefined(options.requirements) ? options.requirements :
-        (!data.requirements || angular.isUndefined(data.requirements.needSelf));
-      if (!options.parameters && !options.requirements) {
-        return $q.when(data);
-      }
-      return refreshData(options);
-    },
+          .then(function() {
+            // Process transactions and sources
+            return processTransactionsAndSources();
+          })
+          .then(function() {
+            finishLoadRequirements(); // must be call after loadParameters() and loadRequirements()
+            return api.data.raisePromise.finishLoad(data)
+              .catch(function(err) {
+                console.error('Error while finishing wallet data load, on extension point. Try to continue');
+                console.error(err);
+              });
+          })
+          .then(function() {
+            data.loaded = true;
+            return data;
+          })
+          .catch(function(err) {
+            data.loaded = false;
+            throw err;
+          });
+      },
+
+      loadMinData = function(options) {
+        options = options || {};
+        options.parameters = angular.isDefined(options.parameters) ? options.parameters : !data.parameters; // do not load if already done
+        options.requirements = angular.isDefined(options.requirements) ? options.requirements :
+          (!data.requirements || angular.isUndefined(data.requirements.needSelf));
+        if (!options.parameters && !options.requirements) {
+          return $q.when(data);
+        }
+        return refreshData(options);
+      },
 
-    refreshData = function(options) {
+      refreshData = function(options) {
         options = options || {
-          parameters: !data.parameters, // do not load if already done
-          currentUd: true,
-          requirements: true,
-          sources: true,
-          tx: {
-            enable: true,
-            fromTime: data.tx ? data.tx.fromTime : undefined // keep previous time
-          },
-          sigStock: true,
-          api: true
-        };
+            parameters: !data.parameters, // do not load if already done
+            currentUd: true,
+            requirements: true,
+            sources: true,
+            tx: {
+              enable: true,
+              fromTime: data.tx ? data.tx.fromTime : undefined // keep previous time
+            },
+            sigStock: true,
+            api: true
+          };
 
-      // Force some load (parameters & requirements) if not already loaded
-      options.parameters = angular.isDefined(options.parameters) ? options.parameters : !data.parameters;
-      options.requirements = angular.isDefined(options.requirements) ? options.requirements : !data.requirements;
+        // Force some load (parameters & requirements) if not already loaded
+        options.parameters = angular.isDefined(options.parameters) ? options.parameters : !data.parameters;
+        options.requirements = angular.isDefined(options.requirements) ? options.requirements : !data.requirements;
 
-      var jobs = [];
+        var jobs = [];
 
-      // Reset events
-      cleanEventsByContext('requirements');
+        // Reset events
+        cleanEventsByContext('requirements');
 
-      // Get parameters
-      if (options.parameters) jobs.push(loadParameters());
+        // Get parameters
+        if (options.parameters) jobs.push(loadParameters());
 
-      // Get current UD
-      if (options.currentUd) jobs.push(loadCurrentUD());
+        // Get current UD
+        if (options.currentUd) jobs.push(loadCurrentUD());
 
-      // Get requirements
-      if (options.requirements) {
-        jobs.push(loadRequirements()
+        // Get requirements
+        if (options.requirements) {
+          jobs.push(loadRequirements()
+            .then(function() {
+              finishLoadRequirements();
+            }));
+        }
+
+        if (options.sources || (options.tx && options.tx.enable)) {
+          // Get sources
+          jobs.push(loadSources());
+
+          // Get transactions
+          jobs.push(loadTransactions(options.tx.fromTime));
+        }
+
+        // Load sigStock
+        if (options.sigStock) jobs.push(loadSigStock());
+
+        // API extension (force if no other jobs)
+        if (!jobs.length || options.api) jobs.push(api.data.raisePromise.load(data, options));
+
+        return $q.all(jobs)
           .then(function() {
-            finishLoadRequirements();
-          }));
-      }
+            if (options.sources || (options.tx && options.tx.enable)) {
+              // Process transactions and sources
+              return processTransactionsAndSources();
+            }
+          })
+          .then(function(){
+            return api.data.raisePromise.finishLoad(data);
+          })
+          .then(function(){
+            return data;
+          });
+      },
 
-      if (options.sources || (options.tx && options.tx.enable)) {
-        // Get sources
-        jobs.push(loadSources());
+      isBase = function(amount, base) {
+        if (!base) return true; // no base
+        if (amount < Math.pow(10, base)) return false; // too small
+        var rest = '00000000' + amount;
+        var lastDigits = parseInt(rest.substring(rest.length-base));
+        return lastDigits === 0; // no rest
+      },
 
-        // Get transactions
-        jobs.push(loadTransactions(options.tx.fromTime));
-      }
+      truncBase = function(amount, base) {
+        var pow = Math.pow(10, base); // = min value in this base
+        if (amount < pow) return 0;
+        return Math.trunc(amount / pow ) * pow;
+      },
 
-      // Load sigStock
-      if (options.sigStock) jobs.push(loadSigStock());
+      truncBaseOrMinBase = function(amount, base) {
+        var pow = Math.pow(10, base);
+        if (amount < pow) return pow; //
+        return Math.trunc(amount / pow ) * pow;
+      },
 
-      // API extension (force if no other jobs)
-      if (!jobs.length || options.api) jobs.push(api.data.raisePromise.load(data, options));
+      powBase = function(amount, base) {
+        return base <= 0 ? amount : amount * Math.pow(10, base);
+      },
 
-      return $q.all(jobs)
-      .then(function() {
-        if (options.sources || (options.tx && options.tx.enable)) {
-          // Process transactions and sources
-          return processTransactionsAndSources();
+      getInputs = function(amount, outputBase, filterBase) {
+        if (angular.isUndefined(filterBase)) {
+          filterBase = outputBase;
         }
-      })
-      .then(function(){
-        return api.data.raisePromise.finishLoad(data);
-      })
-      .then(function(){
-        return data;
-      });
-    },
-
-    isBase = function(amount, base) {
-      if (!base) return true; // no base
-      if (amount < Math.pow(10, base)) return false; // too small
-      var rest = '00000000' + amount;
-      var lastDigits = parseInt(rest.substring(rest.length-base));
-      return lastDigits === 0; // no rest
-    },
-
-    truncBase = function(amount, base) {
-      var pow = Math.pow(10, base); // = min value in this base
-      if (amount < pow) return 0;
-      return Math.trunc(amount / pow ) * pow;
-    },
-
-    truncBaseOrMinBase = function(amount, base) {
-      var pow = Math.pow(10, base);
-      if (amount < pow) return pow; //
-      return Math.trunc(amount / pow ) * pow;
-    },
-
-    powBase = function(amount, base) {
-      return base <= 0 ? amount : amount * Math.pow(10, base);
-    },
-
-    getInputs = function(amount, outputBase, filterBase) {
-      if (angular.isUndefined(filterBase)) {
-        filterBase = outputBase;
-      }
-      var sourcesAmount = 0;
-      var sources = [];
-      var minBase = filterBase;
-      var maxBase = filterBase;
-      _.find(data.sources, function(source) {
-        if (!source.consumed && source.base == filterBase){
-          sourcesAmount += powBase(source.amount, source.base);
-          sources.push(source);
-        }
-        // Stop if enough sources
-        return (sourcesAmount >= amount);
-      });
+        var sourcesAmount = 0;
+        var sources = [];
+        var minBase = filterBase;
+        var maxBase = filterBase;
+        _.find(data.sources, function(source) {
+          if (!source.consumed && source.base == filterBase){
+            sourcesAmount += powBase(source.amount, source.base);
+            sources.push(source);
+          }
+          // Stop if enough sources
+          return (sourcesAmount >= amount);
+        });
 
-      // IF not enough sources, get add inputs from lower base (recursively)
-      if (sourcesAmount < amount && filterBase > 0) {
-        filterBase -= 1;
-        var missingAmount = amount - sourcesAmount;
-        var lowerInputs = getInputs(missingAmount, outputBase, filterBase);
-
-        // Add lower base inputs to result
-        if (lowerInputs.amount > 0) {
-          minBase = lowerInputs.minBase;
-          sourcesAmount += lowerInputs.amount;
-          [].push.apply(sources, lowerInputs.sources);
+        // IF not enough sources, get add inputs from lower base (recursively)
+        if (sourcesAmount < amount && filterBase > 0) {
+          filterBase -= 1;
+          var missingAmount = amount - sourcesAmount;
+          var lowerInputs = getInputs(missingAmount, outputBase, filterBase);
+
+          // Add lower base inputs to result
+          if (lowerInputs.amount > 0) {
+            minBase = lowerInputs.minBase;
+            sourcesAmount += lowerInputs.amount;
+            [].push.apply(sources, lowerInputs.sources);
+          }
         }
-      }
 
-      return {
-        minBase: minBase,
-        maxBase: maxBase,
-        amount: sourcesAmount,
-        sources: sources
-      };
-    },
-
-    /**
-    * Send a new transaction
-    */
-    transfer = function(destPub, amount, comments, useRelative) {
-      return $q(function(resolve, reject) {
-        BMA.blockchain.current(true/*cache*/)
-        .then(function(block) {
-          if (!BMA.regex.PUBKEY.test(destPub)){
-            reject({message:'ERROR.INVALID_PUBKEY'}); return;
-          }
-          if (!BMA.regex.COMMENT.test(comments)){
-            reject({message:'ERROR.INVALID_COMMENT'}); return;
-          }
-          if (!isLogin()){
-            reject({message:'ERROR.NEED_LOGIN_FIRST'}); return;
-          }
-          if (!amount) {
-            reject({message:'ERROR.AMOUNT_REQUIRED'}); return;
-          }
-          if (amount <= 0) {
-            reject({message:'ERROR.AMOUNT_NEGATIVE'}); return;
-          }
-          amount = Math.floor(amount); // remove decimals
+        return {
+          minBase: minBase,
+          maxBase: maxBase,
+          amount: sourcesAmount,
+          sources: sources
+        };
+      },
 
-          var inputs = {
-            amount: 0,
-            minBase: block.unitbase,
-            maxBase: block.unitbase + 1,
-            sources : []
-          };
+      /**
+       * Send a new transaction
+       */
+      transfer = function(destPub, amount, comments, useRelative) {
+        return $q(function(resolve, reject) {
+          BMA.blockchain.current(true/*cache*/)
+            .then(function(block) {
+              if (!BMA.regex.PUBKEY.test(destPub)){
+                reject({message:'ERROR.INVALID_PUBKEY'}); return;
+              }
+              if (!BMA.regex.COMMENT.test(comments)){
+                reject({message:'ERROR.INVALID_COMMENT'}); return;
+              }
+              if (!isLogin()){
+                reject({message:'ERROR.NEED_LOGIN_FIRST'}); return;
+              }
+              if (!amount) {
+                reject({message:'ERROR.AMOUNT_REQUIRED'}); return;
+              }
+              if (amount <= 0) {
+                reject({message:'ERROR.AMOUNT_NEGATIVE'}); return;
+              }
+              amount = Math.floor(amount); // remove decimals
 
-          // Get inputs, starting to use current base sources
-          var amountBase = 0;
-          while (inputs.amount < amount && amountBase <= block.unitbase) {
-            inputs = getInputs(amount, block.unitbase);
+              var inputs = {
+                amount: 0,
+                minBase: block.unitbase,
+                maxBase: block.unitbase + 1,
+                sources : []
+              };
 
-            if (inputs.amount < amount) {
-              // try to reduce amount (replace last digits to zero)
-              amountBase++;
-              if (amountBase <= block.unitbase) {
-                amount = truncBase(amount, amountBase);
+              // Get inputs, starting to use current base sources
+              var amountBase = 0;
+              while (inputs.amount < amount && amountBase <= block.unitbase) {
+                inputs = getInputs(amount, block.unitbase);
+
+                if (inputs.amount < amount) {
+                  // try to reduce amount (replace last digits to zero)
+                  amountBase++;
+                  if (amountBase <= block.unitbase) {
+                    amount = truncBase(amount, amountBase);
+                  }
+                }
               }
-            }
-          }
 
-          if (inputs.amount < amount) {
-            if (data.balance < amount) {
-              reject({message:'ERROR.NOT_ENOUGH_CREDIT'}); return;
-            }
-            else if (inputs.amount === 0) {
-              reject({message:'ERROR.ALL_SOURCES_USED'}); return;
-            }
-            else {
-              $translate('COMMON.UD')
-              .then(function(UD) {
-                var params;
-                if(useRelative) {
-                  params = {
-                    amount: ($filter('formatDecimal')(inputs.amount / data.currentUD)),
-                    unit: UD,
-                    subUnit: $filter('abbreviate')(data.currency)
-                  };
+              if (inputs.amount < amount) {
+                if (data.balance < amount) {
+                  reject({message:'ERROR.NOT_ENOUGH_CREDIT'}); return;
+                }
+                else if (inputs.amount === 0) {
+                  reject({message:'ERROR.ALL_SOURCES_USED'}); return;
                 }
                 else {
-                  params = {
-                    amount: ($filter('formatInteger')(inputs.amount)),
-                    unit: $filter('abbreviate')(data.currency),
-                    subUnit: ''
-                  };
+                  $translate('COMMON.UD')
+                    .then(function(UD) {
+                      var params;
+                      if(useRelative) {
+                        params = {
+                          amount: ($filter('formatDecimal')(inputs.amount / data.currentUD)),
+                          unit: UD,
+                          subUnit: $filter('abbreviate')(data.currency)
+                        };
+                      }
+                      else {
+                        params = {
+                          amount: ($filter('formatInteger')(inputs.amount)),
+                          unit: $filter('abbreviate')(data.currency),
+                          subUnit: ''
+                        };
+                      }
+                      $translate('ERROR.NOT_ENOUGH_SOURCES', params)
+                        .then(function(message) {
+                          reject({message: message});
+                        });
+                    });
                 }
-                $translate('ERROR.NOT_ENOUGH_SOURCES', params)
-                .then(function(message) {
-                  reject({message: message});
-                });
-              });
-            }
-            return;
-          }
-          // Avoid to get outputs on lower base
-          if (amountBase < inputs.minBase && !isBase(amount, inputs.minBase)) {
-            amount = truncBaseOrMinBase(amount, inputs.minBase);
-            console.debug("[wallet] Amount has been truncate to " + amount);
-          }
-          else if (amountBase > 0) {
-            console.debug("[wallet] Amount has been truncate to " + amount);
-          }
-
-          // Send tx
-          createAndSendTx(block, destPub, amount, inputs, comments)
-            .then(function(res) {
-              data.balance -= amount;
-              _.forEach(inputs.sources, function(source) {
-                source.consumed=true;
-              });
-
-              // Add new sources
-              if (res && res.sources.length) {
-                addSources(res.sources);
+                return;
+              }
+              // Avoid to get outputs on lower base
+              if (amountBase < inputs.minBase && !isBase(amount, inputs.minBase)) {
+                amount = truncBaseOrMinBase(amount, inputs.minBase);
+                console.debug("[wallet] Amount has been truncate to " + amount);
+              }
+              else if (amountBase > 0) {
+                console.debug("[wallet] Amount has been truncate to " + amount);
               }
 
-              // Add TX to pendings
-              BMA.wot.member.get(destPub)
-                .then(function(member) {
-                  data.tx.pendings.unshift({
-                    time: (Math.floor(moment().utc().valueOf() / 1000)),
-                    amount: -amount,
-                    pubkey: destPub,
-                    uid: member ? member.uid : null,
-                    comment: comments,
-                    isUD: false,
-                    hash: res.hash,
-                    locktime: 0,
-                    block_number: null
+              // Send tx
+              createAndSendTx(block, destPub, amount, inputs, comments)
+                .then(function(res) {
+                  data.balance -= amount;
+                  _.forEach(inputs.sources, function(source) {
+                    source.consumed=true;
                   });
-                  store(); // save pendings in local storage
-                  resolve();
+
+                  // Add new sources
+                  if (res && res.sources.length) {
+                    addSources(res.sources);
+                  }
+
+                  // Add TX to pendings
+                  BMA.wot.member.get(destPub)
+                    .then(function(member) {
+                      data.tx.pendings.unshift({
+                        time: (Math.floor(moment().utc().valueOf() / 1000)),
+                        amount: -amount,
+                        pubkey: destPub,
+                        uid: member ? member.uid : null,
+                        comment: comments,
+                        isUD: false,
+                        hash: res.hash,
+                        locktime: 0,
+                        block_number: null
+                      });
+                      store(); // save pendings in local storage
+                      resolve();
+                    }).catch(function(err){reject(err);});
                 }).catch(function(err){reject(err);});
-            }).catch(function(err){reject(err);});
-        });
-      });
-    },
-
-    /**
-     * Create TX doc and send it
-     * @param block the current block
-     * @param destPub
-     * @param amount
-     * @param inputs
-     * @param comments
-     * @return the hash of the sent TX
-     */
-    createAndSendTx = function(block, destPub, amount, inputs, comments) {
-
-      // Make sure a TX in compact mode has no more than 100 lines (fix #118)
-      if (inputs.sources.length > 40) {
-        console.debug("[Wallet] TX has to many sources. Will chain TX...");
-
-        // Compute a slice of sources
-        var firstSlice = {
-          minBase: block.unitbase,
-          maxBase: 0,
-          amount: 0,
-          sources: inputs.sources.slice(0, 39)
-        };
-        _.forEach(firstSlice.sources, function(source) {
-          if (source.base < firstSlice.minBase) firstSlice.minBase = source.base;
-          if (source.base > firstSlice.maxBase) firstSlice.maxBase = source.base;
-          firstSlice.amount += powBase(source.amount, source.base);
+            });
         });
+      },
 
-        // Send inputs first slice
-        return createAndSendTx(block, data.pubkey/*to himself*/, firstSlice.amount, firstSlice) // comment ot need
-          .then(function(res) {
-            _.forEach(firstSlice.sources, function(source) {
-              source.consumed=true;
-            });
-            data.sources.push(res.sources);
+      /**
+       * Create TX doc and send it
+       * @param block the current block
+       * @param destPub
+       * @param amount
+       * @param inputs
+       * @param comments
+       * @return the hash of the sent TX
+       */
+      createAndSendTx = function(block, destPub, amount, inputs, comments) {
+
+        // Make sure a TX in compact mode has no more than 100 lines (fix #118)
+        if (inputs.sources.length > 40) {
+          console.debug("[Wallet] TX has to many sources. Will chain TX...");
+
+          // Compute a slice of sources
+          var firstSlice = {
+            minBase: block.unitbase,
+            maxBase: 0,
+            amount: 0,
+            sources: inputs.sources.slice(0, 39)
+          };
+          _.forEach(firstSlice.sources, function(source) {
+            if (source.base < firstSlice.minBase) firstSlice.minBase = source.base;
+            if (source.base > firstSlice.maxBase) firstSlice.maxBase = source.base;
+            firstSlice.amount += powBase(source.amount, source.base);
+          });
 
-            var secondSlice = {
-              minBase: block.unitbase,
-              maxBase: 0,
-              amount: 0,
-              sources: inputs.sources.slice(40).concat(res.sources)
-            };
-            _.forEach(secondSlice.sources, function(source) {
-              if (source.base < secondSlice.minBase) secondSlice.minBase = source.base;
-              if (source.base > secondSlice.maxBase) secondSlice.maxBase = source.base;
-              secondSlice.amount += source.amount;
-            });
+          // Send inputs first slice
+          return createAndSendTx(block, data.pubkey/*to himself*/, firstSlice.amount, firstSlice) // comment ot need
+            .then(function(res) {
+              _.forEach(firstSlice.sources, function(source) {
+                source.consumed=true;
+              });
+              data.sources.push(res.sources);
 
-            // Send inputs second slice (recursive call)
-            return createAndSendTx(block, destPub, amount, secondSlice, comments);
-          });
-      }
+              var secondSlice = {
+                minBase: block.unitbase,
+                maxBase: 0,
+                amount: 0,
+                sources: inputs.sources.slice(40).concat(res.sources)
+              };
+              _.forEach(secondSlice.sources, function(source) {
+                if (source.base < secondSlice.minBase) secondSlice.minBase = source.base;
+                if (source.base > secondSlice.maxBase) secondSlice.maxBase = source.base;
+                secondSlice.amount += source.amount;
+              });
 
-      var tx = 'Version: '+ constants.TX_VERSION +'\n' +
-        'Type: Transaction\n' +
-        'Currency: ' + data.currency + '\n' +
-        'Blockstamp: ' + block.number + '-' + block.hash + '\n' +
-        'Locktime: 0\n' + // no lock
-        'Issuers:\n' +
-        data.pubkey + '\n' +
-        'Inputs:\n';
-
-      _.forEach(inputs.sources, function(source) {
-        // if D : AMOUNT:BASE:D:PUBLIC_KEY:BLOCK_ID
-        // if T : AMOUNT:BASE:T:T_HASH:T_INDEX
-        tx += [source.amount, source.base, source.type, source.identifier,source.noffset].join(':')+"\n";
-      });
+              // Send inputs second slice (recursive call)
+              return createAndSendTx(block, destPub, amount, secondSlice, comments);
+            });
+        }
 
-      tx += 'Unlocks:\n';
-      for (i=0; i<inputs.sources.length; i++) {
-        // INPUT_INDEX:UNLOCK_CONDITION
-        tx += i + ':SIG(0)\n';
-      }
+        var tx = 'Version: '+ constants.TX_VERSION +'\n' +
+          'Type: Transaction\n' +
+          'Currency: ' + data.currency + '\n' +
+          'Blockstamp: ' + block.number + '-' + block.hash + '\n' +
+          'Locktime: 0\n' + // no lock
+          'Issuers:\n' +
+          data.pubkey + '\n' +
+          'Inputs:\n';
+
+        _.forEach(inputs.sources, function(source) {
+          // if D : AMOUNT:BASE:D:PUBLIC_KEY:BLOCK_ID
+          // if T : AMOUNT:BASE:T:T_HASH:T_INDEX
+          tx += [source.amount, source.base, source.type, source.identifier,source.noffset].join(':')+"\n";
+        });
 
-      tx += 'Outputs:\n';
-      // AMOUNT:BASE:CONDITIONS
-      var rest = amount;
-      var outputBase = inputs.maxBase;
-      var outputAmount;
-      var outputOffset = 0;
-      var newSources = [];
-      // Outputs to receiver (if not himself)
-      if (destPub !== data.pubkey) {
+        tx += 'Unlocks:\n';
+        for (i=0; i<inputs.sources.length; i++) {
+          // INPUT_INDEX:UNLOCK_CONDITION
+          tx += i + ':SIG(0)\n';
+        }
+
+        tx += 'Outputs:\n';
+        // AMOUNT:BASE:CONDITIONS
+        var rest = amount;
+        var outputBase = inputs.maxBase;
+        var outputAmount;
+        var outputOffset = 0;
+        var newSources = [];
+        // Outputs to receiver (if not himself)
+        if (destPub !== data.pubkey) {
+          while(rest > 0) {
+            outputAmount = truncBase(rest, outputBase);
+            rest -= outputAmount;
+            if (outputAmount > 0) {
+              outputAmount = outputBase === 0 ? outputAmount : outputAmount / Math.pow(10, outputBase);
+              tx += outputAmount + ':' + outputBase + ':SIG(' + destPub + ')\n';
+              outputOffset++;
+            }
+            outputBase--;
+          }
+          rest = inputs.amount - amount;
+          outputBase = inputs.maxBase;
+        }
+        // Outputs to himself
         while(rest > 0) {
           outputAmount = truncBase(rest, outputBase);
           rest -= outputAmount;
           if (outputAmount > 0) {
             outputAmount = outputBase === 0 ? outputAmount : outputAmount / Math.pow(10, outputBase);
-            tx += outputAmount + ':' + outputBase + ':SIG(' + destPub + ')\n';
+            tx += outputAmount +':'+outputBase+':SIG('+data.pubkey+')\n';
+            newSources.push({
+              type: 'T',
+              noffset: outputOffset,
+              amount: outputAmount,
+              base: outputBase
+            });
             outputOffset++;
           }
           outputBase--;
         }
-        rest = inputs.amount - amount;
-        outputBase = inputs.maxBase;
-      }
-      // Outputs to himself
-      while(rest > 0) {
-        outputAmount = truncBase(rest, outputBase);
-        rest -= outputAmount;
-        if (outputAmount > 0) {
-          outputAmount = outputBase === 0 ? outputAmount : outputAmount / Math.pow(10, outputBase);
-          tx += outputAmount +':'+outputBase+':SIG('+data.pubkey+')\n';
-          newSources.push({
-            type: 'T',
-            noffset: outputOffset,
-            amount: outputAmount,
-            base: outputBase
-          });
-          outputOffset++;
-        }
-        outputBase--;
-      }
 
-      tx += "Comment: "+ (comments||"") + "\n";
+        tx += "Comment: "+ (comments||"") + "\n";
 
-      return CryptoUtils.sign(tx, data.keypair)
-        .then(function(signature) {
-          var signedTx = tx + signature + "\n";
-          return BMA.tx.process({transaction: signedTx})
-            .catch(function(err) {
-              if (err && err.ucode === BMA.errorCodes.TX_ALREADY_PROCESSED) {
-                // continue
-                return;
+        return CryptoUtils.sign(tx, data.keypair)
+          .then(function(signature) {
+            var signedTx = tx + signature + "\n";
+            return BMA.tx.process({transaction: signedTx})
+              .catch(function(err) {
+                if (err && err.ucode === BMA.errorCodes.TX_ALREADY_PROCESSED) {
+                  // continue
+                  return;
+                }
+                throw err;
+              })
+              .then(function() {
+                return CryptoUtils.util.hash(signedTx);
+              })
+              .then(function(txHash) {
+                _.forEach(newSources, function(output) {
+                  output.identifier= txHash;
+                  output.consumed = false;
+                  output.pending = true;
+                });
+                return {
+                  hash: txHash,
+                  sources: newSources
+                };
+              });
+          });
+      },
+
+      checkUidNotExists = function(uid, pubkey) {
+        return $q(function(resolve, reject) {
+          BMA.wot.lookup({ search: uid }) // search on uid
+            .then(function(res) {
+              var found = res.results &&
+                res.results.length > 0 &&
+                res.results.some(function(pub){
+                  return pub.uids && pub.uids.length > 0 &&
+                    pub.uids.some(function(idty) {
+                      return (idty.uid === uid) && // same Uid
+                        (idty.revoked || pub.pubkey !== pubkey); // but not same pubkey
+                    });
+                });
+              if (found) { // uid is already used : display a message and call failed callback
+                reject('ACCOUNT.NEW.MSG_UID_ALREADY_USED');
+              }
+              else {
+                resolve(uid);
               }
-              throw err;
             })
-            .then(function() {
-              return CryptoUtils.util.hash(signedTx);
+            .catch(function() {
+              resolve(uid); // not found, so OK
+            });
+        });
+      },
+
+      checkPubkeyNotExists = function(uid, pubkey) {
+        return $q(function(resolve, reject) {
+          BMA.wot.lookup({ search: pubkey }) // search on pubkey
+            .then(function(res) {
+              var found = res.results &&
+                res.results.length > 0 &&
+                res.results.some(function(pub){
+                  return pub.pubkey === pubkey &&
+                    pub.uids && pub.uids.length > 0 &&
+                    pub.uids.some(function(idty) {
+                      return (!idty.revoked); // excluded revoked uid
+                    });
+                });
+              if (found) { // uid is already used : display a message and reopen the popup
+                reject('ACCOUNT.NEW.MSG_PUBKEY_ALREADY_USED');
+              }
+              else {
+                resolve(uid);
+              }
             })
-            .then(function(txHash) {
-              _.forEach(newSources, function(output) {
-                output.identifier= txHash;
-                output.consumed = false;
-                output.pending = true;
-              });
-              return {
-                hash: txHash,
-                sources: newSources
-              };
+            .catch(function() {
+              resolve(uid); // not found, so OK
             });
         });
-    },
-
-    checkUidNotExists = function(uid, pubkey) {
-      return $q(function(resolve, reject) {
-        BMA.wot.lookup({ search: uid }) // search on uid
-          .then(function(res) {
-            var found = res.results &&
-              res.results.length > 0 &&
-              res.results.some(function(pub){
-                return pub.uids && pub.uids.length > 0 &&
-                  pub.uids.some(function(idty) {
-                    return (idty.uid === uid) && // same Uid
-                      (idty.revoked || pub.pubkey !== pubkey); // but not same pubkey
-                  });
-              });
-            if (found) { // uid is already used : display a message and call failed callback
-              reject('ACCOUNT.NEW.MSG_UID_ALREADY_USED');
-            }
-            else {
-              resolve(uid);
-            }
-          })
-          .catch(function() {
-            resolve(uid); // not found, so OK
-          });
-      });
-    },
-
-    checkPubkeyNotExists = function(uid, pubkey) {
-      return $q(function(resolve, reject) {
-        BMA.wot.lookup({ search: pubkey }) // search on pubkey
-          .then(function(res) {
-            var found = res.results &&
-              res.results.length > 0 &&
-              res.results.some(function(pub){
-                return pub.pubkey === pubkey &&
-                  pub.uids && pub.uids.length > 0 &&
-                  pub.uids.some(function(idty) {
-                    return (!idty.revoked); // excluded revoked uid
-                  });
-              });
-            if (found) { // uid is already used : display a message and reopen the popup
-              reject('ACCOUNT.NEW.MSG_PUBKEY_ALREADY_USED');
-            }
-            else {
-              resolve(uid);
-            }
-          })
-          .catch(function() {
-            resolve(uid); // not found, so OK
+      },
+
+      getIdentityDocument = function(uid, blockUid) {
+        uid = uid || data.uid;
+        blockUid = blockUid || data.blockUid;
+        if (!uid || !blockUid) {
+          throw {message: 'ERROR.WALLET_HAS_NO_SELF'};
+        }
+        if (data.requirements && data.requirements.expired) {
+          throw {message: 'ERROR.WALLET_IDENTITY_EXPIRED'};
+        }
+
+        var identity = 'Version: '+ constants.IDTY_VERSION +'\n' +
+          'Type: Identity\n' +
+          'Currency: ' + data.currency + '\n' +
+          'Issuer: ' + data.pubkey + '\n' +
+          'UniqueID: ' + uid + '\n' +
+          'Timestamp: ' + blockUid + '\n';
+        return CryptoUtils.sign(identity, data.keypair)
+          .then(function(signature) {
+            identity += signature + '\n';
+            console.debug('Has generate an identity document:\n----\n' + identity + '----');
+            return identity;
           });
-      });
-    },
+      },
 
-    getIdentityDocument = function(uid, blockUid) {
-      uid = uid || data.uid;
-      blockUid = blockUid || data.blockUid;
-      if (!uid || !blockUid) {
-        throw {message: 'ERROR.WALLET_HAS_NO_SELF'};
-      }
-      if (data.requirements && data.requirements.expired) {
-        throw {message: 'ERROR.WALLET_IDENTITY_EXPIRED'};
-      }
+      /**
+       * Send self identity
+       */
+      self = function(uid, needToLoadRequirements) {
+        if (!BMA.regex.USER_ID.test(uid)){
+          throw new Error('ERROR.INVALID_USER_ID');
+        }
+        var block;
+        return $q.all([
+          // check uid used by another pubkey
+          checkUidNotExists(uid, data.pubkey),
 
-      var identity = 'Version: '+ constants.IDTY_VERSION +'\n' +
-        'Type: Identity\n' +
-        'Currency: ' + data.currency + '\n' +
-        'Issuer: ' + data.pubkey + '\n' +
-        'UniqueID: ' + uid + '\n' +
-        'Timestamp: ' + blockUid + '\n';
-      return CryptoUtils.sign(identity, data.keypair)
-        .then(function(signature) {
-          identity += signature + '\n';
-          console.debug('Has generate an identity document:\n----\n' + identity + '----');
-          return identity;
-        });
-    },
-
-    /**
-    * Send self identity
-    */
-    self = function(uid, needToLoadRequirements) {
-      if (!BMA.regex.USER_ID.test(uid)){
-        throw new Error('ERROR.INVALID_USER_ID');
-      }
-      var block;
-      return $q.all([
-        // check uid used by another pubkey
-        checkUidNotExists(uid, data.pubkey),
-
-        // Load parameters (need to known the currency)
-        loadParameters(),
-
-        // Get th current block
-        BMA.blockchain.current()
-          .then(function(current) {
-            block = current;
+          // Load parameters (need to known the currency)
+          loadParameters(),
+
+          // Get th current block
+          BMA.blockchain.current()
+            .then(function(current) {
+              block = current;
+            })
+            .catch(function(err) {
+              // Special case for currency init (root block not exists): use fixed values
+              if (err && err.ucode == BMA.errorCodes.NO_CURRENT_BLOCK) {
+                block = {number: 0, hash: BMA.constants.ROOT_BLOCK_HASH};
+              }
+              else {
+                throw err;
+              }
+            })
+        ])
+
+        // Create identity document
+          .then(function() {
+            return getIdentityDocument(uid, block.number + '-' + block.hash);
           })
-          .catch(function(err) {
-            // Special case for currency init (root block not exists): use fixed values
-            if (err && err.ucode == BMA.errorCodes.NO_CURRENT_BLOCK) {
-              block = {number: 0, hash: BMA.constants.ROOT_BLOCK_HASH};
+
+          // Send to node
+          .then(function (identity) {
+            return BMA.wot.add({identity: identity});
+          })
+
+          .then(function () {
+            if (!!needToLoadRequirements) {
+              // Refresh membership data (if need)
+              return loadRequirements();
             }
             else {
-              throw err;
+              data.uid = uid;
+              data.blockUid = block.number + '-' + block.hash;
             }
           })
-      ])
-
-      // Create identity document
-      .then(function() {
-        return getIdentityDocument(uid, block.number + '-' + block.hash);
-      })
-
-      // Send to node
-      .then(function (identity) {
-        return BMA.wot.add({identity: identity});
-      })
-
-      .then(function () {
-        if (!!needToLoadRequirements) {
-          // Refresh membership data (if need)
-          return loadRequirements();
-        }
-        else {
-          data.uid = uid;
-          data.blockUid = block.number + '-' + block.hash;
-        }
-      })
-      .catch(function (err) {
-        if (err && err.ucode === BMA.errorCodes.IDENTITY_SANDBOX_FULL) {
-          throw {ucode: BMA.errorCodes.IDENTITY_SANDBOX_FULL, message: 'ERROR.IDENTITY_SANDBOX_FULL'};
-        }
-        throw err;
-      });
-    },
-
-   /**
-    * Send membership (in or out)
-    */
-    membership = function(sideIn) {
-      return function() {
-        var membership;
-        return BMA.blockchain.current()
+          .catch(function (err) {
+            if (err && err.ucode === BMA.errorCodes.IDENTITY_SANDBOX_FULL) {
+              throw {ucode: BMA.errorCodes.IDENTITY_SANDBOX_FULL, message: 'ERROR.IDENTITY_SANDBOX_FULL'};
+            }
+            throw err;
+          });
+      },
+
+      /**
+       * Send membership (in or out)
+       */
+      membership = function(sideIn) {
+        return function() {
+          var membership;
+          return BMA.blockchain.current()
             .catch(function(err){
               // Special case for currency init (root block not exists): use fixed values
               if (err && err.ucode == BMA.errorCodes.NO_CURRENT_BLOCK) {
@@ -1326,249 +1326,303 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
               }
               throw err;
             })
+            .then(function(block) {
+              // Create membership to sign
+              membership = 'Version: '+ constants.MS_VERSION +'\n' +
+                'Type: Membership\n' +
+                'Currency: ' + data.currency + '\n' +
+                'Issuer: ' + data.pubkey + '\n' +
+                'Block: ' + block.number + '-' + block.hash + '\n' +
+                'Membership: ' + (!!sideIn ? "IN" : "OUT" ) + '\n' +
+                'UserID: ' + data.uid + '\n' +
+                'CertTS: ' + data.blockUid + '\n';
+
+              return CryptoUtils.sign(membership, data.keypair);
+            })
+            .then(function(signature) {
+              var signedMembership = membership + signature + '\n';
+              // Send signed membership
+              return BMA.blockchain.membership({membership: signedMembership});
+            })
+            .then(function() {
+              return $timeout(function() {
+                return loadRequirements();
+              }, 1000); // waiting for node to process membership doc
+            })
+            .then(function() {
+              finishLoadRequirements();
+            });
+        };
+      },
+
+      /**
+       * Send identity certification
+       */
+      certify = function(uid, pubkey, timestamp, signature, isMember, wasMember) {
+        var current;
+        var cert;
+
+        return BMA.blockchain.current()
+          .catch(function(err){
+            // Special case for currency init (root block not exists): use fixed values
+            if (err && err.ucode == BMA.errorCodes.NO_CURRENT_BLOCK) {
+              return {number: 0, hash: BMA.constants.ROOT_BLOCK_HASH, medianTime: Math.trunc(new Date().getTime() / 1000)};
+            }
+            throw err;
+          })
           .then(function(block) {
-            // Create membership to sign
-            membership = 'Version: '+ constants.MS_VERSION +'\n' +
-              'Type: Membership\n' +
+            current = block;
+            // Create the self part to sign
+            cert = 'Version: '+ constants.CERT_VERSION +'\n' +
+              'Type: Certification\n' +
               'Currency: ' + data.currency + '\n' +
               'Issuer: ' + data.pubkey + '\n' +
-              'Block: ' + block.number + '-' + block.hash + '\n' +
-              'Membership: ' + (!!sideIn ? "IN" : "OUT" ) + '\n' +
-              'UserID: ' + data.uid + '\n' +
-              'CertTS: ' + data.blockUid + '\n';
+              'IdtyIssuer: ' + pubkey + '\n' +
+              'IdtyUniqueID: ' + uid + '\n' +
+              'IdtyTimestamp: ' + timestamp + '\n' +
+              'IdtySignature: ' + signature + '\n' +
+              'CertTimestamp: ' + block.number + '-' + block.hash + '\n';
 
-            return CryptoUtils.sign(membership, data.keypair);
+            return CryptoUtils.sign(cert, data.keypair);
           })
           .then(function(signature) {
-            var signedMembership = membership + signature + '\n';
-            // Send signed membership
-            return BMA.blockchain.membership({membership: signedMembership});
-          })
-          .then(function() {
-            return $timeout(function() {
-              return loadRequirements();
-            }, 1000); // waiting for node to process membership doc
+            var signedCert = cert + signature + '\n';
+            return BMA.wot.certify({cert: signedCert});
           })
           .then(function() {
-            finishLoadRequirements();
+            return {
+              pubkey: pubkey,
+              uid: uid,
+              time: current.medianTime,
+              isMember: isMember,
+              wasMember: wasMember,
+              expiresIn: data.parameters.sigWindow,
+              pending: true,
+              block: current.number,
+              valid: true
+            };
           });
-      };
-    },
-
-    /**
-    * Send identity certification
-    */
-    certify = function(uid, pubkey, timestamp, signature, isMember, wasMember) {
-      var current;
-      var cert;
-
-      return BMA.blockchain.current()
-        .catch(function(err){
-          // Special case for currency init (root block not exists): use fixed values
-          if (err && err.ucode == BMA.errorCodes.NO_CURRENT_BLOCK) {
-            return {number: 0, hash: BMA.constants.ROOT_BLOCK_HASH, medianTime: Math.trunc(new Date().getTime() / 1000)};
+      },
+
+      addEvent = function(event, insertAtFirst) {
+        event = event || {};
+        event.type = event.type || 'info';
+        event.message = event.message || '';
+        event.messageParams = event.messageParams || {};
+        event.context = event.context || 'undefined';
+        if (event.message.trim().length) {
+          if (!insertAtFirst) {
+            data.events.push(event);
+          }
+          else {
+            data.events.splice(0, 0, event);
           }
-          throw err;
-        })
-        .then(function(block) {
-          current = block;
-          // Create the self part to sign
-          cert = 'Version: '+ constants.CERT_VERSION +'\n' +
-            'Type: Certification\n' +
-            'Currency: ' + data.currency + '\n' +
-            'Issuer: ' + data.pubkey + '\n' +
-            'IdtyIssuer: ' + pubkey + '\n' +
-            'IdtyUniqueID: ' + uid + '\n' +
-            'IdtyTimestamp: ' + timestamp + '\n' +
-            'IdtySignature: ' + signature + '\n' +
-            'CertTimestamp: ' + block.number + '-' + block.hash + '\n';
-
-          return CryptoUtils.sign(cert, data.keypair);
-        })
-        .then(function(signature) {
-          var signedCert = cert + signature + '\n';
-          return BMA.wot.certify({cert: signedCert});
-        })
-        .then(function() {
-          return {
-            pubkey: pubkey,
-            uid: uid,
-            time: current.medianTime,
-            isMember: isMember,
-            wasMember: wasMember,
-            expiresIn: data.parameters.sigWindow,
-            pending: true,
-            block: current.number,
-            valid: true
-          };
-        });
-    },
-
-    addEvent = function(event, insertAtFirst) {
-      event = event || {};
-      event.type = event.type || 'info';
-      event.message = event.message || '';
-      event.messageParams = event.messageParams || {};
-      event.context = event.context || 'undefined';
-      if (event.message.trim().length) {
-        if (!insertAtFirst) {
-          data.events.push(event);
         }
         else {
-          data.events.splice(0, 0, event);
+          console.debug('Event without message. Skipping this event');
         }
-      }
-      else {
-        console.debug('Event without message. Skipping this event');
-      }
-    },
+      },
+
+      getkeypairSaveId = function(record){
+        var nbCharSalt = Math.round(record.answer.length/2);
+        var salt = record.answer.substr(0, nbCharSalt);
+        var pwd = record.answer.substr(nbCharSalt);
+        return CryptoUtils.connect(salt, pwd)
+          .then(function(keypair){
+            console.debug(keypair);
+            var nonce = CryptoUtils.util.random_nonce();
+            record.nonce = CryptoUtils.util.encode_base58(nonce);
+            record.pubkey = CryptoUtils.util.encode_base58(keypair.signPk);
+
+            return CryptoUtils.box.pack(record.salt, nonce, keypair.boxPk, keypair.boxSk)
+              .then(function (cypherSalt) {
+                record.salt = cypherSalt;
+
+                return CryptoUtils.box.pack(record.pwd, nonce, keypair.boxPk, keypair.boxSk)
+                  .then(function (cypherPwd) {
+                    record.pwd = cypherPwd;
+
+                    return record;
+                  });
+              });
+          });
+      },
+
+      getSaveIDDocument = function(record) {
+         var saveId = 'Version: 10 \n' +
+          'Type: SaveID\n' +
+          'Questions: ' + '\n' + record.questions +
+          'Issuer: ' + data.pubkey + '\n' +
+          'Crypted-Pubkey: '+ record.pubkey +'\n' +
+          'Crypted-Salt: '+ record.salt  + '\n' +
+          'Crypted-Pwd: '+ record.pwd + '\n';
+
+        // Sign SaveId document
+        return CryptoUtils.sign(saveId, data.keypair)
+
+          .then(function(signature) {
+            saveId += signature + '\n';
+            console.debug('Has generate an SaveID document:\n----\n' + saveId + '----');
+            return saveId;
+          });
+
+      },
+
+      downloadSaveId = function(record){
+        return getSaveIDDocument(record)
+         .then(function(saveId) {
+           var saveIdFile = new Blob([saveId], {type: 'text/plain; charset=utf-8'});
+           FileSaver.saveAs(saveIdFile, 'saveID.txt');
+         });
+
+      },
 
-    getRevocationDocument = function() {
+      getRevocationDocument = function() {
 
-      // Get current identity document
-      return getIdentityDocument()
+        // Get current identity document
+        return getIdentityDocument()
 
         // Create membership document (unsigned)
-        .then(function(identity){
-          var identityLines = identity.trim().split('\n');
-          var idtySignature = identityLines[identityLines.length-1];
+          .then(function(identity){
+            var identityLines = identity.trim().split('\n');
+            var idtySignature = identityLines[identityLines.length-1];
 
-          var revocation = 'Version: '+ constants.REVOKE_VERSION +'\n' +
-            'Type: Revocation\n' +
-            'Currency: ' + data.currency + '\n' +
-            'Issuer: ' + data.pubkey + '\n' +
-            'IdtyUniqueID: ' + data.uid + '\n' +
-            'IdtyTimestamp: ' + data.blockUid + '\n' +
-            'IdtySignature: ' + idtySignature + '\n';
+            var revocation = 'Version: '+ constants.REVOKE_VERSION +'\n' +
+              'Type: Revocation\n' +
+              'Currency: ' + data.currency + '\n' +
+              'Issuer: ' + data.pubkey + '\n' +
+              'IdtyUniqueID: ' + data.uid + '\n' +
+              'IdtyTimestamp: ' + data.blockUid + '\n' +
+              'IdtySignature: ' + idtySignature + '\n';
 
 
-          // Sign revocation document
-          return CryptoUtils.sign(revocation, data.keypair)
+            // Sign revocation document
+            return CryptoUtils.sign(revocation, data.keypair)
 
             // Add revocation to document
-            .then(function(signature) {
-              revocation += signature + '\n';
-              console.debug('Has generate an revocation document:\n----\n' + revocation + '----');
-              return revocation;
-            });
-        });
-    },
+              .then(function(signature) {
+                revocation += signature + '\n';
+                console.debug('Has generate an revocation document:\n----\n' + revocation + '----');
+                return revocation;
+              });
+          });
+      },
 
-    /**
-     * Send a revocation
-     */
-    revoke = function() {
+      /**
+       * Send a revocation
+       */
+      revoke = function() {
 
-      // Clear old events
-      cleanEventsByContext('revocation');
+        // Clear old events
+        cleanEventsByContext('revocation');
 
-      // Get revocation document
-      return getRevocationDocument()
+        // Get revocation document
+        return getRevocationDocument()
 
         // Send revocation document
-        .then(function(revocation) {
-          return BMA.wot.revoke({revocation: revocation});
-        })
-
-        // Reload requirements
-        .then(function() {
-
-          return $timeout(function() {
-            return loadRequirements();
-          }, 1000); // waiting for node to process membership doc
-        })
-
-        .then(function() {
-          finishLoadRequirements();
-
-          // Add user event
-          addEvent({type:'pending', message: 'INFO.REVOCATION_SENT_WAITING_PROCESS', context: 'revocation'}, true);
-        })
-        .catch(function(err) {
-          if (err && err.ucode == BMA.errorCodes.REVOCATION_ALREADY_REGISTERED) {
-            // Already registered by node: just add an event
+          .then(function(revocation) {
+            return BMA.wot.revoke({revocation: revocation});
+          })
+
+          // Reload requirements
+          .then(function() {
+
+            return $timeout(function() {
+              return loadRequirements();
+            }, 1000); // waiting for node to process membership doc
+          })
+
+          .then(function() {
+            finishLoadRequirements();
+
+            // Add user event
             addEvent({type:'pending', message: 'INFO.REVOCATION_SENT_WAITING_PROCESS', context: 'revocation'}, true);
+          })
+          .catch(function(err) {
+            if (err && err.ucode == BMA.errorCodes.REVOCATION_ALREADY_REGISTERED) {
+              // Already registered by node: just add an event
+              addEvent({type:'pending', message: 'INFO.REVOCATION_SENT_WAITING_PROCESS', context: 'revocation'}, true);
+            }
+            else {
+              throw err;
+            }
+          })
+          ;
+      },
+
+      downloadRevocation = function(){
+        return getRevocationDocument()
+          .then(function(revocation) {
+            var revocationFile = new Blob([revocation], {type: 'text/plain; charset=utf-8'});
+            FileSaver.saveAs(revocationFile, 'revocation.txt');
+          });
+      },
+
+      cleanEventsByContext = function(context){
+        data.events = data.events.reduce(function(res, event) {
+          if (event.context && event.context == context) return res;
+          return res.concat(event);
+        },[]);
+      },
+
+      /**
+       * Serialize to JSON string
+       */
+      toJson = function() {
+        return $q(function(resolve, reject) {
+          var json = JSON.stringify(data);
+          resolve(json);
+        });
+      },
+
+      /**
+       * De-serialize from JSON string
+       */
+      fromJson = function(json, failIfInvalid) {
+        failIfInvalid = angular.isUndefined(failIfInvalid) ? true : failIfInvalid;
+        return $q(function(resolve, reject) {
+          var obj = JSON.parse(json || '{}');
+          if (obj && obj.keypair && obj.keypair.signPk && obj.keypair.signSk) {
+            var keypair = {};
+            var i;
+
+            // sign Pk : Convert to Uint8Array type
+            var signPk = new Uint8Array(32);
+            for (i = 0; i < 32; i++) signPk[i] = obj.keypair.signPk[i];
+            keypair.signPk = signPk;
+
+            var signSk = new Uint8Array(64);
+            for (i = 0; i < 64; i++) signSk[i] = obj.keypair.signSk[i];
+            keypair.signSk = signSk;
+
+            // box Pk : Convert to Uint8Array type
+            if (obj.keypair.boxPk) {
+              var boxPk = new Uint8Array(32);
+              for (i = 0; i < 32; i++) boxPk[i] = obj.keypair.boxPk[i];
+              keypair.boxPk = boxPk;
+            }
+
+            if (obj.keypair.boxSk) {
+              var boxSk = new Uint8Array(32);
+              for (i = 0; i < 64; i++) boxSk[i] = obj.keypair.boxSk[i];
+              keypair.boxSk = boxSk;
+            }
+
+            resolve({
+              pubkey: obj.pubkey,
+              keypair: keypair,
+              tx: obj.tx
+            });
+          }
+          else if (failIfInvalid) {
+            reject('Not a valid Wallet.data object');
           }
           else {
-            throw err;
+            resolve();
           }
-        })
-        ;
-    },
-
-    downloadRevocation = function(){
-      return getRevocationDocument()
-        .then(function(revocation) {
-          var revocationFile = new Blob([revocation], {type: 'text/plain; charset=utf-8'});
-          FileSaver.saveAs(revocationFile, 'revocation.txt');
         });
-    },
-
-    cleanEventsByContext = function(context){
-      data.events = data.events.reduce(function(res, event) {
-        if (event.context && event.context == context) return res;
-        return res.concat(event);
-      },[]);
-    },
-
-    /**
-    * Serialize to JSON string
-    */
-    toJson = function() {
-      return $q(function(resolve, reject) {
-        var json = JSON.stringify(data);
-        resolve(json);
-      });
-    },
-
-    /**
-    * De-serialize from JSON string
-    */
-    fromJson = function(json, failIfInvalid) {
-      failIfInvalid = angular.isUndefined(failIfInvalid) ? true : failIfInvalid;
-      return $q(function(resolve, reject) {
-        var obj = JSON.parse(json || '{}');
-        if (obj && obj.keypair && obj.keypair.signPk && obj.keypair.signSk) {
-          var keypair = {};
-          var i;
-
-          // sign Pk : Convert to Uint8Array type
-          var signPk = new Uint8Array(32);
-          for (i = 0; i < 32; i++) signPk[i] = obj.keypair.signPk[i];
-          keypair.signPk = signPk;
-
-          var signSk = new Uint8Array(64);
-          for (i = 0; i < 64; i++) signSk[i] = obj.keypair.signSk[i];
-          keypair.signSk = signSk;
-
-          // box Pk : Convert to Uint8Array type
-          if (obj.keypair.boxPk) {
-            var boxPk = new Uint8Array(32);
-            for (i = 0; i < 32; i++) boxPk[i] = obj.keypair.boxPk[i];
-            keypair.boxPk = boxPk;
-          }
-
-          if (obj.keypair.boxSk) {
-            var boxSk = new Uint8Array(32);
-            for (i = 0; i < 64; i++) boxSk[i] = obj.keypair.boxSk[i];
-            keypair.boxSk = boxSk;
-          }
-
-          resolve({
-            pubkey: obj.pubkey,
-            keypair: keypair,
-            tx: obj.tx
-          });
-        }
-        else if (failIfInvalid) {
-          reject('Not a valid Wallet.data object');
-        }
-        else {
-          resolve();
-        }
-      });
-    }
-    ;
+      }
+      ;
 
     // Register extension points
     api.registerEvent('data', 'init');
@@ -1600,6 +1654,8 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser
       transfer: transfer,
       self: self,
       revoke: revoke,
+      downloadSaveId: downloadSaveId,
+      getkeypairSaveId: getkeypairSaveId,
       downloadRevocation: downloadRevocation,
       membership: {
         inside: membership(true),
diff --git a/www/plugins/es/js/controllers/settings-controllers.js b/www/plugins/es/js/controllers/settings-controllers.js
index be768c7f44034cf39a3065d5e4ea606f597b5689..caa9a0adebfaa64035a2386200fbe3eb50b5a62c 100644
--- a/www/plugins/es/js/controllers/settings-controllers.js
+++ b/www/plugins/es/js/controllers/settings-controllers.js
@@ -49,7 +49,7 @@ function ESExtendSettingsController ($scope, PluginService, csSettings) {
  * Settings extend controller
  */
 function ESPluginSettingsController ($scope, $q,  $translate, $ionicPopup, UIUtils, csSettings, csHttp, esMarket,
-  esRegistry, esUser) {
+  esRegistry, esUser, Modals) {
   'ngInject';
 
   $scope.formData = {};
@@ -71,7 +71,7 @@ function ESPluginSettingsController ($scope, $q,  $translate, $ionicPopup, UIUti
     $scope.popupForm = popupForm;
   };
 
-  // Change ESnode
+   // Change ESnode
   $scope.changeEsNode= function(node) {
     $scope.showNodePopup(node || $scope.formData)
     .then(function(node) {
@@ -97,6 +97,31 @@ function ESPluginSettingsController ($scope, $q,  $translate, $ionicPopup, UIUti
     });
   };
 
+  $scope.showNodeList = function() {
+    $ionicPopup._popupStack[0].responseDeferred.promise.close();
+    return Modals.showNetworkLookup({enableFilter: true, type: 'member', endpointFilter: 'ES_USER_API'})
+      .then(function (result) {
+        if (result) {
+          var parts = result.server.split(':');
+          if (result.dns) {
+            return {
+              host: result.dns,
+              port: parts[1] || 80
+            };
+          }
+          else {
+            return {
+              host: parts[0],
+              port: parts[1] || 80
+            };
+          }
+        }
+      })
+      .then(function(newNode) {
+        $scope.changeEsNode(newNode);
+      });
+  };
+
   // Show node popup
   $scope.showNodePopup = function(node) {
     return $q(function(resolve, reject) {
diff --git a/www/templates/modal_login.html b/www/templates/modal_login.html
index ff3d3a71bbba3489c679f4aa659ba93ff88c53fb..409d45dc1c2779953d40040f0ec1ad10e8e1c8f0 100644
--- a/www/templates/modal_login.html
+++ b/www/templates/modal_login.html
@@ -25,7 +25,7 @@
                  ng-model="formData.username"
                  ng-model-options="{ debounce: 650 }"
                  class="highlight-light"
-                 required>
+                 required >
           <input ng-if="showSalt"
                  name="username" type="text" placeholder="{{'LOGIN.SALT_HELP' | translate}}"
                  ng-model="formData.username"
diff --git a/www/templates/wallet/modal_security.html b/www/templates/wallet/modal_security.html
index e6fce021b19b5e3115b90810b76bc2cf6c289da7..81cd38da31697777d985eed2756ef83cc9123071 100644
--- a/www/templates/wallet/modal_security.html
+++ b/www/templates/wallet/modal_security.html
@@ -15,7 +15,7 @@
 
     <button class="button button-clear icon-right visible-xs"
             ng-if="!isLastSlide && slides.slider.activeIndex > 1"
-            ng-click="slideNext()">
+            ng-click="doNext()">
       <span translate>COMMON.BTN_NEXT</span>
       <i class="icon ion-ios-arrow-right"></i>
     </button>
@@ -28,21 +28,21 @@
       <ion-slide-page>
         <ion-content class="has-header padding">
           <div class="list">
-            <a class="item card item-icon-right stable-bg padding ink"
-               ng-click="">
+            <div class="item card item-icon-right stable-bg padding ink dark"
+               ng-click="slideTo(2)">
               <i class="icon ion-ios-arrow-right"></i>
               <span ng-bind-html="'ACCOUNT.SECURITY.SAVE_KEYS' | translate"></span>
-            </a>
-            <a class="item card item-icon-right stable-bg padding ink"
-                 ng-click="">
+            </div>
+            <div class="item card item-icon-right stable-bg padding ink dark"
+                 ng-click="slideNext()">
               <span ng-bind-html="'ACCOUNT.SECURITY.REVOCATION' | translate"></span>
-              <h4 class="gray"></h4>
-              <i class="icon dark ion-ios-arrow-right"></i>
-            </a>
+              <i class="icon ion-ios-arrow-right"></i>
+            </div>
           </div>
 
           <div class="padding hidden-xs text-right">
-            <button class="button button-clear button-dark ink" ng-click="closeModal()" type="button" translate>COMMON.BTN_CANCEL
+            <button class="button button-clear button-dark ink" ng-click="closeModal()"
+                    type="button" translate>COMMON.BTN_CANCEL
             </button>
           </div>
         </ion-content>
@@ -53,16 +53,16 @@
         <ion-content class="has-header padding">
           <h3 translate>ACCOUNT.SECURITY.REVOCATION</h3>
           <div class="list">
-            <a class="item card item-icon-right stable-bg padding ink"
+            <div class="item card item-icon-right stable-bg padding ink dark"
                ng-click="downloadRevokeFile()">
               <i class="icon ion-android-archive"></i>
               <span ng-bind-html="'ACCOUNT.SECURITY.DOWNLOAD_REVOKE' | translate"></span>
-            </a>
-            <a class="item card item-icon-right stable-bg padding ink"
+            </div>
+            <div class="item card item-icon-right stable-bg padding ink dark"
                ng-click="revokeIdentity()">
               <i class="icon ion-minus-circled assertive"></i>
               <span ng-bind-html="'ACCOUNT.SECURITY.DEFINITELY_REVOKE' | translate"></span>
-            </a>
+            </div>
           </div>
           <div class="padding hidden-xs text-right">
             <button class="button button-clear button-dark ink" ng-click="closeModal()" type="button" translate>COMMON.BTN_CANCEL
@@ -71,288 +71,172 @@
         </ion-content>
       </ion-slide-page>
 
-      <!-- STEP 3: save keys -->
+      <!-- STEP 3: choose questions -->
       <ion-slide-page>
-        <ion-content class="has-header" scroll="false">
-          <form name="saltForm" novalidate="" ng-submit="doNext('saltForm')">
-
+        <ion-content class="has-header padding" >
+          <h3 translate>ACCOUNT.SECURITY.SAVE_KEYS</h3>
+           <label class="item item-input item-select">
+            <div class="input-label" translate>
+              ACCOUNT.SECURITY.LEVEL
+            </div>
+            <select ng-model="formData.level">
+              <option value="2" ng-bind-html="'ACCOUNT.SECURITY.LOW_LEVEL' | translate"></option>
+              <option value="4" translate>ACCOUNT.SECURITY.MEDIUM_LEVEL</option>
+              <option value="6" translate>ACCOUNT.SECURITY.STRONG_LEVEL</option>
+            </select>
+          </label>
+          <div class="padding-top" translate="ACCOUNT.SECURITY.HELP_LEVEL "
+               translate-values="{nb: {{formData.level}}}">
+          </div>
+          <form name="questionsForm" novalidate ng-submit="doNext('questionsForm')">
             <div class="list"
-                 ng-init="setForm(saltForm, 'saltForm')">
-
-              <div class="item item-text-wrap text-center padding hidden-xs" >
-                <a class="pull-right icon-help" ng-click="showHelpModal('join-salt')"></a>
-                <span translate>ACCOUNT.SECURITY.SAVE_KEYS</span>
-              </div>
+                 ng-init="setForm(questionsForm, 'questionsForm')">
 
-              <!-- salt -->
-              <div class="item item-input"
-                   ng-class="{ 'item-input-error': saltForm.$submitted && saltForm.username.$invalid}">
-                <span class="input-label" translate>LOGIN.SALT</span>
-                <input ng-if="!showUsername"
-                       name="username" type="password" placeholder="{{'LOGIN.SALT_HELP' | translate}}"
-                       ng-change="formDataChanged()"
-                       ng-model="formData.username"
-                       ng-minlength="8"
-                       required>
-                <input ng-if="showUsername"
-                       name="username" type="text" placeholder="{{'LOGIN.SALT_HELP' | translate}}"
-                       ng-change="formDataChanged()"
-                       ng-model="formData.username"
-                       ng-minlength="8"
-                       required>
-              </div>
-              <div class="form-errors"
-                   ng-show="saltForm.$submitted && saltForm.username.$error"
-                   ng-messages="saltForm.username.$error">
-                <div class="form-error" ng-message="minlength">
-                  <span translate="ERROR.FIELD_TOO_SHORT_WITH_LENGTH" translate-values="{minLength: 8}"></span>
-                </div>
-                <div class="form-error" ng-message="required">
-                  <span translate="ERROR.FIELD_REQUIRED"></span>
+              <ion-checkbox ng-repeat="question in formData.questions" ng-model="question.checked"
+                            ng-required="isRequired()">
+                <span style="white-space: normal;">{{question.value | translate}}</span>
+              </ion-checkbox>
+              <div class="item item-icon-right no-padding-top">
+                <a class="dark"><i class="icon ion-android-add" ng-click="addQuestion()"></i></a>
+                <div class="list list-inset">
+                  <label class="item item-input">
+                    <input type="text" placeholder="{{'ACCOUNT.SECURITY.ADD_QUESTION' | translate}}" ng-model="formData.addQuestion"/>
+                  </label>
                 </div>
               </div>
+            </div>
+            <div class="padding hidden-xs text-right">
+              <button class="button button-clear button-dark ink" ng-click="closeModal()"
+                      type="button" translate>COMMON.BTN_CANCEL
+              </button>
+              <button class="button button-clear button-dark"
+                      ng-click="restore()" type="button" translate>ACCOUNT.SECURITY.BTN_RESET
+              </button>
+              <button class="button button-calm icon-right ion-chevron-right ink"
+                      ng-disabled="questionsForm.$invalid" type="submit" translate>
+                COMMON.BTN_NEXT
+                <i class="icon ion-arrow-right-a"></i>
+              </button>
+            </div>
+          </form>
 
-              <!-- confirm salt -->
-              <div class="item item-input"
-                   ng-class="{ 'item-input-error': saltForm.$submitted && saltForm.confirmSalt.$invalid}">
-                <span class="input-label pull-right" translate>ACCOUNT.NEW.SALT_CONFIRM</span>
-                <input ng-if="!showUsername"
-                       name="confirmUsername" type="password"
-                       placeholder="{{'ACCOUNT.NEW.SALT_CONFIRM_HELP' | translate}}"
-                       ng-model="formData.confirmUsername"
-                       compare-to="formData.username">
-                <input ng-if="showUsername"
-                       name="confirmUsername" type="text"
-                       placeholder="{{'ACCOUNT.NEW.SALT_CONFIRM_HELP' | translate}}"
-                       ng-model="formData.confirmUsername"
-                       compare-to="formData.username">
-              </div>
-              <div class="form-errors"
-                   ng-show="saltForm.$submitted && saltForm.confirmUsername.$error"
-                   ng-messages="saltForm.confirmUsername.$error">
-                <div class="form-error" ng-message="minlength">
-                  <span translate="ERROR.FIELD_TOO_SHORT"></span>
-                </div>
-                <div class="form-error" ng-message="maxlength">
-                  <span translate="ERROR.FIELD_TOO_LONG"></span>
-                </div>
-                <div class="form-error" ng-message="required">
-                  <span translate="ERROR.FIELD_REQUIRED"></span>
-                </div>
-                <div class="form-error" ng-message="compareTo">
-                  <span translate="ERROR.SALT_NOT_CONFIRMED"></span>
-                </div>
-              </div>
+        </ion-content>
+      </ion-slide-page>
 
-              <!-- Show values -->
-              <div class="item item-toggle dark">
-                <span translate>COMMON.SHOW_VALUES</span>
-                <label class="toggle toggle-royal">
-                  <input type="checkbox" ng-model="showUsername">
-                  <div class="track">
-                    <div class="handle"></div>
-                  </div>
+      <!-- STEP 4: answers -->
+      <ion-slide-page>
+        <ion-content class="has-header padding" >
+          <h3 translate>ACCOUNT.SECURITY.SAVE_KEYS</h3>
+
+          <form name="answersForm" novalidate ng-submit="doNext('answersForm')">
+            <div class="list" ng-init="setForm(answersForm, 'answersForm')">
+              <ng-repeat ng-repeat="question in formData.questions |filter:true:checked">
+                <label class="item item-input {{smallscreen ? 'item-stacked-label' : 'item-floating-label'}}"
+                       ng-class="{'item-input-error': answersForm.$submitted && answersForm['question{{$index}}'].$invalid}">
+                  <span class="input-label" style="{{smallscreen ? 'white-space: normal' : ''}}">{{question.value | translate}}</span>
+                  <input type="text" name="question{{$index}}" placeholder="{{smallscreen ? '' : question.value | translate}}" ng-model="question.answer" required />
                 </label>
-              </div>
-
+                <div class="form-errors"
+                     ng-show="answersForm.$submitted && answersForm['question{{$index}}'].$error"
+                     ng-messages="answersForm['question{{$index}}'].$error">
+                  <div class="form-error" ng-message="required">
+                    <span translate="ERROR.FIELD_REQUIRED"></span>
+                  </div>
+                </div>
+              </ng-repeat>
               <div class="padding hidden-xs text-right">
-                <button class="button button-clear button-dark ink" ng-click="closeModal()" type="button" translate>COMMON.BTN_CANCEL
+                <button class="button button-clear button-dark ink" ng-click="closeModal()"
+                        type="button" translate>COMMON.BTN_CANCEL
+                </button>
+                <button class="button button-clear button-dark"
+                        ng-click="restore()" type="button" translate>ACCOUNT.SECURITY.BTN_CLEAN
                 </button>
-                <button class="button button-calm icon-right ion-chevron-right ink" type="submit" translate>
+                <button class="button button-calm icon-right ion-chevron-right ink"
+                        ng-disabled="questionsForm.$invalid" type="submit" translate>
                   COMMON.BTN_NEXT
                   <i class="icon ion-arrow-right-a"></i>
                 </button>
               </div>
             </div>
+
           </form>
         </ion-content>
       </ion-slide-page>
 
-      <!-- STEP 4: password-->
+      <!-- STEP 5: confirm identity -->
       <ion-slide-page>
-        <ion-content class="has-header" scroll="false">
-          <form name="passwordForm" novalidate="" ng-submit="doNext('passwordForm')">
-
-            <div class="item item-text-wrap text-center padding hidden-xs" >
-              <a class="pull-right icon-help" ng-click="showHelpModal('join-password')"></a>
-              <span translate>ACCOUNT.NEW.PASSWORD_WARNING</span>
-            </div>
+        <ion-content>
+          <form name="loginForm" novalidate="" ng-submit="submit()">
 
-            <div class="list"
-                 ng-init="setForm(passwordForm, 'passwordForm')">
+            <div class="list" ng-init="setForm(loginForm, 'loginForm')">
 
-              <!-- password -->
-              <div class="item item-input"
-                   ng-class="{ 'item-input-error': passwordForm.$submitted && passwordForm.password.$invalid}">
-                <span class="input-label" translate>LOGIN.PASSWORD</span>
-                <input ng-if="!showPassword"
-                       name="password" type="password" placeholder="{{'LOGIN.PASSWORD_HELP' | translate}}"
-                       ng-model="formData.password"
-                       ng-change="formDataChanged()"
-                       ng-minlength="8"
+              <!-- salt (=username, to enable browser login cache) -->
+              <label class="item item-input"
+                     ng-class="{ 'item-input-error': loginForm.$submitted && loginForm.username.$invalid}">
+                <span class="input-label hidden-xs" translate>LOGIN.SALT</span>
+                <input ng-if="!showSalt"
+                       name="username" type="{{showSalt ? 'text' : 'password'}}" placeholder="{{'LOGIN.SALT_HELP' | translate}}"
+                       ng-model="formData.username"
+                       ng-model-options="{ debounce: 650 }"
+                       class="highlight-light"
                        required>
-                <input ng-if="showPassword"
-                       name="text" type="text" placeholder="{{'LOGIN.PASSWORD_HELP' | translate}}"
-                       ng-model="formData.password"
-                       ng-change="formDataChanged()"
-                       ng-minlength="8"
+                <input ng-if="showSalt"
+                       name="username" type="text" placeholder="{{'LOGIN.SALT_HELP' | translate}}"
+                       ng-model="formData.username"
+                       ng-model-options="{ debounce: 650 }"
+                       class="highlight-light"
                        required>
-              </div>
+              </label>
               <div class="form-errors"
-                   ng-show="passwordForm.$submitted && passwordForm.password.$error"
-                   ng-messages="passwordForm.password.$error">
-                <div class="form-error" ng-message="minlength">
-                  <span translate="ERROR.FIELD_TOO_SHORT_WITH_LENGTH" translate-values="{minLength: 8}"></span>
-                </div>
-                <div class="form-error" ng-message="maxlength">
-                  <span translate="ERROR.FIELD_TOO_LONG"></span>
-                </div>
+                   ng-show="loginForm.$submitted && loginForm.username.$error"
+                   ng-messages="loginForm.username.$error">
                 <div class="form-error" ng-message="required">
                   <span translate="ERROR.FIELD_REQUIRED"></span>
                 </div>
               </div>
 
-              <!-- confirm password -->
-              <div class="item item-input"
-                   ng-class="{ 'item-input-error': passwordForm.$submitted && passwordForm.confirmPassword.$invalid}">
-                <span class="input-label" translate>ACCOUNT.NEW.PASSWORD_CONFIRM</span>
-                <input ng-if="!showPassword"
-                       name="confirmPassword" type="password"
-                       placeholder="{{'ACCOUNT.NEW.PASSWORD_CONFIRM_HELP' | translate}}"
-                       ng-model="formData.confirmPassword"
-                       compare-to="formData.password">
-                <input ng-if="showPassword"
-                       name="confirmPassword" type="text"
-                       placeholder="{{'ACCOUNT.NEW.PASSWORD_CONFIRM_HELP' | translate}}"
-                       ng-model="formData.confirmPassword"
-                       compare-to="formData.password">
-              </div>
-              <div class="form-errors"
-                   ng-show="passwordForm.$submitted && passwordForm.confirmPassword.$error"
-                   ng-messages="passwordForm.confirmPassword.$error">
-                <div class="form-error" ng-message="minlength">
-                  <span translate="ERROR.FIELD_TOO_SHORT"></span>
-                </div>
-                <div class="form-error" ng-message="maxlength">
-                  <span translate="ERROR.FIELD_TOO_LONG"></span>
-                </div>
-                <div class="form-error" ng-message="required">
-                  <span translate="ERROR.FIELD_REQUIRED"></span>
-                </div>
-                <div class="form-error" ng-message="compareTo">
-                  <span translate="ERROR.PASSWORD_NOT_CONFIRMED"></span>
-                </div>
-              </div>
-
-              <!-- Show values -->
+              <!-- Show salt -->
               <div class="item item-toggle dark">
-                <span translate>COMMON.SHOW_VALUES</span>
-                <label class="toggle toggle-royal">
-                  <input type="checkbox" ng-model="showPassword">
+                <span translate>LOGIN.SHOW_SALT</span>
+                <label class="toggle toggle-stable">
+                  <input type="checkbox" ng-model="showSalt">
                   <div class="track">
                     <div class="handle"></div>
                   </div>
                 </label>
               </div>
-            </div>
-
-            <div class="padding hidden-xs text-right">
-              <button class="button button-clear button-dark ink" ng-click="closeModal()" type="button" translate>COMMON.BTN_CANCEL
-              </button>
-              <button class="button button-calm icon-right ion-chevron-right ink" type="submit" translate>
-                  COMMON.BTN_NEXT
-              </button>
-            </div>
-
-            <div class="padding hidden-xs">
-            </div>
-          </form>
-        </ion-content>
-      </ion-slide-page>
-
-      <!-- STEP 5: pseudo-->
-      <ion-slide-page  ng-if="formData.isMember">
-        <ion-content class="has-header" scroll="false">
-          <form name="pseudoForm" novalidate="" ng-submit="doNext('pseudoForm')">
-
-            <div class="item item-text-wrap text-center padding hidden-xs" >
-              <a class="pull-right icon-help" ng-click="showHelpModal('join-pseudo')"></a>
-              <span translate>ACCOUNT.NEW.PSEUDO_WARNING</span>
-            </div>
-
-            <div class="list"
-                 ng-init="setForm(pseudoForm, 'pseudoForm')">
 
-              <!-- pseudo -->
-              <div class="item item-input"
-                   ng-if="formData.isMember"
-                   ng-class="{'item-input-error': pseudoForm.$submitted && pseudoForm.pseudo.$invalid}">
-                <span class="input-label" translate>ACCOUNT.NEW.PSEUDO</span>
-                <input name="pseudo" type="text" placeholder="{{'ACCOUNT.NEW.PSEUDO_HELP' | translate}}"
-                       ng-model="formData.pseudo"
-                       ng-minlength="3"
-                       ng-maxlength="100"
+              <!-- password-->
+              <label class="item item-input"
+                     ng-class="{ 'item-input-error': loginForm.$submitted && loginForm.password.$invalid}">
+                <span class="input-label hidden-xs" translate>LOGIN.PASSWORD</span>
+                <input name="password" type="password" placeholder="{{'LOGIN.PASSWORD_HELP' | translate}}"
+                       ng-model="formData.password"
+                       ng-model-options="{ debounce: 650 }"
+                       select-on-click
                        required>
-              </div>
+              </label>
               <div class="form-errors"
-                   ng-if="formData.isMember"
-                   ng-show="pseudoForm.$submitted && pseudoForm.pseudo.$error"
-                   ng-messages="pseudoForm.pseudo.$error">
-                <div class="form-error" ng-message="minlength">
-                  <span translate="ERROR.FIELD_TOO_SHORT_WITH_LENGTH" translate-values="{minLength: 3}"></span>
-                </div>
-                <div class="form-error" ng-message="maxlength">
-                  <span translate="ERROR.FIELD_TOO_LONG_WITH_LENGTH" translate-values="{maxLength: 100}"></span>
-                </div>
+                   ng-show="loginForm.$submitted && loginForm.password.$error"
+                   ng-messages="loginForm.password.$error">
                 <div class="form-error" ng-message="required">
                   <span translate="ERROR.FIELD_REQUIRED"></span>
                 </div>
               </div>
+            </div>
 
-              <div class="padding hidden-xs text-right">
-                <button class="button button-clear button-dark ink" ng-click="closeModal()" type="button" translate>COMMON.BTN_CANCEL
-                </button>
-                <button class="button button-calm icon-right ion-chevron-right ink" type="submit" translate>
-                  COMMON.BTN_NEXT
-                </button>
-              </div>
+            <div class="padding hidden-xs text-right">
+              <button class="button button-clear button-dark ink" ng-click="closeModal()"
+                      type="button" translate>COMMON.BTN_CANCEL
+              </button>
+              <button class="button button-positive ink" type="submit" translate>
+                COMMON.BTN_SEND
+                <i class="icon ion-android-send"></i>
+              </button>
             </div>
           </form>
         </ion-content>
       </ion-slide-page>
 
-      <!-- STEP 6: last slide  -->
-      <ion-slide-page>
-        <ion-content class="has-header" scroll="false">
-
-          <div class="padding text-center" translate>ACCOUNT.NEW.LAST_SLIDE_CONGRATULATION</div>
-
-          <div class="list">
-
-            <ion-item class="item item-text-wrap item-border">
-              <div class="dark pull-right padding-right" ng-if="formData.computing">
-                <ion-spinner icon="android"></ion-spinner>
-              </div>
-              <span class="input-label" translate>COMMON.PUBKEY</span>
-              <span class="gray text-no-wrap" ng-if="formData.computing" translate>
-                ACCOUNT.NEW.COMPUTING_PUBKEY
-              </span>
-              <span class="gray text-no-wrap" ng-if="formData.pubkey">
-                {{formData.pubkey}}
-              </span>
-            </ion-item>
-          </div>
-
-          <div class="padding hidden-xs text-right">
-            <button class="button button-clear button-dark ink" ng-click="closeModal()" type="button" translate>COMMON.BTN_CANCEL
-            </button>
-            <button class="button button-positive ink" ng-click="doNewAccount()" translate>
-              COMMON.BTN_SEND
-              <i class="icon ion-android-send"></i>
-            </button>
-          </div>
-        </ion-content>
-      </ion-slide-page>
-
     </ion-slides>
 </ion-modal-view>
diff --git a/www/templates/wallet/popover_actions.html b/www/templates/wallet/popover_actions.html
index 366e276e8214593b3ff22ca2820453f010d4b736..1b3ae35f8650ae878670460f8eccd9866e955bfd 100644
--- a/www/templates/wallet/popover_actions.html
+++ b/www/templates/wallet/popover_actions.html
@@ -70,7 +70,6 @@
       <!--</a>-->
 
       <a class="item item-icon-left ink"
-         ng-if="!walletData.requirements.needSelf"
          ng-click="showSecurityModal()">
         <i class="icon ion-android-lock"></i>
         <span ng-bind-html="'ACCOUNT.SECURITY.TITLE' | translate"></span>