@gblin-protocol/mcp-server 0.3.2 → 0.4.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/AGENTS.md +10 -8
- package/README.md +194 -381
- package/dist/abi.d.ts +541 -74
- package/dist/abi.d.ts.map +1 -1
- package/dist/abi.js +56 -25
- package/dist/abi.js.map +1 -1
- package/dist/auction.d.ts +81 -0
- package/dist/auction.d.ts.map +1 -0
- package/dist/auction.js +161 -0
- package/dist/auction.js.map +1 -0
- package/dist/client.d.ts +12 -12
- package/dist/config.d.ts +6 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +17 -9
- package/dist/config.js.map +1 -1
- package/dist/helpers.d.ts +3 -7
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +34 -34
- package/dist/helpers.js.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/init.js +2 -2
- package/dist/paywall.d.ts.map +1 -1
- package/dist/paywall.js +1 -2
- package/dist/paywall.js.map +1 -1
- package/dist/receipts.d.ts +8 -0
- package/dist/receipts.d.ts.map +1 -1
- package/dist/receipts.js +5 -6
- package/dist/receipts.js.map +1 -1
- package/dist/tools.d.ts +28 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +184 -149
- package/dist/tools.js.map +1 -1
- package/llms.txt +56 -40
- package/package.json +1 -1
- package/dist/keeper.d.ts +0 -51
- package/dist/keeper.d.ts.map +0 -1
- package/dist/keeper.js +0 -251
- package/dist/keeper.js.map +0 -1
package/dist/tools.js
CHANGED
|
@@ -10,20 +10,20 @@
|
|
|
10
10
|
* 5. analyze_treasury_health → balances + gas check + runway estimate (PAID)
|
|
11
11
|
* 6. get_governance_state → verify 48h timelock ownership + pending ops
|
|
12
12
|
* 7. share_skill_with_peer → portable JSON skill seed + referral code
|
|
13
|
-
* 8.
|
|
13
|
+
* 8. get_auction_state → the rebalancing auction: side, gap and premium per row, with the bid
|
|
14
14
|
* 9. get_market_risk_regime → on-chain BTC/ETH risk regime signal (PAID)
|
|
15
15
|
* 10. verify_risk_attestation → verify a perishable Risk Attestation (FREE)
|
|
16
16
|
*/
|
|
17
|
-
import { encodeFunctionData, formatUnits, getAddress, hashTypedData, isAddress, parseUnits, recoverTypedDataAddress, } from "viem";
|
|
17
|
+
import { encodeFunctionData, formatUnits, getAddress, hashTypedData, isAddress, parseUnits, recoverTypedDataAddress, encodeAbiParameters, keccak256, } from "viem";
|
|
18
18
|
import { parseAbi } from "viem";
|
|
19
19
|
import { z } from "zod";
|
|
20
|
-
import { ERC20_ABI, GBLIN_ABI, TIMELOCK_ABI } from "./abi.js";
|
|
20
|
+
import { ERC20_ABI, GBLIN_ABI, TIMELOCK_ABI, LENS_ABI, ZAP_ABI, } from "./abi.js";
|
|
21
21
|
import { client, getOnChainTimestamp } from "./client.js";
|
|
22
22
|
import { requirePayment } from "./paywall.js";
|
|
23
|
-
import { EXPECTED_MIN_DELAY_SECONDS, GBLIN_ATTESTOR, GBLIN_GUARDIAN, GBLIN_TIMELOCK,
|
|
23
|
+
import { EXPECTED_MIN_DELAY_SECONDS, GBLIN_ATTESTOR, GBLIN_GUARDIAN, GBLIN_TIMELOCK, GBLIN_LENS, GBLIN_PREVIOUS, GBLIN_VAULT, GBLIN_ZAP, USDC, WETH, WETH_USDC_POOL_FEE, } from "./config.js";
|
|
24
24
|
import { applySlippageBuffer, checkCooldown, getBasketState, getDynamicSlippage, getEthPriceUsd, getNavUsd, getWalletBalances, quoteGblinForUsdc, } from "./helpers.js";
|
|
25
25
|
import { PACKAGE_VERSION } from "./config.js";
|
|
26
|
-
import {
|
|
26
|
+
import { getAuctionState } from "./auction.js";
|
|
27
27
|
import { RECEIPT_TOOL_DEFINITIONS, RECEIPT_TOOL_HANDLERS } from "./receipts.js";
|
|
28
28
|
// ───────────────────────────────────────────────────────────────────────────
|
|
29
29
|
// SwapRouter02 (Uniswap V3 on Base) — used for USDC→WETH exactInputSingle
|
|
@@ -36,6 +36,13 @@ const SWAP_ROUTER_ABI = parseAbi([
|
|
|
36
36
|
// ERC-8021 Builder Code attribution (Base Builder Rewards)
|
|
37
37
|
// ───────────────────────────────────────────────────────────────────────────
|
|
38
38
|
const BUILDER_CODE_SUFFIX = "62635f6762646f33326a300b0080218021802180218021802180218021";
|
|
39
|
+
// Routing data the Zap hands to its swap adapter: the Uniswap V3 fee tier of the pair, ABI-encoded.
|
|
40
|
+
const VENUE_FEE_DATA = encodeAbiParameters([{ type: "uint24" }], [WETH_USDC_POOL_FEE]);
|
|
41
|
+
/** One routing entry per basket row, index for index; WETH and abandoned rows ignore theirs. */
|
|
42
|
+
async function venueDataPerRow() {
|
|
43
|
+
const n = await client.readContract({ address: GBLIN_LENS, abi: LENS_ABI, functionName: "basketLength", args: [GBLIN_VAULT] }).catch(() => 3n);
|
|
44
|
+
return Array.from({ length: Number(n) }, () => VENUE_FEE_DATA);
|
|
45
|
+
}
|
|
39
46
|
function appendBuilderCode(calldata) {
|
|
40
47
|
// Strip 0x if present, append suffix, restore 0x prefix
|
|
41
48
|
const hex = calldata.startsWith("0x") ? calldata.slice(2) : calldata;
|
|
@@ -90,15 +97,21 @@ export const GET_TREASURY_STATE_DEFINITION = {
|
|
|
90
97
|
};
|
|
91
98
|
export async function handleGetTreasuryState() {
|
|
92
99
|
try {
|
|
93
|
-
const [navUsd, ethPriceUsd, basket, slippage] = await Promise.all([
|
|
100
|
+
const [navUsd, ethPriceUsd, basket, slippage, navReliable, mgmtFeeBps, openedAt] = await Promise.all([
|
|
94
101
|
getNavUsd(),
|
|
95
102
|
getEthPriceUsd(),
|
|
96
103
|
getBasketState(),
|
|
97
104
|
getDynamicSlippage(),
|
|
105
|
+
client.readContract({ address: GBLIN_VAULT, abi: GBLIN_ABI, functionName: "isNavReliable" }).catch(() => false),
|
|
106
|
+
client.readContract({ address: GBLIN_LENS, abi: LENS_ABI, functionName: "managementFeeBps", args: [GBLIN_VAULT] }).catch(() => 0n),
|
|
107
|
+
client.readContract({ address: GBLIN_LENS, abi: LENS_ABI, functionName: "auctionOpenedAt", args: [GBLIN_VAULT] }).catch(() => 0n),
|
|
98
108
|
]);
|
|
99
109
|
return toolResult({
|
|
100
110
|
nav_usd: Number(navUsd.toFixed(6)),
|
|
101
111
|
eth_price_usd: Number(ethPriceUsd.toFixed(2)),
|
|
112
|
+
nav_reliable: navReliable,
|
|
113
|
+
management_fee_bps_per_year: Number(mgmtFeeBps),
|
|
114
|
+
auction_open: openedAt !== 0n,
|
|
102
115
|
crash_shield_active: basket.crashShieldActive,
|
|
103
116
|
slippage_buffer_pct: slippage.pct,
|
|
104
117
|
slippage_reason: slippage.reason,
|
|
@@ -108,10 +121,10 @@ export async function handleGetTreasuryState() {
|
|
|
108
121
|
base_weight_pct: e.baseWeightBps / 100,
|
|
109
122
|
dynamic_weight_pct: e.dynamicWeightBps / 100,
|
|
110
123
|
slashed: e.isSlashed,
|
|
111
|
-
pool_fee_bps: e.poolFee,
|
|
112
124
|
})),
|
|
113
125
|
meta: {
|
|
114
|
-
|
|
126
|
+
vault: GBLIN_VAULT,
|
|
127
|
+
lens: GBLIN_LENS,
|
|
115
128
|
chain: "base",
|
|
116
129
|
chain_id: 8453,
|
|
117
130
|
},
|
|
@@ -156,14 +169,11 @@ export async function handleQuoteSafeSwap(args) {
|
|
|
156
169
|
const slippage = await getDynamicSlippage();
|
|
157
170
|
const amountWei = parseUnits(parsed.amount_in, 18);
|
|
158
171
|
if (parsed.direction === "buy") {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
abi: GBLIN_ABI,
|
|
165
|
-
functionName: "quoteBuyGBLIN",
|
|
166
|
-
args: [amountWei],
|
|
172
|
+
const [gblinOut, protocolFee, stabFee] = await client.readContract({
|
|
173
|
+
address: GBLIN_LENS,
|
|
174
|
+
abi: LENS_ABI,
|
|
175
|
+
functionName: "quoteBuy",
|
|
176
|
+
args: [GBLIN_VAULT, amountWei],
|
|
167
177
|
});
|
|
168
178
|
const safeMin = applySlippageBuffer(gblinOut, slippage.bps);
|
|
169
179
|
return toolResult({
|
|
@@ -172,7 +182,7 @@ export async function handleQuoteSafeSwap(args) {
|
|
|
172
182
|
expected_gblin_out: formatUnits(gblinOut, 18),
|
|
173
183
|
safe_min_gblin_out: formatUnits(safeMin, 18),
|
|
174
184
|
fees: {
|
|
175
|
-
|
|
185
|
+
protocol_eth: formatUnits(protocolFee, 18),
|
|
176
186
|
stability_eth: formatUnits(stabFee, 18),
|
|
177
187
|
total_fee_bps: 10,
|
|
178
188
|
},
|
|
@@ -184,10 +194,10 @@ export async function handleQuoteSafeSwap(args) {
|
|
|
184
194
|
}
|
|
185
195
|
// sell
|
|
186
196
|
const ethOut = await client.readContract({
|
|
187
|
-
address:
|
|
188
|
-
abi:
|
|
189
|
-
functionName: "
|
|
190
|
-
args: [amountWei],
|
|
197
|
+
address: GBLIN_LENS,
|
|
198
|
+
abi: LENS_ABI,
|
|
199
|
+
functionName: "quoteSell",
|
|
200
|
+
args: [GBLIN_VAULT, amountWei],
|
|
191
201
|
});
|
|
192
202
|
const safeMin = applySlippageBuffer(ethOut, slippage.bps);
|
|
193
203
|
return toolResult({
|
|
@@ -197,8 +207,8 @@ export async function handleQuoteSafeSwap(args) {
|
|
|
197
207
|
safe_min_eth_out: formatUnits(safeMin, 18),
|
|
198
208
|
slippage_buffer_bps: Number(slippage.bps),
|
|
199
209
|
slippage_reason: slippage.reason,
|
|
200
|
-
cooldown_note: "
|
|
201
|
-
next_step: "
|
|
210
|
+
cooldown_note: "A redemption reverts with CooldownActive inside the vault's cooldown after your own mint (read live by analyze_treasury_health).",
|
|
211
|
+
next_step: "Redeem in kind with vault.sellGBLIN(amount_in) (no fee, no price feed), or exit to ETH through the Zap: approve the shares to it, then GBLINZap.sellGBLINForEth(amount_in, safe_min_eth_out, venueData, receiver) — all or nothing.",
|
|
202
212
|
});
|
|
203
213
|
}
|
|
204
214
|
catch (err) {
|
|
@@ -214,7 +224,7 @@ const JitSwapSchema = z.object({
|
|
|
214
224
|
});
|
|
215
225
|
export const JIT_SWAP_DEFINITION = {
|
|
216
226
|
name: "swap_gblin_to_usdc_jit",
|
|
217
|
-
description: "When an x402 invoice (or any USDC obligation) arrives and your treasury sits in GBLIN, call this to get ready-to-broadcast calldata that redeems exactly the USDC you need, just in time. Two sequential transactions: (1) redeem GBLIN to ETH directly against the contract (sellGBLINForEth), (2) a Uniswap WETH->USDC swap. EOAs sign twice; ERC-4337 / EIP-7702 wallets can batch both into one UserOp. MEV-safe minOut included, never zero. Free to call
|
|
227
|
+
description: "When an x402 invoice (or any USDC obligation) arrives and your treasury sits in GBLIN, call this to get ready-to-broadcast calldata that redeems exactly the USDC you need, just in time. Two sequential transactions: (1) redeem GBLIN to ETH directly against the contract (sellGBLINForEth), (2) a Uniswap WETH->USDC swap. EOAs sign twice; ERC-4337 / EIP-7702 wallets can batch both into one UserOp. MEV-safe minOut included, never zero. Free to call. A redemption carries no protocol fee; the costs are the Zap's swaps and gas.",
|
|
218
228
|
inputSchema: {
|
|
219
229
|
type: "object",
|
|
220
230
|
properties: {
|
|
@@ -244,26 +254,34 @@ export async function handleJitSwap(args) {
|
|
|
244
254
|
}
|
|
245
255
|
// 2. Reverse quote: how much GBLIN to sell?
|
|
246
256
|
const quote = await quoteGblinForUsdc(parsed.usdc_needed);
|
|
247
|
-
// 3a. Quote the ETH leg:
|
|
257
|
+
// 3a. Quote the ETH leg through the Lens: what the vault would pay for that many shares.
|
|
248
258
|
const ethExpected = await client.readContract({
|
|
249
|
-
address:
|
|
250
|
-
abi:
|
|
251
|
-
functionName: "
|
|
252
|
-
args: [quote.gblinToSell],
|
|
259
|
+
address: GBLIN_LENS,
|
|
260
|
+
abi: LENS_ABI,
|
|
261
|
+
functionName: "quoteSell",
|
|
262
|
+
args: [GBLIN_VAULT, quote.gblinToSell],
|
|
253
263
|
});
|
|
254
264
|
const minEthOut = applySlippageBuffer(ethExpected, quote.slippage.bps);
|
|
255
265
|
if (minEthOut === 0n) {
|
|
256
266
|
return toolError("Quote returned zero ETH out - oracle stale or amount too small.");
|
|
257
267
|
}
|
|
258
|
-
//
|
|
259
|
-
// TX1:
|
|
260
|
-
// TX2:
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
|
|
268
|
+
// The vault redeems in kind and never swaps, so GBLIN -> USDC is three steps:
|
|
269
|
+
// TX1: approve the shares to the Zap (it pulls them);
|
|
270
|
+
// TX2: Zap.sellGBLINForEth(shares, minEthOut, venueData, receiver): redeem in kind on the vault,
|
|
271
|
+
// sell every leg, deliver ETH — all or nothing, so a leg that cannot be sold reverts the
|
|
272
|
+
// whole step instead of paying out less;
|
|
273
|
+
// TX3: Uniswap exactInputSingle WETH->USDC, paid with the ETH received.
|
|
274
|
+
// TX3 amountIn = minEthOut (the GUARANTEED minimum from TX2), so the last tx can never request
|
|
275
|
+
// more ETH than the previous one actually delivered.
|
|
276
|
+
const approveSharesCalldata = encodeFunctionData({
|
|
264
277
|
abi: GBLIN_ABI,
|
|
278
|
+
functionName: "approve",
|
|
279
|
+
args: [GBLIN_ZAP, quote.gblinToSell],
|
|
280
|
+
});
|
|
281
|
+
const sellCalldata = encodeFunctionData({
|
|
282
|
+
abi: ZAP_ABI,
|
|
265
283
|
functionName: "sellGBLINForEth",
|
|
266
|
-
args: [quote.gblinToSell, minEthOut],
|
|
284
|
+
args: [quote.gblinToSell, minEthOut, await venueDataPerRow(), parsed.wallet_address],
|
|
267
285
|
});
|
|
268
286
|
const swapCalldata = encodeFunctionData({
|
|
269
287
|
abi: SWAP_ROUTER_ABI,
|
|
@@ -285,13 +303,20 @@ export async function handleJitSwap(args) {
|
|
|
285
303
|
steps: [
|
|
286
304
|
{
|
|
287
305
|
step: 1,
|
|
288
|
-
description: "
|
|
289
|
-
target:
|
|
290
|
-
calldata: appendBuilderCode(
|
|
306
|
+
description: "Approve the shares to the GBLIN Zap",
|
|
307
|
+
target: GBLIN_VAULT,
|
|
308
|
+
calldata: appendBuilderCode(approveSharesCalldata),
|
|
291
309
|
value: "0",
|
|
292
310
|
},
|
|
293
311
|
{
|
|
294
312
|
step: 2,
|
|
313
|
+
description: "Redeem in kind and sell every leg for ETH through the Zap (all or nothing)",
|
|
314
|
+
target: GBLIN_ZAP,
|
|
315
|
+
calldata: appendBuilderCode(sellCalldata),
|
|
316
|
+
value: "0",
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
step: 3,
|
|
295
320
|
description: "Swap the received ETH to USDC via Uniswap V3 (WETH->USDC)",
|
|
296
321
|
target: SWAP_ROUTER_02,
|
|
297
322
|
calldata: swapCalldata,
|
|
@@ -315,7 +340,7 @@ export async function handleJitSwap(args) {
|
|
|
315
340
|
eoa: true,
|
|
316
341
|
erc4337: true,
|
|
317
342
|
eip7702: true,
|
|
318
|
-
note: "
|
|
343
|
+
note: "Three steps: approve, the Zap exit, the WETH->USDC swap. EOAs sign three times; ERC-4337 / EIP-7702 wallets can batch all of them into one UserOp.",
|
|
319
344
|
},
|
|
320
345
|
gas_hint: 600_000,
|
|
321
346
|
});
|
|
@@ -333,7 +358,7 @@ const InvestSchema = z.object({
|
|
|
333
358
|
});
|
|
334
359
|
export const INVEST_DEFINITION = {
|
|
335
360
|
name: "invest_usdc_to_gblin",
|
|
336
|
-
description: "When your agent's idle USDC exceeds operating needs (rule of thumb: more than 7x daily burn), call this to park the SURPLUS into GBLIN — managed crypto exposure minted at NAV directly from the contract, redeemable any time via swap_gblin_to_usdc_jit. Returns two steps of calldata: (1) approve USDC to the GBLIN contract, (2) buyGBLINInKind(USDC, amount, minGblinOut). No Uniswap leg, MEV-safe minOut, never zero. Free to call -
|
|
361
|
+
description: "When your agent's idle USDC exceeds operating needs (rule of thumb: more than 7x daily burn), call this to park the SURPLUS into GBLIN — managed crypto exposure minted at NAV directly from the contract, redeemable any time via swap_gblin_to_usdc_jit. Returns two steps of calldata: (1) approve USDC to the GBLIN contract, (2) buyGBLINInKind(USDC, amount, minGblinOut). No Uniswap leg, MEV-safe minOut, never zero. Free to call. An in-kind deposit pays a fee of 0.50% to 2.00% (the floor, plus a tax when the deposit moves the USDC row away from its target); 0.05% of it is minted as shares to the fee recipient and the rest stays in the vault.",
|
|
337
362
|
inputSchema: {
|
|
338
363
|
type: "object",
|
|
339
364
|
properties: {
|
|
@@ -373,46 +398,45 @@ export async function handleInvest(args) {
|
|
|
373
398
|
const ethPriceScaled = BigInt(Math.round(ethPriceUsd * 1_000_000)); // 6 decimals
|
|
374
399
|
const wethExpected = (usdcUnits * parseUnits("1", 18)) / ethPriceScaled;
|
|
375
400
|
const minWethOut = applySlippageBuffer(wethExpected, slippage.bps);
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
379
|
-
// Quote GBLIN out: value the USDC in ETH terms (USDC ~ $1) via the contract's
|
|
380
|
-
// own buy quote, then buffer with dynamic slippage so the call won't revert.
|
|
401
|
+
// Quote the shares for that much WETH through the Lens, then buffer with the dynamic slippage so
|
|
402
|
+
// the mint does not revert on a small move between quote and block.
|
|
381
403
|
const [gblinExpected] = await client.readContract({
|
|
382
|
-
address:
|
|
383
|
-
abi:
|
|
384
|
-
functionName: "
|
|
385
|
-
args: [wethExpected],
|
|
404
|
+
address: GBLIN_LENS,
|
|
405
|
+
abi: LENS_ABI,
|
|
406
|
+
functionName: "quoteBuy",
|
|
407
|
+
args: [GBLIN_VAULT, wethExpected],
|
|
386
408
|
});
|
|
387
409
|
const minGblinOut = applySlippageBuffer(gblinExpected, slippage.bps);
|
|
388
|
-
//
|
|
389
|
-
//
|
|
390
|
-
//
|
|
410
|
+
// Two steps, through the Zap: approve USDC to it, then one call that swaps USDC -> WETH on the
|
|
411
|
+
// adapter and mints at NAV. The allowance goes to the Zap, never to the vault, and both bounds
|
|
412
|
+
// travel with the call: minWethOut on the swap, minGblinOut on the mint. (Depositing USDC in kind
|
|
413
|
+
// directly on the vault also works, but pays the in-kind floor of 0.50% plus a deviation tax; the
|
|
414
|
+
// Zap path pays the 0.10% mint fee plus the pool's own cost.)
|
|
391
415
|
const approveUsdcCalldata = encodeFunctionData({
|
|
392
416
|
abi: ERC20_ABI,
|
|
393
417
|
functionName: "approve",
|
|
394
|
-
args: [
|
|
418
|
+
args: [GBLIN_ZAP, usdcUnits],
|
|
395
419
|
});
|
|
396
420
|
const buyCalldata = encodeFunctionData({
|
|
397
|
-
abi:
|
|
398
|
-
functionName: "
|
|
399
|
-
args: [USDC, usdcUnits, minGblinOut],
|
|
421
|
+
abi: ZAP_ABI,
|
|
422
|
+
functionName: "buyGBLINWithToken",
|
|
423
|
+
args: [USDC, usdcUnits, minWethOut, minGblinOut, VENUE_FEE_DATA, parsed.wallet_address],
|
|
400
424
|
});
|
|
401
425
|
return toolResult({
|
|
402
426
|
action: "sequential_txs",
|
|
403
427
|
steps: [
|
|
404
428
|
{
|
|
405
429
|
step: 1,
|
|
406
|
-
description: "Approve USDC to the GBLIN
|
|
430
|
+
description: "Approve USDC to the GBLIN Zap",
|
|
407
431
|
target: USDC,
|
|
408
432
|
calldata: appendBuilderCode(approveUsdcCalldata),
|
|
409
433
|
value: "0",
|
|
410
434
|
},
|
|
411
435
|
{
|
|
412
436
|
step: 2,
|
|
413
|
-
description: "
|
|
414
|
-
target:
|
|
415
|
-
calldata: buyCalldata,
|
|
437
|
+
description: "Swap USDC to WETH and mint GBLIN at NAV, in one transaction",
|
|
438
|
+
target: GBLIN_ZAP,
|
|
439
|
+
calldata: appendBuilderCode(buyCalldata),
|
|
416
440
|
value: "0",
|
|
417
441
|
},
|
|
418
442
|
],
|
|
@@ -424,7 +448,7 @@ export async function handleInvest(args) {
|
|
|
424
448
|
security: {
|
|
425
449
|
mev_protected: true,
|
|
426
450
|
min_outs_set: true,
|
|
427
|
-
note: "minGblinOut
|
|
451
|
+
note: "minWethOut and minGblinOut are both above zero, computed from the oracle price and the Lens quote plus the dynamic slippage. Either bound failing reverts the whole call.",
|
|
428
452
|
},
|
|
429
453
|
});
|
|
430
454
|
}
|
|
@@ -564,7 +588,7 @@ const GovernanceStateSchema = z.object({
|
|
|
564
588
|
});
|
|
565
589
|
export const GET_GOVERNANCE_STATE_DEFINITION = {
|
|
566
590
|
name: "get_governance_state",
|
|
567
|
-
description: "Verify GBLIN protocol governance state: confirms whether
|
|
591
|
+
description: "Verify GBLIN protocol governance state: confirms whether the GBLIN vault is owned by the 48h Timelock, reads the timelock's min delay and grace period, reports role member counts, and surfaces any pending asset-addition proposal on the index contract. If an operation_id is provided, also reports the status of that specific timelock operation. Read-only — use this to gate trust-sensitive agent actions.",
|
|
568
592
|
inputSchema: {
|
|
569
593
|
type: "object",
|
|
570
594
|
properties: {
|
|
@@ -585,22 +609,23 @@ export async function handleGetGovernanceState(args) {
|
|
|
585
609
|
return toolError(`Invalid arguments: ${e.message}`);
|
|
586
610
|
}
|
|
587
611
|
try {
|
|
588
|
-
// 1.
|
|
589
|
-
const [owner,
|
|
612
|
+
// 1. Owner, pending owner and fee recipient of the vault.
|
|
613
|
+
const [owner, pendingOwner, feeRecipient] = await Promise.all([
|
|
590
614
|
client.readContract({
|
|
591
|
-
address:
|
|
615
|
+
address: GBLIN_VAULT,
|
|
592
616
|
abi: GBLIN_ABI,
|
|
593
617
|
functionName: "owner",
|
|
594
618
|
}),
|
|
595
619
|
client.readContract({
|
|
596
|
-
address:
|
|
620
|
+
address: GBLIN_VAULT,
|
|
597
621
|
abi: GBLIN_ABI,
|
|
598
|
-
functionName: "
|
|
622
|
+
functionName: "pendingOwner",
|
|
599
623
|
}),
|
|
600
624
|
client.readContract({
|
|
601
|
-
address:
|
|
602
|
-
abi:
|
|
603
|
-
functionName: "
|
|
625
|
+
address: GBLIN_LENS,
|
|
626
|
+
abi: LENS_ABI,
|
|
627
|
+
functionName: "feeRecipient",
|
|
628
|
+
args: [GBLIN_VAULT],
|
|
604
629
|
}),
|
|
605
630
|
]);
|
|
606
631
|
const ownerNorm = getAddress(owner);
|
|
@@ -641,17 +666,13 @@ export async function handleGetGovernanceState(args) {
|
|
|
641
666
|
// OZ TimelockController v5 uses AccessControl (not AccessControlEnumerable),
|
|
642
667
|
// so getRoleMemberCount is unavailable. Use hasRole on known addresses instead.
|
|
643
668
|
const ZERO = "0x0000000000000000000000000000000000000000";
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
abi: GBLIN_ABI,
|
|
647
|
-
functionName: "founderWallet",
|
|
648
|
-
});
|
|
649
|
-
const [founderIsProposer, guardianIsCanceller, timelockIsSelfAdmin, executorOpen,] = await Promise.all([
|
|
669
|
+
const feeRecipientAddr = feeRecipient;
|
|
670
|
+
const [feeRecipientIsProposer, guardianIsCanceller, timelockIsSelfAdmin, executorOpen,] = await Promise.all([
|
|
650
671
|
client.readContract({
|
|
651
672
|
address: GBLIN_TIMELOCK,
|
|
652
673
|
abi: TIMELOCK_ABI,
|
|
653
674
|
functionName: "hasRole",
|
|
654
|
-
args: [proposerRole,
|
|
675
|
+
args: [proposerRole, feeRecipientAddr],
|
|
655
676
|
}),
|
|
656
677
|
// Guardian multisig should hold CANCELLER_ROLE (veto power)
|
|
657
678
|
client.readContract({
|
|
@@ -682,7 +703,7 @@ export async function handleGetGovernanceState(args) {
|
|
|
682
703
|
min_delay_matches_expected: minDelay === EXPECTED_MIN_DELAY_SECONDS,
|
|
683
704
|
expected_min_delay_seconds: Number(EXPECTED_MIN_DELAY_SECONDS),
|
|
684
705
|
roles: {
|
|
685
|
-
|
|
706
|
+
fee_recipient_is_proposer: feeRecipientIsProposer,
|
|
686
707
|
guardian_is_canceller: guardianIsCanceller,
|
|
687
708
|
timelock_is_self_admin: timelockIsSelfAdmin,
|
|
688
709
|
executor_open_to_anyone: executorOpen,
|
|
@@ -758,40 +779,63 @@ export async function handleGetGovernanceState(args) {
|
|
|
758
779
|
};
|
|
759
780
|
}
|
|
760
781
|
}
|
|
761
|
-
// 4.
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
782
|
+
// 4. A pending handover. Ownership moves in two steps (transferOwnership, then acceptOwnership by
|
|
783
|
+
// the new owner), and the timelock can only accept through a scheduled operation. When the
|
|
784
|
+
// pending owner is the timelock, the operation id is deterministic — TimelockController v5:
|
|
785
|
+
// keccak256(abi.encode(target, value, data, predecessor, salt)) — so its state can be reported.
|
|
786
|
+
const pendingOwnerNorm = getAddress(pendingOwner);
|
|
787
|
+
const handoverPending = pendingOwnerNorm !== "0x0000000000000000000000000000000000000000";
|
|
788
|
+
let pendingHandover = null;
|
|
789
|
+
if (handoverPending) {
|
|
790
|
+
pendingHandover = { pending_owner: pendingOwnerNorm, pending_owner_is_timelock: pendingOwnerNorm === timelockNorm };
|
|
791
|
+
if (pendingOwnerNorm === timelockNorm) {
|
|
792
|
+
const ZERO32 = `0x${"0".repeat(64)}`;
|
|
793
|
+
const acceptCalldata = encodeFunctionData({ abi: GBLIN_ABI, functionName: "acceptOwnership" });
|
|
794
|
+
const opId = keccak256(encodeAbiParameters([{ type: "address" }, { type: "uint256" }, { type: "bytes" }, { type: "bytes32" }, { type: "bytes32" }], [GBLIN_VAULT, 0n, acceptCalldata, ZERO32, ZERO32]));
|
|
795
|
+
try {
|
|
796
|
+
const [isPending, isReady, isDone, ts] = await Promise.all([
|
|
797
|
+
client.readContract({ address: GBLIN_TIMELOCK, abi: TIMELOCK_ABI, functionName: "isOperationPending", args: [opId] }),
|
|
798
|
+
client.readContract({ address: GBLIN_TIMELOCK, abi: TIMELOCK_ABI, functionName: "isOperationReady", args: [opId] }),
|
|
799
|
+
client.readContract({ address: GBLIN_TIMELOCK, abi: TIMELOCK_ABI, functionName: "isOperationDone", args: [opId] }),
|
|
800
|
+
client.readContract({ address: GBLIN_TIMELOCK, abi: TIMELOCK_ABI, functionName: "getTimestamp", args: [opId] }),
|
|
801
|
+
]);
|
|
802
|
+
const tsNum = Number(ts);
|
|
803
|
+
pendingHandover.accept_ownership_operation = {
|
|
804
|
+
id: opId,
|
|
805
|
+
scheduled: isPending || isDone,
|
|
806
|
+
pending: isPending,
|
|
807
|
+
ready: isReady,
|
|
808
|
+
done: isDone,
|
|
809
|
+
execute_after_unix: tsNum > 1 ? tsNum : null,
|
|
810
|
+
execute_after_iso: tsNum > 1 ? new Date(tsNum * 1000).toISOString() : null,
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
catch (err) {
|
|
814
|
+
pendingHandover.accept_ownership_operation = { id: opId, error: err.message };
|
|
815
|
+
}
|
|
775
816
|
}
|
|
776
|
-
|
|
817
|
+
}
|
|
777
818
|
return toolResult({
|
|
778
|
-
|
|
819
|
+
vault: GBLIN_VAULT,
|
|
820
|
+
lens: GBLIN_LENS,
|
|
779
821
|
owner: ownerNorm,
|
|
780
822
|
owner_is_timelock: ownerIsTimelock,
|
|
781
823
|
owner_is_renounced: ownerIsRenounced,
|
|
782
|
-
|
|
824
|
+
fee_recipient: getAddress(feeRecipient),
|
|
783
825
|
trust_summary: ownerIsRenounced
|
|
784
826
|
? "Ownership fully renounced — no admin can touch the contract."
|
|
785
827
|
: ownerIsTimelock
|
|
786
828
|
? "Ownership held by the 48h Timelock. All admin actions are delay-enforced on-chain."
|
|
787
|
-
:
|
|
829
|
+
: handoverPending && pendingOwnerNorm === timelockNorm
|
|
830
|
+
? "Ownership is held by the deployer with the 48h Timelock as pending owner; see pending_handover for the scheduled acceptance. Until it executes, admin actions are NOT timelocked."
|
|
831
|
+
: "WARNING: owner is an EOA / unknown contract — admin actions are NOT timelocked.",
|
|
788
832
|
timelock: timelockState,
|
|
789
|
-
|
|
833
|
+
pending_handover: pendingHandover,
|
|
790
834
|
pending_timelock_operation: operationStatus,
|
|
791
835
|
verification: {
|
|
792
|
-
|
|
836
|
+
vault_basescan: `https://basescan.org/address/${GBLIN_VAULT}#readContract`,
|
|
837
|
+
lens_basescan: `https://basescan.org/address/${GBLIN_LENS}#readContract`,
|
|
793
838
|
timelock_basescan: `https://basescan.org/address/${GBLIN_TIMELOCK}#readContract`,
|
|
794
|
-
ownership_transfer_tx: "https://basescan.org/tx/0xb653f54ffa9b1764b41932e6a411077e7e34550605303f15d90900de682edaaf",
|
|
795
839
|
},
|
|
796
840
|
});
|
|
797
841
|
}
|
|
@@ -885,12 +929,14 @@ async function handleShareSkillWithPeer(args) {
|
|
|
885
929
|
"analyze_treasury_health",
|
|
886
930
|
"get_governance_state",
|
|
887
931
|
"share_skill_with_peer",
|
|
888
|
-
"
|
|
932
|
+
"get_auction_state",
|
|
889
933
|
"get_market_risk_regime",
|
|
890
934
|
"verify_risk_attestation",
|
|
891
935
|
],
|
|
892
936
|
chain: "Base mainnet (8453)",
|
|
893
|
-
|
|
937
|
+
vault: GBLIN_VAULT,
|
|
938
|
+
lens: GBLIN_LENS,
|
|
939
|
+
zap: GBLIN_ZAP,
|
|
894
940
|
timelock: "0x6aBeC8716fFeEcf7C3D6e68255b4797113E8e5Dd",
|
|
895
941
|
},
|
|
896
942
|
recommended_pattern: {
|
|
@@ -906,19 +952,18 @@ async function handleShareSkillWithPeer(args) {
|
|
|
906
952
|
amount_usdc: example_amount_usdc,
|
|
907
953
|
steps: [
|
|
908
954
|
"1. Call quote_safe_swap with action=buy and the desired USDC amount",
|
|
909
|
-
"2. Call invest_usdc_to_gblin to receive 2-step unsigned calldata (approve USDC
|
|
955
|
+
"2. Call invest_usdc_to_gblin to receive 2-step unsigned calldata (approve USDC to the Zap, then one call that swaps and mints at NAV)",
|
|
910
956
|
"3. Sign and broadcast the 2 transactions atomically via send_calls (Base MCP) or individually",
|
|
911
|
-
"4. When liquidity is needed, call swap_gblin_to_usdc_jit for
|
|
957
|
+
"4. When liquidity is needed, call swap_gblin_to_usdc_jit for the GBLIN→USDC exit (three transactions, batchable)",
|
|
912
958
|
],
|
|
913
|
-
|
|
914
|
-
reference_tx: "https://basescan.org/tx/0x3d1084f6374dff2125ba337208c4b547d9821523a0a51f797f9fb422c9b24e2c",
|
|
959
|
+
verified_source: `https://basescan.org/address/${GBLIN_VAULT}#code`,
|
|
915
960
|
},
|
|
916
961
|
natural_language_explanation: [
|
|
917
962
|
"GBLIN is a treasury-backed index token on Base mainnet, collateralized by WETH, cbBTC, and USDC.",
|
|
918
963
|
"Every GBLIN is backed by real on-chain assets — NAV is computed from Chainlink oracle prices, not from market speculation.",
|
|
919
964
|
"Holding GBLIN as treasury preserves agent capital while keeping liquidity for x402 micropayments via Just-In-Time redemption.",
|
|
920
965
|
"The protocol has zero pre-mint (fair launch), 48-hour governance timelock, and an algorithmic Crash Shield that adaptively reduces risk exposure as basket assets draw down (V6 adaptive threshold, from ~15%).",
|
|
921
|
-
"Installing this MCP server gives your AI agent
|
|
966
|
+
"Installing this MCP server gives your AI agent 13 tools to manage treasury, quote swaps, redeem just-in-time, propagate the skill to other agents, and earn passive ETH income as a keeper.",
|
|
922
967
|
].join(" "),
|
|
923
968
|
referral_disclosure: {
|
|
924
969
|
builder_code_standard: "ERC-8021",
|
|
@@ -957,27 +1002,22 @@ async function handleShareSkillWithPeer(args) {
|
|
|
957
1002
|
return toolResult({ text: responseText });
|
|
958
1003
|
}
|
|
959
1004
|
// ───────────────────────────────────────────────────────────────────────────
|
|
960
|
-
// TOOL 8:
|
|
1005
|
+
// TOOL 8: get_auction_state
|
|
961
1006
|
// ───────────────────────────────────────────────────────────────────────────
|
|
962
|
-
const
|
|
963
|
-
name: "
|
|
964
|
-
description: "
|
|
1007
|
+
const GET_AUCTION_STATE_DEFINITION = {
|
|
1008
|
+
name: "get_auction_state",
|
|
1009
|
+
description: "Read the GBLIN rebalancing auction on Base. The vault does not rebalance itself and pays nobody to do it: when a basket row drifts past its band it holds a Dutch auction, and whoever trades with it toward the target weights is the counterparty, at the oracle price adjusted by a premium that starts at a discount and rises to a cap over one ramp. Returns, per row, the side the vault takes, the gap in ETH, the token and amount the bidder hands over, and unsigned calldata (approval + bid). The premium is the whole reward; nothing is paid out of the vault.",
|
|
965
1010
|
inputSchema: {
|
|
966
1011
|
type: "object",
|
|
967
|
-
properties: {
|
|
968
|
-
_payment: {
|
|
969
|
-
type: "string",
|
|
970
|
-
description: "Base64-encoded x402 PaymentProof JSON. Omit on first call to receive the 402 payment manifest.",
|
|
971
|
-
},
|
|
972
|
-
},
|
|
1012
|
+
properties: {},
|
|
973
1013
|
required: [],
|
|
974
1014
|
additionalProperties: false,
|
|
975
1015
|
},
|
|
976
1016
|
};
|
|
977
|
-
async function
|
|
1017
|
+
export async function handleGetAuctionState() {
|
|
978
1018
|
try {
|
|
979
|
-
const
|
|
980
|
-
return toolResult(
|
|
1019
|
+
const state = await getAuctionState();
|
|
1020
|
+
return toolResult(state);
|
|
981
1021
|
}
|
|
982
1022
|
catch (err) {
|
|
983
1023
|
return toolError(err.message, "Check RPC connectivity.");
|
|
@@ -1041,30 +1081,18 @@ async function handleMarketRiskRegime() {
|
|
|
1041
1081
|
defensive_cash_pct: defensiveCashPct,
|
|
1042
1082
|
assets,
|
|
1043
1083
|
source: "GBLIN on-chain Crash Shield (Base mainnet, Chainlink-oracle drawdown)",
|
|
1044
|
-
verify:
|
|
1045
|
-
meta: {
|
|
1084
|
+
verify: `https://basescan.org/address/${GBLIN_VAULT}#readContract`,
|
|
1085
|
+
meta: { vault: GBLIN_VAULT, lens: GBLIN_LENS, chain: "base", chain_id: 8453 },
|
|
1046
1086
|
});
|
|
1047
1087
|
}
|
|
1048
1088
|
catch (err) {
|
|
1049
1089
|
return toolError(err.message, "Check RPC connectivity and oracle freshness.");
|
|
1050
1090
|
}
|
|
1051
1091
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
// attestation is FREE on purpose: every agent that verifies one is exposed to
|
|
1057
|
-
// GBLIN and learns it can mint its own → drives paid calls to the attestation
|
|
1058
|
-
// endpoint. Keyless integrity + EIP-712 recover + live-drift, no RPC secrets.
|
|
1059
|
-
// ───────────────────────────────────────────────────────────────────────────
|
|
1060
|
-
// EIP-712 schema — MUST stay byte-for-byte identical to the webapp signer
|
|
1061
|
-
// (GBLIN_WEBAPP/src/app/api/x402/attestation/route.ts). Do not reorder fields.
|
|
1062
|
-
const ATTESTATION_EIP712_DOMAIN = {
|
|
1063
|
-
name: "GBLIN Risk Attestation",
|
|
1064
|
-
version: "1",
|
|
1065
|
-
chainId: 8453,
|
|
1066
|
-
verifyingContract: GBLIN_V6,
|
|
1067
|
-
};
|
|
1092
|
+
const ATTESTATION_DOMAIN_V1 = { name: "GBLIN Risk Attestation", version: "1", chainId: 8453, verifyingContract: GBLIN_PREVIOUS };
|
|
1093
|
+
const ATTESTATION_DOMAIN_V2 = { name: "GBLIN Risk Attestation", version: "2", chainId: 8453, verifyingContract: GBLIN_VAULT };
|
|
1094
|
+
const ATTESTATION_EIP712_DOMAINS = { "1": ATTESTATION_DOMAIN_V1, "2": ATTESTATION_DOMAIN_V2 };
|
|
1095
|
+
const ATTESTATION_EIP712_DOMAIN = ATTESTATION_DOMAIN_V2;
|
|
1068
1096
|
const ATTESTATION_EIP712_TYPES = {
|
|
1069
1097
|
RiskAttestation: [
|
|
1070
1098
|
{ name: "regime", type: "uint8" },
|
|
@@ -1117,6 +1145,8 @@ async function handleVerifyRiskAttestation(args) {
|
|
|
1117
1145
|
if (!eip712 || !eip712.message || !eip712.domain || !eip712.types) {
|
|
1118
1146
|
return toolError("Attestation is missing its `eip712` payload (domain/types/message).", "Pass the whole object returned by /api/x402/attestation, including the `eip712` field.");
|
|
1119
1147
|
}
|
|
1148
|
+
const declaredVersion = String(eip712.domain?.version ?? "");
|
|
1149
|
+
const canonicalDomain = ATTESTATION_EIP712_DOMAINS[declaredVersion] ?? ATTESTATION_EIP712_DOMAIN;
|
|
1120
1150
|
// Rebuild the typed message (uint64 fields as bigint for viem).
|
|
1121
1151
|
const m = eip712.message;
|
|
1122
1152
|
let message;
|
|
@@ -1137,7 +1167,7 @@ async function handleVerifyRiskAttestation(args) {
|
|
|
1137
1167
|
// 1. INTEGRITY — recompute the id using the CANONICAL schema from this build
|
|
1138
1168
|
// (never trust the schema embedded in the payload for hashing).
|
|
1139
1169
|
const recomputedId = hashTypedData({
|
|
1140
|
-
domain:
|
|
1170
|
+
domain: canonicalDomain,
|
|
1141
1171
|
types: ATTESTATION_EIP712_TYPES,
|
|
1142
1172
|
primaryType: "RiskAttestation",
|
|
1143
1173
|
message,
|
|
@@ -1146,9 +1176,10 @@ async function handleVerifyRiskAttestation(args) {
|
|
|
1146
1176
|
const integrityOk = claimedId ? recomputedId.toLowerCase() === claimedId.toLowerCase() : null;
|
|
1147
1177
|
// The payload's OWN domain must match the canonical GBLIN domain, else it is
|
|
1148
1178
|
// a different (untrusted) schema even if internally consistent.
|
|
1149
|
-
const schemaMatches = eip712.domain?.name ===
|
|
1150
|
-
String(eip712.domain?.version) ===
|
|
1151
|
-
Number(eip712.domain?.chainId) ===
|
|
1179
|
+
const schemaMatches = eip712.domain?.name === canonicalDomain.name &&
|
|
1180
|
+
String(eip712.domain?.version) === canonicalDomain.version &&
|
|
1181
|
+
Number(eip712.domain?.chainId) === canonicalDomain.chainId &&
|
|
1182
|
+
String(eip712.domain?.verifyingContract ?? "").toLowerCase() === canonicalDomain.verifyingContract.toLowerCase();
|
|
1152
1183
|
// 2. AUTHENTICITY — signature recover
|
|
1153
1184
|
const expectedAttestor = typeof a.expected_attestor === "string" && isAddress(a.expected_attestor)
|
|
1154
1185
|
? getAddress(a.expected_attestor)
|
|
@@ -1160,7 +1191,7 @@ async function handleVerifyRiskAttestation(args) {
|
|
|
1160
1191
|
if (signature) {
|
|
1161
1192
|
try {
|
|
1162
1193
|
recoveredSigner = await recoverTypedDataAddress({
|
|
1163
|
-
domain:
|
|
1194
|
+
domain: canonicalDomain,
|
|
1164
1195
|
types: ATTESTATION_EIP712_TYPES,
|
|
1165
1196
|
primaryType: "RiskAttestation",
|
|
1166
1197
|
message,
|
|
@@ -1249,6 +1280,11 @@ async function handleVerifyRiskAttestation(args) {
|
|
|
1249
1280
|
// ───────────────────────────────────────────────────────────────────────────
|
|
1250
1281
|
// REGISTRY
|
|
1251
1282
|
// ───────────────────────────────────────────────────────────────────────────
|
|
1283
|
+
// MCP tool annotations. Every tool here reads the chain or does pure math and returns data or
|
|
1284
|
+
// unsigned calldata: nothing is written, signed or broadcast, so each one is read-only, safe to
|
|
1285
|
+
// repeat with the same arguments, and reaches an open world (Base mainnet through the configured RPC).
|
|
1286
|
+
// The receipt tools declare their own: sealing appends to a public log.
|
|
1287
|
+
const READ_ONLY_ANNOTATIONS = { readOnlyHint: true, idempotentHint: true, destructiveHint: false, openWorldHint: true };
|
|
1252
1288
|
export const TOOL_DEFINITIONS = [
|
|
1253
1289
|
GET_TREASURY_STATE_DEFINITION,
|
|
1254
1290
|
QUOTE_SAFE_SWAP_DEFINITION,
|
|
@@ -1257,15 +1293,14 @@ export const TOOL_DEFINITIONS = [
|
|
|
1257
1293
|
ANALYZE_TREASURY_DEFINITION,
|
|
1258
1294
|
GET_GOVERNANCE_STATE_DEFINITION,
|
|
1259
1295
|
SHARE_SKILL_DEFINITION,
|
|
1260
|
-
|
|
1296
|
+
GET_AUCTION_STATE_DEFINITION,
|
|
1261
1297
|
MARKET_RISK_DEFINITION,
|
|
1262
1298
|
VERIFY_ATTESTATION_DEFINITION,
|
|
1263
|
-
|
|
1264
|
-
];
|
|
1299
|
+
].map((definition) => ({ ...definition, annotations: READ_ONLY_ANNOTATIONS })).concat(RECEIPT_TOOL_DEFINITIONS);
|
|
1265
1300
|
export const TOOL_HANDLERS = {
|
|
1266
1301
|
// ── FREE tools ─────────────────────────────────────────────────────────────
|
|
1267
1302
|
// Read-only (funnel) + action tools kept free to avoid chicken-and-egg.
|
|
1268
|
-
//
|
|
1303
|
+
// The on-chain protocol fee (0.05% of every mint, paid as shares) captures revenue when GBLIN is used.
|
|
1269
1304
|
get_treasury_state: handleGetTreasuryState,
|
|
1270
1305
|
quote_safe_swap: handleQuoteSafeSwap,
|
|
1271
1306
|
get_governance_state: handleGetGovernanceState,
|
|
@@ -1273,10 +1308,10 @@ export const TOOL_HANDLERS = {
|
|
|
1273
1308
|
swap_gblin_to_usdc_jit: handleJitSwap,
|
|
1274
1309
|
invest_usdc_to_gblin: handleInvest,
|
|
1275
1310
|
verify_risk_attestation: handleVerifyRiskAttestation,
|
|
1311
|
+
get_auction_state: handleGetAuctionState,
|
|
1276
1312
|
// ── PAID tools (x402 intelligence layer) ──────────────────────────────────
|
|
1277
|
-
// Analysis
|
|
1313
|
+
// Analysis — "advice", not transport.
|
|
1278
1314
|
analyze_treasury_health: requirePayment({ priceUsdc: "0.003", priceLabel: "$0.003 USDC per call" }, handleAnalyzeTreasury),
|
|
1279
|
-
find_keeper_bounty: requirePayment({ priceUsdc: "0.001", priceLabel: "$0.001 USDC per call" }, handleFindKeeperBounty),
|
|
1280
1315
|
get_market_risk_regime: requirePayment({ priceUsdc: "0.002", priceLabel: "$0.002 USDC per call" }, handleMarketRiskRegime),
|
|
1281
1316
|
// ── AI Action Receipts (free demo + free reads; paid seals via x402 HTTP) ──
|
|
1282
1317
|
...RECEIPT_TOOL_HANDLERS,
|