@sage-protocol/sdk 0.1.11 → 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.10",
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",
@@ -149,11 +150,22 @@ var require_abi = __commonJS({
149
150
  "function stats() view returns (uint128 totalSubDAOsCreated, uint128 totalBurnedForCreation)",
150
151
  // On-chain enumeration fallback (naming follows FactoryCoreFacet)
151
152
  "function getSubDAOCount() view returns (uint256)",
152
- "function subDaos(uint256) view returns (address)"
153
+ "function subDaos(uint256) view returns (address)",
154
+ // ISubDAOFactory interface functions
155
+ "function getAllSubDAOs() view returns (address[])",
156
+ "function getSubDAORegistry(address subdaoAddress) view returns (address)",
157
+ "function getRegistrySubDAO(address registryAddress) view returns (address)",
158
+ "function getFactoryStats() view returns (uint256 totalSubDAOs, uint256 totalBurned, uint256 averageBurnPerSubDAO)",
159
+ "function isSubDAO(address subdaoAddress) view returns (bool)"
153
160
  ];
154
161
  var FactoryWrite = [
155
162
  "function createSubDAO(string name, string description, uint8 accessModel, uint256 minStakeAmount, uint256 burnAmount) returns (address subDAO, address registry)",
156
163
  "function createSubDAOWithStable(string name, string description, uint8 accessModel, uint256 minStakeAmount, (uint256 value,uint256 deadline,uint8 v,bytes32 r,bytes32 s) permit) returns (address subDAO, address registry)",
164
+ "function createSubDAOWithParams(string name, string description, uint8 accessModel, uint256 minStakeAmount, uint256 burnAmount, uint256 votingDelay, uint256 votingPeriod, uint256 proposalThreshold, uint256 quorumPercentage, uint8 initialForkPolicy, uint8 initialMembershipPolicy, string profileCID) returns (address subDAO, address registry)",
165
+ "function createSubDAOOperatorWithStable(string name, string description, uint8 accessModel, uint256 minStakeAmount, address operatorExecutor, address operatorAdmin, (uint256 value,uint256 deadline,uint8 v,bytes32 r,bytes32 s) permit) returns (address subDAO, address registry)",
166
+ "function createSubDAOOperatorWithStableAdvanced(string name, string description, uint8 accessModel, uint256 minStakeAmount, address operatorExecutor, address operatorAdmin, bool anyoneExec, bool governorProposer, (uint256 value,uint256 deadline,uint8 v,bytes32 r,bytes32 s) permit) returns (address subDAO, address registry)",
167
+ "function createSubDAOOperator(string name, string description, uint8 accessModel, uint256 minStakeAmount, uint256 burnAmount, address operatorExecutor, address operatorAdmin) returns (address subDAO, address registry)",
168
+ "function createSubDAOOperatorAdvanced(string name, string description, uint8 accessModel, uint256 minStakeAmount, uint256 burnAmount, address operatorExecutor, address operatorAdmin, bool anyoneExec, bool governorProposer) returns (address subDAO, address registry)",
157
169
  "function createForkedSubDAO(string newName, string newDescription, string originalName, address originalSubDAO, address forker) returns (address subDAO, address registry)",
158
170
  "function createForkedSubDAOWithStable(string newName, string newDescription, string originalName, address originalSubDAO, address forker, uint64 authorizationNonce, (uint256 value,uint256 deadline,uint8 v,bytes32 r,bytes32 s) permit) returns (address subDAO, address registry)"
159
171
  ];
@@ -395,6 +407,32 @@ var require_subgraph = __commonJS({
395
407
  var axios = __require("axios");
396
408
  var { getAddress } = __require("ethers");
397
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
+ }
398
436
  async function query(url, document, variables) {
399
437
  if (!url) throw new Error("subgraph url required");
400
438
  const resp = await axios.post(url, { query: document, variables }, { timeout: 1e4 });
@@ -417,15 +455,21 @@ var require_subgraph = __commonJS({
417
455
  }
418
456
  }
419
457
  `, { governor: String(governor).toLowerCase(), first, skip });
420
- return (data?.proposals || []).map((p) => ({
421
- id: BigInt(p.id),
422
- proposer: getAddress(p.proposer),
423
- description: p.description,
424
- createdAt: Number(p.createdAt || 0),
425
- targets: (p.targets || []).map(getAddress),
426
- values: (p.values || []).map((value) => BigInt(String(value))),
427
- calldatas: p.calldatas || []
428
- }));
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
+ });
429
473
  }
430
474
  var STATE_STRING_TO_NUMBER = {
431
475
  PENDING: 0,
@@ -442,6 +486,8 @@ var require_subgraph = __commonJS({
442
486
  async function listProposalsFiltered({ url, governor, states, fromTimestamp, toTimestamp, first = 20, skip = 0, orderBy = "createdAt", orderDirection = "desc" }) {
443
487
  const govLower = governor ? String(governor).toLowerCase() : null;
444
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");
445
491
  const doc = `
446
492
  query($first: Int!, $skip: Int!, $governor: Bytes, $states: [String!], $from: Int, $to: Int) {
447
493
  proposals(
@@ -453,8 +499,8 @@ var require_subgraph = __commonJS({
453
499
  }
454
500
  first: $first
455
501
  skip: $skip
456
- orderBy: ${orderBy}
457
- orderDirection: ${orderDirection}
502
+ orderBy: ${safeOrderBy}
503
+ orderDirection: ${safeOrderDirection}
458
504
  ) {
459
505
  id
460
506
  proposer
@@ -475,19 +521,23 @@ var require_subgraph = __commonJS({
475
521
  if (fromTimestamp !== void 0) variables.from = Number(fromTimestamp);
476
522
  if (toTimestamp !== void 0) variables.to = Number(toTimestamp);
477
523
  const data = await query(url, doc, variables);
478
- return (data?.proposals || []).map((p) => {
524
+ return mapSafe(data?.proposals, (p) => {
479
525
  const stateStr = String(p.state || "").toUpperCase();
480
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;
481
531
  return {
482
532
  id: BigInt(p.id),
483
- proposer: getAddress(p.proposer),
533
+ proposer,
484
534
  description: p.description || "",
485
535
  createdAt: Number(p.createdAt || 0),
486
536
  updatedAt: Number(p.updatedAt || 0),
487
537
  state: stateStr,
488
538
  stateNum,
489
539
  eta: p.eta ? BigInt(String(p.eta)) : null,
490
- targets: (p.targets || []).map(getAddress),
540
+ targets,
491
541
  values: (p.values || []).map((value) => BigInt(String(value))),
492
542
  calldatas: p.calldatas || []
493
543
  };
@@ -506,19 +556,127 @@ var require_subgraph = __commonJS({
506
556
  }
507
557
  }
508
558
  `, { first, skip });
509
- return (data?.libraries || []).map((lib) => ({
510
- id: lib.id,
511
- manifestCID: lib.manifestCID,
512
- subdao: getAddress(lib.subDAO),
513
- proposer: getAddress(lib.proposer),
514
- createdAt: Number(lib.createdAt || 0)
515
- }));
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
+ });
516
672
  }
517
673
  module2.exports = {
518
674
  query,
519
675
  listProposals,
520
676
  listProposalsFiltered,
521
677
  listLibraries,
678
+ getSubdaoLibraries,
679
+ getSubdaoPrompts,
522
680
  /**
523
681
  * Canonical proposal timeline. Tries common fields first, then event-style fallbacks.
524
682
  * Returns { id, createdAt, queuedAt, executedAt, canceledAt, eta, state } (numbers/strings may be null when unavailable).
@@ -584,86 +742,127 @@ var require_subgraph = __commonJS({
584
742
  async listLiquidityAddPlans({ url, subdao = null, first = 50, skip = 0, orderBy = "blockTimestamp", orderDirection = "desc" }) {
585
743
  if (!url) throw new Error("subgraph url required");
586
744
  const filters = [];
587
- 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
+ }
588
750
  const where = filters.length ? `where: { ${filters.join(", ")} }` : "";
751
+ const safeOrderBy = sanitizeOrderBy(orderBy, ["blockTimestamp", "blockNumber"], "blockTimestamp");
752
+ const safeOrderDirection = sanitizeOrderDirection(orderDirection, "desc");
589
753
  const doc = `
590
754
  query($first:Int!,$skip:Int!){
591
- liquidityAddPlans(${where} first:$first, skip:$skip, orderBy: ${orderBy}, orderDirection: ${orderDirection}){
755
+ liquidityAddPlans(${where} first:$first, skip:$skip, orderBy: ${safeOrderBy}, orderDirection: ${safeOrderDirection}){
592
756
  id subdao pool sxxxToken stableToken sxxxAmount stableAmount lpRecipient blockNumber blockTimestamp transactionHash
593
757
  }
594
758
  }
595
759
  `;
596
760
  const data = await query(url, doc, { first, skip });
597
- return (data?.liquidityAddPlans || []).map((e) => ({
598
- id: String(e.id),
599
- subdao: getAddress(e.subdao),
600
- pool: getAddress(e.pool),
601
- sxxxToken: getAddress(e.sxxxToken),
602
- stableToken: getAddress(e.stableToken),
603
- sxxxAmount: BigInt(String(e.sxxxAmount)),
604
- stableAmount: BigInt(String(e.stableAmount)),
605
- lpRecipient: getAddress(e.lpRecipient),
606
- blockNumber: Number(e.blockNumber || 0),
607
- blockTimestamp: Number(e.blockTimestamp || 0),
608
- transactionHash: e.transactionHash
609
- }));
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
+ });
610
782
  },
611
783
  async listLiquidityRemovePlans({ url, subdao = null, first = 50, skip = 0, orderBy = "blockTimestamp", orderDirection = "desc" }) {
612
784
  if (!url) throw new Error("subgraph url required");
613
785
  const filters = [];
614
- 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
+ }
615
791
  const where = filters.length ? `where: { ${filters.join(", ")} }` : "";
792
+ const safeOrderBy = sanitizeOrderBy(orderBy, ["blockTimestamp", "blockNumber"], "blockTimestamp");
793
+ const safeOrderDirection = sanitizeOrderDirection(orderDirection, "desc");
616
794
  const doc = `
617
795
  query($first:Int!,$skip:Int!){
618
- liquidityRemovePlans(${where} first:$first, skip:$skip, orderBy: ${orderBy}, orderDirection: ${orderDirection}){
796
+ liquidityRemovePlans(${where} first:$first, skip:$skip, orderBy: ${safeOrderBy}, orderDirection: ${safeOrderDirection}){
619
797
  id subdao pool lpToken lpAmount recipient blockNumber blockTimestamp transactionHash
620
798
  }
621
799
  }
622
800
  `;
623
801
  const data = await query(url, doc, { first, skip });
624
- return (data?.liquidityRemovePlans || []).map((e) => ({
625
- id: String(e.id),
626
- subdao: getAddress(e.subdao),
627
- pool: getAddress(e.pool),
628
- lpToken: getAddress(e.lpToken),
629
- lpAmount: BigInt(String(e.lpAmount)),
630
- recipient: getAddress(e.recipient),
631
- blockNumber: Number(e.blockNumber || 0),
632
- blockTimestamp: Number(e.blockTimestamp || 0),
633
- transactionHash: e.transactionHash
634
- }));
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
+ });
635
820
  },
636
821
  async listPromptsByTag({ url, tagsHash, registry = null, first = 50, skip = 0, orderBy = "updatedAt", orderDirection = "desc" }) {
637
822
  if (!url) throw new Error("subgraph url required");
638
823
  const clauses = [`tagsHash: "${String(tagsHash)}"`];
639
- 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
+ }
640
829
  const where = `where: { ${clauses.join(", ")} }`;
830
+ const safeOrderBy = sanitizeOrderBy(orderBy, ["updatedAt"], "updatedAt");
831
+ const safeOrderDirection = sanitizeOrderDirection(orderDirection, "desc");
641
832
  const doc = `
642
833
  query($first:Int!,$skip:Int!) {
643
- prompts(${where} first:$first, skip:$skip, orderBy: ${orderBy}, orderDirection: ${orderDirection}) {
834
+ prompts(${where} first:$first, skip:$skip, orderBy: ${safeOrderBy}, orderDirection: ${safeOrderDirection}) {
644
835
  id key cid version author registry updatedAt tagsHash
645
836
  }
646
837
  }
647
838
  `;
648
839
  const data = await query(url, doc, { first, skip });
649
- return (data?.prompts || []).map((p) => ({
650
- id: String(p.id),
651
- key: String(p.key),
652
- cid: String(p.cid),
653
- version: BigInt(p.version || "0"),
654
- author: getAddress(p.author),
655
- registry: getAddress(p.registry),
656
- updatedAt: Number(p.updatedAt || 0),
657
- tagsHash: String(p.tagsHash || "")
658
- }));
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
+ });
659
855
  },
660
856
  // Prompt helpers (registry scoped)
661
857
  async listRegistryPrompts({ url, registry, first = 50, skip = 0, orderBy = "updatedAt", orderDirection = "desc" }) {
662
858
  if (!url) throw new Error("subgraph url required");
663
- 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");
664
863
  const doc = `
665
864
  query($first:Int!,$skip:Int!,$registry:Bytes!) {
666
- 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}) {
667
866
  id
668
867
  key
669
868
  cid
@@ -675,19 +874,25 @@ var require_subgraph = __commonJS({
675
874
  }
676
875
  `;
677
876
  const data = await query(url, doc, { first, skip, registry: reg.toLowerCase() });
678
- return (data?.prompts || []).map((p) => ({
679
- id: String(p.id),
680
- key: String(p.key),
681
- cid: String(p.cid),
682
- version: BigInt(p.version || "0"),
683
- author: getAddress(p.author),
684
- registry: getAddress(p.registry),
685
- updatedAt: Number(p.updatedAt || 0)
686
- }));
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
+ });
687
891
  },
688
892
  async getPromptByKey({ url, registry, key }) {
689
893
  if (!url) throw new Error("subgraph url required");
690
- const reg = getAddress(registry);
894
+ const reg = safeGetAddress(registry);
895
+ if (!reg) throw new Error("invalid registry address");
691
896
  const doc = `
692
897
  query($registry:Bytes!,$key:String!) {
693
898
  prompts(where:{ registry: $registry, key: $key }, first:1) {
@@ -703,15 +908,19 @@ var require_subgraph = __commonJS({
703
908
  `;
704
909
  const data = await query(url, doc, { registry: reg.toLowerCase(), key: String(key) });
705
910
  const p = (data?.prompts || [])[0];
706
- 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 {
707
916
  id: String(p.id),
708
917
  key: String(p.key),
709
918
  cid: String(p.cid),
710
919
  version: BigInt(p.version || "0"),
711
- author: getAddress(p.author),
712
- registry: getAddress(p.registry),
920
+ author,
921
+ registry: regAddr,
713
922
  updatedAt: Number(p.updatedAt || 0)
714
- } : null;
923
+ };
715
924
  },
716
925
  async getProposalById({ url, id: id2 }) {
717
926
  if (!url) throw new Error("subgraph url required");
@@ -719,18 +928,26 @@ var require_subgraph = __commonJS({
719
928
  const data = await query(url, doc, { id: String(id2) });
720
929
  const p = data?.proposal;
721
930
  if (!p) return null;
722
- return {
723
- id: BigInt(p.id),
724
- proposer: getAddress(p.proposer),
725
- description: p.description || "",
726
- createdAt: Number(p.createdAt || 0),
727
- updatedAt: Number(p.updatedAt || 0),
728
- state: String(p.state || ""),
729
- eta: p.eta ? BigInt(String(p.eta)) : null,
730
- targets: (p.targets || []).map(getAddress),
731
- values: (p.values || []).map((v) => BigInt(String(v))),
732
- calldatas: p.calldatas || []
733
- };
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
+ }
734
951
  }
735
952
  };
736
953
  }
@@ -3981,7 +4198,17 @@ var require_factory = __commonJS({
3981
4198
  ] = await Promise.all([
3982
4199
  contract.stableFeeToken().catch(() => null),
3983
4200
  contract.stableCreationFee().catch(() => null),
3984
- contract.feeReceiver().catch(() => null),
4201
+ (async () => {
4202
+ try {
4203
+ return await contract.stableFeeReceiver();
4204
+ } catch (_) {
4205
+ try {
4206
+ return await contract.feeReceiver();
4207
+ } catch {
4208
+ return null;
4209
+ }
4210
+ }
4211
+ })(),
3985
4212
  contract.allowStableFee().catch(() => null),
3986
4213
  contract.stablePromptForkFee().catch(() => null),
3987
4214
  contract.allowStablePromptForkFee().catch(() => null),
@@ -4148,6 +4375,102 @@ var require_factory = __commonJS({
4148
4375
  ]);
4149
4376
  return { to: addr, data: payload, value: 0n };
4150
4377
  }
4378
+ function buildCreateSubDAOWithParamsTx({
4379
+ factory,
4380
+ name,
4381
+ description,
4382
+ accessModel,
4383
+ minStakeAmount,
4384
+ burnAmount,
4385
+ votingDelay,
4386
+ votingPeriod,
4387
+ proposalThreshold,
4388
+ quorumPercentage,
4389
+ initialForkPolicy,
4390
+ initialMembershipPolicy,
4391
+ profileCID
4392
+ }) {
4393
+ const addr = normalise(factory, "factory");
4394
+ const payload = FactoryWriteInterface.encodeFunctionData("createSubDAOWithParams", [
4395
+ String(name),
4396
+ String(description),
4397
+ Number(accessModel),
4398
+ BigInt(minStakeAmount),
4399
+ BigInt(burnAmount),
4400
+ BigInt(votingDelay),
4401
+ BigInt(votingPeriod),
4402
+ BigInt(proposalThreshold),
4403
+ BigInt(quorumPercentage),
4404
+ Number(initialForkPolicy),
4405
+ Number(initialMembershipPolicy),
4406
+ String(profileCID ?? "")
4407
+ ]);
4408
+ return { to: addr, data: payload, value: 0n };
4409
+ }
4410
+ function buildCreateOperatorSubDAOWithStableTx({
4411
+ factory,
4412
+ name,
4413
+ description,
4414
+ accessModel,
4415
+ minStakeAmount,
4416
+ operatorExecutor,
4417
+ operatorAdmin,
4418
+ permit
4419
+ }) {
4420
+ const addr = normalise(factory, "factory");
4421
+ if (!permit) throw new SageSDKError(CODES.INVALID_ARGS, "permit required for operator stable creation");
4422
+ const tuple = [
4423
+ BigInt(permit.value ?? 0n),
4424
+ BigInt(permit.deadline ?? 0n),
4425
+ Number(permit.v ?? 0),
4426
+ permit.r ?? "0x0000000000000000000000000000000000000000000000000000000000000000",
4427
+ permit.s ?? "0x0000000000000000000000000000000000000000000000000000000000000000"
4428
+ ];
4429
+ const payload = FactoryWriteInterface.encodeFunctionData("createSubDAOOperatorWithStable", [
4430
+ String(name),
4431
+ String(description),
4432
+ Number(accessModel),
4433
+ BigInt(minStakeAmount),
4434
+ normalise(operatorExecutor, "operatorExecutor"),
4435
+ normalise(operatorAdmin, "operatorAdmin"),
4436
+ tuple
4437
+ ]);
4438
+ return { to: addr, data: payload, value: 0n };
4439
+ }
4440
+ function buildCreateOperatorSubDAOWithStableAdvancedTx({
4441
+ factory,
4442
+ name,
4443
+ description,
4444
+ accessModel,
4445
+ minStakeAmount,
4446
+ operatorExecutor,
4447
+ operatorAdmin,
4448
+ anyoneExec = false,
4449
+ governorProposer = false,
4450
+ permit
4451
+ }) {
4452
+ const addr = normalise(factory, "factory");
4453
+ if (!permit) throw new SageSDKError(CODES.INVALID_ARGS, "permit required for operator stable creation");
4454
+ const tuple = [
4455
+ BigInt(permit.value ?? 0n),
4456
+ BigInt(permit.deadline ?? 0n),
4457
+ Number(permit.v ?? 0),
4458
+ permit.r ?? "0x0000000000000000000000000000000000000000000000000000000000000000",
4459
+ permit.s ?? "0x0000000000000000000000000000000000000000000000000000000000000000"
4460
+ ];
4461
+ const payload = FactoryWriteInterface.encodeFunctionData("createSubDAOOperatorWithStableAdvanced", [
4462
+ String(name),
4463
+ String(description),
4464
+ Number(accessModel),
4465
+ BigInt(minStakeAmount),
4466
+ normalise(operatorExecutor, "operatorExecutor"),
4467
+ normalise(operatorAdmin, "operatorAdmin"),
4468
+ Boolean(anyoneExec),
4469
+ Boolean(governorProposer),
4470
+ tuple
4471
+ ]);
4472
+ return { to: addr, data: payload, value: 0n };
4473
+ }
4151
4474
  async function listSubDAOs({ provider, factory }) {
4152
4475
  if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
4153
4476
  const addr = normalise(factory, "factory");
@@ -4228,6 +4551,9 @@ var require_factory = __commonJS({
4228
4551
  getTemplateDetails,
4229
4552
  buildCreateSubDAOTx,
4230
4553
  buildCreateSubDAOWithStableTx,
4554
+ buildCreateSubDAOWithParamsTx,
4555
+ buildCreateOperatorSubDAOWithStableTx,
4556
+ buildCreateOperatorSubDAOWithStableAdvancedTx,
4231
4557
  buildCreateForkedSubDAOTx,
4232
4558
  buildCreateForkedSubDAOWithStableTx
4233
4559
  };
@@ -5510,6 +5836,58 @@ var require_safe = __commonJS({
5510
5836
  }
5511
5837
  });
5512
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
+
5513
5891
  // src/treasury/index.js
5514
5892
  var require_treasury = __commonJS({
5515
5893
  "src/treasury/index.js"(exports2, module2) {
@@ -9074,6 +9452,7 @@ var require_src = __commonJS({
9074
9452
  var subgraph = require_subgraph();
9075
9453
  var privateTx = require_privateTx();
9076
9454
  var safe = require_safe();
9455
+ var time = require_time();
9077
9456
  var treasury = require_treasury();
9078
9457
  var boost = require_boost();
9079
9458
  var bounty = require_bounty();
@@ -9121,7 +9500,7 @@ var require_src = __commonJS({
9121
9500
  boost,
9122
9501
  // bond module removed; bonds deprecated in CLI/SDK
9123
9502
  subgraph,
9124
- utils: { ...utils, privateTx, safe },
9503
+ utils: { ...utils, privateTx, safe, time },
9125
9504
  bounty,
9126
9505
  wallet: Object.assign(wallet, {
9127
9506
  cast: walletCastManager,