@gblin-protocol/mcp-server 0.3.2 → 0.4.1
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 +186 -150
- 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,9 +340,10 @@ 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
|
-
gas_hint:
|
|
345
|
+
gas_hint: 1_100_000,
|
|
346
|
+
gas_hint_note: "Gas limit for the Zap exit step: it uses about 810,000 and forwards gas-capped transfers, so a tight limit reverts.",
|
|
321
347
|
});
|
|
322
348
|
}
|
|
323
349
|
catch (err) {
|
|
@@ -333,7 +359,7 @@ const InvestSchema = z.object({
|
|
|
333
359
|
});
|
|
334
360
|
export const INVEST_DEFINITION = {
|
|
335
361
|
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 -
|
|
362
|
+
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
363
|
inputSchema: {
|
|
338
364
|
type: "object",
|
|
339
365
|
properties: {
|
|
@@ -373,46 +399,45 @@ export async function handleInvest(args) {
|
|
|
373
399
|
const ethPriceScaled = BigInt(Math.round(ethPriceUsd * 1_000_000)); // 6 decimals
|
|
374
400
|
const wethExpected = (usdcUnits * parseUnits("1", 18)) / ethPriceScaled;
|
|
375
401
|
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.
|
|
402
|
+
// Quote the shares for that much WETH through the Lens, then buffer with the dynamic slippage so
|
|
403
|
+
// the mint does not revert on a small move between quote and block.
|
|
381
404
|
const [gblinExpected] = await client.readContract({
|
|
382
|
-
address:
|
|
383
|
-
abi:
|
|
384
|
-
functionName: "
|
|
385
|
-
args: [wethExpected],
|
|
405
|
+
address: GBLIN_LENS,
|
|
406
|
+
abi: LENS_ABI,
|
|
407
|
+
functionName: "quoteBuy",
|
|
408
|
+
args: [GBLIN_VAULT, wethExpected],
|
|
386
409
|
});
|
|
387
410
|
const minGblinOut = applySlippageBuffer(gblinExpected, slippage.bps);
|
|
388
|
-
//
|
|
389
|
-
//
|
|
390
|
-
//
|
|
411
|
+
// Two steps, through the Zap: approve USDC to it, then one call that swaps USDC -> WETH on the
|
|
412
|
+
// adapter and mints at NAV. The allowance goes to the Zap, never to the vault, and both bounds
|
|
413
|
+
// travel with the call: minWethOut on the swap, minGblinOut on the mint. (Depositing USDC in kind
|
|
414
|
+
// directly on the vault also works, but pays the in-kind floor of 0.50% plus a deviation tax; the
|
|
415
|
+
// Zap path pays the 0.10% mint fee plus the pool's own cost.)
|
|
391
416
|
const approveUsdcCalldata = encodeFunctionData({
|
|
392
417
|
abi: ERC20_ABI,
|
|
393
418
|
functionName: "approve",
|
|
394
|
-
args: [
|
|
419
|
+
args: [GBLIN_ZAP, usdcUnits],
|
|
395
420
|
});
|
|
396
421
|
const buyCalldata = encodeFunctionData({
|
|
397
|
-
abi:
|
|
398
|
-
functionName: "
|
|
399
|
-
args: [USDC, usdcUnits, minGblinOut],
|
|
422
|
+
abi: ZAP_ABI,
|
|
423
|
+
functionName: "buyGBLINWithToken",
|
|
424
|
+
args: [USDC, usdcUnits, minWethOut, minGblinOut, VENUE_FEE_DATA, parsed.wallet_address],
|
|
400
425
|
});
|
|
401
426
|
return toolResult({
|
|
402
427
|
action: "sequential_txs",
|
|
403
428
|
steps: [
|
|
404
429
|
{
|
|
405
430
|
step: 1,
|
|
406
|
-
description: "Approve USDC to the GBLIN
|
|
431
|
+
description: "Approve USDC to the GBLIN Zap",
|
|
407
432
|
target: USDC,
|
|
408
433
|
calldata: appendBuilderCode(approveUsdcCalldata),
|
|
409
434
|
value: "0",
|
|
410
435
|
},
|
|
411
436
|
{
|
|
412
437
|
step: 2,
|
|
413
|
-
description: "
|
|
414
|
-
target:
|
|
415
|
-
calldata: buyCalldata,
|
|
438
|
+
description: "Swap USDC to WETH and mint GBLIN at NAV, in one transaction",
|
|
439
|
+
target: GBLIN_ZAP,
|
|
440
|
+
calldata: appendBuilderCode(buyCalldata),
|
|
416
441
|
value: "0",
|
|
417
442
|
},
|
|
418
443
|
],
|
|
@@ -424,7 +449,7 @@ export async function handleInvest(args) {
|
|
|
424
449
|
security: {
|
|
425
450
|
mev_protected: true,
|
|
426
451
|
min_outs_set: true,
|
|
427
|
-
note: "minGblinOut
|
|
452
|
+
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
453
|
},
|
|
429
454
|
});
|
|
430
455
|
}
|
|
@@ -564,7 +589,7 @@ const GovernanceStateSchema = z.object({
|
|
|
564
589
|
});
|
|
565
590
|
export const GET_GOVERNANCE_STATE_DEFINITION = {
|
|
566
591
|
name: "get_governance_state",
|
|
567
|
-
description: "Verify GBLIN protocol governance state: confirms whether
|
|
592
|
+
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
593
|
inputSchema: {
|
|
569
594
|
type: "object",
|
|
570
595
|
properties: {
|
|
@@ -585,22 +610,23 @@ export async function handleGetGovernanceState(args) {
|
|
|
585
610
|
return toolError(`Invalid arguments: ${e.message}`);
|
|
586
611
|
}
|
|
587
612
|
try {
|
|
588
|
-
// 1.
|
|
589
|
-
const [owner,
|
|
613
|
+
// 1. Owner, pending owner and fee recipient of the vault.
|
|
614
|
+
const [owner, pendingOwner, feeRecipient] = await Promise.all([
|
|
590
615
|
client.readContract({
|
|
591
|
-
address:
|
|
616
|
+
address: GBLIN_VAULT,
|
|
592
617
|
abi: GBLIN_ABI,
|
|
593
618
|
functionName: "owner",
|
|
594
619
|
}),
|
|
595
620
|
client.readContract({
|
|
596
|
-
address:
|
|
621
|
+
address: GBLIN_VAULT,
|
|
597
622
|
abi: GBLIN_ABI,
|
|
598
|
-
functionName: "
|
|
623
|
+
functionName: "pendingOwner",
|
|
599
624
|
}),
|
|
600
625
|
client.readContract({
|
|
601
|
-
address:
|
|
602
|
-
abi:
|
|
603
|
-
functionName: "
|
|
626
|
+
address: GBLIN_LENS,
|
|
627
|
+
abi: LENS_ABI,
|
|
628
|
+
functionName: "feeRecipient",
|
|
629
|
+
args: [GBLIN_VAULT],
|
|
604
630
|
}),
|
|
605
631
|
]);
|
|
606
632
|
const ownerNorm = getAddress(owner);
|
|
@@ -641,17 +667,13 @@ export async function handleGetGovernanceState(args) {
|
|
|
641
667
|
// OZ TimelockController v5 uses AccessControl (not AccessControlEnumerable),
|
|
642
668
|
// so getRoleMemberCount is unavailable. Use hasRole on known addresses instead.
|
|
643
669
|
const ZERO = "0x0000000000000000000000000000000000000000";
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
abi: GBLIN_ABI,
|
|
647
|
-
functionName: "founderWallet",
|
|
648
|
-
});
|
|
649
|
-
const [founderIsProposer, guardianIsCanceller, timelockIsSelfAdmin, executorOpen,] = await Promise.all([
|
|
670
|
+
const feeRecipientAddr = feeRecipient;
|
|
671
|
+
const [feeRecipientIsProposer, guardianIsCanceller, timelockIsSelfAdmin, executorOpen,] = await Promise.all([
|
|
650
672
|
client.readContract({
|
|
651
673
|
address: GBLIN_TIMELOCK,
|
|
652
674
|
abi: TIMELOCK_ABI,
|
|
653
675
|
functionName: "hasRole",
|
|
654
|
-
args: [proposerRole,
|
|
676
|
+
args: [proposerRole, feeRecipientAddr],
|
|
655
677
|
}),
|
|
656
678
|
// Guardian multisig should hold CANCELLER_ROLE (veto power)
|
|
657
679
|
client.readContract({
|
|
@@ -682,7 +704,7 @@ export async function handleGetGovernanceState(args) {
|
|
|
682
704
|
min_delay_matches_expected: minDelay === EXPECTED_MIN_DELAY_SECONDS,
|
|
683
705
|
expected_min_delay_seconds: Number(EXPECTED_MIN_DELAY_SECONDS),
|
|
684
706
|
roles: {
|
|
685
|
-
|
|
707
|
+
fee_recipient_is_proposer: feeRecipientIsProposer,
|
|
686
708
|
guardian_is_canceller: guardianIsCanceller,
|
|
687
709
|
timelock_is_self_admin: timelockIsSelfAdmin,
|
|
688
710
|
executor_open_to_anyone: executorOpen,
|
|
@@ -758,40 +780,63 @@ export async function handleGetGovernanceState(args) {
|
|
|
758
780
|
};
|
|
759
781
|
}
|
|
760
782
|
}
|
|
761
|
-
// 4.
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
783
|
+
// 4. A pending handover. Ownership moves in two steps (transferOwnership, then acceptOwnership by
|
|
784
|
+
// the new owner), and the timelock can only accept through a scheduled operation. When the
|
|
785
|
+
// pending owner is the timelock, the operation id is deterministic — TimelockController v5:
|
|
786
|
+
// keccak256(abi.encode(target, value, data, predecessor, salt)) — so its state can be reported.
|
|
787
|
+
const pendingOwnerNorm = getAddress(pendingOwner);
|
|
788
|
+
const handoverPending = pendingOwnerNorm !== "0x0000000000000000000000000000000000000000";
|
|
789
|
+
let pendingHandover = null;
|
|
790
|
+
if (handoverPending) {
|
|
791
|
+
pendingHandover = { pending_owner: pendingOwnerNorm, pending_owner_is_timelock: pendingOwnerNorm === timelockNorm };
|
|
792
|
+
if (pendingOwnerNorm === timelockNorm) {
|
|
793
|
+
const ZERO32 = `0x${"0".repeat(64)}`;
|
|
794
|
+
const acceptCalldata = encodeFunctionData({ abi: GBLIN_ABI, functionName: "acceptOwnership" });
|
|
795
|
+
const opId = keccak256(encodeAbiParameters([{ type: "address" }, { type: "uint256" }, { type: "bytes" }, { type: "bytes32" }, { type: "bytes32" }], [GBLIN_VAULT, 0n, acceptCalldata, ZERO32, ZERO32]));
|
|
796
|
+
try {
|
|
797
|
+
const [isPending, isReady, isDone, ts] = await Promise.all([
|
|
798
|
+
client.readContract({ address: GBLIN_TIMELOCK, abi: TIMELOCK_ABI, functionName: "isOperationPending", args: [opId] }),
|
|
799
|
+
client.readContract({ address: GBLIN_TIMELOCK, abi: TIMELOCK_ABI, functionName: "isOperationReady", args: [opId] }),
|
|
800
|
+
client.readContract({ address: GBLIN_TIMELOCK, abi: TIMELOCK_ABI, functionName: "isOperationDone", args: [opId] }),
|
|
801
|
+
client.readContract({ address: GBLIN_TIMELOCK, abi: TIMELOCK_ABI, functionName: "getTimestamp", args: [opId] }),
|
|
802
|
+
]);
|
|
803
|
+
const tsNum = Number(ts);
|
|
804
|
+
pendingHandover.accept_ownership_operation = {
|
|
805
|
+
id: opId,
|
|
806
|
+
scheduled: isPending || isDone,
|
|
807
|
+
pending: isPending,
|
|
808
|
+
ready: isReady,
|
|
809
|
+
done: isDone,
|
|
810
|
+
execute_after_unix: tsNum > 1 ? tsNum : null,
|
|
811
|
+
execute_after_iso: tsNum > 1 ? new Date(tsNum * 1000).toISOString() : null,
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
catch (err) {
|
|
815
|
+
pendingHandover.accept_ownership_operation = { id: opId, error: err.message };
|
|
816
|
+
}
|
|
775
817
|
}
|
|
776
|
-
|
|
818
|
+
}
|
|
777
819
|
return toolResult({
|
|
778
|
-
|
|
820
|
+
vault: GBLIN_VAULT,
|
|
821
|
+
lens: GBLIN_LENS,
|
|
779
822
|
owner: ownerNorm,
|
|
780
823
|
owner_is_timelock: ownerIsTimelock,
|
|
781
824
|
owner_is_renounced: ownerIsRenounced,
|
|
782
|
-
|
|
825
|
+
fee_recipient: getAddress(feeRecipient),
|
|
783
826
|
trust_summary: ownerIsRenounced
|
|
784
827
|
? "Ownership fully renounced — no admin can touch the contract."
|
|
785
828
|
: ownerIsTimelock
|
|
786
829
|
? "Ownership held by the 48h Timelock. All admin actions are delay-enforced on-chain."
|
|
787
|
-
:
|
|
830
|
+
: handoverPending && pendingOwnerNorm === timelockNorm
|
|
831
|
+
? "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."
|
|
832
|
+
: "WARNING: owner is an EOA / unknown contract — admin actions are NOT timelocked.",
|
|
788
833
|
timelock: timelockState,
|
|
789
|
-
|
|
834
|
+
pending_handover: pendingHandover,
|
|
790
835
|
pending_timelock_operation: operationStatus,
|
|
791
836
|
verification: {
|
|
792
|
-
|
|
837
|
+
vault_basescan: `https://basescan.org/address/${GBLIN_VAULT}#readContract`,
|
|
838
|
+
lens_basescan: `https://basescan.org/address/${GBLIN_LENS}#readContract`,
|
|
793
839
|
timelock_basescan: `https://basescan.org/address/${GBLIN_TIMELOCK}#readContract`,
|
|
794
|
-
ownership_transfer_tx: "https://basescan.org/tx/0xb653f54ffa9b1764b41932e6a411077e7e34550605303f15d90900de682edaaf",
|
|
795
840
|
},
|
|
796
841
|
});
|
|
797
842
|
}
|
|
@@ -885,12 +930,14 @@ async function handleShareSkillWithPeer(args) {
|
|
|
885
930
|
"analyze_treasury_health",
|
|
886
931
|
"get_governance_state",
|
|
887
932
|
"share_skill_with_peer",
|
|
888
|
-
"
|
|
933
|
+
"get_auction_state",
|
|
889
934
|
"get_market_risk_regime",
|
|
890
935
|
"verify_risk_attestation",
|
|
891
936
|
],
|
|
892
937
|
chain: "Base mainnet (8453)",
|
|
893
|
-
|
|
938
|
+
vault: GBLIN_VAULT,
|
|
939
|
+
lens: GBLIN_LENS,
|
|
940
|
+
zap: GBLIN_ZAP,
|
|
894
941
|
timelock: "0x6aBeC8716fFeEcf7C3D6e68255b4797113E8e5Dd",
|
|
895
942
|
},
|
|
896
943
|
recommended_pattern: {
|
|
@@ -906,19 +953,18 @@ async function handleShareSkillWithPeer(args) {
|
|
|
906
953
|
amount_usdc: example_amount_usdc,
|
|
907
954
|
steps: [
|
|
908
955
|
"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
|
|
956
|
+
"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
957
|
"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
|
|
958
|
+
"4. When liquidity is needed, call swap_gblin_to_usdc_jit for the GBLIN→USDC exit (three transactions, batchable)",
|
|
912
959
|
],
|
|
913
|
-
|
|
914
|
-
reference_tx: "https://basescan.org/tx/0x3d1084f6374dff2125ba337208c4b547d9821523a0a51f797f9fb422c9b24e2c",
|
|
960
|
+
verified_source: `https://basescan.org/address/${GBLIN_VAULT}#code`,
|
|
915
961
|
},
|
|
916
962
|
natural_language_explanation: [
|
|
917
963
|
"GBLIN is a treasury-backed index token on Base mainnet, collateralized by WETH, cbBTC, and USDC.",
|
|
918
964
|
"Every GBLIN is backed by real on-chain assets — NAV is computed from Chainlink oracle prices, not from market speculation.",
|
|
919
965
|
"Holding GBLIN as treasury preserves agent capital while keeping liquidity for x402 micropayments via Just-In-Time redemption.",
|
|
920
966
|
"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
|
|
967
|
+
"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
968
|
].join(" "),
|
|
923
969
|
referral_disclosure: {
|
|
924
970
|
builder_code_standard: "ERC-8021",
|
|
@@ -957,27 +1003,22 @@ async function handleShareSkillWithPeer(args) {
|
|
|
957
1003
|
return toolResult({ text: responseText });
|
|
958
1004
|
}
|
|
959
1005
|
// ───────────────────────────────────────────────────────────────────────────
|
|
960
|
-
// TOOL 8:
|
|
1006
|
+
// TOOL 8: get_auction_state
|
|
961
1007
|
// ───────────────────────────────────────────────────────────────────────────
|
|
962
|
-
const
|
|
963
|
-
name: "
|
|
964
|
-
description: "
|
|
1008
|
+
const GET_AUCTION_STATE_DEFINITION = {
|
|
1009
|
+
name: "get_auction_state",
|
|
1010
|
+
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
1011
|
inputSchema: {
|
|
966
1012
|
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
|
-
},
|
|
1013
|
+
properties: {},
|
|
973
1014
|
required: [],
|
|
974
1015
|
additionalProperties: false,
|
|
975
1016
|
},
|
|
976
1017
|
};
|
|
977
|
-
async function
|
|
1018
|
+
export async function handleGetAuctionState() {
|
|
978
1019
|
try {
|
|
979
|
-
const
|
|
980
|
-
return toolResult(
|
|
1020
|
+
const state = await getAuctionState();
|
|
1021
|
+
return toolResult(state);
|
|
981
1022
|
}
|
|
982
1023
|
catch (err) {
|
|
983
1024
|
return toolError(err.message, "Check RPC connectivity.");
|
|
@@ -1041,30 +1082,18 @@ async function handleMarketRiskRegime() {
|
|
|
1041
1082
|
defensive_cash_pct: defensiveCashPct,
|
|
1042
1083
|
assets,
|
|
1043
1084
|
source: "GBLIN on-chain Crash Shield (Base mainnet, Chainlink-oracle drawdown)",
|
|
1044
|
-
verify:
|
|
1045
|
-
meta: {
|
|
1085
|
+
verify: `https://basescan.org/address/${GBLIN_VAULT}#readContract`,
|
|
1086
|
+
meta: { vault: GBLIN_VAULT, lens: GBLIN_LENS, chain: "base", chain_id: 8453 },
|
|
1046
1087
|
});
|
|
1047
1088
|
}
|
|
1048
1089
|
catch (err) {
|
|
1049
1090
|
return toolError(err.message, "Check RPC connectivity and oracle freshness.");
|
|
1050
1091
|
}
|
|
1051
1092
|
}
|
|
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
|
-
};
|
|
1093
|
+
const ATTESTATION_DOMAIN_V1 = { name: "GBLIN Risk Attestation", version: "1", chainId: 8453, verifyingContract: GBLIN_PREVIOUS };
|
|
1094
|
+
const ATTESTATION_DOMAIN_V2 = { name: "GBLIN Risk Attestation", version: "2", chainId: 8453, verifyingContract: GBLIN_VAULT };
|
|
1095
|
+
const ATTESTATION_EIP712_DOMAINS = { "1": ATTESTATION_DOMAIN_V1, "2": ATTESTATION_DOMAIN_V2 };
|
|
1096
|
+
const ATTESTATION_EIP712_DOMAIN = ATTESTATION_DOMAIN_V2;
|
|
1068
1097
|
const ATTESTATION_EIP712_TYPES = {
|
|
1069
1098
|
RiskAttestation: [
|
|
1070
1099
|
{ name: "regime", type: "uint8" },
|
|
@@ -1117,6 +1146,8 @@ async function handleVerifyRiskAttestation(args) {
|
|
|
1117
1146
|
if (!eip712 || !eip712.message || !eip712.domain || !eip712.types) {
|
|
1118
1147
|
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
1148
|
}
|
|
1149
|
+
const declaredVersion = String(eip712.domain?.version ?? "");
|
|
1150
|
+
const canonicalDomain = ATTESTATION_EIP712_DOMAINS[declaredVersion] ?? ATTESTATION_EIP712_DOMAIN;
|
|
1120
1151
|
// Rebuild the typed message (uint64 fields as bigint for viem).
|
|
1121
1152
|
const m = eip712.message;
|
|
1122
1153
|
let message;
|
|
@@ -1137,7 +1168,7 @@ async function handleVerifyRiskAttestation(args) {
|
|
|
1137
1168
|
// 1. INTEGRITY — recompute the id using the CANONICAL schema from this build
|
|
1138
1169
|
// (never trust the schema embedded in the payload for hashing).
|
|
1139
1170
|
const recomputedId = hashTypedData({
|
|
1140
|
-
domain:
|
|
1171
|
+
domain: canonicalDomain,
|
|
1141
1172
|
types: ATTESTATION_EIP712_TYPES,
|
|
1142
1173
|
primaryType: "RiskAttestation",
|
|
1143
1174
|
message,
|
|
@@ -1146,9 +1177,10 @@ async function handleVerifyRiskAttestation(args) {
|
|
|
1146
1177
|
const integrityOk = claimedId ? recomputedId.toLowerCase() === claimedId.toLowerCase() : null;
|
|
1147
1178
|
// The payload's OWN domain must match the canonical GBLIN domain, else it is
|
|
1148
1179
|
// a different (untrusted) schema even if internally consistent.
|
|
1149
|
-
const schemaMatches = eip712.domain?.name ===
|
|
1150
|
-
String(eip712.domain?.version) ===
|
|
1151
|
-
Number(eip712.domain?.chainId) ===
|
|
1180
|
+
const schemaMatches = eip712.domain?.name === canonicalDomain.name &&
|
|
1181
|
+
String(eip712.domain?.version) === canonicalDomain.version &&
|
|
1182
|
+
Number(eip712.domain?.chainId) === canonicalDomain.chainId &&
|
|
1183
|
+
String(eip712.domain?.verifyingContract ?? "").toLowerCase() === canonicalDomain.verifyingContract.toLowerCase();
|
|
1152
1184
|
// 2. AUTHENTICITY — signature recover
|
|
1153
1185
|
const expectedAttestor = typeof a.expected_attestor === "string" && isAddress(a.expected_attestor)
|
|
1154
1186
|
? getAddress(a.expected_attestor)
|
|
@@ -1160,7 +1192,7 @@ async function handleVerifyRiskAttestation(args) {
|
|
|
1160
1192
|
if (signature) {
|
|
1161
1193
|
try {
|
|
1162
1194
|
recoveredSigner = await recoverTypedDataAddress({
|
|
1163
|
-
domain:
|
|
1195
|
+
domain: canonicalDomain,
|
|
1164
1196
|
types: ATTESTATION_EIP712_TYPES,
|
|
1165
1197
|
primaryType: "RiskAttestation",
|
|
1166
1198
|
message,
|
|
@@ -1249,6 +1281,11 @@ async function handleVerifyRiskAttestation(args) {
|
|
|
1249
1281
|
// ───────────────────────────────────────────────────────────────────────────
|
|
1250
1282
|
// REGISTRY
|
|
1251
1283
|
// ───────────────────────────────────────────────────────────────────────────
|
|
1284
|
+
// MCP tool annotations. Every tool here reads the chain or does pure math and returns data or
|
|
1285
|
+
// unsigned calldata: nothing is written, signed or broadcast, so each one is read-only, safe to
|
|
1286
|
+
// repeat with the same arguments, and reaches an open world (Base mainnet through the configured RPC).
|
|
1287
|
+
// The receipt tools declare their own: sealing appends to a public log.
|
|
1288
|
+
const READ_ONLY_ANNOTATIONS = { readOnlyHint: true, idempotentHint: true, destructiveHint: false, openWorldHint: true };
|
|
1252
1289
|
export const TOOL_DEFINITIONS = [
|
|
1253
1290
|
GET_TREASURY_STATE_DEFINITION,
|
|
1254
1291
|
QUOTE_SAFE_SWAP_DEFINITION,
|
|
@@ -1257,15 +1294,14 @@ export const TOOL_DEFINITIONS = [
|
|
|
1257
1294
|
ANALYZE_TREASURY_DEFINITION,
|
|
1258
1295
|
GET_GOVERNANCE_STATE_DEFINITION,
|
|
1259
1296
|
SHARE_SKILL_DEFINITION,
|
|
1260
|
-
|
|
1297
|
+
GET_AUCTION_STATE_DEFINITION,
|
|
1261
1298
|
MARKET_RISK_DEFINITION,
|
|
1262
1299
|
VERIFY_ATTESTATION_DEFINITION,
|
|
1263
|
-
|
|
1264
|
-
];
|
|
1300
|
+
].map((definition) => ({ ...definition, annotations: READ_ONLY_ANNOTATIONS })).concat(RECEIPT_TOOL_DEFINITIONS);
|
|
1265
1301
|
export const TOOL_HANDLERS = {
|
|
1266
1302
|
// ── FREE tools ─────────────────────────────────────────────────────────────
|
|
1267
1303
|
// Read-only (funnel) + action tools kept free to avoid chicken-and-egg.
|
|
1268
|
-
//
|
|
1304
|
+
// The on-chain protocol fee (0.05% of every mint, paid as shares) captures revenue when GBLIN is used.
|
|
1269
1305
|
get_treasury_state: handleGetTreasuryState,
|
|
1270
1306
|
quote_safe_swap: handleQuoteSafeSwap,
|
|
1271
1307
|
get_governance_state: handleGetGovernanceState,
|
|
@@ -1273,10 +1309,10 @@ export const TOOL_HANDLERS = {
|
|
|
1273
1309
|
swap_gblin_to_usdc_jit: handleJitSwap,
|
|
1274
1310
|
invest_usdc_to_gblin: handleInvest,
|
|
1275
1311
|
verify_risk_attestation: handleVerifyRiskAttestation,
|
|
1312
|
+
get_auction_state: handleGetAuctionState,
|
|
1276
1313
|
// ── PAID tools (x402 intelligence layer) ──────────────────────────────────
|
|
1277
|
-
// Analysis
|
|
1314
|
+
// Analysis — "advice", not transport.
|
|
1278
1315
|
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
1316
|
get_market_risk_regime: requirePayment({ priceUsdc: "0.002", priceLabel: "$0.002 USDC per call" }, handleMarketRiskRegime),
|
|
1281
1317
|
// ── AI Action Receipts (free demo + free reads; paid seals via x402 HTTP) ──
|
|
1282
1318
|
...RECEIPT_TOOL_HANDLERS,
|