@spectratools/assembly-cli 0.1.1 → 0.3.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 +47 -197
- package/dist/cli.js +953 -108
- package/package.json +19 -2
package/dist/cli.js
CHANGED
|
@@ -29,6 +29,19 @@ function relTime(unixSeconds) {
|
|
|
29
29
|
function asNum(value) {
|
|
30
30
|
return Number(value);
|
|
31
31
|
}
|
|
32
|
+
function jsonSafe(value) {
|
|
33
|
+
if (typeof value === "bigint") return value.toString();
|
|
34
|
+
if (Array.isArray(value)) return value.map((item) => jsonSafe(item));
|
|
35
|
+
if (value && typeof value === "object") {
|
|
36
|
+
return Object.fromEntries(
|
|
37
|
+
Object.entries(value).map(([key, entry]) => [
|
|
38
|
+
key,
|
|
39
|
+
jsonSafe(entry)
|
|
40
|
+
])
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
32
45
|
|
|
33
46
|
// src/commands/council.ts
|
|
34
47
|
import { Cli, z } from "incur";
|
|
@@ -4268,6 +4281,10 @@ var ABSTRACT_MAINNET_ADDRESSES = {
|
|
|
4268
4281
|
governance: "0xe82a25937e07a3855d8B8352b85fF4B4Aa3fb0C0",
|
|
4269
4282
|
treasury: "0xC2e6DDbdc1A8e4DcCc60A78B6Faa197967a8FEb9"
|
|
4270
4283
|
};
|
|
4284
|
+
var ABSTRACT_MAINNET_DEPLOYMENT_BLOCKS = {
|
|
4285
|
+
// https://abscan.org/tx/0xe1dd27a739944c9847a7f366ea2363e7bf0fc2067377020fd749a8301e09b1ec
|
|
4286
|
+
registry: 43782651n
|
|
4287
|
+
};
|
|
4271
4288
|
|
|
4272
4289
|
// src/contracts/client.ts
|
|
4273
4290
|
import { http, createPublicClient, defineChain } from "viem";
|
|
@@ -4293,12 +4310,40 @@ function createAssemblyPublicClient(rpcUrl) {
|
|
|
4293
4310
|
}
|
|
4294
4311
|
|
|
4295
4312
|
// src/commands/council.ts
|
|
4296
|
-
var env = z.object({
|
|
4313
|
+
var env = z.object({
|
|
4314
|
+
ABSTRACT_RPC_URL: z.string().optional().describe("Abstract RPC URL override")
|
|
4315
|
+
});
|
|
4316
|
+
function decodeSeat(value) {
|
|
4317
|
+
const [owner, startAt, endAt, forfeited] = value;
|
|
4318
|
+
return { owner, startAt, endAt, forfeited };
|
|
4319
|
+
}
|
|
4320
|
+
function decodeAuction(value) {
|
|
4321
|
+
const [highestBidder, highestBid, settled] = value;
|
|
4322
|
+
return { highestBidder, highestBid, settled };
|
|
4323
|
+
}
|
|
4324
|
+
function deriveAuctionStatus(params) {
|
|
4325
|
+
if (params.settled) return "settled";
|
|
4326
|
+
if (params.currentTimestamp < params.windowEnd) return "bidding";
|
|
4327
|
+
return "closed";
|
|
4328
|
+
}
|
|
4297
4329
|
var council = Cli.create("council", {
|
|
4298
|
-
description: "
|
|
4330
|
+
description: "Inspect council seats, members, auctions, and seat parameters."
|
|
4299
4331
|
});
|
|
4300
4332
|
council.command("seats", {
|
|
4333
|
+
description: "List all council seats and their occupancy windows.",
|
|
4301
4334
|
env,
|
|
4335
|
+
output: z.array(
|
|
4336
|
+
z.object({
|
|
4337
|
+
id: z.number(),
|
|
4338
|
+
owner: z.string(),
|
|
4339
|
+
startAt: z.number(),
|
|
4340
|
+
startAtRelative: z.string(),
|
|
4341
|
+
endAt: z.number(),
|
|
4342
|
+
endAtRelative: z.string(),
|
|
4343
|
+
forfeited: z.boolean()
|
|
4344
|
+
})
|
|
4345
|
+
),
|
|
4346
|
+
examples: [{ description: "List all council seats" }],
|
|
4302
4347
|
async run(c) {
|
|
4303
4348
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4304
4349
|
const count = await client.readContract({
|
|
@@ -4307,7 +4352,7 @@ council.command("seats", {
|
|
|
4307
4352
|
functionName: "seatCount"
|
|
4308
4353
|
});
|
|
4309
4354
|
const ids = Array.from({ length: Number(count) }, (_, i) => BigInt(i));
|
|
4310
|
-
const
|
|
4355
|
+
const seatTuples = ids.length ? await client.multicall({
|
|
4311
4356
|
allowFailure: false,
|
|
4312
4357
|
contracts: ids.map((id) => ({
|
|
4313
4358
|
abi: councilSeatsAbi,
|
|
@@ -4316,13 +4361,14 @@ council.command("seats", {
|
|
|
4316
4361
|
args: [id]
|
|
4317
4362
|
}))
|
|
4318
4363
|
}) : [];
|
|
4364
|
+
const seats = seatTuples.map(decodeSeat);
|
|
4319
4365
|
return c.ok(
|
|
4320
4366
|
seats.map((seat, idx) => ({
|
|
4321
4367
|
id: idx,
|
|
4322
4368
|
owner: toChecksum(seat.owner),
|
|
4323
|
-
startAt:
|
|
4369
|
+
startAt: asNum(seat.startAt),
|
|
4324
4370
|
startAtRelative: relTime(seat.startAt),
|
|
4325
|
-
endAt:
|
|
4371
|
+
endAt: asNum(seat.endAt),
|
|
4326
4372
|
endAtRelative: relTime(seat.endAt),
|
|
4327
4373
|
forfeited: seat.forfeited
|
|
4328
4374
|
}))
|
|
@@ -4330,26 +4376,49 @@ council.command("seats", {
|
|
|
4330
4376
|
}
|
|
4331
4377
|
});
|
|
4332
4378
|
council.command("seat", {
|
|
4333
|
-
|
|
4379
|
+
description: "Get detailed seat information for a specific seat id.",
|
|
4380
|
+
args: z.object({
|
|
4381
|
+
id: z.coerce.number().int().nonnegative().describe("Seat id (0-indexed)")
|
|
4382
|
+
}),
|
|
4334
4383
|
env,
|
|
4384
|
+
output: z.object({
|
|
4385
|
+
id: z.number(),
|
|
4386
|
+
owner: z.string(),
|
|
4387
|
+
startAt: z.number(),
|
|
4388
|
+
endAt: z.number(),
|
|
4389
|
+
forfeited: z.boolean(),
|
|
4390
|
+
endAtRelative: z.string()
|
|
4391
|
+
}),
|
|
4392
|
+
examples: [{ args: { id: 0 }, description: "Inspect seat #0" }],
|
|
4335
4393
|
async run(c) {
|
|
4336
4394
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4337
|
-
const
|
|
4395
|
+
const seatTuple = await client.readContract({
|
|
4338
4396
|
abi: councilSeatsAbi,
|
|
4339
4397
|
address: ABSTRACT_MAINNET_ADDRESSES.councilSeats,
|
|
4340
4398
|
functionName: "seats",
|
|
4341
4399
|
args: [BigInt(c.args.id)]
|
|
4342
4400
|
});
|
|
4401
|
+
const seat = decodeSeat(seatTuple);
|
|
4343
4402
|
return c.ok({
|
|
4344
4403
|
id: c.args.id,
|
|
4345
|
-
...seat,
|
|
4346
4404
|
owner: toChecksum(seat.owner),
|
|
4405
|
+
startAt: asNum(seat.startAt),
|
|
4406
|
+
endAt: asNum(seat.endAt),
|
|
4407
|
+
forfeited: seat.forfeited,
|
|
4347
4408
|
endAtRelative: relTime(seat.endAt)
|
|
4348
4409
|
});
|
|
4349
4410
|
}
|
|
4350
4411
|
});
|
|
4351
4412
|
council.command("members", {
|
|
4413
|
+
description: "List currently active council members and voting power.",
|
|
4352
4414
|
env,
|
|
4415
|
+
output: z.array(
|
|
4416
|
+
z.object({
|
|
4417
|
+
address: z.string(),
|
|
4418
|
+
votingPower: z.number()
|
|
4419
|
+
})
|
|
4420
|
+
),
|
|
4421
|
+
examples: [{ description: "List active council members" }],
|
|
4353
4422
|
async run(c) {
|
|
4354
4423
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4355
4424
|
const count = await client.readContract({
|
|
@@ -4358,7 +4427,7 @@ council.command("members", {
|
|
|
4358
4427
|
functionName: "seatCount"
|
|
4359
4428
|
});
|
|
4360
4429
|
const ids = Array.from({ length: Number(count) }, (_, i) => BigInt(i));
|
|
4361
|
-
const
|
|
4430
|
+
const seatTuples = ids.length ? await client.multicall({
|
|
4362
4431
|
allowFailure: false,
|
|
4363
4432
|
contracts: ids.map((id) => ({
|
|
4364
4433
|
abi: councilSeatsAbi,
|
|
@@ -4367,9 +4436,10 @@ council.command("members", {
|
|
|
4367
4436
|
args: [id]
|
|
4368
4437
|
}))
|
|
4369
4438
|
}) : [];
|
|
4439
|
+
const seats = seatTuples.map(decodeSeat);
|
|
4370
4440
|
const activeOwners = [
|
|
4371
4441
|
...new Set(
|
|
4372
|
-
seats.filter((x) => !x.forfeited &&
|
|
4442
|
+
seats.filter((x) => !x.forfeited && asNum(x.endAt) > Math.floor(Date.now() / 1e3)).map((x) => x.owner)
|
|
4373
4443
|
)
|
|
4374
4444
|
];
|
|
4375
4445
|
const powers = activeOwners.length ? await client.multicall({
|
|
@@ -4390,8 +4460,21 @@ council.command("members", {
|
|
|
4390
4460
|
}
|
|
4391
4461
|
});
|
|
4392
4462
|
council.command("is-member", {
|
|
4393
|
-
|
|
4463
|
+
description: "Check whether an address is currently a council member.",
|
|
4464
|
+
args: z.object({
|
|
4465
|
+
address: z.string().describe("Address to check")
|
|
4466
|
+
}),
|
|
4394
4467
|
env,
|
|
4468
|
+
output: z.object({
|
|
4469
|
+
address: z.string(),
|
|
4470
|
+
isMember: z.boolean()
|
|
4471
|
+
}),
|
|
4472
|
+
examples: [
|
|
4473
|
+
{
|
|
4474
|
+
args: { address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" },
|
|
4475
|
+
description: "Check council status for one address"
|
|
4476
|
+
}
|
|
4477
|
+
],
|
|
4395
4478
|
async run(c) {
|
|
4396
4479
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4397
4480
|
const isMember = await client.readContract({
|
|
@@ -4404,8 +4487,21 @@ council.command("is-member", {
|
|
|
4404
4487
|
}
|
|
4405
4488
|
});
|
|
4406
4489
|
council.command("voting-power", {
|
|
4407
|
-
|
|
4490
|
+
description: "Get the current voting power for an address.",
|
|
4491
|
+
args: z.object({
|
|
4492
|
+
address: z.string().describe("Address to inspect")
|
|
4493
|
+
}),
|
|
4408
4494
|
env,
|
|
4495
|
+
output: z.object({
|
|
4496
|
+
address: z.string(),
|
|
4497
|
+
votingPower: z.number()
|
|
4498
|
+
}),
|
|
4499
|
+
examples: [
|
|
4500
|
+
{
|
|
4501
|
+
args: { address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" },
|
|
4502
|
+
description: "Get voting power for one address"
|
|
4503
|
+
}
|
|
4504
|
+
],
|
|
4409
4505
|
async run(c) {
|
|
4410
4506
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4411
4507
|
const votingPower = await client.readContract({
|
|
@@ -4418,7 +4514,25 @@ council.command("voting-power", {
|
|
|
4418
4514
|
}
|
|
4419
4515
|
});
|
|
4420
4516
|
council.command("auctions", {
|
|
4517
|
+
description: "List recent and current council auction slots and leading bids.",
|
|
4421
4518
|
env,
|
|
4519
|
+
output: z.object({
|
|
4520
|
+
currentDay: z.number(),
|
|
4521
|
+
currentSlot: z.number(),
|
|
4522
|
+
auctions: z.array(
|
|
4523
|
+
z.object({
|
|
4524
|
+
day: z.number(),
|
|
4525
|
+
slot: z.number(),
|
|
4526
|
+
highestBidder: z.string(),
|
|
4527
|
+
highestBid: z.string(),
|
|
4528
|
+
settled: z.boolean(),
|
|
4529
|
+
windowEnd: z.number(),
|
|
4530
|
+
windowEndRelative: z.string(),
|
|
4531
|
+
status: z.enum(["bidding", "closed", "settled"])
|
|
4532
|
+
})
|
|
4533
|
+
)
|
|
4534
|
+
}),
|
|
4535
|
+
examples: [{ description: "Inspect current and recent auction slots" }],
|
|
4422
4536
|
async run(c) {
|
|
4423
4537
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4424
4538
|
const [day, slot, slotsPerDay] = await Promise.all([
|
|
@@ -4443,25 +4557,50 @@ council.command("auctions", {
|
|
|
4443
4557
|
if (d < 0) continue;
|
|
4444
4558
|
for (let s = 0; s < Number(slotsPerDay); s++) recent.push({ day: BigInt(d), slot: s });
|
|
4445
4559
|
}
|
|
4446
|
-
const
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4560
|
+
const [auctionTuples, windowEnds, latestBlock] = await Promise.all([
|
|
4561
|
+
recent.length ? client.multicall({
|
|
4562
|
+
allowFailure: false,
|
|
4563
|
+
contracts: recent.map((x) => ({
|
|
4564
|
+
abi: councilSeatsAbi,
|
|
4565
|
+
address: ABSTRACT_MAINNET_ADDRESSES.councilSeats,
|
|
4566
|
+
functionName: "auctions",
|
|
4567
|
+
args: [x.day, x.slot]
|
|
4568
|
+
}))
|
|
4569
|
+
}) : Promise.resolve([]),
|
|
4570
|
+
recent.length ? client.multicall({
|
|
4571
|
+
allowFailure: false,
|
|
4572
|
+
contracts: recent.map((x) => ({
|
|
4573
|
+
abi: councilSeatsAbi,
|
|
4574
|
+
address: ABSTRACT_MAINNET_ADDRESSES.councilSeats,
|
|
4575
|
+
functionName: "auctionWindowEnd",
|
|
4576
|
+
args: [x.day, x.slot]
|
|
4577
|
+
}))
|
|
4578
|
+
}) : Promise.resolve([]),
|
|
4579
|
+
client.getBlock({ blockTag: "latest" })
|
|
4580
|
+
]);
|
|
4581
|
+
const auctions = auctionTuples.map(decodeAuction);
|
|
4582
|
+
const currentTimestamp = latestBlock.timestamp;
|
|
4455
4583
|
return c.ok(
|
|
4456
4584
|
{
|
|
4457
4585
|
currentDay: asNum(day),
|
|
4458
4586
|
currentSlot: asNum(slot),
|
|
4459
|
-
auctions: recent.map((x, i) =>
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4587
|
+
auctions: recent.map((x, i) => {
|
|
4588
|
+
const windowEnd = windowEnds[i];
|
|
4589
|
+
return {
|
|
4590
|
+
day: Number(x.day),
|
|
4591
|
+
slot: x.slot,
|
|
4592
|
+
highestBidder: toChecksum(auctions[i].highestBidder),
|
|
4593
|
+
highestBid: eth(auctions[i].highestBid),
|
|
4594
|
+
settled: auctions[i].settled,
|
|
4595
|
+
windowEnd: asNum(windowEnd),
|
|
4596
|
+
windowEndRelative: relTime(windowEnd),
|
|
4597
|
+
status: deriveAuctionStatus({
|
|
4598
|
+
settled: auctions[i].settled,
|
|
4599
|
+
windowEnd,
|
|
4600
|
+
currentTimestamp
|
|
4601
|
+
})
|
|
4602
|
+
};
|
|
4603
|
+
})
|
|
4465
4604
|
},
|
|
4466
4605
|
{
|
|
4467
4606
|
cta: {
|
|
@@ -4476,31 +4615,76 @@ council.command("auctions", {
|
|
|
4476
4615
|
}
|
|
4477
4616
|
});
|
|
4478
4617
|
council.command("auction", {
|
|
4618
|
+
description: "Get one auction slot by day + slot.",
|
|
4479
4619
|
args: z.object({
|
|
4480
|
-
day: z.coerce.number().int().nonnegative(),
|
|
4481
|
-
slot: z.coerce.number().int().nonnegative()
|
|
4620
|
+
day: z.coerce.number().int().nonnegative().describe("Auction day index"),
|
|
4621
|
+
slot: z.coerce.number().int().nonnegative().describe("Slot index within day")
|
|
4482
4622
|
}),
|
|
4483
4623
|
env,
|
|
4624
|
+
output: z.object({
|
|
4625
|
+
day: z.number(),
|
|
4626
|
+
slot: z.number(),
|
|
4627
|
+
highestBidder: z.string(),
|
|
4628
|
+
highestBid: z.string(),
|
|
4629
|
+
settled: z.boolean(),
|
|
4630
|
+
windowEnd: z.number(),
|
|
4631
|
+
windowEndRelative: z.string(),
|
|
4632
|
+
status: z.enum(["bidding", "closed", "settled"])
|
|
4633
|
+
}),
|
|
4634
|
+
examples: [{ args: { day: 0, slot: 0 }, description: "Inspect day 0, slot 0 auction" }],
|
|
4484
4635
|
async run(c) {
|
|
4485
4636
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4486
|
-
const
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4637
|
+
const [auctionTuple, windowEnd, latestBlock] = await Promise.all([
|
|
4638
|
+
client.readContract({
|
|
4639
|
+
abi: councilSeatsAbi,
|
|
4640
|
+
address: ABSTRACT_MAINNET_ADDRESSES.councilSeats,
|
|
4641
|
+
functionName: "auctions",
|
|
4642
|
+
args: [BigInt(c.args.day), c.args.slot]
|
|
4643
|
+
}),
|
|
4644
|
+
client.readContract({
|
|
4645
|
+
abi: councilSeatsAbi,
|
|
4646
|
+
address: ABSTRACT_MAINNET_ADDRESSES.councilSeats,
|
|
4647
|
+
functionName: "auctionWindowEnd",
|
|
4648
|
+
args: [BigInt(c.args.day), c.args.slot]
|
|
4649
|
+
}),
|
|
4650
|
+
client.getBlock({ blockTag: "latest" })
|
|
4651
|
+
]);
|
|
4652
|
+
const auction = decodeAuction(auctionTuple);
|
|
4653
|
+
const windowEndTimestamp = windowEnd;
|
|
4654
|
+
const currentTimestamp = latestBlock.timestamp;
|
|
4492
4655
|
return c.ok({
|
|
4493
4656
|
day: c.args.day,
|
|
4494
4657
|
slot: c.args.slot,
|
|
4495
4658
|
highestBidder: toChecksum(auction.highestBidder),
|
|
4496
4659
|
highestBid: eth(auction.highestBid),
|
|
4497
|
-
settled: auction.settled
|
|
4660
|
+
settled: auction.settled,
|
|
4661
|
+
windowEnd: asNum(windowEndTimestamp),
|
|
4662
|
+
windowEndRelative: relTime(windowEndTimestamp),
|
|
4663
|
+
status: deriveAuctionStatus({
|
|
4664
|
+
settled: auction.settled,
|
|
4665
|
+
windowEnd: windowEndTimestamp,
|
|
4666
|
+
currentTimestamp
|
|
4667
|
+
})
|
|
4498
4668
|
});
|
|
4499
4669
|
}
|
|
4500
4670
|
});
|
|
4501
4671
|
council.command("pending-refund", {
|
|
4502
|
-
|
|
4672
|
+
description: "Get pending refundable bid amount for an address.",
|
|
4673
|
+
args: z.object({
|
|
4674
|
+
address: z.string().describe("Bidder address")
|
|
4675
|
+
}),
|
|
4503
4676
|
env,
|
|
4677
|
+
output: z.object({
|
|
4678
|
+
address: z.string(),
|
|
4679
|
+
pendingRefund: z.string(),
|
|
4680
|
+
pendingRefundWei: z.string()
|
|
4681
|
+
}),
|
|
4682
|
+
examples: [
|
|
4683
|
+
{
|
|
4684
|
+
args: { address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" },
|
|
4685
|
+
description: "Check pending refund for an address"
|
|
4686
|
+
}
|
|
4687
|
+
],
|
|
4504
4688
|
async run(c) {
|
|
4505
4689
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4506
4690
|
const amount = await client.readContract({
|
|
@@ -4517,7 +4701,17 @@ council.command("pending-refund", {
|
|
|
4517
4701
|
}
|
|
4518
4702
|
});
|
|
4519
4703
|
council.command("params", {
|
|
4704
|
+
description: "Read council seat term and auction scheduling parameters.",
|
|
4520
4705
|
env,
|
|
4706
|
+
output: z.object({
|
|
4707
|
+
SEAT_TERM: z.number(),
|
|
4708
|
+
AUCTION_SLOT_DURATION: z.number(),
|
|
4709
|
+
AUCTION_SLOTS_PER_DAY: z.number(),
|
|
4710
|
+
auctionEpochStart: z.number(),
|
|
4711
|
+
auctionWindowStart: z.number(),
|
|
4712
|
+
auctionWindowEnd: z.number()
|
|
4713
|
+
}),
|
|
4714
|
+
examples: [{ description: "Inspect council seat + auction timing constants" }],
|
|
4521
4715
|
async run(c) {
|
|
4522
4716
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4523
4717
|
const [SEAT_TERM, AUCTION_SLOT_DURATION, AUCTION_SLOTS_PER_DAY, auctionEpochStart] = await Promise.all([
|
|
@@ -4569,10 +4763,78 @@ council.command("params", {
|
|
|
4569
4763
|
|
|
4570
4764
|
// src/commands/forum.ts
|
|
4571
4765
|
import { Cli as Cli2, z as z2 } from "incur";
|
|
4572
|
-
var env2 = z2.object({
|
|
4573
|
-
|
|
4766
|
+
var env2 = z2.object({
|
|
4767
|
+
ABSTRACT_RPC_URL: z2.string().optional().describe("Abstract RPC URL override")
|
|
4768
|
+
});
|
|
4769
|
+
function decodeThread(value) {
|
|
4770
|
+
const [id, kind, author, createdAt, category, title, body, proposalId, petitionId] = value;
|
|
4771
|
+
return {
|
|
4772
|
+
id: asNum(id),
|
|
4773
|
+
kind: asNum(kind),
|
|
4774
|
+
author: toChecksum(author),
|
|
4775
|
+
createdAt: asNum(createdAt),
|
|
4776
|
+
category,
|
|
4777
|
+
title,
|
|
4778
|
+
body,
|
|
4779
|
+
proposalId: asNum(proposalId),
|
|
4780
|
+
petitionId: asNum(petitionId)
|
|
4781
|
+
};
|
|
4782
|
+
}
|
|
4783
|
+
function decodeComment(value) {
|
|
4784
|
+
const [id, threadId, parentId, author, createdAt, body] = value;
|
|
4785
|
+
return {
|
|
4786
|
+
id: asNum(id),
|
|
4787
|
+
threadId: asNum(threadId),
|
|
4788
|
+
parentId: asNum(parentId),
|
|
4789
|
+
author: toChecksum(author),
|
|
4790
|
+
createdAt: asNum(createdAt),
|
|
4791
|
+
body
|
|
4792
|
+
};
|
|
4793
|
+
}
|
|
4794
|
+
function decodePetition(value) {
|
|
4795
|
+
const [
|
|
4796
|
+
id,
|
|
4797
|
+
proposer,
|
|
4798
|
+
createdAt,
|
|
4799
|
+
category,
|
|
4800
|
+
title,
|
|
4801
|
+
body,
|
|
4802
|
+
signatures,
|
|
4803
|
+
promoted,
|
|
4804
|
+
threadId,
|
|
4805
|
+
proposalInput
|
|
4806
|
+
] = value;
|
|
4807
|
+
return {
|
|
4808
|
+
id: asNum(id),
|
|
4809
|
+
proposer: toChecksum(proposer),
|
|
4810
|
+
createdAt: asNum(createdAt),
|
|
4811
|
+
category,
|
|
4812
|
+
title,
|
|
4813
|
+
body,
|
|
4814
|
+
signatures: asNum(signatures),
|
|
4815
|
+
promoted,
|
|
4816
|
+
threadId: asNum(threadId),
|
|
4817
|
+
proposalInput: jsonSafe(proposalInput)
|
|
4818
|
+
};
|
|
4819
|
+
}
|
|
4820
|
+
var forum = Cli2.create("forum", {
|
|
4821
|
+
description: "Browse Assembly forum threads, comments, and petitions."
|
|
4822
|
+
});
|
|
4574
4823
|
forum.command("threads", {
|
|
4824
|
+
description: "List forum threads with author and creation metadata.",
|
|
4575
4825
|
env: env2,
|
|
4826
|
+
output: z2.array(
|
|
4827
|
+
z2.object({
|
|
4828
|
+
id: z2.number(),
|
|
4829
|
+
kind: z2.number(),
|
|
4830
|
+
author: z2.string(),
|
|
4831
|
+
createdAt: z2.number(),
|
|
4832
|
+
createdAtRelative: z2.string(),
|
|
4833
|
+
category: z2.string().nullable().optional(),
|
|
4834
|
+
title: z2.string().nullable().optional()
|
|
4835
|
+
})
|
|
4836
|
+
),
|
|
4837
|
+
examples: [{ description: "List all forum threads" }],
|
|
4576
4838
|
async run(c) {
|
|
4577
4839
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4578
4840
|
const count = await client.readContract({
|
|
@@ -4581,7 +4843,7 @@ forum.command("threads", {
|
|
|
4581
4843
|
functionName: "threadCount"
|
|
4582
4844
|
});
|
|
4583
4845
|
const ids = Array.from({ length: Number(count) }, (_, i) => BigInt(i + 1));
|
|
4584
|
-
const
|
|
4846
|
+
const threadTuples = ids.length ? await client.multicall({
|
|
4585
4847
|
allowFailure: false,
|
|
4586
4848
|
contracts: ids.map((id) => ({
|
|
4587
4849
|
abi: forumAbi,
|
|
@@ -4590,15 +4852,16 @@ forum.command("threads", {
|
|
|
4590
4852
|
args: [id]
|
|
4591
4853
|
}))
|
|
4592
4854
|
}) : [];
|
|
4855
|
+
const items = threadTuples.map(decodeThread);
|
|
4593
4856
|
return c.ok(
|
|
4594
4857
|
items.map((x) => ({
|
|
4595
|
-
id:
|
|
4596
|
-
kind:
|
|
4597
|
-
author:
|
|
4598
|
-
createdAt:
|
|
4858
|
+
id: x.id,
|
|
4859
|
+
kind: x.kind,
|
|
4860
|
+
author: x.author,
|
|
4861
|
+
createdAt: x.createdAt,
|
|
4599
4862
|
createdAtRelative: relTime(x.createdAt),
|
|
4600
|
-
category: x.category,
|
|
4601
|
-
title: x.title
|
|
4863
|
+
category: x.category ?? null,
|
|
4864
|
+
title: x.title ?? null
|
|
4602
4865
|
})),
|
|
4603
4866
|
{
|
|
4604
4867
|
cta: {
|
|
@@ -4613,11 +4876,19 @@ forum.command("threads", {
|
|
|
4613
4876
|
}
|
|
4614
4877
|
});
|
|
4615
4878
|
forum.command("thread", {
|
|
4616
|
-
|
|
4879
|
+
description: "Get one thread and all comments associated with it.",
|
|
4880
|
+
args: z2.object({
|
|
4881
|
+
id: z2.coerce.number().int().positive().describe("Thread id (1-indexed)")
|
|
4882
|
+
}),
|
|
4617
4883
|
env: env2,
|
|
4884
|
+
output: z2.object({
|
|
4885
|
+
thread: z2.record(z2.string(), z2.unknown()),
|
|
4886
|
+
comments: z2.array(z2.record(z2.string(), z2.unknown()))
|
|
4887
|
+
}),
|
|
4888
|
+
examples: [{ args: { id: 1 }, description: "Fetch thread #1 and its comments" }],
|
|
4618
4889
|
async run(c) {
|
|
4619
4890
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4620
|
-
const [
|
|
4891
|
+
const [threadTuple, commentCount] = await Promise.all([
|
|
4621
4892
|
client.readContract({
|
|
4622
4893
|
abi: forumAbi,
|
|
4623
4894
|
address: ABSTRACT_MAINNET_ADDRESSES.forum,
|
|
@@ -4630,8 +4901,9 @@ forum.command("thread", {
|
|
|
4630
4901
|
functionName: "commentCount"
|
|
4631
4902
|
})
|
|
4632
4903
|
]);
|
|
4904
|
+
const thread = decodeThread(threadTuple);
|
|
4633
4905
|
const ids = Array.from({ length: Number(commentCount) }, (_, i) => BigInt(i + 1));
|
|
4634
|
-
const
|
|
4906
|
+
const commentTuples = ids.length ? await client.multicall({
|
|
4635
4907
|
allowFailure: false,
|
|
4636
4908
|
contracts: ids.map((id) => ({
|
|
4637
4909
|
abi: forumAbi,
|
|
@@ -4640,15 +4912,21 @@ forum.command("thread", {
|
|
|
4640
4912
|
args: [id]
|
|
4641
4913
|
}))
|
|
4642
4914
|
}) : [];
|
|
4915
|
+
const comments = commentTuples.map(decodeComment);
|
|
4643
4916
|
return c.ok({
|
|
4644
|
-
thread,
|
|
4645
|
-
comments: comments.filter((x) =>
|
|
4917
|
+
thread: jsonSafe(thread),
|
|
4918
|
+
comments: comments.filter((x) => x.threadId === c.args.id).map((comment) => jsonSafe(comment))
|
|
4646
4919
|
});
|
|
4647
4920
|
}
|
|
4648
4921
|
});
|
|
4649
4922
|
forum.command("comments", {
|
|
4650
|
-
|
|
4923
|
+
description: "List comments for a thread id.",
|
|
4924
|
+
args: z2.object({
|
|
4925
|
+
threadId: z2.coerce.number().int().positive().describe("Thread id to filter comments by")
|
|
4926
|
+
}),
|
|
4651
4927
|
env: env2,
|
|
4928
|
+
output: z2.array(z2.record(z2.string(), z2.unknown())),
|
|
4929
|
+
examples: [{ args: { threadId: 1 }, description: "List comments for thread #1" }],
|
|
4652
4930
|
async run(c) {
|
|
4653
4931
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4654
4932
|
const count = await client.readContract({
|
|
@@ -4657,7 +4935,7 @@ forum.command("comments", {
|
|
|
4657
4935
|
functionName: "commentCount"
|
|
4658
4936
|
});
|
|
4659
4937
|
const ids = Array.from({ length: Number(count) }, (_, i) => BigInt(i + 1));
|
|
4660
|
-
const
|
|
4938
|
+
const commentTuples = ids.length ? await client.multicall({
|
|
4661
4939
|
allowFailure: false,
|
|
4662
4940
|
contracts: ids.map((id) => ({
|
|
4663
4941
|
abi: forumAbi,
|
|
@@ -4666,25 +4944,36 @@ forum.command("comments", {
|
|
|
4666
4944
|
args: [id]
|
|
4667
4945
|
}))
|
|
4668
4946
|
}) : [];
|
|
4669
|
-
|
|
4947
|
+
const comments = commentTuples.map(decodeComment);
|
|
4948
|
+
return c.ok(
|
|
4949
|
+
comments.filter((x) => x.threadId === c.args.threadId).map((comment) => jsonSafe(comment))
|
|
4950
|
+
);
|
|
4670
4951
|
}
|
|
4671
4952
|
});
|
|
4672
4953
|
forum.command("comment", {
|
|
4673
|
-
|
|
4954
|
+
description: "Get one comment by comment id.",
|
|
4955
|
+
args: z2.object({
|
|
4956
|
+
id: z2.coerce.number().int().positive().describe("Comment id (1-indexed)")
|
|
4957
|
+
}),
|
|
4674
4958
|
env: env2,
|
|
4959
|
+
output: z2.record(z2.string(), z2.unknown()),
|
|
4960
|
+
examples: [{ args: { id: 1 }, description: "Fetch comment #1" }],
|
|
4675
4961
|
async run(c) {
|
|
4676
4962
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4677
|
-
const
|
|
4963
|
+
const commentTuple = await client.readContract({
|
|
4678
4964
|
abi: forumAbi,
|
|
4679
4965
|
address: ABSTRACT_MAINNET_ADDRESSES.forum,
|
|
4680
4966
|
functionName: "comments",
|
|
4681
4967
|
args: [BigInt(c.args.id)]
|
|
4682
4968
|
});
|
|
4683
|
-
return c.ok(
|
|
4969
|
+
return c.ok(jsonSafe(decodeComment(commentTuple)));
|
|
4684
4970
|
}
|
|
4685
4971
|
});
|
|
4686
4972
|
forum.command("petitions", {
|
|
4973
|
+
description: "List petitions submitted in the forum contract.",
|
|
4687
4974
|
env: env2,
|
|
4975
|
+
output: z2.array(z2.record(z2.string(), z2.unknown())),
|
|
4976
|
+
examples: [{ description: "List all petitions" }],
|
|
4688
4977
|
async run(c) {
|
|
4689
4978
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4690
4979
|
const count = await client.readContract({
|
|
@@ -4693,7 +4982,7 @@ forum.command("petitions", {
|
|
|
4693
4982
|
functionName: "petitionCount"
|
|
4694
4983
|
});
|
|
4695
4984
|
const ids = Array.from({ length: Number(count) }, (_, i) => BigInt(i + 1));
|
|
4696
|
-
const
|
|
4985
|
+
const petitionTuples = ids.length ? await client.multicall({
|
|
4697
4986
|
allowFailure: false,
|
|
4698
4987
|
contracts: ids.map((id) => ({
|
|
4699
4988
|
abi: forumAbi,
|
|
@@ -4702,32 +4991,58 @@ forum.command("petitions", {
|
|
|
4702
4991
|
args: [id]
|
|
4703
4992
|
}))
|
|
4704
4993
|
}) : [];
|
|
4705
|
-
|
|
4994
|
+
const petitions = petitionTuples.map(decodePetition);
|
|
4995
|
+
return c.ok(petitions.map((petition) => jsonSafe(petition)));
|
|
4706
4996
|
}
|
|
4707
4997
|
});
|
|
4708
4998
|
forum.command("petition", {
|
|
4709
|
-
|
|
4999
|
+
description: "Get one petition plus whether proposer already signed it.",
|
|
5000
|
+
args: z2.object({
|
|
5001
|
+
id: z2.coerce.number().int().positive().describe("Petition id (1-indexed)")
|
|
5002
|
+
}),
|
|
4710
5003
|
env: env2,
|
|
5004
|
+
output: z2.object({ proposerSigned: z2.boolean() }).passthrough(),
|
|
5005
|
+
examples: [{ args: { id: 1 }, description: "Fetch petition #1" }],
|
|
4711
5006
|
async run(c) {
|
|
4712
5007
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4713
|
-
const petition =
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
5008
|
+
const petition = decodePetition(
|
|
5009
|
+
await client.readContract({
|
|
5010
|
+
abi: forumAbi,
|
|
5011
|
+
address: ABSTRACT_MAINNET_ADDRESSES.forum,
|
|
5012
|
+
functionName: "petitions",
|
|
5013
|
+
args: [BigInt(c.args.id)]
|
|
5014
|
+
})
|
|
5015
|
+
);
|
|
4719
5016
|
const proposerSigned = await client.readContract({
|
|
4720
5017
|
abi: forumAbi,
|
|
4721
5018
|
address: ABSTRACT_MAINNET_ADDRESSES.forum,
|
|
4722
5019
|
functionName: "hasSignedPetition",
|
|
4723
5020
|
args: [BigInt(c.args.id), petition.proposer]
|
|
4724
5021
|
});
|
|
4725
|
-
return c.ok({ ...petition, proposerSigned });
|
|
5022
|
+
return c.ok({ ...jsonSafe(petition), proposerSigned });
|
|
4726
5023
|
}
|
|
4727
5024
|
});
|
|
4728
5025
|
forum.command("has-signed", {
|
|
4729
|
-
|
|
5026
|
+
description: "Check whether an address signed a petition.",
|
|
5027
|
+
args: z2.object({
|
|
5028
|
+
petitionId: z2.coerce.number().int().positive().describe("Petition id (1-indexed)"),
|
|
5029
|
+
address: z2.string().describe("Signer address to check")
|
|
5030
|
+
}),
|
|
4730
5031
|
env: env2,
|
|
5032
|
+
output: z2.object({
|
|
5033
|
+
petitionId: z2.number(),
|
|
5034
|
+
address: z2.string(),
|
|
5035
|
+
hasSigned: z2.boolean()
|
|
5036
|
+
}),
|
|
5037
|
+
examples: [
|
|
5038
|
+
{
|
|
5039
|
+
args: {
|
|
5040
|
+
petitionId: 1,
|
|
5041
|
+
address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
|
|
5042
|
+
},
|
|
5043
|
+
description: "Check if an address signed petition #1"
|
|
5044
|
+
}
|
|
5045
|
+
],
|
|
4731
5046
|
async run(c) {
|
|
4732
5047
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4733
5048
|
const hasSigned = await client.readContract({
|
|
@@ -4740,7 +5055,15 @@ forum.command("has-signed", {
|
|
|
4740
5055
|
}
|
|
4741
5056
|
});
|
|
4742
5057
|
forum.command("stats", {
|
|
5058
|
+
description: "Read top-level forum counters and petition threshold.",
|
|
4743
5059
|
env: env2,
|
|
5060
|
+
output: z2.object({
|
|
5061
|
+
threadCount: z2.number(),
|
|
5062
|
+
commentCount: z2.number(),
|
|
5063
|
+
petitionCount: z2.number(),
|
|
5064
|
+
petitionThresholdBps: z2.number()
|
|
5065
|
+
}),
|
|
5066
|
+
examples: [{ description: "Get forum counts and petition threshold" }],
|
|
4744
5067
|
async run(c) {
|
|
4745
5068
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4746
5069
|
const [threadCount, commentCount, petitionCount, petitionThresholdBps] = await Promise.all([
|
|
@@ -4776,12 +5099,105 @@ forum.command("stats", {
|
|
|
4776
5099
|
|
|
4777
5100
|
// src/commands/governance.ts
|
|
4778
5101
|
import { Cli as Cli3, z as z3 } from "incur";
|
|
4779
|
-
var env3 = z3.object({
|
|
5102
|
+
var env3 = z3.object({
|
|
5103
|
+
ABSTRACT_RPC_URL: z3.string().optional().describe("Abstract RPC URL override")
|
|
5104
|
+
});
|
|
5105
|
+
function decodeProposal(value) {
|
|
5106
|
+
const [
|
|
5107
|
+
kind,
|
|
5108
|
+
configRiskTier,
|
|
5109
|
+
origin,
|
|
5110
|
+
status,
|
|
5111
|
+
proposer,
|
|
5112
|
+
threadId,
|
|
5113
|
+
petitionId,
|
|
5114
|
+
createdAt,
|
|
5115
|
+
deliberationEndsAt,
|
|
5116
|
+
voteStartAt,
|
|
5117
|
+
voteEndAt,
|
|
5118
|
+
timelockEndsAt,
|
|
5119
|
+
activeSeatsSnapshot,
|
|
5120
|
+
forVotes,
|
|
5121
|
+
againstVotes,
|
|
5122
|
+
abstainVotes,
|
|
5123
|
+
amount,
|
|
5124
|
+
snapshotAssetBalance,
|
|
5125
|
+
transferIntent,
|
|
5126
|
+
intentDeadline,
|
|
5127
|
+
intentMaxRiskTier,
|
|
5128
|
+
title,
|
|
5129
|
+
description
|
|
5130
|
+
] = value;
|
|
5131
|
+
return {
|
|
5132
|
+
kind,
|
|
5133
|
+
configRiskTier,
|
|
5134
|
+
origin,
|
|
5135
|
+
status,
|
|
5136
|
+
proposer: toChecksum(proposer),
|
|
5137
|
+
threadId,
|
|
5138
|
+
petitionId,
|
|
5139
|
+
createdAt,
|
|
5140
|
+
deliberationEndsAt,
|
|
5141
|
+
voteStartAt,
|
|
5142
|
+
voteEndAt,
|
|
5143
|
+
timelockEndsAt,
|
|
5144
|
+
activeSeatsSnapshot,
|
|
5145
|
+
forVotes,
|
|
5146
|
+
againstVotes,
|
|
5147
|
+
abstainVotes,
|
|
5148
|
+
amount,
|
|
5149
|
+
snapshotAssetBalance,
|
|
5150
|
+
transferIntent,
|
|
5151
|
+
intentDeadline,
|
|
5152
|
+
intentMaxRiskTier,
|
|
5153
|
+
title,
|
|
5154
|
+
description
|
|
5155
|
+
};
|
|
5156
|
+
}
|
|
5157
|
+
function serializeProposal(proposal) {
|
|
5158
|
+
return {
|
|
5159
|
+
kind: asNum(proposal.kind),
|
|
5160
|
+
configRiskTier: asNum(proposal.configRiskTier),
|
|
5161
|
+
origin: asNum(proposal.origin),
|
|
5162
|
+
status: asNum(proposal.status),
|
|
5163
|
+
proposer: proposal.proposer,
|
|
5164
|
+
threadId: asNum(proposal.threadId),
|
|
5165
|
+
petitionId: asNum(proposal.petitionId),
|
|
5166
|
+
createdAt: asNum(proposal.createdAt),
|
|
5167
|
+
deliberationEndsAt: asNum(proposal.deliberationEndsAt),
|
|
5168
|
+
voteStartAt: asNum(proposal.voteStartAt),
|
|
5169
|
+
voteEndAt: asNum(proposal.voteEndAt),
|
|
5170
|
+
timelockEndsAt: asNum(proposal.timelockEndsAt),
|
|
5171
|
+
activeSeatsSnapshot: asNum(proposal.activeSeatsSnapshot),
|
|
5172
|
+
forVotes: proposal.forVotes.toString(),
|
|
5173
|
+
againstVotes: proposal.againstVotes.toString(),
|
|
5174
|
+
abstainVotes: proposal.abstainVotes.toString(),
|
|
5175
|
+
amount: proposal.amount.toString(),
|
|
5176
|
+
snapshotAssetBalance: proposal.snapshotAssetBalance.toString(),
|
|
5177
|
+
transferIntent: proposal.transferIntent,
|
|
5178
|
+
intentDeadline: asNum(proposal.intentDeadline),
|
|
5179
|
+
intentMaxRiskTier: asNum(proposal.intentMaxRiskTier),
|
|
5180
|
+
title: proposal.title,
|
|
5181
|
+
description: proposal.description
|
|
5182
|
+
};
|
|
5183
|
+
}
|
|
4780
5184
|
var governance = Cli3.create("governance", {
|
|
4781
|
-
description: "
|
|
5185
|
+
description: "Inspect Assembly governance proposals, votes, and parameters."
|
|
4782
5186
|
});
|
|
4783
5187
|
governance.command("proposals", {
|
|
5188
|
+
description: "List governance proposals with status and vote end time.",
|
|
4784
5189
|
env: env3,
|
|
5190
|
+
output: z3.array(
|
|
5191
|
+
z3.object({
|
|
5192
|
+
id: z3.number(),
|
|
5193
|
+
kind: z3.number(),
|
|
5194
|
+
status: z3.number(),
|
|
5195
|
+
title: z3.string().nullable().optional(),
|
|
5196
|
+
voteEndAt: z3.number(),
|
|
5197
|
+
voteEndRelative: z3.string()
|
|
5198
|
+
})
|
|
5199
|
+
),
|
|
5200
|
+
examples: [{ description: "List all proposals" }],
|
|
4785
5201
|
async run(c) {
|
|
4786
5202
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4787
5203
|
const count = await client.readContract({
|
|
@@ -4790,7 +5206,7 @@ governance.command("proposals", {
|
|
|
4790
5206
|
functionName: "proposalCount"
|
|
4791
5207
|
});
|
|
4792
5208
|
const ids = Array.from({ length: Number(count) }, (_, i) => BigInt(i + 1));
|
|
4793
|
-
const
|
|
5209
|
+
const proposalTuples = ids.length ? await client.multicall({
|
|
4794
5210
|
allowFailure: false,
|
|
4795
5211
|
contracts: ids.map((id) => ({
|
|
4796
5212
|
abi: governanceAbi,
|
|
@@ -4799,12 +5215,13 @@ governance.command("proposals", {
|
|
|
4799
5215
|
args: [id]
|
|
4800
5216
|
}))
|
|
4801
5217
|
}) : [];
|
|
5218
|
+
const proposals = proposalTuples.map(decodeProposal);
|
|
4802
5219
|
return c.ok(
|
|
4803
5220
|
proposals.map((p, i) => ({
|
|
4804
5221
|
id: i + 1,
|
|
4805
5222
|
kind: asNum(p.kind),
|
|
4806
5223
|
status: asNum(p.status),
|
|
4807
|
-
title: p.title,
|
|
5224
|
+
title: p.title ?? null,
|
|
4808
5225
|
voteEndAt: asNum(p.voteEndAt),
|
|
4809
5226
|
voteEndRelative: relTime(p.voteEndAt)
|
|
4810
5227
|
})),
|
|
@@ -4821,22 +5238,47 @@ governance.command("proposals", {
|
|
|
4821
5238
|
}
|
|
4822
5239
|
});
|
|
4823
5240
|
governance.command("proposal", {
|
|
4824
|
-
|
|
5241
|
+
description: "Get full raw proposal details by proposal id.",
|
|
5242
|
+
args: z3.object({
|
|
5243
|
+
id: z3.coerce.number().int().positive().describe("Proposal id (1-indexed)")
|
|
5244
|
+
}),
|
|
4825
5245
|
env: env3,
|
|
5246
|
+
output: z3.record(z3.string(), z3.unknown()),
|
|
5247
|
+
examples: [{ args: { id: 1 }, description: "Fetch proposal #1" }],
|
|
4826
5248
|
async run(c) {
|
|
4827
5249
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4828
|
-
const proposal =
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
5250
|
+
const proposal = decodeProposal(
|
|
5251
|
+
await client.readContract({
|
|
5252
|
+
abi: governanceAbi,
|
|
5253
|
+
address: ABSTRACT_MAINNET_ADDRESSES.governance,
|
|
5254
|
+
functionName: "proposals",
|
|
5255
|
+
args: [BigInt(c.args.id)]
|
|
5256
|
+
})
|
|
5257
|
+
);
|
|
5258
|
+
return c.ok(serializeProposal(proposal));
|
|
4835
5259
|
}
|
|
4836
5260
|
});
|
|
4837
5261
|
governance.command("has-voted", {
|
|
4838
|
-
|
|
5262
|
+
description: "Check if an address has voted on a proposal.",
|
|
5263
|
+
args: z3.object({
|
|
5264
|
+
proposalId: z3.coerce.number().int().positive().describe("Proposal id (1-indexed)"),
|
|
5265
|
+
address: z3.string().describe("Voter address")
|
|
5266
|
+
}),
|
|
4839
5267
|
env: env3,
|
|
5268
|
+
output: z3.object({
|
|
5269
|
+
proposalId: z3.number(),
|
|
5270
|
+
address: z3.string(),
|
|
5271
|
+
hasVoted: z3.boolean()
|
|
5272
|
+
}),
|
|
5273
|
+
examples: [
|
|
5274
|
+
{
|
|
5275
|
+
args: {
|
|
5276
|
+
proposalId: 1,
|
|
5277
|
+
address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
|
|
5278
|
+
},
|
|
5279
|
+
description: "Check whether an address already voted"
|
|
5280
|
+
}
|
|
5281
|
+
],
|
|
4840
5282
|
async run(c) {
|
|
4841
5283
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4842
5284
|
const hasVoted = await client.readContract({
|
|
@@ -4845,11 +5287,31 @@ governance.command("has-voted", {
|
|
|
4845
5287
|
functionName: "hasVoted",
|
|
4846
5288
|
args: [BigInt(c.args.proposalId), c.args.address]
|
|
4847
5289
|
});
|
|
4848
|
-
return c.ok({
|
|
5290
|
+
return c.ok({
|
|
5291
|
+
proposalId: c.args.proposalId,
|
|
5292
|
+
address: toChecksum(c.args.address),
|
|
5293
|
+
hasVoted
|
|
5294
|
+
});
|
|
4849
5295
|
}
|
|
4850
5296
|
});
|
|
4851
5297
|
governance.command("params", {
|
|
5298
|
+
description: "Read governance threshold and timing parameters.",
|
|
4852
5299
|
env: env3,
|
|
5300
|
+
output: z3.object({
|
|
5301
|
+
deliberationPeriod: z3.number(),
|
|
5302
|
+
votePeriod: z3.number(),
|
|
5303
|
+
quorumBps: z3.number(),
|
|
5304
|
+
constitutionalDeliberationPeriod: z3.number(),
|
|
5305
|
+
constitutionalVotePeriod: z3.number(),
|
|
5306
|
+
constitutionalPassBps: z3.number(),
|
|
5307
|
+
majorPassBps: z3.number(),
|
|
5308
|
+
parameterPassBps: z3.number(),
|
|
5309
|
+
significantPassBps: z3.number(),
|
|
5310
|
+
significantThresholdBps: z3.number(),
|
|
5311
|
+
routineThresholdBps: z3.number(),
|
|
5312
|
+
timelockPeriod: z3.number()
|
|
5313
|
+
}),
|
|
5314
|
+
examples: [{ description: "Inspect governance timing and pass thresholds" }],
|
|
4853
5315
|
async run(c) {
|
|
4854
5316
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4855
5317
|
const getters = [
|
|
@@ -4874,33 +5336,191 @@ governance.command("params", {
|
|
|
4874
5336
|
functionName: name
|
|
4875
5337
|
}))
|
|
4876
5338
|
});
|
|
4877
|
-
return c.ok(
|
|
5339
|
+
return c.ok({
|
|
5340
|
+
deliberationPeriod: asNum(values[0]),
|
|
5341
|
+
votePeriod: asNum(values[1]),
|
|
5342
|
+
quorumBps: asNum(values[2]),
|
|
5343
|
+
constitutionalDeliberationPeriod: asNum(values[3]),
|
|
5344
|
+
constitutionalVotePeriod: asNum(values[4]),
|
|
5345
|
+
constitutionalPassBps: asNum(values[5]),
|
|
5346
|
+
majorPassBps: asNum(values[6]),
|
|
5347
|
+
parameterPassBps: asNum(values[7]),
|
|
5348
|
+
significantPassBps: asNum(values[8]),
|
|
5349
|
+
significantThresholdBps: asNum(values[9]),
|
|
5350
|
+
routineThresholdBps: asNum(values[10]),
|
|
5351
|
+
timelockPeriod: asNum(values[11])
|
|
5352
|
+
});
|
|
4878
5353
|
}
|
|
4879
5354
|
});
|
|
4880
5355
|
|
|
4881
5356
|
// src/commands/members.ts
|
|
4882
5357
|
import { Cli as Cli4, z as z4 } from "incur";
|
|
5358
|
+
var DEFAULT_MEMBER_SNAPSHOT_URL = "https://www.theaiassembly.org/api/indexer/members";
|
|
5359
|
+
var REGISTERED_EVENT_SCAN_STEP = 100000n;
|
|
5360
|
+
var REGISTERED_EVENT_SCAN_TIMEOUT_MS = 2e4;
|
|
4883
5361
|
var env4 = z4.object({
|
|
4884
|
-
ABSTRACT_RPC_URL: z4.string().optional(),
|
|
4885
|
-
ASSEMBLY_INDEXER_URL: z4.string().optional()
|
|
5362
|
+
ABSTRACT_RPC_URL: z4.string().optional().describe("Abstract RPC URL override"),
|
|
5363
|
+
ASSEMBLY_INDEXER_URL: z4.string().optional().describe("Optional members snapshot endpoint (default: theaiassembly.org indexer)")
|
|
4886
5364
|
});
|
|
5365
|
+
var memberSnapshotSchema = z4.array(z4.string());
|
|
5366
|
+
var AssemblyApiValidationError = class extends Error {
|
|
5367
|
+
constructor(details) {
|
|
5368
|
+
super("Assembly API response validation failed");
|
|
5369
|
+
this.details = details;
|
|
5370
|
+
this.name = "AssemblyApiValidationError";
|
|
5371
|
+
}
|
|
5372
|
+
};
|
|
5373
|
+
var AssemblyIndexerUnavailableError = class extends Error {
|
|
5374
|
+
constructor(details) {
|
|
5375
|
+
super("Assembly indexer unavailable");
|
|
5376
|
+
this.details = details;
|
|
5377
|
+
this.name = "AssemblyIndexerUnavailableError";
|
|
5378
|
+
}
|
|
5379
|
+
};
|
|
4887
5380
|
async function memberSnapshot(url) {
|
|
4888
|
-
|
|
4889
|
-
|
|
5381
|
+
let res;
|
|
5382
|
+
try {
|
|
5383
|
+
res = await fetch(url);
|
|
5384
|
+
} catch (error) {
|
|
5385
|
+
throw new AssemblyIndexerUnavailableError({
|
|
5386
|
+
code: "ASSEMBLY_INDEXER_UNAVAILABLE",
|
|
5387
|
+
url,
|
|
5388
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
5389
|
+
});
|
|
5390
|
+
}
|
|
5391
|
+
if (!res.ok) {
|
|
5392
|
+
throw new AssemblyIndexerUnavailableError({
|
|
5393
|
+
code: "ASSEMBLY_INDEXER_UNAVAILABLE",
|
|
5394
|
+
url,
|
|
5395
|
+
status: res.status,
|
|
5396
|
+
statusText: res.statusText
|
|
5397
|
+
});
|
|
5398
|
+
}
|
|
4890
5399
|
const json = await res.json();
|
|
4891
|
-
|
|
4892
|
-
|
|
5400
|
+
const parsed = memberSnapshotSchema.safeParse(json);
|
|
5401
|
+
if (parsed.success) {
|
|
5402
|
+
return parsed.data;
|
|
5403
|
+
}
|
|
5404
|
+
throw new AssemblyApiValidationError({
|
|
5405
|
+
code: "INVALID_ASSEMBLY_API_RESPONSE",
|
|
5406
|
+
url,
|
|
5407
|
+
issues: parsed.error.issues,
|
|
5408
|
+
response: json
|
|
5409
|
+
});
|
|
5410
|
+
}
|
|
5411
|
+
async function withTimeout(promise, timeoutMs, timeoutMessage) {
|
|
5412
|
+
let timer;
|
|
5413
|
+
try {
|
|
5414
|
+
return await Promise.race([
|
|
5415
|
+
promise,
|
|
5416
|
+
new Promise((_, reject) => {
|
|
5417
|
+
timer = setTimeout(() => {
|
|
5418
|
+
reject(new Error(timeoutMessage));
|
|
5419
|
+
}, timeoutMs);
|
|
5420
|
+
})
|
|
5421
|
+
]);
|
|
5422
|
+
} finally {
|
|
5423
|
+
if (timer) clearTimeout(timer);
|
|
5424
|
+
}
|
|
5425
|
+
}
|
|
5426
|
+
async function membersFromRegisteredEvents(client) {
|
|
5427
|
+
const latestBlock = await client.getBlockNumber();
|
|
5428
|
+
const addresses = /* @__PURE__ */ new Set();
|
|
5429
|
+
for (let fromBlock = ABSTRACT_MAINNET_DEPLOYMENT_BLOCKS.registry; fromBlock <= latestBlock; fromBlock += REGISTERED_EVENT_SCAN_STEP) {
|
|
5430
|
+
const toBlock = fromBlock + REGISTERED_EVENT_SCAN_STEP - 1n > latestBlock ? latestBlock : fromBlock + REGISTERED_EVENT_SCAN_STEP - 1n;
|
|
5431
|
+
const events = await client.getContractEvents({
|
|
5432
|
+
abi: registryAbi,
|
|
5433
|
+
address: ABSTRACT_MAINNET_ADDRESSES.registry,
|
|
5434
|
+
eventName: "Registered",
|
|
5435
|
+
fromBlock,
|
|
5436
|
+
toBlock,
|
|
5437
|
+
strict: true
|
|
5438
|
+
});
|
|
5439
|
+
for (const event of events) {
|
|
5440
|
+
const member = event.args.member;
|
|
5441
|
+
if (typeof member === "string") {
|
|
5442
|
+
addresses.add(member);
|
|
5443
|
+
}
|
|
5444
|
+
}
|
|
5445
|
+
}
|
|
5446
|
+
return [...addresses];
|
|
5447
|
+
}
|
|
5448
|
+
function indexerIssue(details) {
|
|
5449
|
+
if (typeof details.status === "number") {
|
|
5450
|
+
return `${details.status}${details.statusText ? ` ${details.statusText}` : ""}`;
|
|
5451
|
+
}
|
|
5452
|
+
if (details.reason) return details.reason;
|
|
5453
|
+
return "unknown error";
|
|
5454
|
+
}
|
|
5455
|
+
function emitIndexerFallbackWarning(details) {
|
|
5456
|
+
process.stderr.write(
|
|
5457
|
+
`${JSON.stringify({
|
|
5458
|
+
level: "warn",
|
|
5459
|
+
code: details.code,
|
|
5460
|
+
message: "Member snapshot indexer is unavailable. Falling back to on-chain Registered events.",
|
|
5461
|
+
url: details.url,
|
|
5462
|
+
issue: indexerIssue(details)
|
|
5463
|
+
})}
|
|
5464
|
+
`
|
|
5465
|
+
);
|
|
4893
5466
|
}
|
|
4894
5467
|
var members = Cli4.create("members", {
|
|
4895
|
-
description: "
|
|
5468
|
+
description: "Inspect Assembly membership and registry fee state."
|
|
4896
5469
|
});
|
|
4897
5470
|
members.command("list", {
|
|
4898
|
-
description: "List members from indexer snapshot
|
|
5471
|
+
description: "List members from an indexer snapshot (or Registered event fallback) plus on-chain active state.",
|
|
4899
5472
|
env: env4,
|
|
5473
|
+
output: z4.array(
|
|
5474
|
+
z4.object({
|
|
5475
|
+
address: z4.string(),
|
|
5476
|
+
active: z4.boolean(),
|
|
5477
|
+
registered: z4.boolean(),
|
|
5478
|
+
activeUntil: z4.number(),
|
|
5479
|
+
activeUntilRelative: z4.string(),
|
|
5480
|
+
lastHeartbeatAt: z4.number(),
|
|
5481
|
+
lastHeartbeatRelative: z4.string()
|
|
5482
|
+
})
|
|
5483
|
+
),
|
|
5484
|
+
examples: [
|
|
5485
|
+
{ description: "List members using default indexer snapshot" },
|
|
5486
|
+
{ description: "Override ASSEMBLY_INDEXER_URL to use a custom snapshot source" }
|
|
5487
|
+
],
|
|
4900
5488
|
async run(c) {
|
|
4901
5489
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4902
|
-
const snapshotUrl = c.env.ASSEMBLY_INDEXER_URL ??
|
|
4903
|
-
|
|
5490
|
+
const snapshotUrl = c.env.ASSEMBLY_INDEXER_URL ?? DEFAULT_MEMBER_SNAPSHOT_URL;
|
|
5491
|
+
let addresses;
|
|
5492
|
+
let fallbackReason;
|
|
5493
|
+
try {
|
|
5494
|
+
addresses = await memberSnapshot(snapshotUrl);
|
|
5495
|
+
} catch (error) {
|
|
5496
|
+
if (error instanceof AssemblyApiValidationError) {
|
|
5497
|
+
return c.error({
|
|
5498
|
+
code: error.details.code,
|
|
5499
|
+
message: `Member snapshot response failed validation. url=${error.details.url}; issues=${JSON.stringify(error.details.issues)}; response=${JSON.stringify(error.details.response)}`,
|
|
5500
|
+
retryable: false
|
|
5501
|
+
});
|
|
5502
|
+
}
|
|
5503
|
+
if (!(error instanceof AssemblyIndexerUnavailableError)) {
|
|
5504
|
+
throw error;
|
|
5505
|
+
}
|
|
5506
|
+
fallbackReason = error.details;
|
|
5507
|
+
try {
|
|
5508
|
+
addresses = await withTimeout(
|
|
5509
|
+
membersFromRegisteredEvents(client),
|
|
5510
|
+
REGISTERED_EVENT_SCAN_TIMEOUT_MS,
|
|
5511
|
+
`Registered event fallback scan timed out after ${REGISTERED_EVENT_SCAN_TIMEOUT_MS}ms`
|
|
5512
|
+
);
|
|
5513
|
+
} catch (fallbackError) {
|
|
5514
|
+
return c.error({
|
|
5515
|
+
code: "MEMBER_LIST_SOURCE_UNAVAILABLE",
|
|
5516
|
+
message: `Member indexer unavailable (${indexerIssue(error.details)} at ${error.details.url}) and on-chain Registered event fallback failed: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`,
|
|
5517
|
+
retryable: true
|
|
5518
|
+
});
|
|
5519
|
+
}
|
|
5520
|
+
}
|
|
5521
|
+
if (fallbackReason) {
|
|
5522
|
+
emitIndexerFallbackWarning(fallbackReason);
|
|
5523
|
+
}
|
|
4904
5524
|
const calls = addresses.flatMap((address) => [
|
|
4905
5525
|
{
|
|
4906
5526
|
abi: registryAbi,
|
|
@@ -4931,16 +5551,32 @@ members.command("list", {
|
|
|
4931
5551
|
});
|
|
4932
5552
|
return c.ok(rows, {
|
|
4933
5553
|
cta: {
|
|
4934
|
-
description: "Inspect one member:",
|
|
5554
|
+
description: fallbackReason ? `Indexer unavailable (${indexerIssue(fallbackReason)}); using on-chain Registered event fallback. Inspect one member:` : "Inspect one member:",
|
|
4935
5555
|
commands: [{ command: "members info", args: { address: "<addr>" } }]
|
|
4936
5556
|
}
|
|
4937
5557
|
});
|
|
4938
5558
|
}
|
|
4939
5559
|
});
|
|
4940
5560
|
members.command("info", {
|
|
4941
|
-
description: "
|
|
4942
|
-
args: z4.object({
|
|
5561
|
+
description: "Get registry record and active status for a member address.",
|
|
5562
|
+
args: z4.object({
|
|
5563
|
+
address: z4.string().describe("Member wallet address")
|
|
5564
|
+
}),
|
|
4943
5565
|
env: env4,
|
|
5566
|
+
output: z4.object({
|
|
5567
|
+
address: z4.string(),
|
|
5568
|
+
active: z4.boolean(),
|
|
5569
|
+
activeUntil: z4.number(),
|
|
5570
|
+
lastHeartbeatAt: z4.number(),
|
|
5571
|
+
activeUntilRelative: z4.string(),
|
|
5572
|
+
lastHeartbeatRelative: z4.string()
|
|
5573
|
+
}),
|
|
5574
|
+
examples: [
|
|
5575
|
+
{
|
|
5576
|
+
args: { address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" },
|
|
5577
|
+
description: "Inspect one member address"
|
|
5578
|
+
}
|
|
5579
|
+
],
|
|
4944
5580
|
async run(c) {
|
|
4945
5581
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4946
5582
|
const [member, active] = await Promise.all([
|
|
@@ -4960,15 +5596,21 @@ members.command("info", {
|
|
|
4960
5596
|
return c.ok({
|
|
4961
5597
|
address: toChecksum(c.args.address),
|
|
4962
5598
|
active,
|
|
4963
|
-
|
|
5599
|
+
activeUntil: Number(member.activeUntil),
|
|
5600
|
+
lastHeartbeatAt: Number(member.lastHeartbeatAt),
|
|
4964
5601
|
activeUntilRelative: relTime(member.activeUntil),
|
|
4965
5602
|
lastHeartbeatRelative: relTime(member.lastHeartbeatAt)
|
|
4966
5603
|
});
|
|
4967
5604
|
}
|
|
4968
5605
|
});
|
|
4969
5606
|
members.command("count", {
|
|
4970
|
-
description: "
|
|
5607
|
+
description: "Get active and total-known member counts from Registry.",
|
|
4971
5608
|
env: env4,
|
|
5609
|
+
output: z4.object({
|
|
5610
|
+
active: z4.number(),
|
|
5611
|
+
total: z4.number()
|
|
5612
|
+
}),
|
|
5613
|
+
examples: [{ description: "Count active and known members" }],
|
|
4972
5614
|
async run(c) {
|
|
4973
5615
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4974
5616
|
const [active, total] = await Promise.all([
|
|
@@ -4987,8 +5629,16 @@ members.command("count", {
|
|
|
4987
5629
|
}
|
|
4988
5630
|
});
|
|
4989
5631
|
members.command("fees", {
|
|
4990
|
-
description: "
|
|
5632
|
+
description: "Get registration and heartbeat fee settings.",
|
|
4991
5633
|
env: env4,
|
|
5634
|
+
output: z4.object({
|
|
5635
|
+
registrationFeeWei: z4.string(),
|
|
5636
|
+
registrationFee: z4.string(),
|
|
5637
|
+
heartbeatFeeWei: z4.string(),
|
|
5638
|
+
heartbeatFee: z4.string(),
|
|
5639
|
+
heartbeatGracePeriodSeconds: z4.number()
|
|
5640
|
+
}),
|
|
5641
|
+
examples: [{ description: "Inspect current registry fee configuration" }],
|
|
4992
5642
|
async run(c) {
|
|
4993
5643
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
4994
5644
|
const [registrationFee, heartbeatFee, heartbeatGracePeriod] = await Promise.all([
|
|
@@ -5020,10 +5670,21 @@ members.command("fees", {
|
|
|
5020
5670
|
|
|
5021
5671
|
// src/commands/treasury.ts
|
|
5022
5672
|
import { Cli as Cli5, z as z5 } from "incur";
|
|
5023
|
-
var env5 = z5.object({
|
|
5024
|
-
|
|
5673
|
+
var env5 = z5.object({
|
|
5674
|
+
ABSTRACT_RPC_URL: z5.string().optional().describe("Abstract RPC URL override")
|
|
5675
|
+
});
|
|
5676
|
+
var treasury = Cli5.create("treasury", {
|
|
5677
|
+
description: "Inspect treasury balances, execution status, and spend controls."
|
|
5678
|
+
});
|
|
5025
5679
|
treasury.command("balance", {
|
|
5680
|
+
description: "Get current native token balance for the treasury contract.",
|
|
5026
5681
|
env: env5,
|
|
5682
|
+
output: z5.object({
|
|
5683
|
+
address: z5.string(),
|
|
5684
|
+
balanceWei: z5.string(),
|
|
5685
|
+
balance: z5.string()
|
|
5686
|
+
}),
|
|
5687
|
+
examples: [{ description: "Check treasury balance" }],
|
|
5027
5688
|
async run(c) {
|
|
5028
5689
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
5029
5690
|
const balance = await client.getBalance({ address: ABSTRACT_MAINNET_ADDRESSES.treasury });
|
|
@@ -5035,8 +5696,21 @@ treasury.command("balance", {
|
|
|
5035
5696
|
}
|
|
5036
5697
|
});
|
|
5037
5698
|
treasury.command("whitelist", {
|
|
5038
|
-
|
|
5699
|
+
description: "Check whether an asset address is treasury-whitelisted.",
|
|
5700
|
+
args: z5.object({
|
|
5701
|
+
asset: z5.string().describe("Token/asset contract address")
|
|
5702
|
+
}),
|
|
5039
5703
|
env: env5,
|
|
5704
|
+
output: z5.object({
|
|
5705
|
+
asset: z5.string(),
|
|
5706
|
+
whitelisted: z5.boolean()
|
|
5707
|
+
}),
|
|
5708
|
+
examples: [
|
|
5709
|
+
{
|
|
5710
|
+
args: { asset: "0x0000000000000000000000000000000000000000" },
|
|
5711
|
+
description: "Check whitelist status for one asset"
|
|
5712
|
+
}
|
|
5713
|
+
],
|
|
5040
5714
|
async run(c) {
|
|
5041
5715
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
5042
5716
|
const whitelisted = await client.readContract({
|
|
@@ -5049,7 +5723,15 @@ treasury.command("whitelist", {
|
|
|
5049
5723
|
}
|
|
5050
5724
|
});
|
|
5051
5725
|
treasury.command("major-spend-status", {
|
|
5726
|
+
description: "Read major-spend cooldown status for the treasury contract.",
|
|
5052
5727
|
env: env5,
|
|
5728
|
+
output: z5.object({
|
|
5729
|
+
majorSpendCooldownSeconds: z5.number(),
|
|
5730
|
+
lastMajorSpendAt: z5.number(),
|
|
5731
|
+
lastMajorSpendRelative: z5.string(),
|
|
5732
|
+
isMajorSpendAllowed: z5.boolean()
|
|
5733
|
+
}),
|
|
5734
|
+
examples: [{ description: "Inspect treasury major-spend guardrails" }],
|
|
5053
5735
|
async run(c) {
|
|
5054
5736
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
5055
5737
|
const [cooldown, lastMajorSpendAt, allowed] = await Promise.all([
|
|
@@ -5080,8 +5762,16 @@ treasury.command("major-spend-status", {
|
|
|
5080
5762
|
}
|
|
5081
5763
|
});
|
|
5082
5764
|
treasury.command("executed", {
|
|
5083
|
-
|
|
5765
|
+
description: "Check whether a treasury action for a proposal has executed.",
|
|
5766
|
+
args: z5.object({
|
|
5767
|
+
proposalId: z5.coerce.number().int().positive().describe("Governance proposal id")
|
|
5768
|
+
}),
|
|
5084
5769
|
env: env5,
|
|
5770
|
+
output: z5.object({
|
|
5771
|
+
proposalId: z5.number(),
|
|
5772
|
+
executed: z5.boolean()
|
|
5773
|
+
}),
|
|
5774
|
+
examples: [{ args: { proposalId: 1 }, description: "Check execution status for proposal #1" }],
|
|
5085
5775
|
async run(c) {
|
|
5086
5776
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
5087
5777
|
const executed = await client.readContract({
|
|
@@ -5094,6 +5784,133 @@ treasury.command("executed", {
|
|
|
5094
5784
|
}
|
|
5095
5785
|
});
|
|
5096
5786
|
|
|
5787
|
+
// src/error-handling.ts
|
|
5788
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
5789
|
+
import { Errors } from "incur";
|
|
5790
|
+
var VIEM_VERSION_PATTERN = /\n*Version:\s*viem@[^\n]+/i;
|
|
5791
|
+
var VIEM_VERSION_PATTERN_GLOBAL = /\n*Version:\s*viem@[^\n]+/gi;
|
|
5792
|
+
var debugFlagStore = new AsyncLocalStorage();
|
|
5793
|
+
var VIEM_ERROR_NAMES = /* @__PURE__ */ new Set([
|
|
5794
|
+
"CallExecutionError",
|
|
5795
|
+
"ContractFunctionExecutionError",
|
|
5796
|
+
"ContractFunctionRevertedError",
|
|
5797
|
+
"HttpRequestError",
|
|
5798
|
+
"InvalidAddressError",
|
|
5799
|
+
"TransactionExecutionError"
|
|
5800
|
+
]);
|
|
5801
|
+
function parseDebugFlag(argv) {
|
|
5802
|
+
const cleaned = [];
|
|
5803
|
+
let debug = false;
|
|
5804
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
5805
|
+
const token = argv[i];
|
|
5806
|
+
if (token === "--debug") {
|
|
5807
|
+
debug = true;
|
|
5808
|
+
continue;
|
|
5809
|
+
}
|
|
5810
|
+
if (token === "--debug=true" || token === "--debug=1") {
|
|
5811
|
+
debug = true;
|
|
5812
|
+
continue;
|
|
5813
|
+
}
|
|
5814
|
+
if (token === "--debug=false" || token === "--debug=0") {
|
|
5815
|
+
debug = false;
|
|
5816
|
+
continue;
|
|
5817
|
+
}
|
|
5818
|
+
cleaned.push(token);
|
|
5819
|
+
}
|
|
5820
|
+
return { argv: cleaned, debug };
|
|
5821
|
+
}
|
|
5822
|
+
function isViemLikeError(error) {
|
|
5823
|
+
if (!(error instanceof Error)) return false;
|
|
5824
|
+
const shortMessage = error.shortMessage;
|
|
5825
|
+
return VIEM_ERROR_NAMES.has(error.name) || VIEM_VERSION_PATTERN.test(error.message) || typeof shortMessage === "string" && error.message.includes("Docs: https://viem.sh");
|
|
5826
|
+
}
|
|
5827
|
+
function sanitizeViemMessage(message) {
|
|
5828
|
+
return message.replace(VIEM_VERSION_PATTERN_GLOBAL, "").trim();
|
|
5829
|
+
}
|
|
5830
|
+
function toFriendlyViemError(error) {
|
|
5831
|
+
const shortMessage = typeof error.shortMessage === "string" && error.shortMessage.trim().length > 0 ? error.shortMessage.trim() : void 0;
|
|
5832
|
+
if (error.name === "InvalidAddressError" || shortMessage?.startsWith('Address "')) {
|
|
5833
|
+
return {
|
|
5834
|
+
code: "INVALID_ADDRESS",
|
|
5835
|
+
message: `${shortMessage ?? "Invalid address."} Use a valid 0x-prefixed 20-byte address. Run with --debug for full error details.`
|
|
5836
|
+
};
|
|
5837
|
+
}
|
|
5838
|
+
if (shortMessage?.toLowerCase().includes("http request failed") || error.message.toLowerCase().includes("http request failed")) {
|
|
5839
|
+
return {
|
|
5840
|
+
code: "RPC_CONNECTION_FAILED",
|
|
5841
|
+
message: "RPC connection failed. Check ABSTRACT_RPC_URL and try again. Run with --debug for full error details."
|
|
5842
|
+
};
|
|
5843
|
+
}
|
|
5844
|
+
if (shortMessage || VIEM_VERSION_PATTERN.test(error.message)) {
|
|
5845
|
+
return {
|
|
5846
|
+
code: "UPSTREAM_ERROR",
|
|
5847
|
+
message: `${sanitizeViemMessage(shortMessage ?? error.message)} Run with --debug for full error details.`
|
|
5848
|
+
};
|
|
5849
|
+
}
|
|
5850
|
+
return void 0;
|
|
5851
|
+
}
|
|
5852
|
+
function isMissingRequiredArgValidation(error) {
|
|
5853
|
+
return error.fieldErrors.some((fieldError) => {
|
|
5854
|
+
const msg = fieldError.message.toLowerCase();
|
|
5855
|
+
return msg.includes("received undefined") || fieldError.received === "";
|
|
5856
|
+
});
|
|
5857
|
+
}
|
|
5858
|
+
function missingArgPaths(error) {
|
|
5859
|
+
const paths = /* @__PURE__ */ new Set();
|
|
5860
|
+
for (const fieldError of error.fieldErrors) {
|
|
5861
|
+
const msg = fieldError.message.toLowerCase();
|
|
5862
|
+
if (msg.includes("received undefined") || fieldError.received === "") {
|
|
5863
|
+
paths.add(fieldError.path);
|
|
5864
|
+
}
|
|
5865
|
+
}
|
|
5866
|
+
return [...paths];
|
|
5867
|
+
}
|
|
5868
|
+
function toFriendlyValidationError(ctx, error) {
|
|
5869
|
+
const missing = missingArgPaths(error);
|
|
5870
|
+
if (missing.length === 0) return void 0;
|
|
5871
|
+
const helpCommand = `${ctx.name} ${ctx.command} --help`;
|
|
5872
|
+
const argsList = missing.join(", ");
|
|
5873
|
+
return {
|
|
5874
|
+
code: "VALIDATION_ERROR",
|
|
5875
|
+
cta: {
|
|
5876
|
+
description: "See command usage:",
|
|
5877
|
+
commands: [{ command: helpCommand }]
|
|
5878
|
+
},
|
|
5879
|
+
message: missing.length === 1 ? `Missing required argument: ${argsList}. Run \`${helpCommand}\` for usage.` : `Missing required arguments: ${argsList}. Run \`${helpCommand}\` for usage.`
|
|
5880
|
+
};
|
|
5881
|
+
}
|
|
5882
|
+
function handleError(ctx, error) {
|
|
5883
|
+
if (debugFlagStore.getStore()) throw error;
|
|
5884
|
+
if (error instanceof Errors.ValidationError && isMissingRequiredArgValidation(error)) {
|
|
5885
|
+
const friendly = toFriendlyValidationError(ctx, error);
|
|
5886
|
+
if (friendly) {
|
|
5887
|
+
return ctx.error(friendly);
|
|
5888
|
+
}
|
|
5889
|
+
}
|
|
5890
|
+
if (isViemLikeError(error)) {
|
|
5891
|
+
const friendly = toFriendlyViemError(error);
|
|
5892
|
+
if (friendly) {
|
|
5893
|
+
return ctx.error(friendly);
|
|
5894
|
+
}
|
|
5895
|
+
}
|
|
5896
|
+
throw error;
|
|
5897
|
+
}
|
|
5898
|
+
function applyFriendlyErrorHandling(cli2) {
|
|
5899
|
+
const originalServe = cli2.serve.bind(cli2);
|
|
5900
|
+
cli2.serve = (async (argv, options) => {
|
|
5901
|
+
const rawArgv = argv ?? process.argv.slice(2);
|
|
5902
|
+
const parsed = parseDebugFlag(rawArgv);
|
|
5903
|
+
return debugFlagStore.run(parsed.debug, () => originalServe(parsed.argv, options));
|
|
5904
|
+
});
|
|
5905
|
+
cli2.use(async (context, next) => {
|
|
5906
|
+
try {
|
|
5907
|
+
await next();
|
|
5908
|
+
} catch (error) {
|
|
5909
|
+
return handleError(context, error);
|
|
5910
|
+
}
|
|
5911
|
+
});
|
|
5912
|
+
}
|
|
5913
|
+
|
|
5097
5914
|
// src/cli.ts
|
|
5098
5915
|
var cli = Cli6.create("assembly", {
|
|
5099
5916
|
description: "Assembly governance CLI for Abstract chain."
|
|
@@ -5103,10 +5920,21 @@ cli.command(council);
|
|
|
5103
5920
|
cli.command(forum);
|
|
5104
5921
|
cli.command(governance);
|
|
5105
5922
|
cli.command(treasury);
|
|
5106
|
-
var rootEnv = z6.object({
|
|
5923
|
+
var rootEnv = z6.object({
|
|
5924
|
+
ABSTRACT_RPC_URL: z6.string().optional().describe("Abstract RPC URL override")
|
|
5925
|
+
});
|
|
5107
5926
|
cli.command("status", {
|
|
5108
|
-
description: "
|
|
5927
|
+
description: "Get a cross-contract Assembly snapshot (members, council, governance, treasury).",
|
|
5109
5928
|
env: rootEnv,
|
|
5929
|
+
output: z6.object({
|
|
5930
|
+
activeMemberCount: z6.number(),
|
|
5931
|
+
seatCount: z6.number(),
|
|
5932
|
+
proposalCount: z6.number(),
|
|
5933
|
+
currentAuctionDay: z6.number(),
|
|
5934
|
+
currentAuctionSlot: z6.number(),
|
|
5935
|
+
treasuryBalance: z6.string()
|
|
5936
|
+
}),
|
|
5937
|
+
examples: [{ description: "Fetch the current Assembly system status" }],
|
|
5110
5938
|
async run(c) {
|
|
5111
5939
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
5112
5940
|
const [
|
|
@@ -5155,9 +5983,25 @@ cli.command("status", {
|
|
|
5155
5983
|
}
|
|
5156
5984
|
});
|
|
5157
5985
|
cli.command("health", {
|
|
5158
|
-
description: "
|
|
5159
|
-
args: z6.object({
|
|
5986
|
+
description: "Check cross-contract health for one address (membership, council, refunds, power).",
|
|
5987
|
+
args: z6.object({
|
|
5988
|
+
address: z6.string().describe("Member or wallet address to inspect")
|
|
5989
|
+
}),
|
|
5160
5990
|
env: rootEnv,
|
|
5991
|
+
output: z6.object({
|
|
5992
|
+
address: z6.string(),
|
|
5993
|
+
isActive: z6.boolean(),
|
|
5994
|
+
activeUntil: z6.number(),
|
|
5995
|
+
isCouncilMember: z6.boolean(),
|
|
5996
|
+
pendingReturnsWei: z6.string(),
|
|
5997
|
+
votingPower: z6.number()
|
|
5998
|
+
}),
|
|
5999
|
+
examples: [
|
|
6000
|
+
{
|
|
6001
|
+
args: { address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" },
|
|
6002
|
+
description: "Inspect one address across Assembly contracts"
|
|
6003
|
+
}
|
|
6004
|
+
],
|
|
5161
6005
|
async run(c) {
|
|
5162
6006
|
const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
5163
6007
|
const [isActive, member, isCouncilMember, pendingReturns, votingPower] = await Promise.all([
|
|
@@ -5193,7 +6037,7 @@ cli.command("health", {
|
|
|
5193
6037
|
})
|
|
5194
6038
|
]);
|
|
5195
6039
|
return c.ok({
|
|
5196
|
-
address: c.args.address,
|
|
6040
|
+
address: toChecksum(c.args.address),
|
|
5197
6041
|
isActive,
|
|
5198
6042
|
activeUntil: Number(member.activeUntil),
|
|
5199
6043
|
isCouncilMember,
|
|
@@ -5202,6 +6046,7 @@ cli.command("health", {
|
|
|
5202
6046
|
});
|
|
5203
6047
|
}
|
|
5204
6048
|
});
|
|
6049
|
+
applyFriendlyErrorHandling(cli);
|
|
5205
6050
|
var isMain = process.argv[1] === fileURLToPath(import.meta.url);
|
|
5206
6051
|
if (isMain) {
|
|
5207
6052
|
cli.serve();
|