@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"(exports, module) {
15
15
  module.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)",
@@ -1316,8 +1341,9 @@ var require_ipfs = __commonJS({
1316
1341
  baseUrl: removeTrailingSlash(options.workerBaseUrl || env.SAGE_IPFS_WORKER_URL || ""),
1317
1342
  uploadUrl: removeTrailingSlash(options.workerUploadUrl || env.SAGE_IPFS_UPLOAD_URL || ""),
1318
1343
  token: options.workerUploadToken || env.SAGE_IPFS_UPLOAD_TOKEN || "",
1319
- challengePath: options.workerChallengePath || env.SAGE_IPFS_WORKER_CHALLENGE_PATH || "/auth/challenge",
1344
+ challengePath: options.workerChallengePath || env.SAGE_IPFS_WORKER_CHALLENGE_PATH || "/ipfs/auth/challenge",
1320
1345
  uploadPath: options.workerUploadPath || env.SAGE_IPFS_WORKER_UPLOAD_PATH || "/ipfs/upload",
1346
+ uploadRawPath: options.workerUploadRawPath || env.SAGE_IPFS_WORKER_UPLOAD_RAW_PATH || "/ipfs/upload-raw",
1321
1347
  pinPath: options.workerPinPath || env.SAGE_IPFS_WORKER_PIN_PATH || "/ipfs/pin",
1322
1348
  warmPath: options.workerWarmPath || env.SAGE_IPFS_WORKER_WARM_PATH || "/ipfs/warm",
1323
1349
  pinUrl: removeTrailingSlash(options.workerPinUrl || env.SAGE_IPFS_WORKER_PIN_URL || ""),
@@ -1325,11 +1351,13 @@ var require_ipfs = __commonJS({
1325
1351
  signer: options.workerSigner || options.signer || null,
1326
1352
  getAuth: options.workerGetAuth || null,
1327
1353
  address: options.workerAddress || env.SAGE_SANDBOX_ADDRESS || "",
1328
- discoveryEventsPath: options.workerDiscoveryEventsPath || env.SAGE_IPFS_WORKER_DISCOVERY_EVENTS_PATH || "/discover/events",
1329
- discoveryMcpPath: options.workerDiscoveryMcpPath || env.SAGE_IPFS_WORKER_DISCOVERY_MCP_PATH || "/discover/mcp",
1330
- discoveryLaunchPath: options.workerDiscoveryLaunchPath || env.SAGE_IPFS_WORKER_DISCOVERY_LAUNCH_PATH || "/discover/launch",
1354
+ // All event types route to /trends/usage - worker tracks usage for trend scoring
1355
+ discoveryEventsPath: options.workerDiscoveryEventsPath || env.SAGE_IPFS_WORKER_DISCOVERY_EVENTS_PATH || "/trends/usage",
1356
+ discoveryMcpPath: options.workerDiscoveryMcpPath || env.SAGE_IPFS_WORKER_DISCOVERY_MCP_PATH || "/trends/usage",
1357
+ discoveryLaunchPath: options.workerDiscoveryLaunchPath || env.SAGE_IPFS_WORKER_DISCOVERY_LAUNCH_PATH || "/trends/usage",
1331
1358
  discoveryFeedPath: options.workerDiscoveryFeedPath || env.SAGE_IPFS_WORKER_DISCOVERY_FEED_PATH || "/discover",
1332
- discoveryMetricsPath: options.workerDiscoveryMetricsPath || env.SAGE_IPFS_WORKER_DISCOVERY_METRICS_PATH || "/discover/metrics",
1359
+ // Metrics uses /discover/stats which returns index statistics
1360
+ discoveryMetricsPath: options.workerDiscoveryMetricsPath || env.SAGE_IPFS_WORKER_DISCOVERY_METRICS_PATH || "/discover/stats",
1333
1361
  governanceReportPath: options.workerGovernanceReportPath || env.SAGE_IPFS_WORKER_GOVERNANCE_REPORT_PATH || "/governance/report",
1334
1362
  governanceReportsPath: options.workerGovernanceReportsPath || env.SAGE_IPFS_WORKER_GOVERNANCE_REPORTS_PATH || "/governance/reports",
1335
1363
  governanceReviewPath: options.workerGovernanceReviewPath || env.SAGE_IPFS_WORKER_GOVERNANCE_REVIEW_PATH || "/governance/report/review",
@@ -1363,8 +1391,9 @@ var require_ipfs = __commonJS({
1363
1391
  const base = workerBaseUrl();
1364
1392
  const ensure = (path) => path.startsWith("/") ? path : `/${path}`;
1365
1393
  if (base) {
1366
- if (kind === "challenge") return `${base}${ensure(config.worker.challengePath || "/auth/challenge")}`;
1394
+ if (kind === "challenge") return `${base}${ensure(config.worker.challengePath || "/ipfs/auth/challenge")}`;
1367
1395
  if (kind === "upload") return `${base}${ensure(config.worker.uploadPath || "/ipfs/upload")}`;
1396
+ if (kind === "upload-raw") return `${base}${ensure(config.worker.uploadRawPath || "/ipfs/upload-raw")}`;
1368
1397
  if (kind === "pin") return `${base}${ensure(config.worker.pinPath || "/ipfs/pin")}`;
1369
1398
  if (kind === "warm") return `${base}${ensure(config.worker.warmPath || "/ipfs/warm")}`;
1370
1399
  if (kind === "status") return `${base}${ensure(config.worker.pinPath || "/ipfs/pin")}/${cid}`;
@@ -1399,6 +1428,26 @@ var require_ipfs = __commonJS({
1399
1428
  const response = await axiosInstance.get(url, { headers, params, timeout: config.timeoutMs });
1400
1429
  return response?.data;
1401
1430
  }
1431
+ async function withRetry(fn, { retries = 2, baseMs = 500 } = {}) {
1432
+ let attempt = 0;
1433
+ let lastErr;
1434
+ while (attempt <= retries) {
1435
+ try {
1436
+ return await fn();
1437
+ } catch (e) {
1438
+ lastErr = e;
1439
+ const status = e?.response?.status;
1440
+ if (status && status >= 400 && status < 500 && status !== 429) {
1441
+ throw e;
1442
+ }
1443
+ if (attempt === retries) break;
1444
+ const delay = baseMs * Math.pow(2, attempt);
1445
+ await new Promise((r) => setTimeout(r, delay));
1446
+ attempt++;
1447
+ }
1448
+ }
1449
+ throw lastErr;
1450
+ }
1402
1451
  async function buildWorkerAuthHeaders(extraHeaders = {}) {
1403
1452
  if (config.worker.token) {
1404
1453
  return { headers: { ...extraHeaders, Authorization: `Bearer ${config.worker.token}` }, auth: null };
@@ -1471,6 +1520,51 @@ var require_ipfs = __commonJS({
1471
1520
  if (warm || config.shouldWarm) await warmGateways(cid, { gateways });
1472
1521
  return { cid, provider: "worker", response: response?.data || null };
1473
1522
  }
1523
+ async function uploadRawViaWorker(content, { name, warm, gateways } = {}) {
1524
+ const { headers, auth } = await buildWorkerAuthHeaders({ "Content-Type": "application/json" });
1525
+ const body = { content };
1526
+ if (auth) body.auth = auth;
1527
+ if (name) body.name = name;
1528
+ const response = await axiosInstance.post(workerUrl("upload-raw"), body, { headers, timeout: config.timeoutMs });
1529
+ const cid = response?.data?.cid || response?.data?.IpfsHash;
1530
+ if (!cid) {
1531
+ throw new Error("Worker response missing cid");
1532
+ }
1533
+ if (warm || config.shouldWarm) await warmGateways(cid, { gateways });
1534
+ return { cid, provider: "worker", response: response?.data || null };
1535
+ }
1536
+ async function uploadRawJson(content, options2 = {}) {
1537
+ if (!content || typeof content !== "object") {
1538
+ throw new Error("content object required");
1539
+ }
1540
+ const { providers, provider, warm, gateways, name = "manifest.json" } = options2;
1541
+ const order = resolveProviderOrder(providers || provider);
1542
+ if (!order.length) {
1543
+ throw new Error("No IPFS providers configured");
1544
+ }
1545
+ const errors = [];
1546
+ for (const candidate of order) {
1547
+ try {
1548
+ if (candidate === "worker") {
1549
+ return await uploadRawViaWorker(content, { name, warm, gateways });
1550
+ }
1551
+ if (candidate === "pinata") {
1552
+ return await uploadToPinata(content, { filename: name, warm, gateways });
1553
+ }
1554
+ if (candidate === "web3") {
1555
+ return await uploadToWeb3Storage(content, { warm, gateways });
1556
+ }
1557
+ if (candidate === "simulate") {
1558
+ return await uploadSimulated(content, { warm, gateways });
1559
+ }
1560
+ } catch (error2) {
1561
+ errors.push({ provider: candidate, error: error2 });
1562
+ }
1563
+ }
1564
+ const error = new Error("All IPFS providers failed");
1565
+ error.details = errors;
1566
+ throw error;
1567
+ }
1474
1568
  async function uploadToPinata(payload, { filename = "payload.json", metadata, warm, gateways } = {}) {
1475
1569
  if (!hasPinataCreds()) {
1476
1570
  throw new Error("Missing Pinata credentials");
@@ -1675,28 +1769,28 @@ var require_ipfs = __commonJS({
1675
1769
  }
1676
1770
  async function recordDiscoveryEvent(event = {}) {
1677
1771
  if (!event || typeof event !== "object") throw new Error("event required");
1678
- const promptId = typeof event.promptId === "string" ? event.promptId.trim() : "";
1679
- if (!promptId) throw new Error("promptId required");
1680
- const payload = { ...event, promptId };
1681
- return postWorkerJson("discoveryEventsPath", "/discover/events", payload);
1682
- }
1683
- async function recordMcpUsageEvent({ promptId, metadata, address } = {}) {
1684
- const id = typeof promptId === "string" ? promptId.trim() : "";
1685
- if (!id) throw new Error("promptId required");
1686
- const payload = { promptId: id };
1772
+ const cid = typeof event.promptId === "string" ? event.promptId.trim() : typeof event.cid === "string" ? event.cid.trim() : "";
1773
+ if (!cid) throw new Error("promptId or cid required");
1774
+ const payload = { ...event, cid };
1775
+ return withRetry(() => postWorkerJson("discoveryEventsPath", "/trends/usage", payload));
1776
+ }
1777
+ async function recordMcpUsageEvent({ promptId, cid, metadata, address } = {}) {
1778
+ const id = typeof promptId === "string" ? promptId.trim() : typeof cid === "string" ? cid.trim() : "";
1779
+ if (!id) throw new Error("promptId or cid required");
1780
+ const payload = { cid: id };
1687
1781
  if (metadata && typeof metadata === "object") payload.metadata = metadata;
1688
1782
  const actor = address || config.worker.address;
1689
1783
  if (actor) payload.address = actor;
1690
- return postWorkerJson("discoveryMcpPath", "/discover/mcp", payload);
1784
+ return withRetry(() => postWorkerJson("discoveryMcpPath", "/trends/usage", payload));
1691
1785
  }
1692
- async function recordLaunchEvent({ promptId, metadata, address } = {}) {
1693
- const id = typeof promptId === "string" ? promptId.trim() : "";
1694
- if (!id) throw new Error("promptId required");
1695
- const payload = { promptId: id };
1786
+ async function recordLaunchEvent({ promptId, cid, metadata, address } = {}) {
1787
+ const id = typeof promptId === "string" ? promptId.trim() : typeof cid === "string" ? cid.trim() : "";
1788
+ if (!id) throw new Error("promptId or cid required");
1789
+ const payload = { cid: id };
1696
1790
  if (metadata && typeof metadata === "object") payload.metadata = metadata;
1697
1791
  const actor = address || config.worker.address;
1698
1792
  if (actor) payload.address = actor;
1699
- return postWorkerJson("discoveryLaunchPath", "/discover/launch", payload);
1793
+ return withRetry(() => postWorkerJson("discoveryLaunchPath", "/trends/usage", payload));
1700
1794
  }
1701
1795
  async function submitGovernanceReport(payload = {}) {
1702
1796
  if (!payload || typeof payload !== "object") throw new Error("payload required");
@@ -1758,6 +1852,7 @@ var require_ipfs = __commonJS({
1758
1852
  uploadPrompt,
1759
1853
  uploadJson,
1760
1854
  uploadText,
1855
+ uploadRawJson,
1761
1856
  downloadJson,
1762
1857
  warmGateways,
1763
1858
  buildGatewayUrls: (cid, extra) => buildGatewayUrls(cid, config.gateway, extra),
@@ -1784,7 +1879,7 @@ var require_ipfs = __commonJS({
1784
1879
  const timeoutMs = Number(options.timeoutMs ?? env.SAGE_IPFS_TIMEOUT_MS ?? 15e3);
1785
1880
  const challengePath = ensureLeadingSlash(
1786
1881
  options.workerChallengePath || env.SAGE_IPFS_WORKER_CHALLENGE_PATH,
1787
- "/auth/challenge"
1882
+ "/ipfs/auth/challenge"
1788
1883
  );
1789
1884
  const warmPath = ensureLeadingSlash(
1790
1885
  options.workerWarmPath || env.SAGE_IPFS_WORKER_WARM_PATH,
@@ -1851,6 +1946,335 @@ var require_ipfs = __commonJS({
1851
1946
  }
1852
1947
  });
1853
1948
 
1949
+ // src/browser/reputation.js
1950
+ var require_reputation = __commonJS({
1951
+ "src/browser/reputation.js"(exports, module) {
1952
+ var { getAddress } = require_utils();
1953
+ var subgraph = require_subgraph();
1954
+ function safeGetAddress(value) {
1955
+ try {
1956
+ return getAddress(value);
1957
+ } catch {
1958
+ return null;
1959
+ }
1960
+ }
1961
+ function clampInt(value, { min = 1, max = 100, fallback = 100 } = {}) {
1962
+ const n = Number(value);
1963
+ if (!Number.isFinite(n)) return fallback;
1964
+ return Math.min(max, Math.max(min, Math.trunc(n)));
1965
+ }
1966
+ function toBigIntString(value, fallback = "0") {
1967
+ try {
1968
+ if (typeof value === "bigint") return value.toString();
1969
+ if (typeof value === "number") return String(Math.trunc(value));
1970
+ const str = String(value).trim();
1971
+ if (!str) return fallback;
1972
+ if (!/^-?\d+$/.test(str)) return fallback;
1973
+ return str;
1974
+ } catch {
1975
+ return fallback;
1976
+ }
1977
+ }
1978
+ function mapSafe(list, mapper) {
1979
+ const out = [];
1980
+ for (const item of list || []) {
1981
+ try {
1982
+ const v = mapper(item);
1983
+ if (v != null) out.push(v);
1984
+ } catch {
1985
+ }
1986
+ }
1987
+ return out;
1988
+ }
1989
+ async function getBadgesByRecipient({ url, recipient, first = 50 }) {
1990
+ if (!url) throw new Error("subgraph url required");
1991
+ if (!recipient) throw new Error("recipient required");
1992
+ const addr = safeGetAddress(recipient);
1993
+ if (!addr) throw new Error("invalid recipient address");
1994
+ const doc = `
1995
+ query($recipient: Bytes!, $first: Int!) {
1996
+ soulboundBadges(
1997
+ where: { recipient: $recipient }
1998
+ first: $first
1999
+ orderBy: blockTimestamp
2000
+ orderDirection: desc
2001
+ ) {
2002
+ id
2003
+ contract
2004
+ badgeId
2005
+ recipient
2006
+ evidenceURI
2007
+ blockNumber
2008
+ blockTimestamp
2009
+ transactionHash
2010
+ }
2011
+ }
2012
+ `;
2013
+ const data = await subgraph.query(url, doc, {
2014
+ recipient: addr.toLowerCase(),
2015
+ first: clampInt(first, { min: 1, max: 100, fallback: 50 })
2016
+ });
2017
+ return mapSafe(data?.soulboundBadges, (row) => {
2018
+ const contract = safeGetAddress(row.contract);
2019
+ const recipientAddr = safeGetAddress(row.recipient);
2020
+ if (!contract || !recipientAddr) return null;
2021
+ return {
2022
+ id: String(row.id),
2023
+ contract,
2024
+ badgeId: toBigIntString(row.badgeId, "0"),
2025
+ recipient: recipientAddr,
2026
+ evidenceURI: row.evidenceURI || null,
2027
+ blockNumber: Number(row.blockNumber || 0),
2028
+ blockTimestamp: Number(row.blockTimestamp || 0),
2029
+ transactionHash: row.transactionHash || null
2030
+ };
2031
+ });
2032
+ }
2033
+ async function listContributions({ url, contributor, subdao, first = 50, orderBy = "updatedAt", orderDirection = "desc" }) {
2034
+ if (!url) throw new Error("subgraph url required");
2035
+ const filters = [];
2036
+ if (contributor) {
2037
+ const addr = safeGetAddress(contributor);
2038
+ if (!addr) throw new Error("invalid contributor address");
2039
+ filters.push(`contributor: "${addr.toLowerCase()}"`);
2040
+ }
2041
+ if (subdao) {
2042
+ const addr = safeGetAddress(subdao);
2043
+ if (!addr) throw new Error("invalid subdao address");
2044
+ filters.push(`dao: "${addr.toLowerCase()}"`);
2045
+ }
2046
+ const where = filters.length ? `where: { ${filters.join(", ")} }` : "";
2047
+ const safeOrderBy = ["updatedAt", "createdAt"].includes(orderBy) ? orderBy : "updatedAt";
2048
+ const safeOrderDirection = orderDirection === "asc" ? "asc" : "desc";
2049
+ const doc = `
2050
+ query($first: Int!) {
2051
+ promptContributions(
2052
+ ${where}
2053
+ first: $first
2054
+ orderBy: ${safeOrderBy}
2055
+ orderDirection: ${safeOrderDirection}
2056
+ ) {
2057
+ id
2058
+ dao
2059
+ promptKey
2060
+ contributor
2061
+ cid
2062
+ proposalId
2063
+ forVotes
2064
+ againstVotes
2065
+ abstainVotes
2066
+ uniqueVoters
2067
+ quorum
2068
+ fromBounty
2069
+ bountyId
2070
+ badgeId
2071
+ badgeEvidenceURI
2072
+ createdAt
2073
+ updatedAt
2074
+ transactionHash
2075
+ }
2076
+ }
2077
+ `;
2078
+ const data = await subgraph.query(url, doc, {
2079
+ first: clampInt(first, { min: 1, max: 100, fallback: 50 })
2080
+ });
2081
+ return mapSafe(data?.promptContributions, (row) => {
2082
+ const dao = safeGetAddress(row.dao);
2083
+ const contributorAddr = safeGetAddress(row.contributor);
2084
+ if (!dao) return null;
2085
+ return {
2086
+ id: String(row.id),
2087
+ dao,
2088
+ promptKey: String(row.promptKey),
2089
+ contributor: contributorAddr || "0x0000000000000000000000000000000000000000",
2090
+ cid: String(row.cid),
2091
+ timestamp: Number(row.updatedAt || 0),
2092
+ createdAt: Number(row.createdAt || 0),
2093
+ updatedAt: Number(row.updatedAt || 0),
2094
+ transactionHash: row.transactionHash || null,
2095
+ proposalId: row.proposalId != null ? toBigIntString(row.proposalId) : null,
2096
+ forVotes: row.forVotes != null ? toBigIntString(row.forVotes) : null,
2097
+ againstVotes: row.againstVotes != null ? toBigIntString(row.againstVotes) : null,
2098
+ abstainVotes: row.abstainVotes != null ? toBigIntString(row.abstainVotes) : null,
2099
+ uniqueVoters: row.uniqueVoters != null ? Number(row.uniqueVoters) : null,
2100
+ quorum: row.quorum != null ? toBigIntString(row.quorum) : null,
2101
+ fromBounty: Boolean(row.fromBounty),
2102
+ bountyId: row.bountyId != null ? toBigIntString(row.bountyId) : null,
2103
+ badgeId: row.badgeId != null ? toBigIntString(row.badgeId) : null,
2104
+ badgeEvidenceURI: row.badgeEvidenceURI || null
2105
+ };
2106
+ });
2107
+ }
2108
+ function computeAggregates(contributions) {
2109
+ if (!contributions || contributions.length === 0) {
2110
+ return {
2111
+ totalContributions: 0,
2112
+ uniqueContributors: 0,
2113
+ bountyOriginated: 0,
2114
+ governanceOriginated: 0,
2115
+ totalForVotes: "0",
2116
+ totalAgainstVotes: "0",
2117
+ averageVoterCount: 0,
2118
+ badgeCount: 0
2119
+ };
2120
+ }
2121
+ const contributorSet = /* @__PURE__ */ new Set();
2122
+ let bountyOriginated = 0;
2123
+ let governanceOriginated = 0;
2124
+ let totalForVotes = 0n;
2125
+ let totalAgainstVotes = 0n;
2126
+ let voterCountSum = 0;
2127
+ let voterCountN = 0;
2128
+ let badgeCount = 0;
2129
+ for (const c of contributions) {
2130
+ if (c.contributor) contributorSet.add(c.contributor.toLowerCase());
2131
+ if (c.fromBounty) {
2132
+ bountyOriginated++;
2133
+ } else {
2134
+ governanceOriginated++;
2135
+ }
2136
+ if (c.forVotes != null) {
2137
+ try {
2138
+ totalForVotes += BigInt(c.forVotes);
2139
+ } catch {
2140
+ }
2141
+ }
2142
+ if (c.againstVotes != null) {
2143
+ try {
2144
+ totalAgainstVotes += BigInt(c.againstVotes);
2145
+ } catch {
2146
+ }
2147
+ }
2148
+ if (c.uniqueVoters != null) {
2149
+ voterCountSum += c.uniqueVoters;
2150
+ voterCountN++;
2151
+ }
2152
+ if (c.badgeId != null) badgeCount++;
2153
+ }
2154
+ return {
2155
+ totalContributions: contributions.length,
2156
+ uniqueContributors: contributorSet.size,
2157
+ bountyOriginated,
2158
+ governanceOriginated,
2159
+ totalForVotes: totalForVotes.toString(),
2160
+ totalAgainstVotes: totalAgainstVotes.toString(),
2161
+ averageVoterCount: voterCountN > 0 ? Math.round(voterCountSum / voterCountN) : 0,
2162
+ badgeCount
2163
+ };
2164
+ }
2165
+ async function getByAddress({
2166
+ url,
2167
+ address,
2168
+ subdao,
2169
+ includeBadges = true,
2170
+ includeContributions = true,
2171
+ first = 100
2172
+ } = {}) {
2173
+ if (!url) throw new Error("subgraph url required");
2174
+ if (!address) throw new Error("address required");
2175
+ const addr = safeGetAddress(address);
2176
+ if (!addr) throw new Error("invalid address");
2177
+ const subdaoAddr = subdao ? safeGetAddress(subdao) : null;
2178
+ if (subdao && !subdaoAddr) throw new Error("invalid subdao address");
2179
+ const limit = clampInt(first, { min: 1, max: 200, fallback: 100 });
2180
+ let badges = void 0;
2181
+ if (includeBadges) {
2182
+ badges = await getBadgesByRecipient({ url, recipient: addr, first: limit });
2183
+ }
2184
+ let contributionAggregates = void 0;
2185
+ let lastContributionAt = null;
2186
+ if (includeContributions) {
2187
+ const rows = await listContributions({
2188
+ url,
2189
+ contributor: addr,
2190
+ subdao: subdaoAddr || void 0,
2191
+ first: limit,
2192
+ orderBy: "updatedAt",
2193
+ orderDirection: "desc"
2194
+ });
2195
+ if (rows && rows.length) {
2196
+ const updatedAt = rows[0]?.updatedAt ?? rows[0]?.timestamp ?? null;
2197
+ lastContributionAt = updatedAt != null ? Number(updatedAt) : null;
2198
+ if (!Number.isFinite(lastContributionAt)) lastContributionAt = null;
2199
+ }
2200
+ contributionAggregates = computeAggregates(rows || []);
2201
+ }
2202
+ const signals = {
2203
+ badgeCount: includeBadges ? badges ? badges.length : 0 : null,
2204
+ totalContributions: includeContributions ? contributionAggregates ? contributionAggregates.totalContributions : 0 : null,
2205
+ governanceOriginated: includeContributions ? contributionAggregates ? contributionAggregates.governanceOriginated : 0 : null,
2206
+ bountyOriginated: includeContributions ? contributionAggregates ? contributionAggregates.bountyOriginated : 0 : null,
2207
+ lastContributionAt: includeContributions ? lastContributionAt : null
2208
+ };
2209
+ const out = {
2210
+ address: addr,
2211
+ subdao: subdaoAddr || null,
2212
+ signals
2213
+ };
2214
+ if (includeBadges) out.badges = badges;
2215
+ if (includeContributions) {
2216
+ out.contributionAggregates = contributionAggregates;
2217
+ out.lastContributionAt = lastContributionAt;
2218
+ }
2219
+ return out;
2220
+ }
2221
+ function normalizeGateInt(value, name) {
2222
+ if (value === void 0 || value === null) return null;
2223
+ const n = Number(value);
2224
+ if (!Number.isFinite(n) || n < 0) {
2225
+ throw new Error(`invalid gate: ${name}`);
2226
+ }
2227
+ return Math.trunc(n);
2228
+ }
2229
+ function normalizeRequiredBadgeIds(value) {
2230
+ const list = Array.isArray(value) ? value : value != null ? [value] : [];
2231
+ const out = [];
2232
+ for (const item of list) {
2233
+ const id = toBigIntString(item, "");
2234
+ if (id) out.push(id);
2235
+ }
2236
+ return out;
2237
+ }
2238
+ function evaluate(result, gates = {}) {
2239
+ const reasons = [];
2240
+ const minBadges = normalizeGateInt(gates.minBadges, "minBadges");
2241
+ const minContributions = normalizeGateInt(gates.minContributions, "minContributions");
2242
+ const requiredBadges = normalizeRequiredBadgeIds(gates.requireBadges ?? gates.requireBadge);
2243
+ const badgeCount = Number(result?.signals?.badgeCount ?? (result?.badges?.length ?? 0));
2244
+ const totalContributions = Number(result?.signals?.totalContributions ?? (result?.contributionAggregates?.totalContributions ?? 0));
2245
+ if (minBadges != null) {
2246
+ if (!Number.isFinite(badgeCount)) {
2247
+ reasons.push("badges_unavailable");
2248
+ } else if (badgeCount < minBadges) {
2249
+ reasons.push(`minBadges:${badgeCount}<${minBadges}`);
2250
+ }
2251
+ }
2252
+ if (minContributions != null) {
2253
+ if (!Number.isFinite(totalContributions)) {
2254
+ reasons.push("contributions_unavailable");
2255
+ } else if (totalContributions < minContributions) {
2256
+ reasons.push(`minContributions:${totalContributions}<${minContributions}`);
2257
+ }
2258
+ }
2259
+ if (requiredBadges.length) {
2260
+ const have = new Set((result?.badges || []).map((b) => toBigIntString(b?.badgeId, "")).filter(Boolean));
2261
+ for (const req of requiredBadges) {
2262
+ if (!have.has(req)) reasons.push(`missingBadge:${req}`);
2263
+ }
2264
+ }
2265
+ return { ok: reasons.length === 0, reasons };
2266
+ }
2267
+ module.exports = {
2268
+ getByAddress,
2269
+ evaluate,
2270
+ // Expose lower-level functions for advanced usage
2271
+ getBadgesByRecipient,
2272
+ listContributions,
2273
+ computeAggregates
2274
+ };
2275
+ }
2276
+ });
2277
+
1854
2278
  // src/errors/index.js
1855
2279
  var require_errors = __commonJS({
1856
2280
  "src/errors/index.js"(exports, module) {
@@ -2256,6 +2680,7 @@ var require_browser = __commonJS({
2256
2680
  var governance = require_governance();
2257
2681
  var subgraph = require_subgraph();
2258
2682
  var ipfs = require_ipfs();
2683
+ var reputation = require_reputation();
2259
2684
  var serviceErrors = require_errors();
2260
2685
  var { SimpleCache } = require_cache();
2261
2686
  var { retryWithBackoff } = require_retry();
@@ -2276,6 +2701,7 @@ var require_browser = __commonJS({
2276
2701
  // Browser-safe modules (no ethers dependencies)
2277
2702
  subgraph,
2278
2703
  ipfs,
2704
+ reputation,
2279
2705
  // Service utilities
2280
2706
  utils: {
2281
2707
  SimpleCache,