@sage-protocol/sdk 0.1.24 → 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.24",
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",
@@ -9789,6 +9789,335 @@ var require_contributions = __commonJS({
9789
9789
  }
9790
9790
  });
9791
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
+
9792
10121
  // src/votingMultiplier/index.js
9793
10122
  var require_votingMultiplier = __commonJS({
9794
10123
  "src/votingMultiplier/index.js"(exports2, module2) {
@@ -14043,6 +14372,7 @@ var require_src = __commonJS({
14043
14372
  var boost = require_boost();
14044
14373
  var bounty = require_bounty();
14045
14374
  var contributions = require_contributions();
14375
+ var reputation = require_reputation();
14046
14376
  var votingMultiplier = require_votingMultiplier();
14047
14377
  var auction = require_auction();
14048
14378
  var wallet = require_wallet();
@@ -14096,6 +14426,7 @@ var require_src = __commonJS({
14096
14426
  utils: { ...utils, privateTx, safe, time },
14097
14427
  bounty,
14098
14428
  contributions,
14429
+ reputation,
14099
14430
  votingMultiplier,
14100
14431
  auction,
14101
14432
  wallet: Object.assign(wallet, {