diff --git a/RULES.md b/RULES.md
index 6c4e4a8..79c8b13 100644
--- a/RULES.md
+++ b/RULES.md
@@ -17,8 +17,8 @@ contradicts either, the originals govern.
## External Communication
-- [ ] Extension contacts exactly two external services: configured RPC endpoint
- and CoinDesk price API
+- [ ] Extension contacts exactly three external services: configured RPC
+ endpoint, CoinDesk price API, and Blockscout block-explorer API
- [ ] No analytics, telemetry, or tracking
- [ ] No user-specific data sent except to the configured RPC endpoint
- [ ] No Infura/Alchemy hard dependency
diff --git a/src/popup/views/helpers.js b/src/popup/views/helpers.js
index fcaa8c3..e265dcb 100644
--- a/src/popup/views/helpers.js
+++ b/src/popup/views/helpers.js
@@ -82,7 +82,7 @@ function showFlash(msg, duration = 2000) {
function balanceLine(symbol, amount, price, tokenId) {
const qty = amount.toFixed(4);
- const usd = price ? formatUsd(amount * price) : "";
+ const usd = price ? formatUsd(amount * price) || " " : " ";
const tokenAttr = tokenId ? ` data-token="${tokenId}"` : "";
const clickClass = tokenId
? " cursor-pointer hover:bg-hover balance-row"
@@ -219,6 +219,41 @@ function formatAddressHtml(address, ensName, maxLen, title) {
return `
${dot}${escapeHtml(displayAddr)}
`;
}
+function isoDate(timestamp) {
+ const d = new Date(timestamp * 1000);
+ const pad = (n) => String(n).padStart(2, "0");
+ return (
+ d.getFullYear() +
+ "-" +
+ pad(d.getMonth() + 1) +
+ "-" +
+ pad(d.getDate()) +
+ " " +
+ pad(d.getHours()) +
+ ":" +
+ pad(d.getMinutes()) +
+ ":" +
+ pad(d.getSeconds())
+ );
+}
+
+function timeAgo(timestamp) {
+ const seconds = Math.floor(Date.now() / 1000 - timestamp);
+ if (seconds < 60) return seconds + " seconds ago";
+ const minutes = Math.floor(seconds / 60);
+ if (minutes < 60)
+ return minutes + " minute" + (minutes !== 1 ? "s" : "") + " ago";
+ const hours = Math.floor(minutes / 60);
+ if (hours < 24) return hours + " hour" + (hours !== 1 ? "s" : "") + " ago";
+ const days = Math.floor(hours / 24);
+ if (days < 30) return days + " day" + (days !== 1 ? "s" : "") + " ago";
+ const months = Math.floor(days / 30);
+ if (months < 12)
+ return months + " month" + (months !== 1 ? "s" : "") + " ago";
+ const years = Math.floor(days / 365);
+ return years + " year" + (years !== 1 ? "s" : "") + " ago";
+}
+
module.exports = {
$,
showError,
@@ -233,4 +268,6 @@ module.exports = {
addressTitle,
formatAddressHtml,
truncateMiddle,
+ isoDate,
+ timeAgo,
};
diff --git a/src/popup/views/home.js b/src/popup/views/home.js
index 7fd2262..62b352b 100644
--- a/src/popup/views/home.js
+++ b/src/popup/views/home.js
@@ -3,6 +3,8 @@ const {
showView,
showFlash,
balanceLinesForAddress,
+ isoDate,
+ timeAgo,
addressDotHtml,
escapeHtml,
truncateMiddle,
@@ -87,41 +89,6 @@ function renderActiveAddress() {
}
}
-function timeAgo(timestamp) {
- const seconds = Math.floor(Date.now() / 1000 - timestamp);
- if (seconds < 60) return seconds + " seconds ago";
- const minutes = Math.floor(seconds / 60);
- if (minutes < 60)
- return minutes + " minute" + (minutes !== 1 ? "s" : "") + " ago";
- const hours = Math.floor(minutes / 60);
- if (hours < 24) return hours + " hour" + (hours !== 1 ? "s" : "") + " ago";
- const days = Math.floor(hours / 24);
- if (days < 30) return days + " day" + (days !== 1 ? "s" : "") + " ago";
- const months = Math.floor(days / 30);
- if (months < 12)
- return months + " month" + (months !== 1 ? "s" : "") + " ago";
- const years = Math.floor(days / 365);
- return years + " year" + (years !== 1 ? "s" : "") + " ago";
-}
-
-function isoDate(timestamp) {
- const d = new Date(timestamp * 1000);
- const pad = (n) => String(n).padStart(2, "0");
- return (
- d.getFullYear() +
- "-" +
- pad(d.getMonth() + 1) +
- "-" +
- pad(d.getDate()) +
- " " +
- pad(d.getHours()) +
- ":" +
- pad(d.getMinutes()) +
- ":" +
- pad(d.getSeconds())
- );
-}
-
let homeTxs = [];
function renderHomeTxList(ctx) {
diff --git a/src/popup/views/transactionDetail.js b/src/popup/views/transactionDetail.js
index 0ec1dc3..8ecbfe3 100644
--- a/src/popup/views/transactionDetail.js
+++ b/src/popup/views/transactionDetail.js
@@ -8,6 +8,8 @@ const {
addressDotHtml,
addressTitle,
escapeHtml,
+ isoDate,
+ timeAgo,
} = require("./helpers");
const { state } = require("../../shared/state");
const makeBlockie = require("ethereum-blockies-base64");
@@ -21,41 +23,6 @@ const EXT_ICON =
let ctx;
-function isoDate(timestamp) {
- const d = new Date(timestamp * 1000);
- const pad = (n) => String(n).padStart(2, "0");
- return (
- d.getFullYear() +
- "-" +
- pad(d.getMonth() + 1) +
- "-" +
- pad(d.getDate()) +
- " " +
- pad(d.getHours()) +
- ":" +
- pad(d.getMinutes()) +
- ":" +
- pad(d.getSeconds())
- );
-}
-
-function timeAgo(timestamp) {
- const seconds = Math.floor(Date.now() / 1000 - timestamp);
- if (seconds < 60) return seconds + " seconds ago";
- const minutes = Math.floor(seconds / 60);
- if (minutes < 60)
- return minutes + " minute" + (minutes !== 1 ? "s" : "") + " ago";
- const hours = Math.floor(minutes / 60);
- if (hours < 24) return hours + " hour" + (hours !== 1 ? "s" : "") + " ago";
- const days = Math.floor(hours / 24);
- if (days < 30) return days + " day" + (days !== 1 ? "s" : "") + " ago";
- const months = Math.floor(days / 30);
- if (months < 12)
- return months + " month" + (months !== 1 ? "s" : "") + " ago";
- const years = Math.floor(days / 365);
- return years + " year" + (years !== 1 ? "s" : "") + " ago";
-}
-
function copyableHtml(text, extraClass) {
const cls =
"underline decoration-dashed cursor-pointer" +