@wzrd_sol/solana-agent-plugin 0.1.0 → 0.1.2

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.
@@ -4,6 +4,24 @@
4
4
  * Fetch the WZRD attention market leaderboard — ranked by velocity EMA.
5
5
  * No auth required. Permissionless read.
6
6
  */
7
+ import { z } from 'zod';
8
+ import { formatVelocity, getLeaderboardData } from '../runtime.js';
9
+ export const leaderboardSchema = z.object({
10
+ limit: z.number().int().min(1).max(50).optional(),
11
+ platform: z.string().min(1).optional(),
12
+ });
13
+ export async function leaderboardHandler(agent, input) {
14
+ const data = await getLeaderboardData(agent, input);
15
+ const lines = data.markets.map((m, i) => `${i + 1}. ${m.metric} — ${formatVelocity(m.velocity_ema)} velocity ` +
16
+ `(${m.platform}) | ${m.multiplier_bps / 10000}x multiplier | ` +
17
+ `${m.position_count} positions | market_id=${m.market_id}`);
18
+ return {
19
+ success: true,
20
+ text: `WZRD Leaderboard (${data.markets.length} markets, ` +
21
+ `root_seq=${data.root.root_seq}):\n${lines.join('\n')}`,
22
+ data,
23
+ };
24
+ }
7
25
  export const LEADERBOARD_ACTION = {
8
26
  name: 'wzrd_leaderboard',
9
27
  similes: ['wzrd_markets', 'wzrd_trending', 'attention_markets', 'top_models'],
@@ -14,50 +32,16 @@ export const LEADERBOARD_ACTION = {
14
32
  examples: [
15
33
  [
16
34
  {
17
- user: 'user',
18
- content: { text: 'Show me the top attention markets' },
19
- },
20
- {
21
- user: 'assistant',
22
- content: {
23
- text: 'Here are the top WZRD markets by velocity:\n' +
24
- '1. Qwen 3.5 35B — 804K velocity (HuggingFace)\n' +
25
- '2. Qwen 3.5 9B — 769K velocity (HuggingFace)\n' +
26
- '3. Ollama — 104K velocity (GitHub)',
27
- },
28
- },
29
- ],
30
- [
31
- {
32
- user: 'user',
33
- content: { text: 'What HuggingFace models are trending?' },
34
- },
35
- {
36
- user: 'assistant',
37
- content: {
38
- text: 'Top HuggingFace models by attention velocity: ...',
35
+ input: { limit: 3 },
36
+ output: {
37
+ success: true,
38
+ root_seq: 1549,
39
+ markets: ['#16 Qwen 3.5 35B', '#14 Qwen 3.5 9B', '#10 Qwen 2.5 72B'],
39
40
  },
41
+ explanation: 'Fetch the top three WZRD markets without requiring auth.',
40
42
  },
41
43
  ],
42
44
  ],
43
- validate: async () => true,
44
- handler: async (client, params) => {
45
- const data = await client.getLeaderboard(params.limit ?? 10, params.platform);
46
- const lines = data.markets.map((m, i) => `${i + 1}. ${m.metric} — ${formatVelocity(m.velocity_ema)} velocity ` +
47
- `(${m.platform}) | ${m.multiplier_bps / 10000}x multiplier | ` +
48
- `${m.position_count} positions | market_id=${m.market_id}`);
49
- return {
50
- success: true,
51
- text: `WZRD Leaderboard (${data.markets.length} markets, ` +
52
- `root_seq=${data.root.root_seq}):\n${lines.join('\n')}`,
53
- data,
54
- };
55
- },
45
+ schema: leaderboardSchema,
46
+ handler: async (agent, input) => leaderboardHandler(agent, leaderboardSchema.parse(input)),
56
47
  };
57
- function formatVelocity(v) {
58
- if (v >= 1_000_000)
59
- return `${(v / 1_000_000).toFixed(1)}M`;
60
- if (v >= 1_000)
61
- return `${(v / 1_000).toFixed(0)}K`;
62
- return v.toFixed(0);
63
- }
@@ -4,21 +4,8 @@
4
4
  * Fetch the agent's WZRD portfolio — positions, USDC deposited, vLOFI held, CCM earned.
5
5
  * Requires auth (Ed25519 agent session).
6
6
  */
7
- import type { WzrdClient } from '../client.js';
8
- export declare const PORTFOLIO_ACTION: {
9
- name: string;
10
- similes: string[];
11
- description: string;
12
- examples: {
13
- user: string;
14
- content: {
15
- text: string;
16
- };
17
- }[][];
18
- validate: () => Promise<boolean>;
19
- handler: (client: WzrdClient) => Promise<{
20
- success: boolean;
21
- text: string;
22
- data: import("../client.js").WzrdPortfolio;
23
- }>;
24
- };
7
+ import { z } from 'zod';
8
+ import type { Action, SolanaAgentKit } from 'solana-agent-kit';
9
+ export declare const portfolioSchema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
10
+ export declare function portfolioHandler(agent: SolanaAgentKit, _input: z.infer<typeof portfolioSchema>): Promise<Record<string, unknown>>;
11
+ export declare const PORTFOLIO_ACTION: Action;
@@ -4,6 +4,25 @@
4
4
  * Fetch the agent's WZRD portfolio — positions, USDC deposited, vLOFI held, CCM earned.
5
5
  * Requires auth (Ed25519 agent session).
6
6
  */
7
+ import { z } from 'zod';
8
+ import { formatCcm, formatUsdc, getPortfolioData } from '../runtime.js';
9
+ export const portfolioSchema = z.object({});
10
+ export async function portfolioHandler(agent, _input) {
11
+ const portfolio = await getPortfolioData(agent);
12
+ const open = portfolio.positions.filter((p) => !p.is_settled);
13
+ const lines = open.map((p) => `• Market #${p.market_id} (${p.metric}): ` +
14
+ `${formatUsdc(p.usdc_deposited)} USDC, ` +
15
+ `${(p.multiplier_bps / 10_000).toFixed(1)}x multiplier` +
16
+ (p.is_settled ? ' [settled]' : ''));
17
+ return {
18
+ success: true,
19
+ text: `Your WZRD portfolio (${open.length} open positions):\n` +
20
+ `${lines.length ? lines.join('\n') : '(no positions)'}\n` +
21
+ `Total: ${formatUsdc(portfolio.total_deposited_usdc)} USDC deposited, ` +
22
+ `${formatCcm(portfolio.total_ccm_earned)} CCM earned`,
23
+ data: portfolio,
24
+ };
25
+ }
7
26
  export const PORTFOLIO_ACTION = {
8
27
  name: 'wzrd_portfolio',
9
28
  similes: ['wzrd_positions', 'wzrd_balance', 'wzrd_holdings', 'my_positions'],
@@ -13,36 +32,17 @@ export const PORTFOLIO_ACTION = {
13
32
  examples: [
14
33
  [
15
34
  {
16
- user: 'user',
17
- content: { text: 'Show my WZRD positions' },
18
- },
19
- {
20
- user: 'assistant',
21
- content: {
22
- text: 'Your WZRD portfolio:\n' +
23
- '• Market #10 (Qwen 2.5 72B): 0.01 USDC, 5.0x multiplier\n' +
24
- '• Market #8 (Ollama): 0.01 USDC, 5.0x multiplier\n' +
25
- 'Total: 0.02 USDC deposited, 0 CCM earned',
35
+ input: {},
36
+ output: {
37
+ success: true,
38
+ open_positions: 2,
39
+ total_deposited_usdc: 20000,
40
+ total_ccm_earned: 0,
26
41
  },
42
+ explanation: 'Fetch the authenticated agent portfolio and summarize open exposure.',
27
43
  },
28
44
  ],
29
45
  ],
30
- validate: async () => true,
31
- handler: async (client) => {
32
- const portfolio = await client.getPortfolio();
33
- const open = portfolio.positions.filter((p) => !p.is_settled);
34
- const lines = open.map((p) => `• Market #${p.market_id} (${p.metric}): ` +
35
- `${(p.usdc_deposited / 1_000_000).toFixed(4)} USDC, ` +
36
- `${(p.multiplier_bps / 10_000).toFixed(1)}x multiplier` +
37
- (p.is_settled ? ' [settled]' : ''));
38
- const totalUsdc = portfolio.total_deposited_usdc / 1_000_000;
39
- const totalCcm = portfolio.total_ccm_earned / 1_000_000;
40
- return {
41
- success: true,
42
- text: `Your WZRD portfolio (${open.length} open positions):\n` +
43
- `${lines.length ? lines.join('\n') : '(no positions)'}\n` +
44
- `Total: ${totalUsdc.toFixed(4)} USDC deposited, ${totalCcm.toFixed(4)} CCM earned`,
45
- data: portfolio,
46
- };
47
- },
46
+ schema: portfolioSchema,
47
+ handler: async (agent, input) => portfolioHandler(agent, portfolioSchema.parse(input)),
48
48
  };
@@ -12,8 +12,9 @@
12
12
  * COOLING — p20-40
13
13
  * WEAK — below p20
14
14
  */
15
- import type { WzrdClient } from '../client.js';
16
- type SignalTier = 'BREAKOUT' | 'MOMENTUM' | 'EMERGING' | 'STABLE' | 'COOLING' | 'WEAK';
15
+ import { z } from 'zod';
16
+ import type { Action, SolanaAgentKit } from 'solana-agent-kit';
17
+ import { type SignalTier } from '../runtime.js';
17
18
  export interface MarketSignal {
18
19
  market_id: number;
19
20
  metric: string;
@@ -22,34 +23,15 @@ export interface MarketSignal {
22
23
  signal: SignalTier;
23
24
  percentile: number;
24
25
  }
25
- export declare const VELOCITY_ACTION: {
26
- name: string;
27
- similes: string[];
28
- description: string;
29
- examples: {
30
- user: string;
31
- content: {
32
- text: string;
33
- };
34
- }[][];
35
- validate: () => Promise<boolean>;
36
- handler: (client: WzrdClient, params: {
37
- platform?: string;
38
- min_signal?: SignalTier;
39
- }) => Promise<{
40
- success: boolean;
41
- text: string;
42
- data: {
43
- signals: never[];
44
- median_velocity?: undefined;
45
- };
46
- } | {
47
- success: boolean;
48
- text: string;
49
- data: {
50
- signals: MarketSignal[];
51
- median_velocity: number;
52
- };
53
- }>;
54
- };
55
- export {};
26
+ export declare const velocitySchema: z.ZodObject<{
27
+ platform: z.ZodOptional<z.ZodString>;
28
+ min_signal: z.ZodOptional<z.ZodEnum<["BREAKOUT", "MOMENTUM", "EMERGING", "STABLE", "COOLING", "WEAK"]>>;
29
+ }, "strip", z.ZodTypeAny, {
30
+ platform?: string | undefined;
31
+ min_signal?: "BREAKOUT" | "MOMENTUM" | "EMERGING" | "STABLE" | "COOLING" | "WEAK" | undefined;
32
+ }, {
33
+ platform?: string | undefined;
34
+ min_signal?: "BREAKOUT" | "MOMENTUM" | "EMERGING" | "STABLE" | "COOLING" | "WEAK" | undefined;
35
+ }>;
36
+ export declare function velocityHandler(agent: SolanaAgentKit, input: z.infer<typeof velocitySchema>): Promise<Record<string, unknown>>;
37
+ export declare const VELOCITY_ACTION: Action;
@@ -12,6 +12,66 @@
12
12
  * COOLING — p20-40
13
13
  * WEAK — below p20
14
14
  */
15
+ import { z } from 'zod';
16
+ import { formatVelocity, getLeaderboardData } from '../runtime.js';
17
+ export const velocitySchema = z.object({
18
+ platform: z.string().min(1).optional(),
19
+ min_signal: z
20
+ .enum(['BREAKOUT', 'MOMENTUM', 'EMERGING', 'STABLE', 'COOLING', 'WEAK'])
21
+ .optional(),
22
+ });
23
+ export async function velocityHandler(agent, input) {
24
+ const data = await getLeaderboardData(agent, { limit: 50, platform: input.platform });
25
+ const markets = data.markets;
26
+ if (markets.length === 0) {
27
+ return { success: true, text: 'No markets found.', data: { signals: [] } };
28
+ }
29
+ const sorted = [...markets].sort((a, b) => a.velocity_ema - b.velocity_ema);
30
+ const signals = markets.map((m) => {
31
+ const rank = sorted.findIndex((s) => s.market_id === m.market_id);
32
+ const percentile = ((rank + 1) / sorted.length) * 100;
33
+ const signal = classify(m, percentile);
34
+ return {
35
+ market_id: m.market_id,
36
+ metric: m.metric,
37
+ platform: m.platform,
38
+ velocity_ema: m.velocity_ema,
39
+ signal,
40
+ percentile: Math.round(percentile),
41
+ };
42
+ });
43
+ const tierOrder = ['BREAKOUT', 'MOMENTUM', 'EMERGING', 'STABLE', 'COOLING', 'WEAK'];
44
+ const minIdx = input.min_signal
45
+ ? tierOrder.indexOf(input.min_signal)
46
+ : tierOrder.length;
47
+ const filtered = minIdx < tierOrder.length
48
+ ? signals.filter((s) => tierOrder.indexOf(s.signal) <= minIdx)
49
+ : signals;
50
+ const grouped = new Map();
51
+ for (const s of filtered) {
52
+ const arr = grouped.get(s.signal) ?? [];
53
+ arr.push(s);
54
+ grouped.set(s.signal, arr);
55
+ }
56
+ const lines = [];
57
+ for (const tier of tierOrder) {
58
+ const group = grouped.get(tier);
59
+ if (!group?.length)
60
+ continue;
61
+ lines.push(`${tier}:`);
62
+ for (const s of group) {
63
+ lines.push(` • ${s.metric} (${formatVelocity(s.velocity_ema)} velocity, p${s.percentile}) [${s.platform}]`);
64
+ }
65
+ }
66
+ const medianIdx = Math.floor(sorted.length / 2);
67
+ const medianVelocity = sorted[medianIdx]?.velocity_ema ?? 0;
68
+ return {
69
+ success: true,
70
+ text: `Velocity analysis (${markets.length} markets, ` +
71
+ `median ${formatVelocity(medianVelocity)}):\n${lines.join('\n')}`,
72
+ data: { signals: filtered, median_velocity: medianVelocity },
73
+ };
74
+ }
15
75
  export const VELOCITY_ACTION = {
16
76
  name: 'wzrd_velocity',
17
77
  similes: ['wzrd_signal', 'attention_signal', 'market_analysis', 'velocity_check'],
@@ -23,78 +83,17 @@ export const VELOCITY_ACTION = {
23
83
  examples: [
24
84
  [
25
85
  {
26
- user: 'user',
27
- content: { text: 'What markets have breakout velocity?' },
28
- },
29
- {
30
- user: 'assistant',
31
- content: {
32
- text: 'BREAKOUT signals:\n' +
33
- '• Qwen 3.5 35B (804K velocity, p95)\n' +
34
- 'MOMENTUM signals:\n' +
35
- '• Qwen 3.5 9B (769K velocity, p85)\n' +
36
- '• Qwen 2.5 72B (378K velocity, p72)',
86
+ input: { min_signal: 'MOMENTUM' },
87
+ output: {
88
+ success: true,
89
+ signals: ['BREAKOUT: Qwen 3.5 35B', 'MOMENTUM: Qwen 2.5 72B'],
37
90
  },
91
+ explanation: 'Classify the current leaderboard into signal tiers for deposit decisions.',
38
92
  },
39
93
  ],
40
94
  ],
41
- validate: async () => true,
42
- handler: async (client, params) => {
43
- const data = await client.getLeaderboard(50, params.platform);
44
- const markets = data.markets;
45
- if (markets.length === 0) {
46
- return { success: true, text: 'No markets found.', data: { signals: [] } };
47
- }
48
- // Sort by velocity for percentile calculation
49
- const sorted = [...markets].sort((a, b) => a.velocity_ema - b.velocity_ema);
50
- const signals = markets.map((m) => {
51
- const rank = sorted.findIndex((s) => s.market_id === m.market_id);
52
- const percentile = ((rank + 1) / sorted.length) * 100;
53
- const signal = classify(m, percentile);
54
- return {
55
- market_id: m.market_id,
56
- metric: m.metric,
57
- platform: m.platform,
58
- velocity_ema: m.velocity_ema,
59
- signal,
60
- percentile: Math.round(percentile),
61
- };
62
- });
63
- // Filter by minimum signal if requested
64
- const tierOrder = ['BREAKOUT', 'MOMENTUM', 'EMERGING', 'STABLE', 'COOLING', 'WEAK'];
65
- const minIdx = params.min_signal
66
- ? tierOrder.indexOf(params.min_signal)
67
- : tierOrder.length;
68
- const filtered = minIdx < tierOrder.length
69
- ? signals.filter((s) => tierOrder.indexOf(s.signal) <= minIdx)
70
- : signals;
71
- // Group by signal tier
72
- const grouped = new Map();
73
- for (const s of filtered) {
74
- const arr = grouped.get(s.signal) ?? [];
75
- arr.push(s);
76
- grouped.set(s.signal, arr);
77
- }
78
- const lines = [];
79
- for (const tier of tierOrder) {
80
- const group = grouped.get(tier);
81
- if (!group?.length)
82
- continue;
83
- lines.push(`${tier}:`);
84
- for (const s of group) {
85
- lines.push(` • ${s.metric} (${formatVelocity(s.velocity_ema)} velocity, p${s.percentile}) [${s.platform}]`);
86
- }
87
- }
88
- // Median velocity
89
- const medianIdx = Math.floor(sorted.length / 2);
90
- const medianVelocity = sorted[medianIdx]?.velocity_ema ?? 0;
91
- return {
92
- success: true,
93
- text: `Velocity analysis (${markets.length} markets, ` +
94
- `median ${formatVelocity(medianVelocity)}):\n${lines.join('\n')}`,
95
- data: { signals: filtered, median_velocity: medianVelocity },
96
- };
97
- },
95
+ schema: velocitySchema,
96
+ handler: async (agent, input) => velocityHandler(agent, velocitySchema.parse(input)),
98
97
  };
99
98
  function classify(m, percentile) {
100
99
  if (percentile >= 90)
@@ -109,10 +108,3 @@ function classify(m, percentile) {
109
108
  return 'COOLING';
110
109
  return 'WEAK';
111
110
  }
112
- function formatVelocity(v) {
113
- if (v >= 1_000_000)
114
- return `${(v / 1_000_000).toFixed(1)}M`;
115
- if (v >= 1_000)
116
- return `${(v / 1_000).toFixed(0)}K`;
117
- return v.toFixed(0);
118
- }
package/dist/client.d.ts CHANGED
@@ -2,9 +2,16 @@
2
2
  * WZRD API client — handles agent auth (Ed25519 challenge/verify),
3
3
  * token caching, and all REST calls.
4
4
  *
5
- * Standalone: no framework dependency. Works with any Keypair holder.
5
+ * Standalone: no framework dependency. Works with a Keypair or any wallet
6
+ * implementation that supports `publicKey` + `signMessage()`.
6
7
  */
7
- import { Keypair } from '@solana/web3.js';
8
+ import { Keypair, type PublicKey } from '@solana/web3.js';
9
+ export declare const DEFAULT_API_URL = "https://api.twzrd.xyz";
10
+ export interface WzrdSigner {
11
+ publicKey: PublicKey;
12
+ signMessage?: (message: Uint8Array) => Promise<Uint8Array>;
13
+ secretKey?: Uint8Array;
14
+ }
8
15
  export interface WzrdMarket {
9
16
  market_id: number;
10
17
  channel_id: string;
@@ -18,6 +25,7 @@ export interface WzrdMarket {
18
25
  status: string;
19
26
  snapshot_count: number;
20
27
  last_scored_at: string;
28
+ depositable: boolean;
21
29
  }
22
30
  export interface WzrdLeaderboard {
23
31
  markets: WzrdMarket[];
@@ -57,15 +65,20 @@ export interface WzrdClaim {
57
65
  export interface WzrdRelayResult {
58
66
  root_seq: number;
59
67
  cumulative_total: number;
60
- tx_sig: string;
68
+ claimed_total?: number;
69
+ tx_sig?: string;
70
+ status?: 'already_claimed';
61
71
  }
62
72
  export declare class WzrdClient {
63
- private readonly keypair;
73
+ private readonly signer;
64
74
  private readonly apiUrl;
65
75
  private token;
66
76
  private tokenExpiresAt;
67
- constructor(keypair: Keypair, apiUrl?: string);
77
+ constructor(signer: WzrdSigner | Keypair, apiUrl?: string);
68
78
  get pubkey(): string;
79
+ private isWalletSigner;
80
+ private hasSecretKey;
81
+ private signAuthMessage;
69
82
  private authenticate;
70
83
  private getToken;
71
84
  private authedFetch;
package/dist/client.js CHANGED
@@ -2,9 +2,11 @@
2
2
  * WZRD API client — handles agent auth (Ed25519 challenge/verify),
3
3
  * token caching, and all REST calls.
4
4
  *
5
- * Standalone: no framework dependency. Works with any Keypair holder.
5
+ * Standalone: no framework dependency. Works with a Keypair or any wallet
6
+ * implementation that supports `publicKey` + `signMessage()`.
6
7
  */
7
- const DEFAULT_API_URL = 'https://api.twzrd.xyz';
8
+ import { Keypair } from '@solana/web3.js';
9
+ export const DEFAULT_API_URL = 'https://api.twzrd.xyz';
8
10
  const TOKEN_REFRESH_MARGIN_MS = 5 * 60 * 1000;
9
11
  // ── Base58 encoder (zero-dependency) ────────────────────
10
12
  const B58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
@@ -34,16 +36,32 @@ function toBase58(bytes) {
34
36
  }
35
37
  // ── Client ──────────────────────────────────────────────
36
38
  export class WzrdClient {
37
- keypair;
39
+ signer;
38
40
  apiUrl;
39
41
  token = null;
40
42
  tokenExpiresAt = 0;
41
- constructor(keypair, apiUrl = DEFAULT_API_URL) {
42
- this.keypair = keypair;
43
+ constructor(signer, apiUrl = DEFAULT_API_URL) {
44
+ this.signer = signer;
43
45
  this.apiUrl = apiUrl;
44
46
  }
45
47
  get pubkey() {
46
- return this.keypair.publicKey.toBase58();
48
+ return this.signer.publicKey.toBase58();
49
+ }
50
+ isWalletSigner(signer) {
51
+ return typeof signer.signMessage === 'function';
52
+ }
53
+ hasSecretKey(signer) {
54
+ return signer instanceof Keypair || signer.secretKey instanceof Uint8Array;
55
+ }
56
+ async signAuthMessage(message) {
57
+ if (this.isWalletSigner(this.signer)) {
58
+ return this.signer.signMessage(message);
59
+ }
60
+ if (this.hasSecretKey(this.signer)) {
61
+ const { default: nacl } = await import('tweetnacl');
62
+ return nacl.sign.detached(message, this.signer.secretKey);
63
+ }
64
+ throw new Error('WzrdClient requires a signer with signMessage() or an Ed25519 secretKey');
47
65
  }
48
66
  // ── Auth ────────────────────────────────────────────
49
67
  async authenticate() {
@@ -54,8 +72,7 @@ export class WzrdClient {
54
72
  const { nonce } = (await cr.json());
55
73
  // 2. Sign with Ed25519
56
74
  const message = `wzrd-agent-auth v1 | wallet:${this.pubkey} | nonce:${nonce}`;
57
- const { default: nacl } = await import('tweetnacl');
58
- const sig = nacl.sign.detached(new TextEncoder().encode(message), this.keypair.secretKey);
75
+ const sig = await this.signAuthMessage(new TextEncoder().encode(message));
59
76
  // 3. Verify
60
77
  const vr = await fetch(`${this.apiUrl}/v1/agent/verify`, {
61
78
  method: 'POST',