@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.
- package/README.md +43 -1
- package/dist/actions/claim.d.ts +11 -30
- package/dist/actions/claim.js +57 -60
- package/dist/actions/deposit.d.ts +15 -46
- package/dist/actions/deposit.js +148 -105
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/leaderboard.d.ts +14 -21
- package/dist/actions/leaderboard.js +26 -42
- package/dist/actions/portfolio.d.ts +5 -18
- package/dist/actions/portfolio.js +28 -28
- package/dist/actions/velocity.d.ts +15 -33
- package/dist/actions/velocity.js +67 -75
- package/dist/client.d.ts +18 -5
- package/dist/client.js +25 -8
- package/dist/index.d.ts +18 -273
- package/dist/index.js +53 -12
- package/dist/runtime.d.ts +16 -0
- package/dist/runtime.js +33 -0
- package/examples/hello-wzrd.mjs +24 -9
- package/package.json +14 -5
- package/src/actions/claim.ts +74 -71
- package/src/actions/deposit.ts +189 -157
- package/src/actions/index.ts +1 -0
- package/src/actions/leaderboard.ts +40 -57
- package/src/actions/portfolio.ts +41 -39
- package/src/actions/velocity.ts +84 -91
- package/src/client.ts +42 -11
- package/src/index.ts +70 -12
- package/src/runtime.ts +60 -0
- package/type-smoke-Cvxu.ts +8 -0
package/src/actions/claim.ts
CHANGED
|
@@ -7,9 +7,73 @@
|
|
|
7
7
|
* Flow: check claimable amount → relay claim → receive CCM.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import type { Action, SolanaAgentKit } from 'solana-agent-kit';
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
import { formatCcm, getClaimsData, getClientForAgent } from '../runtime.js';
|
|
14
|
+
|
|
15
|
+
export const claimSchema = z.object({
|
|
16
|
+
execute: z.boolean().optional(),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export async function claimHandler(
|
|
20
|
+
agent: SolanaAgentKit,
|
|
21
|
+
input: z.infer<typeof claimSchema>,
|
|
22
|
+
): Promise<Record<string, unknown>> {
|
|
23
|
+
const claims = await getClaimsData(agent);
|
|
24
|
+
const claimable = claims.cumulative_total - claims.claimed_total;
|
|
25
|
+
|
|
26
|
+
if (claimable <= 0) {
|
|
27
|
+
return {
|
|
28
|
+
success: true,
|
|
29
|
+
text:
|
|
30
|
+
`No CCM to claim. Cumulative: ${claims.cumulative_total}, ` +
|
|
31
|
+
`already claimed: ${claims.claimed_total}. ` +
|
|
32
|
+
`Deposit into markets and wait for the next scoring cycle.`,
|
|
33
|
+
data: claims,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (input.execute === false) {
|
|
38
|
+
return {
|
|
39
|
+
success: true,
|
|
40
|
+
text:
|
|
41
|
+
`${formatCcm(claimable)} CCM claimable ` +
|
|
42
|
+
`(${formatCcm(claims.cumulative_total)} cumulative, ` +
|
|
43
|
+
`${formatCcm(claims.claimed_total)} claimed). ` +
|
|
44
|
+
`Root seq: ${claims.root_seq}. Call with execute=true to claim.`,
|
|
45
|
+
data: claims,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const result = await getClientForAgent(agent).claimRelay();
|
|
50
|
+
|
|
51
|
+
if (result.status === 'already_claimed') {
|
|
52
|
+
return {
|
|
53
|
+
success: true,
|
|
54
|
+
text:
|
|
55
|
+
`Already claimed through root ${result.root_seq}. ` +
|
|
56
|
+
`Claimed total: ${formatCcm(result.claimed_total ?? claims.claimed_total)} CCM.`,
|
|
57
|
+
data: result,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
success: true,
|
|
63
|
+
text:
|
|
64
|
+
`Claimed CCM via gasless relay. ` +
|
|
65
|
+
`Cumulative total: ${formatCcm(result.cumulative_total)}. ` +
|
|
66
|
+
`Root seq: ${result.root_seq}. ` +
|
|
67
|
+
`Tx: ${result.tx_sig?.slice(0, 16)}...`,
|
|
68
|
+
data: {
|
|
69
|
+
...result,
|
|
70
|
+
claimable_before: claimable,
|
|
71
|
+
explorer: result.tx_sig ? `https://solscan.io/tx/${result.tx_sig}` : null,
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const CLAIM_ACTION: Action = {
|
|
13
77
|
name: 'wzrd_claim',
|
|
14
78
|
similes: ['wzrd_harvest', 'claim_ccm', 'redeem_rewards', 'collect_yield'],
|
|
15
79
|
description:
|
|
@@ -22,77 +86,16 @@ export const CLAIM_ACTION = {
|
|
|
22
86
|
examples: [
|
|
23
87
|
[
|
|
24
88
|
{
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
content: {
|
|
31
|
-
text: 'Claimed 100,000 CCM via gasless relay. ' +
|
|
32
|
-
'Cumulative total: 250,000 CCM. Tx: 2VKz...J39c',
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
],
|
|
36
|
-
[
|
|
37
|
-
{
|
|
38
|
-
user: 'user',
|
|
39
|
-
content: { text: 'How much CCM can I claim?' },
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
user: 'assistant',
|
|
43
|
-
content: {
|
|
44
|
-
text: 'You have 100,000 CCM claimable (250,000 cumulative, 150,000 already claimed). ' +
|
|
45
|
-
'Shall I claim it now?',
|
|
89
|
+
input: { execute: false },
|
|
90
|
+
output: {
|
|
91
|
+
success: true,
|
|
92
|
+
claimable: 100000,
|
|
93
|
+
cumulative_total: 250000,
|
|
46
94
|
},
|
|
95
|
+
explanation: 'Check how much CCM is available before triggering a relay claim.',
|
|
47
96
|
},
|
|
48
97
|
],
|
|
49
98
|
],
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
handler: async (client: WzrdClient, params: { execute?: boolean }) => {
|
|
54
|
-
// 1. Check claimable amount
|
|
55
|
-
const claims = await client.getClaims();
|
|
56
|
-
const claimable = claims.cumulative_total - claims.claimed_total;
|
|
57
|
-
|
|
58
|
-
if (claimable <= 0) {
|
|
59
|
-
return {
|
|
60
|
-
success: true,
|
|
61
|
-
text:
|
|
62
|
-
`No CCM to claim. Cumulative: ${claims.cumulative_total}, ` +
|
|
63
|
-
`already claimed: ${claims.claimed_total}. ` +
|
|
64
|
-
`Deposit into markets and wait for the next scoring cycle.`,
|
|
65
|
-
data: claims,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// 2. If just checking (not executing), report the amount
|
|
70
|
-
if (params.execute === false) {
|
|
71
|
-
return {
|
|
72
|
-
success: true,
|
|
73
|
-
text:
|
|
74
|
-
`${claimable} CCM claimable ` +
|
|
75
|
-
`(${claims.cumulative_total} cumulative, ${claims.claimed_total} claimed). ` +
|
|
76
|
-
`Root seq: ${claims.root_seq}. Call with execute=true to claim.`,
|
|
77
|
-
data: claims,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// 3. Execute gasless relay claim
|
|
82
|
-
const result = await client.claimRelay();
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
success: true,
|
|
86
|
-
text:
|
|
87
|
-
`Claimed CCM via gasless relay. ` +
|
|
88
|
-
`Cumulative total: ${result.cumulative_total}. ` +
|
|
89
|
-
`Root seq: ${result.root_seq}. ` +
|
|
90
|
-
`Tx: ${result.tx_sig.slice(0, 16)}...`,
|
|
91
|
-
data: {
|
|
92
|
-
...result,
|
|
93
|
-
claimable_before: claimable,
|
|
94
|
-
explorer: `https://solscan.io/tx/${result.tx_sig}`,
|
|
95
|
-
},
|
|
96
|
-
};
|
|
97
|
-
},
|
|
99
|
+
schema: claimSchema,
|
|
100
|
+
handler: async (agent, input) => claimHandler(agent, claimSchema.parse(input)),
|
|
98
101
|
};
|
package/src/actions/deposit.ts
CHANGED
|
@@ -9,189 +9,221 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
|
-
Connection,
|
|
13
|
-
Keypair,
|
|
14
12
|
ComputeBudgetProgram,
|
|
15
13
|
TransactionMessage,
|
|
16
14
|
VersionedTransaction,
|
|
17
15
|
} from '@solana/web3.js';
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
fetchOnChainPosition,
|
|
21
|
-
fetchTokenBalance,
|
|
22
|
-
getAta,
|
|
23
|
-
TOKEN_PROGRAM_ID,
|
|
24
|
-
} from '@wzrd_sol/sdk';
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import type { Action, SolanaAgentKit } from 'solana-agent-kit';
|
|
25
18
|
|
|
26
|
-
import
|
|
19
|
+
import { formatUsdc, getWalletPublicKey } from '../runtime.js';
|
|
27
20
|
|
|
28
21
|
const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
|
|
29
22
|
const CONFIRM_TIMEOUT_MS = 60_000;
|
|
30
23
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
24
|
+
type WzrdSdk = typeof import('@wzrd_sol/sdk');
|
|
25
|
+
|
|
26
|
+
export const depositSchema = z.object({
|
|
27
|
+
market_id: z.number().int().min(1),
|
|
28
|
+
amount_usdc: z.number().positive().max(100),
|
|
29
|
+
priority_fee: z.number().int().positive().optional(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export async function depositHandler(
|
|
33
|
+
agent: SolanaAgentKit,
|
|
34
|
+
input: z.infer<typeof depositSchema>,
|
|
35
|
+
): Promise<Record<string, unknown>> {
|
|
36
|
+
const { market_id, amount_usdc, priority_fee = 50_000 } = input;
|
|
37
|
+
const connection = agent.connection;
|
|
38
|
+
const payer = getWalletPublicKey(agent);
|
|
39
|
+
const amountNative = BigInt(Math.round(amount_usdc * 1_000_000));
|
|
40
|
+
const t0 = Date.now();
|
|
41
|
+
const sdk = await loadSdk();
|
|
42
|
+
const {
|
|
43
|
+
createDepositMarketIx,
|
|
44
|
+
fetchMarketVault,
|
|
45
|
+
fetchOnChainPosition,
|
|
46
|
+
fetchTokenBalance,
|
|
47
|
+
getAta,
|
|
48
|
+
TOKEN_PROGRAM_ID,
|
|
49
|
+
} = sdk;
|
|
50
|
+
|
|
51
|
+
const vault = await fetchMarketVault(connection, market_id);
|
|
52
|
+
if (!vault) {
|
|
53
|
+
return {
|
|
54
|
+
success: false,
|
|
55
|
+
text:
|
|
56
|
+
`Market ${market_id} is listed but does not have an on-chain vault yet. ` +
|
|
57
|
+
'Pick a market with an initialized vault before depositing.',
|
|
58
|
+
data: { market_id, reason: 'missing_vault' },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
36
61
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
62
|
+
const existing = await fetchOnChainPosition(connection, payer, market_id);
|
|
63
|
+
if (existing && existing.depositedAmount > 0n && !existing.settled) {
|
|
64
|
+
return {
|
|
65
|
+
success: false,
|
|
66
|
+
text:
|
|
67
|
+
`Position already exists in market ${market_id} ` +
|
|
68
|
+
`(${formatUsdc(existing.depositedAmount)} USDC deposited). Cannot double-deposit.`,
|
|
69
|
+
data: { market_id, deposited_amount: existing.depositedAmount.toString() },
|
|
70
|
+
};
|
|
71
|
+
}
|
|
47
72
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
'Received 10,000 vLOFI. Tx: X7FG...Cziv (772ms, 50687 CU)',
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
],
|
|
62
|
-
[
|
|
63
|
-
{
|
|
64
|
-
user: 'user',
|
|
65
|
-
content: { text: 'Invest in the highest velocity market' },
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
user: 'assistant',
|
|
69
|
-
content: {
|
|
70
|
-
text: 'Looking up leaderboard... Top market is #16 (Qwen 3.5 35B) ' +
|
|
71
|
-
'with 804K velocity. Depositing 0.01 USDC...',
|
|
72
|
-
},
|
|
73
|
+
const usdcAta = getAta(payer, vault.depositMint, TOKEN_PROGRAM_ID);
|
|
74
|
+
const usdcBalance = await fetchTokenBalance(connection, usdcAta);
|
|
75
|
+
if (usdcBalance < amountNative) {
|
|
76
|
+
return {
|
|
77
|
+
success: false,
|
|
78
|
+
text: `Insufficient USDC: have ${formatUsdc(usdcBalance)}, need ${amount_usdc.toFixed(4)}`,
|
|
79
|
+
data: {
|
|
80
|
+
market_id,
|
|
81
|
+
required_native: amountNative.toString(),
|
|
82
|
+
balance_native: usdcBalance.toString(),
|
|
73
83
|
},
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
validate: async (_client: WzrdClient, params: DepositParams) => {
|
|
78
|
-
if (!params.market_id || params.market_id < 1) return false;
|
|
79
|
-
if (!params.amount_usdc || params.amount_usdc <= 0) return false;
|
|
80
|
-
if (params.amount_usdc > 100) return false; // safety cap
|
|
81
|
-
return true;
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
handler: async (
|
|
85
|
-
_client: WzrdClient,
|
|
86
|
-
params: DepositParams,
|
|
87
|
-
connection: Connection,
|
|
88
|
-
wallet: Keypair,
|
|
89
|
-
) => {
|
|
90
|
-
const { market_id, amount_usdc, priority_fee = 50_000 } = params;
|
|
91
|
-
const amountNative = BigInt(Math.round(amount_usdc * 1_000_000));
|
|
92
|
-
const t0 = Date.now();
|
|
93
|
-
|
|
94
|
-
// 1. Idempotency — check existing position
|
|
95
|
-
const existing = await fetchOnChainPosition(
|
|
96
|
-
connection,
|
|
97
|
-
wallet.publicKey,
|
|
98
|
-
market_id,
|
|
99
|
-
);
|
|
100
|
-
if (existing && existing.depositedAmount > 0n && !existing.settled) {
|
|
101
|
-
return {
|
|
102
|
-
success: false,
|
|
103
|
-
text: `Position already exists in market ${market_id} ` +
|
|
104
|
-
`(${existing.depositedAmount} deposited). Cannot double-deposit.`,
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// 2. Balance checks
|
|
109
|
-
const { PublicKey } = await import('@solana/web3.js');
|
|
110
|
-
const usdcMint = new PublicKey(USDC_MINT);
|
|
111
|
-
const usdcAta = getAta(wallet.publicKey, usdcMint, TOKEN_PROGRAM_ID);
|
|
112
|
-
const usdcBalance = await fetchTokenBalance(connection, usdcAta);
|
|
113
|
-
if (usdcBalance < amountNative) {
|
|
114
|
-
return {
|
|
115
|
-
success: false,
|
|
116
|
-
text: `Insufficient USDC: have ${Number(usdcBalance) / 1e6}, need ${amount_usdc}`,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const solBalance = await connection.getBalance(wallet.publicKey);
|
|
121
|
-
if (solBalance < 10_000) {
|
|
122
|
-
return {
|
|
123
|
-
success: false,
|
|
124
|
-
text: `Insufficient SOL for tx fees: ${solBalance} lamports`,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
127
86
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
await connection.getLatestBlockhash('confirmed');
|
|
144
|
-
const message = new TransactionMessage({
|
|
145
|
-
payerKey: wallet.publicKey,
|
|
146
|
-
recentBlockhash: blockhash,
|
|
147
|
-
instructions: ixs,
|
|
148
|
-
}).compileToV0Message();
|
|
149
|
-
|
|
150
|
-
const tx = new VersionedTransaction(message);
|
|
151
|
-
tx.sign([wallet]);
|
|
152
|
-
|
|
153
|
-
const sim = await connection.simulateTransaction(tx);
|
|
154
|
-
if (sim.value.err) {
|
|
87
|
+
const solBalance = await connection.getBalance(payer);
|
|
88
|
+
if (solBalance < 10_000) {
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
text: `Insufficient SOL for tx fees: ${solBalance} lamports`,
|
|
92
|
+
data: { market_id, sol_balance_lamports: solBalance },
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let ixs;
|
|
97
|
+
try {
|
|
98
|
+
ixs = await createDepositMarketIx(connection, payer, market_id, amountNative);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
101
|
+
if (message.includes('MarketVault not found')) {
|
|
155
102
|
return {
|
|
156
103
|
success: false,
|
|
157
|
-
text:
|
|
158
|
-
|
|
104
|
+
text:
|
|
105
|
+
`Market ${market_id} does not have a live vault yet. ` +
|
|
106
|
+
'Choose a depositable market and retry.',
|
|
107
|
+
data: { market_id, reason: 'missing_vault' },
|
|
159
108
|
};
|
|
160
109
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
ixs.unshift(
|
|
114
|
+
ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }),
|
|
115
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority_fee }),
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const { blockhash, lastValidBlockHeight } =
|
|
119
|
+
await connection.getLatestBlockhash('confirmed');
|
|
120
|
+
const message = new TransactionMessage({
|
|
121
|
+
payerKey: payer,
|
|
122
|
+
recentBlockhash: blockhash,
|
|
123
|
+
instructions: ixs,
|
|
124
|
+
}).compileToV0Message();
|
|
125
|
+
|
|
126
|
+
const tx = new VersionedTransaction(message);
|
|
127
|
+
const signedTx = await agent.wallet.signTransaction(tx);
|
|
128
|
+
|
|
129
|
+
const sim = await connection.simulateTransaction(signedTx);
|
|
130
|
+
if (sim.value.err) {
|
|
131
|
+
return {
|
|
132
|
+
success: false,
|
|
133
|
+
text: `Simulation failed: ${JSON.stringify(sim.value.err)}`,
|
|
134
|
+
data: { market_id, logs: sim.value.logs?.slice(-5) ?? [] },
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const { signature } = await agent.wallet.signAndSendTransaction(tx, {
|
|
139
|
+
skipPreflight: true,
|
|
140
|
+
maxRetries: 3,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const confirmed = connection.confirmTransaction(
|
|
144
|
+
{ signature, blockhash, lastValidBlockHeight },
|
|
145
|
+
'confirmed',
|
|
146
|
+
);
|
|
147
|
+
const timeout = new Promise<never>((_, reject) =>
|
|
148
|
+
setTimeout(
|
|
149
|
+
() => reject(new Error(`Timeout after ${CONFIRM_TIMEOUT_MS}ms`)),
|
|
150
|
+
CONFIRM_TIMEOUT_MS,
|
|
151
|
+
),
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
try {
|
|
179
155
|
await Promise.race([confirmed, timeout]);
|
|
180
|
-
|
|
181
|
-
|
|
156
|
+
} catch (error) {
|
|
182
157
|
return {
|
|
183
158
|
success: true,
|
|
184
159
|
text:
|
|
185
|
-
`
|
|
186
|
-
`Tx: ${signature
|
|
160
|
+
`Deposit transaction sent for Market #${market_id}, but confirmation timed out. ` +
|
|
161
|
+
`Tx: ${signature}`,
|
|
187
162
|
data: {
|
|
188
163
|
signature,
|
|
189
164
|
market_id,
|
|
190
165
|
amount_usdc,
|
|
191
|
-
|
|
192
|
-
elapsed_ms: elapsed,
|
|
166
|
+
timed_out: true,
|
|
193
167
|
explorer: `https://solscan.io/tx/${signature}`,
|
|
194
168
|
},
|
|
195
169
|
};
|
|
196
|
-
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const elapsed = Date.now() - t0;
|
|
173
|
+
return {
|
|
174
|
+
success: true,
|
|
175
|
+
text:
|
|
176
|
+
`Deposited ${amount_usdc.toFixed(4)} USDC into Market #${market_id}. ` +
|
|
177
|
+
`Tx: ${signature.slice(0, 16)}... (${elapsed}ms, ${sim.value.unitsConsumed} CU)`,
|
|
178
|
+
data: {
|
|
179
|
+
signature,
|
|
180
|
+
market_id,
|
|
181
|
+
amount_usdc,
|
|
182
|
+
cu_used: sim.value.unitsConsumed,
|
|
183
|
+
elapsed_ms: elapsed,
|
|
184
|
+
explorer: `https://solscan.io/tx/${signature}`,
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function loadSdk(): Promise<WzrdSdk> {
|
|
190
|
+
try {
|
|
191
|
+
return await import('@wzrd_sol/sdk');
|
|
192
|
+
} catch (error) {
|
|
193
|
+
const fallbackUrl = new URL('../../../../sdk/dist/index.js', import.meta.url);
|
|
194
|
+
try {
|
|
195
|
+
return (await import(fallbackUrl.href)) as WzrdSdk;
|
|
196
|
+
} catch {
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export const DEPOSIT_ACTION: Action = {
|
|
203
|
+
name: 'wzrd_deposit',
|
|
204
|
+
similes: ['wzrd_invest', 'deposit_usdc', 'buy_attention', 'enter_market'],
|
|
205
|
+
description:
|
|
206
|
+
'Deposit USDC into a WZRD attention market to mint vLOFI tokens. ' +
|
|
207
|
+
'vLOFI represents your position in the market. As the underlying AI model ' +
|
|
208
|
+
'gains attention (downloads, stars), your multiplier increases and you earn ' +
|
|
209
|
+
'more CCM on each merkle root publication. ' +
|
|
210
|
+
'Requires: market_id (from leaderboard) and amount_usdc (e.g., 0.01). ' +
|
|
211
|
+
'The deposit is atomic — USDC is transferred and vLOFI is minted in one transaction.',
|
|
212
|
+
|
|
213
|
+
examples: [
|
|
214
|
+
[
|
|
215
|
+
{
|
|
216
|
+
input: { market_id: 10, amount_usdc: 0.01 },
|
|
217
|
+
output: {
|
|
218
|
+
success: true,
|
|
219
|
+
market_id: 10,
|
|
220
|
+
amount_usdc: 0.01,
|
|
221
|
+
signature: 'X7FG...',
|
|
222
|
+
},
|
|
223
|
+
explanation: 'Deposit 0.01 USDC into a known vaulted market and mint vLOFI.',
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
],
|
|
227
|
+
schema: depositSchema,
|
|
228
|
+
handler: async (agent, input) => depositHandler(agent, depositSchema.parse(input)),
|
|
197
229
|
};
|
package/src/actions/index.ts
CHANGED
|
@@ -5,9 +5,39 @@
|
|
|
5
5
|
* No auth required. Permissionless read.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import type { Action, SolanaAgentKit } from 'solana-agent-kit';
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
import { formatVelocity, getLeaderboardData } from '../runtime.js';
|
|
12
|
+
|
|
13
|
+
export const leaderboardSchema = z.object({
|
|
14
|
+
limit: z.number().int().min(1).max(50).optional(),
|
|
15
|
+
platform: z.string().min(1).optional(),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export async function leaderboardHandler(
|
|
19
|
+
agent: SolanaAgentKit,
|
|
20
|
+
input: z.infer<typeof leaderboardSchema>,
|
|
21
|
+
): Promise<Record<string, unknown>> {
|
|
22
|
+
const data = await getLeaderboardData(agent, input);
|
|
23
|
+
|
|
24
|
+
const lines = data.markets.map(
|
|
25
|
+
(m, i) =>
|
|
26
|
+
`${i + 1}. ${m.metric} — ${formatVelocity(m.velocity_ema)} velocity ` +
|
|
27
|
+
`(${m.platform}) | ${m.multiplier_bps / 10000}x multiplier | ` +
|
|
28
|
+
`${m.position_count} positions | market_id=${m.market_id}`,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
success: true,
|
|
33
|
+
text:
|
|
34
|
+
`WZRD Leaderboard (${data.markets.length} markets, ` +
|
|
35
|
+
`root_seq=${data.root.root_seq}):\n${lines.join('\n')}`,
|
|
36
|
+
data,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const LEADERBOARD_ACTION: Action = {
|
|
11
41
|
name: 'wzrd_leaderboard',
|
|
12
42
|
similes: ['wzrd_markets', 'wzrd_trending', 'attention_markets', 'top_models'],
|
|
13
43
|
description:
|
|
@@ -19,63 +49,16 @@ export const LEADERBOARD_ACTION = {
|
|
|
19
49
|
examples: [
|
|
20
50
|
[
|
|
21
51
|
{
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
content: {
|
|
28
|
-
text: 'Here are the top WZRD markets by velocity:\n' +
|
|
29
|
-
'1. Qwen 3.5 35B — 804K velocity (HuggingFace)\n' +
|
|
30
|
-
'2. Qwen 3.5 9B — 769K velocity (HuggingFace)\n' +
|
|
31
|
-
'3. Ollama — 104K velocity (GitHub)',
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
],
|
|
35
|
-
[
|
|
36
|
-
{
|
|
37
|
-
user: 'user',
|
|
38
|
-
content: { text: 'What HuggingFace models are trending?' },
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
user: 'assistant',
|
|
42
|
-
content: {
|
|
43
|
-
text: 'Top HuggingFace models by attention velocity: ...',
|
|
52
|
+
input: { limit: 3 },
|
|
53
|
+
output: {
|
|
54
|
+
success: true,
|
|
55
|
+
root_seq: 1549,
|
|
56
|
+
markets: ['#16 Qwen 3.5 35B', '#14 Qwen 3.5 9B', '#10 Qwen 2.5 72B'],
|
|
44
57
|
},
|
|
58
|
+
explanation: 'Fetch the top three WZRD markets without requiring auth.',
|
|
45
59
|
},
|
|
46
60
|
],
|
|
47
61
|
],
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
handler: async (
|
|
52
|
-
client: WzrdClient,
|
|
53
|
-
params: { limit?: number; platform?: string },
|
|
54
|
-
) => {
|
|
55
|
-
const data = await client.getLeaderboard(
|
|
56
|
-
params.limit ?? 10,
|
|
57
|
-
params.platform,
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
const lines = data.markets.map(
|
|
61
|
-
(m: WzrdMarket, i: number) =>
|
|
62
|
-
`${i + 1}. ${m.metric} — ${formatVelocity(m.velocity_ema)} velocity ` +
|
|
63
|
-
`(${m.platform}) | ${m.multiplier_bps / 10000}x multiplier | ` +
|
|
64
|
-
`${m.position_count} positions | market_id=${m.market_id}`,
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
success: true,
|
|
69
|
-
text:
|
|
70
|
-
`WZRD Leaderboard (${data.markets.length} markets, ` +
|
|
71
|
-
`root_seq=${data.root.root_seq}):\n${lines.join('\n')}`,
|
|
72
|
-
data,
|
|
73
|
-
};
|
|
74
|
-
},
|
|
62
|
+
schema: leaderboardSchema,
|
|
63
|
+
handler: async (agent, input) => leaderboardHandler(agent, leaderboardSchema.parse(input)),
|
|
75
64
|
};
|
|
76
|
-
|
|
77
|
-
function formatVelocity(v: number): string {
|
|
78
|
-
if (v >= 1_000_000) return `${(v / 1_000_000).toFixed(1)}M`;
|
|
79
|
-
if (v >= 1_000) return `${(v / 1_000).toFixed(0)}K`;
|
|
80
|
-
return v.toFixed(0);
|
|
81
|
-
}
|