pnpfucius 2.0.0

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.
@@ -0,0 +1,130 @@
1
+ // News tool definitions and implementations for Claude Predict
2
+
3
+ import { createScorer } from '../../ai/scorer.js';
4
+ import { createMarketGenerator } from '../../ai/market-generator.js';
5
+ import { createNewsMonitor } from '../../monitoring/news-monitor.js';
6
+
7
+ // Tool definitions for Claude API
8
+ export const newsToolDefinitions = [
9
+ {
10
+ name: 'score_news',
11
+ description: 'Score a news headline for privacy relevance (0-100). High scores indicate good market potential.',
12
+ input_schema: {
13
+ type: 'object',
14
+ properties: {
15
+ headline: {
16
+ type: 'string',
17
+ description: 'News headline to score'
18
+ },
19
+ summary: {
20
+ type: 'string',
21
+ description: 'Optional article summary for more accurate scoring'
22
+ }
23
+ },
24
+ required: ['headline']
25
+ }
26
+ },
27
+ {
28
+ name: 'fetch_news',
29
+ description: 'Fetch recent privacy-related news from monitored RSS feeds',
30
+ input_schema: {
31
+ type: 'object',
32
+ properties: {
33
+ limit: {
34
+ type: 'number',
35
+ description: 'Max items to return (default: 10)'
36
+ },
37
+ min_score: {
38
+ type: 'number',
39
+ description: 'Minimum relevance score filter (default: 0)'
40
+ }
41
+ }
42
+ }
43
+ },
44
+ {
45
+ name: 'generate_from_news',
46
+ description: 'Generate a prediction market from a news headline',
47
+ input_schema: {
48
+ type: 'object',
49
+ properties: {
50
+ headline: {
51
+ type: 'string',
52
+ description: 'News headline'
53
+ },
54
+ summary: {
55
+ type: 'string',
56
+ description: 'Article summary'
57
+ },
58
+ source: {
59
+ type: 'string',
60
+ description: 'News source'
61
+ }
62
+ },
63
+ required: ['headline']
64
+ }
65
+ }
66
+ ];
67
+
68
+ // Tool implementations
69
+ export async function executeNewsTool(name, input) {
70
+ switch (name) {
71
+ case 'score_news': {
72
+ const scorer = createScorer();
73
+ const result = await scorer.scoreNews({
74
+ title: input.headline,
75
+ description: input.summary || ''
76
+ });
77
+ return {
78
+ headline: input.headline,
79
+ score: result.score,
80
+ category: result.category,
81
+ urgency: result.urgency,
82
+ market_potential: result.marketPotential,
83
+ reasoning: result.reasoning
84
+ };
85
+ }
86
+
87
+ case 'fetch_news': {
88
+ const monitor = createNewsMonitor();
89
+ const items = await monitor.checkFeeds();
90
+ const limit = input.limit || 10;
91
+ const minScore = input.min_score || 0;
92
+
93
+ // Score items if filtering by score
94
+ let scoredItems = items;
95
+ if (minScore > 0) {
96
+ const scorer = createScorer();
97
+ scoredItems = [];
98
+ for (const item of items.slice(0, 20)) {
99
+ const score = await scorer.scoreNews(item);
100
+ if (score.score >= minScore) {
101
+ scoredItems.push({
102
+ ...item,
103
+ relevance_score: score.score,
104
+ category: score.category
105
+ });
106
+ }
107
+ }
108
+ }
109
+
110
+ return {
111
+ items: scoredItems.slice(0, limit),
112
+ total: scoredItems.length,
113
+ min_score_filter: minScore
114
+ };
115
+ }
116
+
117
+ case 'generate_from_news': {
118
+ const generator = createMarketGenerator();
119
+ const result = await generator.generateFromNews({
120
+ title: input.headline,
121
+ description: input.summary || '',
122
+ source: input.source || 'Unknown'
123
+ });
124
+ return result;
125
+ }
126
+
127
+ default:
128
+ return { error: `Unknown news tool: ${name}` };
129
+ }
130
+ }
@@ -0,0 +1,215 @@
1
+ // Output renderer for Claude Predict
2
+
3
+ import chalk from 'chalk';
4
+
5
+ // Purple color definitions
6
+ const purple = chalk.hex('#A855F7');
7
+ const purpleBright = chalk.hex('#C084FC');
8
+ const purpleDim = chalk.hex('#7C3AED');
9
+ const violet = chalk.hex('#8B5CF6');
10
+
11
+ /**
12
+ * Render markdown-like text for terminal output
13
+ * Simple rendering without external dependencies
14
+ */
15
+ export function renderMarkdown(text) {
16
+ if (!text) return '';
17
+
18
+ let result = text;
19
+
20
+ // Headers
21
+ result = result.replace(/^### (.+)$/gm, purpleBright.bold(' $1'));
22
+ result = result.replace(/^## (.+)$/gm, purpleBright.bold(' $1'));
23
+ result = result.replace(/^# (.+)$/gm, purpleBright.bold(' $1'));
24
+
25
+ // Bold
26
+ result = result.replace(/\*\*(.+?)\*\*/g, chalk.bold('$1'));
27
+
28
+ // Italic
29
+ result = result.replace(/\*(.+?)\*/g, chalk.italic('$1'));
30
+
31
+ // Inline code
32
+ result = result.replace(/`([^`]+)`/g, violet('$1'));
33
+
34
+ // Code blocks (simple - just highlight)
35
+ result = result.replace(/```[\w]*\n([\s\S]*?)```/g, (_, code) => {
36
+ return purple('─'.repeat(40)) + '\n' +
37
+ violet(code.trim()) + '\n' +
38
+ purple('─'.repeat(40));
39
+ });
40
+
41
+ // Lists
42
+ result = result.replace(/^- (.+)$/gm, purple(' ◆') + ' $1');
43
+ result = result.replace(/^\d+\. (.+)$/gm, (_, item) => purple(' ◆') + ' ' + item);
44
+
45
+ // Links [text](url)
46
+ result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g,
47
+ purpleBright.underline('$1') + chalk.dim(' ($2)')
48
+ );
49
+
50
+ // Blockquotes
51
+ result = result.replace(/^> (.+)$/gm, purple(' │ ') + chalk.italic('$1'));
52
+
53
+ // Horizontal rules
54
+ result = result.replace(/^---$/gm, purple('─'.repeat(50)));
55
+
56
+ return result;
57
+ }
58
+
59
+ /**
60
+ * Format tool result for display
61
+ */
62
+ export function formatToolResult(toolName, result) {
63
+ const lines = [];
64
+
65
+ lines.push(purpleDim(` ┌─ ${toolName} ─────────────────────`));
66
+
67
+ if (result.error) {
68
+ lines.push(chalk.red(` │ Error: ${result.error}`));
69
+ } else {
70
+ // Format based on tool type
71
+ const formatted = formatResultByType(toolName, result);
72
+ for (const line of formatted.split('\n')) {
73
+ lines.push(purpleDim(' │ ') + line);
74
+ }
75
+ }
76
+
77
+ lines.push(purpleDim(' └' + '─'.repeat(40)));
78
+
79
+ return lines.join('\n');
80
+ }
81
+
82
+ /**
83
+ * Format result based on tool type
84
+ */
85
+ function formatResultByType(toolName, result) {
86
+ switch (toolName) {
87
+ case 'create_market':
88
+ return formatMarketCreation(result);
89
+ case 'generate_market':
90
+ return formatMarketIdea(result);
91
+ case 'list_markets':
92
+ return formatMarketList(result);
93
+ case 'score_news':
94
+ return formatNewsScore(result);
95
+ case 'get_stats':
96
+ return formatStats(result);
97
+ case 'run_command':
98
+ return formatCommandResult(result);
99
+ default:
100
+ return JSON.stringify(result, null, 2);
101
+ }
102
+ }
103
+
104
+ function formatMarketCreation(result) {
105
+ if (!result.success) return chalk.red('Market creation failed');
106
+
107
+ return [
108
+ purple('✓ Market created successfully!'),
109
+ '',
110
+ `${chalk.dim('Question:')} ${result.question}`,
111
+ `${chalk.dim('Address:')} ${violet(result.market)}`,
112
+ `${chalk.dim('Signature:')} ${chalk.gray(result.signature?.slice(0, 20) + '...')}`,
113
+ `${chalk.dim('Duration:')} ${result.duration_days} days`,
114
+ `${chalk.dim('Liquidity:')} ${result.liquidity_usdc} USDC`,
115
+ `${chalk.dim('Network:')} ${result.network}`
116
+ ].join('\n');
117
+ }
118
+
119
+ function formatMarketIdea(result) {
120
+ if (result.markets) {
121
+ return result.markets.map((m, i) =>
122
+ `${i + 1}. ${chalk.bold(m.question)}\n` +
123
+ ` ${chalk.dim('Category:')} ${m.category} | ` +
124
+ `${chalk.dim('Duration:')} ${m.durationDays}d | ` +
125
+ `${chalk.dim('Liquidity:')} ${m.suggestedLiquidity} USDC`
126
+ ).join('\n\n');
127
+ }
128
+
129
+ return [
130
+ chalk.bold(result.question),
131
+ '',
132
+ `${chalk.dim('Category:')} ${result.category}`,
133
+ `${chalk.dim('Duration:')} ${result.durationDays} days`,
134
+ `${chalk.dim('Suggested Liquidity:')} ${result.suggestedLiquidity} USDC`,
135
+ result.reasoning ? `${chalk.dim('Reasoning:')} ${result.reasoning}` : ''
136
+ ].filter(Boolean).join('\n');
137
+ }
138
+
139
+ function formatMarketList(result) {
140
+ if (!result.markets || result.markets.length === 0) {
141
+ return chalk.dim('No markets found');
142
+ }
143
+
144
+ return result.markets.map(m =>
145
+ `• ${chalk.bold(m.question?.slice(0, 50) || 'Unknown')}${m.question?.length > 50 ? '...' : ''}\n` +
146
+ ` ${chalk.dim(m.address?.slice(0, 20) + '...')} | ${m.status || 'unknown'}`
147
+ ).join('\n');
148
+ }
149
+
150
+ function formatNewsScore(result) {
151
+ const scoreColor = result.score >= 70 ? chalk.green :
152
+ result.score >= 40 ? chalk.yellow : chalk.red;
153
+
154
+ return [
155
+ `${chalk.dim('Score:')} ${scoreColor(result.score + '/100')}`,
156
+ `${chalk.dim('Category:')} ${result.category}`,
157
+ `${chalk.dim('Urgency:')} ${result.urgency}`,
158
+ `${chalk.dim('Market Potential:')} ${result.market_potential ? 'High' : 'Low'}`,
159
+ result.reasoning ? `${chalk.dim('Analysis:')} ${result.reasoning}` : ''
160
+ ].filter(Boolean).join('\n');
161
+ }
162
+
163
+ function formatStats(result) {
164
+ return [
165
+ `${chalk.dim('Period:')} ${result.period || 'all'}`,
166
+ `${chalk.dim('Total Markets:')} ${result.totalMarkets || 0}`,
167
+ `${chalk.dim('Active:')} ${result.activeMarkets || 0}`,
168
+ `${chalk.dim('Resolved:')} ${result.resolvedMarkets || 0}`
169
+ ].join('\n');
170
+ }
171
+
172
+ function formatCommandResult(result) {
173
+ const status = result.success
174
+ ? chalk.green('✓ Success')
175
+ : chalk.red(`✗ Exit code ${result.exit_code}`);
176
+
177
+ let output = status;
178
+ if (result.stdout) {
179
+ output += '\n' + result.stdout;
180
+ }
181
+ if (result.stderr && !result.success) {
182
+ output += '\n' + chalk.red(result.stderr);
183
+ }
184
+ return output;
185
+ }
186
+
187
+ /**
188
+ * Print a spinner message
189
+ */
190
+ export function printSpinner(message) {
191
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
192
+ let i = 0;
193
+
194
+ return setInterval(() => {
195
+ process.stdout.write(`\r${purple(frames[i++ % frames.length])} ${message}`);
196
+ }, 80);
197
+ }
198
+
199
+ /**
200
+ * Stop spinner and clear line
201
+ */
202
+ export function stopSpinner(spinner, message = '') {
203
+ clearInterval(spinner);
204
+ process.stdout.write('\r' + ' '.repeat(60) + '\r');
205
+ if (message) {
206
+ console.log(message);
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Print tool usage indicator
212
+ */
213
+ export function printToolUse(toolName) {
214
+ console.log(purpleDim(`\n ◆ ${toolName}`));
215
+ }
@@ -0,0 +1,146 @@
1
+ // Welcome screen for PNPFUCIUS - The PNP Exchange CLI
2
+
3
+ import chalk from 'chalk';
4
+ import { getConfig } from '../../config.js';
5
+
6
+ // Purple color definitions
7
+ const purple = chalk.hex('#A855F7');
8
+ const purpleBright = chalk.hex('#C084FC');
9
+ const purpleDim = chalk.hex('#7C3AED');
10
+ const purpleBold = chalk.hex('#A855F7').bold;
11
+ const violet = chalk.hex('#8B5CF6');
12
+
13
+ const LOGO = `
14
+ ${purple('╔════════════════════════════════════════════════════════════════════════╗')}
15
+ ${purple('║')} ${purple('║')}
16
+ ${purple('║')} ${purpleBright('██████╗ ███╗ ██╗██████╗ ███████╗██╗ ██╗ ██████╗██╗██╗ ██╗███████╗')} ${purple('║')}
17
+ ${purple('║')} ${purpleBright('██╔══██╗████╗ ██║██╔══██╗██╔════╝██║ ██║██╔════╝██║██║ ██║██╔════╝')} ${purple('║')}
18
+ ${purple('║')} ${purpleBright('██████╔╝██╔██╗ ██║██████╔╝█████╗ ██║ ██║██║ ██║██║ ██║███████╗')} ${purple('║')}
19
+ ${purple('║')} ${purpleBright('██╔═══╝ ██║╚██╗██║██╔═══╝ ██╔══╝ ██║ ██║██║ ██║██║ ██║╚════██║')} ${purple('║')}
20
+ ${purple('║')} ${purpleBright('██║ ██║ ╚████║██║ ██║ ╚██████╔╝╚██████╗██║╚██████╔╝███████║')} ${purple('║')}
21
+ ${purple('║')} ${purpleBright('╚═╝ ╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═════╝ ╚══════╝')} ${purple('║')}
22
+ ${purple('║')} ${purple('║')}
23
+ ${purple('║')} ${violet(' "The wise trader predicts with patience"')} ${purple('║')}
24
+ ${purple('║')} ${purple('║')}
25
+ ${purple('╚════════════════════════════════════════════════════════════════════════╝')}
26
+ `;
27
+
28
+ export function printWelcome(options = {}) {
29
+ const config = getConfig();
30
+ const walletKey = config.walletKey;
31
+ const walletDisplay = walletKey
32
+ ? `${walletKey.slice(0, 6)}...${walletKey.slice(-4)}`
33
+ : chalk.yellow('Not configured');
34
+
35
+ console.clear();
36
+
37
+ // Logo
38
+ console.log(LOGO);
39
+
40
+ // Tagline
41
+ console.log(purpleDim(' ◆ The PNP Exchange CLI'));
42
+ console.log(chalk.dim(' ◆ Create, Trade, and Settle Prediction Markets on Solana'));
43
+ console.log();
44
+
45
+ // Status bar
46
+ const networkColor = config.isMainnet ? chalk.red : chalk.green;
47
+ const networkLabel = config.isMainnet ? 'MAINNET' : 'DEVNET';
48
+
49
+ console.log(purple(' ┌' + '─'.repeat(62) + '┐'));
50
+ console.log(purple(' │') +
51
+ ` ${chalk.dim('Network')} ${networkColor.bold(networkLabel.padEnd(8))}` +
52
+ `${chalk.dim('│')} ` +
53
+ `${chalk.dim('Wallet')} ${chalk.white(walletDisplay.padEnd(16))}` +
54
+ `${chalk.dim('│')} ` +
55
+ `${chalk.dim('Helius')} ${config.heliusKey ? chalk.green('●') : chalk.yellow('○')} ` +
56
+ purple('│')
57
+ );
58
+ console.log(purple(' └' + '─'.repeat(62) + '┘'));
59
+ console.log();
60
+
61
+ // Help hint
62
+ console.log(chalk.dim(' Type a message or use ') + purple('/help') + chalk.dim(' for commands'));
63
+ console.log();
64
+ }
65
+
66
+ export function printHelp() {
67
+ console.log();
68
+ console.log(purpleBold(' PNPFUCIUS Commands'));
69
+ console.log(purple(' ' + '─'.repeat(50)));
70
+ console.log();
71
+
72
+ const sections = [
73
+ {
74
+ title: 'Markets',
75
+ commands: [
76
+ ['/create [q]', 'Create AMM market'],
77
+ ['/p2p [q]', 'Create P2P market'],
78
+ ['/odds <q>', 'Market with custom odds'],
79
+ ['/discover', 'Browse ALL PNP markets'],
80
+ ['/markets', 'List your markets'],
81
+ ]
82
+ },
83
+ {
84
+ title: 'Trading',
85
+ commands: [
86
+ ['/buy', 'Buy YES/NO tokens'],
87
+ ['/sell', 'Sell tokens'],
88
+ ['/prices <addr>', 'Get market prices'],
89
+ ['/balance', 'Check your balances'],
90
+ ]
91
+ },
92
+ {
93
+ title: 'Settlement',
94
+ commands: [
95
+ ['/oracle <addr>', 'Get LLM settlement criteria'],
96
+ ['/settle', 'Settle a market'],
97
+ ['/redeem', 'Redeem winning position'],
98
+ ['/refund', 'Claim refund'],
99
+ ]
100
+ },
101
+ {
102
+ title: 'Info',
103
+ commands: [
104
+ ['/info <addr>', 'Market details'],
105
+ ['/config', 'Show configuration'],
106
+ ['/pnp', 'PNP protocol info'],
107
+ ]
108
+ },
109
+ {
110
+ title: 'System',
111
+ commands: [
112
+ ['/clear', 'Clear screen'],
113
+ ['/help', 'Show this help'],
114
+ ['/exit', 'Exit PNPFUCIUS'],
115
+ ]
116
+ }
117
+ ];
118
+
119
+ for (const section of sections) {
120
+ console.log(violet(` ${section.title}`));
121
+ for (const [cmd, desc] of section.commands) {
122
+ console.log(` ${purple(cmd.padEnd(16))} ${chalk.dim(desc)}`);
123
+ }
124
+ console.log();
125
+ }
126
+
127
+ console.log(violet(' "The market reveals truth to those who wait"'));
128
+ console.log();
129
+ }
130
+
131
+ export function printGoodbye() {
132
+ console.log();
133
+ console.log(purple(' ◆ Thanks for using PNPFUCIUS!'));
134
+ console.log(chalk.dim(' ◆ "The wise trader knows when to exit"'));
135
+ console.log();
136
+ }
137
+
138
+ export function printError(message) {
139
+ console.log();
140
+ console.log(chalk.red(` ✗ Error: ${message}`));
141
+ console.log();
142
+ }
143
+
144
+ export function printDivider() {
145
+ console.log(purple('─'.repeat(60)));
146
+ }
@@ -0,0 +1,194 @@
1
+ // Privacy-themed prediction market templates and AI generation logic
2
+
3
+ const PRIVACY_CATEGORIES = {
4
+ regulation: {
5
+ name: 'Privacy Regulation',
6
+ weight: 0.25,
7
+ urgency: 'timely',
8
+ sentiment: 'neutral',
9
+ templates: [
10
+ 'Will {country} pass comprehensive privacy legislation by {date}?',
11
+ 'Will GDPR fines exceed ${amount}B in {year}?',
12
+ 'Will the US pass a federal privacy law by end of {year}?',
13
+ 'Will {company} face privacy-related regulatory action by {date}?',
14
+ 'Will any G7 nation ban end-to-end encryption by {date}?',
15
+ 'Will the SEC take enforcement action against a privacy protocol by {date}?',
16
+ 'Will OFAC add new privacy protocols to sanctions list by {date}?',
17
+ 'Will {country} implement mandatory KYC for self-custody wallets by {date}?',
18
+ 'Will the Tornado Cash developer case result in acquittal by {date}?',
19
+ 'Will any country legalize privacy-preserving payments by {date}?'
20
+ ]
21
+ },
22
+ technology: {
23
+ name: 'Privacy Technology',
24
+ weight: 0.30,
25
+ urgency: 'evergreen',
26
+ sentiment: 'bullish',
27
+ templates: [
28
+ 'Will zkSync TVL exceed ${amount}B by {date}?',
29
+ 'Will Tornado Cash sanctions be lifted by {date}?',
30
+ 'Will a major wallet integrate confidential transactions by {date}?',
31
+ 'Will homomorphic encryption see mainstream blockchain adoption by {year}?',
32
+ 'Will Solana native ZK proofs go live on mainnet by {date}?',
33
+ 'Will any privacy coin enter top 10 market cap by {date}?',
34
+ 'Will Light Protocol TVL exceed ${amount}M by {date}?',
35
+ 'Will Solana Token-2022 confidential transfers see significant adoption by {date}?',
36
+ 'Will a ZK-rollup process over 1000 TPS on mainnet by {date}?',
37
+ 'Will Aztec Network launch on mainnet by {date}?',
38
+ 'Will any major DEX implement private swaps by {date}?',
39
+ 'Will RAILGUN protocol TVL exceed ${amount}M by {date}?',
40
+ 'Will Zcash implement a major protocol upgrade by {date}?',
41
+ 'Will any L2 implement native confidential transactions by {date}?',
42
+ 'Will a privacy-focused stablecoin reach ${amount}M market cap by {date}?'
43
+ ]
44
+ },
45
+ adoption: {
46
+ name: 'Privacy Adoption',
47
+ weight: 0.25,
48
+ urgency: 'timely',
49
+ sentiment: 'bullish',
50
+ templates: [
51
+ 'Will Signal exceed {amount}M monthly active users by {date}?',
52
+ 'Will a major exchange delist all privacy coins by {date}?',
53
+ 'Will privacy-preserving identity solutions see enterprise adoption by {year}?',
54
+ 'Will any Fortune 500 company adopt ZK proofs for supply chain by {date}?',
55
+ 'Will confidential transactions become default on any top 20 chain by {date}?',
56
+ 'Will Brave browser exceed {amount}M monthly active users by {date}?',
57
+ 'Will ProtonMail reach {amount}M paid subscribers by {date}?',
58
+ 'Will any major social platform add E2E encryption for DMs by {date}?',
59
+ 'Will hardware wallets with privacy features exceed {amount}M units sold by {date}?',
60
+ 'Will a privacy-focused search engine enter top 5 globally by {date}?'
61
+ ]
62
+ },
63
+ events: {
64
+ name: 'Privacy Events',
65
+ weight: 0.20,
66
+ urgency: 'breaking',
67
+ sentiment: 'neutral',
68
+ templates: [
69
+ 'Will there be a major data breach affecting 100M+ users by {date}?',
70
+ 'Will any government agency be caught using unauthorized surveillance by {date}?',
71
+ 'Will a privacy-focused project win a major hackathon prize by {date}?',
72
+ 'Will Vitalik publicly endorse a specific privacy solution by {date}?',
73
+ 'Will the Solana Privacy Hackathon see over {amount} submissions?',
74
+ 'Will a major privacy researcher receive significant recognition by {date}?',
75
+ 'Will any privacy protocol suffer a major exploit by {date}?',
76
+ 'Will a whistleblower reveal new government surveillance programs by {date}?',
77
+ 'Will any major tech CEO publicly advocate for privacy by {date}?',
78
+ 'Will a privacy-themed movie or documentary reach mainstream audiences by {date}?'
79
+ ]
80
+ }
81
+ };
82
+
83
+ const FILL_DATA = {
84
+ countries: ['USA', 'EU', 'UK', 'Japan', 'South Korea', 'Australia', 'Canada', 'Brazil'],
85
+ companies: ['Meta', 'Google', 'Apple', 'Microsoft', 'Amazon', 'ByteDance', 'OpenAI', 'Coinbase'],
86
+ amounts: ['1', '5', '10', '50', '100', '500'],
87
+ smallAmounts: ['10', '25', '50', '100', '250']
88
+ };
89
+
90
+ function getRandomElement(arr) {
91
+ return arr[Math.floor(Math.random() * arr.length)];
92
+ }
93
+
94
+ function getRandomDate(minDays = 14, maxDays = 180) {
95
+ const days = minDays + Math.floor(Math.random() * (maxDays - minDays));
96
+ const date = new Date();
97
+ date.setDate(date.getDate() + days);
98
+
99
+ const months = ['January', 'February', 'March', 'April', 'May', 'June',
100
+ 'July', 'August', 'September', 'October', 'November', 'December'];
101
+ return `${months[date.getMonth()]} ${date.getFullYear()}`;
102
+ }
103
+
104
+ function getEndOfYear() {
105
+ const now = new Date();
106
+ const currentYear = now.getFullYear();
107
+ const nextYear = now.getMonth() > 9 ? currentYear + 1 : currentYear;
108
+ return nextYear.toString();
109
+ }
110
+
111
+ function fillTemplate(template) {
112
+ let filled = template;
113
+
114
+ filled = filled.replace('{country}', getRandomElement(FILL_DATA.countries));
115
+ filled = filled.replace('{company}', getRandomElement(FILL_DATA.companies));
116
+ filled = filled.replace('{amount}', getRandomElement(FILL_DATA.amounts));
117
+ filled = filled.replace('{date}', getRandomDate());
118
+ filled = filled.replace('{year}', getEndOfYear());
119
+
120
+ if (filled.includes('{amount}')) {
121
+ filled = filled.replace('{amount}', getRandomElement(FILL_DATA.smallAmounts));
122
+ }
123
+
124
+ return filled;
125
+ }
126
+
127
+ function selectCategory() {
128
+ const rand = Math.random();
129
+ let cumulative = 0;
130
+
131
+ for (const [key, category] of Object.entries(PRIVACY_CATEGORIES)) {
132
+ cumulative += category.weight;
133
+ if (rand < cumulative) {
134
+ return { key, category };
135
+ }
136
+ }
137
+
138
+ return { key: 'technology', category: PRIVACY_CATEGORIES.technology };
139
+ }
140
+
141
+ export function generatePrivacyMarket() {
142
+ const { key, category } = selectCategory();
143
+ const template = getRandomElement(category.templates);
144
+ const question = fillTemplate(template);
145
+
146
+ const durationDays = 14 + Math.floor(Math.random() * 166);
147
+
148
+ return {
149
+ question,
150
+ category: category.name,
151
+ categoryKey: key,
152
+ durationDays,
153
+ suggestedLiquidity: key === 'events' ? 500000n : 1000000n
154
+ };
155
+ }
156
+
157
+ export function generateMultipleMarkets(count = 5) {
158
+ const markets = [];
159
+ const usedQuestions = new Set();
160
+
161
+ while (markets.length < count) {
162
+ const market = generatePrivacyMarket();
163
+ if (!usedQuestions.has(market.question)) {
164
+ usedQuestions.add(market.question);
165
+ markets.push(market);
166
+ }
167
+ }
168
+
169
+ return markets;
170
+ }
171
+
172
+ export function getMarketsByCategory(categoryKey) {
173
+ const category = PRIVACY_CATEGORIES[categoryKey];
174
+ if (!category) {
175
+ throw new Error(`Unknown category: ${categoryKey}`);
176
+ }
177
+
178
+ return category.templates.map(template => ({
179
+ question: fillTemplate(template),
180
+ category: category.name,
181
+ categoryKey
182
+ }));
183
+ }
184
+
185
+ export function listCategories() {
186
+ return Object.entries(PRIVACY_CATEGORIES).map(([key, cat]) => ({
187
+ key,
188
+ name: cat.name,
189
+ weight: cat.weight,
190
+ templateCount: cat.templates.length
191
+ }));
192
+ }
193
+
194
+ export { PRIVACY_CATEGORIES };