@sage-protocol/sdk 0.1.13 → 0.1.14

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