diff --git a/www/index.html b/www/index.html
index 4596ffbcc4c5f10ed0223ad7c33e6d6a6febaf1b..fe6a31274b6cfb3c70104ac2a77d6562ccb62934 100644
--- a/www/index.html
+++ b/www/index.html
@@ -159,9 +159,12 @@
   <script src="dist/dist_js/plugins/graph/js/plugin.js"></script>
   <script src="dist/dist_js/plugins/graph/js/services.js"></script>
   <script src="dist/dist_js/plugins/graph/js/services/data-services.js"></script>
+  <script src="dist/dist_js/plugins/graph/js/services/color-services.js"></script>
+  <script src="dist/dist_js/plugins/graph/js/controllers/common-controllers.js"></script>
   <script src="dist/dist_js/plugins/graph/js/controllers/blockchain-controllers.js"></script>
   <script src="dist/dist_js/plugins/graph/js/controllers/network-controllers.js"></script>
   <script src="dist/dist_js/plugins/graph/js/controllers/currency-controllers.js"></script>
+  <script src="dist/dist_js/plugins/graph/js/controllers/account-controllers.js"></script>
 
   <!--endRemoveIf(no-plugin)-->
 
diff --git a/www/js/services/bma-services.js b/www/js/services/bma-services.js
index dffc4a47127c873e0772e61bbdb9c77580ecc8bf..572d24a06610eeed1e83392bccd4babe37ef58e7 100644
--- a/www/js/services/bma-services.js
+++ b/www/js/services/bma-services.js
@@ -615,7 +615,7 @@ angular.module('cesium.bma.services', ['ngResource', 'ngApi', 'cesium.http.servi
             }
           })
           .catch(function(err){
-            if (err && err.ucode === errorCodes.HTTP_LIMITATION) {
+            if (err && err.ucode === exports.errorCodes.HTTP_LIMITATION) {
               resolve(result);
             }
             else {
@@ -625,6 +625,19 @@ angular.module('cesium.bma.services', ['ngResource', 'ngApi', 'cesium.http.servi
       });
     };
 
+    exports.raw.getHttpWithRetryIfLimitation = function(exec) {
+      return exec()
+        .catch(function(err){
+          // When too many request, retry in 3s
+          if (err && err.ucode == exports.errorCodes.HTTP_LIMITATION) {
+            return $timeout(function() {
+              // retry
+              return exports.raw.getHttpWithRetryIfLimitation(exec);
+            }, exports.constants.LIMIT_REQUEST_DELAY);
+          }
+        });
+    };
+
     exports.blockchain.lastUd = function() {
       return exports.blockchain.stats.ud()
         .then(function(res) {
diff --git a/www/js/services/currency-services.js b/www/js/services/currency-services.js
index 086b898ba6b06261e5e21e2c562166a791e7e08b..97ed4d399b56c3d89e4fdcf038cbe696afa3f8ef 100644
--- a/www/js/services/currency-services.js
+++ b/www/js/services/currency-services.js
@@ -7,6 +7,15 @@ angular.module('cesium.currency.services', ['ngResource', 'ngApi', 'cesium.bma.s
   factory = function(id) {
 
     var
+      constants = {
+        // Avoid to many call on well known currencies
+        WELL_KNOWN_CURRENCIES: {
+          g1: {
+            firstBlockTime: 1488987127
+          }
+        }
+      },
+
       data = {
         loaded: false,
         currencies: null,
@@ -33,7 +42,7 @@ angular.module('cesium.currency.services', ['ngResource', 'ngApi', 'cesium.bma.s
         // Load currency from default node
         var promise = BMA.blockchain.parameters()
           .then(function(res){
-            data.currencies.push({
+            var currency = {
                 name: res.currency,
                 peer: {
                   host: BMA.host,
@@ -41,21 +50,47 @@ angular.module('cesium.currency.services', ['ngResource', 'ngApi', 'cesium.bma.s
                   server: BMA.server
                 },
                 parameters: res
-              });
-
+              };
+            // Add to data
+            data.currencies.push(currency);
+
+            // Well known currencies
+            if (constants.WELL_KNOWN_CURRENCIES[res.currency]){
+              angular.merge(currency, constants.WELL_KNOWN_CURRENCIES[res.currency]);
+            }
+
+            // Load some default values
+            else {
+              return BMA.blockchain.block({block:0})
+                .then(function(json) {
+                  // Need by graph plugin
+                  currency.firstBlockTime = json.medianTime;
+                })
+                .catch(function(err) {
+                  // Special case, when currency not started yet
+                  if (err && err.ucode === BMA.errorCodes.BLOCK_NOT_FOUND) {
+                    currency.firstBlockTime = 0;
+                    return;
+                  }
+                  throw err;
+                })
+                ;
+            }
+          })
+          .then(function() {
             // API extension point
             return api.data.raisePromise.load(data);
           })
           .then(function() {
             console.debug('[currency] Loaded in ' + (new Date().getTime() - now) + 'ms');
             data.loaded = true;
-            delete data.cache.loadJobPromise;
+            delete data.cache.loadPromise;
             return data;
           })
           .catch(function(err) {
             data.loaded = false;
             data.currencies = [];
-            delete data.cache.loadJobPromise;
+            delete data.cache.loadPromise;
             throw err;
           });
 
@@ -83,8 +118,7 @@ angular.module('cesium.currency.services', ['ngResource', 'ngApi', 'cesium.bma.s
           .then(function(data){
             return _.findWhere(data.currencies, {name: name});
           });
-      }
-    ;
+      };
 
     // Register extension points
     api.registerEvent('data', 'load');
diff --git a/www/plugins/es/i18n/locale-fr-FR.json b/www/plugins/es/i18n/locale-fr-FR.json
index ff507e3bfc1312121c2c52fb88b03137cb84e0ce..8db4504476dc992c7f3ff8fd5bc6b50c80a1c8e4 100644
--- a/www/plugins/es/i18n/locale-fr-FR.json
+++ b/www/plugins/es/i18n/locale-fr-FR.json
@@ -470,6 +470,8 @@
     "NODE_BMA_UP": "Le noeud <b>{{params[0]}}:{{params[1]}}</b> est à nouveau accessible.",
     "MEMBER_JOIN": "Vous êtes maintenant <b>membre</b> de la monnaie <b>{{params[0]}}</b> !",
     "MEMBER_LEAVE": "Vous n'êtes <b>plus membre</b> de la monnaie <b>{{params[0]}}</b>!",
+    "MEMBER_EXCLUDE": "Vous n'êtes <b>plus membre</b> de la monnaie <b>{{params[0]}}</b>, faute de non renouvellement ou par manque de certifications.",
+    "MEMBER_REVOKE": "La révocation de votre compte a été effectuée. Il ne pourra plus être un compte membre de la monnaie <b>{{params[0]}}</b>.",
     "MEMBER_ACTIVE": "Votre renouvellement d'adhésion à la monnaie <b>{{params[0]}}</b> a été <b>pris en compte</b>.",
     "TX_SENT": "Votre <b>paiement</b> à <span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\" ><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> a été effectué.",
     "TX_SENT_MULTI": "Votre <b>paiement</b> à <b>{{params[1]}}</b> a été effectué.",
diff --git a/www/plugins/graph/i18n/locale-fr-FR.json b/www/plugins/graph/i18n/locale-fr-FR.json
index 7e781d9af9d27b844e56894e71940e1742a4b8e4..1d2b236c9adbcd515ebf6351b7b9b3c01dbc9f64 100644
--- a/www/plugins/graph/i18n/locale-fr-FR.json
+++ b/www/plugins/graph/i18n/locale-fr-FR.json
@@ -11,6 +11,23 @@
       "BTN_SHOW_STATS": "Voir les statistiques",
       "BTN_SHOW_DETAILED_STATS": "Statistiques détaillées"
     },
+    "ACCOUNT": {
+      "TITLE": "Statistiques",
+      "BALANCE_DIVIDER": "Situation du compte",
+      "BALANCE_TITLE": "Balance - {{pubkey|formatPubkey}}",
+      "TX_RECEIVED_LABEL": "Recettes",
+      "TX_SENT_LABEL": "Dépenses",
+      "TX_ACCUMULATION_LABEL": "Bilan des transactions",
+      "UD_LABEL": "DU",
+      "UD_ACCUMULATION_LABEL": "Bilan des DU",
+      "BALANCE_LABEL": "Solde",
+      "WOT_DIVIDER": "Toile de confiance",
+      "CERTIFICATION_TITLE": "Nombre de certifications - {{pubkey|formatPubkey}}",
+      "RECEIVED_CERT_LABEL": "Total reçues",
+      "RECEIVED_CERT_DELTA_LABEL": "Variation reçues",
+      "GIVEN_CERT_LABEL": "Total envoyées",
+      "GIVEN_CERT_DELTA_LABEL": "Variation envoyées"
+    },
     "BLOCKCHAIN": {
       "TITLE": "Statistiques",
       "BLOCKS_ISSUERS_DIVIDER": "Analyse de la répartition du calcul",
diff --git a/www/plugins/graph/js/controllers/account-controllers.js b/www/plugins/graph/js/controllers/account-controllers.js
new file mode 100644
index 0000000000000000000000000000000000000000..1542daf6d542ae05eb9c92691b05471c10a5180d
--- /dev/null
+++ b/www/plugins/graph/js/controllers/account-controllers.js
@@ -0,0 +1,476 @@
+
+angular.module('cesium.graph.account.controllers', ['chart.js', 'cesium.graph.services'])
+
+  .config(function($stateProvider, PluginServiceProvider, csConfig) {
+    'ngInject';
+
+    var enable = csConfig.plugins && csConfig.plugins.es;
+    if (enable) {
+
+      /*PluginServiceProvider
+        .extendState('app.view_wallet', {
+          points: {
+            'general': {
+              templateUrl: "plugins/graph/templates/network/view_wallet_extend.html",
+              controller: 'GpWalletExtendCtrl'
+            }
+          }
+        })
+
+        // TODO add wot extend
+      ;*/
+
+      $stateProvider
+        .state('app.view_wallet_stats', {
+          url: "/wallet/stats?t&stepUnit",
+          views: {
+            'menuContent': {
+              templateUrl: "plugins/graph/templates/account/view_stats.html"
+            }
+          },
+          data: {
+            auth: true
+          }
+        })
+
+        .state('app.wot_identity_stats', {
+          url: "/wot/:pubkey/stats?t&stepUnit",
+          views: {
+            'menuContent': {
+              templateUrl: "plugins/graph/templates/account/view_stats.html"
+            }
+          }
+        });
+    }
+  })
+
+  .controller('GpWalletExtendCtrl', GpWalletExtendController)
+
+  .controller('GpAccountBalanceCtrl', GpAccountBalanceController)
+
+  .controller('GpAccountCertificationCtrl', GpAccountCertificationController)
+
+;
+
+function GpWalletExtendController($scope, PluginService, esSettings) {
+  'ngInject';
+
+  $scope.extensionPoint = PluginService.extensions.points.current.get();
+  $scope.enable = esSettings.isEnable();
+
+  esSettings.api.state.on.changed($scope, function(enable) {
+    $scope.enable = enable;
+  });
+}
+
+
+function GpAccountBalanceController($scope, $controller, $q, $state, $filter, $translate, gpData, gpColor, csWallet) {
+  'ngInject';
+
+  // Initialize the super class and extend it.
+  angular.extend(this, $controller('GpCurrencyAbstractCtrl', {$scope: $scope}));
+
+  $scope.init = function(e, state) {
+
+      if (state && state.stateParams && state.stateParams.pubkey) { // Currency parameter
+        $scope.formData.pubkey = state.stateParams.pubkey;
+      }
+      else if(csWallet.isLogin()) {
+        $scope.formData.pubkey = csWallet.data.pubkey;
+      }
+
+      // for DEV only
+      //$scope.formData.pubkey = '38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE';
+  };
+
+  $scope.load = function(updateTimePct) {
+
+    updateTimePct = angular.isDefined(updateTimePct) ? updateTimePct : true;
+
+    return $q.all([
+
+      $translate('GRAPH.ACCOUNT.BALANCE_TITLE', $scope.formData),
+
+      // translate i18n keys
+      $translate(['GRAPH.ACCOUNT.UD_LABEL',
+        'GRAPH.ACCOUNT.TX_RECEIVED_LABEL',
+        'GRAPH.ACCOUNT.TX_SENT_LABEL',
+        'GRAPH.ACCOUNT.UD_ACCUMULATION_LABEL',
+        'GRAPH.ACCOUNT.TX_ACCUMULATION_LABEL',
+        'GRAPH.ACCOUNT.BALANCE_LABEL',
+        'COMMON.DATE_PATTERN',
+        'COMMON.DATE_SHORT_PATTERN',
+        'COMMON.DATE_MONTH_YEAR_PATTERN']),
+
+      // get data
+      gpData.blockchain.movement($scope.formData.currency, angular.copy($scope.formData))
+    ])
+      .then(function(result) {
+        var title = result[0];
+        var translations = result[1];
+        result = result[2];
+
+        if (!result || !result.times) return; // no data
+        $scope.times = result.times;
+
+        var formatInteger = $filter('formatInteger');
+        var formatAmount =  $filter('formatDecimal');
+        $scope.currencySymbol = $filter('currencySymbolNoHtml')($scope.currency, $scope.formData.useRelative);
+
+        // Data
+        $scope.data = [
+          result.ud,
+          result.received,
+          result.sent,
+          result.udSum,
+          result.txSum,
+          result.balance
+        ];
+
+        var displayFormats = {
+          hour: translations['COMMON.DATE_PATTERN'],
+          day: translations['COMMON.DATE_SHORT_PATTERN'],
+          month: translations['COMMON.DATE_MONTH_YEAR_PATTERN']
+        };
+        var displayFormat = displayFormats[$scope.formData.rangeDuration];
+        // Labels
+        $scope.labels = result.times.reduce(function(res, time) {
+          return res.concat(moment.unix(time).local().format(displayFormat));
+        }, []);
+
+        // Colors
+        $scope.colors = gpColor.scale.fix(result.times.length);
+
+        // Update range with received values
+        $scope.updateRange(result.times[0], result.times[result.times.length-1], updateTimePct);
+
+        // Options
+        $scope.options = {
+          responsive: true,
+          maintainAspectRatio: true,
+          title: {
+            display: true,
+            text: title
+          },
+          scales: {
+            yAxes: [
+              {
+                id: 'y-axis-left',
+                type: 'linear',
+                position: 'left',
+                ticks: {
+                  beginAtZero:true,
+                  callback: function(value) {
+                    return formatInteger(value);
+                  }
+                }
+              },
+              {
+                id: 'y-axis-right',
+                type: 'linear',
+                position: 'right',
+                stacked: true,
+                display: false,
+                gridLines: {
+                  show: false
+                },
+                ticks: {
+                  beginAtZero:true,
+                  callback: function(value) {
+                    return formatInteger(value);
+                  }
+                }
+              }
+            ]
+          },
+          legend: {
+            display: true
+          },
+          tooltips: {
+            enabled: true,
+            mode: 'index',
+            callbacks: {
+              label: function(tooltipItems, data) {
+                // Should add a '+' before value ?
+                var addPlus = (tooltipItems.datasetIndex < 2)
+                  && tooltipItems.yLabel > 0;
+                return data.datasets[tooltipItems.datasetIndex].label +
+                  ': ' +
+                  (tooltipItems.yLabel === 0 ? '0' :
+                    ((addPlus ? '+' : '') + formatAmount(tooltipItems.yLabel) + ' ' + $scope.currencySymbol));
+              }
+            }
+          }
+        };
+
+        $scope.datasetOverride = [
+          {
+            yAxisID: 'y-axis-right',
+            type: 'bar',
+            label: translations['GRAPH.ACCOUNT.UD_LABEL'],
+            backgroundColor: gpColor.rgba.energized(0.3),
+            hoverBackgroundColor: gpColor.rgba.energized(0.5),
+            borderWidth: 1
+          },
+          {
+            yAxisID: 'y-axis-right',
+            type: 'bar',
+            label: translations['GRAPH.ACCOUNT.TX_RECEIVED_LABEL'],
+            backgroundColor: gpColor.rgba.positive(0.3),
+            hoverBackgroundColor: gpColor.rgba.positive(0.5),
+            borderWidth: 1
+          },
+          {
+            yAxisID: 'y-axis-right',
+            type: 'bar',
+            label: translations['GRAPH.ACCOUNT.TX_SENT_LABEL'],
+            backgroundColor: gpColor.rgba.gray(0.3),
+            hoverBackgroundColor: gpColor.rgba.gray(0.5),
+            borderWidth: 1
+          },
+          {
+            yAxisID: 'y-axis-left',
+            type: 'line',
+            label: translations['GRAPH.ACCOUNT.UD_ACCUMULATION_LABEL'],
+            fill: false,
+            borderColor: gpColor.rgba.energized(0.5),
+            borderWidth: 2,
+            backgroundColor: gpColor.rgba.energized(0.7),
+            pointBackgroundColor: gpColor.rgba.energized(0.5),
+            pointBorderColor: gpColor.rgba.white(),
+            pointHoverBackgroundColor: gpColor.rgba.energized(1),
+            pointHoverBorderColor: 'rgba(0,0,0,0)',
+            pointRadius: 3,
+            lineTension: 0.1
+          },
+          {
+            yAxisID: 'y-axis-left',
+            type: 'line',
+            label: translations['GRAPH.ACCOUNT.TX_ACCUMULATION_LABEL'],
+            fill: false,
+            borderColor: gpColor.rgba.positive(0.5),
+            borderWidth: 2,
+            backgroundColor: gpColor.rgba.positive(0.7),
+            pointBackgroundColor: gpColor.rgba.positive(0.5),
+            pointBorderColor: gpColor.rgba.white(),
+            pointHoverBackgroundColor: gpColor.rgba.positive(1),
+            pointHoverBorderColor: 'rgba(0,0,0,0)',
+            pointRadius: 3,
+            lineTension: 0.1
+          },
+          {
+            yAxisID: 'y-axis-left',
+            type: 'line',
+            label: translations['GRAPH.ACCOUNT.BALANCE_LABEL'],
+            fill: 'origin',
+            borderColor: gpColor.rgba.calm(0.5),
+            borderWidth: 2,
+            pointBackgroundColor: gpColor.rgba.calm(0.5),
+            pointBorderColor: gpColor.rgba.white(),
+            pointHoverBackgroundColor: gpColor.rgba.calm(1),
+            pointHoverBorderColor: 'rgba(0,0,0,0)',
+            pointRadius: 3,
+            lineTension: 0.1
+          }
+        ];
+      });
+  };
+
+  $scope.onChartClick = function(data, e, item) {
+    if (!item) return;
+    var from = $scope.times[item._index];
+    var to = moment.unix(from).utc().add(1, $scope.formData.rangeDuration).unix();
+    var query = '_exists_:transactions AND medianTime:>={0} AND medianTime:<{1}'.format(from, to);
+    if ($scope.formData.pubkey) {
+      query += ' AND (transactions.issuers:' + $scope.formData.pubkey + ' OR transactions.outputs:*' + $scope.formData.pubkey + ')';
+    }
+    $state.go('app.blockchain_search', {q: query});
+  };
+}
+
+
+/**
+ * Graph that display received/sent certification
+ */
+function GpAccountCertificationController($scope, $controller, $q, $state, $filter, $translate, gpData, gpColor, csWallet) {
+  'ngInject';
+
+  // Initialize the super class and extend it.
+  angular.extend(this, $controller('GpCurrencyAbstractCtrl', {$scope: $scope}));
+
+  $scope.init = function(e, state) {
+      if (state && state.stateParams && state.stateParams.pubkey) { // Currency parameter
+        $scope.formData.pubkey = state.stateParams.pubkey;
+      }
+      else if(csWallet.isLogin()) {
+        $scope.formData.pubkey = csWallet.data.pubkey;
+      }
+
+      // for DEV only
+      //$scope.formData.pubkey = '38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE';
+  };
+
+  $scope.load = function(updateTimePct) {
+
+    var formData = $scope.formData;
+
+    return $q.all([
+
+      $translate('GRAPH.ACCOUNT.CERTIFICATION_TITLE', formData),
+
+      // translate i18n keys
+      $translate(['GRAPH.ACCOUNT.GIVEN_CERT_LABEL',
+        'GRAPH.ACCOUNT.RECEIVED_CERT_LABEL',
+        'GRAPH.ACCOUNT.GIVEN_CERT_DELTA_LABEL',
+        'GRAPH.ACCOUNT.RECEIVED_CERT_DELTA_LABEL',
+        'COMMON.DATE_PATTERN',
+        'COMMON.DATE_SHORT_PATTERN',
+        'COMMON.DATE_MONTH_YEAR_PATTERN']),
+
+      // get data
+      gpData.wot.certifications(formData)
+    ])
+      .then(function(result) {
+
+        var title = result[0];
+        var translations = result[1];
+        result = result[2];
+
+        if (!result || !result.times) return; // no data
+        $scope.times = result.times;
+
+        var formatInteger = $filter('formatInteger');
+
+        // Data
+        $scope.data = [
+          result.deltaReceived,
+          result.received,
+          result.deltaGiven,
+          result.given
+        ];
+
+        // Labels
+        $scope.labels = result.labels;
+
+        var displayFormats = {
+          hour: translations['COMMON.DATE_PATTERN'],
+          day: translations['COMMON.DATE_SHORT_PATTERN'],
+          month: translations['COMMON.DATE_MONTH_YEAR_PATTERN']
+        };
+        var displayFormat = displayFormats[$scope.formData.rangeDuration];
+        // Labels
+        $scope.labels = result.times.reduce(function(res, time) {
+          return res.concat(moment.unix(time).local().format(displayFormat));
+        }, []);
+
+        // Colors
+        $scope.colors = gpColor.scale.fix(result.times.length);
+
+        // Update options with received values
+        $scope.updateRange(result.times[0], result.times[result.times.length-1], updateTimePct);
+
+        // Options
+        $scope.options = {
+          responsive: true,
+          maintainAspectRatio: true,
+          title: {
+            display: true,
+            text: title
+          },
+          scales: {
+            yAxes: [
+              {
+                id: 'y-axis-left',
+                type: 'linear',
+                position: 'left'
+              },
+              {
+                id: 'y-axis-hide',
+                type: 'linear',
+                display: false,
+                position: 'right'
+              }
+            ]
+          },
+          legend: {
+            display: true
+          },
+          tooltips: {
+            enabled: true,
+            mode: 'index',
+            callbacks: {
+              label: function(tooltipItems, data) {
+                // Should add a '+' before value ?
+                var addPlus = (tooltipItems.datasetIndex === 0 || tooltipItems.datasetIndex === 2) && tooltipItems.yLabel > 0;
+                return data.datasets[tooltipItems.datasetIndex].label +
+                  ': ' +
+                  (addPlus ? '+' : '') +
+                  formatInteger(tooltipItems.yLabel);
+              }
+            }
+          }
+        };
+
+        $scope.datasetOverride = [
+          {
+            yAxisID: 'y-axis-left',
+            type: 'bar',
+            label: translations['GRAPH.ACCOUNT.RECEIVED_CERT_DELTA_LABEL'],
+            borderColor: gpColor.rgba.positive(0.6),
+            borderWidth: 1,
+            backgroundColor: gpColor.rgba.positive(0.4),
+            hoverBackgroundColor: gpColor.rgba.positive(0.6)
+          },
+          {
+            yAxisID: 'y-axis-left',
+            type: 'line',
+            label: translations['GRAPH.ACCOUNT.RECEIVED_CERT_LABEL'],
+            fill: false,
+            borderColor: gpColor.rgba.positive(0.5),
+            borderWidth: 2,
+            backgroundColor: gpColor.rgba.positive(1),
+            pointBackgroundColor: gpColor.rgba.positive(0.5),
+            pointBorderColor: gpColor.rgba.white(),
+            pointHoverBackgroundColor: gpColor.rgba.positive(1),
+            pointHoverBorderColor: 'rgba(0,0,0,0)',
+            pointRadius: 3
+          },
+          {
+            yAxisID: 'y-axis-left',
+            type: 'bar',
+            label: translations['GRAPH.ACCOUNT.GIVEN_CERT_DELTA_LABEL'],
+            borderColor: gpColor.rgba.assertive(0.6),
+            borderWidth: 1,
+            backgroundColor: gpColor.rgba.assertive(0.4),
+            hoverBackgroundColor: gpColor.rgba.assertive(0.6)
+          },
+          {
+            yAxisID: 'y-axis-left',
+            type: 'line',
+            label: translations['GRAPH.ACCOUNT.GIVEN_CERT_LABEL'],
+            fill: false,
+            borderColor: gpColor.rgba.assertive(0.4),
+            borderWidth: 2,
+            backgroundColor: gpColor.rgba.assertive(1),
+            pointBackgroundColor: gpColor.rgba.assertive(0.4),
+            pointBorderColor: gpColor.rgba.white(),
+            pointHoverBackgroundColor: gpColor.rgba.assertive(1),
+            pointHoverBorderColor: 'rgba(0,0,0,0)',
+            pointRadius: 3,
+            lineTension: 0.1
+          }
+        ];
+      });
+  };
+
+  $scope.onChartClick = function(data, e, item) {
+    if (!item) return;
+    var from = $scope.times[item._index];
+    var to = moment.unix(from).utc().add(1, $scope.formData.rangeDuration).unix();
+    var query = '_exists_:transactions AND medianTime:>={0} AND medianTime:<{1}'.format(from, to);
+    if ($scope.formData.pubkey) {
+      query += ' AND (transactions.issuers:' + $scope.formData.pubkey + ' OR transactions.outputs:*' + $scope.formData.pubkey + ')';
+    }
+    $state.go('app.blockchain_search', {q: query});
+  };
+}
diff --git a/www/plugins/graph/js/controllers/blockchain-controllers.js b/www/plugins/graph/js/controllers/blockchain-controllers.js
index a7f0ba1a97ef964a0789d38010c4b4c931dbe983..a9b770aa7b02022ed98c4fc7820114ee3cd63d91 100644
--- a/www/plugins/graph/js/controllers/blockchain-controllers.js
+++ b/www/plugins/graph/js/controllers/blockchain-controllers.js
@@ -31,61 +31,21 @@ angular.module('cesium.graph.blockchain.controllers', ['chart.js', 'cesium.servi
 ;
 
 
-function GpBlockchainTxCountController($scope, $q, $state, $filter, $translate, $ionicPopover, csCurrency, BMA, esHttp, gpData) {
+function GpBlockchainTxCountController($scope, $controller, $q, $state, $filter, $translate, gpData, gpColor) {
   'ngInject';
 
-  $scope.loading = true;
-  $scope.height=undefined;
-  $scope.width=undefined;
-  $scope.formData = {
-    timePct: 100,
-    useRelative: false /*csSettings.data.useRelative*/
-  };
-
-  // Default TX range duration
-  $scope.txOptions = {
-    rangeDuration: 'day'
-  };
-
-  $scope.enter = function(e, state) {
-    if ($scope.loading) {
-
-      if (state && state.stateParams && state.stateParams.currency) { // Currency parameter
-        $scope.currency = state.stateParams.currency;
-      }
-
-      // Make sure there is currency, or load it not
-      if (!$scope.currency) {
-        return csCurrency.default()
-          .then(function(currency) {
-            $scope.currency = currency ? currency.name : null;
-            return $scope.enter(e, state);
-          });
-      }
-
-      $scope.load()
-        .then(function() {
-          $scope.loading = false;
-        });
-    }
-  };
-  $scope.$on('$ionicParentView.enter', $scope.enter);
+  // Initialize the super class and extend it.
+  angular.extend(this, $controller('GpCurrencyAbstractCtrl', {$scope: $scope}));
 
   $scope.load = function(updateTimePct) {
 
-    updateTimePct = angular.isDefined(updateTimePct) ? updateTimePct : true;
-
-    var truncDate = function(time) {
-      return moment.unix(time).utc().startOf($scope.txOptions.rangeDuration).unix();
-    };
-
-    var txOptions = $scope.txOptions;
+    var formData = $scope.formData;
 
     return $q.all([
 
-      $translate($scope.txOptions.issuer?
+      $translate($scope.formData.issuer?
         'GRAPH.BLOCKCHAIN.TX_AMOUNT_PUBKEY_TITLE':
-        'GRAPH.BLOCKCHAIN.TX_AMOUNT_TITLE', txOptions),
+        'GRAPH.BLOCKCHAIN.TX_AMOUNT_TITLE', formData),
 
       // translate i18n keys
       $translate(['GRAPH.BLOCKCHAIN.TX_AMOUNT_LABEL',
@@ -95,38 +55,25 @@ function GpBlockchainTxCountController($scope, $q, $state, $filter, $translate,
         'COMMON.DATE_SHORT_PATTERN',
         'COMMON.DATE_MONTH_YEAR_PATTERN']),
 
-      // get block #0
-      $scope.firstBlockTime ?
-        $q.when({medianTime: $scope.firstBlockTime}) :
-        BMA.blockchain.block({block: 0})
-          .catch(function(err) {
-            if (err && err.ucode == BMA.errorCodes.BLOCK_NOT_FOUND) {
-              return {medianTime: esHttp.date.now()};
-            }
-          }),
-
       // get data
-      gpData.blockchain.txCount($scope.currency, txOptions)
+      gpData.blockchain.txCount($scope.formData.currency, formData)
     ])
       .then(function(result) {
 
         var title = result[0];
 
         var translations = result[1];
-        $scope.firstBlockTime = $scope.firstBlockTime || result[2].medianTime;
-        $scope.formData.firstBlockTime = $scope.formData.firstBlockTime || truncDate($scope.firstBlockTime);
-        $scope.formData.currencyAge = truncDate(esHttp.date.now()) - $scope.formData.firstBlockTime;
-        result = result[3];
+        result = result[2];
 
         if (!result || !result.times) return; // no data
         $scope.times = result.times;
 
         var formatInteger = $filter('formatInteger');
         var formatAmount =  $filter('formatDecimal');
-        $scope.currencySymbol = $filter('currencySymbolNoHtml')($scope.currency, false/*$scope.formData.useRelative*/);
+        $scope.currencySymbol = $filter('currencySymbolNoHtml')($scope.formData.currency, $scope.formData.useRelative);
 
         // Data
-        if ($scope.txOptions.rangeDuration != 'hour') {
+        if ($scope.formData.rangeDuration != 'hour') {
           $scope.data = [
             result.amount,
             result.count,
@@ -148,27 +95,17 @@ function GpBlockchainTxCountController($scope, $q, $state, $filter, $translate,
           day: translations['COMMON.DATE_SHORT_PATTERN'],
           month: translations['COMMON.DATE_MONTH_YEAR_PATTERN']
         };
-        var displayFormat = displayFormats[$scope.txOptions.rangeDuration];
+        var displayFormat = displayFormats[$scope.formData.rangeDuration];
         // Labels
         $scope.labels = result.times.reduce(function(res, time) {
           return res.concat(moment.unix(time).local().format(displayFormat));
         }, []);
 
         // Colors
-        $scope.colors = result.times.reduce(function(res) {
-          return res.concat('rgba(17,193,243,0.5)');
-        }, []);
+        $scope.colors = gpColor.scale.fix(result.times.length);
 
-        // Update options with received values
-        $scope.txOptions.startTime = result.times[0];
-        $scope.txOptions.endTime = result.times[result.times.length-1];
-        $scope.formData.timeWindow = $scope.formData.timeWindow || $scope.txOptions.endTime - $scope.txOptions.startTime;
-        $scope.formData.rangeDuration = $scope.formData.rangeDuration || $scope.formData.timeWindow / result.times.length;
-
-        if (updateTimePct) {
-          $scope.formData.timePct = Math.ceil(($scope.txOptions.startTime - $scope.formData.firstBlockTime) * 100 /
-            ($scope.formData.currencyAge - $scope.formData.timeWindow));
-        }
+        // Update range options with received values
+        $scope.updateRange(result.times[0], result.times[result.times.length-1], updateTimePct);
 
         // Options
         $scope.options = {
@@ -233,18 +170,18 @@ function GpBlockchainTxCountController($scope, $q, $state, $filter, $translate,
             yAxisID: 'y-axis-amount',
             type: 'bar',
             label: translations['GRAPH.BLOCKCHAIN.TX_AMOUNT_LABEL'],
-            hoverBackgroundColor: 'rgba(17,193,243,0.6)'
+            hoverBackgroundColor: gpColor.rgba.calm(0.6)
           },
           {
             yAxisID: 'y-axis-count',
             type: 'line',
             label: translations['GRAPH.BLOCKCHAIN.TX_COUNT_LABEL'],
             fill: false,
-            borderColor: 'rgba(150,150,150,0.5)',
+            borderColor: gpColor.rgba.gray(0.5),
             borderWidth: 2,
-            pointBackgroundColor: 'rgba(150,150,150,0.5)',
-            pointBorderColor: 'rgba(255,255,255,1)',
-            pointHoverBackgroundColor: 'rgba(150,150,150,1)',
+            pointBackgroundColor: gpColor.rgba.gray(0.5),
+            pointBorderColor: gpColor.rgba.white(),
+            pointHoverBackgroundColor: gpColor.rgba.gray(1),
             pointHoverBorderColor: 'rgba(0,0,0,0)',
             pointRadius: 3
           },
@@ -264,122 +201,25 @@ function GpBlockchainTxCountController($scope, $q, $state, $filter, $translate,
       });
   };
 
-  $scope.setSize = function(height, width, maintainAspectRatio) {
-    $scope.height = height;
-    $scope.width = width;
-    $scope.maintainAspectRatio = angular.isDefined(maintainAspectRatio) ? maintainAspectRatio : $scope.maintainAspectRatio;
-  };
-
-  $scope.showTxRange = function(data, e, item) {
-    if (!item) return
+  $scope.onChartClick = function(data, e, item) {
+    if (!item) return;
     var from = $scope.times[item._index];
-    var to = moment.unix(from).utc().add(1, $scope.txOptions.rangeDuration).unix();
+    var to = moment.unix(from).utc().add(1, $scope.formData.rangeDuration).unix();
     var query = '_exists_:transactions AND medianTime:>={0} AND medianTime:<{1}'.format(from, to);
-    if ($scope.txOptions.issuer) {
-      query += ' AND issuer:' + $scope.txOptions.issuer;
+    if ($scope.formData.issuer) {
+      query += ' AND issuer:' + $scope.formData.issuer;
     }
     $state.go('app.blockchain_search', {q: query});
   };
 
-  $scope.setTxRangeDuration = function(txRangeDuration) {
-    $scope.hideActionsPopover();
-    if ($scope.txOptions && txRangeDuration == $scope.txOptions.rangeDuration) return;
-
-    $scope.txOptions.rangeDuration = txRangeDuration;
-
-    // Restore default values
-    delete $scope.txOptions.startTime;
-    delete $scope.txOptions.endTime;
-    $scope.formData = {
-      timePct: 100
-    };
-
-    // Reload TX data
-    $scope.load();
-  };
-
-  $scope.loadPreviousTx = function() {
-    $scope.txOptions.startTime -= $scope.times.length * $scope.formData.rangeDuration;
-    if ($scope.txOptions.startTime < $scope.firstBlockTime) {
-      $scope.txOptions.startTime = $scope.firstBlockTime;
-    }
-    $scope.txOptions.endTime = $scope.txOptions.startTime + $scope.times.length * $scope.formData.rangeDuration;
-    // Reload TX data
-    $scope.load();
-  };
-
-  $scope.loadNextTx = function() {
-    $scope.txOptions.startTime += $scope.times.length * $scope.formData.rangeDuration;
-    if ($scope.txOptions.startTime > $scope.firstBlockTime + $scope.formData.currencyAge - $scope.formData.timeWindow) {
-      $scope.txOptions.startTime = $scope.firstBlockTime + $scope.formData.currencyAge - $scope.formData.timeWindow;
-    }
-    $scope.txOptions.endTime = $scope.txOptions.startTime + $scope.times.length * $scope.formData.rangeDuration;
-    // Reload TX data
-    $scope.load();
-  };
-
-  $scope.onTxTimeChanged = function() {
-    $scope.txOptions.startTime = $scope.firstBlockTime + (parseFloat($scope.formData.timePct) / 100) * ($scope.formData.currencyAge - $scope.formData.timeWindow) ;
-    $scope.txOptions.endTime = $scope.txOptions.startTime + $scope.times.length * $scope.formData.rangeDuration;
-
-    // Reload TX data
-    $scope.load(false);
-  };
-
-  /* -- Popover -- */
-
-  $scope.showTxActionsPopover = function(event) {
-    $scope.hideActionsPopover();
-    $ionicPopover.fromTemplateUrl('plugins/graph/templates/blockchain/popover_tx_actions.html', {
-      scope: $scope
-    }).then(function(popover) {
-      $scope.actionsPopover = popover;
-      //Cleanup the popover when we're done with it!
-      $scope.$on('$destroy', function() {
-        $scope.actionsPopover.remove();
-      });
-      $scope.actionsPopover.show(event);
-    });
-  };
-
-  $scope.hideActionsPopover = function() {
-    if ($scope.actionsPopover) {
-      $scope.actionsPopover.hide();
-    }
-  };
-
 }
 
 
-function GpBlockchainIssuersController($scope, $q, $state, $translate, csCurrency, gpData) {
+function GpBlockchainIssuersController($scope, $controller, $q, $state, $translate, gpColor, gpData) {
   'ngInject';
-  $scope.loading = true;
-  $scope.height = undefined;
-  $scope.width = undefined;
-
-  $scope.enter = function(e, state) {
-    if ($scope.loading) {
-
-      if (state && state.stateParams && state.stateParams.currency) { // Currency parameter
-        $scope.currency = state.stateParams.currency;
-      }
-
-      // Make sure there is currency, or load it not
-      if (!$scope.currency) {
-        return csCurrency.default()
-          .then(function(currency) {
-            $scope.currency = currency ? currency.name : null;
-            return $scope.enter(e, state);
-          });
-      }
-
-      $scope.load()
-        .then(function() {
-          $scope.loading = false;
-        });
-    }
-  };
-  $scope.$on('$ionicParentView.enter', $scope.enter);
+
+  // Initialize the super class and extend it.
+  angular.extend(this, $controller('GpCurrencyAbstractCtrl', {$scope: $scope}));
 
   $scope.load = function() {
     return $q.all([
@@ -387,7 +227,7 @@ function GpBlockchainIssuersController($scope, $q, $state, $translate, csCurrenc
         'GRAPH.BLOCKCHAIN.BLOCKS_ISSUERS_TITLE',
         'GRAPH.BLOCKCHAIN.BLOCKS_ISSUERS_LABEL'
       ]),
-      gpData.blockchain.countByIssuer($scope.currency)
+      gpData.blockchain.countByIssuer($scope.formData.currency)
     ])
       .then(function(result) {
         var translations =  result[0];
@@ -423,18 +263,12 @@ function GpBlockchainIssuersController($scope, $q, $state, $translate, csCurrenc
         };
 
         // Colors
-        $scope.colors = gpData.util.colors.custom(result.data.length);
+        $scope.colors = gpColor.scale.custom(result.data.length);
 
       });
   };
 
-  $scope.setSize = function(height, width, maintainAspectRatio) {
-    $scope.height = height;
-    $scope.width = width;
-    $scope.maintainAspectRatio = angular.isDefined(maintainAspectRatio) ? maintainAspectRatio : $scope.maintainAspectRatio;
-  };
-
-  $scope.showBlockIssuer = function(data, e, item) {
+  $scope.onChartClick = function(data, e, item) {
     if (!item) return;
     var issuer = $scope.issuers[item._index];
     $state.go('app.wot_identity', issuer);
diff --git a/www/plugins/graph/js/controllers/common-controllers.js b/www/plugins/graph/js/controllers/common-controllers.js
new file mode 100644
index 0000000000000000000000000000000000000000..9615beb5d9137b527f71759a8c34c7fb00892d65
--- /dev/null
+++ b/www/plugins/graph/js/controllers/common-controllers.js
@@ -0,0 +1,232 @@
+
+angular.module('cesium.graph.common.controllers', ['cesium.services'])
+
+  .controller('GpCurrencyAbstractCtrl', GpCurrencyAbstractController)
+;
+
+function GpCurrencyAbstractController($scope, $filter, $ionicPopover, $ionicHistory, $state, csSettings, csCurrency, esHttp) {
+  'ngInject';
+
+  $scope.loading = true;
+  $scope.formData = $scope.formData || {
+    useRelative: csSettings.data.useRelative,
+    timePct: 100,
+    rangeDuration: 'day',
+    firstBlockTime: 0
+  };
+  $scope.formData.useRelative = angular.isDefined($scope.formData.useRelative) ?
+    $scope.formData.useRelative : csSettings.data.useRelative;
+  $scope.scale = 'linear';
+  $scope.height = undefined;
+  $scope.width = undefined;
+  $scope.maintainAspectRatio = true;
+  $scope.times = [];
+
+  function _truncDate(time) {
+    return moment.unix(time).utc().startOf($scope.formData.rangeDuration).unix();
+  }
+
+  $scope.enter = function (e, state) {
+    if ($scope.loading) {
+
+      if (state && state.stateParams) {
+        // remember state, to be able to refresh location
+        $scope.stateName = state && state.stateName;
+        $scope.stateParams = angular.copy(state && state.stateParams||{});
+
+        if (!$scope.formData.currency && state && state.stateParams && state.stateParams.currency) { // Currency parameter
+          $scope.formData.currency = state.stateParams.currency;
+        }
+        if (state.stateParams.timePct) {
+          $scope.formData.timePct = state.stateParams.timePct;
+        }
+        if (state.stateParams.group) {
+          $scope.formData.rangeDuration = state.stateParams.group;
+        }
+      }
+
+      $scope.init(e, state);
+
+      // Make sure there is currency, or load it not
+      if (!$scope.formData.currency) {
+        return csCurrency.default()
+          .then(function (currency) {
+            $scope.formData.currency = currency ? currency.name : null;
+            $scope.formData.firstBlockTime = currency ? _truncDate(currency.firstBlockTime) : 0;
+            if (!$scope.formData.firstBlockTime){
+              console.warn('[graph] currency.firstBlockTime not loaded ! Should have been loaded by currrency service!');
+            }
+            $scope.formData.currencyAge = _truncDate(esHttp.date.now()) - $scope.formData.firstBlockTime;
+            return $scope.enter(e, state);
+          });
+      }
+
+      $scope.load()
+        .then(function () {
+          $scope.loading = false;
+        });
+    }
+  };
+  $scope.$on('$csExtension.enter', $scope.enter);
+  $scope.$on('$ionicParentView.enter', $scope.enter);
+
+  $scope.updateLocation = function() {
+    $ionicHistory.nextViewOptions({
+      disableAnimate: true,
+      disableBack: true,
+      historyRoot: true
+    });
+
+    $scope.stateParams = $scope.stateParams || {};
+    $scope.stateParams.t = $scope.formData.timePct < 100 || $scope.formData.timePct >= 0 ? $scope.formData.timePct : undefined;
+    $scope.stateParams.stepUnit = $scope.formData.rangeDuration != 'day' ? $scope.formData.rangeDuration : undefined;
+
+    $state.go($scope.stateName, $scope.stateParams, {
+      reload: false,
+      inherit: true,
+      notify: false}
+    );
+  };
+
+  // Allow to fixe size, form a template (e.g. in a 'ng-init' tag)
+  $scope.setSize = function(height, width, maintainAspectRatio) {
+    $scope.height = height;
+    $scope.width = width;
+    $scope.maintainAspectRatio = angular.isDefined(maintainAspectRatio) ? maintainAspectRatio : $scope.maintainAspectRatio;
+  };
+
+  // When parent view execute a refresh action
+  $scope.$on('csView.action.refresh', function(event, context) {
+    if (!context || context == 'currency') {
+      return $scope.load();
+    }
+  });
+
+  $scope.init = function(stateParams) {
+    // Should be override by subclasses
+  };
+
+  $scope.load = function() {
+    // Should be override by subclasses
+  };
+
+  $scope.setScale = function(scale) {
+    $scope.hideActionsPopover();
+    $scope.scale = scale;
+
+    var format = $filter('formatInteger');
+
+    _.forEach($scope.options.scales.yAxes, function(yAxe) {
+      yAxe.type = scale;
+      yAxe.ticks = yAxe.ticks || {};
+      if (scale == 'linear') {
+        yAxe.ticks.beginAtZero = true;
+        delete yAxe.ticks.min;
+        yAxe.ticks.callback = function(value) {
+          return format(value);
+        };
+      }
+      else {
+        yAxe.ticks.min = 0;
+        delete yAxe.ticks.beginAtZero;
+        delete yAxe.ticks.callback;
+        yAxe.ticks.callback = function(value, index) {
+          if (!value) return;
+          if (Math.log10(value)%1 === 0 || Math.log10(value/3)%1 === 0) {
+            return format(value);
+          }
+          return '';
+        };
+      }
+    });
+  };
+
+  $scope.setRangeDuration = function(rangeDuration) {
+    $scope.hideActionsPopover();
+    if ($scope.formData && rangeDuration == $scope.formData.rangeDuration) return;
+
+    $scope.formData.rangeDuration = rangeDuration;
+
+    // Restore default values
+    delete $scope.formData.startTime;
+    delete $scope.formData.endTime;
+    delete $scope.formData.rangeDurationSec;
+    //$scope.formData.timePct = 100;
+
+    // Reload data
+    $scope.load();
+    // Update location
+    $scope.updateLocation();
+  };
+
+  $scope.goPreviousRange = function() {
+    $scope.formData.startTime -= $scope.times.length * $scope.formData.rangeDurationSec;
+    if ($scope.formData.startTime < $scope.formData.firstBlockTime) {
+      $scope.formData.startTime = $scope.formData.firstBlockTime;
+    }
+    $scope.formData.endTime = $scope.formData.startTime + $scope.times.length * $scope.formData.rangeDurationSec;
+
+    // Reload data
+    $scope.load();
+    // Update location
+    $scope.updateLocation();
+  };
+
+  $scope.goNextRange = function() {
+    $scope.formData.startTime += $scope.times.length * $scope.formData.rangeDurationSec;
+    if ($scope.formData.startTime > $scope.formData.firstBlockTime + $scope.formData.currencyAge - $scope.formData.timeWindow) {
+      $scope.formData.startTime = $scope.formData.firstBlockTime + $scope.formData.currencyAge - $scope.formData.timeWindow;
+    }
+    $scope.formData.endTime = $scope.formData.startTime + $scope.times.length * $scope.formData.rangeDurationSec;
+    // Reload data
+    $scope.load();
+    // Update location
+    $scope.updateLocation();
+  };
+
+  $scope.onRangeChanged = function() {
+    $scope.formData.startTime = $scope.formData.firstBlockTime + (parseFloat($scope.formData.timePct) / 100) * ($scope.formData.currencyAge - $scope.formData.timeWindow) ;
+    $scope.formData.endTime = $scope.formData.startTime + $scope.times.length * $scope.formData.rangeDurationSec;
+
+    // Reload data
+    $scope.load(false);
+    // Update location
+    $scope.updateLocation();
+  };
+
+  $scope.updateRange = function(startTime, endTime, updateTimePct) {
+    updateTimePct = angular.isDefined(updateTimePct) ? updateTimePct : true;
+
+    $scope.formData.startTime = startTime;
+    $scope.formData.endTime = endTime;
+    $scope.formData.timeWindow = $scope.formData.timeWindow || $scope.formData.endTime - $scope.formData.startTime;
+    $scope.formData.rangeDurationSec = $scope.formData.rangeDurationSec || $scope.formData.timeWindow / ($scope.times.length-1);
+
+    if (updateTimePct) {
+      $scope.formData.timePct = Math.ceil(($scope.formData.startTime - $scope.formData.firstBlockTime) * 100 /
+        ($scope.formData.currencyAge - $scope.formData.timeWindow));
+    }
+  };
+
+  /* -- Popover -- */
+
+  $scope.showActionsPopover = function(event) {
+    $scope.hideActionsPopover();
+    $ionicPopover.fromTemplateUrl('plugins/graph/templates/common/popover_range_actions.html', {
+      scope: $scope
+    }).then(function(popover) {
+      $scope.actionsPopover = popover;
+      //Cleanup the popover when we're done with it!
+      $scope.$on('$destroy', function() {
+        $scope.actionsPopover.remove();
+      });
+      $scope.actionsPopover.show(event);
+    });
+  };
+
+  $scope.hideActionsPopover = function() {
+    if ($scope.actionsPopover) {
+      $scope.actionsPopover.hide();
+    }
+  };
+}
diff --git a/www/plugins/graph/js/controllers/currency-controllers.js b/www/plugins/graph/js/controllers/currency-controllers.js
index 056588024fcd84c5e42f33e97f8894e6c0e353fd..aaf200e9b7ef3034ae93efa5fc0f3968010a6f96 100644
--- a/www/plugins/graph/js/controllers/currency-controllers.js
+++ b/www/plugins/graph/js/controllers/currency-controllers.js
@@ -1,5 +1,5 @@
 
-angular.module('cesium.graph.currency.controllers', ['chart.js', 'cesium.graph.services', 'cesium.graph.blockchain.controllers'])
+angular.module('cesium.graph.currency.controllers', ['chart.js', 'cesium.graph.services', 'cesium.graph.common.controllers'])
 
   .config(function($stateProvider, PluginServiceProvider, csConfig) {
     'ngInject';
@@ -82,8 +82,6 @@ angular.module('cesium.graph.currency.controllers', ['chart.js', 'cesium.graph.s
 
   .controller('GpCurrencyViewExtendCtrl', GpCurrencyViewExtendController)
 
-  .controller('GpCurrencyAbstractCtrl', GpCurrencyAbstractController)
-
   .controller('GpCurrencyMonetaryMassCtrl', GpCurrencyMonetaryMassController)
 
   .controller('GpCurrencyDUCtrl', GpCurrencyDUController)
@@ -103,63 +101,7 @@ function GpCurrencyViewExtendController($scope, PluginService, UIUtils, esSettin
   });
 }
 
-
-
-function GpCurrencyAbstractController($scope, csCurrency) {
-  'ngInject';
-
-  $scope.loading = true;
-  $scope.formData = $scope.formData || {};
-  $scope.height = undefined;
-  $scope.width = undefined;
-  $scope.maintainAspectRatio = true;
-
-  $scope.enter = function (e, state) {
-    if ($scope.loading) {
-
-      if (!$scope.formData.currency && state && state.stateParams && state.stateParams.currency) { // Currency parameter
-        $scope.formData.currency = state.stateParams.currency;
-      }
-
-      // Make sure there is currency, or load it not
-      if (!$scope.formData.currency) {
-        return csCurrency.default()
-          .then(function (currency) {
-            $scope.formData.currency = currency ? currency.name : null;
-            return $scope.enter(e, state);
-          });
-      }
-
-      $scope.load()
-        .then(function () {
-          $scope.loading = false;
-        });
-    }
-  };
-  $scope.$on('$csExtension.enter', $scope.enter);
-  $scope.$on('$ionicParentView.enter', $scope.enter);
-
-  // Allow to fixe size, form a template (e.g. in a 'ng-init' tag)
-  $scope.setSize = function(height, width, maintainAspectRatio) {
-    $scope.height = height;
-    $scope.width = width;
-    $scope.maintainAspectRatio = angular.isDefined(maintainAspectRatio) ? maintainAspectRatio : $scope.maintainAspectRatio;
-  };
-
-  // When parent view execute a refresh action
-  $scope.$on('csView.action.refresh', function(event, context) {
-    if (!context || context == 'currency') {
-      return $scope.load();
-    }
-  });
-
-  $scope.load = function() {
-    // Should be override by subclasses
-  };
-
-}
-
-function GpCurrencyMonetaryMassController($scope, $controller, $q, $state, $translate, $ionicPopover, gpData, $filter, csSettings) {
+function GpCurrencyMonetaryMassController($scope, $controller, $q, $state, $translate, $ionicPopover, gpColor, gpData, $filter, csSettings) {
   'ngInject';
 
   // Initialize the super class and extend it.
@@ -168,7 +110,6 @@ function GpCurrencyMonetaryMassController($scope, $controller, $q, $state, $tran
   $scope.formData.useRelative = angular.isDefined($scope.formData.useRelative) ?
     $scope.formData.useRelative :
     csSettings.data.useRelative;
-  $scope.scale = 'linear';
   $scope.displayShareAxis = true;
 
   $scope.onUseRelativeChanged = function() {
@@ -198,10 +139,11 @@ function GpCurrencyMonetaryMassController($scope, $controller, $q, $state, $tran
       .then(function(result) {
         var translations = result[0];
         result = result[1];
-        if (!result || !result.blocks) return;
+        if (!result || !result.times) return;
+        $scope.times = result.times;
 
         // Choose a date formatter, depending on the blocks period
-        var blocksPeriod = result.blocks[result.blocks.length-1].medianTime - result.blocks[0].medianTime;
+        var blocksPeriod = result.times[result.times.length-1] - result.times[0];
         var formatDate;
         if (blocksPeriod < 15778800/* less than 6 months*/) {
           formatDate = $filter('formatDateShort');
@@ -247,14 +189,12 @@ function GpCurrencyMonetaryMassController($scope, $controller, $q, $state, $tran
         $scope.data = data;
 
         // Labels
-        $scope.labels = result.labels.reduce(function(res, time) {
+        $scope.labels = result.times.reduce(function(res, time) {
           return res.concat(formatDate(time));
         }, []);
 
         // Colors
-        $scope.colors = result.blocks.reduce(function(res) {
-          return res.concat('rgba(17,193,243,0.5)');
-        }, []);
+        $scope.colors = gpColor.scale.fix(result.times.length);
 
         // Options
         $scope.options = {
@@ -264,6 +204,9 @@ function GpCurrencyMonetaryMassController($scope, $controller, $q, $state, $tran
             display: true,
             text: translations['GRAPH.CURRENCY.MONETARY_MASS_TITLE']
           },
+          legend: {
+            display: $scope.displayShareAxis
+          },
           scales: {
             yAxes: [
               {
@@ -293,9 +236,10 @@ function GpCurrencyMonetaryMassController($scope, $controller, $q, $state, $tran
         $scope.datasetOverride = [
           {
             yAxisID: 'y-axis-mass',
-            type: 'bar',
+            type: 'line',
             label: translations['GRAPH.CURRENCY.MONETARY_MASS_LABEL'],
-            hoverBackgroundColor: 'rgba(17,193,243,0.6)'
+            hoverBackgroundColor: gpColor.rgba.calm(0.6),
+            borderWidth: 1
           },
           {
             yAxisID: 'y-axis-mn',
@@ -305,6 +249,7 @@ function GpCurrencyMonetaryMassController($scope, $controller, $q, $state, $tran
             showLine: true,
             borderColor: 'rgba(255,201,0,1)',
             borderWidth: 2,
+            backgroundColor: 'rgba(255,201,0,1)',
             pointBackgroundColor: 'rgba(255,201,0,1)',
             pointBorderColor: 'rgba(255,255,255,1)',
             pointHoverBackgroundColor: 'rgba(255,201,0,1)',
@@ -320,44 +265,12 @@ function GpCurrencyMonetaryMassController($scope, $controller, $q, $state, $tran
 
   };
 
-  $scope.showBlock = function(data, e, item) {
+  $scope.onChartClick = function(data, e, item) {
     if (!item) return;
     var number = $scope.blocks[item._index];
     $state.go('app.view_block', {number: number});
   };
 
-  $scope.setScale = function(scale) {
-    $scope.hideActionsPopover();
-    $scope.scale = scale;
-
-    var format = $filter('formatInteger');
-
-    _.forEach($scope.options.scales.yAxes, function(yAxe) {
-      yAxe.type = scale;
-      yAxe.ticks = yAxe.ticks || {};
-      if (scale == 'linear') {
-        yAxe.ticks.beginAtZero = true;
-        delete yAxe.ticks.min;
-        yAxe.ticks.callback = function(value) {
-          return format(value);
-        };
-      }
-      else {
-        yAxe.ticks.min = 0;
-        delete yAxe.ticks.beginAtZero;
-        delete yAxe.ticks.callback;
-        yAxe.ticks.callback = function(value, index) {
-          if (!value) return;
-          //console.log(value + '->' + Math.log10(value)%1);
-          if (Math.log10(value)%1 === 0 || Math.log10(value/3)%1 === 0) {
-            return format(value);
-          }
-          return '';
-        };
-      }
-    });
-  };
-
   /* -- Popover -- */
 
   $scope.showActionsPopover = function(event) {
@@ -403,10 +316,11 @@ function GpCurrencyDUController($scope, $q, $controller, $translate, gpData, $fi
       .then(function(result) {
         var translations = result[0];
         result = result[1];
-        if (!result || !result.blocks) return;
+        if (!result || !result.times) return;
+        $scope.times = result.times;
 
         // Choose a date formatter, depending on the blocks period
-        var blocksPeriod = result.blocks[result.blocks.length-1].medianTime - result.blocks[0].medianTime;
+        var blocksPeriod = result.times[result.times.length-1] - result.times[0];
         var dateFilter;
         if (blocksPeriod < 15778800/* less than 6 months*/) {
           dateFilter = $filter('formatDateShort');
@@ -426,7 +340,7 @@ function GpCurrencyDUController($scope, $q, $controller, $translate, gpData, $fi
         ];
 
         // Labels
-        $scope.labels = result.labels.reduce(function(res, time) {
+        $scope.labels = result.times.reduce(function(res, time) {
           return res.concat(dateFilter(time));
         }, []);
 
@@ -485,7 +399,7 @@ function GpCurrencyDUController($scope, $q, $controller, $translate, gpData, $fi
 }
 
 
-function GpCurrencyMembersCountController($scope, $controller, $q, $state, $translate, gpData, $filter) {
+function GpCurrencyMembersCountController($scope, $controller, $q, $state, $translate, gpColor, gpData, $filter) {
   'ngInject';
 
   // Initialize the super class and extend it.
@@ -506,10 +420,12 @@ function GpCurrencyMembersCountController($scope, $controller, $q, $state, $tran
       .then(function(result) {
         var translations = result[0];
         result = result[1];
-        if (!result || !result.blocks) return;
+
+        if (!result || !result.times) return;
+        $scope.times = result.times;
 
         // Choose a date formatter, depending on the blocks period
-        var blocksPeriod = result.blocks[result.blocks.length-1].medianTime - result.blocks[0].medianTime;
+        var blocksPeriod = result.times[result.blocks.length-1] - result.times[0].medianTime;
         var dateFormat;
         if (blocksPeriod < 15778800/* less than 6 months*/) {
           dateFormat = $filter('formatDateShort');
@@ -519,7 +435,7 @@ function GpCurrencyMembersCountController($scope, $controller, $q, $state, $tran
         }
 
         // Format time
-        $scope.labels = result.labels.reduce(function(res, time) {
+        $scope.labels = result.times.reduce(function(res, time) {
           return res.concat(dateFormat(time));
         }, []);
 
@@ -558,24 +474,17 @@ function GpCurrencyMembersCountController($scope, $controller, $q, $state, $tran
         ];
 
         // Colors
-        $scope.colors = result.blocks.reduce(function(res) {
-          return res.concat('rgba(17,193,243,0.5)');
-        }, []);
-
-        // Keep times (need for click)
-        $scope.blockTimes = result.blocks.reduce(function(res, block) {
-          return res.concat(block.medianTime);
-        }, []);
+        $scope.colors = gpColor.scale.fix(result.blocks.length);
       });
   };
 
-  $scope.showBlock = function(data, e, item) {
+  $scope.onChartClick = function(data, e, item) {
     if (!item) return;
     if (!item._index) {
       $state.go('app.view_block', {number: 0});
       return;
     }
-    var from = $scope.blockTimes[item._index-1];
+    var from = $scope.times[item._index-1];
     var to = moment.unix(from).utc().add(1, 'day').unix();
     $state.go('app.blockchain_search', {
       q: '(_exists_:joiners OR _exists_:leavers OR _exists_:revoked OR _exists_:excluded) AND medianTime:>{0} AND medianTime:<={1}'.format(from, to)
diff --git a/www/plugins/graph/js/plugin.js b/www/plugins/graph/js/plugin.js
index e2d4965ea4e3b7f547e7fa709061be3bee4cbf48..baa6ca01317f4d4f46d539cc1ed0bbb2a5d299c2 100644
--- a/www/plugins/graph/js/plugin.js
+++ b/www/plugins/graph/js/plugin.js
@@ -3,8 +3,10 @@ angular.module('cesium.graph.plugin', [
     // Services
     'cesium.graph.services',
     // Controllers
+    'cesium.graph.common.controllers',
     'cesium.graph.blockchain.controllers',
     'cesium.graph.network.controllers',
-    'cesium.graph.currency.controllers'
+    'cesium.graph.currency.controllers',
+    'cesium.graph.account.controllers'
   ])
 ;
diff --git a/www/plugins/graph/js/services.js b/www/plugins/graph/js/services.js
index 6cae46c6948f4d5cb752c78b6c91e03699951e61..ac325d0c5568336f71bb25e2010cb8a76e8af2be 100644
--- a/www/plugins/graph/js/services.js
+++ b/www/plugins/graph/js/services.js
@@ -1,6 +1,7 @@
 
 angular.module('cesium.graph.services', [
     // Services
+    'cesium.graph.color.services',
     'cesium.graph.data.services'
   ])
 ;
diff --git a/www/plugins/graph/js/services/color-services.js b/www/plugins/graph/js/services/color-services.js
new file mode 100644
index 0000000000000000000000000000000000000000..d123e545ae14f463c9717faf8b0fd0f2531d8e4f
--- /dev/null
+++ b/www/plugins/graph/js/services/color-services.js
@@ -0,0 +1,138 @@
+angular.module('cesium.graph.color.services', [])
+
+  .factory('gpColor', function($rootScope) {
+    'ngInject';
+
+    var
+      constants = {
+        css2Rgb: {
+          'white': [255, 255, 255],
+          'assertive': [239, 71, 58], // ok
+          'calm': [17, 193, 243], // ok
+          'positive': [56, 126, 245], // ok
+          'balanced': [51, 205, 95], // ok
+          'energized': [255, 201, 0], // ok
+          'royal': [136, 106, 234], // ok
+          'gray': [150, 150, 150], // ok
+          'stable': [248, 248, 248] // ok
+        }
+      },
+      exports = {
+        scale: {}
+      };
+
+
+    /**
+     * Compute colors scale
+     * @param count
+     * @param opacity
+     * @param startColor
+     * @param startState
+     * @returns {Array}
+     */
+    exports.scale.custom = function (count, opacity, startColor, startState) {
+
+      function _state2side(state) {
+        switch (state) {
+          case 0:
+            return 0;
+          case 1:
+            return -1;
+          case 2:
+            return 0;
+          case 3:
+            return 1;
+        }
+      }
+
+      // From [0,1]
+      opacity = opacity>0 && opacity|| '0.55';
+
+      var defaultStateSize = Math.round(count / 2.5/*=4 states max*/);
+
+      // Start color [r,v,b]
+      var color = startColor ? angular.copy(startColor) : [255, 0, 0]; // Red
+
+      // Colors state: 0=keep, 1=decrease, 2=keep, 3=increase
+      var states = startState ? angular.copy(startState) : [0, 2, 3]; // R=keep, V=keep, B=increase
+
+      var steps = startColor ? [
+        Math.round(255 / defaultStateSize),
+        Math.round(255 / defaultStateSize),
+        Math.round(255 / defaultStateSize)
+      ] : [
+        Math.round((color[0] - 50) / defaultStateSize),
+        Math.round((255 - color[1]) / defaultStateSize),
+        Math.round((255 - color[2]) / defaultStateSize)
+      ];
+
+
+      // Compute start sides (1=increase, 0=flat, -1=decrease)
+      var sides = [
+        _state2side(states[0]),
+        _state2side(states[1]),
+        _state2side(states[2])];
+
+      // Use to detect when need to change a 'flat' state (when state = 0 or 2)
+      var stateCounters = [0, 0, 0];
+
+      var result = [];
+      for (var i = 0; i < count; i++) {
+        for (var j = 0; j < 3; j++) {
+          color[j] += sides[j] * steps[j];
+          stateCounters[j]++;
+          // color has reach a limit
+          if (((color[j] <= 0 || color[j] >= 255) && sides[j] !== 0) ||
+            (sides[j] === 0 && stateCounters[j] == defaultStateSize)) {
+            // Max sure not overflow limit
+            if (color[j] <= 0) {
+              color[j] = 0;
+            }
+            else if (color[j] >= 255) {
+              color[j] = 255;
+            }
+            // Go to the next state, in [0..3]
+            states[j] = (states[j] + 1) % 4;
+
+            // Update side from this new state
+            sides[j] = _state2side(states[j]);
+
+            // Reset state counter
+            stateCounters[j] = 0;
+          }
+        }
+
+        // Add the color to result
+        result.push('rgba(' + color[0] + ',' + color[1] + ',' + color[2] + ',' + opacity + ')');
+
+      }
+      return result;
+    };
+
+    exports.scale.default = function () {
+      return exports.scale.custom(25);
+    };
+
+    /**
+     * Create a array with the given color
+     **/
+    exports.scale.fix = function (length, color) {
+      return Array.apply(null, Array(length||25))
+        .map(String.prototype.valueOf, color||exports.rgba.calm(0.5));
+    };
+
+    // Create a function to generate a rgba string, from
+    exports.rgba = _.mapObject(constants.css2Rgb, function(rgbArray){
+      var prefix = 'rgba(' + rgbArray.join(',') + ',';
+      return function(opacity){
+        if (!opacity || opacity < 0) {
+          return 'rgb(' + rgbArray.join(',') + ')';
+        }
+        return prefix + opacity + ')';
+      };
+    });
+
+    return exports;
+  })
+
+;
diff --git a/www/plugins/graph/js/services/data-services.js b/www/plugins/graph/js/services/data-services.js
index ddd2d7733e9e3c70f410147fd9ae27e70e9dc312..198d30b25b5284f184b3a2d1590ff48b0dcd076b 100644
--- a/www/plugins/graph/js/services/data-services.js
+++ b/www/plugins/graph/js/services/data-services.js
@@ -1,119 +1,72 @@
 angular.module('cesium.graph.data.services', ['cesium.wot.services', 'cesium.es.http.services'])
 
-  .factory('gpData', function($rootScope, $q, $timeout, esHttp, BMA, csWot, csCache) {
+  .factory('gpData', function($rootScope, $q, $timeout, esHttp, BMA, csWot, csCache, csCurrency) {
     'ngInject';
 
     var
       currencyCache = csCache.get('gpData-currency-', csCache.constants.SHORT),
       exports = {
         node: {},
+        wot: {},
         blockchain: {},
-        util: {
-          colors: {}
-        },
         raw: {
           block: {
             search: esHttp.post('/:currency/block/_search')
           },
           blockstat: {
-            search: esHttp.post('/:currency/blockstat/_search?pretty')
+            search: esHttp.post('/:currency/blockstat/_search')
+          },
+          movement: {
+            search: esHttp.post('/:currency/movement/_search')
+          },
+          user: {
+            event: esHttp.post('/user/event/_search?pretty')
           }
         },
         regex: {
         }
       };
 
+      function onCurrencyLoad(data, deferred) {
+        deferred = deferred || $q.defer();
+        var currency = data.currencies[data.currencies.length-1];
 
-    /**
-     * Compute colors scale
-     * @param count
-     * @param opacity
-     * @param startColor
-     * @param startState
-     * @returns {Array}
-     */
-    exports.util.colors.custom = function(count, opacity, startColor, startState) {
-
-      function _state2side(state) {
-        switch(state) {
-          case 0:
-            return 0;
-          case 1:
-            return -1;
-          case 2:
-            return 0;
-          case 3:
-            return 1;
+        if (currency.firstBlockTime) {
+          deferred.resolve();
+          return deferred.promise;
         }
+        // Fill first block time value
+        BMA.blockchain.block({block: 0})
+            .then(function(block) {
+              currency.firstBlockTime = block.medianTime;
+              deferred.resolve();
+            })
+            .catch(function(err) {
+              if (err && err.ucode == BMA.errorCodes.BLOCK_NOT_FOUND) {
+                currency.firstBlockTime = esHttp.date.now();
+                deferred.resolve();
+              }
+              deferred.reject(err);
+            });
+        return deferred.promise;
       }
 
-      // From [0,1]
-      opacity = opacity || '0.55';
-
-      var defaultStateSize = Math.round(count / 2.5/*=4 states max*/);
-
-      // Start color [r,v,b]
-      var color = startColor ? angular.copy(startColor) : [255,0,0]; // Red
-
-      // Colors state: 0=keep, 1=decrease, 2=keep, 3=increase
-      var states = startState ? angular.copy(startState) : [0,2,3]; // R=keep, V=keep, B=increase
-
-      var steps = startColor ? [
-        Math.round(255 / defaultStateSize),
-        Math.round(255 / defaultStateSize),
-        Math.round(255 / defaultStateSize)
-      ] : [
-        Math.round((color[0]-50) / defaultStateSize),
-        Math.round((255-color[1]) / defaultStateSize),
-        Math.round((255-color[2]) / defaultStateSize)
-      ];
-
-
-      // Compute start sides (1=increase, 0=flat, -1=decrease)
-      var sides = [
-        _state2side(states[0]),
-        _state2side(states[1]),
-        _state2side(states[2])];
-
-      // Use to detect when need to change a 'flat' state (when state = 0 or 2)
-      var stateCounters  = [0,0,0];
-
-      var result = [];
-      for (var i = 0; i<count; i++) {
-        for (var j=0; j<3;j++) {
-          color[j] +=  sides[j] * steps[j];
-          stateCounters[j]++;
-          // color has reach a limit
-          if (((color[j] <= 0 || color[j] >= 255) && sides[j] !== 0) ||
-            (sides[j] === 0 && stateCounters[j] == defaultStateSize)) {
-            // Max sure not overflow limit
-            if (color[j] <= 0) {
-              color[j] = 0;
-            }
-            else if (color[j] >= 255) {
-              color[j] = 255;
-            }
-            // Go to the next state, in [0..3]
-            states[j] = (states[j] + 1) % 4;
-
-            // Update side from this new state
-            sides[j] = _state2side(states[j]);
 
-            // Reset state counter
-            stateCounters[j] = 0;
-          }
-        }
+    function _powBase(amount, base) {
+      return base <= 0 ? amount : amount * Math.pow(10, base);
+    }
 
-        // Add the color to result
-        result.push('rgba(' + color[0] + ',' + color[1] + ',' + color[2] + ',' + opacity+')');
-
-      }
-      return result;
-    };
+    function _initRangeOptions(options) {
+      options = options || {};
+      options.maxRangeSize = options.maxRangeSize || 30;
+      options.defaultTotalRangeCount = options.defaultTotalRangeCount || options.maxRangeSize*2;
 
-    exports.util.colors.default = function() {
-      return exports.util.colors.custom(25);
-    };
+      options.rangeDuration = options.rangeDuration || 'day';
+      options.endTime = options.endTime || moment().utc().add(1, options.rangeDuration).unix();
+      options.startTime = options.startTime ||
+        moment.unix(options.endTime).utc().subtract(options.defaultTotalRangeCount, options.rangeDuration).unix();
+      return options;
+    }
 
     /**
      * Graph: "blocks count by issuer"
@@ -188,10 +141,6 @@ angular.module('cesium.graph.data.services', ['cesium.wot.services', 'cesium.es.
         }
       }
 
-      function _powBase(amount, base) {
-        return base <= 0 ? amount : amount * Math.pow(10, base);
-      }
-
       var request = {
         query: {
           filtered: {
@@ -255,7 +204,7 @@ angular.module('cesium.graph.data.services', ['cesium.wot.services', 'cesium.es.
             }
           }
 
-          result.labels = result.blocks.reduce(function(res, block){
+          result.times = result.blocks.reduce(function(res, block){
             return res.concat(block.medianTime);
           }, []);
 
@@ -275,20 +224,14 @@ angular.module('cesium.graph.data.services', ['cesium.wot.services', 'cesium.es.
      */
     exports.blockchain.txCount = function(currency, options) {
 
-      var maxRangeSize = 30;
-      var defaultTotalRangeCount = maxRangeSize*2;
-
-      options = options || {};
-      options.rangeDuration = options.rangeDuration || 'day';
-      options.endTime = options.endTime || moment.unix(esHttp.date.now()).utc().add(1, options.rangeDuration).unix();
-      options.startTime = options.startTime ||
-        moment.unix(options.endTime).utc().subtract(defaultTotalRangeCount, options.rangeDuration).unix();
+      options = _initRangeOptions(options);
 
       var jobs = [];
 
       var from = moment.unix(options.startTime).utc().startOf(options.rangeDuration);
+      var to = moment.unix(options.endTime).utc();
       var ranges = [];
-      while(from.unix() < options.endTime) {
+      while(from.isBefore(to)) {
 
         ranges.push({
           from: from.unix(),
@@ -296,7 +239,7 @@ angular.module('cesium.graph.data.services', ['cesium.wot.services', 'cesium.es.
         });
 
         // Do not exceed max range count
-        if (ranges.length == maxRangeSize) {
+        if (ranges.length == options.maxRangeSize) {
           var request = {
             size: 0,
             aggs: {
@@ -329,8 +272,11 @@ angular.module('cesium.graph.data.services', ['cesium.wot.services', 'cesium.es.
           // prepare next loop
           ranges = [];
 
-          if (jobs.length < 10) {
-
+          if (jobs.length == 10) {
+            console.error('Too many parallel jobs!');
+            from = moment.unix(options.endTime).utc(); // stop while
+          }
+          else {
             jobs.push(
               exports.raw.blockstat.search(request, {currency: currency})
                 .then(function (res) {
@@ -349,11 +295,6 @@ angular.module('cesium.graph.data.services', ['cesium.wot.services', 'cesium.es.
                 })
             );
           }
-          else {
-            console.error('Too many call of txCount request ! ');
-            from = moment.unix(options.endTime).utc();
-          }
-
         }
       }
 
@@ -366,23 +307,15 @@ angular.module('cesium.graph.data.services', ['cesium.wot.services', 'cesium.es.
 
           res = _.sortBy(res, 'from');
 
-          var result = {};
-          result.count =  res.reduce(function(res, hit){
-            return res.concat(hit.count);
-          }, []);
-          result.avgByBlock =  res.reduce(function(res, hit){
-            return res.concat(hit.avgByBlock);
-          }, []);
-          result.maxByBlock =  res.reduce(function(res, hit){
-            return res.concat(hit.maxByBlock);
-          }, []);
-          result.amount =  res.reduce(function(res, hit){
-            return res.concat(hit.amount/100);
-          }, []);
-          result.times =  res.reduce(function(res, hit){
-            return res.concat(hit.from);
-          }, []);
-          return result;
+          return {
+            count: _.pluck(res, 'count'),
+            avgByBlock: _.pluck(res, 'avgByBlock'),
+            maxByBlock: _.pluck(res, 'maxByBlock'),
+            amount:  res.reduce(function(res, hit){
+              return res.concat(hit.amount/100);
+            }, []),
+            times: _.pluck(res, 'from')
+          };
         });
     };
 
@@ -391,9 +324,7 @@ angular.module('cesium.graph.data.services', ['cesium.wot.services', 'cesium.es.
      * @param currency
      * @returns {*}
      */
-    exports.node.blockCount = function(currency, pubkey, options) {
-
-      options = options || {};
+    exports.node.blockCount = function(currency, pubkey) {
 
       var request = {
         size: 0,
@@ -406,6 +337,356 @@ angular.module('cesium.graph.data.services', ['cesium.wot.services', 'cesium.es.
         });
     };
 
+
+    exports.raw.movement.getByRange = function(currency, pubkey, ranges) {
+      if (!pubkey) {
+        throw new Error('Missing \'pubkey\' argument!');
+      }
+      var request = {
+        size: 0,
+        query: {
+          bool: {
+            should: [
+              {term: {recipient: pubkey}},
+              {term: {issuer: pubkey}}
+            ]
+          }
+        },
+        aggs: {
+          tx: {
+            range: {
+              field: "medianTime",
+              ranges: ranges
+            },
+            aggs: {
+              received: {
+                filter: {term: {recipient: pubkey}},
+                aggs: {
+                  received_stats: {
+                    stats: {
+                      field: "amount"
+                    }
+                  }
+                }
+              },
+              sent: {
+                filter: {term: {issuer: pubkey}},
+                aggs: {
+                  sent_stats: {
+                    stats: {
+                      field: "amount"
+                    }
+                  }
+                }
+              }
+            }
+
+          }
+        }
+      };
+
+      return exports.raw.movement.search(request, {currency: currency})
+        .then(function(res) {
+          var aggs = res.aggregations;
+          if (!aggs.tx || !aggs.tx.buckets || !aggs.tx.buckets.length) return;
+          return (aggs.tx.buckets || []).reduce(function (res, agg) {
+            var sent = agg.sent.sent_stats;
+            var received = agg.received.received_stats;
+            return res.concat({
+              from: agg.from,
+              to: agg.to,
+              sent: -sent.sum / 100,
+              received: received.sum / 100,
+              delta: (received.sum - sent.sum) / 100,
+              count: received.count + sent.count
+            });
+          }, []);
+        });
+    };
+
+    exports.raw.movement.getUds = function(currency, ranges, fromMapping) {
+      var request = {
+        size: 0,
+        query: {
+          bool: {
+            should: [
+              {exists: {field: 'dividend'}}
+            ]
+          }
+        },
+        aggs: {
+          ud: {
+            range: {
+              field: 'medianTime',
+              ranges: ranges
+            },
+            aggs: {
+              ud_stats: {
+                stats: {
+                  field: 'dividend'
+                }
+              },
+              unitbase_stats: {
+                stats: {
+                  field: 'unitbase'
+                }
+              }
+            }
+          }
+        }
+      };
+
+      return exports.raw.block.search(request, {currency: currency})
+        .then(function(res) {
+          var aggs = res.aggregations;
+          if (!aggs.ud || !aggs.ud.buckets || !aggs.ud.buckets.length) return;
+          return (aggs.ud.buckets || []).reduce(function (res, agg) {
+            var from = fromMapping[agg.from];
+            res[from] = _powBase(agg.ud_stats.sum, agg.unitbase_stats.min) / 100;
+            return res;
+          }, {});
+        });
+    };
+
+    /**
+     * Graph: "tx count"
+     * @param currency
+     * @returns {*}
+     */
+    exports.blockchain.movement = function(currency, options) {
+
+      options = _initRangeOptions(options);
+      options.withUD = angular.isDefined(options.withUD) ? options.withUD : true;
+
+      var jobs = [];
+
+      // If need and missing: load membership periods
+      if (options.withUD && !options.memberships) {
+        return exports.wot.memberships(options)
+          .then(function(res) {
+            options.memberships = res || [];
+            return exports.blockchain.movement(currency, options);
+          });
+      }
+
+      var from = moment.unix(options.startTime).utc().startOf(options.rangeDuration);
+      var to = moment.unix(options.endTime).utc();
+
+      // Add a range to get TX before startTime
+      var ranges = [{
+        from: 0,
+        to: from.unix()
+      }];
+
+      var memberships = angular.copy(options.memberships).reverse();
+      var membership = memberships.pop();
+      while (membership && (membership.leaveTime && membership.leaveTime < from.unix())) {
+        membership = memberships.pop();
+      }
+
+      var udRanges = [];
+      var udFromMapping = {};
+
+      while(from.isBefore(to)) {
+
+        var range = {
+          from: from.unix(),
+          to: from.add(1, options.rangeDuration).unix()
+        };
+        ranges.push(range);
+
+        var member = membership && membership.joinTime < range.to;
+        if (member) {
+          var udRange = {
+            from: Math.max(membership.joinTime, range.from),
+            to: Math.min(membership.leaveTime, range.to)
+          };
+          udRanges.push(udRange);
+          udFromMapping[udRange.from] = range.from;
+          while (membership && (membership.leaveTime && membership.leaveTime < range.to)) {
+            membership = memberships.pop();
+          }
+        }
+
+        // Do not exceed max range count
+        if ((!jobs.length && ranges.length == options.maxRangeSize+1) || (jobs.length && ranges.length == options.maxRangeSize)) {
+
+          if (udRanges.length) {
+            jobs.push($q.all([
+              exports.raw.movement.getUds(currency, udRanges, udFromMapping),
+              exports.raw.movement.getByRange(currency, options.pubkey, ranges)
+            ])
+            .then(function(res){
+              var udsMap = res[0];
+              res[1].forEach(function(hit){
+                hit.ud = udsMap[hit.from]||0;
+              });
+              return res[1];
+            }));
+          }
+          else {
+            jobs.push(exports.raw.movement.getByRange(currency, options.pubkey, ranges));
+          }
+
+          // reset ranges for the next loop
+          ranges = [];
+        }
+      } // loop
+
+      return $q.all(jobs)
+        .then(function(res) {
+          // concat all results
+          res = res.reduce(function(res, hits){
+            if (!hits || !hits.length) return res;
+            return res.concat(hits);
+          }, []);
+
+          // Sort by 'from' field
+          res = _.sortBy(res, 'from');
+
+          // First item should be history (tx before startTime)
+          var history = res.splice(0,1)[0];
+          var txSum = history.delta||0;
+          var udSum = history.ud||0;
+          var balance = history.delta+history.ud||0;
+
+          return {
+            times: _.pluck(res, 'from'),
+            sent: _.pluck(res, 'sent'),
+            received: _.pluck(res, 'received'),
+            delta: _.pluck(res, 'delta'),
+            ud: _.pluck(res, 'ud'),
+            txSum: res.reduce(function(res, hit){
+              txSum += hit.delta;
+              return res.concat(txSum);
+            }, []),
+            udSum: res.reduce(function(res, hit){
+              udSum += hit.ud||0;
+              return res.concat(udSum);
+            }, []),
+            balance: res.reduce(function(res, hit){
+              balance += hit.delta+hit.ud||0;
+              return res.concat(balance);
+            }, []),
+            count: _.pluck(res, 'count')
+          };
+        });
+    };
+
+
+    /**
+     * Graph: "tx count"
+     * @param currency
+     * @returns {*}
+     */
+    exports.wot.certifications = function(options) {
+
+      options = _initRangeOptions(options);
+
+      return csWot.load(options.pubkey)
+        .then(function(idty) {
+          var res = {};
+          _.forEach(idty.given_cert||[], function(cert){
+            var truncTime = moment.unix(cert.time).utc().startOf(options.rangeDuration).unix();
+            res[truncTime] = res[truncTime] || {time:truncTime,given:0,received:0};
+            res[truncTime].given++;
+          });
+          _.forEach(idty.received_cert||[], function(cert){
+            var truncTime = moment.unix(cert.time).utc().startOf(options.rangeDuration).unix();
+            res[truncTime] = res[truncTime] || {time:truncTime,given:0,received:0};
+            res[truncTime].received++;
+          });
+
+          // Sort by time
+          res = _.sortBy(_.values(res), 'time');
+
+          // create final result
+          var result = {
+            times: _.pluck(res, 'time'),
+            deltaGiven: _.pluck(res, 'given'),
+            deltaReceived: _.pluck(res, 'received')
+          };
+          var sum = 0;
+          result.given = result.deltaGiven.reduce(function(res, delta) {
+            sum += delta;
+            return res.concat(sum);
+          }, []);
+          sum = 0;
+          result.received = result.deltaReceived.reduce(function(res, delta) {
+            sum += delta;
+            return res.concat(sum);
+          }, []);
+          return result;
+
+        });
+    };
+
+
+    exports.wot.memberships = function(options) {
+
+      options = options || {};
+
+      // Get user events on membership state
+      var request = {
+        "size": 1000,
+        "query": {
+          "bool": {
+            "filter": [
+              {"term": {"recipient" : options.pubkey }},
+              {"terms": {"code" : ["MEMBER_JOIN","MEMBER_ACTIVE","MEMBER_LEAVE","MEMBER_EXCLUDE","MEMBER_REVOKE"] }}
+            ]
+          }
+        },
+        "sort" : [
+          { "time" : {"order" : "asc"}}
+        ],
+        _source: ["code", "time"]
+      };
+
+      return exports.raw.user.event(request)
+
+        .then(function(res) {
+          if (!res.hits || !res.hits.total) return;
+
+          // Compute member periods
+          var lastJoinTime;
+          var result = res.hits.hits.reduce(function(res, hit){
+            var isMember = hit._source.code == 'MEMBER_JOIN' || hit._source.code == 'MEMBER_ACTIVE';
+            // If join
+            if (isMember && !lastJoinTime) {
+              lastJoinTime = hit._source.time;
+            }
+            // If leave
+            else if (!isMember && lastJoinTime) {
+              // Add an entry
+              res = res.concat({
+                joinTime: lastJoinTime,
+                leaveTime: hit._source.time
+              });
+              lastJoinTime = 0; // reset
+            }
+            return res;
+          }, []);
+
+          if (lastJoinTime) {
+            // Add last entry if need
+            result.push({
+              joinTime: lastJoinTime,
+              leaveTime: moment().utc().unix()
+            });
+          }
+
+          return result;
+        });
+    };
+
+
+    // register listener
+//    csCurrency.api.data.on.load($rootScope, onCurrencyLoad, this);
+
     return exports;
   })
+
+
+
 ;
diff --git a/www/plugins/graph/templates/account/graph_balance.html b/www/plugins/graph/templates/account/graph_balance.html
new file mode 100644
index 0000000000000000000000000000000000000000..7939969f663c4fb884147d4536794ceac7cef274
--- /dev/null
+++ b/www/plugins/graph/templates/account/graph_balance.html
@@ -0,0 +1,24 @@
+
+    <!-- button bar -->
+    <div class="button-bar-inline "
+         style="top: 33px; margin-top:-33px; position: relative;">
+      <button
+        class="button button-stable button-clear no-padding-xs pull-right"
+        ng-click="showActionsPopover($event)">
+        <i class="icon ion-navicon-round"></i>
+      </button>
+    </div>
+
+    <div class="padding-left padding-right">
+      <canvas id="account-balance" class="chart-bar"
+              height="{{height}}" width="{{width}}"
+              chart-data="data"
+              chart-dataset-override="datasetOverride"
+              chart-colors="colors"
+              chart-options="options"
+              chart-labels="labels"
+              chart-click="onChartClick">
+      </canvas>
+    </div>
+
+    <ng-include src="'plugins/graph/templates/common/graph_range_bar.html'"></ng-include>
diff --git a/www/plugins/graph/templates/account/graph_certifications.html b/www/plugins/graph/templates/account/graph_certifications.html
new file mode 100644
index 0000000000000000000000000000000000000000..6e4816e12f11abf0994697373a5ddc6d06e93b09
--- /dev/null
+++ b/www/plugins/graph/templates/account/graph_certifications.html
@@ -0,0 +1,12 @@
+
+    <div class="padding-left padding-right">
+      <canvas id="account-certifications" class="chart-bar"
+              height="{{height}}" width="{{width}}"
+              chart-data="data"
+              chart-dataset-override="datasetOverride"
+              chart-colors="colors"
+              chart-options="options"
+              chart-labels="labels"
+              chart-click="onChartClick">
+      </canvas>
+    </div>
diff --git a/www/plugins/graph/templates/account/view_stats.html b/www/plugins/graph/templates/account/view_stats.html
new file mode 100644
index 0000000000000000000000000000000000000000..7ded7f9e7d90a27307927b17e3d3c9ba5392a5fe
--- /dev/null
+++ b/www/plugins/graph/templates/account/view_stats.html
@@ -0,0 +1,39 @@
+<ion-view left-buttons="leftButtons"
+          cache-view="false">
+  <ion-nav-title>
+    {{'GRAPH.ACCOUNT.TITLE' | translate}}{{id}}
+  </ion-nav-title>
+
+  <ion-content scroll="true" class="no-padding">
+
+    <div class="center padding" ng-if="loading">
+      <ion-spinner icon="android"></ion-spinner>
+    </div>
+
+    <div class="list" ng-if="!loading">
+
+      <!--  - - - - Balance - - - - -->
+      <div class="item item-divider" translate>
+        GRAPH.ACCOUNT.BALANCE_DIVIDER
+      </div>
+
+      <div class="item no-padding-xs"
+           ng-include="'plugins/graph/templates/account/graph_balance.html'"
+           ng-controller="GpAccountBalanceCtrl"
+           ng-init="setSize(350, 1000)">
+      </div>
+
+      <!--  - - - - Balance - - - - -->
+      <div class="item item-divider" translate>
+        GRAPH.ACCOUNT.WOT_DIVIDER
+      </div>
+
+      <div class="item no-padding-xs"
+           ng-include="'plugins/graph/templates/account/graph_certifications.html'"
+           ng-controller="GpAccountCertificationCtrl"
+           ng-init="setSize(350, 1000)">
+      </div>
+
+  </ion-content>
+
+</ion-view>
diff --git a/www/plugins/graph/templates/blockchain/graph_block_issuers.html b/www/plugins/graph/templates/blockchain/graph_block_issuers.html
index 81ef7618a48a95fc40f396c33d63e60426977b49..c97178f57a54362cd3abb6ccf35b1a5ecb0d8e53 100644
--- a/www/plugins/graph/templates/blockchain/graph_block_issuers.html
+++ b/www/plugins/graph/templates/blockchain/graph_block_issuers.html
@@ -9,7 +9,7 @@
                 chart-labels="labels"
                 chart-colors="colors"
                 chart-options="barOptions"
-                chart-click="showBlockIssuer">
+                chart-click="onChartClick">
         </canvas>
       </div>
 
@@ -19,7 +19,7 @@
                 chart-data="data"
                 chart-labels="labels"
                 chart-colors="colors"
-                chart-click="showBlockIssuer">
+                chart-click="onChartClick">
         </canvas>
 
         <div class="gray padding-top text-center">
diff --git a/www/plugins/graph/templates/blockchain/graph_tx_count.html b/www/plugins/graph/templates/blockchain/graph_tx_count.html
index 93ea3734bd20d56567ec3feae2277451c91f640a..def80ecccdd8a248237444117435d4476ed015f0 100644
--- a/www/plugins/graph/templates/blockchain/graph_tx_count.html
+++ b/www/plugins/graph/templates/blockchain/graph_tx_count.html
@@ -4,7 +4,7 @@
          style="top: 33px; margin-top:-33px; position: relative;">
       <button
         class="button button-stable button-clear no-padding-xs pull-right"
-        ng-click="showTxActionsPopover($event)">
+        ng-click="showActionsPopover($event)">
         <i class="icon ion-navicon-round"></i>
       </button>
     </div>
@@ -17,26 +17,8 @@
               chart-colors="colors"
               chart-options="options"
               chart-labels="labels"
-              chart-click="showTxRange">
+              chart-click="onChartClick">
       </canvas>
     </div>
 
-    <div class="range range-positive no-padding-left no-padding-right">
-      <a
-        class="button button-stable button-clear no-padding pull-left"
-        ng-click="loadPreviousTx()">
-        <i class="icon ion-chevron-left"></i>
-      </a>
-      <input type="range"
-             ng-model="formData.timePct"
-             name="timePct"
-             min="0" max="100"
-             value="{{formData.timePct}}"
-             ng-change="onTxTimeChanged();"
-             ng-model-options="{ debounce: 250 }">
-      <a
-        class="button button-stable button-clear no-padding pull-right"
-        ng-click="loadNextTx($event)">
-        <i class="icon ion-chevron-right"></i>
-      </a>
-    </div>
+    <ng-include src="'plugins/graph/templates/common/graph_range_bar.html'"></ng-include>
diff --git a/www/plugins/graph/templates/common/graph_range_bar.html b/www/plugins/graph/templates/common/graph_range_bar.html
new file mode 100644
index 0000000000000000000000000000000000000000..3a7c75a0648e5a6707e313eec8bfbdc8d40a74a0
--- /dev/null
+++ b/www/plugins/graph/templates/common/graph_range_bar.html
@@ -0,0 +1,20 @@
+
+  <div class="range range-positive no-padding-left no-padding-right">
+    <a
+      class="button button-stable button-clear no-padding pull-left"
+      ng-click="goPreviousRange($event)">
+      <i class="icon ion-chevron-left"></i>
+    </a>
+    <input type="range"
+           ng-model="formData.timePct"
+           name="timePct"
+           min="0" max="100"
+           value="{{formData.timePct}}"
+           ng-change="onRangeChanged();"
+           ng-model-options="{ debounce: 250 }">
+    <a
+      class="button button-stable button-clear no-padding pull-right"
+      ng-click="goNextRange($event)">
+      <i class="icon ion-chevron-right"></i>
+    </a>
+  </div>
diff --git a/www/plugins/graph/templates/blockchain/popover_tx_actions.html b/www/plugins/graph/templates/common/popover_range_actions.html
similarity index 64%
rename from www/plugins/graph/templates/blockchain/popover_tx_actions.html
rename to www/plugins/graph/templates/common/popover_range_actions.html
index 970a901d9fa6602797c1b0a03d18d280b2cb6aa3..2211f9359fe7ab2aea5042bb324fd1abb7d5cfa3 100644
--- a/www/plugins/graph/templates/blockchain/popover_tx_actions.html
+++ b/www/plugins/graph/templates/common/popover_range_actions.html
@@ -7,23 +7,23 @@
 
       <!-- duration: hour -->
       <a class="item item-icon-right ink"
-         ng-click="setTxRangeDuration('hour')">
+         ng-click="setRangeDuration('hour')">
         <span ng-bind-html="'GRAPH.BLOCKCHAIN.TX_RANGE_DURATION.HOUR' | translate"></span>
-        <i class="icon ion-ios-checkmark-empty" ng-show="txOptions.rangeDuration=='hour'"></i>
+        <i class="icon ion-ios-checkmark-empty" ng-show="formData.rangeDuration=='hour'"></i>
       </a>
 
       <!-- duration: day -->
       <a class="item item-icon-right ink"
-         ng-click="setTxRangeDuration('day')">
+         ng-click="setRangeDuration('day')">
         <span ng-bind-html="'GRAPH.BLOCKCHAIN.TX_RANGE_DURATION.DAY' | translate"></span>
-        <i class="icon ion-ios-checkmark-empty" ng-show="txOptions.rangeDuration=='day'"></i>
+        <i class="icon ion-ios-checkmark-empty" ng-show="formData.rangeDuration=='day'"></i>
       </a>
 
       <!-- duration: month -->
       <a class="item item-icon-right ink"
-         ng-click="setTxRangeDuration('month')">
+         ng-click="setRangeDuration('month')">
         <span ng-bind-html="'GRAPH.BLOCKCHAIN.TX_RANGE_DURATION.MONTH' | translate"></span>
-        <i class="icon ion-ios-checkmark-empty" ng-show="txOptions.rangeDuration=='month'"></i>
+        <i class="icon ion-ios-checkmark-empty" ng-show="formData.rangeDuration=='month'"></i>
       </a>
 
     </div>
diff --git a/www/plugins/graph/templates/currency/graph_members_count.html b/www/plugins/graph/templates/currency/graph_members_count.html
index 2b26b0a4cb9760a31ff528350ce430bb7aeb6620..0b261fb47c03da64debe4bf15307864060e163ee 100644
--- a/www/plugins/graph/templates/currency/graph_members_count.html
+++ b/www/plugins/graph/templates/currency/graph_members_count.html
@@ -6,5 +6,5 @@
           chart-colors="colors"
           chart-options="options"
           chart-dataset-override="datasetOverride"
-          chart-click="showBlock">
+          chart-click="onChartClick">
   </canvas>
diff --git a/www/plugins/graph/templates/currency/graph_monetary_mass.html b/www/plugins/graph/templates/currency/graph_monetary_mass.html
index 2b34a02ac999b5c537c9ce34b5e15ea3bd8dd750..894e82b841d4b7375a9ca917d3d88d845ed95797 100644
--- a/www/plugins/graph/templates/currency/graph_monetary_mass.html
+++ b/www/plugins/graph/templates/currency/graph_monetary_mass.html
@@ -17,5 +17,5 @@
           chart-colors="colors"
           chart-dataset-override="datasetOverride"
           chart-options="options"
-          chart-click="showBlock">
+          chart-click="onChartClick">
   </canvas>
diff --git a/www/templates/network/view_peer.html b/www/templates/network/view_peer.html
index c8b1ec992c509ffb75a0b90bf8eea2954d013e22..2f52296f5794763aa23e78e2f423ecfd48f1bdb3 100644
--- a/www/templates/network/view_peer.html
+++ b/www/templates/network/view_peer.html
@@ -3,9 +3,9 @@
     <span translate>PEER.VIEW.TITLE</span>
   </ion-nav-title>
 
-  <ion-content class="has-header padding" scroll="true">
+  <ion-content class="has-header" scroll="true">
 
-    <div class="row">
+    <div class="row no-padding">
       <div class="col col-20 hidden-xs hidden-sm">&nbsp;
       </div>