@wzrd_sol/sdk 0.1.1 → 0.2.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.
package/README.md CHANGED
@@ -107,6 +107,8 @@ const ixs = await createSettleMarketIx(connection, wallet.publicKey, 6);
107
107
  // Build, sign, send as above
108
108
  ```
109
109
 
110
+ `settle_market` returns USDC from reserve and burns vLOFI. It does not mint CCM; CCM is claimed via merkle proof (`claim_global` / `claim_global_v2`).
111
+
110
112
  ### Read On-Chain State
111
113
 
112
114
  ```typescript
@@ -15,6 +15,8 @@ export interface MarketVaultFull extends MarketVaultData {
15
15
  marketId: number;
16
16
  totalDeposited: bigint;
17
17
  totalShares: bigint;
18
+ navPerShareBps: bigint;
19
+ lastNavUpdateSlot: bigint;
18
20
  }
19
21
  export interface ProtocolStateData {
20
22
  isInitialized: boolean;
@@ -40,6 +42,16 @@ export interface OnChainPosition {
40
42
  settled: boolean;
41
43
  entrySlot: bigint;
42
44
  }
45
+ export type LifecyclePhase = 'deposited' | 'accruing' | 'claimable' | 'claimed' | 'settled';
46
+ export interface LifecyclePhaseInput {
47
+ settled: boolean;
48
+ positionCreatedAt?: Date | string | null;
49
+ latestRootCreatedAt?: Date | string | null;
50
+ latestProofCreatedAt?: Date | string | null;
51
+ proofCumulativeTotalBase?: bigint | number | null;
52
+ claimedTotalBase?: bigint | number | null;
53
+ claimableCcmBase?: bigint | number | null;
54
+ }
43
55
  /** Parse a MarketVault account's core fields (skip Anchor discriminator). */
44
56
  export declare function parseMarketVault(data: Buffer): MarketVaultData;
45
57
  /**
@@ -52,6 +64,7 @@ export declare function parseMarketVault(data: Buffer): MarketVaultData;
52
64
  export declare function parseProtocolState(data: Buffer): ProtocolStateData;
53
65
  /** Parse a UserMarketPosition account. Returns null if data is too short. */
54
66
  export declare function parseUserMarketPosition(data: Buffer): OnChainPosition | null;
67
+ export declare function deriveLifecyclePhase(input: LifecyclePhaseInput): LifecyclePhase;
55
68
  /** Fetch a user's position for a specific market directly from chain. */
56
69
  export declare function fetchOnChainPosition(connection: Connection, user: PublicKey, marketId: number, programId?: PublicKey): Promise<OnChainPosition | null>;
57
70
  /** Fetch the full MarketVault data from chain. */
package/dist/accounts.js CHANGED
@@ -42,7 +42,7 @@ export function parseProtocolState(data) {
42
42
  }
43
43
  /** Parse a UserMarketPosition account. Returns null if data is too short. */
44
44
  export function parseUserMarketPosition(data) {
45
- if (data.length < 106)
45
+ if (data.length < 114)
46
46
  return null;
47
47
  const d = data.subarray(8); // skip Anchor discriminator
48
48
  return {
@@ -55,6 +55,45 @@ export function parseUserMarketPosition(data) {
55
55
  entrySlot: d.readBigUInt64LE(90),
56
56
  };
57
57
  }
58
+ function toMillis(value) {
59
+ if (!value)
60
+ return null;
61
+ const date = value instanceof Date ? value : new Date(value);
62
+ const time = date.getTime();
63
+ return Number.isFinite(time) ? time : null;
64
+ }
65
+ function toBigIntOrZero(value) {
66
+ if (typeof value === 'bigint')
67
+ return value;
68
+ if (typeof value === 'number' && Number.isFinite(value))
69
+ return BigInt(Math.trunc(value));
70
+ return 0n;
71
+ }
72
+ export function deriveLifecyclePhase(input) {
73
+ if (input.settled)
74
+ return 'settled';
75
+ const positionCreatedAtMs = toMillis(input.positionCreatedAt);
76
+ const latestRootCreatedAtMs = toMillis(input.latestRootCreatedAt);
77
+ if (positionCreatedAtMs !== null &&
78
+ (latestRootCreatedAtMs === null || latestRootCreatedAtMs < positionCreatedAtMs)) {
79
+ return 'deposited';
80
+ }
81
+ const latestProofCreatedAtMs = toMillis(input.latestProofCreatedAt);
82
+ const proofAppliesToPosition = positionCreatedAtMs === null ||
83
+ (latestProofCreatedAtMs !== null && latestProofCreatedAtMs >= positionCreatedAtMs);
84
+ const claimableCcmBase = toBigIntOrZero(input.claimableCcmBase);
85
+ if (proofAppliesToPosition && claimableCcmBase > 0n) {
86
+ return 'claimable';
87
+ }
88
+ const proofCumulativeTotalBase = toBigIntOrZero(input.proofCumulativeTotalBase);
89
+ const claimedTotalBase = toBigIntOrZero(input.claimedTotalBase);
90
+ if (proofAppliesToPosition &&
91
+ proofCumulativeTotalBase > 0n &&
92
+ claimedTotalBase >= proofCumulativeTotalBase) {
93
+ return 'claimed';
94
+ }
95
+ return 'accruing';
96
+ }
58
97
  // ── Fetch Helpers ──────────────────────────────────────
59
98
  /** Fetch a user's position for a specific market directly from chain. */
60
99
  export async function fetchOnChainPosition(connection, user, marketId, programId) {
@@ -82,6 +121,8 @@ export async function fetchMarketVault(connection, marketId, programId) {
82
121
  marketId,
83
122
  totalDeposited: d.readBigUInt64LE(105),
84
123
  totalShares: d.readBigUInt64LE(113),
124
+ navPerShareBps: d.length >= 137 ? d.readBigUInt64LE(129) : 0n,
125
+ lastNavUpdateSlot: d.length >= 145 ? d.readBigUInt64LE(137) : 0n,
85
126
  };
86
127
  }
87
128
  /** Read a token account balance from chain. */
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,47 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { deriveLifecyclePhase } from './accounts.js';
3
+ describe('deriveLifecyclePhase', () => {
4
+ it('returns settled when the position is settled', () => {
5
+ expect(deriveLifecyclePhase({ settled: true })).toBe('settled');
6
+ });
7
+ it('returns deposited before any newer root has landed', () => {
8
+ expect(deriveLifecyclePhase({
9
+ settled: false,
10
+ positionCreatedAt: '2026-03-17T10:00:00Z',
11
+ latestRootCreatedAt: '2026-03-17T09:59:59Z',
12
+ })).toBe('deposited');
13
+ });
14
+ it('returns claimable when an unclaimed proof exists after the position', () => {
15
+ expect(deriveLifecyclePhase({
16
+ settled: false,
17
+ positionCreatedAt: '2026-03-17T10:00:00Z',
18
+ latestRootCreatedAt: '2026-03-17T10:05:00Z',
19
+ latestProofCreatedAt: '2026-03-17T10:06:00Z',
20
+ proofCumulativeTotalBase: 50n,
21
+ claimedTotalBase: 25n,
22
+ claimableCcmBase: 25n,
23
+ })).toBe('claimable');
24
+ });
25
+ it('returns claimed when the latest proof is fully claimed', () => {
26
+ expect(deriveLifecyclePhase({
27
+ settled: false,
28
+ positionCreatedAt: '2026-03-17T10:00:00Z',
29
+ latestRootCreatedAt: '2026-03-17T10:05:00Z',
30
+ latestProofCreatedAt: '2026-03-17T10:06:00Z',
31
+ proofCumulativeTotalBase: 50n,
32
+ claimedTotalBase: 50n,
33
+ claimableCcmBase: 0n,
34
+ })).toBe('claimed');
35
+ });
36
+ it('returns accruing when no proof applies to the position yet', () => {
37
+ expect(deriveLifecyclePhase({
38
+ settled: false,
39
+ positionCreatedAt: '2026-03-17T10:00:00Z',
40
+ latestRootCreatedAt: '2026-03-17T10:05:00Z',
41
+ latestProofCreatedAt: '2026-03-17T09:55:00Z',
42
+ proofCumulativeTotalBase: 50n,
43
+ claimedTotalBase: 50n,
44
+ claimableCcmBase: 0n,
45
+ })).toBe('accruing');
46
+ });
47
+ });
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Agent Auth — Ed25519 challenge/verify handshake + inference/report/earned helpers.
3
+ *
4
+ * Stateless functions for the WZRD agent API. Used by AgentLoop and
5
+ * directly by any TypeScript agent that needs to authenticate and earn CCM.
6
+ *
7
+ * Runtime dependency: tweetnacl (direct dep — required for Ed25519 signing).
8
+ *
9
+ * @module agent-auth
10
+ */
11
+ import type { Keypair } from '@solana/web3.js';
12
+ /** Response from the challenge endpoint. */
13
+ export interface ChallengeResponse {
14
+ nonce: string;
15
+ message_format: string;
16
+ }
17
+ /** Response from the verify endpoint. */
18
+ export interface VerifyResponse {
19
+ token: string;
20
+ pubkey: string;
21
+ expires_at: string;
22
+ }
23
+ /** Response from the infer endpoint. */
24
+ export interface InferResponse {
25
+ execution_id: string | null;
26
+ quality_score: number | null;
27
+ latency_ms: number | null;
28
+ cost_usd: number | null;
29
+ provider: string | null;
30
+ executed_model: string | null;
31
+ response_preview: string | null;
32
+ }
33
+ /** Response from the report endpoint. */
34
+ export interface ReportResponse {
35
+ pending_ccm: number;
36
+ status: string;
37
+ }
38
+ /** Response from the earned endpoint. */
39
+ export interface EarnedResponse {
40
+ total_earned_ccm: number;
41
+ pending_ccm: number;
42
+ rank: number | null;
43
+ contribution_streak_days: number;
44
+ pipeline: {
45
+ state: string | null;
46
+ next_root_eta_secs: number | null;
47
+ hint: string | null;
48
+ } | null;
49
+ }
50
+ /**
51
+ * Request a challenge nonce from the WZRD server.
52
+ */
53
+ export declare function agentChallenge(apiBase?: string): Promise<ChallengeResponse>;
54
+ /**
55
+ * Submit a signed challenge for verification. Returns a session token.
56
+ */
57
+ export declare function agentVerify(pubkey: string, nonce: string, signature: string, apiBase?: string): Promise<VerifyResponse>;
58
+ /**
59
+ * Full auth flow: challenge -> sign -> verify -> token.
60
+ *
61
+ * Uses tweetnacl (transitive dep of @solana/web3.js) for Ed25519 signing.
62
+ * Message format: `wzrd-agent-auth v1 | wallet:{pubkey} | nonce:{nonce}`
63
+ */
64
+ export declare function agentAuth(keypair: Keypair, apiBase?: string): Promise<string>;
65
+ /**
66
+ * Run server-witnessed inference. Returns execution_id for verified reports.
67
+ *
68
+ * The server calls the LLM, grades the response, and stores an execution
69
+ * receipt. Pass the returned `execution_id` to `agentReport()` so the
70
+ * contribution is marked verified (highest reward tier).
71
+ */
72
+ export declare function agentInfer(token: string, model: string, taskType: string, apiBase?: string): Promise<InferResponse>;
73
+ /**
74
+ * Report a model pick for reward eligibility.
75
+ */
76
+ export declare function agentReport(token: string, modelId: string, opts?: {
77
+ taskType?: string;
78
+ executionId?: string | null;
79
+ qualityScore?: number | null;
80
+ latencyMs?: number | null;
81
+ costUsd?: number | null;
82
+ outcome?: string;
83
+ /** Full WZRD signal metadata — mirrors Python report_pick().metadata.wzrd */
84
+ metadata?: Record<string, unknown>;
85
+ }, apiBase?: string): Promise<ReportResponse>;
86
+ /**
87
+ * Check earned CCM status.
88
+ */
89
+ export declare function agentEarned(token: string, apiBase?: string): Promise<EarnedResponse>;
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Agent Auth — Ed25519 challenge/verify handshake + inference/report/earned helpers.
3
+ *
4
+ * Stateless functions for the WZRD agent API. Used by AgentLoop and
5
+ * directly by any TypeScript agent that needs to authenticate and earn CCM.
6
+ *
7
+ * Runtime dependency: tweetnacl (direct dep — required for Ed25519 signing).
8
+ *
9
+ * @module agent-auth
10
+ */
11
+ // ── Constants ────────────────────────────────────────────────────
12
+ const DEFAULT_API_BASE = 'https://api.twzrd.xyz';
13
+ // ── Base58 Encoder (inline — avoids adding bs58 as a direct dep) ─
14
+ const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
15
+ function base58Encode(bytes) {
16
+ let zeroes = 0;
17
+ for (let i = 0; i < bytes.length && bytes[i] === 0; i++)
18
+ zeroes++;
19
+ const size = Math.ceil(bytes.length * 138 / 100) + 1;
20
+ const b58 = new Uint8Array(size);
21
+ let length = 0;
22
+ for (let i = zeroes; i < bytes.length; i++) {
23
+ let carry = bytes[i];
24
+ let j = 0;
25
+ for (let k = size - 1; k >= 0 && (carry !== 0 || j < length); k--, j++) {
26
+ carry += 256 * b58[k];
27
+ b58[k] = carry % 58;
28
+ carry = Math.floor(carry / 58);
29
+ }
30
+ length = j;
31
+ }
32
+ let result = '1'.repeat(zeroes);
33
+ let started = false;
34
+ for (let i = size - length; i < size; i++) {
35
+ if (!started && b58[i] === 0)
36
+ continue;
37
+ started = true;
38
+ result += BASE58_ALPHABET[b58[i]];
39
+ }
40
+ return result || '1';
41
+ }
42
+ // ── Auth Functions ───────────────────────────────────────────────
43
+ /**
44
+ * Request a challenge nonce from the WZRD server.
45
+ */
46
+ export async function agentChallenge(apiBase = DEFAULT_API_BASE) {
47
+ const resp = await fetch(`${apiBase}/v1/agent/challenge`);
48
+ if (!resp.ok)
49
+ throw new Error(`[wzrd] challenge failed: ${resp.status}`);
50
+ return resp.json();
51
+ }
52
+ /**
53
+ * Submit a signed challenge for verification. Returns a session token.
54
+ */
55
+ export async function agentVerify(pubkey, nonce, signature, apiBase = DEFAULT_API_BASE) {
56
+ const resp = await fetch(`${apiBase}/v1/agent/verify`, {
57
+ method: 'POST',
58
+ headers: { 'Content-Type': 'application/json' },
59
+ body: JSON.stringify({ pubkey, nonce, signature }),
60
+ });
61
+ if (!resp.ok)
62
+ throw new Error(`[wzrd] verify failed: ${resp.status}`);
63
+ return resp.json();
64
+ }
65
+ /**
66
+ * Full auth flow: challenge -> sign -> verify -> token.
67
+ *
68
+ * Uses tweetnacl (transitive dep of @solana/web3.js) for Ed25519 signing.
69
+ * Message format: `wzrd-agent-auth v1 | wallet:{pubkey} | nonce:{nonce}`
70
+ */
71
+ export async function agentAuth(keypair, apiBase = DEFAULT_API_BASE) {
72
+ // tweetnacl is a direct dependency (declared in package.json)
73
+ const naclMod = await import('tweetnacl');
74
+ const nacl = naclMod.default ?? naclMod;
75
+ const pubkey = keypair.publicKey.toBase58();
76
+ const { nonce } = await agentChallenge(apiBase);
77
+ const message = `wzrd-agent-auth v1 | wallet:${pubkey} | nonce:${nonce}`;
78
+ const messageBytes = new TextEncoder().encode(message);
79
+ const sig = nacl.sign.detached(messageBytes, keypair.secretKey);
80
+ const signatureB58 = base58Encode(sig);
81
+ const session = await agentVerify(pubkey, nonce, signatureB58, apiBase);
82
+ return session.token;
83
+ }
84
+ // ── API Functions ────────────────────────────────────────────────
85
+ /**
86
+ * Run server-witnessed inference. Returns execution_id for verified reports.
87
+ *
88
+ * The server calls the LLM, grades the response, and stores an execution
89
+ * receipt. Pass the returned `execution_id` to `agentReport()` so the
90
+ * contribution is marked verified (highest reward tier).
91
+ */
92
+ export async function agentInfer(token, model, taskType, apiBase = DEFAULT_API_BASE) {
93
+ const resp = await fetch(`${apiBase}/v1/agent/infer`, {
94
+ method: 'POST',
95
+ headers: {
96
+ 'Content-Type': 'application/json',
97
+ Authorization: `Bearer ${token}`,
98
+ },
99
+ body: JSON.stringify({ model, task_type: taskType }),
100
+ });
101
+ if (!resp.ok)
102
+ throw new Error(`[wzrd] infer failed: ${resp.status}`);
103
+ return resp.json();
104
+ }
105
+ /**
106
+ * Report a model pick for reward eligibility.
107
+ */
108
+ export async function agentReport(token, modelId, opts = {}, apiBase = DEFAULT_API_BASE) {
109
+ const payload = {
110
+ model_id: modelId,
111
+ outcome: opts.outcome ?? 'success',
112
+ };
113
+ if (opts.taskType != null)
114
+ payload.task_type = opts.taskType;
115
+ if (opts.executionId != null)
116
+ payload.execution_id = opts.executionId;
117
+ if (opts.qualityScore != null)
118
+ payload.quality_score = opts.qualityScore;
119
+ if (opts.latencyMs != null)
120
+ payload.latency_ms = opts.latencyMs;
121
+ if (opts.costUsd != null)
122
+ payload.cost_usd = opts.costUsd;
123
+ if (opts.metadata != null)
124
+ payload.metadata = opts.metadata;
125
+ const resp = await fetch(`${apiBase}/v1/agent/report`, {
126
+ method: 'POST',
127
+ headers: {
128
+ 'Content-Type': 'application/json',
129
+ Authorization: `Bearer ${token}`,
130
+ },
131
+ body: JSON.stringify(payload),
132
+ });
133
+ if (!resp.ok)
134
+ throw new Error(`[wzrd] report failed: ${resp.status}`);
135
+ return resp.json();
136
+ }
137
+ /**
138
+ * Check earned CCM status.
139
+ */
140
+ export async function agentEarned(token, apiBase = DEFAULT_API_BASE) {
141
+ const resp = await fetch(`${apiBase}/v1/agent/earned`, {
142
+ headers: { Authorization: `Bearer ${token}` },
143
+ });
144
+ if (!resp.ok)
145
+ throw new Error(`[wzrd] earned check failed: ${resp.status}`);
146
+ return resp.json();
147
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * AgentLoop — turn any TypeScript agent into a WZRD earning agent.
3
+ *
4
+ * Ports the core earn loop from Python `wzrd.run_loop()` to TypeScript:
5
+ * 1. Auth via Ed25519 challenge/verify
6
+ * 2. Per task: fetchPick() (momentum API) -> agentInfer() -> agentReport()
7
+ * 3. Check earned -> relay claim if available
8
+ * 4. Sleep -> repeat
9
+ *
10
+ * Scoring parity with Python: confidence × action × trend × task-platform × quality.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { AgentLoop } from '@wzrd_sol/sdk';
15
+ * import { Keypair } from '@solana/web3.js';
16
+ *
17
+ * const loop = new AgentLoop({
18
+ * keypair: Keypair.fromSecretKey(secretKey),
19
+ * tasks: ['code', 'chat', 'reasoning'],
20
+ * cycleSeconds: 300,
21
+ * });
22
+ *
23
+ * loop.start(); // runs forever, handles auth + infer + report + claim
24
+ * ```
25
+ *
26
+ * @module agent-loop
27
+ */
28
+ import type { Keypair } from '@solana/web3.js';
29
+ import type { EarnedResponse } from './agent-auth.js';
30
+ /** Result of a single cycle, delivered via onCycle callback. */
31
+ export interface CycleResult {
32
+ cycle: number;
33
+ tasks: Array<{
34
+ task: string;
35
+ model: string | null;
36
+ qualityScore: number | null;
37
+ executionId: string | null;
38
+ pendingCcm: number;
39
+ }>;
40
+ earned: EarnedResponse | null;
41
+ error: string | null;
42
+ }
43
+ /** Configuration for the AgentLoop. */
44
+ export interface AgentLoopOptions {
45
+ /** Solana keypair for Ed25519 signing. */
46
+ keypair: Keypair;
47
+ /** Task types to report on each cycle. Default: ['code', 'chat', 'reasoning']. */
48
+ tasks?: string[];
49
+ /** Seconds between cycles (minimum 30). Default: 300. */
50
+ cycleSeconds?: number;
51
+ /** Stop after N cycles. Omit or 0 for infinite. */
52
+ maxCycles?: number;
53
+ /** Attempt gasless relay claim when CCM is claimable. Default: true. */
54
+ claim?: boolean;
55
+ /** API base URL. Default: 'https://api.twzrd.xyz'. */
56
+ apiBase?: string;
57
+ /** Optional callback invoked after each cycle completes. */
58
+ onCycle?: (result: CycleResult) => void;
59
+ }
60
+ /**
61
+ * Runs the WZRD earn loop: authenticate, pick models, infer, report, claim.
62
+ *
63
+ * Create one instance and call `start()`. The loop runs until `maxCycles`
64
+ * is reached or `stop()` is called. Handles SIGINT/SIGTERM for graceful
65
+ * shutdown.
66
+ */
67
+ export declare class AgentLoop {
68
+ private readonly keypair;
69
+ private readonly tasks;
70
+ private readonly cycleSeconds;
71
+ private readonly maxCycles;
72
+ private readonly claim;
73
+ private readonly apiBase;
74
+ private readonly onCycle?;
75
+ private running;
76
+ private token;
77
+ private sleepResolve;
78
+ constructor(options: AgentLoopOptions);
79
+ /**
80
+ * Start the earn loop. Blocks until maxCycles is reached or stop() is called.
81
+ */
82
+ start(): Promise<void>;
83
+ /**
84
+ * Signal the loop to stop after the current cycle completes.
85
+ */
86
+ stop(): void;
87
+ }