Skip to content

Commit 5cb6f0b

Browse files
committed
feat: address feedback and swap tab order
1 parent 1e0e997 commit 5cb6f0b

3 files changed

Lines changed: 54 additions & 23 deletions

File tree

public/staking-rewards.html

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,10 @@
7979
return { path, label: dateMatch ? dateMatch[1] : "2021-01 & 02" };
8080
}
8181

82-
function parseSnapshots(byChainId) {
82+
function parseSnapshots(snapshotsByChainId) {
8383
return {
84-
mainnet: (byChainId?.["1"] ?? []).map(toSnapshot),
85-
gnosis: (byChainId?.["100"] ?? []).map(toSnapshot),
84+
mainnet: (snapshotsByChainId?.["1"] ?? []).map(toSnapshot),
85+
gnosis: (snapshotsByChainId?.["100"] ?? []).map(toSnapshot),
8686
};
8787
}
8888

@@ -448,8 +448,8 @@
448448
const [errors, setErrors] = useState([]);
449449
const [monthData, setMonthData] = useState(null);
450450
const [grandTotals, setGrandTotals] = useState(null);
451-
const [activeTab, setActiveTab] = useState("Summary");
452-
const [tabList, setTabList] = useState(["Summary"]);
451+
const [activeTab, setActiveTab] = useState("Monthly Totals");
452+
const [tabList, setTabList] = useState(["Monthly Totals"]);
453453
const [sourceError, setSourceError] = useState(null);
454454

455455
// Auto-start everything on mount
@@ -527,8 +527,8 @@
527527

528528
setMonthData(md);
529529
setGrandTotals(gt);
530-
setTabList(["Summary", "Monthly Totals", ...sorted]);
531-
setActiveTab("Summary");
530+
setTabList(["Monthly Totals", "Summary", ...sorted]);
531+
setActiveTab("Monthly Totals");
532532
setErrors(errs);
533533
setPhase("done");
534534
}
@@ -568,6 +568,9 @@
568568
if (!grandTotals || !monthData) return;
569569
const wb = XLSX.utils.book_new();
570570

