@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.
@@ -20,7 +20,7 @@ var require_package = __commonJS({
20
20
  "package.json"(exports2, module2) {
21
21
  module2.exports = {
22
22
  name: "@sage-protocol/sdk",
23
- version: "0.1.24",
23
+ version: "0.1.25",
24
24
  description: "Backend-agnostic SDK for interacting with the Sage Protocol (governance, SubDAOs, tokens).",
25
25
  main: "dist/index.cjs",
26
26
  module: "dist/index.mjs",
@@ -9795,6 +9795,335 @@ var require_contributions = __commonJS({
9795
9795
  }
9796
9796
  });
9797
9797
 
9798
+ // src/browser/reputation.js
9799
+ var require_reputation = __commonJS({
9800
+ "src/browser/reputation.js"(exports2, module2) {
9801
+ var { getAddress } = require_utils();
9802
+ var subgraph = require_subgraph2();
9803
+ function safeGetAddress(value) {
9804
+ try {
9805
+ return getAddress(value);
9806
+ } catch {
9807
+ return null;
9808
+ }
9809
+ }
9810
+ function clampInt(value, { min = 1, max = 100, fallback = 100 } = {}) {
9811
+ const n = Number(value);
9812
+ if (!Number.isFinite(n)) return fallback;
9813
+ return Math.min(max, Math.max(min, Math.trunc(n)));
9814
+ }
9815
+ function toBigIntString(value, fallback = "0") {
9816
+ try {
9817
+ if (typeof value === "bigint") return value.toString();
9818
+ if (typeof value === "number") return String(Math.trunc(value));
9819
+ const str = String(value).trim();
9820
+ if (!str) return fallback;
9821
+ if (!/^-?\d+$/.test(str)) return fallback;
9822
+ return str;
9823
+ } catch {
9824
+ return fallback;
9825
+ }
9826
+ }
9827
+ function mapSafe(list, mapper) {
9828
+ const out = [];
9829
+ for (const item of list || []) {
9830
+ try {
9831
+ const v = mapper(item);
9832
+ if (v != null) out.push(v);
9833
+ } catch {
9834
+ }
9835
+ }
9836
+ return out;
9837
+ }
9838
+ async function getBadgesByRecipient({ url, recipient, first = 50 }) {
9839
+ if (!url) throw new Error("subgraph url required");
9840
+ if (!recipient) throw new Error("recipient required");
9841
+ const addr = safeGetAddress(recipient);
9842
+ if (!addr) throw new Error("invalid recipient address");
9843
+ const doc = `
9844
+ query($recipient: Bytes!, $first: Int!) {
9845
+ soulboundBadges(
9846
+ where: { recipient: $recipient }
9847
+ first: $first
9848
+ orderBy: blockTimestamp
9849
+ orderDirection: desc
9850
+ ) {
9851
+ id
9852
+ contract
9853
+ badgeId
9854
+ recipient
9855
+ evidenceURI
9856
+ blockNumber
9857
+ blockTimestamp
9858
+ transactionHash
9859
+ }
9860
+ }
9861
+ `;
9862
+ const data = await subgraph.query(url, doc, {
9863
+ recipient: addr.toLowerCase(),
9864
+ first: clampInt(first, { min: 1, max: 100, fallback: 50 })
9865
+ });
9866
+ return mapSafe(data?.soulboundBadges, (row) => {
9867
+ const contract = safeGetAddress(row.contract);
9868
+ const recipientAddr = safeGetAddress(row.recipient);
9869
+ if (!contract || !recipientAddr) return null;
9870
+ return {
9871
+ id: String(row.id),
9872
+ contract,
9873
+ badgeId: toBigIntString(row.badgeId, "0"),
9874
+ recipient: recipientAddr,
9875
+ evidenceURI: row.evidenceURI || null,
9876
+ blockNumber: Number(row.blockNumber || 0),
9877
+ blockTimestamp: Number(row.blockTimestamp || 0),
9878
+ transactionHash: row.transactionHash || null
9879
+ };
9880
+ });
9881
+ }
9882
+ async function listContributions({ url, contributor, subdao, first = 50, orderBy = "updatedAt", orderDirection = "desc" }) {
9883
+ if (!url) throw new Error("subgraph url required");
9884
+ const filters = [];
9885
+ if (contributor) {
9886
+ const addr = safeGetAddress(contributor);
9887
+ if (!addr) throw new Error("invalid contributor address");
9888
+ filters.push(`contributor: "${addr.toLowerCase()}"`);
9889
+ }
9890
+ if (subdao) {
9891
+ const addr = safeGetAddress(subdao);
9892
+ if (!addr) throw new Error("invalid subdao address");
9893
+ filters.push(`dao: "${addr.toLowerCase()}"`);
9894
+ }
9895
+ const where = filters.length ? `where: { ${filters.join(", ")} }` : "";
9896
+ const safeOrderBy = ["updatedAt", "createdAt"].includes(orderBy) ? orderBy : "updatedAt";
9897
+ const safeOrderDirection = orderDirection === "asc" ? "asc" : "desc";
9898
+ const doc = `
9899
+ query($first: Int!) {
9900
+ promptContributions(
9901
+ ${where}
9902
+ first: $first
9903
+ orderBy: ${safeOrderBy}
9904
+ orderDirection: ${safeOrderDirection}
9905
+ ) {
9906
+ id
9907
+ dao
9908
+ promptKey
9909
+ contributor
9910
+ cid
9911
+ proposalId
9912
+ forVotes
9913
+ againstVotes
9914
+ abstainVotes
9915
+ uniqueVoters
9916
+ quorum
9917
+ fromBounty
9918
+ bountyId
9919
+ badgeId
9920
+ badgeEvidenceURI
9921
+ createdAt
9922
+ updatedAt
9923
+ transactionHash
9924
+ }
9925
+ }
9926
+ `;
9927
+ const data = await subgraph.query(url, doc, {
9928
+ first: clampInt(first, { min: 1, max: 100, fallback: 50 })
9929
+ });
9930
+ return mapSafe(data?.promptContributions, (row) => {
9931
+ const dao = safeGetAddress(row.dao);
9932
+ const contributorAddr = safeGetAddress(row.contributor);
9933
+ if (!dao) return null;
9934
+ return {
9935
+ id: String(row.id),
9936
+ dao,
9937
+ promptKey: String(row.promptKey),
9938
+ contributor: contributorAddr || "0x0000000000000000000000000000000000000000",
9939
+ cid: String(row.cid),
9940
+ timestamp: Number(row.updatedAt || 0),
9941
+ createdAt: Number(row.createdAt || 0),
9942
+ updatedAt: Number(row.updatedAt || 0),
9943
+ transactionHash: row.transactionHash || null,
9944
+ proposalId: row.proposalId != null ? toBigIntString(row.proposalId) : null,
9945
+ forVotes: row.forVotes != null ? toBigIntString(row.forVotes) : null,
9946
+ againstVotes: row.againstVotes != null ? toBigIntString(row.againstVotes) : null,
9947
+ abstainVotes: row.abstainVotes != null ? toBigIntString(row.abstainVotes) : null,
9948
+ uniqueVoters: row.uniqueVoters != null ? Number(row.uniqueVoters) : null,
9949
+ quorum: row.quorum != null ? toBigIntString(row.quorum) : null,
9950
+ fromBounty: Boolean(row.fromBounty),
9951
+ bountyId: row.bountyId != null ? toBigIntString(row.bountyId) : null,
9952
+ badgeId: row.badgeId != null ? toBigIntString(row.badgeId) : null,
9953
+ badgeEvidenceURI: row.badgeEvidenceURI || null
9954
+ };
9955
+ });
9956
+ }
9957
+ function computeAggregates(contributions) {
9958
+ if (!contributions || contributions.length === 0) {
9959
+ return {
9960
+ totalContributions: 0,
9961
+ uniqueContributors: 0,
9962
+ bountyOriginated: 0,
9963
+ governanceOriginated: 0,
9964
+ totalForVotes: "0",
9965
+ totalAgainstVotes: "0",
9966
+ averageVoterCount: 0,
9967
+ badgeCount: 0
9968
+ };
9969
+ }
9970
+ const contributorSet = /* @__PURE__ */ new Set();
9971
+ let bountyOriginated = 0;
9972
+ let governanceOriginated = 0;
9973
+ let totalForVotes = 0n;
9974
+ let totalAgainstVotes = 0n;
9975
+ let voterCountSum = 0;
9976
+ let voterCountN = 0;
9977
+ let badgeCount = 0;
9978
+ for (const c of contributions) {
9979
+ if (c.contributor) contributorSet.add(c.contributor.toLowerCase());
9980
+ if (c.fromBounty) {
9981
+ bountyOriginated++;
9982
+ } else {
9983
+ governanceOriginated++;
9984
+ }
9985
+ if (c.forVotes != null) {
9986
+ try {
9987
+ totalForVotes += BigInt(c.forVotes);
9988
+ } catch {
9989
+ }
9990
+ }
9991
+ if (c.againstVotes != null) {
9992
+ try {
9993
+ totalAgainstVotes += BigInt(c.againstVotes);
9994
+ } catch {
9995
+ }
9996
+ }
9997
+ if (c.uniqueVoters != null) {
9998
+ voterCountSum += c.uniqueVoters;
9999
+ voterCountN++;
10000
+ }
10001
+ if (c.badgeId != null) badgeCount++;
10002
+ }
10003
+ return {
10004
+ totalContributions: contributions.length,
10005
+ uniqueContributors: contributorSet.size,
10006
+ bountyOriginated,
10007
+ governanceOriginated,
10008
+ totalForVotes: totalForVotes.toString(),
10009
+ totalAgainstVotes: totalAgainstVotes.toString(),
10010
+ averageVoterCount: voterCountN > 0 ? Math.round(voterCountSum / voterCountN) : 0,
10011
+ badgeCount
10012
+ };
10013
+ }
10014
+ async function getByAddress({
10015
+ url,
10016
+ address,
10017
+ subdao,
10018
+ includeBadges = true,
10019
+ includeContributions = true,
10020
+ first = 100
10021
+ } = {}) {
10022
+ if (!url) throw new Error("subgraph url required");
10023
+ if (!address) throw new Error("address required");
10024
+ const addr = safeGetAddress(address);
10025
+ if (!addr) throw new Error("invalid address");
10026
+ const subdaoAddr = subdao ? safeGetAddress(subdao) : null;
10027
+ if (subdao && !subdaoAddr) throw new Error("invalid subdao address");
10028
+ const limit = clampInt(first, { min: 1, max: 200, fallback: 100 });
10029
+ let badges = void 0;
10030
+ if (includeBadges) {
10031
+ badges = await getBadgesByRecipient({ url, recipient: addr, first: limit });
10032
+ }
10033
+ let contributionAggregates = void 0;
10034
+ let lastContributionAt = null;
10035
+ if (includeContributions) {
10036
+ const rows = await listContributions({
10037
+ url,
10038
+ contributor: addr,
10039
+ subdao: subdaoAddr || void 0,
10040
+ first: limit,
10041
+ orderBy: "updatedAt",
10042
+ orderDirection: "desc"
10043
+ });
10044
+ if (rows && rows.length) {
10045
+ const updatedAt = rows[0]?.updatedAt ?? rows[0]?.timestamp ?? null;
10046
+ lastContributionAt = updatedAt != null ? Number(updatedAt) : null;
10047
+ if (!Number.isFinite(lastContributionAt)) lastContributionAt = null;
10048
+ }
10049
+ contributionAggregates = computeAggregates(rows || []);
10050
+ }
10051
+ const signals = {
10052
+ badgeCount: includeBadges ? badges ? badges.length : 0 : null,
10053
+ totalContributions: includeContributions ? contributionAggregates ? contributionAggregates.totalContributions : 0 : null,
10054
+ governanceOriginated: includeContributions ? contributionAggregates ? contributionAggregates.governanceOriginated : 0 : null,
10055
+ bountyOriginated: includeContributions ? contributionAggregates ? contributionAggregates.bountyOriginated : 0 : null,
10056
+ lastContributionAt: includeContributions ? lastContributionAt : null
10057
+ };
10058
+ const out = {
10059
+ address: addr,
10060
+ subdao: subdaoAddr || null,
10061
+ signals
10062
+ };
10063
+ if (includeBadges) out.badges = badges;
10064
+ if (includeContributions) {
10065
+ out.contributionAggregates = contributionAggregates;
10066
+ out.lastContributionAt = lastContributionAt;
10067
+ }
10068
+ return out;
10069
+ }
10070
+ function normalizeGateInt(value, name) {
10071
+ if (value === void 0 || value === null) return null;
10072
+ const n = Number(value);
10073
+ if (!Number.isFinite(n) || n < 0) {
10074
+ throw new Error(`invalid gate: ${name}`);
10075
+ }
10076
+ return Math.trunc(n);
10077
+ }
10078
+ function normalizeRequiredBadgeIds(value) {
10079
+ const list = Array.isArray(value) ? value : value != null ? [value] : [];
10080
+ const out = [];
10081
+ for (const item of list) {
10082
+ const id2 = toBigIntString(item, "");
10083
+ if (id2) out.push(id2);
10084
+ }
10085
+ return out;
10086
+ }
10087
+ function evaluate(result, gates = {}) {
10088
+ const reasons = [];
10089
+ const minBadges = normalizeGateInt(gates.minBadges, "minBadges");
10090
+ const minContributions = normalizeGateInt(gates.minContributions, "minContributions");
10091
+ const requiredBadges = normalizeRequiredBadgeIds(gates.requireBadges ?? gates.requireBadge);
10092
+ const badgeCount = Number(result?.signals?.badgeCount ?? (result?.badges?.length ?? 0));
10093
+ const totalContributions = Number(result?.signals?.totalContributions ?? (result?.contributionAggregates?.totalContributions ?? 0));
10094
+ if (minBadges != null) {
10095
+ if (!Number.isFinite(badgeCount)) {
10096
+ reasons.push("badges_unavailable");
10097
+ } else if (badgeCount < minBadges) {
10098
+ reasons.push(`minBadges:${badgeCount}<${minBadges}`);
10099
+ }
10100
+ }
10101
+ if (minContributions != null) {
10102
+ if (!Number.isFinite(totalContributions)) {
10103
+ reasons.push("contributions_unavailable");
10104
+ } else if (totalContributions < minContributions) {
10105
+ reasons.push(`minContributions:${totalContributions}<${minContributions}`);
10106
+ }
10107
+ }
10108
+ if (requiredBadges.length) {
10109
+ const have = new Set((result?.badges || []).map((b) => toBigIntString(b?.badgeId, "")).filter(Boolean));
10110
+ for (const req2 of requiredBadges) {
10111
+ if (!have.has(req2)) reasons.push(`missingBadge:${req2}`);
10112
+ }
10113
+ }
10114
+ return { ok: reasons.length === 0, reasons };
10115
+ }
10116
+ module2.exports = {
10117
+ getByAddress,
10118
+ evaluate,
10119
+ // Expose lower-level functions for advanced usage
10120
+ getBadgesByRecipient,
10121
+ listContributions,
10122
+ computeAggregates
10123
+ };
10124
+ }
10125
+ });
10126
+
9798
10127
  // src/votingMultiplier/index.js
9799
10128
  var require_votingMultiplier = __commonJS({
9800
10129
  "src/votingMultiplier/index.js"(exports2, module2) {
@@ -14049,6 +14378,7 @@ var require_src = __commonJS({
14049
14378
  var boost = require_boost();
14050
14379
  var bounty = require_bounty();
14051
14380
  var contributions = require_contributions();
14381
+ var reputation = require_reputation();
14052
14382
  var votingMultiplier = require_votingMultiplier();
14053
14383
  var auction = require_auction();
14054
14384
  var wallet = require_wallet();
@@ -14102,6 +14432,7 @@ var require_src = __commonJS({
14102
14432
  utils: { ...utils, privateTx, safe, time },
14103
14433
  bounty,
14104
14434
  contributions,
14435
+ reputation,
14105
14436
  votingMultiplier,
14106
14437
  auction,
14107
14438
  wallet: Object.assign(wallet, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sage-protocol/sdk",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "description": "Backend-agnostic SDK for interacting with the Sage Protocol (governance, SubDAOs, tokens).",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",