mpp32-mcp-server 1.4.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +29 -51
- package/dist/pivx-provider.d.ts +42 -0
- package/dist/pivx-provider.js +232 -0
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { signX402Payment } from "./x402-signers.js";
|
|
6
|
-
const SERVER_VERSION = "1.4.
|
|
6
|
+
const SERVER_VERSION = "1.4.1";
|
|
7
7
|
// ── Env loading: trim and sanitize aggressively ─────────────────────────────
|
|
8
8
|
// Copy-paste from Claude Desktop / Cursor / Windsurf JSON config UIs frequently
|
|
9
9
|
// adds trailing \n, \r, NBSP, BOM, or wraps the value in literal quotes. Any
|
|
@@ -575,7 +575,10 @@ server.tool("scan_portfolio_m32", "M32-gated full wallet portfolio scan. Discove
|
|
|
575
575
|
}
|
|
576
576
|
});
|
|
577
577
|
// ── Tool 7: get_pivx_dao_intelligence ─────────────────────────────────────
|
|
578
|
-
|
|
578
|
+
// Self-contained: scrapes pivx.org/proposals + Chainz CryptoID directly.
|
|
579
|
+
// Does NOT depend on any backend endpoint — works for every npm user out of the box.
|
|
580
|
+
import { fetchPivxGovernance } from "./pivx-provider.js";
|
|
581
|
+
server.tool("get_pivx_dao_intelligence", "Get real-time PIVX DAO governance intelligence. Returns active budget proposals with masternode voting tallies (Yes/No counts, net yes percentages), budget allocation status, network deflation metrics (unallocated treasury PIV that are never minted), and masternode network health. PIVX is a fully community-governed cryptocurrency where Masternode owners vote on budget proposals every ~30 days (43,200 blocks per superblock cycle, 432,000 PIV max monthly budget). Data scraped live from pivx.org/proposals and the PIVX blockchain via Chainz CryptoID. Cached for 5 minutes. Free — no payment or API key required.", {
|
|
579
582
|
filter: z
|
|
580
583
|
.enum(["all", "passing", "failing"])
|
|
581
584
|
.default("all")
|
|
@@ -588,52 +591,31 @@ server.tool("get_pivx_dao_intelligence", "Get real-time PIVX DAO governance inte
|
|
|
588
591
|
.describe("Include network stats and deflation metrics (default: true)."),
|
|
589
592
|
}, async ({ filter, includeStats }) => {
|
|
590
593
|
try {
|
|
591
|
-
const
|
|
592
|
-
const endpoint = includeStats !== false ? "/api/governance" : `/api/governance/proposals${statusFilter}`;
|
|
593
|
-
const res = await fetchWithTimeout(`${API_URL}${endpoint}`, {
|
|
594
|
-
headers: buildHeaders(),
|
|
595
|
-
});
|
|
596
|
-
if (!res.ok) {
|
|
597
|
-
const err = await res.json().catch(() => null);
|
|
598
|
-
return {
|
|
599
|
-
content: [{
|
|
600
|
-
type: "text",
|
|
601
|
-
text: `Error fetching PIVX governance data: ${err?.error?.message ?? `HTTP ${res.status}`}`,
|
|
602
|
-
}],
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
const json = await res.json();
|
|
606
|
-
const data = json.data;
|
|
607
|
-
// Build readable output
|
|
594
|
+
const gov = await fetchPivxGovernance();
|
|
608
595
|
const lines = [];
|
|
609
596
|
lines.push("# PIVX DAO Governance Intelligence");
|
|
610
597
|
lines.push("");
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
if (network) {
|
|
598
|
+
if (includeStats !== false) {
|
|
599
|
+
const n = gov.network;
|
|
614
600
|
lines.push("## Network Overview");
|
|
615
|
-
lines.push(`- **Masternodes Online:** ${
|
|
616
|
-
lines.push(`- **Passing Threshold:** ${
|
|
617
|
-
lines.push(`- **Monthly Budget:** ${
|
|
618
|
-
lines.push(`- **Budget Allocated:** ${
|
|
619
|
-
if (
|
|
620
|
-
lines.push(`- **Block Height:** ${
|
|
621
|
-
if (
|
|
622
|
-
lines.push(`- **Total Supply:** ${Math.round(
|
|
601
|
+
lines.push(`- **Masternodes Online:** ${n.masternodeCount.toLocaleString()}`);
|
|
602
|
+
lines.push(`- **Passing Threshold:** ${n.passingThreshold} votes (10% of masternodes)`);
|
|
603
|
+
lines.push(`- **Monthly Budget:** ${n.monthlyBudgetPiv.toLocaleString()} PIV (~$${n.monthlyBudgetUsd.toLocaleString()})`);
|
|
604
|
+
lines.push(`- **Budget Allocated:** ${n.budgetAllocatedPiv.toLocaleString()} PIV (${n.budgetAllocatedPercent}%)`);
|
|
605
|
+
if (n.blockHeight)
|
|
606
|
+
lines.push(`- **Block Height:** ${n.blockHeight.toLocaleString()}`);
|
|
607
|
+
if (n.totalSupply)
|
|
608
|
+
lines.push(`- **Total Supply:** ${Math.round(n.totalSupply).toLocaleString()} PIV`);
|
|
623
609
|
lines.push("");
|
|
624
|
-
|
|
625
|
-
// Deflation stats
|
|
626
|
-
const deflation = data.deflation;
|
|
627
|
-
if (deflation) {
|
|
610
|
+
const d = gov.deflation;
|
|
628
611
|
lines.push("## Deflation / Fee Burn Metrics");
|
|
629
|
-
lines.push(`- **Unallocated PIV This Cycle:** ${
|
|
630
|
-
lines.push(`- **Annual Unallocated (est.):** ${
|
|
631
|
-
lines.push(`- **Effective Inflation Reduction:** ${
|
|
632
|
-
lines.push(`- **Proposal Submission Fee:** ${
|
|
612
|
+
lines.push(`- **Unallocated PIV This Cycle:** ${d.unallocatedPivPerCycle.toLocaleString()} PIV (never minted)`);
|
|
613
|
+
lines.push(`- **Annual Unallocated (est.):** ${d.annualUnallocatedPiv.toLocaleString()} PIV`);
|
|
614
|
+
lines.push(`- **Effective Inflation Reduction:** ${d.effectiveInflationReduction}`);
|
|
615
|
+
lines.push(`- **Proposal Submission Fee:** ${d.proposalFeeBurnPiv} PIV (burned/destroyed)`);
|
|
633
616
|
lines.push("");
|
|
634
617
|
}
|
|
635
|
-
|
|
636
|
-
let proposals = (data.proposals ?? []);
|
|
618
|
+
let proposals = gov.proposals;
|
|
637
619
|
if (filter === "passing")
|
|
638
620
|
proposals = proposals.filter((p) => p.status === "passing");
|
|
639
621
|
else if (filter === "failing")
|
|
@@ -646,11 +628,11 @@ server.tool("get_pivx_dao_intelligence", "Get real-time PIVX DAO governance inte
|
|
|
646
628
|
const fundedTag = p.funded ? " (Funded)" : "";
|
|
647
629
|
lines.push(`### ${p.name} — ${status}${fundedTag}`);
|
|
648
630
|
lines.push(`- **Votes:** ${p.yesVotes} Yes / ${p.noVotes} No (${p.netYesPercent}% net yes)`);
|
|
649
|
-
lines.push(`- **Monthly Payment:** ${
|
|
650
|
-
if (
|
|
651
|
-
lines.push(`- **Total Budget:** ${
|
|
631
|
+
lines.push(`- **Monthly Payment:** ${p.monthlyPaymentPiv.toLocaleString()} PIV (~$${p.monthlyPaymentUsd.toLocaleString()})`);
|
|
632
|
+
if (p.totalPaymentPiv > p.monthlyPaymentPiv) {
|
|
633
|
+
lines.push(`- **Total Budget:** ${p.totalPaymentPiv.toLocaleString()} PIV`);
|
|
652
634
|
}
|
|
653
|
-
if (
|
|
635
|
+
if (p.installmentsRemaining > 0) {
|
|
654
636
|
lines.push(`- **Installments Remaining:** ${p.installmentsRemaining}`);
|
|
655
637
|
}
|
|
656
638
|
if (p.budgetPercent)
|
|
@@ -663,12 +645,8 @@ server.tool("get_pivx_dao_intelligence", "Get real-time PIVX DAO governance inte
|
|
|
663
645
|
else {
|
|
664
646
|
lines.push("No proposals found matching the filter.");
|
|
665
647
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
if (meta) {
|
|
669
|
-
lines.push("---");
|
|
670
|
-
lines.push(`Source: ${meta.source} | ${meta.timestamp}${meta.cacheHit ? " (cached)" : ""}`);
|
|
671
|
-
}
|
|
648
|
+
lines.push("---");
|
|
649
|
+
lines.push(`Source: ${gov.source} | ${gov.timestamp}${gov.cacheHit ? " (cached)" : ""}`);
|
|
672
650
|
return {
|
|
673
651
|
content: [{ type: "text", text: lines.join("\n") }],
|
|
674
652
|
};
|
|
@@ -677,7 +655,7 @@ server.tool("get_pivx_dao_intelligence", "Get real-time PIVX DAO governance inte
|
|
|
677
655
|
return {
|
|
678
656
|
content: [{
|
|
679
657
|
type: "text",
|
|
680
|
-
text: `Failed to fetch PIVX governance data: ${err instanceof Error ? err.message : String(err)}
|
|
658
|
+
text: `Failed to fetch PIVX governance data: ${err instanceof Error ? err.message : String(err)}. The tool scrapes pivx.org/proposals directly — the site may be temporarily unreachable.`,
|
|
681
659
|
}],
|
|
682
660
|
};
|
|
683
661
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface PivxProposal {
|
|
2
|
+
name: string;
|
|
3
|
+
url: string;
|
|
4
|
+
status: "passing" | "failing";
|
|
5
|
+
funded: boolean;
|
|
6
|
+
netYesPercent: number;
|
|
7
|
+
yesVotes: number;
|
|
8
|
+
noVotes: number;
|
|
9
|
+
monthlyPaymentPiv: number;
|
|
10
|
+
monthlyPaymentUsd: number;
|
|
11
|
+
totalPaymentPiv: number;
|
|
12
|
+
installmentsRemaining: number;
|
|
13
|
+
totalInstallments: number;
|
|
14
|
+
budgetPercent: number;
|
|
15
|
+
}
|
|
16
|
+
export interface PivxNetworkStats {
|
|
17
|
+
masternodeCount: number;
|
|
18
|
+
passingThreshold: number;
|
|
19
|
+
monthlyBudgetPiv: number;
|
|
20
|
+
monthlyBudgetUsd: number;
|
|
21
|
+
budgetAllocatedPiv: number;
|
|
22
|
+
budgetAllocatedUsd: number;
|
|
23
|
+
budgetAllocatedPercent: number;
|
|
24
|
+
blockHeight: number;
|
|
25
|
+
totalSupply: number;
|
|
26
|
+
circulatingSupply: number;
|
|
27
|
+
}
|
|
28
|
+
export interface PivxGovernanceData {
|
|
29
|
+
proposals: PivxProposal[];
|
|
30
|
+
network: PivxNetworkStats;
|
|
31
|
+
deflation: {
|
|
32
|
+
unallocatedPivPerCycle: number;
|
|
33
|
+
unallocatedPercent: number;
|
|
34
|
+
annualUnallocatedPiv: number;
|
|
35
|
+
proposalFeeBurnPiv: number;
|
|
36
|
+
effectiveInflationReduction: string;
|
|
37
|
+
};
|
|
38
|
+
timestamp: string;
|
|
39
|
+
source: string;
|
|
40
|
+
cacheHit: boolean;
|
|
41
|
+
}
|
|
42
|
+
export declare function fetchPivxGovernance(): Promise<PivxGovernanceData>;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import * as cheerio from "cheerio";
|
|
2
|
+
// 5-minute cache
|
|
3
|
+
const CACHE_TTL_MS = 5 * 60 * 1000;
|
|
4
|
+
let cachedData = null;
|
|
5
|
+
let cacheTimestamp = 0;
|
|
6
|
+
const CHAINZ_BASE = "https://chainz.cryptoid.info/pivx/api.dws";
|
|
7
|
+
async function chainzFetch(query) {
|
|
8
|
+
const res = await fetch(`${CHAINZ_BASE}?q=${query}`, {
|
|
9
|
+
signal: AbortSignal.timeout(8000),
|
|
10
|
+
});
|
|
11
|
+
if (!res.ok)
|
|
12
|
+
throw new Error(`Chainz API ${query}: HTTP ${res.status}`);
|
|
13
|
+
return (await res.text()).trim();
|
|
14
|
+
}
|
|
15
|
+
async function fetchNetworkStats() {
|
|
16
|
+
const [masternodeCount, blockHeight, totalSupply, circulating] = await Promise.allSettled([
|
|
17
|
+
chainzFetch("masternodecount"),
|
|
18
|
+
chainzFetch("getblockcount"),
|
|
19
|
+
chainzFetch("totalcoins"),
|
|
20
|
+
chainzFetch("circulating"),
|
|
21
|
+
]);
|
|
22
|
+
const mn = masternodeCount.status === "fulfilled"
|
|
23
|
+
? parseInt(masternodeCount.value, 10)
|
|
24
|
+
: 0;
|
|
25
|
+
const bh = blockHeight.status === "fulfilled"
|
|
26
|
+
? parseInt(blockHeight.value, 10)
|
|
27
|
+
: 0;
|
|
28
|
+
const ts = totalSupply.status === "fulfilled"
|
|
29
|
+
? parseFloat(totalSupply.value)
|
|
30
|
+
: 0;
|
|
31
|
+
const cs = circulating.status === "fulfilled"
|
|
32
|
+
? parseFloat(circulating.value)
|
|
33
|
+
: 0;
|
|
34
|
+
return {
|
|
35
|
+
masternodeCount: isNaN(mn) ? 0 : mn,
|
|
36
|
+
blockHeight: isNaN(bh) ? 0 : bh,
|
|
37
|
+
totalSupply: isNaN(ts) ? 0 : ts,
|
|
38
|
+
circulatingSupply: isNaN(cs) ? 0 : cs,
|
|
39
|
+
passingThreshold: isNaN(mn) ? 0 : Math.ceil(mn * 0.1),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function parseNumber(text) {
|
|
43
|
+
const cleaned = text.replace(/[^0-9.\-]/g, "");
|
|
44
|
+
const num = parseFloat(cleaned);
|
|
45
|
+
return isNaN(num) ? 0 : num;
|
|
46
|
+
}
|
|
47
|
+
async function scrapePivxProposals() {
|
|
48
|
+
const res = await fetch("https://pivx.org/proposals", {
|
|
49
|
+
signal: AbortSignal.timeout(15000),
|
|
50
|
+
headers: {
|
|
51
|
+
"User-Agent": "MPP32-Governance-Oracle/1.0 (+https://mpp32.org)",
|
|
52
|
+
Accept: "text/html",
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
if (!res.ok)
|
|
56
|
+
throw new Error(`pivx.org/proposals returned HTTP ${res.status}`);
|
|
57
|
+
const html = await res.text();
|
|
58
|
+
const $ = cheerio.load(html);
|
|
59
|
+
const pageText = $("body").text();
|
|
60
|
+
let monthlyBudgetPiv = 432000;
|
|
61
|
+
let monthlyBudgetUsd = 0;
|
|
62
|
+
let budgetAllocatedPiv = 0;
|
|
63
|
+
let budgetAllocatedUsd = 0;
|
|
64
|
+
let masternodeCount = 0;
|
|
65
|
+
let passingThreshold = 0;
|
|
66
|
+
const budgetMatch = pageText.match(/Monthly\s*Budget[:\s]*([\d,]+)\s*PIV/i);
|
|
67
|
+
if (budgetMatch?.[1])
|
|
68
|
+
monthlyBudgetPiv = parseNumber(budgetMatch[1]);
|
|
69
|
+
const budgetUsdMatch = pageText.match(/Monthly\s*Budget[^$]*US?\$([\d,.]+)/i);
|
|
70
|
+
if (budgetUsdMatch?.[1])
|
|
71
|
+
monthlyBudgetUsd = parseNumber(budgetUsdMatch[1]);
|
|
72
|
+
const allocatedMatch = pageText.match(/Budget\s*Allocated[:\s]*([\d,]+)\s*PIV/i);
|
|
73
|
+
if (allocatedMatch?.[1])
|
|
74
|
+
budgetAllocatedPiv = parseNumber(allocatedMatch[1]);
|
|
75
|
+
const allocatedUsdMatch = pageText.match(/Budget\s*Allocated[^$]*US?\$([\d,.]+)/i);
|
|
76
|
+
if (allocatedUsdMatch?.[1])
|
|
77
|
+
budgetAllocatedUsd = parseNumber(allocatedUsdMatch[1]);
|
|
78
|
+
const mnMatch = pageText.match(/([\d,]+)\s*masternodes?\s*online/i);
|
|
79
|
+
if (mnMatch?.[1])
|
|
80
|
+
masternodeCount = parseNumber(mnMatch[1]);
|
|
81
|
+
const thresholdMatch = pageText.match(/Positive\s*votes\s*required[^:]*:\s*(\d+)/i);
|
|
82
|
+
if (thresholdMatch?.[1])
|
|
83
|
+
passingThreshold = parseInt(thresholdMatch[1], 10);
|
|
84
|
+
const proposals = [];
|
|
85
|
+
const seenHashes = new Set();
|
|
86
|
+
$("table#js_table tbody tr[data-hash]").each((_i, el) => {
|
|
87
|
+
const $row = $(el);
|
|
88
|
+
const hash = $row.attr("data-hash") || "";
|
|
89
|
+
if (!hash || seenHashes.has(hash))
|
|
90
|
+
return;
|
|
91
|
+
seenHashes.add(hash);
|
|
92
|
+
const name = ($row.attr("data-title") || "").trim();
|
|
93
|
+
if (!name)
|
|
94
|
+
return;
|
|
95
|
+
const cells = $row.find("td");
|
|
96
|
+
if (cells.length < 5)
|
|
97
|
+
return;
|
|
98
|
+
const statusCell = $(cells[0]);
|
|
99
|
+
const statusText = statusCell.text();
|
|
100
|
+
const isPassing = /passing/i.test(statusText);
|
|
101
|
+
const funded = /funded/i.test(statusText);
|
|
102
|
+
let netYesPercent = 0;
|
|
103
|
+
const netYesMatch = statusText.match(/([-\d.]+)%/);
|
|
104
|
+
if (netYesMatch?.[1])
|
|
105
|
+
netYesPercent = parseFloat(netYesMatch[1]);
|
|
106
|
+
const nameCell = $(cells[1]);
|
|
107
|
+
const link = nameCell.find("a").first();
|
|
108
|
+
const url = link.attr("href") || "";
|
|
109
|
+
const paymentCell = $(cells[2]);
|
|
110
|
+
const monthlyPaymentPiv = parseNumber(paymentCell.attr("data-piv") || paymentCell.attr("data-order") || "0");
|
|
111
|
+
const budgetPercent = parseFloat(paymentCell.attr("data-percent") || "0");
|
|
112
|
+
let monthlyPaymentUsd = 0;
|
|
113
|
+
const usdSpan = paymentCell.find(".curr-prefix").parent();
|
|
114
|
+
if (usdSpan.length) {
|
|
115
|
+
const usdText = usdSpan.text();
|
|
116
|
+
const usdMatch = usdText.match(/US?\$\s*([\d,]+(?:\.\d+)?)/i);
|
|
117
|
+
if (usdMatch?.[1])
|
|
118
|
+
monthlyPaymentUsd = parseNumber(usdMatch[1]);
|
|
119
|
+
}
|
|
120
|
+
let installmentsRemaining = 1;
|
|
121
|
+
const longLine = paymentCell.find(".long-line");
|
|
122
|
+
if (longLine.length) {
|
|
123
|
+
const installB = longLine.find("b").first();
|
|
124
|
+
if (installB.length) {
|
|
125
|
+
const n = parseInt(installB.text().trim(), 10);
|
|
126
|
+
if (!isNaN(n))
|
|
127
|
+
installmentsRemaining = n;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
let totalPaymentPiv = monthlyPaymentPiv;
|
|
131
|
+
if (longLine.length) {
|
|
132
|
+
const totalB = longLine.find("b").last();
|
|
133
|
+
if (totalB.length) {
|
|
134
|
+
const totalText = totalB.text();
|
|
135
|
+
const totalMatch = totalText.match(/([\d,]+(?:\.\d+)?)\s*PIV/i);
|
|
136
|
+
if (totalMatch?.[1])
|
|
137
|
+
totalPaymentPiv = parseNumber(totalMatch[1]);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const voteCell = $(cells[4]);
|
|
141
|
+
const voteText = voteCell.text();
|
|
142
|
+
let yesVotes = 0;
|
|
143
|
+
let noVotes = 0;
|
|
144
|
+
const voteMatch = voteText.match(/(\d+)\s*\/\s*(\d+)/);
|
|
145
|
+
if (voteMatch?.[1] && voteMatch[2]) {
|
|
146
|
+
yesVotes = parseInt(voteMatch[1], 10);
|
|
147
|
+
noVotes = parseInt(voteMatch[2], 10);
|
|
148
|
+
}
|
|
149
|
+
proposals.push({
|
|
150
|
+
name,
|
|
151
|
+
url,
|
|
152
|
+
status: isPassing ? "passing" : "failing",
|
|
153
|
+
funded,
|
|
154
|
+
netYesPercent,
|
|
155
|
+
yesVotes,
|
|
156
|
+
noVotes,
|
|
157
|
+
monthlyPaymentPiv,
|
|
158
|
+
monthlyPaymentUsd,
|
|
159
|
+
totalPaymentPiv,
|
|
160
|
+
installmentsRemaining,
|
|
161
|
+
totalInstallments: installmentsRemaining,
|
|
162
|
+
budgetPercent,
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
return {
|
|
166
|
+
proposals,
|
|
167
|
+
budgetSummary: {
|
|
168
|
+
monthlyBudgetPiv,
|
|
169
|
+
monthlyBudgetUsd,
|
|
170
|
+
budgetAllocatedPiv,
|
|
171
|
+
budgetAllocatedUsd,
|
|
172
|
+
masternodeCount,
|
|
173
|
+
passingThreshold,
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
export async function fetchPivxGovernance() {
|
|
178
|
+
if (cachedData && Date.now() - cacheTimestamp < CACHE_TTL_MS) {
|
|
179
|
+
return { ...cachedData, cacheHit: true };
|
|
180
|
+
}
|
|
181
|
+
const [scrapeResult, networkStats] = await Promise.allSettled([
|
|
182
|
+
scrapePivxProposals(),
|
|
183
|
+
fetchNetworkStats(),
|
|
184
|
+
]);
|
|
185
|
+
const scraped = scrapeResult.status === "fulfilled" ? scrapeResult.value : null;
|
|
186
|
+
const chainzStats = networkStats.status === "fulfilled" ? networkStats.value : {};
|
|
187
|
+
if (!scraped) {
|
|
188
|
+
throw new Error(`Failed to fetch PIVX governance data: ${scrapeResult.status === "rejected" ? scrapeResult.reason : "unknown"}`);
|
|
189
|
+
}
|
|
190
|
+
const budgetAllocatedPercent = scraped.budgetSummary.monthlyBudgetPiv > 0
|
|
191
|
+
? Math.round((scraped.budgetSummary.budgetAllocatedPiv /
|
|
192
|
+
scraped.budgetSummary.monthlyBudgetPiv) *
|
|
193
|
+
10000) / 100
|
|
194
|
+
: 0;
|
|
195
|
+
const network = {
|
|
196
|
+
masternodeCount: scraped.budgetSummary.masternodeCount ||
|
|
197
|
+
chainzStats.masternodeCount ||
|
|
198
|
+
0,
|
|
199
|
+
passingThreshold: scraped.budgetSummary.passingThreshold ||
|
|
200
|
+
chainzStats.passingThreshold ||
|
|
201
|
+
0,
|
|
202
|
+
monthlyBudgetPiv: scraped.budgetSummary.monthlyBudgetPiv,
|
|
203
|
+
monthlyBudgetUsd: scraped.budgetSummary.monthlyBudgetUsd,
|
|
204
|
+
budgetAllocatedPiv: scraped.budgetSummary.budgetAllocatedPiv,
|
|
205
|
+
budgetAllocatedUsd: scraped.budgetSummary.budgetAllocatedUsd,
|
|
206
|
+
budgetAllocatedPercent,
|
|
207
|
+
blockHeight: chainzStats.blockHeight || 0,
|
|
208
|
+
totalSupply: chainzStats.totalSupply || 0,
|
|
209
|
+
circulatingSupply: chainzStats.circulatingSupply || 0,
|
|
210
|
+
};
|
|
211
|
+
const unallocatedPivPerCycle = network.monthlyBudgetPiv - network.budgetAllocatedPiv;
|
|
212
|
+
const annualUnallocatedPiv = unallocatedPivPerCycle * 12;
|
|
213
|
+
const data = {
|
|
214
|
+
proposals: scraped.proposals,
|
|
215
|
+
network,
|
|
216
|
+
deflation: {
|
|
217
|
+
unallocatedPivPerCycle,
|
|
218
|
+
unallocatedPercent: 100 - budgetAllocatedPercent,
|
|
219
|
+
annualUnallocatedPiv,
|
|
220
|
+
proposalFeeBurnPiv: 50,
|
|
221
|
+
effectiveInflationReduction: network.totalSupply > 0
|
|
222
|
+
? `${((annualUnallocatedPiv / network.totalSupply) * 100).toFixed(3)}%`
|
|
223
|
+
: "N/A",
|
|
224
|
+
},
|
|
225
|
+
timestamp: new Date().toISOString(),
|
|
226
|
+
source: "pivx.org/proposals + chainz.cryptoid.info",
|
|
227
|
+
cacheHit: false,
|
|
228
|
+
};
|
|
229
|
+
cachedData = data;
|
|
230
|
+
cacheTimestamp = Date.now();
|
|
231
|
+
return data;
|
|
232
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mpp32-mcp-server",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"mcpName": "io.github.MPP32/mpp32-mcp-server",
|
|
5
5
|
"description": "Payment layer for AI agents. One MCP, five protocols, thousands of paid APIs your agent can call.",
|
|
6
6
|
"type": "module",
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"@solana-program/token": "^0.13.0",
|
|
77
77
|
"@solana/kit": "^6.9.0",
|
|
78
78
|
"bs58": "^6.0.0",
|
|
79
|
+
"cheerio": "^1.2.0",
|
|
79
80
|
"tweetnacl": "^1.0.3",
|
|
80
81
|
"viem": "^2.48.11",
|
|
81
82
|
"zod": "^3.23.0"
|