@grinta-mcp/server 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 +49 -0
- package/dist/config.d.ts +38 -0
- package/dist/config.js +45 -0
- package/dist/grinta.d.ts +99 -0
- package/dist/grinta.js +561 -0
- package/dist/identity.d.ts +24 -0
- package/dist/identity.js +74 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.js +284 -0
- package/dist/mcp.d.ts +8 -0
- package/dist/mcp.js +322 -0
- package/dist/price-feed.d.ts +23 -0
- package/dist/price-feed.js +151 -0
- package/dist/scan-safes.d.ts +5 -0
- package/dist/scan-safes.js +87 -0
- package/dist/swap.d.ts +25 -0
- package/dist/swap.js +271 -0
- package/dist/test-swap.d.ts +5 -0
- package/dist/test-swap.js +93 -0
- package/dist/utils.d.ts +19 -0
- package/dist/utils.js +35 -0
- package/package.json +35 -0
package/dist/grinta.js
ADDED
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grinta CDP Protocol Client
|
|
3
|
+
*
|
|
4
|
+
* Manages SAFEs, borrows GRIT stablecoin, monitors health.
|
|
5
|
+
* All write operations go through SafeManager.
|
|
6
|
+
*/
|
|
7
|
+
import { Contract } from "starknet";
|
|
8
|
+
import { GRINTA, TOKENS } from "./config.js";
|
|
9
|
+
import { formatAmount } from "./utils.js";
|
|
10
|
+
// --- Constants ---
|
|
11
|
+
const WAD = BigInt(10) ** 18n;
|
|
12
|
+
const RAY = BigInt(10) ** 27n;
|
|
13
|
+
// --- ABIs ---
|
|
14
|
+
const SAFE_MANAGER_ABI = [
|
|
15
|
+
{
|
|
16
|
+
type: "interface",
|
|
17
|
+
name: "ISafeManager",
|
|
18
|
+
items: [
|
|
19
|
+
{
|
|
20
|
+
type: "function",
|
|
21
|
+
name: "open_safe",
|
|
22
|
+
inputs: [],
|
|
23
|
+
outputs: [{ type: "core::integer::u64" }],
|
|
24
|
+
state_mutability: "external",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
type: "function",
|
|
28
|
+
name: "open_and_borrow",
|
|
29
|
+
inputs: [
|
|
30
|
+
{ name: "collateral_amount", type: "core::integer::u256" },
|
|
31
|
+
{ name: "borrow_amount", type: "core::integer::u256" },
|
|
32
|
+
],
|
|
33
|
+
outputs: [{ type: "core::integer::u64" }],
|
|
34
|
+
state_mutability: "external",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: "function",
|
|
38
|
+
name: "deposit",
|
|
39
|
+
inputs: [
|
|
40
|
+
{ name: "safe_id", type: "core::integer::u64" },
|
|
41
|
+
{ name: "amount", type: "core::integer::u256" },
|
|
42
|
+
],
|
|
43
|
+
outputs: [],
|
|
44
|
+
state_mutability: "external",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
type: "function",
|
|
48
|
+
name: "withdraw",
|
|
49
|
+
inputs: [
|
|
50
|
+
{ name: "safe_id", type: "core::integer::u64" },
|
|
51
|
+
{ name: "amount", type: "core::integer::u256" },
|
|
52
|
+
],
|
|
53
|
+
outputs: [],
|
|
54
|
+
state_mutability: "external",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
type: "function",
|
|
58
|
+
name: "borrow",
|
|
59
|
+
inputs: [
|
|
60
|
+
{ name: "safe_id", type: "core::integer::u64" },
|
|
61
|
+
{ name: "amount", type: "core::integer::u256" },
|
|
62
|
+
],
|
|
63
|
+
outputs: [],
|
|
64
|
+
state_mutability: "external",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
type: "function",
|
|
68
|
+
name: "repay",
|
|
69
|
+
inputs: [
|
|
70
|
+
{ name: "safe_id", type: "core::integer::u64" },
|
|
71
|
+
{ name: "amount", type: "core::integer::u256" },
|
|
72
|
+
],
|
|
73
|
+
outputs: [],
|
|
74
|
+
state_mutability: "external",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
type: "function",
|
|
78
|
+
name: "close_safe",
|
|
79
|
+
inputs: [{ name: "safe_id", type: "core::integer::u64" }],
|
|
80
|
+
outputs: [],
|
|
81
|
+
state_mutability: "external",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
type: "function",
|
|
85
|
+
name: "authorize_agent",
|
|
86
|
+
inputs: [
|
|
87
|
+
{ name: "safe_id", type: "core::integer::u64" },
|
|
88
|
+
{ name: "agent", type: "core::starknet::contract_address::ContractAddress" },
|
|
89
|
+
],
|
|
90
|
+
outputs: [],
|
|
91
|
+
state_mutability: "external",
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
type: "function",
|
|
95
|
+
name: "revoke_agent",
|
|
96
|
+
inputs: [
|
|
97
|
+
{ name: "safe_id", type: "core::integer::u64" },
|
|
98
|
+
{ name: "agent", type: "core::starknet::contract_address::ContractAddress" },
|
|
99
|
+
],
|
|
100
|
+
outputs: [],
|
|
101
|
+
state_mutability: "external",
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
type: "function",
|
|
105
|
+
name: "get_position_health",
|
|
106
|
+
inputs: [{ name: "safe_id", type: "core::integer::u64" }],
|
|
107
|
+
outputs: [
|
|
108
|
+
{
|
|
109
|
+
type: "(core::integer::u256, core::integer::u256, core::integer::u256, core::integer::u256)",
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
state_mutability: "view",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: "function",
|
|
116
|
+
name: "get_max_borrow",
|
|
117
|
+
inputs: [{ name: "safe_id", type: "core::integer::u64" }],
|
|
118
|
+
outputs: [{ type: "core::integer::u256" }],
|
|
119
|
+
state_mutability: "view",
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
type: "function",
|
|
123
|
+
name: "get_safe_owner",
|
|
124
|
+
inputs: [{ name: "safe_id", type: "core::integer::u64" }],
|
|
125
|
+
outputs: [{ type: "core::starknet::contract_address::ContractAddress" }],
|
|
126
|
+
state_mutability: "view",
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
type: "function",
|
|
130
|
+
name: "is_authorized",
|
|
131
|
+
inputs: [
|
|
132
|
+
{ name: "safe_id", type: "core::integer::u64" },
|
|
133
|
+
{ name: "agent", type: "core::starknet::contract_address::ContractAddress" },
|
|
134
|
+
],
|
|
135
|
+
outputs: [{ type: "core::bool" }],
|
|
136
|
+
state_mutability: "view",
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
];
|
|
141
|
+
const SAFE_ENGINE_ABI = [
|
|
142
|
+
{
|
|
143
|
+
type: "interface",
|
|
144
|
+
name: "ISAFEEngine",
|
|
145
|
+
items: [
|
|
146
|
+
{
|
|
147
|
+
type: "function",
|
|
148
|
+
name: "get_safe",
|
|
149
|
+
inputs: [{ name: "safe_id", type: "core::integer::u64" }],
|
|
150
|
+
outputs: [{ type: "(core::integer::u256, core::integer::u256)" }],
|
|
151
|
+
state_mutability: "view",
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
type: "function",
|
|
155
|
+
name: "get_safe_count",
|
|
156
|
+
inputs: [],
|
|
157
|
+
outputs: [{ type: "core::integer::u64" }],
|
|
158
|
+
state_mutability: "view",
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
type: "function",
|
|
162
|
+
name: "get_collateral_price",
|
|
163
|
+
inputs: [],
|
|
164
|
+
outputs: [{ type: "core::integer::u256" }],
|
|
165
|
+
state_mutability: "view",
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
type: "function",
|
|
169
|
+
name: "get_redemption_price",
|
|
170
|
+
inputs: [],
|
|
171
|
+
outputs: [{ type: "core::integer::u256" }],
|
|
172
|
+
state_mutability: "view",
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
type: "function",
|
|
176
|
+
name: "get_redemption_rate",
|
|
177
|
+
inputs: [],
|
|
178
|
+
outputs: [{ type: "core::integer::u256" }],
|
|
179
|
+
state_mutability: "view",
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
type: "function",
|
|
183
|
+
name: "get_total_debt",
|
|
184
|
+
inputs: [],
|
|
185
|
+
outputs: [{ type: "core::integer::u256" }],
|
|
186
|
+
state_mutability: "view",
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
type: "function",
|
|
190
|
+
name: "get_total_collateral",
|
|
191
|
+
inputs: [],
|
|
192
|
+
outputs: [{ type: "core::integer::u256" }],
|
|
193
|
+
state_mutability: "view",
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
type: "function",
|
|
197
|
+
name: "get_debt_ceiling",
|
|
198
|
+
inputs: [],
|
|
199
|
+
outputs: [{ type: "core::integer::u256" }],
|
|
200
|
+
state_mutability: "view",
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
type: "function",
|
|
204
|
+
name: "get_liquidation_ratio",
|
|
205
|
+
inputs: [],
|
|
206
|
+
outputs: [{ type: "core::integer::u256" }],
|
|
207
|
+
state_mutability: "view",
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
type: "function",
|
|
211
|
+
name: "get_grit_balance",
|
|
212
|
+
inputs: [{ name: "account", type: "core::starknet::contract_address::ContractAddress" }],
|
|
213
|
+
outputs: [{ type: "core::integer::u256" }],
|
|
214
|
+
state_mutability: "view",
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
type: "function",
|
|
218
|
+
name: "get_system_health",
|
|
219
|
+
inputs: [],
|
|
220
|
+
outputs: [
|
|
221
|
+
{
|
|
222
|
+
type: "(core::integer::u256, core::integer::u256, core::integer::u256, core::integer::u256)",
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
state_mutability: "view",
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
},
|
|
229
|
+
];
|
|
230
|
+
const GRINTA_HOOK_ABI = [
|
|
231
|
+
{
|
|
232
|
+
type: "interface",
|
|
233
|
+
name: "IGrintaHook",
|
|
234
|
+
items: [
|
|
235
|
+
{
|
|
236
|
+
type: "function",
|
|
237
|
+
name: "update",
|
|
238
|
+
inputs: [],
|
|
239
|
+
outputs: [],
|
|
240
|
+
state_mutability: "external",
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
type: "function",
|
|
244
|
+
name: "set_market_price",
|
|
245
|
+
inputs: [{ name: "price", type: "core::integer::u256" }],
|
|
246
|
+
outputs: [],
|
|
247
|
+
state_mutability: "external",
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
type: "function",
|
|
251
|
+
name: "get_market_price",
|
|
252
|
+
inputs: [],
|
|
253
|
+
outputs: [{ type: "core::integer::u256" }],
|
|
254
|
+
state_mutability: "view",
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
type: "function",
|
|
258
|
+
name: "get_collateral_price",
|
|
259
|
+
inputs: [],
|
|
260
|
+
outputs: [{ type: "core::integer::u256" }],
|
|
261
|
+
state_mutability: "view",
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
type: "function",
|
|
265
|
+
name: "get_last_update_time",
|
|
266
|
+
inputs: [],
|
|
267
|
+
outputs: [{ type: "core::integer::u64" }],
|
|
268
|
+
state_mutability: "view",
|
|
269
|
+
},
|
|
270
|
+
],
|
|
271
|
+
},
|
|
272
|
+
];
|
|
273
|
+
const ERC20_ABI = [
|
|
274
|
+
{
|
|
275
|
+
type: "interface",
|
|
276
|
+
name: "IERC20",
|
|
277
|
+
items: [
|
|
278
|
+
{
|
|
279
|
+
type: "function",
|
|
280
|
+
name: "approve",
|
|
281
|
+
inputs: [
|
|
282
|
+
{ name: "spender", type: "core::starknet::contract_address::ContractAddress" },
|
|
283
|
+
{ name: "amount", type: "core::integer::u256" },
|
|
284
|
+
],
|
|
285
|
+
outputs: [{ type: "core::bool" }],
|
|
286
|
+
state_mutability: "external",
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
type: "function",
|
|
290
|
+
name: "balance_of",
|
|
291
|
+
inputs: [{ name: "account", type: "core::starknet::contract_address::ContractAddress" }],
|
|
292
|
+
outputs: [{ type: "core::integer::u256" }],
|
|
293
|
+
state_mutability: "view",
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
},
|
|
297
|
+
];
|
|
298
|
+
// --- Client ---
|
|
299
|
+
export class GrintaClient {
|
|
300
|
+
safeManager;
|
|
301
|
+
safeEngine;
|
|
302
|
+
grintaHook;
|
|
303
|
+
wbtc;
|
|
304
|
+
account;
|
|
305
|
+
constructor(account, provider) {
|
|
306
|
+
this.account = account;
|
|
307
|
+
this.safeManager = new Contract({
|
|
308
|
+
abi: SAFE_MANAGER_ABI,
|
|
309
|
+
address: GRINTA.SAFE_MANAGER,
|
|
310
|
+
providerOrAccount: account,
|
|
311
|
+
});
|
|
312
|
+
this.safeEngine = new Contract({
|
|
313
|
+
abi: SAFE_ENGINE_ABI,
|
|
314
|
+
address: GRINTA.SAFE_ENGINE,
|
|
315
|
+
providerOrAccount: provider,
|
|
316
|
+
});
|
|
317
|
+
this.grintaHook = new Contract({
|
|
318
|
+
abi: GRINTA_HOOK_ABI,
|
|
319
|
+
address: GRINTA.GRINTA_HOOK,
|
|
320
|
+
providerOrAccount: provider,
|
|
321
|
+
});
|
|
322
|
+
this.wbtc = new Contract({
|
|
323
|
+
abi: ERC20_ABI,
|
|
324
|
+
address: TOKENS.WBTC,
|
|
325
|
+
providerOrAccount: account,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
// --- Read Operations ---
|
|
329
|
+
async getPositionHealth(safeId) {
|
|
330
|
+
const result = await this.safeManager.get_position_health(safeId);
|
|
331
|
+
return {
|
|
332
|
+
collateralValue: BigInt(result[0]),
|
|
333
|
+
debt: BigInt(result[1]),
|
|
334
|
+
ltv: BigInt(result[2]),
|
|
335
|
+
liquidationPrice: BigInt(result[3]),
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
async getMaxBorrow(safeId) {
|
|
339
|
+
const result = await this.safeManager.get_max_borrow(safeId);
|
|
340
|
+
return BigInt(result);
|
|
341
|
+
}
|
|
342
|
+
async getSafeOwner(safeId) {
|
|
343
|
+
const result = await this.safeManager.get_safe_owner(safeId);
|
|
344
|
+
return String(result);
|
|
345
|
+
}
|
|
346
|
+
async isAuthorized(safeId, agent) {
|
|
347
|
+
const result = await this.safeManager.is_authorized(safeId, agent);
|
|
348
|
+
return Boolean(result);
|
|
349
|
+
}
|
|
350
|
+
async getSafe(safeId) {
|
|
351
|
+
const result = await this.safeEngine.get_safe(safeId);
|
|
352
|
+
return {
|
|
353
|
+
collateral: BigInt(result[0]),
|
|
354
|
+
debt: BigInt(result[1]),
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
async getSystemStatus() {
|
|
358
|
+
const [totalDebt, totalCollateral, debtCeiling, liquidationRatio, collateralPrice, redemptionPrice, redemptionRate, marketPrice,] = await Promise.all([
|
|
359
|
+
this.safeEngine.get_total_debt().then((r) => BigInt(r)),
|
|
360
|
+
this.safeEngine.get_total_collateral().then((r) => BigInt(r)),
|
|
361
|
+
this.safeEngine.get_debt_ceiling().then((r) => BigInt(r)),
|
|
362
|
+
this.safeEngine.get_liquidation_ratio().then((r) => BigInt(r)),
|
|
363
|
+
this.safeEngine.get_collateral_price().then((r) => BigInt(r)),
|
|
364
|
+
this.safeEngine.get_redemption_price().then((r) => BigInt(r)),
|
|
365
|
+
this.safeEngine.get_redemption_rate().then((r) => BigInt(r)),
|
|
366
|
+
this.grintaHook.get_market_price().then((r) => BigInt(r)),
|
|
367
|
+
]);
|
|
368
|
+
return {
|
|
369
|
+
totalDebt,
|
|
370
|
+
totalCollateral,
|
|
371
|
+
debtCeiling,
|
|
372
|
+
liquidationRatio,
|
|
373
|
+
collateralPrice,
|
|
374
|
+
redemptionPrice,
|
|
375
|
+
redemptionRate,
|
|
376
|
+
marketPrice,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
async getGritBalance(address) {
|
|
380
|
+
const result = await this.safeEngine.get_grit_balance(address);
|
|
381
|
+
return BigInt(result);
|
|
382
|
+
}
|
|
383
|
+
async getWbtcBalance(address) {
|
|
384
|
+
const result = await this.wbtc.balance_of(address);
|
|
385
|
+
return BigInt(result);
|
|
386
|
+
}
|
|
387
|
+
// --- Write Operations ---
|
|
388
|
+
/**
|
|
389
|
+
* Open an empty SAFE. Returns the safe_id.
|
|
390
|
+
*/
|
|
391
|
+
async openSafe() {
|
|
392
|
+
const result = await this.account.execute([
|
|
393
|
+
this.safeManager.populate("open_safe", []),
|
|
394
|
+
]);
|
|
395
|
+
console.log(` open_safe tx: ${result.transaction_hash}`);
|
|
396
|
+
await this.account.waitForTransaction(result.transaction_hash);
|
|
397
|
+
// The safe_id is returned by the contract; parse from events
|
|
398
|
+
return -1; // TODO: parse safe_id from tx receipt events
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Open a SAFE, deposit WBTC collateral, and borrow GRIT in one transaction.
|
|
402
|
+
* @param collateralAmount WBTC in 8 decimals (e.g. 50_000_000 = 0.5 BTC)
|
|
403
|
+
* @param borrowAmount GRIT in WAD (e.g. 10000n * WAD = 10,000 GRIT)
|
|
404
|
+
*/
|
|
405
|
+
async openAndBorrow(collateralAmount, borrowAmount) {
|
|
406
|
+
// Step 1: Approve WBTC to CollateralJoin
|
|
407
|
+
// Step 2: Open and borrow via SafeManager
|
|
408
|
+
// Batched as multicall
|
|
409
|
+
const calls = [
|
|
410
|
+
this.wbtc.populate("approve", [GRINTA.COLLATERAL_JOIN, collateralAmount]),
|
|
411
|
+
this.safeManager.populate("open_and_borrow", [collateralAmount, borrowAmount]),
|
|
412
|
+
];
|
|
413
|
+
const result = await this.account.execute(calls);
|
|
414
|
+
console.log(` open_and_borrow tx: ${result.transaction_hash}`);
|
|
415
|
+
await this.account.waitForTransaction(result.transaction_hash);
|
|
416
|
+
return result.transaction_hash;
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Deposit WBTC collateral into an existing SAFE.
|
|
420
|
+
* @param amount WBTC in 8 decimals
|
|
421
|
+
*/
|
|
422
|
+
async deposit(safeId, amount) {
|
|
423
|
+
const calls = [
|
|
424
|
+
this.wbtc.populate("approve", [GRINTA.COLLATERAL_JOIN, amount]),
|
|
425
|
+
this.safeManager.populate("deposit", [safeId, amount]),
|
|
426
|
+
];
|
|
427
|
+
const result = await this.account.execute(calls);
|
|
428
|
+
console.log(` deposit tx: ${result.transaction_hash}`);
|
|
429
|
+
await this.account.waitForTransaction(result.transaction_hash);
|
|
430
|
+
return result.transaction_hash;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Withdraw WBTC collateral from a SAFE.
|
|
434
|
+
* @param amount Internal WAD amount (not 8 decimals)
|
|
435
|
+
*/
|
|
436
|
+
async withdraw(safeId, amount) {
|
|
437
|
+
const result = await this.account.execute([
|
|
438
|
+
this.safeManager.populate("withdraw", [safeId, amount]),
|
|
439
|
+
]);
|
|
440
|
+
console.log(` withdraw tx: ${result.transaction_hash}`);
|
|
441
|
+
await this.account.waitForTransaction(result.transaction_hash);
|
|
442
|
+
return result.transaction_hash;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Borrow additional GRIT against existing collateral.
|
|
446
|
+
* @param amount GRIT in WAD
|
|
447
|
+
*/
|
|
448
|
+
async borrow(safeId, amount) {
|
|
449
|
+
const result = await this.account.execute([
|
|
450
|
+
this.safeManager.populate("borrow", [safeId, amount]),
|
|
451
|
+
]);
|
|
452
|
+
console.log(` borrow tx: ${result.transaction_hash}`);
|
|
453
|
+
await this.account.waitForTransaction(result.transaction_hash);
|
|
454
|
+
return result.transaction_hash;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Repay GRIT debt. If amount > debt, only repays the debt.
|
|
458
|
+
* @param amount GRIT in WAD
|
|
459
|
+
*/
|
|
460
|
+
async repay(safeId, amount) {
|
|
461
|
+
const result = await this.account.execute([
|
|
462
|
+
this.safeManager.populate("repay", [safeId, amount]),
|
|
463
|
+
]);
|
|
464
|
+
console.log(` repay tx: ${result.transaction_hash}`);
|
|
465
|
+
await this.account.waitForTransaction(result.transaction_hash);
|
|
466
|
+
return result.transaction_hash;
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Close a SAFE. Requires zero debt. Returns remaining collateral.
|
|
470
|
+
*/
|
|
471
|
+
async closeSafe(safeId) {
|
|
472
|
+
const result = await this.account.execute([
|
|
473
|
+
this.safeManager.populate("close_safe", [safeId]),
|
|
474
|
+
]);
|
|
475
|
+
console.log(` close_safe tx: ${result.transaction_hash}`);
|
|
476
|
+
await this.account.waitForTransaction(result.transaction_hash);
|
|
477
|
+
return result.transaction_hash;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Authorize an agent address to operate on a SAFE.
|
|
481
|
+
*/
|
|
482
|
+
async authorizeAgent(safeId, agent) {
|
|
483
|
+
const result = await this.account.execute([
|
|
484
|
+
this.safeManager.populate("authorize_agent", [safeId, agent]),
|
|
485
|
+
]);
|
|
486
|
+
console.log(` authorize_agent tx: ${result.transaction_hash}`);
|
|
487
|
+
await this.account.waitForTransaction(result.transaction_hash);
|
|
488
|
+
return result.transaction_hash;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Revoke an agent's permission on a SAFE.
|
|
492
|
+
*/
|
|
493
|
+
async revokeAgent(safeId, agent) {
|
|
494
|
+
const result = await this.account.execute([
|
|
495
|
+
this.safeManager.populate("revoke_agent", [safeId, agent]),
|
|
496
|
+
]);
|
|
497
|
+
console.log(` revoke_agent tx: ${result.transaction_hash}`);
|
|
498
|
+
await this.account.waitForTransaction(result.transaction_hash);
|
|
499
|
+
return result.transaction_hash;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Trigger a price/rate update on the GrintaHook.
|
|
503
|
+
*/
|
|
504
|
+
async triggerUpdate() {
|
|
505
|
+
const hookWithAccount = new Contract({
|
|
506
|
+
abi: GRINTA_HOOK_ABI,
|
|
507
|
+
address: GRINTA.GRINTA_HOOK,
|
|
508
|
+
providerOrAccount: this.account,
|
|
509
|
+
});
|
|
510
|
+
const result = await this.account.execute([
|
|
511
|
+
hookWithAccount.populate("update", []),
|
|
512
|
+
]);
|
|
513
|
+
console.log(` update tx: ${result.transaction_hash}`);
|
|
514
|
+
await this.account.waitForTransaction(result.transaction_hash);
|
|
515
|
+
return result.transaction_hash;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Set the GRIT market price (WAD). Anyone can call.
|
|
519
|
+
* @param priceWad Price in WAD (e.g. 1e18 = $1.00)
|
|
520
|
+
*/
|
|
521
|
+
async setMarketPrice(priceWad) {
|
|
522
|
+
const hookWithAccount = new Contract({
|
|
523
|
+
abi: GRINTA_HOOK_ABI,
|
|
524
|
+
address: GRINTA.GRINTA_HOOK,
|
|
525
|
+
providerOrAccount: this.account,
|
|
526
|
+
});
|
|
527
|
+
const result = await this.account.execute([
|
|
528
|
+
hookWithAccount.populate("set_market_price", [priceWad]),
|
|
529
|
+
]);
|
|
530
|
+
console.log(` set_market_price tx: ${result.transaction_hash}`);
|
|
531
|
+
await this.account.waitForTransaction(result.transaction_hash);
|
|
532
|
+
return result.transaction_hash;
|
|
533
|
+
}
|
|
534
|
+
// --- Display Helpers ---
|
|
535
|
+
formatHealth(health) {
|
|
536
|
+
const ltvPct = Number(health.ltv * 10000n / WAD) / 100;
|
|
537
|
+
return [
|
|
538
|
+
` Collateral Value: $${formatAmount(health.collateralValue, 18)}`,
|
|
539
|
+
` Debt: ${formatAmount(health.debt, 18)} GRIT`,
|
|
540
|
+
` LTV: ${ltvPct}%`,
|
|
541
|
+
` Liquidation Price: $${formatAmount(health.liquidationPrice, 18)}`,
|
|
542
|
+
].join("\n");
|
|
543
|
+
}
|
|
544
|
+
formatSystemStatus(status) {
|
|
545
|
+
const liqRatioPct = Number(status.liquidationRatio * 100n / WAD);
|
|
546
|
+
const redemptionPriceUsd = Number(status.redemptionPrice * 1000000n / RAY) / 1000000;
|
|
547
|
+
const rateDelta = Number(status.redemptionRate - RAY);
|
|
548
|
+
const rateSign = rateDelta >= 0 ? "+" : "";
|
|
549
|
+
return [
|
|
550
|
+
` BTC/USD: $${formatAmount(status.collateralPrice, 18)}`,
|
|
551
|
+
` GRIT Market: $${formatAmount(status.marketPrice, 18)}`,
|
|
552
|
+
` Redemption Price: $${redemptionPriceUsd.toFixed(6)}`,
|
|
553
|
+
` Redemption Rate: ${rateSign}${rateDelta} (per-second, RAY delta)`,
|
|
554
|
+
` Total Debt: ${formatAmount(status.totalDebt, 18)} GRIT`,
|
|
555
|
+
` Total Collateral: ${formatAmount(status.totalCollateral, 18)} WBTC (WAD)`,
|
|
556
|
+
` Debt Ceiling: ${formatAmount(status.debtCeiling, 18)} GRIT`,
|
|
557
|
+
` Liquidation Ratio: ${liqRatioPct}%`,
|
|
558
|
+
].join("\n");
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
export { WAD, RAY };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identity Module - ERC-8004 Integration
|
|
3
|
+
*
|
|
4
|
+
* Provides on-chain identity and reputation for the agent.
|
|
5
|
+
*/
|
|
6
|
+
import { type RpcProvider, type Account } from "starknet";
|
|
7
|
+
export interface AgentIdentity {
|
|
8
|
+
tokenId: bigint;
|
|
9
|
+
name: string;
|
|
10
|
+
agentType: string;
|
|
11
|
+
version: string;
|
|
12
|
+
}
|
|
13
|
+
export declare class IdentityClient {
|
|
14
|
+
private contract;
|
|
15
|
+
constructor(registryAddress: string, providerOrAccount: RpcProvider | Account);
|
|
16
|
+
/**
|
|
17
|
+
* Get agent metadata by token ID
|
|
18
|
+
*/
|
|
19
|
+
getMetadata(tokenId: bigint, key: string): Promise<string>;
|
|
20
|
+
/**
|
|
21
|
+
* Get full agent identity
|
|
22
|
+
*/
|
|
23
|
+
getIdentity(tokenId: bigint): Promise<AgentIdentity | null>;
|
|
24
|
+
}
|
package/dist/identity.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identity Module - ERC-8004 Integration
|
|
3
|
+
*
|
|
4
|
+
* Provides on-chain identity and reputation for the agent.
|
|
5
|
+
*/
|
|
6
|
+
import { Contract } from "starknet";
|
|
7
|
+
// Minimal ABI for IdentityRegistry
|
|
8
|
+
const IDENTITY_REGISTRY_ABI = [
|
|
9
|
+
{
|
|
10
|
+
type: "interface",
|
|
11
|
+
name: "IIdentityRegistry",
|
|
12
|
+
items: [
|
|
13
|
+
{
|
|
14
|
+
type: "function",
|
|
15
|
+
name: "get_metadata",
|
|
16
|
+
inputs: [
|
|
17
|
+
{ name: "token_id", type: "core::integer::u256" },
|
|
18
|
+
{ name: "key", type: "core::byte_array::ByteArray" },
|
|
19
|
+
],
|
|
20
|
+
outputs: [{ type: "core::byte_array::ByteArray" }],
|
|
21
|
+
state_mutability: "view",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
type: "function",
|
|
25
|
+
name: "set_metadata",
|
|
26
|
+
inputs: [
|
|
27
|
+
{ name: "token_id", type: "core::integer::u256" },
|
|
28
|
+
{ name: "key", type: "core::byte_array::ByteArray" },
|
|
29
|
+
{ name: "value", type: "core::byte_array::ByteArray" },
|
|
30
|
+
],
|
|
31
|
+
outputs: [],
|
|
32
|
+
state_mutability: "external",
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
export class IdentityClient {
|
|
38
|
+
contract;
|
|
39
|
+
constructor(registryAddress, providerOrAccount) {
|
|
40
|
+
this.contract = new Contract({
|
|
41
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
42
|
+
address: registryAddress,
|
|
43
|
+
providerOrAccount,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get agent metadata by token ID
|
|
48
|
+
*/
|
|
49
|
+
async getMetadata(tokenId, key) {
|
|
50
|
+
try {
|
|
51
|
+
const result = await this.contract.get_metadata(tokenId, key);
|
|
52
|
+
return String(result);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return "";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get full agent identity
|
|
60
|
+
*/
|
|
61
|
+
async getIdentity(tokenId) {
|
|
62
|
+
try {
|
|
63
|
+
const [name, agentType, version] = await Promise.all([
|
|
64
|
+
this.getMetadata(tokenId, "agentName"),
|
|
65
|
+
this.getMetadata(tokenId, "agentType"),
|
|
66
|
+
this.getMetadata(tokenId, "version"),
|
|
67
|
+
]);
|
|
68
|
+
return { tokenId, name, agentType, version };
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|