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