@sage-protocol/sdk 0.1.23 → 0.1.25

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.23",
23
+ version: "0.1.25",
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",
@@ -112,20 +112,21 @@ var require_abi = __commonJS({
112
112
  var SubDAO = [
113
113
  "function governor() view returns (address)",
114
114
  "function timelock() view returns (address)",
115
+ // SubDAO ops Safe / treasury (multisig) address. On newer deployments opsSafe() is an alias.
116
+ "function treasury() view returns (address)",
117
+ "function opsSafe() view returns (address)",
115
118
  "function getGovernanceMode() view returns (uint8)",
119
+ // New governance profile (3-axis model). May be unavailable on legacy SubDAOs.
120
+ "function profileInitialized() view returns (bool)",
121
+ "function getGovernanceProfile() view returns (uint8 kind, uint8 proposalAccess, uint8 executionAccess)",
116
122
  "function promptRegistry() view returns (address)",
117
123
  "function sageTreasury() view returns (address)",
118
- "function stakeToken() view returns (address)",
119
- "function minStakeAmount() view returns (uint256)",
120
124
  "function accessModel() view returns (uint8)",
121
125
  "function membershipPolicy() view returns (uint8)",
122
126
  "function profileCID() view returns (string)",
123
127
  "function factoryDeployer() view returns (address)",
124
- "function stakedAmount(address) view returns (uint256)",
125
128
  "function userForkCount(address) view returns (uint256)",
126
129
  "function forkCount(string) view returns (uint256)",
127
- "function stake(uint256)",
128
- "function unstake(uint256)",
129
130
  // Fork functions
130
131
  "function forkSubDAO(string,string)",
131
132
  "function forkSubDAO(string,string,bool)",
@@ -165,7 +166,7 @@ var require_abi = __commonJS({
165
166
  var FactoryWrite = [
166
167
  "function createSubDAO(string name, string description, uint8 accessModel, uint256 minStakeAmount, uint256 burnAmount) returns (address subDAO, address registry)",
167
168
  "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)",
168
- "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)",
169
+ "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, string manifestCID, string manifestVersion) returns (address subDAO, address registry)",
169
170
  "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)",
170
171
  "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)",
171
172
  "function createSubDAOOperator(string name, string description, uint8 accessModel, uint256 minStakeAmount, uint256 burnAmount, address operatorExecutor, address operatorAdmin) returns (address subDAO, address registry)",
@@ -182,19 +183,21 @@ var require_abi = __commonJS({
182
183
  "function maxCreationBurn() view returns (uint256)"
183
184
  ];
184
185
  var LibraryRegistry = [
185
- "function libraryByDAO(address) view returns (string manifestCID, address lastUpdater, uint256 lastUpdated, string version, address forkedFromDAO, uint256 sxxxForkFee)",
186
+ "function libraryByDAO(address) view returns (string manifestCID, address lastUpdater, uint256 lastUpdated, string version, address forkedFromDAO, uint256 sxxxForkFee, uint256 nonce)",
186
187
  "function daoTimelock(address) view returns (address)",
187
- "function getLibrary(address) view returns (tuple(string manifestCID, address lastUpdater, uint256 lastUpdated, string version, address forkedFromDAO, uint256 sxxxForkFee))",
188
+ "function getLibrary(address) view returns (tuple(string manifestCID, address lastUpdater, uint256 lastUpdated, string version, address forkedFromDAO, uint256 sxxxForkFee, uint256 nonce))",
188
189
  "function getLibraryForkFee(address dao) view returns (uint256)",
189
190
  "function setLibraryForkFee(address dao, uint256 fee)",
190
191
  "function updateLibrary(address dao, string manifestCID, string version)",
192
+ "function updateLibraryCAS(address dao, string manifestCID, string version, uint256 expectedNonce)",
191
193
  "function registerDAO(address dao, address timelock)",
192
194
  "function registerForkedDAO(address childDAO, address childTimelock, address parentDAO, string manifestCID, string version)",
193
195
  "function initializeLibraryFromFork(address sourceDao, address forkedDao)",
194
- "event LibraryUpdated(address indexed dao, string manifestCID, address indexed timelock, string version)",
196
+ "event LibraryUpdated(address indexed dao, string manifestCID, address indexed timelock, string version, uint256 nonce)",
195
197
  "event DAORegistered(address indexed dao, address indexed timelock)",
196
198
  "event LibraryForked(address indexed sourceDao, address indexed forkedDao, string manifestCID)",
197
- "event LibraryForkFeeUpdated(address indexed dao, uint256 fee)"
199
+ "event LibraryForkFeeUpdated(address indexed dao, uint256 fee)",
200
+ "error NonceMismatch(uint256 expected, uint256 actual)"
198
201
  ];
199
202
  var PromptRegistry = [
200
203
  "function prompts(string key) view returns (string cid, uint256 version, uint256 timestamp, address author, string forkedFromCID, address originalAuthor, bool isFork, uint256 forkDepth)",
@@ -221,14 +224,23 @@ var require_abi = __commonJS({
221
224
  ];
222
225
  var Governor = [
223
226
  "function name() view returns (string)",
227
+ // ERC6372 clock (Governor uses this for vote snapshots)
228
+ "function clock() view returns (uint48)",
224
229
  "function timelock() view returns (address)",
225
230
  "function _executor() view returns (address)",
226
231
  "function sxxxToken() view returns (address)",
227
232
  "function votingDelay() view returns (uint256)",
228
233
  "function votingPeriod() view returns (uint256)",
229
234
  "function proposalThreshold() view returns (uint256)",
235
+ "function minVotesToVote() view returns (uint256)",
236
+ // OZ Governor: voting power at a past timepoint
237
+ "function getVotes(address,uint256) view returns (uint256)",
230
238
  // OZ Governor: quorum at a past block
231
239
  "function quorum(uint256) view returns (uint256)",
240
+ // Fixed quorumVotes model (PromptGovernor v2+)
241
+ "function quorumVotes() view returns (uint256)",
242
+ "function setQuorumVotes(uint256)",
243
+ // Legacy OZ Governor: percentage-based quorum (deprecated)
232
244
  "function quorumNumerator() view returns (uint256)",
233
245
  "function proposalStakeAmount(uint256) view returns (uint256)",
234
246
  "function state(uint256) view returns (uint8)",
@@ -241,9 +253,24 @@ var require_abi = __commonJS({
241
253
  "function castVoteWithReason(uint256 proposalId, uint8 support, string reason) returns (uint256)",
242
254
  "function queue(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash)",
243
255
  "function execute(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash)",
256
+ "function cancel(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) returns (uint256)",
244
257
  // Some Governors expose id-based queue/execute via TimelockController
245
258
  "function queue(uint256)",
246
- "function execute(uint256)"
259
+ "function execute(uint256)",
260
+ // Threshold model (PromptGovernor)
261
+ "function proposalProposer(uint256 proposalId) view returns (address)",
262
+ "function proposalThresholdSnapshot(uint256 proposalId) view returns (uint256)",
263
+ "function proposalCreator(uint256 proposalId) view returns (address)",
264
+ "function proposalCooldown() view returns (uint256)",
265
+ "function lastProposalTimestamp(address) view returns (uint256)",
266
+ "function getProposalCooldownInfo(address) view returns (bool canPropose, uint256 cooldownRemaining)",
267
+ "function canCreateProposal(address) view returns (bool)",
268
+ "function setMinVotesToVote(uint256)",
269
+ // Multiplier helpers
270
+ "function baseVotesToken() view returns (address)",
271
+ "function isMultiplierEnabled() view returns (bool)",
272
+ "function multiplierNFT() view returns (address)",
273
+ "function getVotingBreakdown(address) view returns (uint256 baseVotes, uint256 multiplier, uint256 effectiveVotes)"
247
274
  ];
248
275
  var Timelock = [
249
276
  "function getMinDelay() view returns (uint256)",
@@ -463,13 +490,11 @@ var require_abi = __commonJS({
463
490
  "function governanceConfig() view returns (address)",
464
491
  "function communityTreasury() view returns (address)",
465
492
  "function governanceContract() view returns (address)",
466
- "function libraryRegistry() view returns (address)",
467
493
  "function subdao() view returns (address)",
468
494
  "function soulboundBadge() view returns (address)",
469
495
  // Admin
470
496
  "function setGovernanceContract(address _gov)",
471
497
  "function setCommunityTreasury(address _treasury)",
472
- "function setLibraryRegistry(address _registry)",
473
498
  "function setSoulboundBadge(address _sbtAddress)",
474
499
  "function grantRole(bytes32 role, address account)",
475
500
  "function revokeRole(bytes32 role, address account)",
@@ -483,7 +508,7 @@ var require_abi = __commonJS({
483
508
  "event VotingStarted(uint256 indexed bountyId, uint256 votingEndTime)",
484
509
  "event BountyCompleted(uint256 indexed bountyId, address indexed winner, uint256 reward, string deliverable)",
485
510
  "event BountyAutoApproved(uint256 indexed bountyId, address indexed winner)",
486
- "event PromptAddedToLibrary(uint256 indexed bountyId, string libraryKey, string promptIPFS)",
511
+ "event BountyWinnerSelected(uint256 indexed bountyId, address indexed subdao, string libraryKey, string winnerCid, address indexed winner, uint8 libraryAction)",
487
512
  "event BountyCancelled(uint256 indexed bountyId, address indexed creator)",
488
513
  "event BountyExpired(uint256 indexed bountyId)",
489
514
  "event BountyRewardIncreased(uint256 indexed bountyId, uint256 additionalReward, uint256 newTotal)",
@@ -1577,193 +1602,496 @@ var require_transports = __commonJS({
1577
1602
  }
1578
1603
  });
1579
1604
 
1580
- // src/browser/utils.js
1581
- var require_utils = __commonJS({
1582
- "src/browser/utils.js"(exports2, module2) {
1583
- function getAddress(address) {
1584
- if (!address) return address;
1585
- const addr = String(address).trim();
1586
- const normalized = addr.startsWith("0x") ? addr.slice(2).toLowerCase() : addr.toLowerCase();
1587
- return `0x${normalized}`;
1588
- }
1589
- async function keccak256Async(data) {
1590
- const encoder = new TextEncoder();
1591
- const dataBuffer = typeof data === "string" ? encoder.encode(data) : data;
1592
- const hashBuffer = await crypto.subtle.digest("SHA-256", dataBuffer);
1593
- const hashArray = Array.from(new Uint8Array(hashBuffer));
1594
- const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
1595
- return `0x${hashHex}`;
1596
- }
1597
- function keccak256Sync(data) {
1598
- const str = typeof data === "string" ? data : JSON.stringify(data);
1599
- let hash = 0;
1600
- for (let i = 0; i < str.length; i++) {
1601
- const char = str.charCodeAt(i);
1602
- hash = (hash << 5) - hash + char;
1603
- hash = hash & hash;
1605
+ // src/utils/logs.js
1606
+ var require_logs = __commonJS({
1607
+ "src/utils/logs.js"(exports2, module2) {
1608
+ async function fetchLogsChunked({ provider, address, topics = [], fromBlock = 0, toBlock = "latest", chunkSize = 5e4 }) {
1609
+ if (!provider) throw new Error("provider required");
1610
+ const latest = toBlock === "latest" ? await provider.getBlockNumber() : Number(toBlock);
1611
+ let start = Number(fromBlock);
1612
+ const logs = [];
1613
+ while (start <= latest) {
1614
+ const end = Math.min(start + chunkSize - 1, latest);
1615
+ const chunk = await provider.getLogs({ address, topics, fromBlock: start, toBlock: end });
1616
+ logs.push(...chunk);
1617
+ start = end + 1;
1604
1618
  }
1605
- const hex = Math.abs(hash).toString(16).padStart(8, "0");
1606
- return `0x${hex.repeat(8)}`.slice(0, 66);
1619
+ return logs;
1607
1620
  }
1608
1621
  module2.exports = {
1609
- getAddress,
1610
- keccak256Async,
1611
- keccak256Sync
1622
+ fetchLogsChunked
1612
1623
  };
1613
1624
  }
1614
1625
  });
1615
1626
 
1616
- // src/browser/subgraph.js
1617
- var require_subgraph2 = __commonJS({
1618
- "src/browser/subgraph.js"(exports2, module2) {
1619
- var { getAddress } = require_utils();
1620
- function sanitizeOrderBy(orderBy, allowed, fallback) {
1621
- const value = (orderBy || "").toString();
1622
- return allowed.includes(value) ? value : fallback;
1623
- }
1624
- function sanitizeOrderDirection(direction, fallback = "desc") {
1625
- const v = (direction || "").toString().toLowerCase();
1626
- return v === "asc" ? "asc" : "desc";
1627
- }
1628
- function safeGetAddress(value) {
1627
+ // src/subdao/index.js
1628
+ var require_subdao = __commonJS({
1629
+ "src/subdao/index.js"(exports2, module2) {
1630
+ var { Contract, Interface, getAddress } = __require("ethers");
1631
+ var ABI = require_abi();
1632
+ var { fetchLogsChunked } = require_logs();
1633
+ var { SageSDKError, CODES } = require_errors();
1634
+ function normalise(address, label) {
1635
+ if (!address) throw new SageSDKError(CODES.INVALID_ARGS, `${label} required`);
1629
1636
  try {
1630
- return getAddress(value);
1631
- } catch {
1632
- return null;
1637
+ return getAddress(address);
1638
+ } catch (err) {
1639
+ throw new SageSDKError(CODES.INVALID_ARGS, `invalid ${label}`, { cause: err });
1633
1640
  }
1634
1641
  }
1635
- function mapSafe(list, mapper) {
1642
+ async function discoverSubDAOs({ provider, factoryAddress, fromBlock = 0, toBlock = "latest", limit = 5e3, chunkSize = 5e4 }) {
1643
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
1644
+ const fac = normalise(factoryAddress, "factoryAddress");
1645
+ const iface = new Interface(ABI.Factory);
1646
+ const topic = iface.getEvent("SubDAOGovernanceDeployed").topicHash;
1647
+ const logs = await fetchLogsChunked({ provider, address: fac, topics: [topic], fromBlock, toBlock, chunkSize });
1636
1648
  const out = [];
1637
- for (const item of list || []) {
1649
+ const slice = limit > 0 ? logs.slice(-limit) : logs;
1650
+ for (const log of slice) {
1638
1651
  try {
1639
- const v = mapper(item);
1640
- if (v != null) out.push(v);
1641
- } catch {
1652
+ const decoded = iface.decodeEventLog("SubDAOGovernanceDeployed", log.data, log.topics);
1653
+ out.push({
1654
+ subdao: getAddress(decoded[0]),
1655
+ governor: getAddress(decoded[1]),
1656
+ timelock: getAddress(decoded[2]),
1657
+ treasury: decoded[3] ? getAddress(decoded[3]) : null,
1658
+ txHash: log.transactionHash,
1659
+ blockNumber: log.blockNumber
1660
+ });
1661
+ } catch (err) {
1662
+ continue;
1642
1663
  }
1643
1664
  }
1644
1665
  return out;
1645
1666
  }
1646
- async function query(url, document, variables) {
1647
- if (!url) throw new Error("subgraph url required");
1648
- const controller = new AbortController();
1649
- const timeoutId = setTimeout(() => controller.abort(), 1e4);
1667
+ async function getSubDAOInfo({ provider, subdao }) {
1668
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
1669
+ const addr = normalise(subdao, "subdao");
1670
+ const contract = new Contract(addr, ABI.SubDAO, provider);
1671
+ const [governor, timelock, treasury, mode, promptRegistry, sageTreasury, accessModel, membershipPolicy, profileCID, factoryDeployer] = await Promise.all([
1672
+ contract.governor(),
1673
+ contract.timelock(),
1674
+ // The ops Safe address (multisig) controlling treasury/admin ops.
1675
+ contract.treasury().catch(() => null),
1676
+ contract.getGovernanceMode().catch(() => null),
1677
+ contract.promptRegistry().catch(() => null),
1678
+ contract.sageTreasury().catch(() => null),
1679
+ contract.accessModel().catch(() => null),
1680
+ contract.membershipPolicy().catch(() => null),
1681
+ contract.profileCID().catch(() => null),
1682
+ contract.factoryDeployer().catch(() => null)
1683
+ ]);
1684
+ return {
1685
+ address: addr,
1686
+ governor: getAddress(governor),
1687
+ timelock: getAddress(timelock),
1688
+ treasury: treasury && treasury !== "0x0000000000000000000000000000000000000000" ? getAddress(treasury) : null,
1689
+ governanceMode: mode,
1690
+ promptRegistry: promptRegistry ? getAddress(promptRegistry) : null,
1691
+ sageTreasury: sageTreasury ? getAddress(sageTreasury) : null,
1692
+ accessModel: accessModel != null ? Number(accessModel) : null,
1693
+ membershipPolicy: membershipPolicy != null ? Number(membershipPolicy) : null,
1694
+ profileCID: profileCID || null,
1695
+ factoryDeployer: factoryDeployer && factoryDeployer !== "0x0000000000000000000000000000000000000000" ? getAddress(factoryDeployer) : null
1696
+ };
1697
+ }
1698
+ function decodeGovernanceProfile({ kind, proposalAccess, executionAccess }) {
1699
+ const kindLabel = Number(kind) === 0 ? "OPERATOR" : Number(kind) === 1 ? "TOKEN" : null;
1700
+ const proposalLabel = Number(proposalAccess) === 0 ? "COUNCIL_ONLY" : Number(proposalAccess) === 1 ? "COMMUNITY_THRESHOLD" : null;
1701
+ const execLabel = Number(executionAccess) === 0 ? "COUNCIL_ONLY" : Number(executionAccess) === 1 ? "ANYONE" : null;
1702
+ return {
1703
+ kind: kindLabel,
1704
+ proposalAccess: proposalLabel,
1705
+ executionAccess: execLabel
1706
+ };
1707
+ }
1708
+ async function getGovernanceProfile({ provider, subdao }) {
1709
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
1710
+ const addr = normalise(subdao, "subdao");
1711
+ const contract = new Contract(addr, ABI.SubDAO, provider);
1712
+ let profileInitialized = false;
1713
+ let source = "legacy";
1650
1714
  try {
1651
- const resp = await fetch(url, {
1652
- method: "POST",
1653
- headers: { "Content-Type": "application/json" },
1654
- body: JSON.stringify({ query: document, variables }),
1655
- signal: controller.signal
1656
- });
1657
- clearTimeout(timeoutId);
1658
- if (!resp.ok) {
1659
- throw new Error(`HTTP ${resp.status}: ${resp.statusText}`);
1660
- }
1661
- const data = await resp.json();
1662
- if (data && data.errors) {
1663
- throw new Error(data.errors.map((e) => e.message).join("; "));
1715
+ profileInitialized = !!await contract.profileInitialized();
1716
+ source = profileInitialized ? "profile" : "legacy";
1717
+ } catch (_) {
1718
+ profileInitialized = false;
1719
+ source = "legacy";
1720
+ }
1721
+ if (profileInitialized) {
1722
+ try {
1723
+ const [kind, proposalAccess, executionAccess] = await contract.getGovernanceProfile();
1724
+ return {
1725
+ ...decodeGovernanceProfile({ kind, proposalAccess, executionAccess }),
1726
+ profileInitialized: true,
1727
+ source
1728
+ };
1729
+ } catch (_) {
1730
+ profileInitialized = false;
1731
+ source = "legacy";
1664
1732
  }
1665
- return data.data;
1666
- } catch (error) {
1667
- clearTimeout(timeoutId);
1668
- throw error;
1669
1733
  }
1670
- }
1671
- async function listProposals({ url, governor, first = 20, skip = 0 }) {
1672
- const data = await query(url, `
1673
- query($governor: Bytes!, $first: Int!, $skip: Int!) {
1674
- proposals(where: { governor: $governor }, first: $first, skip: $skip, orderBy: createdAt, orderDirection: desc) {
1675
- id
1676
- proposer
1677
- description
1678
- createdAt
1679
- targets
1680
- values
1681
- calldatas
1734
+ let mode = null;
1735
+ try {
1736
+ mode = await contract.getGovernanceMode();
1737
+ } catch (_) {
1682
1738
  }
1683
- }
1684
- `, { governor: String(governor).toLowerCase(), first, skip });
1685
- return mapSafe(data?.proposals, (p) => {
1686
- const proposer = safeGetAddress(p.proposer);
1687
- if (!proposer) return null;
1688
- const targets = (p.targets || []).map((t) => safeGetAddress(t)).filter(Boolean);
1689
- if (!targets.length && (p.targets || []).length) return null;
1739
+ if (mode != null && Number(mode) === 0) {
1690
1740
  return {
1691
- id: BigInt(p.id),
1692
- proposer,
1693
- description: p.description,
1694
- createdAt: Number(p.createdAt || 0),
1695
- targets,
1696
- values: (p.values || []).map((value) => BigInt(String(value))),
1697
- calldatas: p.calldatas || []
1741
+ kind: "OPERATOR",
1742
+ proposalAccess: "COUNCIL_ONLY",
1743
+ executionAccess: "COUNCIL_ONLY",
1744
+ profileInitialized: false,
1745
+ source
1698
1746
  };
1699
- });
1700
- }
1701
- var STATE_STRING_TO_NUMBER = {
1702
- PENDING: 0,
1703
- ACTIVE: 1,
1704
- CANCELED: 2,
1705
- CANCELLED: 2,
1706
- // some indexes may use British spelling
1707
- DEFEATED: 3,
1708
- SUCCEEDED: 4,
1709
- QUEUED: 5,
1710
- EXPIRED: 6,
1711
- EXECUTED: 7
1712
- };
1713
- async function listProposalsFiltered({ url, governor, states, fromTimestamp, toTimestamp, first = 20, skip = 0, orderBy = "createdAt", orderDirection = "desc" }) {
1714
- const govLower = governor ? String(governor).toLowerCase() : null;
1715
- const statesUpper = Array.isArray(states) && states.length ? states.map((s) => String(s).toUpperCase()) : null;
1716
- const safeOrderBy = sanitizeOrderBy(orderBy, ["createdAt", "updatedAt", "eta"], "createdAt");
1717
- const safeOrderDirection = sanitizeOrderDirection(orderDirection, "desc");
1718
- const doc = `
1719
- query($first: Int!, $skip: Int!, $governor: Bytes, $states: [String!], $from: Int, $to: Int) {
1720
- proposals(
1721
- where: {
1722
- ${governor ? "governor: $governor," : ""}
1723
- ${statesUpper ? "state_in: $states," : ""}
1724
- ${fromTimestamp !== void 0 ? "createdAt_gte: $from," : ""}
1725
- ${toTimestamp !== void 0 ? "createdAt_lte: $to," : ""}
1726
- }
1727
- first: $first
1728
- skip: $skip
1729
- orderBy: ${safeOrderBy}
1730
- orderDirection: ${safeOrderDirection}
1731
- ) {
1732
- id
1733
- proposer
1734
- description
1735
- createdAt
1736
- updatedAt
1737
- state
1738
- eta
1739
- targets
1740
- values
1741
- calldatas
1742
1747
  }
1743
- }
1744
- `;
1745
- const variables = { first, skip };
1746
- if (govLower) variables.governor = govLower;
1747
- if (statesUpper) variables.states = statesUpper;
1748
- if (fromTimestamp !== void 0) variables.from = Number(fromTimestamp);
1749
- if (toTimestamp !== void 0) variables.to = Number(toTimestamp);
1750
- const data = await query(url, doc, variables);
1751
- return mapSafe(data?.proposals, (p) => {
1752
- const stateStr = String(p.state || "").toUpperCase();
1753
- const stateNum = STATE_STRING_TO_NUMBER[stateStr] != null ? STATE_STRING_TO_NUMBER[stateStr] : null;
1754
- const proposer = safeGetAddress(p.proposer);
1755
- if (!proposer) return null;
1756
- const targets = (p.targets || []).map((t) => safeGetAddress(t)).filter(Boolean);
1757
- if (!targets.length && (p.targets || []).length) return null;
1748
+ if (mode != null && Number(mode) === 1) {
1758
1749
  return {
1759
- id: BigInt(p.id),
1760
- proposer,
1761
- description: p.description || "",
1762
- createdAt: Number(p.createdAt || 0),
1763
- updatedAt: Number(p.updatedAt || 0),
1764
- state: stateStr,
1765
- stateNum,
1766
- eta: p.eta ? BigInt(String(p.eta)) : null,
1750
+ kind: "TOKEN",
1751
+ proposalAccess: "COMMUNITY_THRESHOLD",
1752
+ executionAccess: "ANYONE",
1753
+ profileInitialized: false,
1754
+ source
1755
+ };
1756
+ }
1757
+ return {
1758
+ kind: null,
1759
+ proposalAccess: null,
1760
+ executionAccess: null,
1761
+ profileInitialized: false,
1762
+ source
1763
+ };
1764
+ }
1765
+ async function getSubDAOUserStats({ provider, subdao, account }) {
1766
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
1767
+ const addr = normalise(subdao, "subdao");
1768
+ const user = normalise(account, "account");
1769
+ const contract = new Contract(addr, ABI.SubDAO, provider);
1770
+ const userForkCount = await contract.userForkCount(user).catch(() => 0n);
1771
+ return {
1772
+ userForkCount: Number(userForkCount)
1773
+ };
1774
+ }
1775
+ var SubDAOInterface = new Interface([
1776
+ // Optional on newer SubDAO implementations; provided for tx building only.
1777
+ "function setProfileCid(string)"
1778
+ ]);
1779
+ function buildSetProfileCidTx({ subdao, profileCid }) {
1780
+ const addr = normalise(subdao, "subdao");
1781
+ if (!profileCid || typeof profileCid !== "string" || !profileCid.trim()) {
1782
+ throw new SageSDKError(CODES.INVALID_ARGS, "profileCid (CID string) required");
1783
+ }
1784
+ const data = SubDAOInterface.encodeFunctionData("setProfileCid", [String(profileCid)]);
1785
+ return { to: addr, data, value: 0n };
1786
+ }
1787
+ module2.exports = {
1788
+ discoverSubDAOs,
1789
+ getSubDAOInfo,
1790
+ getGovernanceProfile,
1791
+ getSubDAOUserStats,
1792
+ buildSetProfileCidTx
1793
+ };
1794
+ async function ensureSxxxBurnAllowance({ signer, sxxx, owner, spender, amount }) {
1795
+ if (!signer) throw new SageSDKError(CODES.INVALID_ARGS, "signer required");
1796
+ if (!sxxx) throw new SageSDKError(CODES.INVALID_ARGS, "sxxx address required");
1797
+ if (!spender) throw new SageSDKError(CODES.INVALID_ARGS, "spender (factory) required");
1798
+ const sxxxAddr = getAddress(sxxx);
1799
+ const spenderAddr = getAddress(spender);
1800
+ const erc = new Contract(sxxxAddr, ABI.SXXX, signer);
1801
+ const ownerAddr = owner ? getAddress(owner) : await signer.getAddress();
1802
+ const need = BigInt(amount || 0n);
1803
+ if (need === 0n) return { approved: false, tx: null };
1804
+ const current = await erc.allowance(ownerAddr, spenderAddr);
1805
+ const cur = BigInt(current.toString());
1806
+ if (cur >= need) return { approved: false, tx: null };
1807
+ const tx = await erc.approve(spenderAddr, need);
1808
+ const rcpt = await tx.wait();
1809
+ return { approved: true, tx, receipt: rcpt };
1810
+ }
1811
+ async function createSubDAO({ signer, factoryAddress, name, description, accessModel, burnAmount, sxxx }) {
1812
+ if (!signer) throw new SageSDKError(CODES.INVALID_ARGS, "signer required");
1813
+ if (!factoryAddress) throw new SageSDKError(CODES.INVALID_ARGS, "factoryAddress required");
1814
+ const provider = signer.provider;
1815
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "signer.provider required");
1816
+ if (sxxx && BigInt(burnAmount || 0n) > 0n) {
1817
+ await ensureSxxxBurnAllowance({ signer, sxxx, spender: factoryAddress, amount: BigInt(burnAmount) });
1818
+ }
1819
+ const fac = new Contract(getAddress(factoryAddress), ABI.FactoryWrite, signer);
1820
+ const args = [
1821
+ String(name || ""),
1822
+ String(description || ""),
1823
+ Number(accessModel ?? 0),
1824
+ 0n,
1825
+ // minStakeAmount deprecated
1826
+ BigInt(burnAmount ?? 0n)
1827
+ ];
1828
+ let tx;
1829
+ try {
1830
+ tx = await fac.createSubDAO(...args);
1831
+ } catch (err) {
1832
+ tx = await fac.createSubDAO(...args, { gasLimit: 8e6 });
1833
+ }
1834
+ const receipt = await tx.wait();
1835
+ const iface = new Interface(ABI.Factory);
1836
+ let subdao = null, governor = null, timelock = null, treasury = null, registry = null;
1837
+ for (const log of receipt.logs || []) {
1838
+ try {
1839
+ const parsed = iface.parseLog(log);
1840
+ if (parsed && parsed.name === "SubDAOGovernanceDeployed") {
1841
+ subdao = getAddress(parsed.args.subDAO || parsed.args[0]);
1842
+ governor = getAddress(parsed.args.governor || parsed.args[1]);
1843
+ timelock = getAddress(parsed.args.timelock || parsed.args[2]);
1844
+ treasury = getAddress(parsed.args.treasury || parsed.args[3]);
1845
+ } else if (parsed && parsed.name === "SubDAOCreated") {
1846
+ const maybe = parsed.args.registry || parsed.args[2];
1847
+ if (maybe) registry = getAddress(maybe);
1848
+ if (!subdao) subdao = getAddress(parsed.args.subDAO || parsed.args[0]);
1849
+ }
1850
+ } catch (_) {
1851
+ }
1852
+ }
1853
+ try {
1854
+ if (subdao && !registry) {
1855
+ const sub = new Contract(subdao, ABI.SubDAO, provider);
1856
+ const reg = await sub.promptRegistry().catch(() => null);
1857
+ if (reg && /^0x/.test(String(reg))) registry = getAddress(reg);
1858
+ }
1859
+ } catch (_) {
1860
+ }
1861
+ return { tx, receipt, subdao, governor, timelock, treasury, registry };
1862
+ }
1863
+ async function makeOperator({ signer, subdao, operator, grantAdmin = false }) {
1864
+ if (!signer) throw new SageSDKError(CODES.INVALID_ARGS, "signer required");
1865
+ const provider = signer.provider;
1866
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "signer.provider required");
1867
+ const addr = normalise(subdao, "subdao");
1868
+ const sub = new Contract(addr, ABI.SubDAO, provider);
1869
+ const governor = getAddress(await sub.governor());
1870
+ const timelock = getAddress(await sub.timelock());
1871
+ const TL_ABI = [
1872
+ "function PROPOSER_ROLE() view returns (bytes32)",
1873
+ "function EXECUTOR_ROLE() view returns (bytes32)",
1874
+ "function DEFAULT_ADMIN_ROLE() view returns (bytes32)",
1875
+ "function grantRole(bytes32,address)",
1876
+ "function revokeRole(bytes32,address)"
1877
+ ];
1878
+ const tl = new Contract(timelock, TL_ABI, signer);
1879
+ const [PR, ER, AR] = await Promise.all([
1880
+ tl.PROPOSER_ROLE(),
1881
+ tl.EXECUTOR_ROLE(),
1882
+ tl.DEFAULT_ADMIN_ROLE().catch(() => null)
1883
+ ]);
1884
+ const ops = [];
1885
+ ops.push(await tl.grantRole(PR, getAddress(operator)));
1886
+ ops.push(await tl.grantRole(ER, getAddress(operator)));
1887
+ ops.push(await tl.revokeRole(PR, governor));
1888
+ if (grantAdmin && AR) ops.push(await tl.grantRole(AR, getAddress(operator)));
1889
+ const receipts = [];
1890
+ for (const tx of ops) {
1891
+ receipts.push(await tx.wait());
1892
+ }
1893
+ return { governor, timelock, operator: getAddress(operator), txs: ops, receipts };
1894
+ }
1895
+ async function createOperatorSubDAO({ signer, factoryAddress, operator, name, description, accessModel, burnAmount, sxxx, grantAdmin = false }) {
1896
+ const created = await createSubDAO({ signer, factoryAddress, name, description, accessModel, burnAmount, sxxx });
1897
+ if (!created.subdao) throw new SageSDKError(CODES.UNKNOWN, "createSubDAO did not emit SubDAO address");
1898
+ const op = await makeOperator({ signer, subdao: created.subdao, operator, grantAdmin });
1899
+ return { ...created, operatorResult: op };
1900
+ }
1901
+ module2.exports.ensureSxxxBurnAllowance = ensureSxxxBurnAllowance;
1902
+ module2.exports.createSubDAO = createSubDAO;
1903
+ module2.exports.makeOperator = makeOperator;
1904
+ module2.exports.createOperatorSubDAO = createOperatorSubDAO;
1905
+ }
1906
+ });
1907
+
1908
+ // src/browser/utils.js
1909
+ var require_utils = __commonJS({
1910
+ "src/browser/utils.js"(exports2, module2) {
1911
+ function getAddress(address) {
1912
+ if (!address) return address;
1913
+ const addr = String(address).trim();
1914
+ const normalized = addr.startsWith("0x") ? addr.slice(2).toLowerCase() : addr.toLowerCase();
1915
+ return `0x${normalized}`;
1916
+ }
1917
+ async function keccak256Async(data) {
1918
+ const encoder = new TextEncoder();
1919
+ const dataBuffer = typeof data === "string" ? encoder.encode(data) : data;
1920
+ const hashBuffer = await crypto.subtle.digest("SHA-256", dataBuffer);
1921
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
1922
+ const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
1923
+ return `0x${hashHex}`;
1924
+ }
1925
+ function keccak256Sync(data) {
1926
+ const str = typeof data === "string" ? data : JSON.stringify(data);
1927
+ let hash = 0;
1928
+ for (let i = 0; i < str.length; i++) {
1929
+ const char = str.charCodeAt(i);
1930
+ hash = (hash << 5) - hash + char;
1931
+ hash = hash & hash;
1932
+ }
1933
+ const hex = Math.abs(hash).toString(16).padStart(8, "0");
1934
+ return `0x${hex.repeat(8)}`.slice(0, 66);
1935
+ }
1936
+ module2.exports = {
1937
+ getAddress,
1938
+ keccak256Async,
1939
+ keccak256Sync
1940
+ };
1941
+ }
1942
+ });
1943
+
1944
+ // src/browser/subgraph.js
1945
+ var require_subgraph2 = __commonJS({
1946
+ "src/browser/subgraph.js"(exports2, module2) {
1947
+ var { getAddress } = require_utils();
1948
+ function sanitizeOrderBy(orderBy, allowed, fallback) {
1949
+ const value = (orderBy || "").toString();
1950
+ return allowed.includes(value) ? value : fallback;
1951
+ }
1952
+ function sanitizeOrderDirection(direction, fallback = "desc") {
1953
+ const v = (direction || "").toString().toLowerCase();
1954
+ return v === "asc" ? "asc" : "desc";
1955
+ }
1956
+ function safeGetAddress(value) {
1957
+ try {
1958
+ return getAddress(value);
1959
+ } catch {
1960
+ return null;
1961
+ }
1962
+ }
1963
+ function mapSafe(list, mapper) {
1964
+ const out = [];
1965
+ for (const item of list || []) {
1966
+ try {
1967
+ const v = mapper(item);
1968
+ if (v != null) out.push(v);
1969
+ } catch {
1970
+ }
1971
+ }
1972
+ return out;
1973
+ }
1974
+ async function query(url, document, variables) {
1975
+ if (!url) throw new Error("subgraph url required");
1976
+ const controller = new AbortController();
1977
+ const timeoutId = setTimeout(() => controller.abort(), 1e4);
1978
+ try {
1979
+ const resp = await fetch(url, {
1980
+ method: "POST",
1981
+ headers: { "Content-Type": "application/json" },
1982
+ body: JSON.stringify({ query: document, variables }),
1983
+ signal: controller.signal
1984
+ });
1985
+ clearTimeout(timeoutId);
1986
+ if (!resp.ok) {
1987
+ throw new Error(`HTTP ${resp.status}: ${resp.statusText}`);
1988
+ }
1989
+ const data = await resp.json();
1990
+ if (data && data.errors) {
1991
+ throw new Error(data.errors.map((e) => e.message).join("; "));
1992
+ }
1993
+ return data.data;
1994
+ } catch (error) {
1995
+ clearTimeout(timeoutId);
1996
+ throw error;
1997
+ }
1998
+ }
1999
+ async function listProposals({ url, governor, first = 20, skip = 0 }) {
2000
+ const data = await query(url, `
2001
+ query($governor: Bytes!, $first: Int!, $skip: Int!) {
2002
+ proposals(where: { governor: $governor }, first: $first, skip: $skip, orderBy: createdAt, orderDirection: desc) {
2003
+ id
2004
+ proposer
2005
+ description
2006
+ createdAt
2007
+ targets
2008
+ values
2009
+ calldatas
2010
+ }
2011
+ }
2012
+ `, { governor: String(governor).toLowerCase(), first, skip });
2013
+ return mapSafe(data?.proposals, (p) => {
2014
+ const proposer = safeGetAddress(p.proposer);
2015
+ if (!proposer) return null;
2016
+ const targets = (p.targets || []).map((t) => safeGetAddress(t)).filter(Boolean);
2017
+ if (!targets.length && (p.targets || []).length) return null;
2018
+ return {
2019
+ id: BigInt(p.id),
2020
+ proposer,
2021
+ description: p.description,
2022
+ createdAt: Number(p.createdAt || 0),
2023
+ targets,
2024
+ values: (p.values || []).map((value) => BigInt(String(value))),
2025
+ calldatas: p.calldatas || []
2026
+ };
2027
+ });
2028
+ }
2029
+ var STATE_STRING_TO_NUMBER = {
2030
+ PENDING: 0,
2031
+ ACTIVE: 1,
2032
+ CANCELED: 2,
2033
+ CANCELLED: 2,
2034
+ // some indexes may use British spelling
2035
+ DEFEATED: 3,
2036
+ SUCCEEDED: 4,
2037
+ QUEUED: 5,
2038
+ EXPIRED: 6,
2039
+ EXECUTED: 7
2040
+ };
2041
+ async function listProposalsFiltered({ url, governor, states, fromTimestamp, toTimestamp, first = 20, skip = 0, orderBy = "createdAt", orderDirection = "desc" }) {
2042
+ const govLower = governor ? String(governor).toLowerCase() : null;
2043
+ const statesUpper = Array.isArray(states) && states.length ? states.map((s) => String(s).toUpperCase()) : null;
2044
+ const safeOrderBy = sanitizeOrderBy(orderBy, ["createdAt", "updatedAt", "eta"], "createdAt");
2045
+ const safeOrderDirection = sanitizeOrderDirection(orderDirection, "desc");
2046
+ const doc = `
2047
+ query($first: Int!, $skip: Int!, $governor: Bytes, $states: [String!], $from: Int, $to: Int) {
2048
+ proposals(
2049
+ where: {
2050
+ ${governor ? "governor: $governor," : ""}
2051
+ ${statesUpper ? "state_in: $states," : ""}
2052
+ ${fromTimestamp !== void 0 ? "createdAt_gte: $from," : ""}
2053
+ ${toTimestamp !== void 0 ? "createdAt_lte: $to," : ""}
2054
+ }
2055
+ first: $first
2056
+ skip: $skip
2057
+ orderBy: ${safeOrderBy}
2058
+ orderDirection: ${safeOrderDirection}
2059
+ ) {
2060
+ id
2061
+ proposer
2062
+ description
2063
+ createdAt
2064
+ updatedAt
2065
+ state
2066
+ eta
2067
+ targets
2068
+ values
2069
+ calldatas
2070
+ }
2071
+ }
2072
+ `;
2073
+ const variables = { first, skip };
2074
+ if (govLower) variables.governor = govLower;
2075
+ if (statesUpper) variables.states = statesUpper;
2076
+ if (fromTimestamp !== void 0) variables.from = Number(fromTimestamp);
2077
+ if (toTimestamp !== void 0) variables.to = Number(toTimestamp);
2078
+ const data = await query(url, doc, variables);
2079
+ return mapSafe(data?.proposals, (p) => {
2080
+ const stateStr = String(p.state || "").toUpperCase();
2081
+ const stateNum = STATE_STRING_TO_NUMBER[stateStr] != null ? STATE_STRING_TO_NUMBER[stateStr] : null;
2082
+ const proposer = safeGetAddress(p.proposer);
2083
+ if (!proposer) return null;
2084
+ const targets = (p.targets || []).map((t) => safeGetAddress(t)).filter(Boolean);
2085
+ if (!targets.length && (p.targets || []).length) return null;
2086
+ return {
2087
+ id: BigInt(p.id),
2088
+ proposer,
2089
+ description: p.description || "",
2090
+ createdAt: Number(p.createdAt || 0),
2091
+ updatedAt: Number(p.updatedAt || 0),
2092
+ state: stateStr,
2093
+ stateNum,
2094
+ eta: p.eta ? BigInt(String(p.eta)) : null,
1767
2095
  targets,
1768
2096
  values: (p.values || []).map((value) => BigInt(String(value))),
1769
2097
  calldatas: p.calldatas || []
@@ -2198,6 +2526,7 @@ var require_ipfs = __commonJS({
2198
2526
  var FormData = __require("form-data");
2199
2527
  var { ethers: ethers2 } = __require("ethers");
2200
2528
  var DEFAULT_GATEWAY = "https://ipfs.dev.sageprotocol.io/ipfs";
2529
+ var DEFAULT_WORKER_BASE = "https://api.sageprotocol.io";
2201
2530
  function toLowerSafe(value, fallback = "") {
2202
2531
  if (value == null) return fallback;
2203
2532
  try {
@@ -2269,7 +2598,8 @@ var require_ipfs = __commonJS({
2269
2598
  const formDataFactory = options.formDataFactory || (() => new FormData());
2270
2599
  const generateCid = options.generateCid || generateDeterministicCid;
2271
2600
  const config = {
2272
- provider: toLowerSafe(options.provider ?? env.SAGE_IPFS_PROVIDER ?? "auto"),
2601
+ // Default to 'worker' to ensure uploads go through worker for indexing
2602
+ provider: toLowerSafe(options.provider ?? env.SAGE_IPFS_PROVIDER ?? "worker"),
2273
2603
  gateway: options.gateway || env.SAGE_IPFS_GATEWAY || DEFAULT_GATEWAY,
2274
2604
  timeoutMs: Number(options.timeoutMs ?? env.SAGE_IPFS_TIMEOUT_MS ?? 15e3),
2275
2605
  retries: Math.max(0, Number(options.retries ?? env.SAGE_IPFS_RETRIES ?? 2)),
@@ -2282,11 +2612,12 @@ var require_ipfs = __commonJS({
2282
2612
  jwt: options.pinataJwt || env.PINATA_JWT || null
2283
2613
  },
2284
2614
  worker: {
2285
- baseUrl: removeTrailingSlash(options.workerBaseUrl || env.SAGE_IPFS_WORKER_URL || ""),
2615
+ baseUrl: removeTrailingSlash(options.workerBaseUrl || env.SAGE_IPFS_WORKER_URL || DEFAULT_WORKER_BASE),
2286
2616
  uploadUrl: removeTrailingSlash(options.workerUploadUrl || env.SAGE_IPFS_UPLOAD_URL || ""),
2287
2617
  token: options.workerUploadToken || env.SAGE_IPFS_UPLOAD_TOKEN || "",
2288
- challengePath: options.workerChallengePath || env.SAGE_IPFS_WORKER_CHALLENGE_PATH || "/auth/challenge",
2618
+ challengePath: options.workerChallengePath || env.SAGE_IPFS_WORKER_CHALLENGE_PATH || "/ipfs/auth/challenge",
2289
2619
  uploadPath: options.workerUploadPath || env.SAGE_IPFS_WORKER_UPLOAD_PATH || "/ipfs/upload",
2620
+ uploadRawPath: options.workerUploadRawPath || env.SAGE_IPFS_WORKER_UPLOAD_RAW_PATH || "/ipfs/upload-raw",
2290
2621
  pinPath: options.workerPinPath || env.SAGE_IPFS_WORKER_PIN_PATH || "/ipfs/pin",
2291
2622
  warmPath: options.workerWarmPath || env.SAGE_IPFS_WORKER_WARM_PATH || "/ipfs/warm",
2292
2623
  pinUrl: removeTrailingSlash(options.workerPinUrl || env.SAGE_IPFS_WORKER_PIN_URL || ""),
@@ -2294,11 +2625,15 @@ var require_ipfs = __commonJS({
2294
2625
  signer: options.workerSigner || options.signer || null,
2295
2626
  getAuth: options.workerGetAuth || null,
2296
2627
  address: options.workerAddress || env.SAGE_SANDBOX_ADDRESS || "",
2297
- discoveryEventsPath: options.workerDiscoveryEventsPath || env.SAGE_IPFS_WORKER_DISCOVERY_EVENTS_PATH || "/discover/events",
2298
- discoveryMcpPath: options.workerDiscoveryMcpPath || env.SAGE_IPFS_WORKER_DISCOVERY_MCP_PATH || "/discover/mcp",
2299
- discoveryLaunchPath: options.workerDiscoveryLaunchPath || env.SAGE_IPFS_WORKER_DISCOVERY_LAUNCH_PATH || "/discover/launch",
2628
+ // All event types route to /trends/usage - worker tracks usage for trend scoring
2629
+ discoveryEventsPath: options.workerDiscoveryEventsPath || env.SAGE_IPFS_WORKER_DISCOVERY_EVENTS_PATH || "/trends/usage",
2630
+ discoveryMcpPath: options.workerDiscoveryMcpPath || env.SAGE_IPFS_WORKER_DISCOVERY_MCP_PATH || "/trends/usage",
2631
+ discoveryLaunchPath: options.workerDiscoveryLaunchPath || env.SAGE_IPFS_WORKER_DISCOVERY_LAUNCH_PATH || "/trends/usage",
2632
+ // Install events track skill installations separately for trend scoring
2633
+ discoveryInstallPath: options.workerDiscoveryInstallPath || env.SAGE_IPFS_WORKER_DISCOVERY_INSTALL_PATH || "/trends/install",
2300
2634
  discoveryFeedPath: options.workerDiscoveryFeedPath || env.SAGE_IPFS_WORKER_DISCOVERY_FEED_PATH || "/discover",
2301
- discoveryMetricsPath: options.workerDiscoveryMetricsPath || env.SAGE_IPFS_WORKER_DISCOVERY_METRICS_PATH || "/discover/metrics",
2635
+ // Metrics uses /discover/stats which returns index statistics
2636
+ discoveryMetricsPath: options.workerDiscoveryMetricsPath || env.SAGE_IPFS_WORKER_DISCOVERY_METRICS_PATH || "/discover/stats",
2302
2637
  governanceReportPath: options.workerGovernanceReportPath || env.SAGE_IPFS_WORKER_GOVERNANCE_REPORT_PATH || "/governance/report",
2303
2638
  governanceReportsPath: options.workerGovernanceReportsPath || env.SAGE_IPFS_WORKER_GOVERNANCE_REPORTS_PATH || "/governance/reports",
2304
2639
  governanceReviewPath: options.workerGovernanceReviewPath || env.SAGE_IPFS_WORKER_GOVERNANCE_REVIEW_PATH || "/governance/report/review",
@@ -2332,8 +2667,9 @@ var require_ipfs = __commonJS({
2332
2667
  const base = workerBaseUrl();
2333
2668
  const ensure = (path2) => path2.startsWith("/") ? path2 : `/${path2}`;
2334
2669
  if (base) {
2335
- if (kind === "challenge") return `${base}${ensure(config.worker.challengePath || "/auth/challenge")}`;
2670
+ if (kind === "challenge") return `${base}${ensure(config.worker.challengePath || "/ipfs/auth/challenge")}`;
2336
2671
  if (kind === "upload") return `${base}${ensure(config.worker.uploadPath || "/ipfs/upload")}`;
2672
+ if (kind === "upload-raw") return `${base}${ensure(config.worker.uploadRawPath || "/ipfs/upload-raw")}`;
2337
2673
  if (kind === "pin") return `${base}${ensure(config.worker.pinPath || "/ipfs/pin")}`;
2338
2674
  if (kind === "warm") return `${base}${ensure(config.worker.warmPath || "/ipfs/warm")}`;
2339
2675
  if (kind === "status") return `${base}${ensure(config.worker.pinPath || "/ipfs/pin")}/${cid}`;
@@ -2368,6 +2704,26 @@ var require_ipfs = __commonJS({
2368
2704
  const response = await axiosInstance.get(url, { headers, params, timeout: config.timeoutMs });
2369
2705
  return response?.data;
2370
2706
  }
2707
+ async function withRetry(fn, { retries = 2, baseMs = 500 } = {}) {
2708
+ let attempt = 0;
2709
+ let lastErr;
2710
+ while (attempt <= retries) {
2711
+ try {
2712
+ return await fn();
2713
+ } catch (e) {
2714
+ lastErr = e;
2715
+ const status = e?.response?.status;
2716
+ if (status && status >= 400 && status < 500 && status !== 429) {
2717
+ throw e;
2718
+ }
2719
+ if (attempt === retries) break;
2720
+ const delay = baseMs * Math.pow(2, attempt);
2721
+ await new Promise((r) => setTimeout(r, delay));
2722
+ attempt++;
2723
+ }
2724
+ }
2725
+ throw lastErr;
2726
+ }
2371
2727
  async function buildWorkerAuthHeaders(extraHeaders = {}) {
2372
2728
  if (config.worker.token) {
2373
2729
  return { headers: { ...extraHeaders, Authorization: `Bearer ${config.worker.token}` }, auth: null };
@@ -2410,9 +2766,7 @@ var require_ipfs = __commonJS({
2410
2766
  const hasEndpoint = workerBaseUrl() || config.worker.uploadUrl;
2411
2767
  if (!hasEndpoint) return false;
2412
2768
  if (!workerHasAuth()) return false;
2413
- if (config.provider === "worker") return true;
2414
- if (config.provider === "auto") return true;
2415
- return false;
2769
+ return true;
2416
2770
  }
2417
2771
  function resolveProviderOrder(preference) {
2418
2772
  const order = [];
@@ -2428,8 +2782,8 @@ var require_ipfs = __commonJS({
2428
2782
  console.log("[SDK DEBUG] shouldUseWorker():", shouldUseWorker());
2429
2783
  console.log("[SDK DEBUG] hasPinataCreds():", hasPinataCreds());
2430
2784
  }
2431
- normalizedPreference.forEach(append);
2432
2785
  if (shouldUseWorker()) append("worker");
2786
+ normalizedPreference.forEach(append);
2433
2787
  if (hasPinataCreds()) append("pinata");
2434
2788
  if (config.web3.token) append("web3");
2435
2789
  if (config.simulate) append("simulate");
@@ -2520,6 +2874,107 @@ var require_ipfs = __commonJS({
2520
2874
  }
2521
2875
  return result;
2522
2876
  }
2877
+ async function uploadRawViaWorker(content, { name, warm, gateways } = {}) {
2878
+ const uploadUrl = workerUrl("upload-raw");
2879
+ if (process.env.SAGE_DEBUG_WORKER === "1") {
2880
+ console.log("[SDK DEBUG] uploadRawViaWorker called");
2881
+ console.log("[SDK DEBUG] uploadUrl:", uploadUrl);
2882
+ }
2883
+ const { headers, auth } = await buildWorkerAuthHeaders({ "Content-Type": "application/json" });
2884
+ const body = { content };
2885
+ if (auth) body.auth = auth;
2886
+ if (name) body.name = name;
2887
+ if (process.env.SAGE_DEBUG_WORKER === "1") {
2888
+ console.log("[SDK DEBUG] headers:", Object.keys(headers));
2889
+ console.log("[SDK DEBUG] hasAuth:", !!auth);
2890
+ }
2891
+ let response;
2892
+ try {
2893
+ response = await axiosInstance.post(uploadUrl, body, { headers, timeout: config.timeoutMs });
2894
+ if (process.env.SAGE_DEBUG_WORKER === "1") {
2895
+ console.log("[SDK DEBUG] Upload response status:", response?.status);
2896
+ console.log("[SDK DEBUG] Upload response data:", JSON.stringify(response?.data || {}));
2897
+ }
2898
+ } catch (uploadErr) {
2899
+ if (process.env.SAGE_DEBUG_WORKER === "1") {
2900
+ console.log("[SDK DEBUG] Upload error:", uploadErr.message);
2901
+ console.log("[SDK DEBUG] Response status:", uploadErr.response?.status);
2902
+ console.log("[SDK DEBUG] Response data:", JSON.stringify(uploadErr.response?.data || {}));
2903
+ }
2904
+ const status = uploadErr.response?.status;
2905
+ const data2 = uploadErr.response?.data;
2906
+ if (status === 400 && data2?.error === "content_blocked") {
2907
+ const error = new Error("content_blocked");
2908
+ error.code = "CONTENT_BLOCKED";
2909
+ error.categories = data2.categories || [];
2910
+ error.reason = data2.reason;
2911
+ error.response = data2;
2912
+ throw error;
2913
+ }
2914
+ if (status === 402) {
2915
+ const error = new Error("payment_required");
2916
+ error.code = "PAYMENT_REQUIRED";
2917
+ error.creditsNeeded = data2?.creditsNeeded;
2918
+ error.balance = data2?.balance;
2919
+ error.accepts = data2?.accepts;
2920
+ error.response = data2;
2921
+ throw error;
2922
+ }
2923
+ throw uploadErr;
2924
+ }
2925
+ const data = response?.data || {};
2926
+ const cid = data.cid || data.IpfsHash;
2927
+ if (!cid) {
2928
+ throw new Error("Worker response missing cid");
2929
+ }
2930
+ if (warm || config.shouldWarm) await warmGateways(cid, { gateways });
2931
+ return {
2932
+ cid,
2933
+ provider: "worker",
2934
+ response: data
2935
+ };
2936
+ }
2937
+ async function uploadRawJson(content, options2 = {}) {
2938
+ if (!content || typeof content !== "object") {
2939
+ throw new Error("content object required");
2940
+ }
2941
+ const { providers, provider, warm, gateways, name = "manifest.json" } = options2;
2942
+ const order = resolveProviderOrder(providers || provider);
2943
+ if (!order.length) {
2944
+ throw new Error("No IPFS providers configured");
2945
+ }
2946
+ const errors = [];
2947
+ for (const candidate of order) {
2948
+ try {
2949
+ if (candidate === "worker") {
2950
+ return await uploadRawViaWorker(content, { name, warm, gateways });
2951
+ }
2952
+ if (candidate === "pinata") {
2953
+ if (process.env.SAGE_DEBUG_WORKER === "1") {
2954
+ console.log("[SDK WARN] uploadRawJson falling back to pinata - content will be wrapped");
2955
+ }
2956
+ return await uploadToPinata(content, { filename: name, warm, gateways });
2957
+ }
2958
+ if (candidate === "web3") {
2959
+ if (process.env.SAGE_DEBUG_WORKER === "1") {
2960
+ console.log("[SDK WARN] uploadRawJson falling back to web3 - content will be wrapped");
2961
+ }
2962
+ return await uploadToWeb3Storage(content, { warm, gateways });
2963
+ }
2964
+ if (candidate === "simulate") {
2965
+ return await uploadSimulated(content, { warm, gateways });
2966
+ }
2967
+ } catch (error2) {
2968
+ if (error2.code === "CONTENT_BLOCKED" || error2.code === "PAYMENT_REQUIRED") {
2969
+ throw error2;
2970
+ }
2971
+ errors.push({ provider: candidate, error: error2 });
2972
+ }
2973
+ }
2974
+ const error = new Error("All IPFS providers failed");
2975
+ error.details = errors;
2976
+ throw error;
2977
+ }
2523
2978
  async function uploadToPinata(payload, { filename = "payload.json", metadata, warm, gateways } = {}) {
2524
2979
  if (!hasPinataCreds()) {
2525
2980
  throw new Error("Missing Pinata credentials");
@@ -2748,28 +3203,37 @@ var require_ipfs = __commonJS({
2748
3203
  }
2749
3204
  async function recordDiscoveryEvent(event = {}) {
2750
3205
  if (!event || typeof event !== "object") throw new Error("event required");
2751
- const promptId = typeof event.promptId === "string" ? event.promptId.trim() : "";
2752
- if (!promptId) throw new Error("promptId required");
2753
- const payload = { ...event, promptId };
2754
- return postWorkerJson("discoveryEventsPath", "/discover/events", payload);
3206
+ const cid = typeof event.promptId === "string" ? event.promptId.trim() : typeof event.cid === "string" ? event.cid.trim() : "";
3207
+ if (!cid) throw new Error("promptId or cid required");
3208
+ const payload = { ...event, cid };
3209
+ return withRetry(() => postWorkerJson("discoveryEventsPath", "/trends/usage", payload));
3210
+ }
3211
+ async function recordMcpUsageEvent({ promptId, cid, metadata, address } = {}) {
3212
+ const id2 = typeof promptId === "string" ? promptId.trim() : typeof cid === "string" ? cid.trim() : "";
3213
+ if (!id2) throw new Error("promptId or cid required");
3214
+ const payload = { cid: id2 };
3215
+ if (metadata && typeof metadata === "object") payload.metadata = metadata;
3216
+ const actor = address || config.worker.address;
3217
+ if (actor) payload.address = actor;
3218
+ return withRetry(() => postWorkerJson("discoveryMcpPath", "/trends/usage", payload));
2755
3219
  }
2756
- async function recordMcpUsageEvent({ promptId, metadata, address } = {}) {
2757
- const id2 = typeof promptId === "string" ? promptId.trim() : "";
2758
- if (!id2) throw new Error("promptId required");
2759
- const payload = { promptId: id2 };
3220
+ async function recordLaunchEvent({ promptId, cid, metadata, address } = {}) {
3221
+ const id2 = typeof promptId === "string" ? promptId.trim() : typeof cid === "string" ? cid.trim() : "";
3222
+ if (!id2) throw new Error("promptId or cid required");
3223
+ const payload = { cid: id2 };
2760
3224
  if (metadata && typeof metadata === "object") payload.metadata = metadata;
2761
3225
  const actor = address || config.worker.address;
2762
3226
  if (actor) payload.address = actor;
2763
- return postWorkerJson("discoveryMcpPath", "/discover/mcp", payload);
3227
+ return withRetry(() => postWorkerJson("discoveryLaunchPath", "/trends/usage", payload));
2764
3228
  }
2765
- async function recordLaunchEvent({ promptId, metadata, address } = {}) {
2766
- const id2 = typeof promptId === "string" ? promptId.trim() : "";
2767
- if (!id2) throw new Error("promptId required");
2768
- const payload = { promptId: id2 };
3229
+ async function recordInstallEvent({ skillCid, cid, metadata, address } = {}) {
3230
+ const id2 = typeof skillCid === "string" ? skillCid.trim() : typeof cid === "string" ? cid.trim() : "";
3231
+ if (!id2) throw new Error("skillCid or cid required");
3232
+ const payload = { cid: id2 };
2769
3233
  if (metadata && typeof metadata === "object") payload.metadata = metadata;
2770
3234
  const actor = address || config.worker.address;
2771
3235
  if (actor) payload.address = actor;
2772
- return postWorkerJson("discoveryLaunchPath", "/discover/launch", payload);
3236
+ return withRetry(() => postWorkerJson("discoveryInstallPath", "/trends/install", payload));
2773
3237
  }
2774
3238
  async function submitGovernanceReport(payload = {}) {
2775
3239
  if (!payload || typeof payload !== "object") throw new Error("payload required");
@@ -2831,6 +3295,7 @@ var require_ipfs = __commonJS({
2831
3295
  uploadPrompt,
2832
3296
  uploadJson,
2833
3297
  uploadText,
3298
+ uploadRawJson,
2834
3299
  downloadJson,
2835
3300
  warmGateways,
2836
3301
  buildGatewayUrls: (cid, extra) => buildGatewayUrls(cid, config.gateway, extra),
@@ -2838,6 +3303,7 @@ var require_ipfs = __commonJS({
2838
3303
  recordDiscoveryEvent,
2839
3304
  recordMcpUsage: recordMcpUsageEvent,
2840
3305
  recordLaunchEvent,
3306
+ recordInstallEvent,
2841
3307
  submitGovernanceReport,
2842
3308
  listGovernanceReports,
2843
3309
  reviewGovernanceReport,
@@ -2857,7 +3323,7 @@ var require_ipfs = __commonJS({
2857
3323
  const timeoutMs = Number(options.timeoutMs ?? env.SAGE_IPFS_TIMEOUT_MS ?? 15e3);
2858
3324
  const challengePath = ensureLeadingSlash(
2859
3325
  options.workerChallengePath || env.SAGE_IPFS_WORKER_CHALLENGE_PATH,
2860
- "/auth/challenge"
3326
+ "/ipfs/auth/challenge"
2861
3327
  );
2862
3328
  const warmPath = ensureLeadingSlash(
2863
3329
  options.workerWarmPath || env.SAGE_IPFS_WORKER_WARM_PATH,
@@ -3389,7 +3855,10 @@ var require_library = __commonJS({
3389
3855
  proposer: getAddress(info.lastUpdater),
3390
3856
  promptCount: 0,
3391
3857
  // Not tracked on-chain in V4
3392
- version: info.version
3858
+ version: info.version,
3859
+ nonce: BigInt(info.nonce.toString()),
3860
+ forkedFromDAO: info.forkedFromDAO && info.forkedFromDAO !== "0x0000000000000000000000000000000000000000" ? getAddress(info.forkedFromDAO) : null,
3861
+ sxxxForkFee: BigInt(info.sxxxForkFee.toString())
3393
3862
  };
3394
3863
  }
3395
3864
  async function getManifestInfo({ provider, registry, manifestCID }) {
@@ -3425,6 +3894,21 @@ var require_library = __commonJS({
3425
3894
  ]);
3426
3895
  return { to, data, value: 0n };
3427
3896
  }
3897
+ function buildUpdateLibraryCAS({ registry, subdao, manifestCID, version = "1.0.0", expectedNonce }) {
3898
+ const to = normalise(registry, "registry");
3899
+ if (!manifestCID) throw new SageSDKError(CODES.INVALID_ARGS, "manifestCID required");
3900
+ if (expectedNonce === void 0 || expectedNonce === null) {
3901
+ throw new SageSDKError(CODES.INVALID_ARGS, "expectedNonce required for CAS update");
3902
+ }
3903
+ const iface = new Interface(["function updateLibraryCAS(address,string,string,uint256)"]);
3904
+ const data = iface.encodeFunctionData("updateLibraryCAS", [
3905
+ normalise(subdao, "subdao"),
3906
+ String(manifestCID),
3907
+ String(version),
3908
+ BigInt(expectedNonce)
3909
+ ]);
3910
+ return { to, data, value: 0n };
3911
+ }
3428
3912
  module2.exports = {
3429
3913
  listManifests,
3430
3914
  getManifestInfo,
@@ -3432,6 +3916,7 @@ var require_library = __commonJS({
3432
3916
  getLatestLibrary,
3433
3917
  hasScopedOwnership,
3434
3918
  buildUpdateLibraryTx,
3919
+ buildUpdateLibraryCAS,
3435
3920
  _computeLibraryKey,
3436
3921
  getBeforeAfterForUpdate,
3437
3922
  /** Build an authorizeTimelock(timelock, subdao) call for a LibraryRegistry. */
@@ -3450,73 +3935,305 @@ var require_library = __commonJS({
3450
3935
  const tgt = to ? normalise(to, "to") : reg;
3451
3936
  const from = normalise(timelock, "timelock");
3452
3937
  try {
3453
- await provider.call({ to: tgt, data, from });
3454
- return { ok: true };
3455
- } catch (err) {
3456
- return { ok: false, error: { type: "Revert", message: String(err && err.message || err) } };
3938
+ await provider.call({ to: tgt, data, from });
3939
+ return { ok: true };
3940
+ } catch (err) {
3941
+ return { ok: false, error: { type: "Revert", message: String(err && err.message || err) } };
3942
+ }
3943
+ },
3944
+ executionReadiness: async function executionReadiness({ provider, registry, timelock, subdao, manifestCID = "", version = "1.0.0" }) {
3945
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
3946
+ const payload = buildUpdateLibraryTx({ registry, subdao, manifestCID, version });
3947
+ const sim = await this.simulateAsTimelock({ provider, registry, to: payload.to, data: payload.data, timelock });
3948
+ const out = { ok: !!sim.ok, error: sim.ok ? null : sim.error?.message || "revert", missingRole: null };
3949
+ return out;
3950
+ }
3951
+ };
3952
+ }
3953
+ });
3954
+
3955
+ // src/governance/index.js
3956
+ var require_governance = __commonJS({
3957
+ "src/governance/index.js"(exports2, module2) {
3958
+ var { Contract, Interface, AbiCoder, ZeroAddress, getAddress, hexlify, keccak256, toUtf8Bytes } = __require("ethers");
3959
+ var ABI = require_abi();
3960
+ var { BigIntZero } = require_types();
3961
+ var { SageSDKError, CODES } = require_errors();
3962
+ var { makeProposalDescription, stripProposalSalt } = require_description();
3963
+ var subgraph = require_subgraph();
3964
+ var adapters = require_transports();
3965
+ var subdao = require_subdao();
3966
+ function normaliseGovernor(governor) {
3967
+ if (!governor) throw new SageSDKError(CODES.INVALID_ARGS, "governor required");
3968
+ try {
3969
+ return getAddress(governor);
3970
+ } catch (err) {
3971
+ throw new SageSDKError(CODES.INVALID_ARGS, "invalid governor address", { cause: err });
3972
+ }
3973
+ }
3974
+ async function getGovernorInfo({ provider, governor }) {
3975
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
3976
+ const addr = normaliseGovernor(governor);
3977
+ const g = new Contract(addr, ABI.Governor, provider);
3978
+ const results = await Promise.all([
3979
+ g.name().catch(() => null),
3980
+ g.votingDelay().catch(() => null),
3981
+ g.votingPeriod().catch(() => null),
3982
+ g.proposalThreshold().catch(() => null),
3983
+ g.timelock().catch(async () => {
3984
+ try {
3985
+ return await g._executor();
3986
+ } catch {
3987
+ return null;
3988
+ }
3989
+ }),
3990
+ g.sxxxToken().catch(() => null),
3991
+ // Try quorumVotes first (new fixed model), fall back to quorumNumerator (legacy OZ)
3992
+ g.quorumVotes ? g.quorumVotes().catch(() => null) : null,
3993
+ g.quorumNumerator ? g.quorumNumerator().catch(() => null) : null
3994
+ ]);
3995
+ const [name, votingDelay, votingPeriod, proposalThreshold, timelockRaw, sxxxToken, quorumVotes, quorumNumerator] = results;
3996
+ const timelock = timelockRaw ? getAddress(timelockRaw) : null;
3997
+ return {
3998
+ address: addr,
3999
+ name,
4000
+ votingDelay,
4001
+ votingPeriod,
4002
+ proposalThreshold,
4003
+ timelock,
4004
+ sxxxToken: sxxxToken && /^0x/.test(String(sxxxToken)) ? getAddress(sxxxToken) : null,
4005
+ // Primary: quorumVotes (fixed absolute amount)
4006
+ quorumVotes,
4007
+ // Legacy alias (deprecated)
4008
+ quorumNumerator: quorumNumerator ?? quorumVotes
4009
+ };
4010
+ }
4011
+ async function getGovernorTimepointMinusOne({ provider, governor }) {
4012
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
4013
+ const govAddr = normaliseGovernor(governor);
4014
+ try {
4015
+ const iface = new Interface(["function clock() view returns (uint48)"]);
4016
+ const data = iface.encodeFunctionData("clock", []);
4017
+ const ret = await provider.call({ to: govAddr, data });
4018
+ if (ret && ret !== "0x") {
4019
+ const [clk] = AbiCoder.defaultAbiCoder().decode(["uint48"], ret);
4020
+ const now = BigInt(clk.toString());
4021
+ return now > 0n ? now - 1n : 0n;
4022
+ }
4023
+ } catch (_) {
4024
+ }
4025
+ try {
4026
+ const latest = await provider.getBlockNumber();
4027
+ const now = BigInt(latest);
4028
+ return now > 0n ? now - 1n : 0n;
4029
+ } catch (_) {
4030
+ }
4031
+ return null;
4032
+ }
4033
+ async function getGovernorVotesAt({ provider, governor, account, timepoint }) {
4034
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
4035
+ if (!account) throw new SageSDKError(CODES.INVALID_ARGS, "account required");
4036
+ if (timepoint == null) throw new SageSDKError(CODES.INVALID_ARGS, "timepoint required");
4037
+ const govAddr = normaliseGovernor(governor);
4038
+ const acct = getAddress(account);
4039
+ const tp = typeof timepoint === "bigint" ? timepoint : BigInt(String(timepoint));
4040
+ try {
4041
+ const iface = new Interface(["function getVotes(address,uint256) view returns (uint256)"]);
4042
+ const data = iface.encodeFunctionData("getVotes", [acct, tp]);
4043
+ const ret = await provider.call({ to: govAddr, data });
4044
+ if (ret && ret !== "0x") {
4045
+ const [val] = AbiCoder.defaultAbiCoder().decode(["uint256"], ret);
4046
+ return BigInt(val.toString());
4047
+ }
4048
+ } catch (_) {
4049
+ }
4050
+ try {
4051
+ const { votingToken } = await resolveVotesTokenChain({ provider, governor: govAddr });
4052
+ const tokenAddr = getAddress(votingToken);
4053
+ const iface = new Interface(["function getPastVotes(address,uint256) view returns (uint256)"]);
4054
+ const data = iface.encodeFunctionData("getPastVotes", [acct, tp]);
4055
+ const ret = await provider.call({ to: tokenAddr, data });
4056
+ if (ret && ret !== "0x") {
4057
+ const [val] = AbiCoder.defaultAbiCoder().decode(["uint256"], ret);
4058
+ return BigInt(val.toString());
4059
+ }
4060
+ } catch (_) {
4061
+ }
4062
+ return null;
4063
+ }
4064
+ async function getProposalThresholdStatus({ provider, governor, proposer }) {
4065
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
4066
+ if (!proposer) throw new SageSDKError(CODES.INVALID_ARGS, "proposer required");
4067
+ const govAddr = normaliseGovernor(governor);
4068
+ const user = getAddress(proposer);
4069
+ const g = new Contract(govAddr, ABI.Governor, provider);
4070
+ const [threshold, sxxxTokenAddr] = await Promise.all([
4071
+ g.proposalThreshold().catch(() => null),
4072
+ g.sxxxToken().catch(() => null)
4073
+ ]);
4074
+ const proposalThreshold = threshold != null ? BigInt(threshold.toString()) : null;
4075
+ const sxxxToken = sxxxTokenAddr && /^0x[0-9a-fA-F]{40}$/.test(String(sxxxTokenAddr)) ? getAddress(sxxxTokenAddr) : null;
4076
+ const votesTimepoint = await getGovernorTimepointMinusOne({ provider, governor: govAddr });
4077
+ const proposerVotes = votesTimepoint != null ? await getGovernorVotesAt({ provider, governor: govAddr, account: user, timepoint: votesTimepoint }) : null;
4078
+ let sxxxBalance = null;
4079
+ if (sxxxToken) {
4080
+ try {
4081
+ const iface = new Interface(["function balanceOf(address) view returns (uint256)"]);
4082
+ const data = iface.encodeFunctionData("balanceOf", [user]);
4083
+ const ret = await provider.call({ to: sxxxToken, data });
4084
+ if (ret && ret !== "0x") {
4085
+ const [bal] = AbiCoder.defaultAbiCoder().decode(["uint256"], ret);
4086
+ sxxxBalance = BigInt(bal.toString());
4087
+ }
4088
+ } catch (_) {
4089
+ }
4090
+ }
4091
+ const thresholdMet = proposalThreshold != null && proposerVotes != null ? proposerVotes >= proposalThreshold : null;
4092
+ let cooldownRemaining = null;
4093
+ let canPropose = null;
4094
+ try {
4095
+ const iface = new Interface(["function getProposalCooldownInfo(address) view returns (bool canPropose, uint256 cooldownRemaining)"]);
4096
+ const data = iface.encodeFunctionData("getProposalCooldownInfo", [user]);
4097
+ const ret = await provider.call({ to: govAddr, data });
4098
+ if (ret && ret !== "0x") {
4099
+ const decoded = AbiCoder.defaultAbiCoder().decode(["bool", "uint256"], ret);
4100
+ canPropose = decoded[0];
4101
+ cooldownRemaining = BigInt(decoded[1].toString());
4102
+ }
4103
+ } catch (_) {
4104
+ try {
4105
+ const cooldownIface = new Interface(["function proposalCooldown() view returns (uint256)"]);
4106
+ const lastIface = new Interface(["function lastProposalTimestamp(address) view returns (uint256)"]);
4107
+ const [cooldownRet, lastRet] = await Promise.all([
4108
+ provider.call({ to: govAddr, data: cooldownIface.encodeFunctionData("proposalCooldown", []) }).catch(() => null),
4109
+ provider.call({ to: govAddr, data: lastIface.encodeFunctionData("lastProposalTimestamp", [user]) }).catch(() => null)
4110
+ ]);
4111
+ if (cooldownRet && cooldownRet !== "0x" && lastRet && lastRet !== "0x") {
4112
+ const [cooldown] = AbiCoder.defaultAbiCoder().decode(["uint256"], cooldownRet);
4113
+ const [last] = AbiCoder.defaultAbiCoder().decode(["uint256"], lastRet);
4114
+ const block = await provider.getBlock("latest").catch(() => null);
4115
+ if (block && block.timestamp != null) {
4116
+ const now = BigInt(block.timestamp);
4117
+ const endsAt = BigInt(last.toString()) + BigInt(cooldown.toString());
4118
+ cooldownRemaining = endsAt > now ? endsAt - now : 0n;
4119
+ canPropose = cooldownRemaining === 0n;
4120
+ }
4121
+ }
4122
+ } catch (_2) {
4123
+ }
4124
+ }
4125
+ const canProposeOverall = thresholdMet === true && (canPropose === true || canPropose === null);
4126
+ return {
4127
+ governor: govAddr,
4128
+ proposer: user,
4129
+ proposalThreshold,
4130
+ sxxxToken,
4131
+ sxxxBalance,
4132
+ proposerVotes,
4133
+ votesTimepoint,
4134
+ thresholdMet,
4135
+ cooldownRemaining,
4136
+ cooldownElapsed: cooldownRemaining != null ? cooldownRemaining === 0n : null,
4137
+ canPropose: canProposeOverall
4138
+ };
4139
+ }
4140
+ async function getProposalCancelability({ provider, governor, proposalId }) {
4141
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
4142
+ if (proposalId == null) throw new SageSDKError(CODES.INVALID_ARGS, "proposalId required");
4143
+ const govAddr = normaliseGovernor(governor);
4144
+ const pid = typeof proposalId === "bigint" ? proposalId : String(proposalId).startsWith("0x") ? BigInt(proposalId) : BigInt(String(proposalId));
4145
+ let proposalProposer = null;
4146
+ try {
4147
+ const iface = new Interface(["function proposalProposer(uint256) view returns (address)"]);
4148
+ const data = iface.encodeFunctionData("proposalProposer", [pid]);
4149
+ const ret = await provider.call({ to: govAddr, data });
4150
+ if (ret && ret !== "0x") {
4151
+ const [addr] = AbiCoder.defaultAbiCoder().decode(["address"], ret);
4152
+ if (addr && /^0x[0-9a-fA-F]{40}$/.test(String(addr)) && addr !== ZeroAddress) {
4153
+ proposalProposer = getAddress(addr);
4154
+ }
4155
+ }
4156
+ } catch (_) {
4157
+ }
4158
+ let proposalCreator = null;
4159
+ if (proposalProposer) {
4160
+ proposalCreator = proposalProposer;
4161
+ } else {
4162
+ try {
4163
+ const iface = new Interface(["function proposalCreator(uint256) view returns (address)"]);
4164
+ const data = iface.encodeFunctionData("proposalCreator", [pid]);
4165
+ const ret = await provider.call({ to: govAddr, data });
4166
+ if (ret && ret !== "0x") {
4167
+ const [addr] = AbiCoder.defaultAbiCoder().decode(["address"], ret);
4168
+ if (addr && /^0x[0-9a-fA-F]{40}$/.test(String(addr)) && addr !== ZeroAddress) {
4169
+ proposalCreator = getAddress(addr);
4170
+ proposalProposer = proposalCreator;
4171
+ }
4172
+ }
4173
+ } catch (_) {
3457
4174
  }
3458
- },
3459
- executionReadiness: async function executionReadiness({ provider, registry, timelock, subdao, manifestCID = "", version = "1.0.0" }) {
3460
- if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
3461
- const payload = buildUpdateLibraryTx({ registry, subdao, manifestCID, version });
3462
- const sim = await this.simulateAsTimelock({ provider, registry, to: payload.to, data: payload.data, timelock });
3463
- const out = { ok: !!sim.ok, error: sim.ok ? null : sim.error?.message || "revert", missingRole: null };
3464
- return out;
3465
4175
  }
3466
- };
3467
- }
3468
- });
3469
-
3470
- // src/governance/index.js
3471
- var require_governance = __commonJS({
3472
- "src/governance/index.js"(exports2, module2) {
3473
- var { Contract, Interface, AbiCoder, getAddress, hexlify, keccak256, toUtf8Bytes } = __require("ethers");
3474
- var ABI = require_abi();
3475
- var { BigIntZero } = require_types();
3476
- var { SageSDKError, CODES } = require_errors();
3477
- var { makeProposalDescription, stripProposalSalt } = require_description();
3478
- var subgraph = require_subgraph();
3479
- var adapters = require_transports();
3480
- function normaliseGovernor(governor) {
3481
- if (!governor) throw new SageSDKError(CODES.INVALID_ARGS, "governor required");
4176
+ let proposalThresholdSnapshot = null;
3482
4177
  try {
3483
- return getAddress(governor);
3484
- } catch (err) {
3485
- throw new SageSDKError(CODES.INVALID_ARGS, "invalid governor address", { cause: err });
4178
+ const iface = new Interface(["function proposalThresholdSnapshot(uint256) view returns (uint256)"]);
4179
+ const data = iface.encodeFunctionData("proposalThresholdSnapshot", [pid]);
4180
+ const ret = await provider.call({ to: govAddr, data });
4181
+ if (ret && ret !== "0x") {
4182
+ const [val] = AbiCoder.defaultAbiCoder().decode(["uint256"], ret);
4183
+ proposalThresholdSnapshot = BigInt(val.toString());
4184
+ }
4185
+ } catch (_) {
3486
4186
  }
3487
- }
3488
- async function getGovernorInfo({ provider, governor }) {
3489
- if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
3490
- const addr = normaliseGovernor(governor);
3491
- const g = new Contract(addr, ABI.Governor, provider);
3492
- const results = await Promise.all([
3493
- g.name().catch(() => null),
3494
- g.votingDelay().catch(() => null),
3495
- g.votingPeriod().catch(() => null),
4187
+ const g = new Contract(govAddr, ABI.Governor, provider);
4188
+ const [state, threshold, sxxxTokenAddr] = await Promise.all([
4189
+ g.state(pid).catch(() => null),
3496
4190
  g.proposalThreshold().catch(() => null),
3497
- g.timelock().catch(async () => {
3498
- try {
3499
- return await g._executor();
3500
- } catch {
3501
- return null;
3502
- }
3503
- }),
3504
- g.sxxxToken().catch(() => null),
3505
- g.quorumNumerator ? g.quorumNumerator().catch(() => null) : null,
3506
- g.stakeAmount ? g.stakeAmount().catch(() => null) : null
4191
+ g.sxxxToken().catch(() => null)
3507
4192
  ]);
3508
- const [name, votingDelay, votingPeriod, proposalThreshold, timelockRaw, sxxxToken, quorumNumerator, stakeAmount] = results;
3509
- const timelock = timelockRaw ? getAddress(timelockRaw) : null;
4193
+ const proposalState = state != null ? Number(state) : null;
4194
+ const proposalThresholdCurrent = threshold != null ? BigInt(threshold.toString()) : null;
4195
+ const sxxxToken = sxxxTokenAddr && /^0x[0-9a-fA-F]{40}$/.test(String(sxxxTokenAddr)) ? getAddress(sxxxTokenAddr) : null;
4196
+ const cancelThreshold = proposalThresholdSnapshot != null ? proposalThresholdSnapshot : proposalThresholdCurrent;
4197
+ const votesTimepoint = await getGovernorTimepointMinusOne({ provider, governor: govAddr });
4198
+ const proposerVotes = proposalProposer && votesTimepoint != null ? await getGovernorVotesAt({ provider, governor: govAddr, account: proposalProposer, timepoint: votesTimepoint }) : null;
4199
+ let proposerBalance = null;
4200
+ if (proposalProposer && sxxxToken) {
4201
+ try {
4202
+ const iface = new Interface(["function balanceOf(address) view returns (uint256)"]);
4203
+ const data = iface.encodeFunctionData("balanceOf", [proposalProposer]);
4204
+ const ret = await provider.call({ to: sxxxToken, data });
4205
+ if (ret && ret !== "0x") {
4206
+ const [bal] = AbiCoder.defaultAbiCoder().decode(["uint256"], ret);
4207
+ proposerBalance = BigInt(bal.toString());
4208
+ }
4209
+ } catch (_) {
4210
+ }
4211
+ }
4212
+ const cancelableStates = /* @__PURE__ */ new Set([0, 1]);
4213
+ const isInCancelableState = proposalState != null && cancelableStates.has(proposalState);
4214
+ const proposerBelowThreshold = cancelThreshold != null && proposerVotes != null ? proposerVotes < cancelThreshold : null;
4215
+ const isCancelableByAnyone = isInCancelableState && proposerBelowThreshold === true;
3510
4216
  return {
3511
- address: addr,
3512
- name,
3513
- votingDelay,
3514
- votingPeriod,
3515
- proposalThreshold,
3516
- timelock,
3517
- sxxxToken: sxxxToken && /^0x/.test(String(sxxxToken)) ? getAddress(sxxxToken) : null,
3518
- quorumNumerator,
3519
- stakeAmount
4217
+ governor: govAddr,
4218
+ proposalId: pid,
4219
+ proposalProposer,
4220
+ proposalCreator,
4221
+ proposalState,
4222
+ proposalThresholdSnapshot,
4223
+ proposalThresholdCurrent,
4224
+ cancelThreshold,
4225
+ sxxxToken,
4226
+ proposerBalance,
4227
+ votesTimepoint,
4228
+ proposerVotes,
4229
+ proposerBelowThreshold,
4230
+ isInCancelableState,
4231
+ isCancelableByAnyone,
4232
+ cancelReason: isCancelableByAnyone ? "Proposer voting power dropped below proposal threshold" : isInCancelableState ? "Only proposer can cancel (above threshold)" : "Proposal not in cancelable state",
4233
+ // Backward-compat aliases
4234
+ proposalThreshold: cancelThreshold,
4235
+ creatorBalance: proposerBalance,
4236
+ creatorBelowThreshold: proposerBelowThreshold
3520
4237
  };
3521
4238
  }
3522
4239
  async function getProposal({ provider, governor, id: id2 }) {
@@ -3713,6 +4430,7 @@ var require_governance = __commonJS({
3713
4430
  }
3714
4431
  var buildQueueTx = (args) => cueTx("queue(address[],uint256[],bytes[],bytes32)", args);
3715
4432
  var buildExecuteTx = (args) => cueTx("execute(address[],uint256[],bytes[],bytes32)", args);
4433
+ var buildCancelTx = (args) => cueTx("cancel(address[],uint256[],bytes[],bytes32)", args);
3716
4434
  function buildQueueByIdTx({ governor, proposalId }) {
3717
4435
  const addr = normaliseGovernor(governor);
3718
4436
  const iface = new Interface(ABI.Governor);
@@ -3859,6 +4577,47 @@ var require_governance = __commonJS({
3859
4577
  }
3860
4578
  return 0n;
3861
4579
  }
4580
+ async function getVotesLatestMinusOneWithTimepoint({ provider, token, governor, account }) {
4581
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
4582
+ if (!token && !governor) throw new SageSDKError(CODES.INVALID_ARGS, "token or governor required");
4583
+ if (!account) throw new SageSDKError(CODES.INVALID_ARGS, "account required");
4584
+ let addr;
4585
+ if (token) {
4586
+ addr = getAddress(token);
4587
+ } else {
4588
+ const govAddr = normaliseGovernor(governor);
4589
+ try {
4590
+ const chain = await resolveVotesTokenChain({ provider, governor: govAddr });
4591
+ addr = getAddress(chain.votingToken);
4592
+ } catch (err) {
4593
+ throw new SageSDKError(CODES.NOT_FOUND, "failed to resolve votes token from governor", { cause: err });
4594
+ }
4595
+ }
4596
+ const user = getAddress(account);
4597
+ const latest = await provider.getBlockNumber();
4598
+ const timepoint = latest > 0 ? BigInt(latest - 1) : 0n;
4599
+ try {
4600
+ const iface = new Interface(["function getPastVotes(address,uint256) view returns (uint256)"]);
4601
+ const data = iface.encodeFunctionData("getPastVotes", [user, timepoint]);
4602
+ const ret = await provider.call({ to: addr, data });
4603
+ if (ret && ret !== "0x") {
4604
+ const [val] = AbiCoder.defaultAbiCoder().decode(["uint256"], ret);
4605
+ return { votes: BigInt(val.toString()), timepoint };
4606
+ }
4607
+ } catch (_) {
4608
+ }
4609
+ try {
4610
+ const iface2 = new Interface(["function getVotes(address) view returns (uint256)"]);
4611
+ const d2 = iface2.encodeFunctionData("getVotes", [user]);
4612
+ const ret2 = await provider.call({ to: addr, data: d2 });
4613
+ if (ret2 && ret2 !== "0x") {
4614
+ const [val2] = AbiCoder.defaultAbiCoder().decode(["uint256"], ret2);
4615
+ return { votes: BigInt(val2.toString()), timepoint };
4616
+ }
4617
+ } catch (_) {
4618
+ }
4619
+ return { votes: 0n, timepoint };
4620
+ }
3862
4621
  async function simulatePropose({ provider, governor, targets = [], values = [], calldatas = [], description = "", sender }) {
3863
4622
  if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
3864
4623
  const addr = normaliseGovernor(governor);
@@ -3908,13 +4667,18 @@ var require_governance = __commonJS({
3908
4667
  async function buildDelegateSelfTx({ provider, governor, account }) {
3909
4668
  if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
3910
4669
  const govAddr = normaliseGovernor(governor);
3911
- const g = new Contract(govAddr, ABI.Governor, provider);
3912
- const token = await g.sxxxToken();
3913
- return buildDelegateTx({ token, delegatee: account });
4670
+ try {
4671
+ const { baseToken } = await resolveVotesTokenChain({ provider, governor: govAddr });
4672
+ return buildDelegateTx({ token: baseToken, delegatee: account });
4673
+ } catch (_) {
4674
+ const g = new Contract(govAddr, ABI.Governor, provider);
4675
+ const token = await g.sxxxToken();
4676
+ return buildDelegateTx({ token, delegatee: account });
4677
+ }
3914
4678
  }
3915
- async function resolveVotesTokenChain({ provider, subdao, governor }) {
4679
+ async function resolveVotesTokenChain({ provider, subdao: subdao2, governor }) {
3916
4680
  if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
3917
- if (!subdao && !governor) throw new SageSDKError(CODES.INVALID_ARGS, "subdao or governor required");
4681
+ if (!subdao2 && !governor) throw new SageSDKError(CODES.INVALID_ARGS, "subdao or governor required");
3918
4682
  let votingToken = null;
3919
4683
  if (governor) {
3920
4684
  const govAddr = normaliseGovernor(governor);
@@ -3941,19 +4705,6 @@ var require_governance = __commonJS({
3941
4705
  }
3942
4706
  }
3943
4707
  }
3944
- if (!votingToken && subdao) {
3945
- try {
3946
- const subAddr = getAddress(subdao);
3947
- const iSub = new Interface(["function stakeToken() view returns (address)"]);
3948
- const d3 = iSub.encodeFunctionData("stakeToken", []);
3949
- const r3 = await provider.call({ to: subAddr, data: d3 });
3950
- if (r3 && r3 !== "0x") {
3951
- const [addr] = AbiCoder.defaultAbiCoder().decode(["address"], r3);
3952
- votingToken = addr;
3953
- }
3954
- } catch (_) {
3955
- }
3956
- }
3957
4708
  if (!votingToken) {
3958
4709
  throw new SageSDKError(CODES.NOT_FOUND, "failed to resolve votes token");
3959
4710
  }
@@ -3974,7 +4725,7 @@ var require_governance = __commonJS({
3974
4725
  const [decodedDao] = AbiCoder.defaultAbiCoder().decode(["address"], rawDao);
3975
4726
  const baseNorm = getAddress(decodedBase);
3976
4727
  const daoNorm = getAddress(decodedDao);
3977
- if (!subdao || daoNorm === getAddress(subdao)) {
4728
+ if (!subdao2 || daoNorm === getAddress(subdao2)) {
3978
4729
  baseToken = baseNorm;
3979
4730
  isWrapper = true;
3980
4731
  }
@@ -3983,8 +4734,8 @@ var require_governance = __commonJS({
3983
4734
  }
3984
4735
  return { votingToken: votingTokenNorm, baseToken, isWrapper };
3985
4736
  }
3986
- async function describeVotesToken({ provider, subdao, governor }) {
3987
- const { votingToken, baseToken, isWrapper } = await resolveVotesTokenChain({ provider, subdao, governor });
4737
+ async function describeVotesToken({ provider, subdao: subdao2, governor }) {
4738
+ const { votingToken, baseToken, isWrapper } = await resolveVotesTokenChain({ provider, subdao: subdao2, governor });
3988
4739
  let multiplierNFT = null;
3989
4740
  let basis = null;
3990
4741
  if (isWrapper) {
@@ -4005,8 +4756,8 @@ var require_governance = __commonJS({
4005
4756
  } else {
4006
4757
  const basisStr = basisBig != null ? basisBig.toString() : "10000";
4007
4758
  description = `Voting token = MultipliedVotes(base=${baseNorm}, multiplierNFT=${multiplierNorm || "unknown"}, BASIS=${basisStr})`;
4008
- if (subdao) {
4009
- description += ` for DAO ${getAddress(subdao)}`;
4759
+ if (subdao2) {
4760
+ description += ` for DAO ${getAddress(subdao2)}`;
4010
4761
  }
4011
4762
  }
4012
4763
  return {
@@ -4018,9 +4769,9 @@ var require_governance = __commonJS({
4018
4769
  description
4019
4770
  };
4020
4771
  }
4021
- async function getVotingStatus({ provider, subdao, governor, account }) {
4772
+ async function getVotingStatus({ provider, subdao: subdao2, governor, account }) {
4022
4773
  if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
4023
- if (!governor && !subdao) throw new SageSDKError(CODES.INVALID_ARGS, "governor or subdao required");
4774
+ if (!governor && !subdao2) throw new SageSDKError(CODES.INVALID_ARGS, "governor or subdao required");
4024
4775
  if (!account) throw new SageSDKError(CODES.INVALID_ARGS, "account required");
4025
4776
  const acct = getAddress(account);
4026
4777
  let govAddr = null;
@@ -4028,8 +4779,8 @@ var require_governance = __commonJS({
4028
4779
  if (governor) {
4029
4780
  govAddr = normaliseGovernor(governor);
4030
4781
  }
4031
- if (subdao) {
4032
- subdaoAddr = getAddress(subdao);
4782
+ if (subdao2) {
4783
+ subdaoAddr = getAddress(subdao2);
4033
4784
  if (!govAddr) {
4034
4785
  try {
4035
4786
  const sub = new Contract(subdaoAddr, ["function governor() view returns (address)"], provider);
@@ -4047,17 +4798,6 @@ var require_governance = __commonJS({
4047
4798
  } catch (_) {
4048
4799
  }
4049
4800
  const desc = await describeVotesToken({ provider, subdao: subdaoAddr || void 0, governor: govAddr });
4050
- let stakeToken = null;
4051
- if (subdaoAddr) {
4052
- try {
4053
- const sub = new Contract(subdaoAddr, ["function stakeToken() view returns (address)"], provider);
4054
- const st = await sub.stakeToken();
4055
- if (st && /^0x[0-9a-fA-F]{40}$/.test(String(st))) {
4056
- stakeToken = getAddress(st);
4057
- }
4058
- } catch (_) {
4059
- }
4060
- }
4061
4801
  const VotesABI = [
4062
4802
  "function getVotes(address) view returns (uint256)",
4063
4803
  "function delegates(address) view returns (address)",
@@ -4077,28 +4817,9 @@ var require_governance = __commonJS({
4077
4817
  } catch (_) {
4078
4818
  }
4079
4819
  const canPropose = threshold != null && votingPower != null ? votingPower >= threshold : null;
4080
- let stakeTokenVotes = null;
4081
- let stakeTokenDelegate = null;
4082
- let stakeTokenBalance = null;
4083
- const tokenMismatch = !!stakeToken && stakeToken.toLowerCase() !== desc.votingToken.toLowerCase();
4084
- if (stakeToken && tokenMismatch) {
4085
- try {
4086
- const stakeC = new Contract(stakeToken, VotesABI, provider);
4087
- const sv = await stakeC.getVotes(acct).catch(() => null);
4088
- if (sv != null) stakeTokenVotes = BigInt(sv.toString());
4089
- const sd = await stakeC.delegates(acct).catch(() => null);
4090
- if (sd && /^0x[0-9a-fA-F]{40}$/.test(String(sd))) {
4091
- stakeTokenDelegate = getAddress(sd);
4092
- }
4093
- const sb = await stakeC.balanceOf(acct).catch(() => null);
4094
- if (sb != null) stakeTokenBalance = BigInt(sb.toString());
4095
- } catch (_) {
4096
- }
4097
- }
4098
4820
  return {
4099
4821
  subdao: subdaoAddr,
4100
4822
  governor: govAddr,
4101
- stakeToken,
4102
4823
  votingToken: desc.votingToken,
4103
4824
  baseToken: desc.baseToken,
4104
4825
  isWrapper: desc.isWrapper,
@@ -4109,10 +4830,6 @@ var require_governance = __commonJS({
4109
4830
  tokenBalance,
4110
4831
  delegate,
4111
4832
  canPropose,
4112
- tokenMismatch,
4113
- stakeTokenVotes,
4114
- stakeTokenDelegate,
4115
- stakeTokenBalance,
4116
4833
  description: desc.description
4117
4834
  };
4118
4835
  }
@@ -4126,6 +4843,7 @@ var require_governance = __commonJS({
4126
4843
  buildCastVoteTx,
4127
4844
  buildQueueTx,
4128
4845
  buildExecuteTx,
4846
+ buildCancelTx,
4129
4847
  buildQueueByIdTx,
4130
4848
  buildExecuteByIdTx,
4131
4849
  decodeProposalEffects,
@@ -4138,16 +4856,19 @@ var require_governance = __commonJS({
4138
4856
  buildDelegateSelfTx,
4139
4857
  computeProposalIdHex,
4140
4858
  getVotesLatestMinusOne,
4859
+ getVotesLatestMinusOneWithTimepoint,
4141
4860
  simulatePropose,
4861
+ // Threshold model (PromptGovernor)
4862
+ getProposalThresholdStatus,
4863
+ getProposalCancelability,
4142
4864
  /**
4143
4865
  * Resolve the IVotes token used for governance voting.
4144
4866
  * Preference order:
4145
4867
  * - Governor.token() (if exposed)
4146
4868
  * - Governor.sxxxToken()
4147
- * - SubDAO.stakeToken() (when subdao provided)
4148
4869
  */
4149
- resolveVotesToken: async function resolveVotesToken({ provider, subdao, governor }) {
4150
- const { votingToken } = await resolveVotesTokenChain({ provider, subdao, governor });
4870
+ resolveVotesToken: async function resolveVotesToken({ provider, subdao: subdao2, governor }) {
4871
+ const { votingToken } = await resolveVotesTokenChain({ provider, subdao: subdao2, governor });
4151
4872
  return votingToken;
4152
4873
  },
4153
4874
  /**
@@ -4164,20 +4885,20 @@ var require_governance = __commonJS({
4164
4885
  */
4165
4886
  getVotingStatus,
4166
4887
  /** Build a delegate(self) tx using the preferred votes token resolution path. */
4167
- buildDelegateSelfPreferred: async function buildDelegateSelfPreferred({ provider, subdao, governor, account }) {
4888
+ buildDelegateSelfPreferred: async function buildDelegateSelfPreferred({ provider, subdao: subdao2, governor, account }) {
4168
4889
  if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
4169
4890
  if (!account) throw new SageSDKError(CODES.INVALID_ARGS, "account required");
4170
- const { baseToken } = await resolveVotesTokenChain({ provider, subdao, governor });
4891
+ const { baseToken } = await resolveVotesTokenChain({ provider, subdao: subdao2, governor });
4171
4892
  return buildDelegateTx({ token: baseToken, delegatee: account });
4172
4893
  },
4173
4894
  /**
4174
4895
  * Send a delegate(self) and verify votes at latest-1.
4175
4896
  * If signer is omitted, returns payload and current votes without sending.
4176
4897
  */
4177
- delegateSelfAndVerify: async function delegateSelfAndVerify({ provider, subdao, governor, account, signer = null, minVotes = null }) {
4898
+ delegateSelfAndVerify: async function delegateSelfAndVerify({ provider, subdao: subdao2, governor, account, signer = null, minVotes = null }) {
4178
4899
  if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
4179
4900
  if (!account) throw new SageSDKError(CODES.INVALID_ARGS, "account required");
4180
- const { votingToken, baseToken } = await resolveVotesTokenChain({ provider, subdao, governor });
4901
+ const { votingToken, baseToken } = await resolveVotesTokenChain({ provider, subdao: subdao2, governor });
4181
4902
  const payload = buildDelegateTx({ token: baseToken, delegatee: account });
4182
4903
  let txHash = null;
4183
4904
  if (signer && typeof signer.sendTransaction === "function") {
@@ -4190,7 +4911,8 @@ var require_governance = __commonJS({
4190
4911
  return { txHash, ok, votes, payload };
4191
4912
  },
4192
4913
  /**
4193
- * Ensure propose gates based on threshold, cooldown (best-effort), stake+allowance, and votes at latest-1.
4914
+ * Ensure propose gates based on THRESHOLD MODEL (votes-based threshold + cooldown).
4915
+ * No stake or allowance checks - proposer just needs sufficient voting power at clock()-1.
4194
4916
  * Returns a non-throwing summary.
4195
4917
  */
4196
4918
  ensureProposeGates: async function ensureProposeGates({ provider, governor, proposer }) {
@@ -4198,90 +4920,488 @@ var require_governance = __commonJS({
4198
4920
  const govAddr = normaliseGovernor(governor);
4199
4921
  const user = getAddress(proposer);
4200
4922
  const g = new Contract(govAddr, ABI.Governor, provider);
4201
- const threshold = await g.proposalThreshold().catch(() => null);
4202
- let cooldown = null;
4203
- try {
4204
- const i = new Interface(["function proposalCooldown() view returns (uint256)"]);
4205
- const r = await provider.call({ to: govAddr, data: i.encodeFunctionData("proposalCooldown", []) });
4206
- if (r && r !== "0x") cooldown = BigInt(AbiCoder.defaultAbiCoder().decode(["uint256"], r)[0].toString());
4207
- } catch (_) {
4923
+ const status = await getProposalThresholdStatus({ provider, governor: govAddr, proposer: user });
4924
+ return {
4925
+ // Core threshold model
4926
+ threshold: status.proposalThreshold,
4927
+ sxxxBalance: status.sxxxBalance,
4928
+ thresholdMet: status.thresholdMet,
4929
+ cooldownRemaining: status.cooldownRemaining,
4930
+ cooldownElapsed: status.cooldownElapsed,
4931
+ canPropose: status.canPropose,
4932
+ // Voting power (canonical: clock()-1)
4933
+ votesTimepoint: status.votesTimepoint,
4934
+ votingPower: status.proposerVotes,
4935
+ votesOk: status.thresholdMet,
4936
+ // Details
4937
+ details: { sxxxToken: status.sxxxToken, proposer: user, governor: govAddr },
4938
+ cooldown: status.cooldownRemaining
4939
+ // alias for cooldownRemaining
4940
+ };
4941
+ },
4942
+ /**
4943
+ * Compose propose readiness with optional execution readiness checks.
4944
+ * execution?: { provider, registry, timelock, subdao, manifestCID, version }
4945
+ */
4946
+ readinessToPropose: async function readinessToPropose({ provider, governor, proposer, execution = null }) {
4947
+ const gates = await this.ensureProposeGates({ provider, governor, proposer });
4948
+ let executionReady = null;
4949
+ let executionError = null;
4950
+ if (execution && execution.registry && execution.timelock && execution.subdao) {
4951
+ try {
4952
+ const library = require_library();
4953
+ const { to, data } = library.buildUpdateLibraryTx({
4954
+ registry: execution.registry,
4955
+ subdao: execution.subdao,
4956
+ manifestCID: execution.manifestCID || "",
4957
+ version: execution.version || "1.0.0"
4958
+ });
4959
+ const sim = await library.simulateAsTimelock({ provider, registry: execution.registry, to, data, timelock: execution.timelock });
4960
+ executionReady = !!sim.ok;
4961
+ executionError = sim.ok ? null : sim.error?.message || "revert";
4962
+ } catch (err) {
4963
+ executionReady = false;
4964
+ executionError = String(err && err.message || err);
4965
+ }
4208
4966
  }
4209
- let stakeRequired = null;
4210
- try {
4211
- const i2 = new Interface(["function proposalStakeRequired() view returns (uint256)"]);
4212
- const r2 = await provider.call({ to: govAddr, data: i2.encodeFunctionData("proposalStakeRequired", []) });
4213
- if (r2 && r2 !== "0x") stakeRequired = BigInt(AbiCoder.defaultAbiCoder().decode(["uint256"], r2)[0].toString());
4214
- } catch (_) {
4967
+ return { ...gates, executionReady, executionError };
4968
+ },
4969
+ /**
4970
+ * Unified authority + readiness envelope for agent-native governance UX.
4971
+ *
4972
+ * This is intentionally **read-only**: it returns diagnostics and optional tx payloads/CLI commands,
4973
+ * but never sends transactions.
4974
+ *
4975
+ * Inputs:
4976
+ * - provider (required)
4977
+ * - subdao OR governor (required)
4978
+ * - account (optional, enables per-account readiness checks)
4979
+ * - action: propose | vote | queue | execute | timelock-schedule | update-library
4980
+ * - proposalId (optional, for queue/execute diagnostics)
4981
+ * - libraryRegistry / dao / manifestCID / version (optional, for update-library diagnostics)
4982
+ */
4983
+ getAuthorityAndReadiness: async function getAuthorityAndReadiness({
4984
+ provider,
4985
+ subdao: subdaoAddress = null,
4986
+ governor: governorAddress = null,
4987
+ account = null,
4988
+ action,
4989
+ proposalId = null,
4990
+ libraryRegistry = null,
4991
+ dao = null,
4992
+ manifestCID = null,
4993
+ version = null
4994
+ } = {}) {
4995
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
4996
+ if (!subdaoAddress && !governorAddress) throw new SageSDKError(CODES.INVALID_ARGS, "subdao or governor required");
4997
+ const actionNorm = String(action || "propose").trim().toLowerCase();
4998
+ const allowed = /* @__PURE__ */ new Set(["propose", "vote", "queue", "execute", "timelock-schedule", "update-library"]);
4999
+ if (!allowed.has(actionNorm)) {
5000
+ throw new SageSDKError(CODES.INVALID_ARGS, `unsupported action: ${String(action || "")}`);
5001
+ }
5002
+ const readiness = [];
5003
+ const push = (item) => readiness.push(item);
5004
+ const normalizeAccount = () => {
5005
+ if (!account) return null;
5006
+ try {
5007
+ return getAddress(account);
5008
+ } catch (err) {
5009
+ throw new SageSDKError(CODES.INVALID_ARGS, "invalid account address", { cause: err });
5010
+ }
5011
+ };
5012
+ const acct = normalizeAccount();
5013
+ const proposal = {
5014
+ proposalThreshold: null,
5015
+ sxxxToken: null,
5016
+ proposerSxxxBalance: null,
5017
+ thresholdMet: null,
5018
+ cooldownRemaining: null,
5019
+ cooldownElapsed: null,
5020
+ proposerVotes: null,
5021
+ votesTimepoint: null,
5022
+ cancelPolicy: {
5023
+ enabled: null,
5024
+ cancelableStates: ["Pending", "Active"],
5025
+ thresholdSource: "proposalThresholdSnapshot",
5026
+ note: "Keep voting power (delegation + multipliers) above proposalThreshold while proposal is active, or it may be canceled by anyone"
5027
+ },
5028
+ cancelability: null
5029
+ };
5030
+ let subdaoAddr = subdaoAddress ? getAddress(subdaoAddress) : null;
5031
+ let governorAddr = governorAddress ? normaliseGovernor(governorAddress) : null;
5032
+ let timelockAddr = null;
5033
+ let opsSafe = null;
5034
+ if (subdaoAddr) {
4215
5035
  try {
4216
- const i3 = new Interface(["function stakeAmount() view returns (uint256)"]);
4217
- const r3 = await provider.call({ to: govAddr, data: i3.encodeFunctionData("stakeAmount", []) });
4218
- if (r3 && r3 !== "0x") stakeRequired = BigInt(AbiCoder.defaultAbiCoder().decode(["uint256"], r3)[0].toString());
4219
- } catch (_2) {
5036
+ const info = await subdao.getSubDAOInfo({ provider, subdao: subdaoAddr });
5037
+ governorAddr = governorAddr || info.governor;
5038
+ timelockAddr = info.timelock || timelockAddr;
5039
+ opsSafe = info.treasury || null;
5040
+ } catch (err) {
5041
+ push({
5042
+ item: "Resolve SubDAO context",
5043
+ met: false,
5044
+ severity: "warn",
5045
+ evidence: { subdao: subdaoAddr, error: String(err?.message || err) }
5046
+ });
4220
5047
  }
4221
5048
  }
4222
- let allowanceOk = null;
4223
- let sxxx = null;
5049
+ if (!governorAddr) {
5050
+ throw new SageSDKError(CODES.INVALID_ARGS, "could not resolve governor address");
5051
+ }
5052
+ let govInfo = null;
4224
5053
  try {
4225
- const i4 = new Interface(["function sxxxToken() view returns (address)"]);
4226
- const r4 = await provider.call({ to: govAddr, data: i4.encodeFunctionData("sxxxToken", []) });
4227
- const [addr] = AbiCoder.defaultAbiCoder().decode(["address"], r4);
4228
- sxxx = getAddress(addr);
5054
+ govInfo = await getGovernorInfo({ provider, governor: governorAddr });
5055
+ timelockAddr = timelockAddr || govInfo.timelock || null;
4229
5056
  } catch (_) {
4230
5057
  }
4231
- if (sxxx && stakeRequired != null) {
5058
+ let governanceProfile = { kind: null, proposalAccess: null, executionAccess: null, profileInitialized: false };
5059
+ if (subdaoAddr) {
4232
5060
  try {
4233
- const i5 = new Interface(["function allowance(address,address) view returns (uint256)"]);
4234
- const r5 = await provider.call({ to: sxxx, data: i5.encodeFunctionData("allowance", [user, govAddr]) });
4235
- const [allow] = AbiCoder.defaultAbiCoder().decode(["uint256"], r5);
4236
- allowanceOk = BigInt(allow.toString()) >= BigInt(stakeRequired);
5061
+ const prof = await subdao.getGovernanceProfile({ provider, subdao: subdaoAddr });
5062
+ governanceProfile = {
5063
+ kind: prof.kind,
5064
+ proposalAccess: prof.proposalAccess,
5065
+ executionAccess: prof.executionAccess,
5066
+ profileInitialized: !!prof.profileInitialized
5067
+ };
4237
5068
  } catch (_) {
4238
5069
  }
4239
5070
  }
4240
- let votesOk = null;
5071
+ let votesChain = null;
4241
5072
  try {
4242
- const { votingToken } = await resolveVotesTokenChain({ provider, governor });
4243
- const votes = await getVotesLatestMinusOne({ provider, token: votingToken, account: user });
4244
- const th = threshold != null ? BigInt(threshold) : 0n;
4245
- votesOk = votes >= th;
5073
+ votesChain = await resolveVotesTokenChain({ provider, subdao: subdaoAddr || void 0, governor: governorAddr });
4246
5074
  } catch (_) {
5075
+ votesChain = null;
5076
+ }
5077
+ if (proposalId != null) {
5078
+ try {
5079
+ proposal.cancelability = await getProposalCancelability({ provider, governor: governorAddr, proposalId });
5080
+ } catch (_) {
5081
+ proposal.cancelability = null;
5082
+ }
5083
+ }
5084
+ let timelockInfo = {
5085
+ proposerRole: null,
5086
+ executorRole: null,
5087
+ governorIsProposer: null,
5088
+ governorIsExecutor: null,
5089
+ governorCanExecute: null,
5090
+ anyoneCanExecute: null,
5091
+ accountIsProposer: null,
5092
+ accountIsExecutor: null
5093
+ };
5094
+ if (timelockAddr) {
5095
+ try {
5096
+ const tl = new Contract(timelockAddr, ABI.Timelock, provider);
5097
+ const [PR, ER] = await Promise.all([
5098
+ tl.PROPOSER_ROLE().catch(() => null),
5099
+ tl.EXECUTOR_ROLE().catch(() => null)
5100
+ ]);
5101
+ timelockInfo.proposerRole = PR;
5102
+ timelockInfo.executorRole = ER;
5103
+ if (PR && governorAddr) {
5104
+ timelockInfo.governorIsProposer = await tl.hasRole(PR, governorAddr).catch(() => null);
5105
+ }
5106
+ if (ER && governorAddr) {
5107
+ timelockInfo.governorIsExecutor = await tl.hasRole(ER, governorAddr).catch(() => null);
5108
+ }
5109
+ if (ER) {
5110
+ timelockInfo.anyoneCanExecute = await tl.hasRole(ER, ZeroAddress).catch(() => null);
5111
+ }
5112
+ timelockInfo.governorCanExecute = timelockInfo.anyoneCanExecute === true || timelockInfo.governorIsExecutor === true;
5113
+ if (acct && PR) {
5114
+ timelockInfo.accountIsProposer = await tl.hasRole(PR, acct).catch(() => null);
5115
+ }
5116
+ if (acct && ER) {
5117
+ timelockInfo.accountIsExecutor = await tl.hasRole(ER, acct).catch(() => null);
5118
+ }
5119
+ } catch (_) {
5120
+ }
5121
+ }
5122
+ const governorCanQueue = timelockInfo.governorIsProposer === true;
5123
+ const governorCanExecute = timelockInfo.governorCanExecute === true;
5124
+ const authority = { route: "unknown", rationale: "insufficient context", requiredActor: "unknown" };
5125
+ const requireCouncil = governanceProfile.proposalAccess === "COUNCIL_ONLY";
5126
+ const requireTokenThreshold = governanceProfile.proposalAccess === "COMMUNITY_THRESHOLD";
5127
+ const execIsAnyone = governanceProfile.executionAccess === "ANYONE";
5128
+ const execIsCouncilOnly = governanceProfile.executionAccess === "COUNCIL_ONLY";
5129
+ const safeOrCouncil = opsSafe ? `opsSafe ${opsSafe}` : "council/safe";
5130
+ if (actionNorm === "timelock-schedule") {
5131
+ authority.route = "timelock-schedule";
5132
+ authority.rationale = "Direct timelock scheduling/execution path (operator/council governance)";
5133
+ authority.requiredActor = `Timelock PROPOSER_ROLE holder (often ${safeOrCouncil})`;
5134
+ } else if (actionNorm === "update-library") {
5135
+ if (governorCanQueue && governanceProfile.kind === "TOKEN") {
5136
+ authority.route = "governor-propose";
5137
+ authority.rationale = "LibraryRegistry updates are executed by the DAO timelock; token DAOs route via Governor \u2192 Timelock";
5138
+ authority.requiredActor = requireCouncil ? `COUNCIL_ROLE (${safeOrCouncil})` : "Proposer meeting proposalThreshold";
5139
+ } else {
5140
+ authority.route = "timelock-schedule";
5141
+ authority.rationale = "LibraryRegistry updates are executed by the DAO timelock; operator/council DAOs schedule directly on Timelock";
5142
+ authority.requiredActor = `Timelock PROPOSER_ROLE holder (often ${safeOrCouncil})`;
5143
+ }
5144
+ } else if (actionNorm === "propose") {
5145
+ if (!governorCanQueue && timelockAddr) {
5146
+ authority.route = "timelock-schedule";
5147
+ authority.rationale = "Governor is not a Timelock proposer; governance changes must be scheduled directly on Timelock (operator/council mode)";
5148
+ authority.requiredActor = `Timelock PROPOSER_ROLE holder (often ${safeOrCouncil})`;
5149
+ } else {
5150
+ authority.route = "governor-propose";
5151
+ authority.rationale = "Standard token-governance proposal flow (Governor \u2192 Timelock)";
5152
+ authority.requiredActor = requireCouncil ? `COUNCIL_ROLE (${safeOrCouncil})` : requireTokenThreshold ? "Proposer meeting proposalThreshold" : "Proposer meeting proposalThreshold";
5153
+ }
5154
+ } else if (actionNorm === "vote") {
5155
+ if (governanceProfile.kind === "OPERATOR") {
5156
+ authority.route = "not-applicable";
5157
+ authority.rationale = "Operator governance: token voting is not required for changes";
5158
+ authority.requiredActor = `Operator/council (${safeOrCouncil})`;
5159
+ } else {
5160
+ authority.route = "governor-vote";
5161
+ authority.rationale = "Token governance voting on Governor proposals";
5162
+ authority.requiredActor = "Voter with delegated voting power";
5163
+ }
5164
+ } else if (actionNorm === "queue") {
5165
+ if (governorCanQueue) {
5166
+ authority.route = "governor-queue";
5167
+ authority.rationale = "Queue via GovernorTimelockControl (Governor schedules on Timelock)";
5168
+ authority.requiredActor = "Any account (Governor performs the Timelock schedule as proposer)";
5169
+ } else {
5170
+ authority.route = "timelock-schedule";
5171
+ authority.rationale = "Queue by scheduling directly on Timelock (operator/council mode)";
5172
+ authority.requiredActor = `Timelock PROPOSER_ROLE holder (often ${safeOrCouncil})`;
5173
+ }
5174
+ } else if (actionNorm === "execute") {
5175
+ if (governorCanQueue && governorCanExecute) {
5176
+ authority.route = "governor-execute";
5177
+ authority.rationale = "Execute via GovernorTimelockControl (Timelock.execute called by Governor)";
5178
+ authority.requiredActor = "Any account (Governor performs the Timelock execute)";
5179
+ } else {
5180
+ authority.route = "timelock-execute";
5181
+ authority.rationale = "Execute by calling Timelock.execute directly (restricted executor or operator/council mode)";
5182
+ authority.requiredActor = execIsCouncilOnly ? `Timelock EXECUTOR_ROLE holder (${safeOrCouncil})` : `Timelock EXECUTOR_ROLE holder (often ${safeOrCouncil})`;
5183
+ }
5184
+ }
5185
+ if (!acct) {
5186
+ push({
5187
+ item: "Account provided for readiness checks",
5188
+ met: false,
5189
+ severity: "info",
5190
+ evidence: { note: "Pass account to enable per-user readiness checks" }
5191
+ });
5192
+ } else {
5193
+ if ((actionNorm === "propose" || actionNorm === "vote") && votesChain && votesChain.votingToken) {
5194
+ try {
5195
+ const res = await getVotesLatestMinusOneWithTimepoint({ provider, token: votesChain.votingToken, account: acct });
5196
+ proposal.proposerVotes = res.votes;
5197
+ proposal.votesTimepoint = res.timepoint;
5198
+ } catch (_) {
5199
+ }
5200
+ }
5201
+ if (actionNorm === "propose" || actionNorm === "vote") {
5202
+ let voting = null;
5203
+ try {
5204
+ voting = await getVotingStatus({ provider, subdao: subdaoAddr || void 0, governor: governorAddr, account: acct });
5205
+ } catch (_) {
5206
+ voting = null;
5207
+ }
5208
+ if (!voting || !voting.baseToken) {
5209
+ push({ item: "Resolve voting token wiring", met: false, severity: "warn" });
5210
+ } else {
5211
+ const threshold = voting.threshold != null ? BigInt(voting.threshold) : null;
5212
+ const votingPower = voting.votingPower != null ? BigInt(voting.votingPower) : null;
5213
+ let baseBalance = null;
5214
+ let baseDelegate = null;
5215
+ try {
5216
+ const base = new Contract(voting.baseToken, ABI.ERC20Votes, provider);
5217
+ const [bal, del] = await Promise.all([
5218
+ base.balanceOf(acct).catch(() => null),
5219
+ base.delegates(acct).catch(() => null)
5220
+ ]);
5221
+ if (bal != null) baseBalance = BigInt(bal.toString());
5222
+ if (del && /^0x[0-9a-fA-F]{40}$/.test(String(del))) baseDelegate = getAddress(del);
5223
+ } catch (_) {
5224
+ }
5225
+ const hasVotes = votingPower != null ? votingPower > 0n : null;
5226
+ push({
5227
+ item: "Voting power is non-zero (delegated)",
5228
+ met: hasVotes,
5229
+ severity: hasVotes === false ? "error" : "info",
5230
+ evidence: {
5231
+ votingPower: votingPower?.toString?.() ?? null,
5232
+ votingToken: voting.votingToken,
5233
+ baseToken: voting.baseToken,
5234
+ note: threshold != null ? `proposalThreshold=${threshold.toString()}` : void 0
5235
+ }
5236
+ });
5237
+ const noDelegateSet = !baseDelegate || baseDelegate === ZeroAddress;
5238
+ if ((actionNorm === "propose" || actionNorm === "vote") && baseBalance != null && baseBalance > 0n && (votingPower === 0n || votingPower === null) && noDelegateSet) {
5239
+ try {
5240
+ const payload = buildDelegateTx({ token: voting.baseToken, delegatee: acct });
5241
+ push({
5242
+ item: "Delegation set (ERC20Votes)",
5243
+ met: false,
5244
+ severity: "error",
5245
+ evidence: { baseToken: voting.baseToken, delegate: baseDelegate || ZeroAddress, baseBalance: baseBalance.toString() },
5246
+ fix: { kind: "tx", commandOrCalldata: payload }
5247
+ });
5248
+ } catch (_) {
5249
+ push({
5250
+ item: "Delegation set (ERC20Votes)",
5251
+ met: false,
5252
+ severity: "error",
5253
+ evidence: { baseToken: voting.baseToken, delegate: baseDelegate || ZeroAddress, baseBalance: baseBalance?.toString?.() ?? null }
5254
+ });
5255
+ }
5256
+ }
5257
+ }
5258
+ }
5259
+ if (actionNorm === "propose" && authority.route === "governor-propose") {
5260
+ let thresholdStatus = null;
5261
+ try {
5262
+ thresholdStatus = await getProposalThresholdStatus({ provider, governor: governorAddr, proposer: acct });
5263
+ } catch (_) {
5264
+ }
5265
+ if (thresholdStatus) {
5266
+ const {
5267
+ proposalThreshold,
5268
+ proposerVotes,
5269
+ votesTimepoint,
5270
+ sxxxBalance,
5271
+ thresholdMet,
5272
+ cooldownRemaining,
5273
+ cooldownElapsed,
5274
+ sxxxToken
5275
+ } = thresholdStatus;
5276
+ proposal.proposalThreshold = proposalThreshold;
5277
+ proposal.sxxxToken = sxxxToken;
5278
+ proposal.proposerSxxxBalance = sxxxBalance;
5279
+ proposal.proposerVotes = proposerVotes;
5280
+ proposal.votesTimepoint = votesTimepoint;
5281
+ proposal.thresholdMet = thresholdMet;
5282
+ proposal.cooldownRemaining = cooldownRemaining;
5283
+ proposal.cooldownElapsed = cooldownElapsed;
5284
+ proposal.cancelPolicy.enabled = proposalThreshold != null ? proposalThreshold > 0n : null;
5285
+ if (proposalThreshold != null) {
5286
+ push({
5287
+ item: "Voting power meets proposalThreshold (clock()-1)",
5288
+ met: thresholdMet,
5289
+ severity: thresholdMet === false ? "error" : "info",
5290
+ evidence: {
5291
+ proposerVotes: proposerVotes?.toString?.() ?? null,
5292
+ votesTimepoint: votesTimepoint?.toString?.() ?? null,
5293
+ proposalThreshold: proposalThreshold.toString(),
5294
+ sxxxToken,
5295
+ note: "No approve/escrow needed - delegate and keep sufficient voting power"
5296
+ }
5297
+ });
5298
+ }
5299
+ if (cooldownRemaining != null) {
5300
+ push({
5301
+ item: "Proposal cooldown elapsed",
5302
+ met: cooldownElapsed,
5303
+ severity: cooldownElapsed === false ? "warn" : "info",
5304
+ evidence: { cooldownRemainingSeconds: cooldownRemaining.toString() }
5305
+ });
5306
+ }
5307
+ if (proposalThreshold != null && proposalThreshold > 0n) {
5308
+ push({
5309
+ item: "Note: Proposal cancelability",
5310
+ met: true,
5311
+ severity: "info",
5312
+ evidence: {
5313
+ note: "Keep voting power (delegation + multipliers) above threshold while proposal is active, or it may be canceled by anyone",
5314
+ proposalThreshold: proposalThreshold.toString()
5315
+ }
5316
+ });
5317
+ }
5318
+ }
5319
+ }
5320
+ if (actionNorm === "timelock-schedule" || authority.route === "timelock-schedule") {
5321
+ if (!timelockAddr) {
5322
+ push({ item: "Timelock resolved", met: false, severity: "error" });
5323
+ } else if (timelockInfo.proposerRole) {
5324
+ push({
5325
+ item: "Account has Timelock PROPOSER_ROLE",
5326
+ met: timelockInfo.accountIsProposer,
5327
+ severity: timelockInfo.accountIsProposer === false ? "error" : "info",
5328
+ evidence: { timelock: timelockAddr, account: acct }
5329
+ });
5330
+ }
5331
+ }
5332
+ if ((actionNorm === "queue" || actionNorm === "execute") && proposalId != null) {
5333
+ try {
5334
+ const g = new Contract(governorAddr, ABI.Governor, provider);
5335
+ const pid = typeof proposalId === "bigint" ? proposalId : String(proposalId).startsWith("0x") ? BigInt(String(proposalId)) : BigInt(String(proposalId));
5336
+ const st = await g.state(pid).catch(() => null);
5337
+ if (st != null) {
5338
+ push({
5339
+ item: "Proposal state readable",
5340
+ met: true,
5341
+ severity: "info",
5342
+ evidence: { proposalId: pid.toString(), state: Number(st) }
5343
+ });
5344
+ }
5345
+ } catch (_) {
5346
+ }
5347
+ }
5348
+ if (actionNorm === "update-library") {
5349
+ const regAddr = libraryRegistry || process.env.LIBRARY_REGISTRY_ADDRESS || process.env.SAGE_LIBRARY_REGISTRY_ADDRESS || null;
5350
+ const daoAddr = dao ? getAddress(dao) : subdaoAddr || null;
5351
+ if (!regAddr) {
5352
+ push({ item: "LibraryRegistry address provided", met: false, severity: "error" });
5353
+ } else if (!daoAddr) {
5354
+ push({ item: "DAO/SubDAO address provided", met: false, severity: "error" });
5355
+ } else if (!timelockAddr) {
5356
+ push({ item: "Timelock resolved for DAO", met: false, severity: "error" });
5357
+ } else {
5358
+ try {
5359
+ const reg = new Contract(getAddress(regAddr), ABI.LibraryRegistry, provider);
5360
+ const daoTl = await reg.daoTimelock(daoAddr).catch(() => null);
5361
+ const daoTimelock = daoTl && /^0x[0-9a-fA-F]{40}$/.test(String(daoTl)) ? getAddress(daoTl) : null;
5362
+ const match = daoTimelock ? daoTimelock.toLowerCase() === timelockAddr.toLowerCase() : null;
5363
+ push({
5364
+ item: "LibraryRegistry daoTimelock matches resolved Timelock",
5365
+ met: match,
5366
+ severity: match === false ? "error" : "info",
5367
+ evidence: { libraryRegistry: getAddress(regAddr), dao: daoAddr, daoTimelock, timelock: timelockAddr }
5368
+ });
5369
+ if (match === true && manifestCID) {
5370
+ const library = require_library();
5371
+ const payload = library.buildUpdateLibraryTx({
5372
+ registry: getAddress(regAddr),
5373
+ subdao: daoAddr,
5374
+ manifestCID: String(manifestCID),
5375
+ version: String(version || "1.0.0")
5376
+ });
5377
+ const sim = await library.simulateAsTimelock({ provider, registry: getAddress(regAddr), to: payload.to, data: payload.data, timelock: timelockAddr });
5378
+ push({
5379
+ item: "Simulate LibraryRegistry.updateLibrary as Timelock",
5380
+ met: !!sim.ok,
5381
+ severity: sim.ok ? "info" : "error",
5382
+ evidence: sim.ok ? { ok: true } : { ok: false, error: sim.error?.message || "revert" }
5383
+ });
5384
+ }
5385
+ } catch (_) {
5386
+ }
5387
+ }
5388
+ }
4247
5389
  }
4248
5390
  return {
4249
- threshold: threshold != null ? BigInt(threshold) : null,
4250
- cooldown,
4251
- // bigint | null
4252
- stake: stakeRequired,
4253
- allowanceOk,
4254
- votesOk,
4255
- details: { sxxxToken: sxxx, proposer: user, governor: govAddr }
5391
+ governanceProfile,
5392
+ proposal,
5393
+ addresses: {
5394
+ subdao: subdaoAddr,
5395
+ governor: governorAddr,
5396
+ timelock: timelockAddr,
5397
+ opsSafe,
5398
+ votingToken: votesChain ? votesChain.votingToken : null,
5399
+ baseVotingToken: votesChain ? votesChain.baseToken : null
5400
+ },
5401
+ authority,
5402
+ readiness
4256
5403
  };
4257
5404
  },
4258
- /**
4259
- * Compose propose readiness with optional execution readiness checks.
4260
- * execution?: { provider, registry, timelock, subdao, manifestCID, version }
4261
- */
4262
- readinessToPropose: async function readinessToPropose({ provider, governor, proposer, execution = null }) {
4263
- const gates = await this.ensureProposeGates({ provider, governor, proposer });
4264
- let executionReady = null;
4265
- let executionError = null;
4266
- if (execution && execution.registry && execution.timelock && execution.subdao) {
4267
- try {
4268
- const library = require_library();
4269
- const { to, data } = library.buildUpdateLibraryTx({
4270
- registry: execution.registry,
4271
- subdao: execution.subdao,
4272
- manifestCID: execution.manifestCID || "",
4273
- version: execution.version || "1.0.0"
4274
- });
4275
- const sim = await library.simulateAsTimelock({ provider, registry: execution.registry, to, data, timelock: execution.timelock });
4276
- executionReady = !!sim.ok;
4277
- executionError = sim.ok ? null : sim.error?.message || "revert";
4278
- } catch (err) {
4279
- executionReady = false;
4280
- executionError = String(err && err.message || err);
4281
- }
4282
- }
4283
- return { ...gates, executionReady, executionError };
4284
- },
4285
5405
  /** List active proposals (default states: PENDING, ACTIVE) via subgraph. */
4286
5406
  listActiveProposals: async function listActiveProposals({ url, governor, first = 20, skip = 0 }) {
4287
5407
  if (!url) throw new SageSDKError(CODES.INVALID_ARGS, "subgraph url required");
@@ -4307,22 +5427,21 @@ var require_governance = __commonJS({
4307
5427
  /**
4308
5428
  * Detect governance mode and operator status.
4309
5429
  * Heuristics mirror CLI detectGovMode.
4310
- * Returns { operator: boolean, governance: 'Personal'|'Community'|'Unknown', governor, timelock, subdao, stakeRequired, depositWei }
5430
+ * Returns { operator: boolean, governance: 'Personal'|'Community'|'Unknown', governor, timelock, subdao, depositWei }
4311
5431
  */
4312
- detectMode: async function detectMode({ provider, governor, timelock, subdao }) {
5432
+ detectMode: async function detectMode({ provider, governor, timelock, subdao: subdao2 }) {
4313
5433
  const out = {
4314
5434
  operator: false,
4315
5435
  governance: "Unknown",
4316
5436
  governor: governor || null,
4317
5437
  timelock: timelock || null,
4318
- subdao: subdao || null,
4319
- stakeRequired: null,
5438
+ subdao: subdao2 || null,
4320
5439
  depositWei: null
4321
5440
  };
4322
5441
  if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
4323
5442
  try {
4324
- if (subdao && (!governor || !timelock)) {
4325
- const sub = new Contract(getAddress(subdao), ["function governor() view returns (address)", "function timelock() view returns (address)", "function getGovernanceMode() view returns (uint8)"], provider);
5443
+ if (subdao2 && (!governor || !timelock)) {
5444
+ const sub = new Contract(getAddress(subdao2), ["function governor() view returns (address)", "function timelock() view returns (address)", "function getGovernanceMode() view returns (uint8)"], provider);
4326
5445
  if (!governor) out.governor = await sub.governor();
4327
5446
  if (!timelock) out.timelock = await sub.timelock();
4328
5447
  try {
@@ -4344,313 +5463,61 @@ var require_governance = __commonJS({
4344
5463
  }
4345
5464
  try {
4346
5465
  if (out.governor) {
4347
- const ifaceStake = new Interface(["function proposalStakeRequired() view returns (uint256)"]);
4348
- const retStake = await provider.call({ to: getAddress(out.governor), data: ifaceStake.encodeFunctionData("proposalStakeRequired", []) }).catch(() => null);
4349
- if (retStake && retStake !== "0x") out.stakeRequired = BigInt(retStake);
4350
- const ifaceDep = new Interface(["function bootstrapDepositWei() view returns (uint256)"]);
4351
- const retDep = await provider.call({ to: getAddress(out.governor), data: ifaceDep.encodeFunctionData("bootstrapDepositWei", []) }).catch(() => null);
4352
- if (retDep && retDep !== "0x") out.depositWei = BigInt(retDep);
4353
- }
4354
- } catch (_) {
4355
- }
4356
- return out;
4357
- }
4358
- };
4359
- }
4360
- });
4361
-
4362
- // src/governance/intents.js
4363
- var require_intents = __commonJS({
4364
- "src/governance/intents.js"(exports2, module2) {
4365
- var { Interface, getAddress } = __require("ethers");
4366
- var { SageSDKError, CODES } = require_errors();
4367
- function normalise(addr, label) {
4368
- try {
4369
- return getAddress(addr);
4370
- } catch {
4371
- throw new SageSDKError(CODES.INVALID_ARGS, `${label} required/invalid`);
4372
- }
4373
- }
4374
- var GovIface = new Interface([
4375
- "function updateQuorumNumerator(uint256)",
4376
- "function setVotingPeriodBlocks(uint32)",
4377
- "function setDepositOnlyMode(bool)"
4378
- ]);
4379
- var RegIface = new Interface([
4380
- "function setForkPolicy(uint8)",
4381
- "function updatePromptByGovernance(string,string)",
4382
- "function addPrompt(string,bytes32[],string,string,uint8)"
4383
- ]);
4384
- var CfgIface = new Interface([
4385
- "function setPremiumRevSplit(address,uint16,uint16,uint16)",
4386
- "function setCreatorStakeBps(address,uint256)"
4387
- ]);
4388
- module2.exports = {
4389
- // Governor targets
4390
- buildUpdateQuorumTx: ({ governor, quorumPercent }) => ({ to: normalise(governor, "governor"), data: GovIface.encodeFunctionData("updateQuorumNumerator", [Number(quorumPercent)]), value: 0n }),
4391
- buildSetVotingPeriodTx: ({ governor, blocks }) => ({ to: normalise(governor, "governor"), data: GovIface.encodeFunctionData("setVotingPeriodBlocks", [Number(blocks)]), value: 0n }),
4392
- buildSetDepositOnlyTx: ({ governor, enabled }) => ({ to: normalise(governor, "governor"), data: GovIface.encodeFunctionData("setDepositOnlyMode", [!!enabled]), value: 0n }),
4393
- // Registry targets
4394
- buildSetForkPolicyTx: ({ registry, policy }) => ({ to: normalise(registry, "registry"), data: RegIface.encodeFunctionData("setForkPolicy", [Number(policy)]), value: 0n }),
4395
- buildUpdatePromptByGovernanceTx: ({ registry, key, cid }) => ({ to: normalise(registry, "registry"), data: RegIface.encodeFunctionData("updatePromptByGovernance", [String(key), String(cid)]), value: 0n }),
4396
- buildAddPromptTx: ({ registry, title, tags = [], contentCID, metadataCID = "", category = 0 }) => ({ to: normalise(registry, "registry"), data: RegIface.encodeFunctionData("addPrompt", [String(title), tags, String(contentCID), String(metadataCID), Number(category)]), value: 0n }),
4397
- // GovernanceConfig targets
4398
- buildSetPremiumSplitTx: ({ config, subdao, treasuryBps, protocolBps, creatorBps }) => ({ to: normalise(config, "config"), data: CfgIface.encodeFunctionData("setPremiumRevSplit", [normalise(subdao, "subdao"), Number(treasuryBps), Number(protocolBps), Number(creatorBps)]), value: 0n }),
4399
- buildSetCreatorStakeBpsTx: ({ config, subdao, bps }) => ({ to: normalise(config, "config"), data: CfgIface.encodeFunctionData("setCreatorStakeBps", [normalise(subdao, "subdao"), Number(bps)]), value: 0n })
4400
- };
4401
- }
4402
- });
4403
-
4404
- // src/utils/logs.js
4405
- var require_logs = __commonJS({
4406
- "src/utils/logs.js"(exports2, module2) {
4407
- async function fetchLogsChunked({ provider, address, topics = [], fromBlock = 0, toBlock = "latest", chunkSize = 5e4 }) {
4408
- if (!provider) throw new Error("provider required");
4409
- const latest = toBlock === "latest" ? await provider.getBlockNumber() : Number(toBlock);
4410
- let start = Number(fromBlock);
4411
- const logs = [];
4412
- while (start <= latest) {
4413
- const end = Math.min(start + chunkSize - 1, latest);
4414
- const chunk = await provider.getLogs({ address, topics, fromBlock: start, toBlock: end });
4415
- logs.push(...chunk);
4416
- start = end + 1;
4417
- }
4418
- return logs;
4419
- }
4420
- module2.exports = {
4421
- fetchLogsChunked
4422
- };
4423
- }
4424
- });
4425
-
4426
- // src/subdao/index.js
4427
- var require_subdao = __commonJS({
4428
- "src/subdao/index.js"(exports2, module2) {
4429
- var { Contract, Interface, getAddress } = __require("ethers");
4430
- var ABI = require_abi();
4431
- var { fetchLogsChunked } = require_logs();
4432
- var { SageSDKError, CODES } = require_errors();
4433
- function normalise(address, label) {
4434
- if (!address) throw new SageSDKError(CODES.INVALID_ARGS, `${label} required`);
4435
- try {
4436
- return getAddress(address);
4437
- } catch (err) {
4438
- throw new SageSDKError(CODES.INVALID_ARGS, `invalid ${label}`, { cause: err });
4439
- }
4440
- }
4441
- async function discoverSubDAOs({ provider, factoryAddress, fromBlock = 0, toBlock = "latest", limit = 5e3, chunkSize = 5e4 }) {
4442
- if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
4443
- const fac = normalise(factoryAddress, "factoryAddress");
4444
- const iface = new Interface(ABI.Factory);
4445
- const topic = iface.getEvent("SubDAOGovernanceDeployed").topicHash;
4446
- const logs = await fetchLogsChunked({ provider, address: fac, topics: [topic], fromBlock, toBlock, chunkSize });
4447
- const out = [];
4448
- const slice = limit > 0 ? logs.slice(-limit) : logs;
4449
- for (const log of slice) {
4450
- try {
4451
- const decoded = iface.decodeEventLog("SubDAOGovernanceDeployed", log.data, log.topics);
4452
- out.push({
4453
- subdao: getAddress(decoded[0]),
4454
- governor: getAddress(decoded[1]),
4455
- timelock: getAddress(decoded[2]),
4456
- treasury: decoded[3] ? getAddress(decoded[3]) : null,
4457
- txHash: log.transactionHash,
4458
- blockNumber: log.blockNumber
4459
- });
4460
- } catch (err) {
4461
- continue;
4462
- }
4463
- }
4464
- return out;
4465
- }
4466
- async function getSubDAOInfo({ provider, subdao }) {
4467
- if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
4468
- const addr = normalise(subdao, "subdao");
4469
- const contract = new Contract(addr, ABI.SubDAO, provider);
4470
- const [governor, timelock, mode, promptRegistry, sageTreasury, stakeToken, minStakeAmount, accessModel, membershipPolicy, profileCID, factoryDeployer] = await Promise.all([
4471
- contract.governor(),
4472
- contract.timelock(),
4473
- contract.getGovernanceMode().catch(() => null),
4474
- contract.promptRegistry().catch(() => null),
4475
- contract.sageTreasury().catch(() => null),
4476
- contract.stakeToken().catch(() => null),
4477
- contract.minStakeAmount().catch(() => null),
4478
- contract.accessModel().catch(() => null),
4479
- contract.membershipPolicy().catch(() => null),
4480
- contract.profileCID().catch(() => null),
4481
- contract.factoryDeployer().catch(() => null)
4482
- ]);
4483
- return {
4484
- address: addr,
4485
- governor: getAddress(governor),
4486
- timelock: getAddress(timelock),
4487
- governanceMode: mode,
4488
- promptRegistry: promptRegistry ? getAddress(promptRegistry) : null,
4489
- sageTreasury: sageTreasury ? getAddress(sageTreasury) : null,
4490
- stakeToken: stakeToken && stakeToken !== "0x0000000000000000000000000000000000000000" ? getAddress(stakeToken) : null,
4491
- minStakeAmount: minStakeAmount != null ? BigInt(minStakeAmount.toString()) : null,
4492
- accessModel: accessModel != null ? Number(accessModel) : null,
4493
- membershipPolicy: membershipPolicy != null ? Number(membershipPolicy) : null,
4494
- profileCID: profileCID || null,
4495
- factoryDeployer: factoryDeployer && factoryDeployer !== "0x0000000000000000000000000000000000000000" ? getAddress(factoryDeployer) : null
4496
- };
4497
- }
4498
- async function getSubDAOUserStats({ provider, subdao, account }) {
4499
- if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
4500
- const addr = normalise(subdao, "subdao");
4501
- const user = normalise(account, "account");
4502
- const contract = new Contract(addr, ABI.SubDAO, provider);
4503
- const [staked, userForkCount] = await Promise.all([
4504
- contract.stakedAmount(user).catch(() => 0n),
4505
- contract.userForkCount(user).catch(() => 0n)
4506
- ]);
4507
- return {
4508
- stakedAmount: BigInt(staked.toString()),
4509
- userForkCount: Number(userForkCount)
4510
- };
4511
- }
4512
- var SubDAOInterface = new Interface([
4513
- "function stake(uint256)",
4514
- "function unstake(uint256)",
4515
- // Optional on newer SubDAO implementations; provided for tx building only.
4516
- "function setProfileCid(string)"
4517
- ]);
4518
- function buildStakeTx({ subdao, amount }) {
4519
- const addr = normalise(subdao, "subdao");
4520
- const data = SubDAOInterface.encodeFunctionData("stake", [BigInt(amount)]);
4521
- return { to: addr, data, value: 0n };
4522
- }
4523
- function buildUnstakeTx({ subdao, amount }) {
4524
- const addr = normalise(subdao, "subdao");
4525
- const data = SubDAOInterface.encodeFunctionData("unstake", [BigInt(amount)]);
4526
- return { to: addr, data, value: 0n };
4527
- }
4528
- function buildSetProfileCidTx({ subdao, profileCid }) {
4529
- const addr = normalise(subdao, "subdao");
4530
- if (!profileCid || typeof profileCid !== "string" || !profileCid.trim()) {
4531
- throw new SageSDKError(CODES.INVALID_ARGS, "profileCid (CID string) required");
4532
- }
4533
- const data = SubDAOInterface.encodeFunctionData("setProfileCid", [String(profileCid)]);
4534
- return { to: addr, data, value: 0n };
4535
- }
4536
- module2.exports = {
4537
- discoverSubDAOs,
4538
- getSubDAOInfo,
4539
- getSubDAOUserStats,
4540
- buildStakeTx,
4541
- buildUnstakeTx,
4542
- buildSetProfileCidTx
4543
- };
4544
- async function ensureSxxxBurnAllowance({ signer, sxxx, owner, spender, amount }) {
4545
- if (!signer) throw new SageSDKError(CODES.INVALID_ARGS, "signer required");
4546
- if (!sxxx) throw new SageSDKError(CODES.INVALID_ARGS, "sxxx address required");
4547
- if (!spender) throw new SageSDKError(CODES.INVALID_ARGS, "spender (factory) required");
4548
- const sxxxAddr = getAddress(sxxx);
4549
- const spenderAddr = getAddress(spender);
4550
- const erc = new Contract(sxxxAddr, ABI.SXXX, signer);
4551
- const ownerAddr = owner ? getAddress(owner) : await signer.getAddress();
4552
- const need = BigInt(amount || 0n);
4553
- if (need === 0n) return { approved: false, tx: null };
4554
- const current = await erc.allowance(ownerAddr, spenderAddr);
4555
- const cur = BigInt(current.toString());
4556
- if (cur >= need) return { approved: false, tx: null };
4557
- const tx = await erc.approve(spenderAddr, need);
4558
- const rcpt = await tx.wait();
4559
- return { approved: true, tx, receipt: rcpt };
4560
- }
4561
- async function createSubDAO({ signer, factoryAddress, name, description, accessModel, minStakeAmount, burnAmount, sxxx }) {
4562
- if (!signer) throw new SageSDKError(CODES.INVALID_ARGS, "signer required");
4563
- if (!factoryAddress) throw new SageSDKError(CODES.INVALID_ARGS, "factoryAddress required");
4564
- const provider = signer.provider;
4565
- if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "signer.provider required");
4566
- if (sxxx && BigInt(burnAmount || 0n) > 0n) {
4567
- await ensureSxxxBurnAllowance({ signer, sxxx, spender: factoryAddress, amount: BigInt(burnAmount) });
4568
- }
4569
- const fac = new Contract(getAddress(factoryAddress), ABI.FactoryWrite, signer);
4570
- const args = [
4571
- String(name || ""),
4572
- String(description || ""),
4573
- Number(accessModel ?? 0),
4574
- BigInt(minStakeAmount ?? 0n),
4575
- BigInt(burnAmount ?? 0n)
4576
- ];
4577
- let tx;
4578
- try {
4579
- tx = await fac.createSubDAO(...args);
4580
- } catch (err) {
4581
- tx = await fac.createSubDAO(...args, { gasLimit: 8e6 });
4582
- }
4583
- const receipt = await tx.wait();
4584
- const iface = new Interface(ABI.Factory);
4585
- let subdao = null, governor = null, timelock = null, treasury = null, registry = null;
4586
- for (const log of receipt.logs || []) {
4587
- try {
4588
- const parsed = iface.parseLog(log);
4589
- if (parsed && parsed.name === "SubDAOGovernanceDeployed") {
4590
- subdao = getAddress(parsed.args.subDAO || parsed.args[0]);
4591
- governor = getAddress(parsed.args.governor || parsed.args[1]);
4592
- timelock = getAddress(parsed.args.timelock || parsed.args[2]);
4593
- treasury = getAddress(parsed.args.treasury || parsed.args[3]);
4594
- } else if (parsed && parsed.name === "SubDAOCreated") {
4595
- const maybe = parsed.args.registry || parsed.args[2];
4596
- if (maybe) registry = getAddress(maybe);
4597
- if (!subdao) subdao = getAddress(parsed.args.subDAO || parsed.args[0]);
5466
+ const ifaceDep = new Interface(["function bootstrapDepositWei() view returns (uint256)"]);
5467
+ const retDep = await provider.call({ to: getAddress(out.governor), data: ifaceDep.encodeFunctionData("bootstrapDepositWei", []) }).catch(() => null);
5468
+ if (retDep && retDep !== "0x") out.depositWei = BigInt(retDep);
4598
5469
  }
4599
5470
  } catch (_) {
4600
5471
  }
5472
+ return out;
4601
5473
  }
5474
+ };
5475
+ }
5476
+ });
5477
+
5478
+ // src/governance/intents.js
5479
+ var require_intents = __commonJS({
5480
+ "src/governance/intents.js"(exports2, module2) {
5481
+ var { Interface, getAddress } = __require("ethers");
5482
+ var { SageSDKError, CODES } = require_errors();
5483
+ function normalise(addr, label) {
4602
5484
  try {
4603
- if (subdao && !registry) {
4604
- const sub = new Contract(subdao, ABI.SubDAO, provider);
4605
- const reg = await sub.promptRegistry().catch(() => null);
4606
- if (reg && /^0x/.test(String(reg))) registry = getAddress(reg);
4607
- }
4608
- } catch (_) {
4609
- }
4610
- return { tx, receipt, subdao, governor, timelock, treasury, registry };
4611
- }
4612
- async function makeOperator({ signer, subdao, operator, grantAdmin = false }) {
4613
- if (!signer) throw new SageSDKError(CODES.INVALID_ARGS, "signer required");
4614
- const provider = signer.provider;
4615
- if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "signer.provider required");
4616
- const addr = normalise(subdao, "subdao");
4617
- const sub = new Contract(addr, ABI.SubDAO, provider);
4618
- const governor = getAddress(await sub.governor());
4619
- const timelock = getAddress(await sub.timelock());
4620
- const TL_ABI = [
4621
- "function PROPOSER_ROLE() view returns (bytes32)",
4622
- "function EXECUTOR_ROLE() view returns (bytes32)",
4623
- "function DEFAULT_ADMIN_ROLE() view returns (bytes32)",
4624
- "function grantRole(bytes32,address)",
4625
- "function revokeRole(bytes32,address)"
4626
- ];
4627
- const tl = new Contract(timelock, TL_ABI, signer);
4628
- const [PR, ER, AR] = await Promise.all([
4629
- tl.PROPOSER_ROLE(),
4630
- tl.EXECUTOR_ROLE(),
4631
- tl.DEFAULT_ADMIN_ROLE().catch(() => null)
4632
- ]);
4633
- const ops = [];
4634
- ops.push(await tl.grantRole(PR, getAddress(operator)));
4635
- ops.push(await tl.grantRole(ER, getAddress(operator)));
4636
- ops.push(await tl.revokeRole(PR, governor));
4637
- if (grantAdmin && AR) ops.push(await tl.grantRole(AR, getAddress(operator)));
4638
- const receipts = [];
4639
- for (const tx of ops) {
4640
- receipts.push(await tx.wait());
5485
+ return getAddress(addr);
5486
+ } catch {
5487
+ throw new SageSDKError(CODES.INVALID_ARGS, `${label} required/invalid`);
4641
5488
  }
4642
- return { governor, timelock, operator: getAddress(operator), txs: ops, receipts };
4643
5489
  }
4644
- async function createOperatorSubDAO({ signer, factoryAddress, operator, name, description, accessModel, minStakeAmount, burnAmount, sxxx, grantAdmin = false }) {
4645
- const created = await createSubDAO({ signer, factoryAddress, name, description, accessModel, minStakeAmount, burnAmount, sxxx });
4646
- if (!created.subdao) throw new SageSDKError(CODES.UNKNOWN, "createSubDAO did not emit SubDAO address");
4647
- const op = await makeOperator({ signer, subdao: created.subdao, operator, grantAdmin });
4648
- return { ...created, operatorResult: op };
4649
- }
4650
- module2.exports.ensureSxxxBurnAllowance = ensureSxxxBurnAllowance;
4651
- module2.exports.createSubDAO = createSubDAO;
4652
- module2.exports.makeOperator = makeOperator;
4653
- module2.exports.createOperatorSubDAO = createOperatorSubDAO;
5490
+ var GovIface = new Interface([
5491
+ "function setQuorumVotes(uint256)",
5492
+ "function setVotingPeriodBlocks(uint32)",
5493
+ "function setDepositOnlyMode(bool)",
5494
+ // Legacy (deprecated)
5495
+ "function updateQuorumNumerator(uint256)"
5496
+ ]);
5497
+ var RegIface = new Interface([
5498
+ "function setForkPolicy(uint8)",
5499
+ "function updatePromptByGovernance(string,string)",
5500
+ "function addPrompt(string,bytes32[],string,string,uint8)"
5501
+ ]);
5502
+ var CfgIface = new Interface([
5503
+ "function setPremiumRevSplit(address,uint16,uint16,uint16)",
5504
+ "function setCreatorStakeBps(address,uint256)"
5505
+ ]);
5506
+ module2.exports = {
5507
+ // Governor targets
5508
+ buildSetQuorumVotesTx: ({ governor, quorumVotes }) => ({ to: normalise(governor, "governor"), data: GovIface.encodeFunctionData("setQuorumVotes", [BigInt(quorumVotes)]), value: 0n }),
5509
+ buildSetVotingPeriodTx: ({ governor, blocks }) => ({ to: normalise(governor, "governor"), data: GovIface.encodeFunctionData("setVotingPeriodBlocks", [Number(blocks)]), value: 0n }),
5510
+ buildSetDepositOnlyTx: ({ governor, enabled }) => ({ to: normalise(governor, "governor"), data: GovIface.encodeFunctionData("setDepositOnlyMode", [!!enabled]), value: 0n }),
5511
+ // Legacy (deprecated) - use buildSetQuorumVotesTx instead
5512
+ buildUpdateQuorumTx: ({ governor, quorumPercent }) => ({ to: normalise(governor, "governor"), data: GovIface.encodeFunctionData("updateQuorumNumerator", [Number(quorumPercent)]), value: 0n }),
5513
+ // Registry targets
5514
+ buildSetForkPolicyTx: ({ registry, policy }) => ({ to: normalise(registry, "registry"), data: RegIface.encodeFunctionData("setForkPolicy", [Number(policy)]), value: 0n }),
5515
+ buildUpdatePromptByGovernanceTx: ({ registry, key, cid }) => ({ to: normalise(registry, "registry"), data: RegIface.encodeFunctionData("updatePromptByGovernance", [String(key), String(cid)]), value: 0n }),
5516
+ buildAddPromptTx: ({ registry, title, tags = [], contentCID, metadataCID = "", category = 0 }) => ({ to: normalise(registry, "registry"), data: RegIface.encodeFunctionData("addPrompt", [String(title), tags, String(contentCID), String(metadataCID), Number(category)]), value: 0n }),
5517
+ // GovernanceConfig targets
5518
+ buildSetPremiumSplitTx: ({ config, subdao, treasuryBps, protocolBps, creatorBps }) => ({ to: normalise(config, "config"), data: CfgIface.encodeFunctionData("setPremiumRevSplit", [normalise(subdao, "subdao"), Number(treasuryBps), Number(protocolBps), Number(creatorBps)]), value: 0n }),
5519
+ buildSetCreatorStakeBpsTx: ({ config, subdao, bps }) => ({ to: normalise(config, "config"), data: CfgIface.encodeFunctionData("setCreatorStakeBps", [normalise(subdao, "subdao"), Number(bps)]), value: 0n })
5520
+ };
4654
5521
  }
4655
5522
  });
4656
5523
 
@@ -4696,7 +5563,6 @@ var require_templates = __commonJS({
4696
5563
  signer,
4697
5564
  subdao: subdaoAddr,
4698
5565
  operator,
4699
- stakeAmount,
4700
5566
  cooldownSeconds,
4701
5567
  depositOnly = true
4702
5568
  }) {
@@ -4714,10 +5580,6 @@ var require_templates = __commonJS({
4714
5580
  const operatorHasExecutor = await tl.hasRole(executorRole, operatorAddress).catch(() => false);
4715
5581
  if (!operatorHasExecutor) actions.push(() => tl.grantRole(executorRole, operatorAddress));
4716
5582
  const governorContract = new Contract(governor, abi.Governor, signer);
4717
- if (stakeAmount != null) {
4718
- const value = BigInt(stakeAmount);
4719
- actions.push(() => governorContract.setProposalStakeRequired(value));
4720
- }
4721
5583
  if (cooldownSeconds != null) {
4722
5584
  const value = BigInt(cooldownSeconds);
4723
5585
  actions.push(() => governorContract.setProposalCooldown(value));
@@ -4732,7 +5594,6 @@ var require_templates = __commonJS({
4732
5594
  provider,
4733
5595
  signer,
4734
5596
  subdao: subdaoAddr,
4735
- stakeAmount,
4736
5597
  cooldownSeconds,
4737
5598
  anyoneExec = true,
4738
5599
  depositOnly = false
@@ -4752,10 +5613,6 @@ var require_templates = __commonJS({
4752
5613
  if (!hasZeroExec) actions.push(() => tl.grantRole(executorRole, ZeroAddress));
4753
5614
  }
4754
5615
  const governorContract = new Contract(governor, abi.Governor, signer);
4755
- if (stakeAmount != null) {
4756
- const value = BigInt(stakeAmount);
4757
- actions.push(() => governorContract.setProposalStakeRequired(value));
4758
- }
4759
5616
  if (cooldownSeconds != null) {
4760
5617
  const value = BigInt(cooldownSeconds);
4761
5618
  actions.push(() => governorContract.setProposalCooldown(value));
@@ -5360,8 +6217,7 @@ var require_operations = __commonJS({
5360
6217
  signer,
5361
6218
  governor,
5362
6219
  timelock = null,
5363
- subdao = null,
5364
- sxxxToken = null
6220
+ subdao = null
5365
6221
  }) {
5366
6222
  if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
5367
6223
  if (!governor) throw new SageSDKError(CODES.INVALID_ARGS, "governor required");
@@ -5388,29 +6244,11 @@ var require_operations = __commonJS({
5388
6244
  let mode = null;
5389
6245
  let eligible = null;
5390
6246
  if (resolvedSubDAO) {
5391
- const subdaoContract = new Contract(resolvedSubDAO, ["function getGovernanceMode() view returns (uint8)", "function isEligibleMember(address) view returns (bool)", "function stakeToken() view returns (address)"], provider);
6247
+ const subdaoContract = new Contract(resolvedSubDAO, ["function getGovernanceMode() view returns (uint8)", "function isEligibleMember(address) view returns (bool)"], provider);
5392
6248
  mode = await subdaoContract.getGovernanceMode().catch(() => null);
5393
6249
  if (mode === 0 && signerAddr) {
5394
6250
  eligible = await subdaoContract.isEligibleMember(signerAddr).catch(() => null);
5395
6251
  }
5396
- if (!sxxxToken) {
5397
- sxxxToken = await subdaoContract.stakeToken().catch(() => null);
5398
- if (sxxxToken === ZeroAddress) sxxxToken = null;
5399
- }
5400
- }
5401
- if (!sxxxToken) {
5402
- sxxxToken = await governorContract.sxxxToken().catch(() => null);
5403
- if (sxxxToken === ZeroAddress) sxxxToken = null;
5404
- }
5405
- let balance = null;
5406
- let allowance = null;
5407
- let authorizedBurner = null;
5408
- const stakeRequired = sxxxToken ? BigInt(50) * BigInt(10) ** BigInt(18) : null;
5409
- if (sxxxToken && signerAddr) {
5410
- const tokenContract = new Contract(sxxxToken, ["function balanceOf(address) view returns (uint256)", "function allowance(address,address) view returns (uint256)", "function authorizedBurners(address) view returns (bool)"], provider);
5411
- balance = await tokenContract.balanceOf(signerAddr).catch(() => null);
5412
- allowance = await tokenContract.allowance(signerAddr, govAddr).catch(() => null);
5413
- authorizedBurner = await tokenContract.authorizedBurners(govAddr).catch(() => null);
5414
6252
  }
5415
6253
  let cooldownSeconds = null;
5416
6254
  let cooldownRemaining = null;
@@ -5440,13 +6278,6 @@ var require_operations = __commonJS({
5440
6278
  subdao: resolvedSubDAO,
5441
6279
  governanceMode: mode,
5442
6280
  eligibleMember: eligible,
5443
- stake: {
5444
- token: sxxxToken,
5445
- requiredWei: stakeRequired,
5446
- balanceWei: balance,
5447
- allowanceWei: allowance,
5448
- governorAuthorizedBurner: authorizedBurner
5449
- },
5450
6281
  timelock
5451
6282
  };
5452
6283
  }
@@ -5881,13 +6712,16 @@ var require_factory = __commonJS({
5881
6712
  accessModel: Number(template[2]),
5882
6713
  forkPolicy: Number(template[3]),
5883
6714
  membershipPolicy: Number(template[4]),
5884
- minStakeAmount: BigInt(template[5].toString()),
6715
+ // template[5] was minStakeAmount (deprecated)
5885
6716
  burnAmount: BigInt(template[6].toString()),
5886
6717
  votingDelay: BigInt(template[7].toString()),
5887
6718
  votingPeriod: BigInt(template[8].toString()),
5888
6719
  proposalThreshold: BigInt(template[9].toString()),
6720
+ // template[10] is quorumVotes (absolute token amount, e.g., 100e18 = 100 SXXX)
6721
+ quorumVotes: BigInt(template[10].toString()),
5889
6722
  quorumPercentage: Number(template[10]),
5890
- proposalStakeRequired: BigInt(template[11].toString()),
6723
+ // deprecated alias
6724
+ // template[11] was proposalStakeRequired (deprecated)
5891
6725
  proposalCooldownSeconds: BigInt(template[12].toString()),
5892
6726
  isActive: Boolean(template[13])
5893
6727
  });
@@ -5908,13 +6742,16 @@ var require_factory = __commonJS({
5908
6742
  accessModel: Number(template[2]),
5909
6743
  forkPolicy: Number(template[3]),
5910
6744
  membershipPolicy: Number(template[4]),
5911
- minStakeAmount: BigInt(template[5].toString()),
6745
+ // template[5] was minStakeAmount (deprecated)
5912
6746
  burnAmount: BigInt(template[6].toString()),
5913
6747
  votingDelay: BigInt(template[7].toString()),
5914
6748
  votingPeriod: BigInt(template[8].toString()),
5915
6749
  proposalThreshold: BigInt(template[9].toString()),
6750
+ // template[10] is quorumVotes (absolute token amount, e.g., 100e18 = 100 SXXX)
6751
+ quorumVotes: BigInt(template[10].toString()),
5916
6752
  quorumPercentage: Number(template[10]),
5917
- proposalStakeRequired: BigInt(template[11].toString()),
6753
+ // deprecated alias
6754
+ // template[11] was proposalStakeRequired (deprecated)
5918
6755
  proposalCooldownSeconds: BigInt(template[12].toString()),
5919
6756
  isActive: Boolean(template[13]),
5920
6757
  usage: usage != null ? BigInt(usage.toString()) : null,
@@ -5922,18 +6759,19 @@ var require_factory = __commonJS({
5922
6759
  };
5923
6760
  }
5924
6761
  var FactoryWriteInterface = new Interface(ABI.FactoryWrite);
5925
- function buildCreateSubDAOTx({ factory, name, description, accessModel, minStakeAmount, burnAmount }) {
6762
+ function buildCreateSubDAOTx({ factory, name, description, accessModel, burnAmount }) {
5926
6763
  const addr = normalise(factory, "factory");
5927
6764
  const payload = FactoryWriteInterface.encodeFunctionData("createSubDAO", [
5928
6765
  String(name),
5929
6766
  String(description),
5930
6767
  Number(accessModel),
5931
- BigInt(minStakeAmount),
6768
+ 0n,
6769
+ // minStakeAmount deprecated
5932
6770
  BigInt(burnAmount)
5933
6771
  ]);
5934
6772
  return { to: addr, data: payload, value: 0n };
5935
6773
  }
5936
- function buildCreateSubDAOWithStableTx({ factory, name, description, accessModel, minStakeAmount, permit }) {
6774
+ function buildCreateSubDAOWithStableTx({ factory, name, description, accessModel, permit }) {
5937
6775
  const addr = normalise(factory, "factory");
5938
6776
  if (!permit) throw new SageSDKError(CODES.INVALID_ARGS, "permit required for stable creation");
5939
6777
  const tuple = [
@@ -5947,7 +6785,8 @@ var require_factory = __commonJS({
5947
6785
  String(name),
5948
6786
  String(description),
5949
6787
  Number(accessModel),
5950
- BigInt(minStakeAmount),
6788
+ 0n,
6789
+ // minStakeAmount deprecated
5951
6790
  tuple
5952
6791
  ]);
5953
6792
  return { to: addr, data: payload, value: 0n };
@@ -5989,30 +6828,37 @@ var require_factory = __commonJS({
5989
6828
  name,
5990
6829
  description,
5991
6830
  accessModel,
5992
- minStakeAmount,
5993
6831
  burnAmount,
5994
6832
  votingDelay,
5995
6833
  votingPeriod,
5996
6834
  proposalThreshold,
6835
+ quorumVotes,
5997
6836
  quorumPercentage,
6837
+ // deprecated alias for quorumVotes
5998
6838
  initialForkPolicy,
5999
6839
  initialMembershipPolicy,
6000
- profileCID
6840
+ profileCID,
6841
+ manifestCID,
6842
+ manifestVersion
6001
6843
  }) {
6002
6844
  const addr = normalise(factory, "factory");
6845
+ const quorum = quorumVotes ?? quorumPercentage;
6003
6846
  const payload = FactoryWriteInterface.encodeFunctionData("createSubDAOWithParams", [
6004
6847
  String(name),
6005
6848
  String(description),
6006
6849
  Number(accessModel),
6007
- BigInt(minStakeAmount),
6850
+ 0n,
6851
+ // minStakeAmount deprecated
6008
6852
  BigInt(burnAmount),
6009
6853
  BigInt(votingDelay),
6010
6854
  BigInt(votingPeriod),
6011
6855
  BigInt(proposalThreshold),
6012
- BigInt(quorumPercentage),
6856
+ BigInt(quorum),
6013
6857
  Number(initialForkPolicy),
6014
6858
  Number(initialMembershipPolicy),
6015
- String(profileCID ?? "")
6859
+ String(profileCID ?? ""),
6860
+ String(manifestCID ?? ""),
6861
+ String(manifestVersion ?? "")
6016
6862
  ]);
6017
6863
  return { to: addr, data: payload, value: 0n };
6018
6864
  }
@@ -6021,7 +6867,6 @@ var require_factory = __commonJS({
6021
6867
  name,
6022
6868
  description,
6023
6869
  accessModel,
6024
- minStakeAmount,
6025
6870
  operatorExecutor,
6026
6871
  operatorAdmin,
6027
6872
  permit
@@ -6039,7 +6884,8 @@ var require_factory = __commonJS({
6039
6884
  String(name),
6040
6885
  String(description),
6041
6886
  Number(accessModel),
6042
- BigInt(minStakeAmount),
6887
+ 0n,
6888
+ // minStakeAmount deprecated
6043
6889
  normalise(operatorExecutor, "operatorExecutor"),
6044
6890
  normalise(operatorAdmin, "operatorAdmin"),
6045
6891
  tuple
@@ -6051,7 +6897,6 @@ var require_factory = __commonJS({
6051
6897
  name,
6052
6898
  description,
6053
6899
  accessModel,
6054
- minStakeAmount,
6055
6900
  operatorExecutor,
6056
6901
  operatorAdmin,
6057
6902
  anyoneExec = false,
@@ -6071,7 +6916,8 @@ var require_factory = __commonJS({
6071
6916
  String(name),
6072
6917
  String(description),
6073
6918
  Number(accessModel),
6074
- BigInt(minStakeAmount),
6919
+ 0n,
6920
+ // minStakeAmount deprecated
6075
6921
  normalise(operatorExecutor, "operatorExecutor"),
6076
6922
  normalise(operatorAdmin, "operatorAdmin"),
6077
6923
  Boolean(anyoneExec),
@@ -7007,7 +7853,7 @@ var require_ipns = __commonJS({
7007
7853
  }
7008
7854
  const worker = workerConfig ? {
7009
7855
  baseUrl: removeTrailingSlash(workerConfig.baseUrl || workerConfig.url || workerConfig.workerBaseUrl || ""),
7010
- challengePath: ensureLeadingSlash(workerConfig.challengePath || workerConfig.challenge || "/auth/challenge"),
7856
+ challengePath: ensureLeadingSlash(workerConfig.challengePath || workerConfig.challenge || "/ipfs/auth/challenge"),
7011
7857
  publishPath: ensureLeadingSlash(workerConfig.publishPath || workerConfig.publish || "/ipns/publish"),
7012
7858
  token: workerConfig.token || null,
7013
7859
  getAuth: workerConfig.getAuth || null,
@@ -7439,51 +8285,345 @@ var require_access = __commonJS({
7439
8285
  if (!response || !response.decryptedData) {
7440
8286
  throw new Error("[personal] Lit decryption failed: missing decryptedData");
7441
8287
  }
7442
- return uint8arrayToString(response.decryptedData, "utf8");
8288
+ return uint8arrayToString(response.decryptedData, "utf8");
8289
+ }
8290
+ module2.exports = {
8291
+ resolveEncryptedResource,
8292
+ decryptWithLit
8293
+ };
8294
+ }
8295
+ });
8296
+
8297
+ // src/personal/index.js
8298
+ var require_personal = __commonJS({
8299
+ "src/personal/index.js"(exports2, module2) {
8300
+ var { Interface } = __require("ethers");
8301
+ var ABI = require_abi();
8302
+ var { normaliseAddress, toBytes32Key } = require_helpers();
8303
+ function buildCreatePersonalRegistryTx({ factory, policy }) {
8304
+ const to = normaliseAddress(factory, "factory");
8305
+ const iface = new Interface(ABI.PersonalLibraryFacet);
8306
+ const data = iface.encodeFunctionData("createPersonalRegistry", [policy >>> 0]);
8307
+ return { to, data, value: 0n };
8308
+ }
8309
+ function buildSetPriceTx({ marketplace, key, price }) {
8310
+ const to = normaliseAddress(marketplace, "marketplace");
8311
+ const k = toBytes32Key(key);
8312
+ const iface = new Interface(ABI.PersonalMarketplace);
8313
+ const data = iface.encodeFunctionData("setPrice", [k, BigInt(price)]);
8314
+ return { to, data, value: 0n };
8315
+ }
8316
+ function buildBuyTx({ marketplace, creator, key, expectedPrice, deadline }) {
8317
+ const to = normaliseAddress(marketplace, "marketplace");
8318
+ const iface = new Interface(ABI.PersonalMarketplace);
8319
+ const data = iface.encodeFunctionData("purchase", [
8320
+ normaliseAddress(creator, "creator"),
8321
+ toBytes32Key(key),
8322
+ BigInt(expectedPrice),
8323
+ BigInt(deadline)
8324
+ ]);
8325
+ return { to, data, value: 0n };
8326
+ }
8327
+ module2.exports = {
8328
+ buildCreatePersonalRegistryTx,
8329
+ buildSetPriceTx,
8330
+ buildBuyTx,
8331
+ ...require_receipt(),
8332
+ ...require_access()
8333
+ };
8334
+ }
8335
+ });
8336
+
8337
+ // src/premium/index.js
8338
+ var require_premium = __commonJS({
8339
+ "src/premium/index.js"(exports2, module2) {
8340
+ var axios = __require("axios");
8341
+ var { getAddress } = __require("ethers");
8342
+ function safeGetAddress(value) {
8343
+ try {
8344
+ return getAddress(value);
8345
+ } catch {
8346
+ return null;
8347
+ }
8348
+ }
8349
+ function mapSafe(list, mapper) {
8350
+ const out = [];
8351
+ for (const item of list || []) {
8352
+ try {
8353
+ const v = mapper(item);
8354
+ if (v != null) out.push(v);
8355
+ } catch {
8356
+ }
8357
+ }
8358
+ return out;
8359
+ }
8360
+ async function query(url, document, variables) {
8361
+ if (!url) throw new Error("subgraph url required");
8362
+ const resp = await axios.post(url, { query: document, variables }, { timeout: 1e4 });
8363
+ if (resp.data && resp.data.errors) {
8364
+ throw new Error(resp.data.errors.map((e) => e.message).join("; "));
8365
+ }
8366
+ return resp.data.data;
8367
+ }
8368
+ async function listPremiumPrompts({ url, subdao = null, first = 50, skip = 0, orderBy = "blockTimestamp", orderDirection = "desc" }) {
8369
+ const safeOrderBy = ["blockTimestamp", "price"].includes(orderBy) ? orderBy : "blockTimestamp";
8370
+ const safeOrderDirection = orderDirection === "asc" ? "asc" : "desc";
8371
+ let doc;
8372
+ const variables = {
8373
+ first: Math.min(Math.max(1, Number(first || 50)), 200),
8374
+ skip: Number(skip || 0)
8375
+ };
8376
+ if (subdao) {
8377
+ const addr = safeGetAddress(subdao);
8378
+ if (!addr) throw new Error("invalid subdao address");
8379
+ variables.subdao = addr.toLowerCase();
8380
+ doc = `
8381
+ query($subdao: Bytes!, $first: Int!, $skip: Int!) {
8382
+ premiumPrompts(
8383
+ where: { subdao: $subdao }
8384
+ first: $first
8385
+ skip: $skip
8386
+ orderBy: ${safeOrderBy}
8387
+ orderDirection: ${safeOrderDirection}
8388
+ ) {
8389
+ id
8390
+ cidHash
8391
+ subdao
8392
+ price
8393
+ manifestCID
8394
+ blockNumber
8395
+ blockTimestamp
8396
+ transactionHash
8397
+ }
8398
+ }
8399
+ `;
8400
+ } else {
8401
+ doc = `
8402
+ query($first: Int!, $skip: Int!) {
8403
+ premiumPrompts(
8404
+ first: $first
8405
+ skip: $skip
8406
+ orderBy: ${safeOrderBy}
8407
+ orderDirection: ${safeOrderDirection}
8408
+ ) {
8409
+ id
8410
+ cidHash
8411
+ subdao
8412
+ price
8413
+ manifestCID
8414
+ blockNumber
8415
+ blockTimestamp
8416
+ transactionHash
8417
+ }
8418
+ }
8419
+ `;
8420
+ }
8421
+ const data = await query(url, doc, variables);
8422
+ return mapSafe(data?.premiumPrompts, (p) => {
8423
+ const sub = safeGetAddress(p.subdao);
8424
+ if (!sub) return null;
8425
+ return {
8426
+ id: String(p.id),
8427
+ cidHash: String(p.cidHash),
8428
+ subdao: sub,
8429
+ price: BigInt(String(p.price || "0")),
8430
+ manifestCID: p.manifestCID || null,
8431
+ blockNumber: Number(p.blockNumber || 0),
8432
+ blockTimestamp: Number(p.blockTimestamp || 0),
8433
+ transactionHash: p.transactionHash || null
8434
+ };
8435
+ });
8436
+ }
8437
+ async function getPremiumPrompt({ url, cidHash }) {
8438
+ const id2 = cidHash.toLowerCase().startsWith("0x") ? cidHash.toLowerCase() : `0x${cidHash.toLowerCase()}`;
8439
+ const doc = `
8440
+ query($id: ID!) {
8441
+ premiumPrompt(id: $id) {
8442
+ id
8443
+ cidHash
8444
+ subdao
8445
+ price
8446
+ manifestCID
8447
+ blockNumber
8448
+ blockTimestamp
8449
+ transactionHash
8450
+ }
8451
+ premiumPromptStats(id: $id) {
8452
+ totalRevenue
8453
+ purchaseCount
8454
+ uniqueBuyers
8455
+ currentHolders
8456
+ }
8457
+ }
8458
+ `;
8459
+ const data = await query(url, doc, { id: id2 });
8460
+ const p = data?.premiumPrompt;
8461
+ if (!p) return null;
8462
+ const sub = safeGetAddress(p.subdao);
8463
+ if (!sub) return null;
8464
+ const stats = data?.premiumPromptStats || {};
8465
+ return {
8466
+ id: String(p.id),
8467
+ cidHash: String(p.cidHash),
8468
+ subdao: sub,
8469
+ price: BigInt(String(p.price || "0")),
8470
+ manifestCID: p.manifestCID || null,
8471
+ blockNumber: Number(p.blockNumber || 0),
8472
+ blockTimestamp: Number(p.blockTimestamp || 0),
8473
+ transactionHash: p.transactionHash || null,
8474
+ // Stats
8475
+ totalRevenue: stats.totalRevenue ? BigInt(String(stats.totalRevenue)) : null,
8476
+ purchaseCount: stats.purchaseCount ? BigInt(String(stats.purchaseCount)) : null,
8477
+ uniqueBuyers: stats.uniqueBuyers ? BigInt(String(stats.uniqueBuyers)) : null,
8478
+ currentHolders: stats.currentHolders ? BigInt(String(stats.currentHolders)) : null
8479
+ };
8480
+ }
8481
+ async function listPremiumPurchases({ url, cidHash, first = 50, skip = 0 }) {
8482
+ const normalizedCidHash = cidHash.toLowerCase().startsWith("0x") ? cidHash.toLowerCase() : `0x${cidHash.toLowerCase()}`;
8483
+ const doc = `
8484
+ query($cidHash: Bytes!, $first: Int!, $skip: Int!) {
8485
+ premiumPurchases(
8486
+ where: { cidHash: $cidHash }
8487
+ first: $first
8488
+ skip: $skip
8489
+ orderBy: blockTimestamp
8490
+ orderDirection: desc
8491
+ ) {
8492
+ id
8493
+ cidHash
8494
+ buyer
8495
+ price
8496
+ treasury
8497
+ blockNumber
8498
+ blockTimestamp
8499
+ transactionHash
8500
+ }
8501
+ }
8502
+ `;
8503
+ const data = await query(url, doc, {
8504
+ cidHash: normalizedCidHash,
8505
+ first: Math.min(Math.max(1, Number(first || 50)), 200),
8506
+ skip: Number(skip || 0)
8507
+ });
8508
+ return mapSafe(data?.premiumPurchases, (p) => {
8509
+ const buyer = safeGetAddress(p.buyer);
8510
+ const treasury = safeGetAddress(p.treasury);
8511
+ if (!buyer || !treasury) return null;
8512
+ return {
8513
+ id: String(p.id),
8514
+ cidHash: String(p.cidHash),
8515
+ buyer,
8516
+ price: BigInt(String(p.price || "0")),
8517
+ treasury,
8518
+ blockNumber: Number(p.blockNumber || 0),
8519
+ blockTimestamp: Number(p.blockTimestamp || 0),
8520
+ transactionHash: p.transactionHash || null
8521
+ };
8522
+ });
8523
+ }
8524
+ async function hasPurchased({ url, cidHash, holder }) {
8525
+ const normalizedCidHash = cidHash.toLowerCase().startsWith("0x") ? cidHash.toLowerCase() : `0x${cidHash.toLowerCase()}`;
8526
+ const normalizedHolder = holder.toLowerCase();
8527
+ const id2 = `${normalizedCidHash}-${normalizedHolder}`;
8528
+ const doc = `
8529
+ query($id: ID!) {
8530
+ premiumHolderBalance(id: $id) {
8531
+ balance
8532
+ }
8533
+ }
8534
+ `;
8535
+ const data = await query(url, doc, { id: id2 });
8536
+ const balance = data?.premiumHolderBalance?.balance;
8537
+ return balance ? BigInt(String(balance)) > 0n : false;
8538
+ }
8539
+ async function getHolderBalance({ url, cidHash, holder }) {
8540
+ const normalizedCidHash = cidHash.toLowerCase().startsWith("0x") ? cidHash.toLowerCase() : `0x${cidHash.toLowerCase()}`;
8541
+ const normalizedHolder = holder.toLowerCase();
8542
+ const id2 = `${normalizedCidHash}-${normalizedHolder}`;
8543
+ const doc = `
8544
+ query($id: ID!) {
8545
+ premiumHolderBalance(id: $id) {
8546
+ balance
8547
+ updatedAt
8548
+ }
8549
+ }
8550
+ `;
8551
+ const data = await query(url, doc, { id: id2 });
8552
+ const row = data?.premiumHolderBalance;
8553
+ if (!row) return 0n;
8554
+ return BigInt(String(row.balance || "0"));
8555
+ }
8556
+ async function listHolders({ url, cidHash, first = 100, skip = 0 }) {
8557
+ const normalizedCidHash = cidHash.toLowerCase().startsWith("0x") ? cidHash.toLowerCase() : `0x${cidHash.toLowerCase()}`;
8558
+ const doc = `
8559
+ query($cidHash: Bytes!, $first: Int!, $skip: Int!) {
8560
+ premiumHolderBalances(
8561
+ where: { cidHash: $cidHash, balance_gt: "0" }
8562
+ first: $first
8563
+ skip: $skip
8564
+ orderBy: balance
8565
+ orderDirection: desc
8566
+ ) {
8567
+ id
8568
+ cidHash
8569
+ holder
8570
+ balance
8571
+ updatedAt
8572
+ }
7443
8573
  }
7444
- module2.exports = {
7445
- resolveEncryptedResource,
7446
- decryptWithLit
7447
- };
7448
- }
7449
- });
7450
-
7451
- // src/personal/index.js
7452
- var require_personal = __commonJS({
7453
- "src/personal/index.js"(exports2, module2) {
7454
- var { Interface } = __require("ethers");
7455
- var ABI = require_abi();
7456
- var { normaliseAddress, toBytes32Key } = require_helpers();
7457
- function buildCreatePersonalRegistryTx({ factory, policy }) {
7458
- const to = normaliseAddress(factory, "factory");
7459
- const iface = new Interface(ABI.PersonalLibraryFacet);
7460
- const data = iface.encodeFunctionData("createPersonalRegistry", [policy >>> 0]);
7461
- return { to, data, value: 0n };
8574
+ `;
8575
+ const data = await query(url, doc, {
8576
+ cidHash: normalizedCidHash,
8577
+ first: Math.min(Math.max(1, Number(first || 100)), 500),
8578
+ skip: Number(skip || 0)
8579
+ });
8580
+ return mapSafe(data?.premiumHolderBalances, (h) => {
8581
+ const holder = safeGetAddress(h.holder);
8582
+ if (!holder) return null;
8583
+ return {
8584
+ id: String(h.id),
8585
+ cidHash: String(h.cidHash),
8586
+ holder,
8587
+ balance: BigInt(String(h.balance || "0")),
8588
+ updatedAt: Number(h.updatedAt || 0)
8589
+ };
8590
+ });
7462
8591
  }
7463
- function buildSetPriceTx({ marketplace, key, price }) {
7464
- const to = normaliseAddress(marketplace, "marketplace");
7465
- const k = toBytes32Key(key);
7466
- const iface = new Interface(ABI.PersonalMarketplace);
7467
- const data = iface.encodeFunctionData("setPrice", [k, BigInt(price)]);
7468
- return { to, data, value: 0n };
8592
+ async function getRevSplit({ url, subdao }) {
8593
+ const addr = safeGetAddress(subdao);
8594
+ if (!addr) throw new Error("invalid subdao address");
8595
+ const doc = `
8596
+ query($id: ID!) {
8597
+ premiumRevSplit(id: $id) {
8598
+ id
8599
+ subdao
8600
+ treasuryBps
8601
+ protocolBps
8602
+ creatorBps
8603
+ updatedAt
8604
+ }
7469
8605
  }
7470
- function buildBuyTx({ marketplace, creator, key, expectedPrice, deadline }) {
7471
- const to = normaliseAddress(marketplace, "marketplace");
7472
- const iface = new Interface(ABI.PersonalMarketplace);
7473
- const data = iface.encodeFunctionData("purchase", [
7474
- normaliseAddress(creator, "creator"),
7475
- toBytes32Key(key),
7476
- BigInt(expectedPrice),
7477
- BigInt(deadline)
7478
- ]);
7479
- return { to, data, value: 0n };
8606
+ `;
8607
+ const data = await query(url, doc, { id: addr.toLowerCase() });
8608
+ const split = data?.premiumRevSplit;
8609
+ if (!split) return null;
8610
+ return {
8611
+ id: String(split.id),
8612
+ subdao: safeGetAddress(split.subdao) || addr,
8613
+ treasuryBps: Number(split.treasuryBps || 0),
8614
+ protocolBps: Number(split.protocolBps || 0),
8615
+ creatorBps: Number(split.creatorBps || 0),
8616
+ updatedAt: Number(split.updatedAt || 0)
8617
+ };
7480
8618
  }
7481
8619
  module2.exports = {
7482
- buildCreatePersonalRegistryTx,
7483
- buildSetPriceTx,
7484
- buildBuyTx,
7485
- ...require_receipt(),
7486
- ...require_access()
8620
+ listPremiumPrompts,
8621
+ getPremiumPrompt,
8622
+ listPremiumPurchases,
8623
+ hasPurchased,
8624
+ getHolderBalance,
8625
+ listHolders,
8626
+ getRevSplit
7487
8627
  };
7488
8628
  }
7489
8629
  });
@@ -8655,6 +9795,335 @@ var require_contributions = __commonJS({
8655
9795
  }
8656
9796
  });
8657
9797
 
9798
+ // src/browser/reputation.js
9799
+ var require_reputation = __commonJS({
9800
+ "src/browser/reputation.js"(exports2, module2) {
9801
+ var { getAddress } = require_utils();
9802
+ var subgraph = require_subgraph2();
9803
+ function safeGetAddress(value) {
9804
+ try {
9805
+ return getAddress(value);
9806
+ } catch {
9807
+ return null;
9808
+ }
9809
+ }
9810
+ function clampInt(value, { min = 1, max = 100, fallback = 100 } = {}) {
9811
+ const n = Number(value);
9812
+ if (!Number.isFinite(n)) return fallback;
9813
+ return Math.min(max, Math.max(min, Math.trunc(n)));
9814
+ }
9815
+ function toBigIntString(value, fallback = "0") {
9816
+ try {
9817
+ if (typeof value === "bigint") return value.toString();
9818
+ if (typeof value === "number") return String(Math.trunc(value));
9819
+ const str = String(value).trim();
9820
+ if (!str) return fallback;
9821
+ if (!/^-?\d+$/.test(str)) return fallback;
9822
+ return str;
9823
+ } catch {
9824
+ return fallback;
9825
+ }
9826
+ }
9827
+ function mapSafe(list, mapper) {
9828
+ const out = [];
9829
+ for (const item of list || []) {
9830
+ try {
9831
+ const v = mapper(item);
9832
+ if (v != null) out.push(v);
9833
+ } catch {
9834
+ }
9835
+ }
9836
+ return out;
9837
+ }
9838
+ async function getBadgesByRecipient({ url, recipient, first = 50 }) {
9839
+ if (!url) throw new Error("subgraph url required");
9840
+ if (!recipient) throw new Error("recipient required");
9841
+ const addr = safeGetAddress(recipient);
9842
+ if (!addr) throw new Error("invalid recipient address");
9843
+ const doc = `
9844
+ query($recipient: Bytes!, $first: Int!) {
9845
+ soulboundBadges(
9846
+ where: { recipient: $recipient }
9847
+ first: $first
9848
+ orderBy: blockTimestamp
9849
+ orderDirection: desc
9850
+ ) {
9851
+ id
9852
+ contract
9853
+ badgeId
9854
+ recipient
9855
+ evidenceURI
9856
+ blockNumber
9857
+ blockTimestamp
9858
+ transactionHash
9859
+ }
9860
+ }
9861
+ `;
9862
+ const data = await subgraph.query(url, doc, {
9863
+ recipient: addr.toLowerCase(),
9864
+ first: clampInt(first, { min: 1, max: 100, fallback: 50 })
9865
+ });
9866
+ return mapSafe(data?.soulboundBadges, (row) => {
9867
+ const contract = safeGetAddress(row.contract);
9868
+ const recipientAddr = safeGetAddress(row.recipient);
9869
+ if (!contract || !recipientAddr) return null;
9870
+ return {
9871
+ id: String(row.id),
9872
+ contract,
9873
+ badgeId: toBigIntString(row.badgeId, "0"),
9874
+ recipient: recipientAddr,
9875
+ evidenceURI: row.evidenceURI || null,
9876
+ blockNumber: Number(row.blockNumber || 0),
9877
+ blockTimestamp: Number(row.blockTimestamp || 0),
9878
+ transactionHash: row.transactionHash || null
9879
+ };
9880
+ });
9881
+ }
9882
+ async function listContributions({ url, contributor, subdao, first = 50, orderBy = "updatedAt", orderDirection = "desc" }) {
9883
+ if (!url) throw new Error("subgraph url required");
9884
+ const filters = [];
9885
+ if (contributor) {
9886
+ const addr = safeGetAddress(contributor);
9887
+ if (!addr) throw new Error("invalid contributor address");
9888
+ filters.push(`contributor: "${addr.toLowerCase()}"`);
9889
+ }
9890
+ if (subdao) {
9891
+ const addr = safeGetAddress(subdao);
9892
+ if (!addr) throw new Error("invalid subdao address");
9893
+ filters.push(`dao: "${addr.toLowerCase()}"`);
9894
+ }
9895
+ const where = filters.length ? `where: { ${filters.join(", ")} }` : "";
9896
+ const safeOrderBy = ["updatedAt", "createdAt"].includes(orderBy) ? orderBy : "updatedAt";
9897
+ const safeOrderDirection = orderDirection === "asc" ? "asc" : "desc";
9898
+ const doc = `
9899
+ query($first: Int!) {
9900
+ promptContributions(
9901
+ ${where}
9902
+ first: $first
9903
+ orderBy: ${safeOrderBy}
9904
+ orderDirection: ${safeOrderDirection}
9905
+ ) {
9906
+ id
9907
+ dao
9908
+ promptKey
9909
+ contributor
9910
+ cid
9911
+ proposalId
9912
+ forVotes
9913
+ againstVotes
9914
+ abstainVotes
9915
+ uniqueVoters
9916
+ quorum
9917
+ fromBounty
9918
+ bountyId
9919
+ badgeId
9920
+ badgeEvidenceURI
9921
+ createdAt
9922
+ updatedAt
9923
+ transactionHash
9924
+ }
9925
+ }
9926
+ `;
9927
+ const data = await subgraph.query(url, doc, {
9928
+ first: clampInt(first, { min: 1, max: 100, fallback: 50 })
9929
+ });
9930
+ return mapSafe(data?.promptContributions, (row) => {
9931
+ const dao = safeGetAddress(row.dao);
9932
+ const contributorAddr = safeGetAddress(row.contributor);
9933
+ if (!dao) return null;
9934
+ return {
9935
+ id: String(row.id),
9936
+ dao,
9937
+ promptKey: String(row.promptKey),
9938
+ contributor: contributorAddr || "0x0000000000000000000000000000000000000000",
9939
+ cid: String(row.cid),
9940
+ timestamp: Number(row.updatedAt || 0),
9941
+ createdAt: Number(row.createdAt || 0),
9942
+ updatedAt: Number(row.updatedAt || 0),
9943
+ transactionHash: row.transactionHash || null,
9944
+ proposalId: row.proposalId != null ? toBigIntString(row.proposalId) : null,
9945
+ forVotes: row.forVotes != null ? toBigIntString(row.forVotes) : null,
9946
+ againstVotes: row.againstVotes != null ? toBigIntString(row.againstVotes) : null,
9947
+ abstainVotes: row.abstainVotes != null ? toBigIntString(row.abstainVotes) : null,
9948
+ uniqueVoters: row.uniqueVoters != null ? Number(row.uniqueVoters) : null,
9949
+ quorum: row.quorum != null ? toBigIntString(row.quorum) : null,
9950
+ fromBounty: Boolean(row.fromBounty),
9951
+ bountyId: row.bountyId != null ? toBigIntString(row.bountyId) : null,
9952
+ badgeId: row.badgeId != null ? toBigIntString(row.badgeId) : null,
9953
+ badgeEvidenceURI: row.badgeEvidenceURI || null
9954
+ };
9955
+ });
9956
+ }
9957
+ function computeAggregates(contributions) {
9958
+ if (!contributions || contributions.length === 0) {
9959
+ return {
9960
+ totalContributions: 0,
9961
+ uniqueContributors: 0,
9962
+ bountyOriginated: 0,
9963
+ governanceOriginated: 0,
9964
+ totalForVotes: "0",
9965
+ totalAgainstVotes: "0",
9966
+ averageVoterCount: 0,
9967
+ badgeCount: 0
9968
+ };
9969
+ }
9970
+ const contributorSet = /* @__PURE__ */ new Set();
9971
+ let bountyOriginated = 0;
9972
+ let governanceOriginated = 0;
9973
+ let totalForVotes = 0n;
9974
+ let totalAgainstVotes = 0n;
9975
+ let voterCountSum = 0;
9976
+ let voterCountN = 0;
9977
+ let badgeCount = 0;
9978
+ for (const c of contributions) {
9979
+ if (c.contributor) contributorSet.add(c.contributor.toLowerCase());
9980
+ if (c.fromBounty) {
9981
+ bountyOriginated++;
9982
+ } else {
9983
+ governanceOriginated++;
9984
+ }
9985
+ if (c.forVotes != null) {
9986
+ try {
9987
+ totalForVotes += BigInt(c.forVotes);
9988
+ } catch {
9989
+ }
9990
+ }
9991
+ if (c.againstVotes != null) {
9992
+ try {
9993
+ totalAgainstVotes += BigInt(c.againstVotes);
9994
+ } catch {
9995
+ }
9996
+ }
9997
+ if (c.uniqueVoters != null) {
9998
+ voterCountSum += c.uniqueVoters;
9999
+ voterCountN++;
10000
+ }
10001
+ if (c.badgeId != null) badgeCount++;
10002
+ }
10003
+ return {
10004
+ totalContributions: contributions.length,
10005
+ uniqueContributors: contributorSet.size,
10006
+ bountyOriginated,
10007
+ governanceOriginated,
10008
+ totalForVotes: totalForVotes.toString(),
10009
+ totalAgainstVotes: totalAgainstVotes.toString(),
10010
+ averageVoterCount: voterCountN > 0 ? Math.round(voterCountSum / voterCountN) : 0,
10011
+ badgeCount
10012
+ };
10013
+ }
10014
+ async function getByAddress({
10015
+ url,
10016
+ address,
10017
+ subdao,
10018
+ includeBadges = true,
10019
+ includeContributions = true,
10020
+ first = 100
10021
+ } = {}) {
10022
+ if (!url) throw new Error("subgraph url required");
10023
+ if (!address) throw new Error("address required");
10024
+ const addr = safeGetAddress(address);
10025
+ if (!addr) throw new Error("invalid address");
10026
+ const subdaoAddr = subdao ? safeGetAddress(subdao) : null;
10027
+ if (subdao && !subdaoAddr) throw new Error("invalid subdao address");
10028
+ const limit = clampInt(first, { min: 1, max: 200, fallback: 100 });
10029
+ let badges = void 0;
10030
+ if (includeBadges) {
10031
+ badges = await getBadgesByRecipient({ url, recipient: addr, first: limit });
10032
+ }
10033
+ let contributionAggregates = void 0;
10034
+ let lastContributionAt = null;
10035
+ if (includeContributions) {
10036
+ const rows = await listContributions({
10037
+ url,
10038
+ contributor: addr,
10039
+ subdao: subdaoAddr || void 0,
10040
+ first: limit,
10041
+ orderBy: "updatedAt",
10042
+ orderDirection: "desc"
10043
+ });
10044
+ if (rows && rows.length) {
10045
+ const updatedAt = rows[0]?.updatedAt ?? rows[0]?.timestamp ?? null;
10046
+ lastContributionAt = updatedAt != null ? Number(updatedAt) : null;
10047
+ if (!Number.isFinite(lastContributionAt)) lastContributionAt = null;
10048
+ }
10049
+ contributionAggregates = computeAggregates(rows || []);
10050
+ }
10051
+ const signals = {
10052
+ badgeCount: includeBadges ? badges ? badges.length : 0 : null,
10053
+ totalContributions: includeContributions ? contributionAggregates ? contributionAggregates.totalContributions : 0 : null,
10054
+ governanceOriginated: includeContributions ? contributionAggregates ? contributionAggregates.governanceOriginated : 0 : null,
10055
+ bountyOriginated: includeContributions ? contributionAggregates ? contributionAggregates.bountyOriginated : 0 : null,
10056
+ lastContributionAt: includeContributions ? lastContributionAt : null
10057
+ };
10058
+ const out = {
10059
+ address: addr,
10060
+ subdao: subdaoAddr || null,
10061
+ signals
10062
+ };
10063
+ if (includeBadges) out.badges = badges;
10064
+ if (includeContributions) {
10065
+ out.contributionAggregates = contributionAggregates;
10066
+ out.lastContributionAt = lastContributionAt;
10067
+ }
10068
+ return out;
10069
+ }
10070
+ function normalizeGateInt(value, name) {
10071
+ if (value === void 0 || value === null) return null;
10072
+ const n = Number(value);
10073
+ if (!Number.isFinite(n) || n < 0) {
10074
+ throw new Error(`invalid gate: ${name}`);
10075
+ }
10076
+ return Math.trunc(n);
10077
+ }
10078
+ function normalizeRequiredBadgeIds(value) {
10079
+ const list = Array.isArray(value) ? value : value != null ? [value] : [];
10080
+ const out = [];
10081
+ for (const item of list) {
10082
+ const id2 = toBigIntString(item, "");
10083
+ if (id2) out.push(id2);
10084
+ }
10085
+ return out;
10086
+ }
10087
+ function evaluate(result, gates = {}) {
10088
+ const reasons = [];
10089
+ const minBadges = normalizeGateInt(gates.minBadges, "minBadges");
10090
+ const minContributions = normalizeGateInt(gates.minContributions, "minContributions");
10091
+ const requiredBadges = normalizeRequiredBadgeIds(gates.requireBadges ?? gates.requireBadge);
10092
+ const badgeCount = Number(result?.signals?.badgeCount ?? (result?.badges?.length ?? 0));
10093
+ const totalContributions = Number(result?.signals?.totalContributions ?? (result?.contributionAggregates?.totalContributions ?? 0));
10094
+ if (minBadges != null) {
10095
+ if (!Number.isFinite(badgeCount)) {
10096
+ reasons.push("badges_unavailable");
10097
+ } else if (badgeCount < minBadges) {
10098
+ reasons.push(`minBadges:${badgeCount}<${minBadges}`);
10099
+ }
10100
+ }
10101
+ if (minContributions != null) {
10102
+ if (!Number.isFinite(totalContributions)) {
10103
+ reasons.push("contributions_unavailable");
10104
+ } else if (totalContributions < minContributions) {
10105
+ reasons.push(`minContributions:${totalContributions}<${minContributions}`);
10106
+ }
10107
+ }
10108
+ if (requiredBadges.length) {
10109
+ const have = new Set((result?.badges || []).map((b) => toBigIntString(b?.badgeId, "")).filter(Boolean));
10110
+ for (const req2 of requiredBadges) {
10111
+ if (!have.has(req2)) reasons.push(`missingBadge:${req2}`);
10112
+ }
10113
+ }
10114
+ return { ok: reasons.length === 0, reasons };
10115
+ }
10116
+ module2.exports = {
10117
+ getByAddress,
10118
+ evaluate,
10119
+ // Expose lower-level functions for advanced usage
10120
+ getBadgesByRecipient,
10121
+ listContributions,
10122
+ computeAggregates
10123
+ };
10124
+ }
10125
+ });
10126
+
8658
10127
  // src/votingMultiplier/index.js
8659
10128
  var require_votingMultiplier = __commonJS({
8660
10129
  "src/votingMultiplier/index.js"(exports2, module2) {
@@ -12900,6 +14369,7 @@ var require_src = __commonJS({
12900
14369
  var ipns = require_ipns();
12901
14370
  var token = require_token();
12902
14371
  var personal = require_personal();
14372
+ var premium = require_premium();
12903
14373
  var subgraph = require_subgraph();
12904
14374
  var privateTx = require_privateTx();
12905
14375
  var safe = require_safe();
@@ -12908,6 +14378,7 @@ var require_src = __commonJS({
12908
14378
  var boost = require_boost();
12909
14379
  var bounty = require_bounty();
12910
14380
  var contributions = require_contributions();
14381
+ var reputation = require_reputation();
12911
14382
  var votingMultiplier = require_votingMultiplier();
12912
14383
  var auction = require_auction();
12913
14384
  var wallet = require_wallet();
@@ -12953,6 +14424,7 @@ var require_src = __commonJS({
12953
14424
  SageEchoExecutor,
12954
14425
  token,
12955
14426
  personal,
14427
+ premium,
12956
14428
  treasury,
12957
14429
  boost,
12958
14430
  // bond module removed; bonds deprecated in CLI/SDK
@@ -12960,6 +14432,7 @@ var require_src = __commonJS({
12960
14432
  utils: { ...utils, privateTx, safe, time },
12961
14433
  bounty,
12962
14434
  contributions,
14435
+ reputation,
12963
14436
  votingMultiplier,
12964
14437
  auction,
12965
14438
  wallet: Object.assign(wallet, {