@wzrd_sol/eliza-plugin 0.1.2 → 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/dist/actions/claim.d.ts +2 -5
- package/dist/actions/claim.js +33 -30
- package/dist/actions/earn.d.ts +8 -0
- package/dist/actions/earn.js +83 -0
- package/dist/actions/infer.d.ts +8 -0
- package/dist/actions/infer.js +41 -0
- package/dist/actions/report.d.ts +8 -0
- package/dist/actions/report.js +45 -0
- package/dist/actions/rewards.d.ts +5 -0
- package/dist/actions/rewards.js +31 -0
- package/dist/client-factory.d.ts +5 -0
- package/dist/client-factory.js +20 -0
- package/dist/client.d.ts +90 -3
- package/dist/client.js +133 -16
- package/dist/index.d.ts +15 -9
- package/dist/index.js +10 -9
- package/package.json +12 -9
- package/dist/actions/deposit.d.ts +0 -2
- package/dist/actions/deposit.js +0 -79
- package/dist/actions/leaderboard.d.ts +0 -3
- package/dist/actions/leaderboard.js +0 -25
- package/dist/actions/portfolio.d.ts +0 -3
- package/dist/actions/portfolio.js +0 -20
- package/dist/actions/velocity.d.ts +0 -3
- package/dist/actions/velocity.js +0 -57
package/dist/actions/claim.d.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Agents can check current APR before deciding whether to claim or hold:
|
|
5
|
-
* WzrdClient.getMarketApr(marketId) — projected_total_apr_bps shows current yield rate.
|
|
6
|
-
* WzrdClient.getMarketPreview(marketId) — projected_multiplier_bps for live attention signal.
|
|
2
|
+
* WZRD_CLAIM — Claim accrued CCM via gasless relay.
|
|
3
|
+
* Server pays all transaction fees. Agent needs 0 SOL.
|
|
7
4
|
*/
|
|
8
5
|
import type { Action } from '@elizaos/core';
|
|
9
6
|
export declare const claimAction: Action;
|
package/dist/actions/claim.js
CHANGED
|
@@ -1,45 +1,48 @@
|
|
|
1
|
-
import { getWzrdClient } from '../client.js';
|
|
1
|
+
import { getWzrdClient } from '../client-factory.js';
|
|
2
2
|
export const claimAction = {
|
|
3
3
|
name: 'WZRD_CLAIM',
|
|
4
|
-
similes: ['WZRD_HARVEST', 'CLAIM_CCM', '
|
|
5
|
-
description: 'Claim accrued CCM tokens via gasless relay. Server pays tx fees.',
|
|
6
|
-
examples: [
|
|
4
|
+
similes: ['WZRD_HARVEST', 'CLAIM_CCM', 'COLLECT_REWARDS'],
|
|
5
|
+
description: 'Claim accrued CCM tokens via gasless relay. Server pays tx fees — agent needs 0 SOL.',
|
|
6
|
+
examples: [
|
|
7
|
+
[
|
|
7
8
|
{ name: '{{user1}}', content: { text: 'Claim my WZRD rewards' } },
|
|
8
|
-
{ name: '{{agentName}}', content: { text: 'Claimed CCM via gasless relay. Tx: 5S7L...' } },
|
|
9
|
-
]
|
|
9
|
+
{ name: '{{agentName}}', content: { text: 'Claimed 142.5 CCM via gasless relay. Tx: 5S7L...' } },
|
|
10
|
+
],
|
|
11
|
+
],
|
|
10
12
|
validate: async () => true,
|
|
11
13
|
handler: async (runtime, _msg, _state, _opt, callback) => {
|
|
12
14
|
const client = getWzrdClient(runtime);
|
|
13
|
-
let claims;
|
|
14
15
|
try {
|
|
15
|
-
|
|
16
|
+
// Check if there's anything to claim
|
|
17
|
+
const status = await client.getClaimStatus();
|
|
18
|
+
if (status.claimable <= 0) {
|
|
19
|
+
const text = `No CCM to claim right now.\n` +
|
|
20
|
+
`Cumulative: ${(status.cumulative_total / 1e9).toFixed(2)} CCM\n` +
|
|
21
|
+
`Already claimed: ${(status.claimed_total / 1e9).toFixed(2)} CCM\n` +
|
|
22
|
+
`Run WZRD_EARN first to accrue rewards.`;
|
|
23
|
+
await callback?.({ text });
|
|
24
|
+
return { success: true, data: status };
|
|
25
|
+
}
|
|
26
|
+
const result = await client.claimRelay();
|
|
27
|
+
if (result.status === 'already_claimed') {
|
|
28
|
+
await callback?.({ text: `Already claimed through root ${result.root_seq}.` });
|
|
29
|
+
return { success: true, data: result };
|
|
30
|
+
}
|
|
31
|
+
const text = `Claimed CCM via gasless relay.\n` +
|
|
32
|
+
`Amount: ${(result.cumulative_total / 1e9).toFixed(2)} CCM (cumulative)\n` +
|
|
33
|
+
`Root: ${result.root_seq}\n` +
|
|
34
|
+
`Tx: ${result.tx_sig?.slice(0, 20)}...`;
|
|
35
|
+
await callback?.({ text });
|
|
36
|
+
return { success: true, data: result };
|
|
16
37
|
}
|
|
17
38
|
catch (err) {
|
|
18
39
|
const msg = err instanceof Error ? err.message : String(err);
|
|
19
40
|
if (msg.includes('404')) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return { success: true, text };
|
|
41
|
+
await callback?.({ text: 'No claims found yet. Run WZRD_EARN to start accruing CCM.' });
|
|
42
|
+
return { success: true, data: {} };
|
|
23
43
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return { success: false, error: text };
|
|
27
|
-
}
|
|
28
|
-
const claimable = claims.cumulative_total - claims.claimed_total;
|
|
29
|
-
if (claimable <= 0) {
|
|
30
|
-
const text = `No CCM to claim. Cumulative: ${claims.cumulative_total}, claimed: ${claims.claimed_total}.`;
|
|
31
|
-
await callback?.({ text });
|
|
32
|
-
return { success: true, text, data: claims };
|
|
33
|
-
}
|
|
34
|
-
const result = await client.claimRelay();
|
|
35
|
-
if (result.status === 'already_claimed') {
|
|
36
|
-
const text = `Already claimed through root ${result.root_seq}.`;
|
|
37
|
-
await callback?.({ text });
|
|
38
|
-
return { success: true, text, data: result };
|
|
44
|
+
await callback?.({ text: `Claim failed: ${msg}` });
|
|
45
|
+
return { success: false, error: msg };
|
|
39
46
|
}
|
|
40
|
-
const text = `Claimed CCM via relay. Cumulative: ${(result.cumulative_total / 1e6).toFixed(4)} CCM. ` +
|
|
41
|
-
`Root: ${result.root_seq}. Tx: ${result.tx_sig?.slice(0, 16)}...`;
|
|
42
|
-
await callback?.({ text });
|
|
43
|
-
return { success: true, text, data: result };
|
|
44
47
|
},
|
|
45
48
|
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WZRD_EARN — Full earn cycle: infer → report → check rewards.
|
|
3
|
+
*
|
|
4
|
+
* Single action that runs the complete server-witnessed earn loop.
|
|
5
|
+
* For agents that want one-shot earning without managing execution_ids.
|
|
6
|
+
*/
|
|
7
|
+
import type { Action } from '@elizaos/core';
|
|
8
|
+
export declare const earnAction: Action;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { getWzrdClient } from '../client-factory.js';
|
|
2
|
+
const EVAL_PROMPTS = {
|
|
3
|
+
code: [
|
|
4
|
+
'Write a Python function that checks if a binary tree is balanced. Include time complexity analysis.',
|
|
5
|
+
'Implement a thread-safe LRU cache in Rust with O(1) get and put operations.',
|
|
6
|
+
'Write a TypeScript generic function that deep-merges two objects, handling arrays and nested objects.',
|
|
7
|
+
],
|
|
8
|
+
chat: [
|
|
9
|
+
'Explain the difference between TCP and UDP to someone who has never programmed before.',
|
|
10
|
+
'What are the tradeoffs between microservices and monolithic architecture?',
|
|
11
|
+
'Describe how consensus works in proof-of-stake blockchains.',
|
|
12
|
+
],
|
|
13
|
+
reasoning: [
|
|
14
|
+
'A farmer has a fox, a chicken, and a bag of grain. He must cross a river in a boat that can only carry him and one item. How does he do it?',
|
|
15
|
+
'If it takes 5 machines 5 minutes to make 5 widgets, how long does it take 100 machines to make 100 widgets?',
|
|
16
|
+
'Three people check into a hotel room that costs $30. They each pay $10. The manager realizes the room should cost $25 and gives $5 to the bellboy to return. The bellboy keeps $2 and gives $1 back to each person. So each person paid $9 (total $27) plus the bellboy kept $2 (total $29). Where is the missing dollar?',
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
function pickPrompt(taskType) {
|
|
20
|
+
const prompts = EVAL_PROMPTS[taskType] || EVAL_PROMPTS.chat;
|
|
21
|
+
return prompts[Math.floor(Math.random() * prompts.length)];
|
|
22
|
+
}
|
|
23
|
+
export const earnAction = {
|
|
24
|
+
name: 'WZRD_EARN',
|
|
25
|
+
similes: ['WZRD_EARN_CCM', 'EARN_REWARDS', 'RUN_EARN_LOOP'],
|
|
26
|
+
description: 'Run the full WZRD earn cycle: pick a prompt, run server-witnessed inference, ' +
|
|
27
|
+
'report the outcome, and check pending rewards. One action, complete loop.',
|
|
28
|
+
examples: [
|
|
29
|
+
[
|
|
30
|
+
{ name: '{{user1}}', content: { text: 'Earn some CCM on WZRD' } },
|
|
31
|
+
{
|
|
32
|
+
name: '{{agentName}}',
|
|
33
|
+
content: {
|
|
34
|
+
text: 'Earn cycle complete. Model: gemini-2.5-flash, quality: 0.85, verified. Pending: 142.5 CCM.',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
],
|
|
39
|
+
validate: async () => true,
|
|
40
|
+
handler: async (runtime, message, _state, _opt, callback) => {
|
|
41
|
+
const content = message.content;
|
|
42
|
+
const taskType = content.task_type || 'code';
|
|
43
|
+
const prompt = content.prompt || pickPrompt(taskType);
|
|
44
|
+
const client = getWzrdClient(runtime);
|
|
45
|
+
const steps = [];
|
|
46
|
+
try {
|
|
47
|
+
// Step 1: Pick model + Infer
|
|
48
|
+
steps.push('→ Picking model from leaderboard...');
|
|
49
|
+
const model = await client.pickModel(taskType);
|
|
50
|
+
steps.push(`→ Running inference: ${model} (${taskType})...`);
|
|
51
|
+
const infer = await client.infer(prompt, model, taskType);
|
|
52
|
+
steps.push(`✓ Inference: ${infer.executed_model} (${infer.provider}), quality ${infer.quality_score.toFixed(2)}, ${infer.latency_ms}ms`);
|
|
53
|
+
// Step 2: Report with execution_id
|
|
54
|
+
steps.push('→ Reporting outcome...');
|
|
55
|
+
const report = await client.report({
|
|
56
|
+
model_id: infer.requested_model,
|
|
57
|
+
execution_id: infer.execution_id,
|
|
58
|
+
task_type: taskType,
|
|
59
|
+
quality_score: infer.quality_score,
|
|
60
|
+
latency_ms: infer.latency_ms,
|
|
61
|
+
});
|
|
62
|
+
steps.push(`✓ Reported: ${report.verification_state}, contribution #${report.contribution_id}, pending ${(report.pending_ccm / 1e9).toFixed(2)} CCM`);
|
|
63
|
+
// Step 3: Check rewards
|
|
64
|
+
steps.push('→ Checking rewards...');
|
|
65
|
+
const rewards = await client.getRewards();
|
|
66
|
+
steps.push(`✓ Rewards: ${(rewards.pending_ccm / 1e9).toFixed(2)} CCM pending, ` +
|
|
67
|
+
`${(rewards.total_rewarded_ccm / 1e9).toFixed(2)} CCM lifetime` +
|
|
68
|
+
(rewards.rank ? `, rank #${rewards.rank}` : ''));
|
|
69
|
+
const text = `Earn cycle complete:\n${steps.join('\n')}`;
|
|
70
|
+
await callback?.({ text });
|
|
71
|
+
return {
|
|
72
|
+
success: true,
|
|
73
|
+
data: { infer, report, rewards },
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
78
|
+
steps.push(`✗ Failed: ${msg}`);
|
|
79
|
+
await callback?.({ text: `Earn cycle failed:\n${steps.join('\n')}` });
|
|
80
|
+
return { success: false, error: msg };
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WZRD_INFER — Server-witnessed inference through WZRD.
|
|
3
|
+
*
|
|
4
|
+
* WZRD calls the AI provider (Gemini/Nous/OpenRouter), grades the response,
|
|
5
|
+
* and returns an execution_id receipt. Pass this to WZRD_REPORT for verified rewards.
|
|
6
|
+
*/
|
|
7
|
+
import type { Action } from '@elizaos/core';
|
|
8
|
+
export declare const inferAction: Action;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { getWzrdClient } from '../client-factory.js';
|
|
2
|
+
export const inferAction = {
|
|
3
|
+
name: 'WZRD_INFER',
|
|
4
|
+
similes: ['WZRD_RUN_INFERENCE', 'ASK_MODEL', 'RUN_MODEL'],
|
|
5
|
+
description: 'Run inference through WZRD. The server calls the AI provider, grades quality, ' +
|
|
6
|
+
'and returns an execution receipt. Use the execution_id with WZRD_REPORT to earn CCM.',
|
|
7
|
+
examples: [
|
|
8
|
+
[
|
|
9
|
+
{ name: '{{user1}}', content: { text: 'Run inference through WZRD: explain quicksort in Python' } },
|
|
10
|
+
{ name: '{{agentName}}', content: { text: 'Inference complete. Model: gemini-2.5-flash, quality: 0.85. execution_id: abc-123...' } },
|
|
11
|
+
],
|
|
12
|
+
],
|
|
13
|
+
validate: async () => true,
|
|
14
|
+
handler: async (runtime, message, _state, _opt, callback) => {
|
|
15
|
+
const content = message.content;
|
|
16
|
+
const prompt = content.prompt || content.text || '';
|
|
17
|
+
if (!prompt) {
|
|
18
|
+
await callback?.({ text: 'No prompt provided. Include a prompt or text to run inference.' });
|
|
19
|
+
return { success: false, error: 'No prompt' };
|
|
20
|
+
}
|
|
21
|
+
const taskType = content.task_type || 'chat';
|
|
22
|
+
const model = content.model;
|
|
23
|
+
const client = getWzrdClient(runtime);
|
|
24
|
+
try {
|
|
25
|
+
const result = await client.infer(prompt, model, taskType);
|
|
26
|
+
const text = `Inference complete.\n` +
|
|
27
|
+
`Model: ${result.executed_model} (${result.provider})\n` +
|
|
28
|
+
`Quality: ${result.quality_score.toFixed(2)}\n` +
|
|
29
|
+
`Latency: ${result.latency_ms}ms\n` +
|
|
30
|
+
`execution_id: ${result.execution_id}\n\n` +
|
|
31
|
+
`Response: ${result.response_preview}`;
|
|
32
|
+
await callback?.({ text });
|
|
33
|
+
return { success: true, data: result };
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
37
|
+
await callback?.({ text: `Inference failed: ${msg}` });
|
|
38
|
+
return { success: false, error: msg };
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WZRD_REPORT — Report a model pick with execution receipt for verified CCM rewards.
|
|
3
|
+
*
|
|
4
|
+
* Must be called after WZRD_INFER. Pass the execution_id from the infer result
|
|
5
|
+
* to get server-verified status and quality-weighted rewards.
|
|
6
|
+
*/
|
|
7
|
+
import type { Action } from '@elizaos/core';
|
|
8
|
+
export declare const reportAction: Action;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { getWzrdClient } from '../client-factory.js';
|
|
2
|
+
export const reportAction = {
|
|
3
|
+
name: 'WZRD_REPORT',
|
|
4
|
+
similes: ['WZRD_REPORT_OUTCOME', 'REPORT_MODEL_PICK', 'SUBMIT_SIGNAL'],
|
|
5
|
+
description: 'Report a model pick to WZRD with an execution_id from WZRD_INFER. ' +
|
|
6
|
+
'Verified reports earn CCM rewards with quality multiplier.',
|
|
7
|
+
examples: [
|
|
8
|
+
[
|
|
9
|
+
{ name: '{{user1}}', content: { text: 'Report the inference result to earn CCM' } },
|
|
10
|
+
{ name: '{{agentName}}', content: { text: 'Reported. Verification: verified, contribution #4521.' } },
|
|
11
|
+
],
|
|
12
|
+
],
|
|
13
|
+
validate: async () => true,
|
|
14
|
+
handler: async (runtime, message, _state, _opt, callback) => {
|
|
15
|
+
const content = message.content;
|
|
16
|
+
if (!content.execution_id || !content.model_id) {
|
|
17
|
+
await callback?.({
|
|
18
|
+
text: 'Missing required: execution_id and model_id. Run WZRD_INFER first to get an execution receipt.',
|
|
19
|
+
});
|
|
20
|
+
return { success: false, error: 'Missing execution_id or model_id' };
|
|
21
|
+
}
|
|
22
|
+
const client = getWzrdClient(runtime);
|
|
23
|
+
try {
|
|
24
|
+
const result = await client.report({
|
|
25
|
+
model_id: content.model_id,
|
|
26
|
+
execution_id: content.execution_id,
|
|
27
|
+
task_type: content.task_type,
|
|
28
|
+
quality_score: content.quality_score,
|
|
29
|
+
latency_ms: content.latency_ms,
|
|
30
|
+
});
|
|
31
|
+
const text = `Reported to WZRD.\n` +
|
|
32
|
+
`Verification: ${result.verification_state}\n` +
|
|
33
|
+
`Contribution: #${result.contribution_id}\n` +
|
|
34
|
+
`Pending CCM: ${(result.pending_ccm / 1e9).toFixed(2)}\n` +
|
|
35
|
+
`Pipeline: ${result.pipeline_state}`;
|
|
36
|
+
await callback?.({ text });
|
|
37
|
+
return { success: true, data: result };
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
41
|
+
await callback?.({ text: `Report failed: ${msg}` });
|
|
42
|
+
return { success: false, error: msg };
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { getWzrdClient } from '../client-factory.js';
|
|
2
|
+
export const rewardsAction = {
|
|
3
|
+
name: 'WZRD_REWARDS',
|
|
4
|
+
similes: ['WZRD_BALANCE', 'CHECK_REWARDS', 'MY_CCM'],
|
|
5
|
+
description: 'Check your pending CCM rewards, lifetime total, rank, and contribution count.',
|
|
6
|
+
examples: [
|
|
7
|
+
[
|
|
8
|
+
{ name: '{{user1}}', content: { text: 'How much CCM have I earned?' } },
|
|
9
|
+
{ name: '{{agentName}}', content: { text: 'Pending: 142.5 CCM. Lifetime: 326,000 CCM. Rank #3.' } },
|
|
10
|
+
],
|
|
11
|
+
],
|
|
12
|
+
validate: async () => true,
|
|
13
|
+
handler: async (runtime, _msg, _state, _opt, callback) => {
|
|
14
|
+
const client = getWzrdClient(runtime);
|
|
15
|
+
try {
|
|
16
|
+
const rewards = await client.getRewards();
|
|
17
|
+
const text = `WZRD Rewards:\n` +
|
|
18
|
+
`Pending: ${(rewards.pending_ccm / 1e9).toFixed(2)} CCM\n` +
|
|
19
|
+
`Lifetime: ${(rewards.total_rewarded_ccm / 1e9).toFixed(2)} CCM\n` +
|
|
20
|
+
`Contributions: ${rewards.contribution_count}\n` +
|
|
21
|
+
(rewards.rank ? `Rank: #${rewards.rank}` : 'Rank: unranked');
|
|
22
|
+
await callback?.({ text });
|
|
23
|
+
return { success: true, data: rewards };
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
27
|
+
await callback?.({ text: `Failed to fetch rewards: ${msg}` });
|
|
28
|
+
return { success: false, error: msg };
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { WzrdClient } from './client.js';
|
|
2
|
+
import type { IAgentRuntime } from '@elizaos/core';
|
|
3
|
+
export declare function getWzrdClient(runtime: IAgentRuntime): WzrdClient;
|
|
4
|
+
/** Reset client cache — useful for tests. */
|
|
5
|
+
export declare function clearClientCache(): void;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Extract keypair from ElizaOS runtime, return cached WzrdClient. */
|
|
2
|
+
import { Keypair } from '@solana/web3.js';
|
|
3
|
+
import { WzrdClient } from './client.js';
|
|
4
|
+
const cache = new Map();
|
|
5
|
+
export function getWzrdClient(runtime) {
|
|
6
|
+
const sk = runtime.getSetting('SOLANA_PRIVATE_KEY');
|
|
7
|
+
if (!sk)
|
|
8
|
+
throw new Error('SOLANA_PRIVATE_KEY not configured in ElizaOS runtime');
|
|
9
|
+
const kp = Keypair.fromSecretKey(Uint8Array.from(JSON.parse(String(sk))));
|
|
10
|
+
const pub = kp.publicKey.toBase58();
|
|
11
|
+
if (!cache.has(pub)) {
|
|
12
|
+
const apiUrl = runtime.getSetting('WZRD_API_URL');
|
|
13
|
+
cache.set(pub, new WzrdClient(kp, typeof apiUrl === 'string' ? apiUrl : undefined));
|
|
14
|
+
}
|
|
15
|
+
return cache.get(pub);
|
|
16
|
+
}
|
|
17
|
+
/** Reset client cache — useful for tests. */
|
|
18
|
+
export function clearClientCache() {
|
|
19
|
+
cache.clear();
|
|
20
|
+
}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,3 +1,90 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Standalone WZRD API client for ElizaOS agents.
|
|
3
|
+
* No external dependencies beyond @solana/web3.js for Ed25519 signing.
|
|
4
|
+
*
|
|
5
|
+
* Auth flow: challenge → sign → verify → Bearer token (24h TTL)
|
|
6
|
+
* Earn flow: infer → report(execution_id) → claim
|
|
7
|
+
*/
|
|
8
|
+
import { Keypair } from '@solana/web3.js';
|
|
9
|
+
export interface InferResult {
|
|
10
|
+
execution_id: string;
|
|
11
|
+
executed_model: string;
|
|
12
|
+
requested_model: string;
|
|
13
|
+
provider: string;
|
|
14
|
+
quality_score: number;
|
|
15
|
+
response_preview: string;
|
|
16
|
+
latency_ms: number;
|
|
17
|
+
cost_usd?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface ReportResult {
|
|
20
|
+
contribution_id: number;
|
|
21
|
+
verification_state: string;
|
|
22
|
+
lifetime_contributions: number;
|
|
23
|
+
pending_ccm: number;
|
|
24
|
+
pipeline_state: string;
|
|
25
|
+
idempotent: boolean;
|
|
26
|
+
provider_receipt_present: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface RewardsBalance {
|
|
29
|
+
pending_ccm: number;
|
|
30
|
+
total_rewarded_ccm: number;
|
|
31
|
+
rank: number | null;
|
|
32
|
+
contribution_count: number;
|
|
33
|
+
}
|
|
34
|
+
export interface ClaimResult {
|
|
35
|
+
tx_sig: string | null;
|
|
36
|
+
root_seq: number;
|
|
37
|
+
cumulative_total: number;
|
|
38
|
+
status: string;
|
|
39
|
+
}
|
|
40
|
+
export interface LeaderboardResult {
|
|
41
|
+
market_count: number;
|
|
42
|
+
total_tvl: number;
|
|
43
|
+
root: {
|
|
44
|
+
root_seq: number;
|
|
45
|
+
};
|
|
46
|
+
markets: Array<{
|
|
47
|
+
market_id: number;
|
|
48
|
+
metric: string;
|
|
49
|
+
platform: string;
|
|
50
|
+
velocity_ema: number;
|
|
51
|
+
multiplier_bps: number;
|
|
52
|
+
snapshot_count: number;
|
|
53
|
+
}>;
|
|
54
|
+
}
|
|
55
|
+
export declare class WzrdClient {
|
|
56
|
+
private keypair;
|
|
57
|
+
private apiUrl;
|
|
58
|
+
private token;
|
|
59
|
+
private tokenExpiry;
|
|
60
|
+
constructor(keypair: Keypair, apiUrl?: string);
|
|
61
|
+
get pubkey(): string;
|
|
62
|
+
/** Ed25519 challenge → sign → verify → Bearer token */
|
|
63
|
+
private authenticate;
|
|
64
|
+
/** Authenticated fetch helper */
|
|
65
|
+
private authedFetch;
|
|
66
|
+
/** Pick a model from the momentum signal — returns top model's channel_id */
|
|
67
|
+
pickModel(taskType?: string): Promise<string>;
|
|
68
|
+
/** Server-witnessed inference — WZRD calls the provider, grades quality */
|
|
69
|
+
infer(prompt: string, model?: string, taskType?: string): Promise<InferResult>;
|
|
70
|
+
/** Report model pick with execution_id for verified rewards */
|
|
71
|
+
report(params: {
|
|
72
|
+
model_id: string;
|
|
73
|
+
execution_id: string;
|
|
74
|
+
task_type?: string;
|
|
75
|
+
quality_score?: number;
|
|
76
|
+
latency_ms?: number;
|
|
77
|
+
}): Promise<ReportResult>;
|
|
78
|
+
/** Check pending + total rewards (flattens nested /v1/agent/earned response) */
|
|
79
|
+
getRewards(): Promise<RewardsBalance>;
|
|
80
|
+
/** Gasless CCM claim via server relay */
|
|
81
|
+
claimRelay(): Promise<ClaimResult>;
|
|
82
|
+
/** Public: fetch leaderboard (no auth) */
|
|
83
|
+
getLeaderboard(limit?: number): Promise<LeaderboardResult>;
|
|
84
|
+
/** Public: fetch claims status */
|
|
85
|
+
getClaimStatus(): Promise<{
|
|
86
|
+
cumulative_total: number;
|
|
87
|
+
claimed_total: number;
|
|
88
|
+
claimable: number;
|
|
89
|
+
}>;
|
|
90
|
+
}
|
package/dist/client.js
CHANGED
|
@@ -1,17 +1,134 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
1
|
+
import nacl from 'tweetnacl';
|
|
2
|
+
import bs58 from 'bs58';
|
|
3
|
+
const DEFAULT_API = 'https://api.twzrd.xyz';
|
|
4
|
+
export class WzrdClient {
|
|
5
|
+
keypair;
|
|
6
|
+
apiUrl;
|
|
7
|
+
token = null;
|
|
8
|
+
tokenExpiry = 0;
|
|
9
|
+
constructor(keypair, apiUrl) {
|
|
10
|
+
this.keypair = keypair;
|
|
11
|
+
this.apiUrl = apiUrl || DEFAULT_API;
|
|
12
|
+
}
|
|
13
|
+
get pubkey() {
|
|
14
|
+
return this.keypair.publicKey.toBase58();
|
|
15
|
+
}
|
|
16
|
+
/** Ed25519 challenge → sign → verify → Bearer token */
|
|
17
|
+
async authenticate() {
|
|
18
|
+
if (this.token && Date.now() < this.tokenExpiry)
|
|
19
|
+
return this.token;
|
|
20
|
+
// 1. Get challenge
|
|
21
|
+
const challengeRes = await fetch(`${this.apiUrl}/v1/agent/challenge?pubkey=${this.pubkey}`);
|
|
22
|
+
if (!challengeRes.ok)
|
|
23
|
+
throw new Error(`Challenge failed: ${challengeRes.status}`);
|
|
24
|
+
const { nonce } = await challengeRes.json();
|
|
25
|
+
// 2. Construct message locally (must match server's agent_auth_message format)
|
|
26
|
+
const message = `wzrd-agent-auth v1 | wallet:${this.pubkey} | nonce:${nonce}`;
|
|
27
|
+
const messageBytes = new TextEncoder().encode(message);
|
|
28
|
+
const signature = nacl.sign.detached(messageBytes, this.keypair.secretKey);
|
|
29
|
+
// 3. Verify — signature must be base58-encoded (Solana Signature format)
|
|
30
|
+
const verifyRes = await fetch(`${this.apiUrl}/v1/agent/verify`, {
|
|
31
|
+
method: 'POST',
|
|
32
|
+
headers: { 'Content-Type': 'application/json' },
|
|
33
|
+
body: JSON.stringify({
|
|
34
|
+
pubkey: this.pubkey,
|
|
35
|
+
nonce,
|
|
36
|
+
signature: bs58.encode(signature),
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
if (!verifyRes.ok)
|
|
40
|
+
throw new Error(`Verify failed: ${verifyRes.status}`);
|
|
41
|
+
const { token } = await verifyRes.json();
|
|
42
|
+
this.token = token;
|
|
43
|
+
this.tokenExpiry = Date.now() + 23 * 60 * 60 * 1000; // refresh 1h before 24h expiry
|
|
44
|
+
return token;
|
|
45
|
+
}
|
|
46
|
+
/** Authenticated fetch helper */
|
|
47
|
+
async authedFetch(path, init) {
|
|
48
|
+
const token = await this.authenticate();
|
|
49
|
+
const headers = new Headers(init?.headers);
|
|
50
|
+
headers.set('Authorization', `Bearer ${token}`);
|
|
51
|
+
headers.set('Content-Type', 'application/json');
|
|
52
|
+
return fetch(`${this.apiUrl}${path}`, { ...init, headers });
|
|
53
|
+
}
|
|
54
|
+
/** Pick a model from the momentum signal — returns top model's channel_id */
|
|
55
|
+
async pickModel(taskType) {
|
|
56
|
+
const res = await fetch(`${this.apiUrl}/v1/signals/momentum?limit=10&trending=true`);
|
|
57
|
+
if (!res.ok)
|
|
58
|
+
throw new Error(`Momentum fetch failed: ${res.status}`);
|
|
59
|
+
const data = await res.json();
|
|
60
|
+
if (!data.models?.length)
|
|
61
|
+
throw new Error('No models available in momentum feed');
|
|
62
|
+
// Return the top-ranked model's channel_id (e.g. "moonshotai/Kimi-K2.5")
|
|
63
|
+
return data.models[0].model;
|
|
64
|
+
}
|
|
65
|
+
/** Server-witnessed inference — WZRD calls the provider, grades quality */
|
|
66
|
+
async infer(prompt, model, taskType) {
|
|
67
|
+
const resolvedModel = model || await this.pickModel(taskType);
|
|
68
|
+
const res = await this.authedFetch('/v1/agent/infer', {
|
|
69
|
+
method: 'POST',
|
|
70
|
+
body: JSON.stringify({ model: resolvedModel, prompt, task_type: taskType || 'chat' }),
|
|
71
|
+
});
|
|
72
|
+
if (!res.ok) {
|
|
73
|
+
const body = await res.text().catch(() => '');
|
|
74
|
+
throw new Error(`Infer failed (${res.status}): ${body}`);
|
|
75
|
+
}
|
|
76
|
+
return res.json();
|
|
77
|
+
}
|
|
78
|
+
/** Report model pick with execution_id for verified rewards */
|
|
79
|
+
async report(params) {
|
|
80
|
+
const res = await this.authedFetch('/v1/agent/report', {
|
|
81
|
+
method: 'POST',
|
|
82
|
+
body: JSON.stringify(params),
|
|
83
|
+
});
|
|
84
|
+
if (!res.ok) {
|
|
85
|
+
const body = await res.text().catch(() => '');
|
|
86
|
+
throw new Error(`Report failed (${res.status}): ${body}`);
|
|
87
|
+
}
|
|
88
|
+
return res.json();
|
|
89
|
+
}
|
|
90
|
+
/** Check pending + total rewards (flattens nested /v1/agent/earned response) */
|
|
91
|
+
async getRewards() {
|
|
92
|
+
const res = await this.authedFetch('/v1/agent/earned');
|
|
93
|
+
if (!res.ok)
|
|
94
|
+
throw new Error(`Rewards check failed: ${res.status}`);
|
|
95
|
+
const data = await res.json();
|
|
96
|
+
const economy = data.economy;
|
|
97
|
+
const routing = data.routing;
|
|
98
|
+
return {
|
|
99
|
+
pending_ccm: Number(economy?.pending_ccm ?? 0),
|
|
100
|
+
total_rewarded_ccm: Number(economy?.earned_ccm ?? 0),
|
|
101
|
+
rank: null, // rank not in this endpoint
|
|
102
|
+
contribution_count: Number(routing?.lifetime_contributions ?? 0),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/** Gasless CCM claim via server relay */
|
|
106
|
+
async claimRelay() {
|
|
107
|
+
const res = await this.authedFetch(`/v1/claims/${this.pubkey}/relay`, {
|
|
108
|
+
method: 'POST',
|
|
109
|
+
});
|
|
110
|
+
if (!res.ok) {
|
|
111
|
+
const body = await res.text().catch(() => '');
|
|
112
|
+
throw new Error(`Claim relay failed (${res.status}): ${body}`);
|
|
113
|
+
}
|
|
114
|
+
return res.json();
|
|
115
|
+
}
|
|
116
|
+
/** Public: fetch leaderboard (no auth) */
|
|
117
|
+
async getLeaderboard(limit = 20) {
|
|
118
|
+
const res = await fetch(`${this.apiUrl}/v1/leaderboard?limit=${limit}`);
|
|
119
|
+
if (!res.ok)
|
|
120
|
+
throw new Error(`Leaderboard failed: ${res.status}`);
|
|
121
|
+
return res.json();
|
|
122
|
+
}
|
|
123
|
+
/** Public: fetch claims status */
|
|
124
|
+
async getClaimStatus() {
|
|
125
|
+
const res = await this.authedFetch(`/v1/claims/${this.pubkey}`);
|
|
126
|
+
if (!res.ok) {
|
|
127
|
+
if (res.status === 404)
|
|
128
|
+
return { cumulative_total: 0, claimed_total: 0, claimable: 0 };
|
|
129
|
+
throw new Error(`Claim status failed: ${res.status}`);
|
|
130
|
+
}
|
|
131
|
+
const data = await res.json();
|
|
132
|
+
return { ...data, claimable: data.cumulative_total - data.claimed_total };
|
|
133
|
+
}
|
|
17
134
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @wzrd_sol/eliza-plugin — WZRD
|
|
3
|
-
* Thin adapter wrapping @wzrd_sol/solana-agent-plugin's WzrdClient.
|
|
2
|
+
* @wzrd_sol/eliza-plugin — WZRD Earn Loop for ElizaOS
|
|
4
3
|
*
|
|
5
|
-
*
|
|
4
|
+
* Server-witnessed inference: infer → report → earn CCM → claim.
|
|
5
|
+
* No external dependencies beyond @solana/web3.js + tweetnacl.
|
|
6
|
+
*
|
|
7
|
+
* Config (runtime.getSetting):
|
|
8
|
+
* SOLANA_PRIVATE_KEY — required, JSON array of secret key bytes
|
|
9
|
+
* WZRD_API_URL — optional, defaults to https://api.twzrd.xyz
|
|
6
10
|
*/
|
|
7
11
|
import type { Plugin } from '@elizaos/core';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
12
|
+
import { inferAction } from './actions/infer.js';
|
|
13
|
+
import { reportAction } from './actions/report.js';
|
|
14
|
+
import { earnAction } from './actions/earn.js';
|
|
10
15
|
import { claimAction } from './actions/claim.js';
|
|
11
|
-
import {
|
|
12
|
-
import { velocityAction } from './actions/velocity.js';
|
|
16
|
+
import { rewardsAction } from './actions/rewards.js';
|
|
13
17
|
export declare const wzrdPlugin: Plugin;
|
|
14
18
|
export default wzrdPlugin;
|
|
15
|
-
export {
|
|
16
|
-
export { getWzrdClient } from './client.js';
|
|
19
|
+
export { earnAction, inferAction, reportAction, claimAction, rewardsAction };
|
|
20
|
+
export { getWzrdClient, clearClientCache } from './client-factory.js';
|
|
21
|
+
export { WzrdClient } from './client.js';
|
|
22
|
+
export type { InferResult, ReportResult, RewardsBalance, ClaimResult } from './client.js';
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { inferAction } from './actions/infer.js';
|
|
2
|
+
import { reportAction } from './actions/report.js';
|
|
3
|
+
import { earnAction } from './actions/earn.js';
|
|
3
4
|
import { claimAction } from './actions/claim.js';
|
|
4
|
-
import {
|
|
5
|
-
import { velocityAction } from './actions/velocity.js';
|
|
5
|
+
import { rewardsAction } from './actions/rewards.js';
|
|
6
6
|
export const wzrdPlugin = {
|
|
7
7
|
name: 'wzrd',
|
|
8
|
-
description: 'WZRD Liquid Attention Protocol —
|
|
9
|
-
'
|
|
10
|
-
actions: [
|
|
8
|
+
description: 'WZRD Liquid Attention Protocol — earn CCM on Solana via server-witnessed inference. ' +
|
|
9
|
+
'Infer through WZRD, report outcomes, claim rewards via gasless relay.',
|
|
10
|
+
actions: [earnAction, inferAction, reportAction, claimAction, rewardsAction],
|
|
11
11
|
};
|
|
12
12
|
export default wzrdPlugin;
|
|
13
|
-
export {
|
|
14
|
-
export { getWzrdClient } from './client.js';
|
|
13
|
+
export { earnAction, inferAction, reportAction, claimAction, rewardsAction };
|
|
14
|
+
export { getWzrdClient, clearClientCache } from './client-factory.js';
|
|
15
|
+
export { WzrdClient } from './client.js';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wzrd_sol/eliza-plugin",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "WZRD
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "WZRD earn loop for ElizaOS — server-witnessed inference, verified CCM rewards, gasless claims",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -14,12 +14,13 @@
|
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "tsc",
|
|
17
|
+
"test": "node --test dist/test/*.js",
|
|
17
18
|
"prepublishOnly": "npm run build"
|
|
18
19
|
},
|
|
19
20
|
"dependencies": {
|
|
20
|
-
"@
|
|
21
|
-
"
|
|
22
|
-
"
|
|
21
|
+
"@solana/web3.js": "^1.95.0",
|
|
22
|
+
"bs58": "^5.0.0",
|
|
23
|
+
"tweetnacl": "^1.0.3"
|
|
23
24
|
},
|
|
24
25
|
"peerDependencies": {
|
|
25
26
|
"@elizaos/core": "^1.0.0"
|
|
@@ -38,9 +39,11 @@
|
|
|
38
39
|
"eliza",
|
|
39
40
|
"elizaos",
|
|
40
41
|
"wzrd",
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
42
|
+
"ccm",
|
|
43
|
+
"inference",
|
|
44
|
+
"earn",
|
|
45
|
+
"ai-agent",
|
|
46
|
+
"mcp"
|
|
44
47
|
],
|
|
45
48
|
"license": "MIT",
|
|
46
49
|
"publishConfig": {
|
|
@@ -48,7 +51,7 @@
|
|
|
48
51
|
},
|
|
49
52
|
"repository": {
|
|
50
53
|
"type": "git",
|
|
51
|
-
"url": "git+https://github.com/twzrd-sol/wzrd-
|
|
54
|
+
"url": "git+https://github.com/twzrd-sol/wzrd-velocity.git",
|
|
52
55
|
"directory": "agents/eliza-plugin"
|
|
53
56
|
},
|
|
54
57
|
"files": [
|
package/dist/actions/deposit.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ELIZA Action: WZRD_DEPOSIT — deposit USDC into attention market, mint vLOFI (auth + keypair).
|
|
3
|
-
*
|
|
4
|
-
* Before depositing, agents can check expected returns via the APR endpoint:
|
|
5
|
-
* GET /v1/markets/:id/apr — returns base_apr_bps, attention_multiplier_bps,
|
|
6
|
-
* projected_total_apr_bps, tvl_usdc, and the yield formula.
|
|
7
|
-
* GET /v1/markets/:id/preview — returns projected_multiplier_bps and freshness_secs
|
|
8
|
-
* for a live multiplier snapshot before committing capital.
|
|
9
|
-
* Use WzrdClient.getMarketApr(marketId) or WzrdClient.getMarketPreview(marketId).
|
|
10
|
-
*/
|
|
11
|
-
import { Keypair, ComputeBudgetProgram, TransactionMessage, VersionedTransaction, Connection } from '@solana/web3.js';
|
|
12
|
-
export const depositAction = {
|
|
13
|
-
name: 'WZRD_DEPOSIT',
|
|
14
|
-
similes: ['DEPOSIT_USDC', 'BUY_ATTENTION', 'ENTER_MARKET'],
|
|
15
|
-
description: 'Deposit USDC into a WZRD attention market to mint vLOFI. Requires market_id and amount_usdc in message content.',
|
|
16
|
-
examples: [[
|
|
17
|
-
{ name: '{{user1}}', content: { text: 'Deposit 0.01 USDC into WZRD market 10' } },
|
|
18
|
-
{ name: '{{agentName}}', content: { text: 'Deposited 0.0100 USDC into Market #10. Tx: X7FG...' } },
|
|
19
|
-
]],
|
|
20
|
-
validate: async () => true,
|
|
21
|
-
handler: async (runtime, message, _state, _opt, callback) => {
|
|
22
|
-
const { market_id: marketId, amount_usdc: amountUsdc, priority_fee = 50_000 } = message.content;
|
|
23
|
-
if (!marketId || !amountUsdc || amountUsdc > 100) {
|
|
24
|
-
const text = !marketId || !amountUsdc
|
|
25
|
-
? 'Missing required: market_id and amount_usdc.' : 'Max 100 USDC per deposit.';
|
|
26
|
-
await callback?.({ text });
|
|
27
|
-
return { success: false, text };
|
|
28
|
-
}
|
|
29
|
-
const sk = runtime.getSetting('SOLANA_PRIVATE_KEY');
|
|
30
|
-
if (!sk)
|
|
31
|
-
throw new Error('SOLANA_PRIVATE_KEY not configured');
|
|
32
|
-
const kp = Keypair.fromSecretKey(Uint8Array.from(JSON.parse(String(sk))));
|
|
33
|
-
const payer = kp.publicKey;
|
|
34
|
-
const amt = BigInt(Math.round(amountUsdc * 1e6));
|
|
35
|
-
const rpcSetting = runtime.getSetting('SOLANA_RPC_URL');
|
|
36
|
-
const rpc = (typeof rpcSetting === 'string' ? rpcSetting : undefined) || 'https://api.mainnet-beta.solana.com';
|
|
37
|
-
const conn = new Connection(rpc, 'confirmed');
|
|
38
|
-
const sdk = await import('@wzrd_sol/sdk');
|
|
39
|
-
const vault = await sdk.fetchMarketVault(conn, marketId);
|
|
40
|
-
if (!vault) {
|
|
41
|
-
await callback?.({ text: `Market ${marketId} has no vault.` });
|
|
42
|
-
return { success: false };
|
|
43
|
-
}
|
|
44
|
-
const pos = await sdk.fetchOnChainPosition(conn, payer, marketId);
|
|
45
|
-
if (pos && pos.depositedAmount > 0n && !pos.settled) {
|
|
46
|
-
await callback?.({ text: `Position exists in market ${marketId}.` });
|
|
47
|
-
return { success: false };
|
|
48
|
-
}
|
|
49
|
-
const bal = await sdk.fetchTokenBalance(conn, sdk.getAta(payer, vault.depositMint, sdk.TOKEN_PROGRAM_ID));
|
|
50
|
-
if (bal < amt) {
|
|
51
|
-
await callback?.({ text: `Insufficient USDC.` });
|
|
52
|
-
return { success: false };
|
|
53
|
-
}
|
|
54
|
-
const ixs = await sdk.createDepositMarketIx(conn, payer, marketId, amt);
|
|
55
|
-
ixs.unshift(ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }), ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority_fee }));
|
|
56
|
-
const { blockhash, lastValidBlockHeight } = await conn.getLatestBlockhash('confirmed');
|
|
57
|
-
const tx = new VersionedTransaction(new TransactionMessage({ payerKey: payer, recentBlockhash: blockhash, instructions: ixs }).compileToV0Message());
|
|
58
|
-
tx.sign([kp]);
|
|
59
|
-
const sim = await conn.simulateTransaction(tx);
|
|
60
|
-
if (sim.value.err) {
|
|
61
|
-
await callback?.({ text: `Sim failed: ${JSON.stringify(sim.value.err)}` });
|
|
62
|
-
return { success: false };
|
|
63
|
-
}
|
|
64
|
-
const sig = await conn.sendTransaction(tx, { skipPreflight: true, maxRetries: 3 });
|
|
65
|
-
try {
|
|
66
|
-
await Promise.race([
|
|
67
|
-
conn.confirmTransaction({ signature: sig, blockhash, lastValidBlockHeight }, 'confirmed'),
|
|
68
|
-
new Promise((_, rej) => setTimeout(() => rej(new Error('Timeout')), 60_000)),
|
|
69
|
-
]);
|
|
70
|
-
}
|
|
71
|
-
catch {
|
|
72
|
-
await callback?.({ text: `Deposit sent, confirmation timed out. Tx: ${sig}` });
|
|
73
|
-
return { success: true, data: { signature: sig, timed_out: true } };
|
|
74
|
-
}
|
|
75
|
-
const text = `Deposited ${amountUsdc.toFixed(4)} USDC into Market #${marketId}. Tx: ${sig.slice(0, 16)}...`;
|
|
76
|
-
await callback?.({ text });
|
|
77
|
-
return { success: true, text, data: { signature: sig, market_id: marketId, amount_usdc: amountUsdc } };
|
|
78
|
-
},
|
|
79
|
-
};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { getWzrdClient } from '../client.js';
|
|
2
|
-
export const leaderboardAction = {
|
|
3
|
-
name: 'WZRD_LEADERBOARD',
|
|
4
|
-
similes: ['WZRD_MARKETS', 'WZRD_TRENDING', 'TOP_MODELS'],
|
|
5
|
-
description: 'Fetch the WZRD attention market leaderboard ranked by velocity EMA.',
|
|
6
|
-
examples: [[
|
|
7
|
-
{ name: '{{user1}}', content: { text: 'Show me the top WZRD markets' } },
|
|
8
|
-
{ name: '{{agentName}}', content: { text: 'Here are the top WZRD attention markets...' } },
|
|
9
|
-
]],
|
|
10
|
-
validate: async () => true,
|
|
11
|
-
handler: async (runtime, _msg, _state, _opt, callback) => {
|
|
12
|
-
const result = await getWzrdClient(runtime).getLeaderboard(10);
|
|
13
|
-
const lines = result.markets.map((m, i) => `${i + 1}. ${m.metric} — ${fmtV(m.velocity_ema)} velocity (${m.platform}) | ${m.multiplier_bps / 10_000}x | id=${m.market_id}`);
|
|
14
|
-
const text = `WZRD Leaderboard (${result.markets.length} markets, root_seq=${result.root.root_seq}):\n${lines.join('\n')}`;
|
|
15
|
-
await callback?.({ text });
|
|
16
|
-
return { success: true, data: result };
|
|
17
|
-
},
|
|
18
|
-
};
|
|
19
|
-
function fmtV(v) {
|
|
20
|
-
if (v >= 1e6)
|
|
21
|
-
return `${(v / 1e6).toFixed(1)}M`;
|
|
22
|
-
if (v >= 1e3)
|
|
23
|
-
return `${(v / 1e3).toFixed(0)}K`;
|
|
24
|
-
return v.toFixed(0);
|
|
25
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { getWzrdClient } from '../client.js';
|
|
2
|
-
export const portfolioAction = {
|
|
3
|
-
name: 'WZRD_PORTFOLIO',
|
|
4
|
-
similes: ['WZRD_POSITIONS', 'WZRD_BALANCE', 'MY_POSITIONS'],
|
|
5
|
-
description: 'Fetch your WZRD portfolio — open positions, USDC deposited, CCM earned.',
|
|
6
|
-
examples: [[
|
|
7
|
-
{ name: '{{user1}}', content: { text: 'Show my WZRD portfolio' } },
|
|
8
|
-
{ name: '{{agentName}}', content: { text: 'Your WZRD portfolio has 2 open positions...' } },
|
|
9
|
-
]],
|
|
10
|
-
validate: async () => true,
|
|
11
|
-
handler: async (runtime, _msg, _state, _opt, callback) => {
|
|
12
|
-
const p = await getWzrdClient(runtime).getPortfolio();
|
|
13
|
-
const open = p.positions.filter((x) => !x.is_settled);
|
|
14
|
-
const lines = open.map((x) => `Market #${x.market_id} (${x.metric}): ${(x.usdc_deposited / 1e6).toFixed(4)} USDC, ${(x.multiplier_bps / 1e4).toFixed(1)}x`);
|
|
15
|
-
const text = `Portfolio (${open.length} open):\n${lines.length ? lines.join('\n') : '(none)'}\n` +
|
|
16
|
-
`Total: ${(p.total_deposited_usdc / 1e6).toFixed(4)} USDC, ${(p.total_ccm_earned / 1e6).toFixed(4)} CCM`;
|
|
17
|
-
await callback?.({ text });
|
|
18
|
-
return { success: true, data: p };
|
|
19
|
-
},
|
|
20
|
-
};
|
package/dist/actions/velocity.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { getWzrdClient } from '../client.js';
|
|
2
|
-
export const velocityAction = {
|
|
3
|
-
name: 'WZRD_VELOCITY',
|
|
4
|
-
similes: ['WZRD_SIGNAL', 'ATTENTION_SIGNAL', 'VELOCITY_CHECK'],
|
|
5
|
-
description: 'Analyze velocity across WZRD markets. Classifies into BREAKOUT/MOMENTUM/EMERGING/STABLE/COOLING/WEAK.',
|
|
6
|
-
examples: [[
|
|
7
|
-
{ name: '{{user1}}', content: { text: 'Analyze WZRD velocity signals' } },
|
|
8
|
-
{ name: '{{agentName}}', content: { text: 'BREAKOUT: Qwen 3.5 35B (450K velocity, p95)...' } },
|
|
9
|
-
]],
|
|
10
|
-
validate: async () => true,
|
|
11
|
-
handler: async (runtime, _msg, _state, _opt, callback) => {
|
|
12
|
-
const { markets } = await getWzrdClient(runtime).getLeaderboard(50);
|
|
13
|
-
if (!markets.length) {
|
|
14
|
-
await callback?.({ text: 'No markets found.' });
|
|
15
|
-
return { success: true, data: { signals: [] } };
|
|
16
|
-
}
|
|
17
|
-
const sorted = [...markets].sort((a, b) => a.velocity_ema - b.velocity_ema);
|
|
18
|
-
const signals = markets.map((m) => {
|
|
19
|
-
const pct = ((sorted.findIndex((s) => s.market_id === m.market_id) + 1) / sorted.length) * 100;
|
|
20
|
-
return { market_id: m.market_id, metric: m.metric, platform: m.platform,
|
|
21
|
-
velocity_ema: m.velocity_ema, signal: classify(m, pct), percentile: Math.round(pct) };
|
|
22
|
-
});
|
|
23
|
-
const tiers = ['BREAKOUT', 'MOMENTUM', 'EMERGING', 'STABLE', 'COOLING', 'WEAK'];
|
|
24
|
-
const lines = [];
|
|
25
|
-
for (const t of tiers) {
|
|
26
|
-
const g = signals.filter((s) => s.signal === t);
|
|
27
|
-
if (!g.length)
|
|
28
|
-
continue;
|
|
29
|
-
lines.push(`${t}:`);
|
|
30
|
-
g.forEach((s) => lines.push(` ${s.metric} (${fmtV(s.velocity_ema)}, p${s.percentile}) [${s.platform}]`));
|
|
31
|
-
}
|
|
32
|
-
const median = sorted[Math.floor(sorted.length / 2)]?.velocity_ema ?? 0;
|
|
33
|
-
const text = `Velocity analysis (${markets.length} markets, median ${fmtV(median)}):\n${lines.join('\n')}`;
|
|
34
|
-
await callback?.({ text });
|
|
35
|
-
return { success: true, data: { signals, median_velocity: median } };
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
function classify(m, p) {
|
|
39
|
-
if (p >= 90)
|
|
40
|
-
return 'BREAKOUT';
|
|
41
|
-
if (p >= 70)
|
|
42
|
-
return 'MOMENTUM';
|
|
43
|
-
if (m.snapshot_count < 300 && p >= 50)
|
|
44
|
-
return 'EMERGING';
|
|
45
|
-
if (p >= 40)
|
|
46
|
-
return 'STABLE';
|
|
47
|
-
if (p >= 20)
|
|
48
|
-
return 'COOLING';
|
|
49
|
-
return 'WEAK';
|
|
50
|
-
}
|
|
51
|
-
function fmtV(v) {
|
|
52
|
-
if (v >= 1e6)
|
|
53
|
-
return `${(v / 1e6).toFixed(1)}M`;
|
|
54
|
-
if (v >= 1e3)
|
|
55
|
-
return `${(v / 1e3).toFixed(0)}K`;
|
|
56
|
-
return v.toFixed(0);
|
|
57
|
-
}
|