@sage-protocol/sdk 0.1.13 → 0.1.15

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.
@@ -14,7 +14,7 @@ var require_package = __commonJS({
14
14
  "package.json"(exports2, module2) {
15
15
  module2.exports = {
16
16
  name: "@sage-protocol/sdk",
17
- version: "0.1.12",
17
+ version: "0.1.15",
18
18
  description: "Backend-agnostic SDK for interacting with the Sage Protocol (governance, SubDAOs, tokens).",
19
19
  main: "dist/index.cjs",
20
20
  module: "dist/index.mjs",
@@ -70,6 +70,7 @@ var require_package = __commonJS({
70
70
  release: "yarn build && npm publish --workspace @sage-protocol/sdk"
71
71
  },
72
72
  dependencies: {
73
+ "content-hash": "^2.5.2",
73
74
  "@merit-systems/echo-typescript-sdk": "^1.0.17",
74
75
  "@whetstone-research/doppler-sdk": "^0.0.1-alpha.40",
75
76
  ai: "^3.2.3",
@@ -400,6 +401,32 @@ var require_subgraph = __commonJS({
400
401
  var axios = require("axios");
401
402
  var { getAddress } = require("ethers");
402
403
  var { keccak256, toUtf8Bytes } = require("ethers");
404
+ function sanitizeOrderBy(orderBy, allowed, fallback) {
405
+ const value = (orderBy || "").toString();
406
+ return allowed.includes(value) ? value : fallback;
407
+ }
408
+ function sanitizeOrderDirection(direction, fallback = "desc") {
409
+ const v = (direction || "").toString().toLowerCase();
410
+ return v === "asc" ? "asc" : "desc";
411
+ }
412
+ function safeGetAddress(value) {
413
+ try {
414
+ return getAddress(value);
415
+ } catch {
416
+ return null;
417
+ }
418
+ }
419
+ function mapSafe(list, mapper) {
420
+ const out = [];
421
+ for (const item of list || []) {
422
+ try {
423
+ const v = mapper(item);
424
+ if (v != null) out.push(v);
425
+ } catch {
426
+ }
427
+ }
428
+ return out;
429
+ }
403
430
  async function query(url, document, variables) {
404
431
  if (!url) throw new Error("subgraph url required");
405
432
  const resp = await axios.post(url, { query: document, variables }, { timeout: 1e4 });
@@ -422,15 +449,21 @@ var require_subgraph = __commonJS({
422
449
  }
423
450
  }
424
451
  `, { governor: String(governor).toLowerCase(), first, skip });
425
- return (data?.proposals || []).map((p) => ({
426
- id: BigInt(p.id),
427
- proposer: getAddress(p.proposer),
428
- description: p.description,
429
- createdAt: Number(p.createdAt || 0),
430
- targets: (p.targets || []).map(getAddress),
431
- values: (p.values || []).map((value) => BigInt(String(value))),
432
- calldatas: p.calldatas || []
433
- }));
452
+ return mapSafe(data?.proposals, (p) => {
453
+ const proposer = safeGetAddress(p.proposer);
454
+ if (!proposer) return null;
455
+ const targets = (p.targets || []).map((t) => safeGetAddress(t)).filter(Boolean);
456
+ if (!targets.length && (p.targets || []).length) return null;
457
+ return {
458
+ id: BigInt(p.id),
459
+ proposer,
460
+ description: p.description,
461
+ createdAt: Number(p.createdAt || 0),
462
+ targets,
463
+ values: (p.values || []).map((value) => BigInt(String(value))),
464
+ calldatas: p.calldatas || []
465
+ };
466
+ });
434
467
  }
435
468
  var STATE_STRING_TO_NUMBER = {
436
469
  PENDING: 0,
@@ -447,6 +480,8 @@ var require_subgraph = __commonJS({
447
480
  async function listProposalsFiltered({ url, governor, states, fromTimestamp, toTimestamp, first = 20, skip = 0, orderBy = "createdAt", orderDirection = "desc" }) {
448
481
  const govLower = governor ? String(governor).toLowerCase() : null;
449
482
  const statesUpper = Array.isArray(states) && states.length ? states.map((s) => String(s).toUpperCase()) : null;
483
+ const safeOrderBy = sanitizeOrderBy(orderBy, ["createdAt", "updatedAt", "eta"], "createdAt");
484
+ const safeOrderDirection = sanitizeOrderDirection(orderDirection, "desc");
450
485
  const doc = `
451
486
  query($first: Int!, $skip: Int!, $governor: Bytes, $states: [String!], $from: Int, $to: Int) {
452
487
  proposals(
@@ -458,8 +493,8 @@ var require_subgraph = __commonJS({
458
493
  }
459
494
  first: $first
460
495
  skip: $skip
461
- orderBy: ${orderBy}
462
- orderDirection: ${orderDirection}
496
+ orderBy: ${safeOrderBy}
497
+ orderDirection: ${safeOrderDirection}
463
498
  ) {
464
499
  id
465
500
  proposer
@@ -480,19 +515,23 @@ var require_subgraph = __commonJS({
480
515
  if (fromTimestamp !== void 0) variables.from = Number(fromTimestamp);
481
516
  if (toTimestamp !== void 0) variables.to = Number(toTimestamp);
482
517
  const data = await query(url, doc, variables);
483
- return (data?.proposals || []).map((p) => {
518
+ return mapSafe(data?.proposals, (p) => {
484
519
  const stateStr = String(p.state || "").toUpperCase();
485
520
  const stateNum = STATE_STRING_TO_NUMBER[stateStr] != null ? STATE_STRING_TO_NUMBER[stateStr] : null;
521
+ const proposer = safeGetAddress(p.proposer);
522
+ if (!proposer) return null;
523
+ const targets = (p.targets || []).map((t) => safeGetAddress(t)).filter(Boolean);
524
+ if (!targets.length && (p.targets || []).length) return null;
486
525
  return {
487
526
  id: BigInt(p.id),
488
- proposer: getAddress(p.proposer),
527
+ proposer,
489
528
  description: p.description || "",
490
529
  createdAt: Number(p.createdAt || 0),
491
530
  updatedAt: Number(p.updatedAt || 0),
492
531
  state: stateStr,
493
532
  stateNum,
494
533
  eta: p.eta ? BigInt(String(p.eta)) : null,
495
- targets: (p.targets || []).map(getAddress),
534
+ targets,
496
535
  values: (p.values || []).map((value) => BigInt(String(value))),
497
536
  calldatas: p.calldatas || []
498
537
  };
@@ -511,19 +550,127 @@ var require_subgraph = __commonJS({
511
550
  }
512
551
  }
513
552
  `, { first, skip });
514
- return (data?.libraries || []).map((lib) => ({
515
- id: lib.id,
516
- manifestCID: lib.manifestCID,
517
- subdao: getAddress(lib.subDAO),
518
- proposer: getAddress(lib.proposer),
519
- createdAt: Number(lib.createdAt || 0)
520
- }));
553
+ return mapSafe(data?.libraries, (lib) => {
554
+ const sub = safeGetAddress(lib.subDAO);
555
+ const proposer = safeGetAddress(lib.proposer);
556
+ if (!sub || !proposer) return null;
557
+ return {
558
+ id: lib.id,
559
+ manifestCID: lib.manifestCID,
560
+ subdao: sub,
561
+ proposer,
562
+ createdAt: Number(lib.createdAt || 0)
563
+ };
564
+ });
565
+ }
566
+ async function getSubdaoLibraries({ url, subdao, first = 20, skip = 0 }) {
567
+ if (!url) throw new Error("subgraph url required");
568
+ const hasFilter = !!subdao;
569
+ const whereClause = hasFilter ? "where:{ subdao:$subdao }" : "";
570
+ const doc = `
571
+ query($subdao:Bytes,$first:Int!,$skip:Int!){
572
+ subDAOLibraryPointers(
573
+ ${whereClause}
574
+ first:$first,
575
+ skip:$skip,
576
+ orderBy: updatedAt,
577
+ orderDirection: desc
578
+ ){
579
+ id
580
+ subdao
581
+ libraryId
582
+ manifestCID
583
+ previousCID
584
+ promptCount
585
+ updatedAt
586
+ createdAt
587
+ blockNumber
588
+ blockTimestamp
589
+ }
590
+ }
591
+ `;
592
+ const variables = {
593
+ first: Math.min(Math.max(1, Number(first || 20)), 100),
594
+ skip
595
+ };
596
+ if (hasFilter) {
597
+ const addr = safeGetAddress(subdao);
598
+ if (!addr) throw new Error("invalid subdao address");
599
+ variables.subdao = addr.toLowerCase();
600
+ }
601
+ const data = await query(url, doc, variables);
602
+ return mapSafe(data?.subDAOLibraryPointers, (row) => {
603
+ const id2 = row?.id;
604
+ const manifestCID = row?.manifestCID;
605
+ const sub = safeGetAddress(row?.subdao);
606
+ if (!id2 || !manifestCID || !sub) return null;
607
+ const timestamp = (row.updatedAt != null ? Number(row.updatedAt) : null) || (row.createdAt != null ? Number(row.createdAt) : null) || (row.blockTimestamp != null ? Number(row.blockTimestamp) : null);
608
+ return {
609
+ id: String(id2),
610
+ subdao: sub,
611
+ libraryId: String(row.libraryId || "main"),
612
+ manifestCID: String(manifestCID),
613
+ previousCID: row.previousCID || null,
614
+ promptCount: row.promptCount != null ? Number(row.promptCount) : null,
615
+ updatedAt: timestamp,
616
+ createdAt: row.createdAt != null ? Number(row.createdAt) : null,
617
+ blockNumber: row.blockNumber != null ? Number(row.blockNumber) : null,
618
+ blockTimestamp: row.blockTimestamp != null ? Number(row.blockTimestamp) : null
619
+ };
620
+ });
621
+ }
622
+ async function getSubdaoPrompts({ url, registry, first = 50, skip = 0, orderBy = "updatedAt", orderDirection = "desc" }) {
623
+ if (!url) throw new Error("subgraph url required");
624
+ const reg = safeGetAddress(registry);
625
+ if (!reg) throw new Error("invalid registry address");
626
+ const safeOrderBy = sanitizeOrderBy(orderBy, ["updatedAt"], "updatedAt");
627
+ const safeOrderDirection = sanitizeOrderDirection(orderDirection, "desc");
628
+ const doc = `
629
+ query($registry:Bytes!,$first:Int!,$skip:Int!){
630
+ prompts(
631
+ where:{ registry:$registry },
632
+ first:$first,
633
+ skip:$skip,
634
+ orderBy: ${safeOrderBy},
635
+ orderDirection: ${safeOrderDirection}
636
+ ){
637
+ id
638
+ key
639
+ cid
640
+ version
641
+ author
642
+ registry
643
+ updatedAt
644
+ }
645
+ }
646
+ `;
647
+ const data = await query(url, doc, {
648
+ registry: reg.toLowerCase(),
649
+ first: Math.min(Math.max(1, Number(first || 50)), 100),
650
+ skip
651
+ });
652
+ return mapSafe(data?.prompts, (p) => {
653
+ const author = safeGetAddress(p.author);
654
+ const regAddr = safeGetAddress(p.registry);
655
+ if (!author || !regAddr) return null;
656
+ return {
657
+ id: String(p.id),
658
+ key: String(p.key),
659
+ cid: String(p.cid),
660
+ version: BigInt(p.version || "0"),
661
+ author,
662
+ registry: regAddr,
663
+ updatedAt: Number(p.updatedAt || 0)
664
+ };
665
+ });
521
666
  }
522
667
  module2.exports = {
523
668
  query,
524
669
  listProposals,
525
670
  listProposalsFiltered,
526
671
  listLibraries,
672
+ getSubdaoLibraries,
673
+ getSubdaoPrompts,
527
674
  /**
528
675
  * Canonical proposal timeline. Tries common fields first, then event-style fallbacks.
529
676
  * Returns { id, createdAt, queuedAt, executedAt, canceledAt, eta, state } (numbers/strings may be null when unavailable).
@@ -589,86 +736,127 @@ var require_subgraph = __commonJS({
589
736
  async listLiquidityAddPlans({ url, subdao = null, first = 50, skip = 0, orderBy = "blockTimestamp", orderDirection = "desc" }) {
590
737
  if (!url) throw new Error("subgraph url required");
591
738
  const filters = [];
592
- if (subdao) filters.push(`subdao: "${String(getAddress(subdao)).toLowerCase()}"`);
739
+ if (subdao) {
740
+ const addr = safeGetAddress(subdao);
741
+ if (!addr) throw new Error("invalid subdao address");
742
+ filters.push(`subdao: "${addr.toLowerCase()}"`);
743
+ }
593
744
  const where = filters.length ? `where: { ${filters.join(", ")} }` : "";
745
+ const safeOrderBy = sanitizeOrderBy(orderBy, ["blockTimestamp", "blockNumber"], "blockTimestamp");
746
+ const safeOrderDirection = sanitizeOrderDirection(orderDirection, "desc");
594
747
  const doc = `
595
748
  query($first:Int!,$skip:Int!){
596
- liquidityAddPlans(${where} first:$first, skip:$skip, orderBy: ${orderBy}, orderDirection: ${orderDirection}){
749
+ liquidityAddPlans(${where} first:$first, skip:$skip, orderBy: ${safeOrderBy}, orderDirection: ${safeOrderDirection}){
597
750
  id subdao pool sxxxToken stableToken sxxxAmount stableAmount lpRecipient blockNumber blockTimestamp transactionHash
598
751
  }
599
752
  }
600
753
  `;
601
754
  const data = await query(url, doc, { first, skip });
602
- return (data?.liquidityAddPlans || []).map((e) => ({
603
- id: String(e.id),
604
- subdao: getAddress(e.subdao),
605
- pool: getAddress(e.pool),
606
- sxxxToken: getAddress(e.sxxxToken),
607
- stableToken: getAddress(e.stableToken),
608
- sxxxAmount: BigInt(String(e.sxxxAmount)),
609
- stableAmount: BigInt(String(e.stableAmount)),
610
- lpRecipient: getAddress(e.lpRecipient),
611
- blockNumber: Number(e.blockNumber || 0),
612
- blockTimestamp: Number(e.blockTimestamp || 0),
613
- transactionHash: e.transactionHash
614
- }));
755
+ return mapSafe(data?.liquidityAddPlans, (e) => {
756
+ const sub = safeGetAddress(e.subdao);
757
+ const pool = safeGetAddress(e.pool);
758
+ const sxxxToken = safeGetAddress(e.sxxxToken);
759
+ const stableToken = safeGetAddress(e.stableToken);
760
+ const lpRecipient = safeGetAddress(e.lpRecipient);
761
+ if (!sub || !pool || !sxxxToken || !stableToken || !lpRecipient) return null;
762
+ return {
763
+ id: String(e.id),
764
+ subdao: sub,
765
+ pool,
766
+ sxxxToken,
767
+ stableToken,
768
+ sxxxAmount: BigInt(String(e.sxxxAmount)),
769
+ stableAmount: BigInt(String(e.stableAmount)),
770
+ lpRecipient,
771
+ blockNumber: Number(e.blockNumber || 0),
772
+ blockTimestamp: Number(e.blockTimestamp || 0),
773
+ transactionHash: e.transactionHash
774
+ };
775
+ });
615
776
  },
616
777
  async listLiquidityRemovePlans({ url, subdao = null, first = 50, skip = 0, orderBy = "blockTimestamp", orderDirection = "desc" }) {
617
778
  if (!url) throw new Error("subgraph url required");
618
779
  const filters = [];
619
- if (subdao) filters.push(`subdao: "${String(getAddress(subdao)).toLowerCase()}"`);
780
+ if (subdao) {
781
+ const addr = safeGetAddress(subdao);
782
+ if (!addr) throw new Error("invalid subdao address");
783
+ filters.push(`subdao: "${addr.toLowerCase()}"`);
784
+ }
620
785
  const where = filters.length ? `where: { ${filters.join(", ")} }` : "";
786
+ const safeOrderBy = sanitizeOrderBy(orderBy, ["blockTimestamp", "blockNumber"], "blockTimestamp");
787
+ const safeOrderDirection = sanitizeOrderDirection(orderDirection, "desc");
621
788
  const doc = `
622
789
  query($first:Int!,$skip:Int!){
623
- liquidityRemovePlans(${where} first:$first, skip:$skip, orderBy: ${orderBy}, orderDirection: ${orderDirection}){
790
+ liquidityRemovePlans(${where} first:$first, skip:$skip, orderBy: ${safeOrderBy}, orderDirection: ${safeOrderDirection}){
624
791
  id subdao pool lpToken lpAmount recipient blockNumber blockTimestamp transactionHash
625
792
  }
626
793
  }
627
794
  `;
628
795
  const data = await query(url, doc, { first, skip });
629
- return (data?.liquidityRemovePlans || []).map((e) => ({
630
- id: String(e.id),
631
- subdao: getAddress(e.subdao),
632
- pool: getAddress(e.pool),
633
- lpToken: getAddress(e.lpToken),
634
- lpAmount: BigInt(String(e.lpAmount)),
635
- recipient: getAddress(e.recipient),
636
- blockNumber: Number(e.blockNumber || 0),
637
- blockTimestamp: Number(e.blockTimestamp || 0),
638
- transactionHash: e.transactionHash
639
- }));
796
+ return mapSafe(data?.liquidityRemovePlans, (e) => {
797
+ const sub = safeGetAddress(e.subdao);
798
+ const pool = safeGetAddress(e.pool);
799
+ const lpToken = safeGetAddress(e.lpToken);
800
+ const recipient = safeGetAddress(e.recipient);
801
+ if (!sub || !pool || !lpToken || !recipient) return null;
802
+ return {
803
+ id: String(e.id),
804
+ subdao: sub,
805
+ pool,
806
+ lpToken,
807
+ lpAmount: BigInt(String(e.lpAmount)),
808
+ recipient,
809
+ blockNumber: Number(e.blockNumber || 0),
810
+ blockTimestamp: Number(e.blockTimestamp || 0),
811
+ transactionHash: e.transactionHash
812
+ };
813
+ });
640
814
  },
641
815
  async listPromptsByTag({ url, tagsHash, registry = null, first = 50, skip = 0, orderBy = "updatedAt", orderDirection = "desc" }) {
642
816
  if (!url) throw new Error("subgraph url required");
643
817
  const clauses = [`tagsHash: "${String(tagsHash)}"`];
644
- if (registry) clauses.push(`registry: "${String(getAddress(registry)).toLowerCase()}"`);
818
+ if (registry) {
819
+ const addr = safeGetAddress(registry);
820
+ if (!addr) throw new Error("invalid registry address");
821
+ clauses.push(`registry: "${addr.toLowerCase()}"`);
822
+ }
645
823
  const where = `where: { ${clauses.join(", ")} }`;
824
+ const safeOrderBy = sanitizeOrderBy(orderBy, ["updatedAt"], "updatedAt");
825
+ const safeOrderDirection = sanitizeOrderDirection(orderDirection, "desc");
646
826
  const doc = `
647
827
  query($first:Int!,$skip:Int!) {
648
- prompts(${where} first:$first, skip:$skip, orderBy: ${orderBy}, orderDirection: ${orderDirection}) {
828
+ prompts(${where} first:$first, skip:$skip, orderBy: ${safeOrderBy}, orderDirection: ${safeOrderDirection}) {
649
829
  id key cid version author registry updatedAt tagsHash
650
830
  }
651
831
  }
652
832
  `;
653
833
  const data = await query(url, doc, { first, skip });
654
- return (data?.prompts || []).map((p) => ({
655
- id: String(p.id),
656
- key: String(p.key),
657
- cid: String(p.cid),
658
- version: BigInt(p.version || "0"),
659
- author: getAddress(p.author),
660
- registry: getAddress(p.registry),
661
- updatedAt: Number(p.updatedAt || 0),
662
- tagsHash: String(p.tagsHash || "")
663
- }));
834
+ return mapSafe(data?.prompts, (p) => {
835
+ const author = safeGetAddress(p.author);
836
+ const regAddr = safeGetAddress(p.registry);
837
+ if (!author || !regAddr) return null;
838
+ return {
839
+ id: String(p.id),
840
+ key: String(p.key),
841
+ cid: String(p.cid),
842
+ version: BigInt(p.version || "0"),
843
+ author,
844
+ registry: regAddr,
845
+ updatedAt: Number(p.updatedAt || 0),
846
+ tagsHash: String(p.tagsHash || "")
847
+ };
848
+ });
664
849
  },
665
850
  // Prompt helpers (registry scoped)
666
851
  async listRegistryPrompts({ url, registry, first = 50, skip = 0, orderBy = "updatedAt", orderDirection = "desc" }) {
667
852
  if (!url) throw new Error("subgraph url required");
668
- const reg = getAddress(registry);
853
+ const reg = safeGetAddress(registry);
854
+ if (!reg) throw new Error("invalid registry address");
855
+ const safeOrderBy = sanitizeOrderBy(orderBy, ["updatedAt"], "updatedAt");
856
+ const safeOrderDirection = sanitizeOrderDirection(orderDirection, "desc");
669
857
  const doc = `
670
858
  query($first:Int!,$skip:Int!,$registry:Bytes!) {
671
- prompts(where:{ registry: $registry }, first:$first, skip:$skip, orderBy: ${orderBy}, orderDirection: ${orderDirection}) {
859
+ prompts(where:{ registry: $registry }, first:$first, skip:$skip, orderBy: ${safeOrderBy}, orderDirection: ${safeOrderDirection}) {
672
860
  id
673
861
  key
674
862
  cid
@@ -680,19 +868,25 @@ var require_subgraph = __commonJS({
680
868
  }
681
869
  `;
682
870
  const data = await query(url, doc, { first, skip, registry: reg.toLowerCase() });
683
- return (data?.prompts || []).map((p) => ({
684
- id: String(p.id),
685
- key: String(p.key),
686
- cid: String(p.cid),
687
- version: BigInt(p.version || "0"),
688
- author: getAddress(p.author),
689
- registry: getAddress(p.registry),
690
- updatedAt: Number(p.updatedAt || 0)
691
- }));
871
+ return mapSafe(data?.prompts, (p) => {
872
+ const author = safeGetAddress(p.author);
873
+ const regAddr = safeGetAddress(p.registry);
874
+ if (!author || !regAddr) return null;
875
+ return {
876
+ id: String(p.id),
877
+ key: String(p.key),
878
+ cid: String(p.cid),
879
+ version: BigInt(p.version || "0"),
880
+ author,
881
+ registry: regAddr,
882
+ updatedAt: Number(p.updatedAt || 0)
883
+ };
884
+ });
692
885
  },
693
886
  async getPromptByKey({ url, registry, key }) {
694
887
  if (!url) throw new Error("subgraph url required");
695
- const reg = getAddress(registry);
888
+ const reg = safeGetAddress(registry);
889
+ if (!reg) throw new Error("invalid registry address");
696
890
  const doc = `
697
891
  query($registry:Bytes!,$key:String!) {
698
892
  prompts(where:{ registry: $registry, key: $key }, first:1) {
@@ -708,15 +902,19 @@ var require_subgraph = __commonJS({
708
902
  `;
709
903
  const data = await query(url, doc, { registry: reg.toLowerCase(), key: String(key) });
710
904
  const p = (data?.prompts || [])[0];
711
- return p ? {
905
+ if (!p) return null;
906
+ const author = safeGetAddress(p.author);
907
+ const regAddr = safeGetAddress(p.registry);
908
+ if (!author || !regAddr) return null;
909
+ return {
712
910
  id: String(p.id),
713
911
  key: String(p.key),
714
912
  cid: String(p.cid),
715
913
  version: BigInt(p.version || "0"),
716
- author: getAddress(p.author),
717
- registry: getAddress(p.registry),
914
+ author,
915
+ registry: regAddr,
718
916
  updatedAt: Number(p.updatedAt || 0)
719
- } : null;
917
+ };
720
918
  },
721
919
  async getProposalById({ url, id: id2 }) {
722
920
  if (!url) throw new Error("subgraph url required");
@@ -724,18 +922,26 @@ var require_subgraph = __commonJS({
724
922
  const data = await query(url, doc, { id: String(id2) });
725
923
  const p = data?.proposal;
726
924
  if (!p) return null;
727
- return {
728
- id: BigInt(p.id),
729
- proposer: getAddress(p.proposer),
730
- description: p.description || "",
731
- createdAt: Number(p.createdAt || 0),
732
- updatedAt: Number(p.updatedAt || 0),
733
- state: String(p.state || ""),
734
- eta: p.eta ? BigInt(String(p.eta)) : null,
735
- targets: (p.targets || []).map(getAddress),
736
- values: (p.values || []).map((v) => BigInt(String(v))),
737
- calldatas: p.calldatas || []
738
- };
925
+ try {
926
+ const proposer = safeGetAddress(p.proposer);
927
+ if (!proposer) return null;
928
+ const targets = (p.targets || []).map((t) => safeGetAddress(t)).filter(Boolean);
929
+ if (!targets.length && (p.targets || []).length) return null;
930
+ return {
931
+ id: BigInt(p.id),
932
+ proposer,
933
+ description: p.description || "",
934
+ createdAt: Number(p.createdAt || 0),
935
+ updatedAt: Number(p.updatedAt || 0),
936
+ state: String(p.state || ""),
937
+ eta: p.eta ? BigInt(String(p.eta)) : null,
938
+ targets,
939
+ values: (p.values || []).map((v) => BigInt(String(v))),
940
+ calldatas: p.calldatas || []
941
+ };
942
+ } catch {
943
+ return null;
944
+ }
739
945
  }
740
946
  };
741
947
  }
@@ -5624,6 +5830,58 @@ var require_safe = __commonJS({
5624
5830
  }
5625
5831
  });
5626
5832
 
5833
+ // src/utils/time.js
5834
+ var require_time = __commonJS({
5835
+ "src/utils/time.js"(exports2, module2) {
5836
+ function relativeTime(ts) {
5837
+ if (!ts || typeof ts !== "number") return "\u2014";
5838
+ try {
5839
+ const ms = ts < 1e12 ? ts * 1e3 : ts;
5840
+ const diff = Date.now() - ms;
5841
+ if (diff < 0) return "just now";
5842
+ const minutes = Math.round(diff / 6e4);
5843
+ if (minutes < 1) return "just now";
5844
+ if (minutes < 60) return `${minutes}m ago`;
5845
+ const hours = Math.round(minutes / 60);
5846
+ if (hours < 24) return `${hours}h ago`;
5847
+ const days = Math.round(hours / 24);
5848
+ if (days < 30) return `${days}d ago`;
5849
+ const months = Math.round(days / 30);
5850
+ if (months < 12) return `${months}mo ago`;
5851
+ const years = Math.round(months / 12);
5852
+ return `${years}y ago`;
5853
+ } catch {
5854
+ return "\u2014";
5855
+ }
5856
+ }
5857
+ function formatDate(ts) {
5858
+ if (!ts || typeof ts !== "number") return null;
5859
+ try {
5860
+ const ms = ts < 1e12 ? ts * 1e3 : ts;
5861
+ const d = new Date(ms);
5862
+ return d.toLocaleDateString();
5863
+ } catch {
5864
+ return null;
5865
+ }
5866
+ }
5867
+ function formatDateTime(ts) {
5868
+ if (!ts || typeof ts !== "number") return null;
5869
+ try {
5870
+ const ms = ts < 1e12 ? ts * 1e3 : ts;
5871
+ const d = new Date(ms);
5872
+ return d.toLocaleString();
5873
+ } catch {
5874
+ return null;
5875
+ }
5876
+ }
5877
+ module2.exports = {
5878
+ relativeTime,
5879
+ formatDate,
5880
+ formatDateTime
5881
+ };
5882
+ }
5883
+ });
5884
+
5627
5885
  // src/treasury/index.js
5628
5886
  var require_treasury = __commonJS({
5629
5887
  "src/treasury/index.js"(exports2, module2) {
@@ -9188,6 +9446,7 @@ var require_src = __commonJS({
9188
9446
  var subgraph = require_subgraph();
9189
9447
  var privateTx = require_privateTx();
9190
9448
  var safe = require_safe();
9449
+ var time = require_time();
9191
9450
  var treasury = require_treasury();
9192
9451
  var boost = require_boost();
9193
9452
  var bounty = require_bounty();
@@ -9235,7 +9494,7 @@ var require_src = __commonJS({
9235
9494
  boost,
9236
9495
  // bond module removed; bonds deprecated in CLI/SDK
9237
9496
  subgraph,
9238
- utils: { ...utils, privateTx, safe },
9497
+ utils: { ...utils, privateTx, safe, time },
9239
9498
  bounty,
9240
9499
  wallet: Object.assign(wallet, {
9241
9500
  cast: walletCastManager,