@indigoprotocol/openclaw-indigo 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -79
- package/openclaw.plugin.json +4 -19
- package/package.json +17 -7
- package/src/index.ts +605 -60
- package/SKILL.md +0 -65
- package/src/alerts/cdp-alerts.ts +0 -38
- package/src/alerts/index.ts +0 -10
- package/src/alerts/price-alerts.ts +0 -44
- package/src/alerts/staking-alerts.ts +0 -36
- package/src/commands/cdp.ts +0 -58
- package/src/commands/index.ts +0 -11
- package/src/commands/portfolio.ts +0 -38
- package/src/commands/price.ts +0 -33
- package/src/commands/staking.ts +0 -38
- package/src/formatters/cdp-formatter.ts +0 -90
- package/src/formatters/index.ts +0 -9
- package/src/formatters/portfolio-formatter.ts +0 -91
- package/src/formatters/price-formatter.ts +0 -74
package/src/index.ts
CHANGED
|
@@ -1,69 +1,614 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import axios, { type AxiosInstance } from "axios";
|
|
3
|
+
import {
|
|
4
|
+
markdownAdapter,
|
|
5
|
+
formatPrice,
|
|
6
|
+
formatPriceList,
|
|
7
|
+
formatCDPList,
|
|
8
|
+
formatCDPHealth,
|
|
9
|
+
formatStabilityList,
|
|
10
|
+
formatStakingList,
|
|
11
|
+
formatAPRList,
|
|
12
|
+
formatTVL,
|
|
13
|
+
formatError,
|
|
14
|
+
type PriceData,
|
|
15
|
+
type CDPData,
|
|
16
|
+
type CDPHealthData,
|
|
17
|
+
type StabilityAccountData,
|
|
18
|
+
type StakingPositionData,
|
|
19
|
+
type APRData,
|
|
20
|
+
type TVLData,
|
|
21
|
+
type FormatAdapter,
|
|
22
|
+
} from "@indigoprotocol/shared";
|
|
23
|
+
|
|
24
|
+
const DEFAULT_BASE_URL = "https://analytics.indigoprotocol.io/api/v1";
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Helpers
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
function text(value: string) {
|
|
31
|
+
return { content: [{ type: "text" as const, text: value }] };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function errText(adapter: FormatAdapter, message: string, code?: string) {
|
|
35
|
+
return text(formatError(adapter, { message, code }));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Data mapping helpers — map indexer responses to shared types
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
function mapAssetToPrice(asset: Record<string, unknown>): PriceData {
|
|
43
|
+
return {
|
|
44
|
+
asset: String(asset.name ?? asset.asset ?? ""),
|
|
45
|
+
price: Number(asset.price ?? 0),
|
|
46
|
+
change24h: asset.change24h != null ? Number(asset.change24h) : undefined,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function mapLoanToCDP(loan: Record<string, unknown>): CDPData {
|
|
51
|
+
return {
|
|
52
|
+
id: String(loan.id ?? loan.nftId ?? ""),
|
|
53
|
+
owner: String(loan.owner ?? ""),
|
|
54
|
+
asset: String(loan.asset ?? loan.iasset ?? ""),
|
|
55
|
+
collateral: Number(loan.collateral ?? loan.adaAmount ?? 0),
|
|
56
|
+
minted: Number(loan.minted ?? loan.mintedAmount ?? 0),
|
|
57
|
+
ratio: Number(loan.ratio ?? loan.collateralRatio ?? 0),
|
|
58
|
+
minRatio: Number(loan.minRatio ?? loan.minimumRatio ?? 0),
|
|
59
|
+
};
|
|
22
60
|
}
|
|
23
61
|
|
|
24
|
-
|
|
62
|
+
function mapLoanToCDPHealth(
|
|
63
|
+
loan: Record<string, unknown>,
|
|
64
|
+
currentPrice: number
|
|
65
|
+
): CDPHealthData {
|
|
66
|
+
const cdp = mapLoanToCDP(loan);
|
|
67
|
+
const ratio = cdp.ratio;
|
|
68
|
+
const healthStatus =
|
|
69
|
+
ratio > cdp.minRatio * 1.5
|
|
70
|
+
? "healthy"
|
|
71
|
+
: ratio > cdp.minRatio
|
|
72
|
+
? "warning"
|
|
73
|
+
: "danger";
|
|
74
|
+
|
|
75
|
+
const liquidationPrice =
|
|
76
|
+
cdp.minted > 0 ? (cdp.collateral * currentPrice) / (cdp.minted * (cdp.minRatio / 100)) : 0;
|
|
77
|
+
|
|
78
|
+
return { ...cdp, healthStatus, liquidationPrice, currentPrice };
|
|
79
|
+
}
|
|
25
80
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
81
|
+
function mapStabilityAccount(
|
|
82
|
+
account: Record<string, unknown>
|
|
83
|
+
): StabilityAccountData {
|
|
84
|
+
return {
|
|
85
|
+
id: String(account.id ?? ""),
|
|
86
|
+
owner: String(account.owner ?? ""),
|
|
87
|
+
deposited: Number(account.deposited ?? account.amount ?? 0),
|
|
88
|
+
asset: String(account.asset ?? ""),
|
|
89
|
+
rewards: Number(account.rewards ?? 0),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
35
92
|
|
|
36
|
-
|
|
93
|
+
function mapStakingPosition(
|
|
94
|
+
pos: Record<string, unknown>
|
|
95
|
+
): StakingPositionData {
|
|
96
|
+
return {
|
|
97
|
+
id: String(pos.id ?? ""),
|
|
98
|
+
owner: String(pos.owner ?? ""),
|
|
99
|
+
stakedAmount: Number(pos.stakedAmount ?? pos.amount ?? 0),
|
|
100
|
+
rewardsEarned: Number(pos.rewardsEarned ?? pos.rewards ?? 0),
|
|
101
|
+
asset: String(pos.asset ?? "INDY"),
|
|
102
|
+
startDate: String(pos.startDate ?? pos.createdAt ?? ""),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
37
105
|
|
|
38
|
-
|
|
106
|
+
function mapAPR(apr: Record<string, unknown>): APRData {
|
|
39
107
|
return {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
108
|
+
pool: String(apr.pool ?? apr.name ?? ""),
|
|
109
|
+
apr: Number(apr.apr ?? 0),
|
|
110
|
+
tvl: Number(apr.tvl ?? 0),
|
|
111
|
+
asset: String(apr.asset ?? ""),
|
|
43
112
|
};
|
|
44
113
|
}
|
|
45
114
|
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// Plugin register function
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
export default function register(api: any) {
|
|
120
|
+
const config = api.pluginConfig ?? {};
|
|
121
|
+
const baseURL = (config.indexerUrl as string) || DEFAULT_BASE_URL;
|
|
122
|
+
const defaultWallet = (config.walletAddress as string) || "";
|
|
123
|
+
|
|
124
|
+
const http: AxiosInstance = axios.create({ baseURL, timeout: 15_000 });
|
|
125
|
+
const adapter = markdownAdapter;
|
|
126
|
+
|
|
127
|
+
// =========================================================================
|
|
128
|
+
// Agent Tools (16 read-only)
|
|
129
|
+
// =========================================================================
|
|
130
|
+
|
|
131
|
+
// 1. indigo_assets
|
|
132
|
+
api.registerTool({
|
|
133
|
+
name: "indigo_assets",
|
|
134
|
+
description: "List all Indigo iAssets with current prices",
|
|
135
|
+
parameters: Type.Object({}),
|
|
136
|
+
execute: async () => {
|
|
137
|
+
const { data } = await http.get("/assets/");
|
|
138
|
+
const prices: PriceData[] = (data as Record<string, unknown>[]).map(mapAssetToPrice);
|
|
139
|
+
return text(formatPriceList(adapter, prices));
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// 2. indigo_asset_price
|
|
144
|
+
api.registerTool({
|
|
145
|
+
name: "indigo_asset_price",
|
|
146
|
+
description: "Get the price of a specific Indigo iAsset",
|
|
147
|
+
parameters: Type.Object({
|
|
148
|
+
asset: Type.String({ description: "iAsset name (e.g. iUSD, iBTC, iETH)" }),
|
|
149
|
+
}),
|
|
150
|
+
execute: async (params: { asset: string }) => {
|
|
151
|
+
const { data } = await http.get("/assets/");
|
|
152
|
+
const assets = data as Record<string, unknown>[];
|
|
153
|
+
const match = assets.find(
|
|
154
|
+
(a) =>
|
|
155
|
+
String(a.name ?? a.asset ?? "").toLowerCase() ===
|
|
156
|
+
params.asset.toLowerCase()
|
|
157
|
+
);
|
|
158
|
+
if (!match) return errText(adapter, `Asset "${params.asset}" not found`);
|
|
159
|
+
return text(formatPrice(adapter, mapAssetToPrice(match)));
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// 3. indigo_ada_price
|
|
164
|
+
api.registerTool({
|
|
165
|
+
name: "indigo_ada_price",
|
|
166
|
+
description: "Get the current ADA price",
|
|
167
|
+
parameters: Type.Object({}),
|
|
168
|
+
execute: async () => {
|
|
169
|
+
const { data } = await http.post("/ada-price/");
|
|
170
|
+
return text(
|
|
171
|
+
formatPrice(adapter, {
|
|
172
|
+
asset: "ADA",
|
|
173
|
+
price: Number((data as Record<string, unknown>).price ?? 0),
|
|
174
|
+
})
|
|
175
|
+
);
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// 4. indigo_indy_price
|
|
180
|
+
api.registerTool({
|
|
181
|
+
name: "indigo_indy_price",
|
|
182
|
+
description: "Get the current INDY token price",
|
|
183
|
+
parameters: Type.Object({}),
|
|
184
|
+
execute: async () => {
|
|
185
|
+
const { data } = await http.post("/indy-price/");
|
|
186
|
+
return text(
|
|
187
|
+
formatPrice(adapter, {
|
|
188
|
+
asset: "INDY",
|
|
189
|
+
price: Number((data as Record<string, unknown>).price ?? 0),
|
|
190
|
+
})
|
|
191
|
+
);
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// 5. indigo_tvl
|
|
196
|
+
api.registerTool({
|
|
197
|
+
name: "indigo_tvl",
|
|
198
|
+
description: "Get the total value locked in Indigo Protocol",
|
|
199
|
+
parameters: Type.Object({}),
|
|
200
|
+
execute: async () => {
|
|
201
|
+
const { data } = await http.get("/analytics/tvl");
|
|
202
|
+
const tvl: TVLData = {
|
|
203
|
+
protocol: "Indigo Protocol",
|
|
204
|
+
tvl: Number((data as Record<string, unknown>).tvl ?? 0),
|
|
205
|
+
change24h:
|
|
206
|
+
(data as Record<string, unknown>).change24h != null
|
|
207
|
+
? Number((data as Record<string, unknown>).change24h)
|
|
208
|
+
: undefined,
|
|
209
|
+
breakdown: (data as Record<string, unknown>).breakdown as
|
|
210
|
+
| Record<string, number>
|
|
211
|
+
| undefined,
|
|
212
|
+
};
|
|
213
|
+
return text(formatTVL(adapter, tvl));
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// 6. indigo_protocol_stats
|
|
218
|
+
api.registerTool({
|
|
219
|
+
name: "indigo_protocol_stats",
|
|
220
|
+
description: "Get Indigo Protocol statistics (TVL, assets, CDPs)",
|
|
221
|
+
parameters: Type.Object({}),
|
|
222
|
+
execute: async () => {
|
|
223
|
+
const [tvlRes, assetsRes, loansRes] = await Promise.all([
|
|
224
|
+
http.get("/analytics/tvl"),
|
|
225
|
+
http.get("/assets/"),
|
|
226
|
+
http.get("/loans/"),
|
|
227
|
+
]);
|
|
228
|
+
|
|
229
|
+
const tvl = Number((tvlRes.data as Record<string, unknown>).tvl ?? 0);
|
|
230
|
+
const assets = (assetsRes.data as Record<string, unknown>[]).length;
|
|
231
|
+
const cdps = (loansRes.data as Record<string, unknown>[]).length;
|
|
232
|
+
|
|
233
|
+
const lines = [
|
|
234
|
+
adapter.header("Indigo Protocol Stats"),
|
|
235
|
+
adapter.keyValue("TVL", `$${tvl.toLocaleString()}`),
|
|
236
|
+
adapter.keyValue("iAssets", String(assets)),
|
|
237
|
+
adapter.keyValue("Active CDPs", String(cdps)),
|
|
238
|
+
];
|
|
239
|
+
return text(lines.join("\n"));
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// 7. indigo_apr_rewards
|
|
244
|
+
api.registerTool({
|
|
245
|
+
name: "indigo_apr_rewards",
|
|
246
|
+
description: "Get current APR rewards for Indigo pools",
|
|
247
|
+
parameters: Type.Object({}),
|
|
248
|
+
execute: async () => {
|
|
249
|
+
const { data } = await http.get("/apr/");
|
|
250
|
+
const aprs: APRData[] = (data as Record<string, unknown>[]).map(mapAPR);
|
|
251
|
+
return text(formatAPRList(adapter, aprs));
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// 8. indigo_dex_yields
|
|
256
|
+
api.registerTool({
|
|
257
|
+
name: "indigo_dex_yields",
|
|
258
|
+
description: "Get DEX yield farming opportunities for Indigo iAssets",
|
|
259
|
+
parameters: Type.Object({}),
|
|
260
|
+
execute: async () => {
|
|
261
|
+
const { data } = await http.get("/dex/yields");
|
|
262
|
+
return text(adapter.codeBlock(JSON.stringify(data, null, 2)));
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// 9. indigo_cdps
|
|
267
|
+
api.registerTool({
|
|
268
|
+
name: "indigo_cdps",
|
|
269
|
+
description: "List CDPs, optionally filtered by owner address or asset",
|
|
270
|
+
parameters: Type.Object({
|
|
271
|
+
owner: Type.Optional(
|
|
272
|
+
Type.String({ description: "Owner wallet address to filter by" })
|
|
273
|
+
),
|
|
274
|
+
asset: Type.Optional(
|
|
275
|
+
Type.String({ description: "iAsset name to filter by" })
|
|
276
|
+
),
|
|
277
|
+
}),
|
|
278
|
+
execute: async (params: { owner?: string; asset?: string }) => {
|
|
279
|
+
const query: Record<string, string> = {};
|
|
280
|
+
if (params.owner) query.owner = params.owner;
|
|
281
|
+
if (params.asset) query.asset = params.asset;
|
|
282
|
+
const { data } = await http.get("/loans/", { params: query });
|
|
283
|
+
const cdps: CDPData[] = (data as Record<string, unknown>[]).map(mapLoanToCDP);
|
|
284
|
+
return text(formatCDPList(adapter, cdps));
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// 10. indigo_cdp_health
|
|
289
|
+
api.registerTool({
|
|
290
|
+
name: "indigo_cdp_health",
|
|
291
|
+
description:
|
|
292
|
+
"Analyze health of CDPs for a given owner — shows collateral ratio, liquidation risk",
|
|
293
|
+
parameters: Type.Object({
|
|
294
|
+
owner: Type.String({ description: "Owner wallet address" }),
|
|
295
|
+
}),
|
|
296
|
+
execute: async (params: { owner: string }) => {
|
|
297
|
+
const [loansRes, assetsRes] = await Promise.all([
|
|
298
|
+
http.get("/loans/", { params: { owner: params.owner } }),
|
|
299
|
+
http.get("/assets/"),
|
|
300
|
+
]);
|
|
301
|
+
|
|
302
|
+
const loans = loansRes.data as Record<string, unknown>[];
|
|
303
|
+
if (loans.length === 0)
|
|
304
|
+
return errText(adapter, "No CDPs found for this address");
|
|
305
|
+
|
|
306
|
+
const assets = assetsRes.data as Record<string, unknown>[];
|
|
307
|
+
const priceMap = new Map<string, number>();
|
|
308
|
+
for (const a of assets) {
|
|
309
|
+
priceMap.set(
|
|
310
|
+
String(a.name ?? a.asset ?? "").toLowerCase(),
|
|
311
|
+
Number(a.price ?? 0)
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const healthItems: CDPHealthData[] = loans.map((loan) => {
|
|
316
|
+
const assetName = String(loan.asset ?? loan.iasset ?? "").toLowerCase();
|
|
317
|
+
const currentPrice = priceMap.get(assetName) ?? 0;
|
|
318
|
+
return mapLoanToCDPHealth(loan, currentPrice);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
const lines = healthItems.map((h) => formatCDPHealth(adapter, h));
|
|
322
|
+
return text(lines.join(`\n${adapter.divider()}\n`));
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// 11. indigo_stability_pools
|
|
327
|
+
api.registerTool({
|
|
328
|
+
name: "indigo_stability_pools",
|
|
329
|
+
description: "Get Indigo stability pool information",
|
|
330
|
+
parameters: Type.Object({}),
|
|
331
|
+
execute: async () => {
|
|
332
|
+
const { data } = await http.get("/stability-pools/");
|
|
333
|
+
const accounts: StabilityAccountData[] = (
|
|
334
|
+
data as Record<string, unknown>[]
|
|
335
|
+
).map(mapStabilityAccount);
|
|
336
|
+
return text(formatStabilityList(adapter, accounts));
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// 12. indigo_staking_info
|
|
341
|
+
api.registerTool({
|
|
342
|
+
name: "indigo_staking_info",
|
|
343
|
+
description: "Get INDY staking overview and statistics",
|
|
344
|
+
parameters: Type.Object({}),
|
|
345
|
+
execute: async () => {
|
|
346
|
+
const { data } = await http.get("/staking/");
|
|
347
|
+
return text(adapter.codeBlock(JSON.stringify(data, null, 2)));
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// 13. indigo_staking_positions
|
|
352
|
+
api.registerTool({
|
|
353
|
+
name: "indigo_staking_positions",
|
|
354
|
+
description: "List INDY staking positions, optionally filtered by owner",
|
|
355
|
+
parameters: Type.Object({
|
|
356
|
+
owner: Type.Optional(
|
|
357
|
+
Type.String({ description: "Owner wallet address to filter by" })
|
|
358
|
+
),
|
|
359
|
+
}),
|
|
360
|
+
execute: async (params: { owner?: string }) => {
|
|
361
|
+
const query: Record<string, string> = {};
|
|
362
|
+
if (params.owner) query.owner = params.owner;
|
|
363
|
+
const { data } = await http.get("/staking-positions/", {
|
|
364
|
+
params: query,
|
|
365
|
+
});
|
|
366
|
+
const positions: StakingPositionData[] = (
|
|
367
|
+
data as Record<string, unknown>[]
|
|
368
|
+
).map(mapStakingPosition);
|
|
369
|
+
return text(formatStakingList(adapter, positions));
|
|
370
|
+
},
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
// 14. indigo_polls
|
|
374
|
+
api.registerTool({
|
|
375
|
+
name: "indigo_polls",
|
|
376
|
+
description: "Get current Indigo governance polls",
|
|
377
|
+
parameters: Type.Object({}),
|
|
378
|
+
execute: async () => {
|
|
379
|
+
const { data } = await http.get("/polls/");
|
|
380
|
+
return text(adapter.codeBlock(JSON.stringify(data, null, 2)));
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// 15. indigo_wallet_balances
|
|
385
|
+
api.registerTool({
|
|
386
|
+
name: "indigo_wallet_balances",
|
|
387
|
+
description: "Get token balances for a Cardano wallet address",
|
|
388
|
+
parameters: Type.Object({
|
|
389
|
+
address: Type.String({ description: "Cardano wallet address" }),
|
|
390
|
+
}),
|
|
391
|
+
execute: async (params: { address: string }) => {
|
|
392
|
+
const { data } = await http.get("/blockfrost/balances", {
|
|
393
|
+
params: { address: params.address },
|
|
394
|
+
});
|
|
395
|
+
return text(adapter.codeBlock(JSON.stringify(data, null, 2)));
|
|
396
|
+
},
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// 16. indigo_order_book
|
|
400
|
+
api.registerTool({
|
|
401
|
+
name: "indigo_order_book",
|
|
402
|
+
description: "Get the Indigo redemption order book",
|
|
403
|
+
parameters: Type.Object({}),
|
|
404
|
+
execute: async () => {
|
|
405
|
+
const { data } = await http.get("/order-book/");
|
|
406
|
+
return text(adapter.codeBlock(JSON.stringify(data, null, 2)));
|
|
407
|
+
},
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// =========================================================================
|
|
411
|
+
// Auto-reply Commands (8)
|
|
412
|
+
// =========================================================================
|
|
413
|
+
|
|
414
|
+
// /price <asset>
|
|
415
|
+
api.registerCommand({
|
|
416
|
+
name: "price",
|
|
417
|
+
description: "Get the price of an iAsset (e.g. /price iUSD)",
|
|
418
|
+
parameters: Type.Object({
|
|
419
|
+
asset: Type.String({ description: "Asset name" }),
|
|
420
|
+
}),
|
|
421
|
+
execute: async (params: { asset: string }) => {
|
|
422
|
+
try {
|
|
423
|
+
const assetLower = params.asset.toLowerCase();
|
|
424
|
+
|
|
425
|
+
if (assetLower === "ada") {
|
|
426
|
+
const { data } = await http.post("/ada-price/");
|
|
427
|
+
return {
|
|
428
|
+
text: formatPrice(adapter, {
|
|
429
|
+
asset: "ADA",
|
|
430
|
+
price: Number((data as Record<string, unknown>).price ?? 0),
|
|
431
|
+
}),
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (assetLower === "indy") {
|
|
436
|
+
const { data } = await http.post("/indy-price/");
|
|
437
|
+
return {
|
|
438
|
+
text: formatPrice(adapter, {
|
|
439
|
+
asset: "INDY",
|
|
440
|
+
price: Number((data as Record<string, unknown>).price ?? 0),
|
|
441
|
+
}),
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const { data } = await http.get("/assets/");
|
|
446
|
+
const assets = data as Record<string, unknown>[];
|
|
447
|
+
const match = assets.find(
|
|
448
|
+
(a) =>
|
|
449
|
+
String(a.name ?? a.asset ?? "").toLowerCase() === assetLower
|
|
450
|
+
);
|
|
451
|
+
if (!match)
|
|
452
|
+
return { text: formatError(adapter, { message: `Asset "${params.asset}" not found` }) };
|
|
453
|
+
return { text: formatPrice(adapter, mapAssetToPrice(match)) };
|
|
454
|
+
} catch (e: any) {
|
|
455
|
+
return { text: formatError(adapter, { message: e.message, code: "PRICE_ERROR" }) };
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// /prices
|
|
461
|
+
api.registerCommand({
|
|
462
|
+
name: "prices",
|
|
463
|
+
description: "Get all iAsset prices",
|
|
464
|
+
execute: async () => {
|
|
465
|
+
try {
|
|
466
|
+
const { data } = await http.get("/assets/");
|
|
467
|
+
const prices: PriceData[] = (data as Record<string, unknown>[]).map(
|
|
468
|
+
mapAssetToPrice
|
|
469
|
+
);
|
|
470
|
+
return { text: formatPriceList(adapter, prices) };
|
|
471
|
+
} catch (e: any) {
|
|
472
|
+
return { text: formatError(adapter, { message: e.message, code: "PRICES_ERROR" }) };
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
// /tvl
|
|
478
|
+
api.registerCommand({
|
|
479
|
+
name: "tvl",
|
|
480
|
+
description: "Show Indigo Protocol total value locked",
|
|
481
|
+
execute: async () => {
|
|
482
|
+
try {
|
|
483
|
+
const { data } = await http.get("/analytics/tvl");
|
|
484
|
+
const tvl: TVLData = {
|
|
485
|
+
protocol: "Indigo Protocol",
|
|
486
|
+
tvl: Number((data as Record<string, unknown>).tvl ?? 0),
|
|
487
|
+
change24h:
|
|
488
|
+
(data as Record<string, unknown>).change24h != null
|
|
489
|
+
? Number((data as Record<string, unknown>).change24h)
|
|
490
|
+
: undefined,
|
|
491
|
+
breakdown: (data as Record<string, unknown>).breakdown as
|
|
492
|
+
| Record<string, number>
|
|
493
|
+
| undefined,
|
|
494
|
+
};
|
|
495
|
+
return { text: formatTVL(adapter, tvl) };
|
|
496
|
+
} catch (e: any) {
|
|
497
|
+
return { text: formatError(adapter, { message: e.message, code: "TVL_ERROR" }) };
|
|
498
|
+
}
|
|
499
|
+
},
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
// /balance [addr]
|
|
503
|
+
api.registerCommand({
|
|
504
|
+
name: "balance",
|
|
505
|
+
description: "Show wallet token balances (defaults to configured wallet)",
|
|
506
|
+
parameters: Type.Object({
|
|
507
|
+
address: Type.Optional(
|
|
508
|
+
Type.String({ description: "Cardano wallet address" })
|
|
509
|
+
),
|
|
510
|
+
}),
|
|
511
|
+
execute: async (params: { address?: string }) => {
|
|
512
|
+
try {
|
|
513
|
+
const addr = params.address || defaultWallet;
|
|
514
|
+
if (!addr)
|
|
515
|
+
return {
|
|
516
|
+
text: formatError(adapter, {
|
|
517
|
+
message: "No wallet address provided. Pass an address or set walletAddress in plugin config.",
|
|
518
|
+
}),
|
|
519
|
+
};
|
|
520
|
+
const { data } = await http.get("/blockfrost/balances", {
|
|
521
|
+
params: { address: addr },
|
|
522
|
+
});
|
|
523
|
+
return { text: adapter.codeBlock(JSON.stringify(data, null, 2)) };
|
|
524
|
+
} catch (e: any) {
|
|
525
|
+
return { text: formatError(adapter, { message: e.message, code: "BALANCE_ERROR" }) };
|
|
526
|
+
}
|
|
527
|
+
},
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
// /cdps [owner]
|
|
531
|
+
api.registerCommand({
|
|
532
|
+
name: "cdps",
|
|
533
|
+
description: "List CDPs (defaults to configured wallet)",
|
|
534
|
+
parameters: Type.Object({
|
|
535
|
+
owner: Type.Optional(
|
|
536
|
+
Type.String({ description: "Owner wallet address" })
|
|
537
|
+
),
|
|
538
|
+
}),
|
|
539
|
+
execute: async (params: { owner?: string }) => {
|
|
540
|
+
try {
|
|
541
|
+
const owner = params.owner || defaultWallet;
|
|
542
|
+
const query: Record<string, string> = {};
|
|
543
|
+
if (owner) query.owner = owner;
|
|
544
|
+
const { data } = await http.get("/loans/", { params: query });
|
|
545
|
+
const cdps: CDPData[] = (data as Record<string, unknown>[]).map(
|
|
546
|
+
mapLoanToCDP
|
|
547
|
+
);
|
|
548
|
+
return { text: formatCDPList(adapter, cdps) };
|
|
549
|
+
} catch (e: any) {
|
|
550
|
+
return { text: formatError(adapter, { message: e.message, code: "CDPS_ERROR" }) };
|
|
551
|
+
}
|
|
552
|
+
},
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
// /staking
|
|
556
|
+
api.registerCommand({
|
|
557
|
+
name: "staking",
|
|
558
|
+
description: "Show INDY staking overview",
|
|
559
|
+
execute: async () => {
|
|
560
|
+
try {
|
|
561
|
+
const { data } = await http.get("/staking/");
|
|
562
|
+
return { text: adapter.codeBlock(JSON.stringify(data, null, 2)) };
|
|
563
|
+
} catch (e: any) {
|
|
564
|
+
return { text: formatError(adapter, { message: e.message, code: "STAKING_ERROR" }) };
|
|
565
|
+
}
|
|
566
|
+
},
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
// /pools
|
|
570
|
+
api.registerCommand({
|
|
571
|
+
name: "pools",
|
|
572
|
+
description: "Show stability pool information",
|
|
573
|
+
execute: async () => {
|
|
574
|
+
try {
|
|
575
|
+
const { data } = await http.get("/stability-pools/");
|
|
576
|
+
const accounts: StabilityAccountData[] = (
|
|
577
|
+
data as Record<string, unknown>[]
|
|
578
|
+
).map(mapStabilityAccount);
|
|
579
|
+
return { text: formatStabilityList(adapter, accounts) };
|
|
580
|
+
} catch (e: any) {
|
|
581
|
+
return { text: formatError(adapter, { message: e.message, code: "POOLS_ERROR" }) };
|
|
582
|
+
}
|
|
583
|
+
},
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
// /polls
|
|
587
|
+
api.registerCommand({
|
|
588
|
+
name: "polls",
|
|
589
|
+
description: "Show current governance polls",
|
|
590
|
+
execute: async () => {
|
|
591
|
+
try {
|
|
592
|
+
const { data } = await http.get("/polls/");
|
|
593
|
+
return { text: adapter.codeBlock(JSON.stringify(data, null, 2)) };
|
|
594
|
+
} catch (e: any) {
|
|
595
|
+
return { text: formatError(adapter, { message: e.message, code: "POLLS_ERROR" }) };
|
|
596
|
+
}
|
|
597
|
+
},
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
// =========================================================================
|
|
601
|
+
// Service (placeholder)
|
|
602
|
+
// =========================================================================
|
|
603
|
+
|
|
604
|
+
api.registerService({
|
|
605
|
+
name: "indigo-protocol-monitor",
|
|
606
|
+
description: "Indigo Protocol monitoring service",
|
|
607
|
+
start: async () => {
|
|
608
|
+
console.log("[openclaw-indigo] monitor service started");
|
|
609
|
+
},
|
|
610
|
+
stop: async () => {
|
|
611
|
+
console.log("[openclaw-indigo] monitor service stopped");
|
|
612
|
+
},
|
|
613
|
+
});
|
|
614
|
+
}
|