571+
const monthlyTotalRows = sortedMonths.map((label) => monthTotalRow(label, monthData[label]));
572+
addSheet(wb, "Monthly Totals", monthlyTotalRows, [14, 18, 18, 18]);
573+
571574
const summaryRows = Object.entries(grandTotals)
572575
.map(([addr, t]) => ({
573576
Address: addr,
@@ -578,9 +581,6 @@
578581
.sort((a, b) => b["Grand Total PNK"] - a["Grand Total PNK"]);
579582
addSheet(wb, "Summary", summaryRows, [44, 20, 20, 20]);
580583

581-
const monthlyTotalRows = sortedMonths.map((label) => monthTotalRow(label, monthData[label]));
582-
addSheet(wb, "Monthly Totals", monthlyTotalRows, [14, 18, 18, 18]);
583-
584584
for (const label of sortedMonths) {
585585
const rows = monthWalletRows(monthData[label]);
586586
if (rows.length === 0) continue;

scripts/emit-snapshots.js

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,14 @@ const source = path.join(root, "src", "assets", "snapshots.json");
2323
const claimModal = path.join(root, "src", "components", "claim-modal.js");
2424
const destination = path.join(root, "public", "snapshots.json");
2525

26-
const CHAIN_IDS = ["1", "100"];
27-
const FILENAME_PREFIX = { 1: "snapshot-", 100: "xdai-snapshot-" };
26+
// Every chain the reward snapshots cover, declared once. To support a new chain, add one entry
27+
// here (with its filename prefix); CHAIN_IDS and the prefix lookup derive from it, so the set of
28+
// chains and their prefixes can never drift apart.
29+
const CHAINS = {
30+
1: { filenamePrefix: "snapshot-" },
31+
100: { filenamePrefix: "xdai-snapshot-" },
32+
};
33+
const CHAIN_IDS = Object.keys(CHAINS);
2834
const ENTRY = /^[A-Za-z0-9]+\/[A-Za-z0-9._-]+\.json$/;
2935

3036
// rewards.js derives the IPFS url, the month and the chain from these strings alone, so a
@@ -35,9 +41,10 @@ function assertEntriesAreWellFormed(entries, chainId) {
3541
throw new Error(`snapshots.json: chain ${chainId} entry "${entry}" is not of the form "<cid>/<filename>.json"`);
3642
}
3743
const filename = entry.split("/")[1];
38-
if (!filename.startsWith(FILENAME_PREFIX[chainId])) {
44+
const { filenamePrefix } = CHAINS[chainId];
45+
if (!filename.startsWith(filenamePrefix)) {
3946
throw new Error(
40-
`snapshots.json: chain ${chainId} entry "${entry}" must start with "${FILENAME_PREFIX[chainId]}" ` +
47+
`snapshots.json: chain ${chainId} entry "${entry}" must start with "${filenamePrefix}" ` +
4148
`— rewards.js tells the chains apart by that prefix.`
4249
);
4350
}
@@ -49,14 +56,27 @@ function assertEntriesAreWellFormed(entries, chainId) {
4956
function assertNewestMonthIsOnEveryChain(snapshots) {
5057
const newestMonth = (chainId) => {
5158
const entries = snapshots[chainId];
52-
return (entries[entries.length - 1].match(/(\d{4}-\d{2})/) || [])[1];
59+
const newest = entries[entries.length - 1];
60+
const month = (newest.match(/(\d{4}-\d{2})/) || [])[1];
61+
if (!month) {
62+
throw new Error(
63+
`snapshots.json: chain ${chainId}'s newest entry "${newest}" has no YYYY-MM, ` +
64+
`so it can't be checked against the other chains.`
65+
);
66+
}
67+
return month;
5368
};
54-
const [mainnet, gnosis] = CHAIN_IDS.map(newestMonth);
55-
if (mainnet !== gnosis) {
56-
throw new Error(
57-
`snapshots.json: newest month differs per chain (mainnet ${mainnet}, gnosis ${gnosis}). Add both, or the ` +
58-
`monthly reward total will be missing a chain.`
59-
);
69+
const [referenceChain, ...otherChains] = CHAIN_IDS;
70+
const referenceMonth = newestMonth(referenceChain);
71+
for (const chainId of otherChains) {
72+
const month = newestMonth(chainId);
73+
if (month !== referenceMonth) {
74+
throw new Error(
75+
`snapshots.json: newest month differs per chain ` +
76+
`(chain ${referenceChain} ${referenceMonth}, chain ${chainId} ${month}). ` +
77+
`Add the missing month, or the monthly reward total will be missing a chain.`
78+
);
79+
}
6080
}
6181
}
6282

@@ -84,6 +104,17 @@ function assertParity(snapshots) {
84104
}
85105

86106
const snapshots = JSON.parse(fs.readFileSync(source, "utf8"));
107+
108+
// Everything below is verified per chain in CHAIN_IDS, but the whole object is written out. Reject
109+
// any other chain so nothing reaches public/snapshots.json without being validated first.
110+
const unverifiedChains = Object.keys(snapshots).filter((chainId) => !CHAIN_IDS.includes(chainId));
111+
if (unverifiedChains.length > 0) {
112+
throw new Error(
113+
`snapshots.json: chain(s) ${unverifiedChains.join(", ")} are not verified. Add them to CHAINS ` +
114+
`(and claim-modal.js) so they are validated before being published.`
115+
);
116+
}
117+
87118
for (const chainId of CHAIN_IDS) {
88119
if (!Array.isArray(snapshots[chainId]) || snapshots[chainId].length === 0) {
89120
throw new Error(`snapshots.json is missing snapshots for chain ${chainId}`);

src/helpers/rewards.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import PNKAbi from "../assets/contracts/pinakion.json";
33
import Web3 from "web3";
44
import { klerosboardSubgraph } from "../bootstrap/subgraph";
55
import { getReadOnlyRpcUrl } from "../bootstrap/web3";
6+
import { IPFS_GATEWAY } from "../utils/ipfs";
67
import snapshotsByChainId from "../assets/snapshots.json";
78

8-
const ipfsEndpoint = "https://cdn.kleros.link";
99
const allSnapshotPaths = [...snapshotsByChainId["1"], ...snapshotsByChainId["100"]];
1010

1111
function getTarget() {
@@ -47,7 +47,7 @@ function snapshotUrlsForMonth(year, month) {
4747
return allSnapshotPaths
4848
.filter((path) => filenames.some((filename) => path.endsWith(`/${filename}`)))
4949
.map((path) => ({
50-
url: `${ipfsEndpoint}/ipfs/${path}`,
50+
url: `${IPFS_GATEWAY}/ipfs/${path}`,
5151
isGnosis: path.includes("/xdai-snapshot-"),
5252
}));
5353
}

0 commit comments

Comments
 (0)