@wzrd_sol/sdk 0.1.2 → 0.3.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 +106 -124
- package/dist/accounts.d.ts +11 -0
- package/dist/accounts.js +40 -1
- package/dist/accounts.test.d.ts +1 -0
- package/dist/accounts.test.js +47 -0
- package/dist/agent-auth.d.ts +89 -0
- package/dist/agent-auth.js +147 -0
- package/dist/agent-loop.d.ts +87 -0
- package/dist/agent-loop.js +388 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +3 -0
- package/dist/index.d.ts +13 -6
- package/dist/index.js +12 -5
- package/dist/instructions.d.ts +69 -0
- package/dist/instructions.js +200 -2
- package/dist/instructions.test.js +2 -2
- package/dist/model-selector.d.ts +141 -0
- package/dist/model-selector.js +247 -0
- package/dist/pda.d.ts +4 -0
- package/dist/pda.js +10 -1
- package/dist/stream.d.ts +88 -0
- package/dist/stream.js +220 -0
- package/dist/stream.test.d.ts +7 -0
- package/dist/stream.test.js +296 -0
- package/package.json +11 -6
package/README.md
CHANGED
|
@@ -1,171 +1,153 @@
|
|
|
1
|
-
#
|
|
1
|
+
# WZRD — AI Model Velocity Oracle
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Builds `deposit_market`, `settle_market`, and `claim_global` transaction instructions that wallets sign directly — no server signing needed.
|
|
3
|
+
Real-time adoption tracking for 100+ open-source AI models across HuggingFace, GitHub, OpenRouter, and ArtificialAnalysis. Free to read. Agents earn CCM on Solana.
|
|
6
4
|
|
|
7
5
|
## Install
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
|
-
|
|
8
|
+
pip install wzrd-client
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
From the repo root:
|
|
11
|
+
```python
|
|
12
|
+
import wzrd
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
npm run typecheck:examples --workspace=sdk
|
|
14
|
+
model = wzrd.pick("code") # Best model for coding right now
|
|
15
|
+
details = wzrd.pick_details("reasoning") # With score, trend, confidence
|
|
20
16
|
```
|
|
21
17
|
|
|
22
|
-
|
|
18
|
+
No API key. No auth. Returns in <100ms (cached).
|
|
23
19
|
|
|
24
|
-
|
|
25
|
-
- `npm run example:claim --workspace=sdk`
|
|
20
|
+
## Earn CCM
|
|
26
21
|
|
|
27
|
-
|
|
22
|
+
Agents that contribute model selection data earn CCM tokens. The loop is: **authenticate, pick, infer, report, claim.**
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
```python
|
|
25
|
+
import wzrd
|
|
31
26
|
|
|
32
|
-
|
|
27
|
+
wzrd.run_loop(
|
|
28
|
+
keypair="~/.config/solana/id.json",
|
|
29
|
+
tasks=["code", "chat", "reasoning"],
|
|
30
|
+
cycle_seconds=60,
|
|
31
|
+
)
|
|
32
|
+
```
|
|
33
33
|
|
|
34
|
-
-
|
|
35
|
-
- `WZRD_DEPOSIT_USDC`
|
|
34
|
+
Claims are gasless — no SOL required. Server-witnessed inference ensures real usage.
|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
**[Full getting-started guide](https://twzrd.xyz/start)**
|
|
38
37
|
|
|
39
|
-
|
|
38
|
+
## MCP
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const marketVault = getMarketVaultPDA(protocolState, 6); // market ID 6
|
|
53
|
-
const position = getUserPositionPDA(marketVault, walletPubkey);
|
|
40
|
+
26 tools. Zero config. Connect from Claude Desktop, Cursor, or any MCP client.
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"wzrd": {
|
|
46
|
+
"transport": "streamable-http",
|
|
47
|
+
"url": "https://app.twzrd.xyz/api/mcp"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
54
51
|
```
|
|
55
52
|
|
|
56
|
-
|
|
53
|
+
Hero tool: `pick_model` — one call, best model for your task.
|
|
57
54
|
|
|
58
|
-
|
|
59
|
-
import { Connection, Keypair, VersionedTransaction, TransactionMessage } from '@solana/web3.js';
|
|
60
|
-
import { createDepositMarketIx } from '@wzrd_sol/sdk';
|
|
55
|
+
## TypeScript SDK
|
|
61
56
|
|
|
62
|
-
|
|
63
|
-
const wallet = Keypair.fromSecretKey(/* your key */);
|
|
57
|
+
Full instruction builders for the on-chain protocol: deposit, settle, claim, LP.
|
|
64
58
|
|
|
65
|
-
|
|
66
|
-
|
|
59
|
+
```bash
|
|
60
|
+
npm install @wzrd_sol/sdk
|
|
61
|
+
```
|
|
67
62
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
payerKey: wallet.publicKey,
|
|
71
|
-
recentBlockhash: blockhash,
|
|
72
|
-
instructions: ixs,
|
|
73
|
-
}).compileToV0Message();
|
|
63
|
+
```typescript
|
|
64
|
+
import { createDepositMarketIx } from '@wzrd_sol/sdk';
|
|
74
65
|
|
|
75
|
-
const
|
|
76
|
-
tx.sign([wallet]);
|
|
77
|
-
const sig = await connection.sendTransaction(tx);
|
|
78
|
-
console.log('Deposit tx:', sig);
|
|
66
|
+
const ixs = await createDepositMarketIx(connection, wallet, marketId, 1_000_000n);
|
|
79
67
|
```
|
|
80
68
|
|
|
81
|
-
###
|
|
82
|
-
|
|
83
|
-
```typescript
|
|
84
|
-
import { createClaimGlobalIx } from '@wzrd_sol/sdk';
|
|
69
|
+
### Framework Plugins
|
|
85
70
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
71
|
+
| Framework | Package | Install |
|
|
72
|
+
|-----------|---------|---------|
|
|
73
|
+
| ElizaOS | `@wzrd_sol/eliza-plugin` | `npm i @wzrd_sol/eliza-plugin` |
|
|
74
|
+
| Solana Agent Kit | `@wzrd_sol/solana-agent-plugin` | `npm i @wzrd_sol/solana-agent-plugin` |
|
|
75
|
+
| GOAT | `@wzrd_sol/goat-plugin` | `npm i @wzrd_sol/goat-plugin` |
|
|
89
76
|
|
|
90
|
-
|
|
91
|
-
connection,
|
|
92
|
-
wallet.publicKey,
|
|
93
|
-
root_seq,
|
|
94
|
-
BigInt(cumulative_total),
|
|
95
|
-
proof, // hex-encoded [u8; 32] nodes
|
|
96
|
-
);
|
|
77
|
+
## Python API
|
|
97
78
|
|
|
98
|
-
|
|
99
|
-
|
|
79
|
+
| Function | What it does |
|
|
80
|
+
|----------|-------------|
|
|
81
|
+
| `wzrd.pick(task)` | Best model name for a task |
|
|
82
|
+
| `wzrd.pick_details(task)` | Structured result with score, trend, confidence |
|
|
83
|
+
| `wzrd.shortlist(task, limit=5)` | Top N ranked models |
|
|
84
|
+
| `wzrd.compare(a, b)` | Head-to-head comparison |
|
|
85
|
+
| `wzrd.pick_onchain(task)` | Trustless — reads Switchboard feeds directly |
|
|
86
|
+
| `wzrd.run_loop(keypair=...)` | Full earn loop: auth, pick, report, claim |
|
|
87
|
+
| `WZRDRouter(client)` | Drop-in wrapper for OpenAI/Anthropic clients |
|
|
100
88
|
|
|
101
|
-
|
|
89
|
+
## Public API
|
|
102
90
|
|
|
103
|
-
|
|
104
|
-
import { createSettleMarketIx } from '@wzrd_sol/sdk';
|
|
91
|
+
Base URL: `https://api.twzrd.xyz`
|
|
105
92
|
|
|
106
|
-
|
|
107
|
-
|
|
93
|
+
```
|
|
94
|
+
GET /v1/signals/momentum — model velocity signals
|
|
95
|
+
GET /v1/leaderboard — ranked attention markets
|
|
96
|
+
GET /v1/markets/:id — single market detail
|
|
97
|
+
GET /v1/feeds — Switchboard oracle feeds
|
|
98
|
+
GET /health — service health
|
|
108
99
|
```
|
|
109
100
|
|
|
110
|
-
|
|
101
|
+
Full spec: [openapi.json](https://api.twzrd.xyz/openapi.json)
|
|
111
102
|
|
|
112
|
-
|
|
103
|
+
## Links
|
|
113
104
|
|
|
114
|
-
|
|
115
|
-
|
|
105
|
+
| | |
|
|
106
|
+
|---|---|
|
|
107
|
+
| Get Started | [twzrd.xyz/start](https://twzrd.xyz/start) |
|
|
108
|
+
| Live Feed | [twzrd.xyz/feed](https://twzrd.xyz/feed) |
|
|
109
|
+
| MCP Guide | [twzrd.xyz/mcp-guide.md](https://twzrd.xyz/mcp-guide.md) |
|
|
110
|
+
| Machine Manifest | [twzrd.xyz/llms.txt](https://twzrd.xyz/llms.txt) |
|
|
111
|
+
| API | [api.twzrd.xyz/v1/leaderboard](https://api.twzrd.xyz/v1/leaderboard) |
|
|
112
|
+
| PyPI | [wzrd-client](https://pypi.org/project/wzrd-client/) |
|
|
113
|
+
| npm | [@wzrd_sol/sdk](https://www.npmjs.com/package/@wzrd_sol/sdk) |
|
|
116
114
|
|
|
117
|
-
|
|
118
|
-
console.log('Total deposited:', vault?.totalDeposited);
|
|
115
|
+
## Key Identifiers
|
|
119
116
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
| Item | Address |
|
|
118
|
+
|------|---------|
|
|
119
|
+
| Program | `GnGzNdsQMxMpJfMeqnkGPsvHm8kwaDidiKjNU2dCVZop` |
|
|
120
|
+
| CCM Mint | `Dxk8mAb3C7AM8JN6tAJfVuSja5yidhZM5sEKW3SRX2BM` (Token-2022, 50 BPS transfer fee) |
|
|
121
|
+
| vLOFI Mint | `E9Kt33axpCy3ve2PCY9BSrbPhcR9wdDsWQECAahzw2dS` |
|
|
123
122
|
|
|
124
|
-
##
|
|
123
|
+
## Architecture
|
|
125
124
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
125
|
+
```
|
|
126
|
+
wzrd-final/
|
|
127
|
+
├── programs/attention-oracle/ # On-chain program (Solana)
|
|
128
|
+
├── server/ # Backend API + background jobs (Axum/Rust)
|
|
129
|
+
├── app/ # Frontend (React/Vite)
|
|
130
|
+
├── sdk/ # TypeScript SDK — 35 instruction builders
|
|
131
|
+
├── integrations/wzrd-client/ # Python client (PyPI: wzrd-client)
|
|
132
|
+
├── agents/ # 13 agent implementations across 6 frameworks
|
|
133
|
+
├── crates/ # Rust crates (stream ingestor, merkle, types)
|
|
134
|
+
├── ops/ # Deployment scripts
|
|
135
|
+
└── migrations/ # SQL migrations
|
|
136
|
+
```
|
|
134
137
|
|
|
135
|
-
|
|
138
|
+
## Build
|
|
136
139
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
| `getMarketVaultPDA(protocolState, marketId)` | `["market_vault", protocolState, marketId]` |
|
|
141
|
-
| `getUserPositionPDA(marketVault, user)` | `["market_position", marketVault, user]` |
|
|
142
|
-
| `getGlobalRootConfigPDA(ccmMint)` | `["global_root", ccmMint]` |
|
|
143
|
-
| `getClaimStatePDA(ccmMint, claimer)` | `["claim_global", ccmMint, claimer]` |
|
|
140
|
+
```bash
|
|
141
|
+
# Server
|
|
142
|
+
cargo build -p wzrd-server
|
|
144
143
|
|
|
145
|
-
|
|
144
|
+
# Frontend
|
|
145
|
+
cd app && npm install && npm run dev
|
|
146
146
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
| `createSettleMarketIx(conn, user, marketId)` | `settle_market` |
|
|
151
|
-
| `createClaimGlobalIx(conn, claimer, rootSeq, total, proof)` | `claim_global` |
|
|
152
|
-
| `createInitializeMarketVaultIx(admin, marketId, ...)` | `initialize_market_vault` |
|
|
147
|
+
# On-chain program
|
|
148
|
+
anchor build
|
|
149
|
+
```
|
|
153
150
|
|
|
154
|
-
|
|
151
|
+
## License
|
|
155
152
|
|
|
156
|
-
|
|
157
|
-
|----------|-------------|
|
|
158
|
-
| `parseMarketVault(data)` | `MarketVault` |
|
|
159
|
-
| `parseProtocolState(data)` | `ProtocolState` |
|
|
160
|
-
| `parseUserMarketPosition(data)` | `UserMarketPosition` |
|
|
161
|
-
| `fetchMarketVault(conn, marketId)` | Fetch + parse |
|
|
162
|
-
| `fetchOnChainPosition(conn, user, marketId)` | Fetch + parse |
|
|
163
|
-
| `fetchTokenBalance(conn, ata)` | Raw token balance |
|
|
164
|
-
|
|
165
|
-
## Key Addresses
|
|
166
|
-
|
|
167
|
-
| Asset | Mint | Token Program |
|
|
168
|
-
|-------|------|---------------|
|
|
169
|
-
| USDC | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` | SPL Token |
|
|
170
|
-
| vLOFI | `E9Kt33axpCy3ve2PCY9BSrbPhcR9wdDsWQECAahzw2dS` | SPL Token |
|
|
171
|
-
| CCM | `Dxk8mAb3C7AM8JN6tAJfVuSja5yidhZM5sEKW3SRX2BM` | Token-2022 |
|
|
153
|
+
All rights reserved. Source available for transparency and agent integration.
|
package/dist/accounts.d.ts
CHANGED
|
@@ -42,6 +42,16 @@ export interface OnChainPosition {
|
|
|
42
42
|
settled: boolean;
|
|
43
43
|
entrySlot: bigint;
|
|
44
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
|
+
}
|
|
45
55
|
/** Parse a MarketVault account's core fields (skip Anchor discriminator). */
|
|
46
56
|
export declare function parseMarketVault(data: Buffer): MarketVaultData;
|
|
47
57
|
/**
|
|
@@ -54,6 +64,7 @@ export declare function parseMarketVault(data: Buffer): MarketVaultData;
|
|
|
54
64
|
export declare function parseProtocolState(data: Buffer): ProtocolStateData;
|
|
55
65
|
/** Parse a UserMarketPosition account. Returns null if data is too short. */
|
|
56
66
|
export declare function parseUserMarketPosition(data: Buffer): OnChainPosition | null;
|
|
67
|
+
export declare function deriveLifecyclePhase(input: LifecyclePhaseInput): LifecyclePhase;
|
|
57
68
|
/** Fetch a user's position for a specific market directly from chain. */
|
|
58
69
|
export declare function fetchOnChainPosition(connection: Connection, user: PublicKey, marketId: number, programId?: PublicKey): Promise<OnChainPosition | null>;
|
|
59
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 <
|
|
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) {
|
|
@@ -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
|
+
}
|