-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlaunches.js
More file actions
132 lines (120 loc) · 5.15 KB
/
Copy pathlaunches.js
File metadata and controls
132 lines (120 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/**
* Launch shape + derived values and formatters. All launch data is read
* from the chain (src/chain/launches.js → CovenantRegistry) and mapped
* into the Launch typedef below — this module holds no data of its own.
*/
import { BLOCK_TIME_SECONDS } from '../config/ritual.js';
/**
* @typedef {'active' | 'enforcing' | 'reviving'} GuardianStatus
*
* @typedef {Object} VestingTranche
* @property {string} label Human label, e.g. "Team tranche 2 of 8"
* @property {number} pct Percent of total supply in this tranche
* @property {number} atBlock Block at which the guardian releases it
* @property {boolean} released Whether the guardian has executed the release
*
* @typedef {Object} EnforcementEvent
* @property {number} block
* @property {'wake'|'release'|'check_ok'|'flag'|'freeze'|'checkpoint'|'revival'} type
* @property {string} detail One-line human-readable description
* @property {string} attestation TEE attestation hash for the action
*
* @typedef {Object} Guardian
* @property {string} address Agent's own address (keys held via DKMS)
* @property {string} model Attested guardian build identifier
* @property {number} deployedBlock
* @property {number} lastHeartbeatBlock
* @property {number} revivals Times consensus revived it from checkpoint
* @property {GuardianStatus} status
*
* @typedef {Object} CovenantTerms
* @property {number} lpLockUntilBlock
* @property {number} lpPctLocked Percent of LP under guardian custody
* @property {number} devWalletCapPct Max % of dev holdings sellable per 30 days
* @property {number} monitorEveryBlocks Guardian audit cadence
* @property {VestingTranche[]} vesting
*
* @typedef {Object} Launch
* @property {string} id Token address (lowercase)
* @property {string} name
* @property {string} symbol
* @property {string} tagline
* @property {string} description
* @property {string} creator Creator address
* @property {number} createdAtBlock
* @property {Guardian} guardian
* @property {CovenantTerms} terms
* @property {EnforcementEvent[]} log Newest first
*/
// ---------------------------------------------------------------------------
// Derived values & formatters (UI depends only on these, not raw fields)
// ---------------------------------------------------------------------------
/** Percent of the vesting allocation already released by the guardian. */
export function vestedPct(launch) {
const total = launch.terms.vesting.reduce((s, t) => s + t.pct, 0);
if (!total) return 0;
const done = launch.terms.vesting.filter((t) => t.released).reduce((s, t) => s + t.pct, 0);
return Math.round((done / total) * 100);
}
/**
* Guardian trust score, derived purely from enforcement history:
* successful audits and on-schedule releases raise it; violations found
* (which prove enforcement works, but indicate a misbehaving team) lower it.
* Range 0–100.
*/
export function trustScore(launch) {
let score = 62;
for (const e of launch.log) {
if (e.type === 'check_ok' || e.type === 'checkpoint') score += 2;
if (e.type === 'wake') score += 1;
if (e.type === 'release') score += 5;
if (e.type === 'flag') score -= 6;
if (e.type === 'freeze') score -= 12;
}
if (launch.guardian.revivals > 0) score += 2; // survived a crash: resilience proven
return Math.max(5, Math.min(99, score));
}
export function blocksToApproxTime(blocks) {
const secs = Math.max(0, blocks) * BLOCK_TIME_SECONDS;
if (secs < 60) return `~${Math.round(secs)}s`;
if (secs < 3600) return `~${Math.round(secs / 60)}m`;
if (secs < 86400) return `~${Math.round(secs / 3600)}h`;
return `~${Math.round(secs / 86400)}d`;
}
export function fmtBlock(n) {
return n.toLocaleString('en-US');
}
export function shortAddr(a) {
return `${a.slice(0, 6)}…${a.slice(-4)}`;
}
export function shortHash(h) {
return `${h.slice(0, 10)}…${h.slice(-6)}`;
}
/** Token amounts: compact past 1M, whole-ish above 1, precise below. */
export function fmtAmount(v) {
if (v >= 1_000_000) return v.toLocaleString('en-US', { notation: 'compact', maximumFractionDigits: 2 });
if (v >= 1) return v.toLocaleString('en-US', { maximumFractionDigits: 2 });
return v.toLocaleString('en-US', { maximumFractionDigits: 6 });
}
const SUBSCRIPT_DIGITS = '₀₁₂₃₄₅₆₇₈₉';
/**
* Native-coin price formatting across the pool's tiny magnitudes.
* Sub-0.01 values expand to 4 significant digits instead of exponent
* notation; runs of 4+ leading zeros compress DEX-style: 0.0₇5645
* means 7 zeros after the point, then the digits.
*/
export function fmtNative(v) {
if (!(v > 0)) return '0';
if (v >= 1000) return v.toLocaleString('en-US', { maximumFractionDigits: 2 });
if (v >= 0.01) return v.toFixed(4);
let zeros = Math.max(0, -Math.floor(Math.log10(v)) - 1);
let digits = Math.round(v * 10 ** (zeros + 4));
if (digits >= 10_000) {
// Rounding carried into the next magnitude (0.0099999 → 0.01000).
digits = Math.round(digits / 10);
zeros -= 1;
}
if (zeros < 4) return v.toFixed(zeros + 4);
const sub = String(zeros).replace(/\d/g, (d) => SUBSCRIPT_DIGITS[+d]);
return `0.0${sub}${String(digits).padStart(4, '0')}`;
}