@sage-protocol/sdk 0.2.4 → 0.2.5

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.2.4",
17
+ version: "0.2.5",
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",
package/dist/index.cjs CHANGED
@@ -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.2.4",
17
+ version: "0.2.5",
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",
@@ -5022,9 +5022,127 @@ var require_governance = __commonJS({
5022
5022
  description: desc.description
5023
5023
  };
5024
5024
  }
5025
+ async function getProposalVotingStatus({ provider, governor, proposalId }) {
5026
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
5027
+ if (!proposalId) throw new SageSDKError(CODES.INVALID_ARGS, "proposalId required");
5028
+ const addr = normaliseGovernor(governor);
5029
+ const g = new Contract(addr, ABI.Governor, provider);
5030
+ const pid = typeof proposalId === "bigint" ? proposalId : BigInt(String(proposalId));
5031
+ const [
5032
+ stateNum,
5033
+ snapshot,
5034
+ deadline,
5035
+ votes,
5036
+ votingDelay,
5037
+ votingPeriod,
5038
+ proposalThreshold,
5039
+ govName
5040
+ ] = await Promise.all([
5041
+ g.state(pid).catch(() => null),
5042
+ g.proposalSnapshot(pid).catch(() => null),
5043
+ g.proposalDeadline(pid).catch(() => null),
5044
+ g.proposalVotes(pid).catch(() => null),
5045
+ g.votingDelay().catch(() => null),
5046
+ g.votingPeriod().catch(() => null),
5047
+ g.proposalThreshold().catch(() => null),
5048
+ g.name().catch(() => null)
5049
+ ]);
5050
+ let quorum = null;
5051
+ if (snapshot != null) {
5052
+ try {
5053
+ quorum = await g.quorum(snapshot);
5054
+ quorum = BigInt(quorum.toString());
5055
+ } catch {
5056
+ try {
5057
+ const qv = await g.quorumVotes();
5058
+ quorum = BigInt(qv.toString());
5059
+ } catch {
5060
+ }
5061
+ }
5062
+ }
5063
+ let forVotes = 0n;
5064
+ let againstVotes = 0n;
5065
+ let abstainVotes = 0n;
5066
+ if (Array.isArray(votes)) {
5067
+ againstVotes = BigInt(votes[0]);
5068
+ forVotes = BigInt(votes[1]);
5069
+ abstainVotes = BigInt(votes[2]);
5070
+ } else if (votes && typeof votes === "object") {
5071
+ if (votes.againstVotes != null) againstVotes = BigInt(votes.againstVotes);
5072
+ if (votes.forVotes != null) forVotes = BigInt(votes.forVotes);
5073
+ if (votes.abstainVotes != null) abstainVotes = BigInt(votes.abstainVotes);
5074
+ }
5075
+ const totalVotes = forVotes + againstVotes + abstainVotes;
5076
+ const participatingVotes = forVotes + againstVotes;
5077
+ const quorumMet = quorum != null ? forVotes >= quorum : null;
5078
+ const quorumProgress = quorum != null && quorum > 0n ? Number(forVotes * 10000n / quorum) / 100 : null;
5079
+ const stateMap = ["PENDING", "ACTIVE", "CANCELED", "DEFEATED", "SUCCEEDED", "QUEUED", "EXPIRED", "EXECUTED"];
5080
+ const state = typeof stateNum === "number" && stateNum >= 0 && stateNum < stateMap.length ? stateMap[stateNum] : null;
5081
+ const BLOCK_TIME_SECONDS = 2;
5082
+ const votingDelaySeconds = votingDelay != null ? Number(votingDelay) * BLOCK_TIME_SECONDS : null;
5083
+ const votingPeriodSeconds = votingPeriod != null ? Number(votingPeriod) * BLOCK_TIME_SECONDS : null;
5084
+ const formatSXXX = (val) => {
5085
+ if (val == null) return null;
5086
+ const num = Number(val) / 1e18;
5087
+ if (num >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
5088
+ if (num >= 1e3) return `${(num / 1e3).toFixed(1)}K`;
5089
+ return num.toFixed(2);
5090
+ };
5091
+ const formatDuration = (seconds) => {
5092
+ if (seconds == null) return null;
5093
+ if (seconds < 60) return `${seconds} seconds`;
5094
+ if (seconds < 3600) return `${Math.round(seconds / 60)} minutes`;
5095
+ if (seconds < 86400) return `${(seconds / 3600).toFixed(1)} hours`;
5096
+ return `${(seconds / 86400).toFixed(1)} days`;
5097
+ };
5098
+ return {
5099
+ governor: addr,
5100
+ governorName: govName,
5101
+ proposalId: pid.toString(),
5102
+ state,
5103
+ stateNum: typeof stateNum === "number" ? stateNum : null,
5104
+ // Raw values (bigint as string for JSON serialization)
5105
+ votes: {
5106
+ for: forVotes.toString(),
5107
+ against: againstVotes.toString(),
5108
+ abstain: abstainVotes.toString(),
5109
+ total: totalVotes.toString()
5110
+ },
5111
+ quorum: quorum?.toString() || null,
5112
+ proposalThreshold: proposalThreshold?.toString() || null,
5113
+ snapshot: snapshot?.toString() || null,
5114
+ deadline: deadline?.toString() || null,
5115
+ votingDelay: votingDelay?.toString() || null,
5116
+ votingPeriod: votingPeriod?.toString() || null,
5117
+ // Computed status
5118
+ quorumMet,
5119
+ quorumProgress,
5120
+ // percentage (0-100+)
5121
+ passing: forVotes > againstVotes,
5122
+ // Formatted for display
5123
+ formatted: {
5124
+ forVotes: formatSXXX(forVotes),
5125
+ againstVotes: formatSXXX(againstVotes),
5126
+ abstainVotes: formatSXXX(abstainVotes),
5127
+ totalVotes: formatSXXX(totalVotes),
5128
+ quorum: formatSXXX(quorum),
5129
+ proposalThreshold: formatSXXX(proposalThreshold),
5130
+ votingDelay: formatDuration(votingDelaySeconds),
5131
+ votingPeriod: formatDuration(votingPeriodSeconds)
5132
+ },
5133
+ // Plain language summary
5134
+ summary: {
5135
+ quorumStatus: quorum != null ? quorumMet ? `Quorum reached (${formatSXXX(forVotes)} of ${formatSXXX(quorum)} SXXX required)` : `${formatSXXX(forVotes)} of ${formatSXXX(quorum)} SXXX needed for quorum (${quorumProgress?.toFixed(1)}%)` : "Quorum information unavailable",
5136
+ voteStatus: `${formatSXXX(forVotes)} SXXX for, ${formatSXXX(againstVotes)} SXXX against`,
5137
+ outcome: forVotes > againstVotes ? "Currently passing" : forVotes < againstVotes ? "Currently failing" : "Tied",
5138
+ votingPeriod: votingPeriodSeconds ? `Voting period: ${formatDuration(votingPeriodSeconds)}` : null
5139
+ }
5140
+ };
5141
+ }
5025
5142
  module2.exports = {
5026
5143
  getGovernorInfo,
5027
5144
  getProposal,
5145
+ getProposalVotingStatus,
5028
5146
  listProposals,
5029
5147
  getProposalMetadata,
5030
5148
  hashDescription,
package/dist/index.mjs CHANGED
@@ -20,7 +20,7 @@ var require_package = __commonJS({
20
20
  "package.json"(exports2, module2) {
21
21
  module2.exports = {
22
22
  name: "@sage-protocol/sdk",
23
- version: "0.2.4",
23
+ version: "0.2.5",
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",
@@ -5028,9 +5028,127 @@ var require_governance = __commonJS({
5028
5028
  description: desc.description
5029
5029
  };
5030
5030
  }
5031
+ async function getProposalVotingStatus({ provider, governor, proposalId }) {
5032
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
5033
+ if (!proposalId) throw new SageSDKError(CODES.INVALID_ARGS, "proposalId required");
5034
+ const addr = normaliseGovernor(governor);
5035
+ const g = new Contract(addr, ABI.Governor, provider);
5036
+ const pid = typeof proposalId === "bigint" ? proposalId : BigInt(String(proposalId));
5037
+ const [
5038
+ stateNum,
5039
+ snapshot,
5040
+ deadline,
5041
+ votes,
5042
+ votingDelay,
5043
+ votingPeriod,
5044
+ proposalThreshold,
5045
+ govName
5046
+ ] = await Promise.all([
5047
+ g.state(pid).catch(() => null),
5048
+ g.proposalSnapshot(pid).catch(() => null),
5049
+ g.proposalDeadline(pid).catch(() => null),
5050
+ g.proposalVotes(pid).catch(() => null),
5051
+ g.votingDelay().catch(() => null),
5052
+ g.votingPeriod().catch(() => null),
5053
+ g.proposalThreshold().catch(() => null),
5054
+ g.name().catch(() => null)
5055
+ ]);
5056
+ let quorum = null;
5057
+ if (snapshot != null) {
5058
+ try {
5059
+ quorum = await g.quorum(snapshot);
5060
+ quorum = BigInt(quorum.toString());
5061
+ } catch {
5062
+ try {
5063
+ const qv = await g.quorumVotes();
5064
+ quorum = BigInt(qv.toString());
5065
+ } catch {
5066
+ }
5067
+ }
5068
+ }
5069
+ let forVotes = 0n;
5070
+ let againstVotes = 0n;
5071
+ let abstainVotes = 0n;
5072
+ if (Array.isArray(votes)) {
5073
+ againstVotes = BigInt(votes[0]);
5074
+ forVotes = BigInt(votes[1]);
5075
+ abstainVotes = BigInt(votes[2]);
5076
+ } else if (votes && typeof votes === "object") {
5077
+ if (votes.againstVotes != null) againstVotes = BigInt(votes.againstVotes);
5078
+ if (votes.forVotes != null) forVotes = BigInt(votes.forVotes);
5079
+ if (votes.abstainVotes != null) abstainVotes = BigInt(votes.abstainVotes);
5080
+ }
5081
+ const totalVotes = forVotes + againstVotes + abstainVotes;
5082
+ const participatingVotes = forVotes + againstVotes;
5083
+ const quorumMet = quorum != null ? forVotes >= quorum : null;
5084
+ const quorumProgress = quorum != null && quorum > 0n ? Number(forVotes * 10000n / quorum) / 100 : null;
5085
+ const stateMap = ["PENDING", "ACTIVE", "CANCELED", "DEFEATED", "SUCCEEDED", "QUEUED", "EXPIRED", "EXECUTED"];
5086
+ const state = typeof stateNum === "number" && stateNum >= 0 && stateNum < stateMap.length ? stateMap[stateNum] : null;
5087
+ const BLOCK_TIME_SECONDS = 2;
5088
+ const votingDelaySeconds = votingDelay != null ? Number(votingDelay) * BLOCK_TIME_SECONDS : null;
5089
+ const votingPeriodSeconds = votingPeriod != null ? Number(votingPeriod) * BLOCK_TIME_SECONDS : null;
5090
+ const formatSXXX = (val) => {
5091
+ if (val == null) return null;
5092
+ const num = Number(val) / 1e18;
5093
+ if (num >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
5094
+ if (num >= 1e3) return `${(num / 1e3).toFixed(1)}K`;
5095
+ return num.toFixed(2);
5096
+ };
5097
+ const formatDuration = (seconds) => {
5098
+ if (seconds == null) return null;
5099
+ if (seconds < 60) return `${seconds} seconds`;
5100
+ if (seconds < 3600) return `${Math.round(seconds / 60)} minutes`;
5101
+ if (seconds < 86400) return `${(seconds / 3600).toFixed(1)} hours`;
5102
+ return `${(seconds / 86400).toFixed(1)} days`;
5103
+ };
5104
+ return {
5105
+ governor: addr,
5106
+ governorName: govName,
5107
+ proposalId: pid.toString(),
5108
+ state,
5109
+ stateNum: typeof stateNum === "number" ? stateNum : null,
5110
+ // Raw values (bigint as string for JSON serialization)
5111
+ votes: {
5112
+ for: forVotes.toString(),
5113
+ against: againstVotes.toString(),
5114
+ abstain: abstainVotes.toString(),
5115
+ total: totalVotes.toString()
5116
+ },
5117
+ quorum: quorum?.toString() || null,
5118
+ proposalThreshold: proposalThreshold?.toString() || null,
5119
+ snapshot: snapshot?.toString() || null,
5120
+ deadline: deadline?.toString() || null,
5121
+ votingDelay: votingDelay?.toString() || null,
5122
+ votingPeriod: votingPeriod?.toString() || null,
5123
+ // Computed status
5124
+ quorumMet,
5125
+ quorumProgress,
5126
+ // percentage (0-100+)
5127
+ passing: forVotes > againstVotes,
5128
+ // Formatted for display
5129
+ formatted: {
5130
+ forVotes: formatSXXX(forVotes),
5131
+ againstVotes: formatSXXX(againstVotes),
5132
+ abstainVotes: formatSXXX(abstainVotes),
5133
+ totalVotes: formatSXXX(totalVotes),
5134
+ quorum: formatSXXX(quorum),
5135
+ proposalThreshold: formatSXXX(proposalThreshold),
5136
+ votingDelay: formatDuration(votingDelaySeconds),
5137
+ votingPeriod: formatDuration(votingPeriodSeconds)
5138
+ },
5139
+ // Plain language summary
5140
+ summary: {
5141
+ quorumStatus: quorum != null ? quorumMet ? `Quorum reached (${formatSXXX(forVotes)} of ${formatSXXX(quorum)} SXXX required)` : `${formatSXXX(forVotes)} of ${formatSXXX(quorum)} SXXX needed for quorum (${quorumProgress?.toFixed(1)}%)` : "Quorum information unavailable",
5142
+ voteStatus: `${formatSXXX(forVotes)} SXXX for, ${formatSXXX(againstVotes)} SXXX against`,
5143
+ outcome: forVotes > againstVotes ? "Currently passing" : forVotes < againstVotes ? "Currently failing" : "Tied",
5144
+ votingPeriod: votingPeriodSeconds ? `Voting period: ${formatDuration(votingPeriodSeconds)}` : null
5145
+ }
5146
+ };
5147
+ }
5031
5148
  module2.exports = {
5032
5149
  getGovernorInfo,
5033
5150
  getProposal,
5151
+ getProposalVotingStatus,
5034
5152
  listProposals,
5035
5153
  getProposalMetadata,
5036
5154
  hashDescription,
@@ -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.2.4",
17
+ version: "0.2.5",
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",
@@ -5022,9 +5022,127 @@ var require_governance = __commonJS({
5022
5022
  description: desc.description
5023
5023
  };
5024
5024
  }
5025
+ async function getProposalVotingStatus({ provider, governor, proposalId }) {
5026
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
5027
+ if (!proposalId) throw new SageSDKError(CODES.INVALID_ARGS, "proposalId required");
5028
+ const addr = normaliseGovernor(governor);
5029
+ const g = new Contract(addr, ABI.Governor, provider);
5030
+ const pid = typeof proposalId === "bigint" ? proposalId : BigInt(String(proposalId));
5031
+ const [
5032
+ stateNum,
5033
+ snapshot,
5034
+ deadline,
5035
+ votes,
5036
+ votingDelay,
5037
+ votingPeriod,
5038
+ proposalThreshold,
5039
+ govName
5040
+ ] = await Promise.all([
5041
+ g.state(pid).catch(() => null),
5042
+ g.proposalSnapshot(pid).catch(() => null),
5043
+ g.proposalDeadline(pid).catch(() => null),
5044
+ g.proposalVotes(pid).catch(() => null),
5045
+ g.votingDelay().catch(() => null),
5046
+ g.votingPeriod().catch(() => null),
5047
+ g.proposalThreshold().catch(() => null),
5048
+ g.name().catch(() => null)
5049
+ ]);
5050
+ let quorum = null;
5051
+ if (snapshot != null) {
5052
+ try {
5053
+ quorum = await g.quorum(snapshot);
5054
+ quorum = BigInt(quorum.toString());
5055
+ } catch {
5056
+ try {
5057
+ const qv = await g.quorumVotes();
5058
+ quorum = BigInt(qv.toString());
5059
+ } catch {
5060
+ }
5061
+ }
5062
+ }
5063
+ let forVotes = 0n;
5064
+ let againstVotes = 0n;
5065
+ let abstainVotes = 0n;
5066
+ if (Array.isArray(votes)) {
5067
+ againstVotes = BigInt(votes[0]);
5068
+ forVotes = BigInt(votes[1]);
5069
+ abstainVotes = BigInt(votes[2]);
5070
+ } else if (votes && typeof votes === "object") {
5071
+ if (votes.againstVotes != null) againstVotes = BigInt(votes.againstVotes);
5072
+ if (votes.forVotes != null) forVotes = BigInt(votes.forVotes);
5073
+ if (votes.abstainVotes != null) abstainVotes = BigInt(votes.abstainVotes);
5074
+ }
5075
+ const totalVotes = forVotes + againstVotes + abstainVotes;
5076
+ const participatingVotes = forVotes + againstVotes;
5077
+ const quorumMet = quorum != null ? forVotes >= quorum : null;
5078
+ const quorumProgress = quorum != null && quorum > 0n ? Number(forVotes * 10000n / quorum) / 100 : null;
5079
+ const stateMap = ["PENDING", "ACTIVE", "CANCELED", "DEFEATED", "SUCCEEDED", "QUEUED", "EXPIRED", "EXECUTED"];
5080
+ const state = typeof stateNum === "number" && stateNum >= 0 && stateNum < stateMap.length ? stateMap[stateNum] : null;
5081
+ const BLOCK_TIME_SECONDS = 2;
5082
+ const votingDelaySeconds = votingDelay != null ? Number(votingDelay) * BLOCK_TIME_SECONDS : null;
5083
+ const votingPeriodSeconds = votingPeriod != null ? Number(votingPeriod) * BLOCK_TIME_SECONDS : null;
5084
+ const formatSXXX = (val) => {
5085
+ if (val == null) return null;
5086
+ const num = Number(val) / 1e18;
5087
+ if (num >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
5088
+ if (num >= 1e3) return `${(num / 1e3).toFixed(1)}K`;
5089
+ return num.toFixed(2);
5090
+ };
5091
+ const formatDuration = (seconds) => {
5092
+ if (seconds == null) return null;
5093
+ if (seconds < 60) return `${seconds} seconds`;
5094
+ if (seconds < 3600) return `${Math.round(seconds / 60)} minutes`;
5095
+ if (seconds < 86400) return `${(seconds / 3600).toFixed(1)} hours`;
5096
+ return `${(seconds / 86400).toFixed(1)} days`;
5097
+ };
5098
+ return {
5099
+ governor: addr,
5100
+ governorName: govName,
5101
+ proposalId: pid.toString(),
5102
+ state,
5103
+ stateNum: typeof stateNum === "number" ? stateNum : null,
5104
+ // Raw values (bigint as string for JSON serialization)
5105
+ votes: {
5106
+ for: forVotes.toString(),
5107
+ against: againstVotes.toString(),
5108
+ abstain: abstainVotes.toString(),
5109
+ total: totalVotes.toString()
5110
+ },
5111
+ quorum: quorum?.toString() || null,
5112
+ proposalThreshold: proposalThreshold?.toString() || null,
5113
+ snapshot: snapshot?.toString() || null,
5114
+ deadline: deadline?.toString() || null,
5115
+ votingDelay: votingDelay?.toString() || null,
5116
+ votingPeriod: votingPeriod?.toString() || null,
5117
+ // Computed status
5118
+ quorumMet,
5119
+ quorumProgress,
5120
+ // percentage (0-100+)
5121
+ passing: forVotes > againstVotes,
5122
+ // Formatted for display
5123
+ formatted: {
5124
+ forVotes: formatSXXX(forVotes),
5125
+ againstVotes: formatSXXX(againstVotes),
5126
+ abstainVotes: formatSXXX(abstainVotes),
5127
+ totalVotes: formatSXXX(totalVotes),
5128
+ quorum: formatSXXX(quorum),
5129
+ proposalThreshold: formatSXXX(proposalThreshold),
5130
+ votingDelay: formatDuration(votingDelaySeconds),
5131
+ votingPeriod: formatDuration(votingPeriodSeconds)
5132
+ },
5133
+ // Plain language summary
5134
+ summary: {
5135
+ quorumStatus: quorum != null ? quorumMet ? `Quorum reached (${formatSXXX(forVotes)} of ${formatSXXX(quorum)} SXXX required)` : `${formatSXXX(forVotes)} of ${formatSXXX(quorum)} SXXX needed for quorum (${quorumProgress?.toFixed(1)}%)` : "Quorum information unavailable",
5136
+ voteStatus: `${formatSXXX(forVotes)} SXXX for, ${formatSXXX(againstVotes)} SXXX against`,
5137
+ outcome: forVotes > againstVotes ? "Currently passing" : forVotes < againstVotes ? "Currently failing" : "Tied",
5138
+ votingPeriod: votingPeriodSeconds ? `Voting period: ${formatDuration(votingPeriodSeconds)}` : null
5139
+ }
5140
+ };
5141
+ }
5025
5142
  module2.exports = {
5026
5143
  getGovernorInfo,
5027
5144
  getProposal,
5145
+ getProposalVotingStatus,
5028
5146
  listProposals,
5029
5147
  getProposalMetadata,
5030
5148
  hashDescription,
@@ -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.2.4",
23
+ version: "0.2.5",
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",
@@ -5028,9 +5028,127 @@ var require_governance = __commonJS({
5028
5028
  description: desc.description
5029
5029
  };
5030
5030
  }
5031
+ async function getProposalVotingStatus({ provider, governor, proposalId }) {
5032
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
5033
+ if (!proposalId) throw new SageSDKError(CODES.INVALID_ARGS, "proposalId required");
5034
+ const addr = normaliseGovernor(governor);
5035
+ const g = new Contract(addr, ABI.Governor, provider);
5036
+ const pid = typeof proposalId === "bigint" ? proposalId : BigInt(String(proposalId));
5037
+ const [
5038
+ stateNum,
5039
+ snapshot,
5040
+ deadline,
5041
+ votes,
5042
+ votingDelay,
5043
+ votingPeriod,
5044
+ proposalThreshold,
5045
+ govName
5046
+ ] = await Promise.all([
5047
+ g.state(pid).catch(() => null),
5048
+ g.proposalSnapshot(pid).catch(() => null),
5049
+ g.proposalDeadline(pid).catch(() => null),
5050
+ g.proposalVotes(pid).catch(() => null),
5051
+ g.votingDelay().catch(() => null),
5052
+ g.votingPeriod().catch(() => null),
5053
+ g.proposalThreshold().catch(() => null),
5054
+ g.name().catch(() => null)
5055
+ ]);
5056
+ let quorum = null;
5057
+ if (snapshot != null) {
5058
+ try {
5059
+ quorum = await g.quorum(snapshot);
5060
+ quorum = BigInt(quorum.toString());
5061
+ } catch {
5062
+ try {
5063
+ const qv = await g.quorumVotes();
5064
+ quorum = BigInt(qv.toString());
5065
+ } catch {
5066
+ }
5067
+ }
5068
+ }
5069
+ let forVotes = 0n;
5070
+ let againstVotes = 0n;
5071
+ let abstainVotes = 0n;
5072
+ if (Array.isArray(votes)) {
5073
+ againstVotes = BigInt(votes[0]);
5074
+ forVotes = BigInt(votes[1]);
5075
+ abstainVotes = BigInt(votes[2]);
5076
+ } else if (votes && typeof votes === "object") {
5077
+ if (votes.againstVotes != null) againstVotes = BigInt(votes.againstVotes);
5078
+ if (votes.forVotes != null) forVotes = BigInt(votes.forVotes);
5079
+ if (votes.abstainVotes != null) abstainVotes = BigInt(votes.abstainVotes);
5080
+ }
5081
+ const totalVotes = forVotes + againstVotes + abstainVotes;
5082
+ const participatingVotes = forVotes + againstVotes;
5083
+ const quorumMet = quorum != null ? forVotes >= quorum : null;
5084
+ const quorumProgress = quorum != null && quorum > 0n ? Number(forVotes * 10000n / quorum) / 100 : null;
5085
+ const stateMap = ["PENDING", "ACTIVE", "CANCELED", "DEFEATED", "SUCCEEDED", "QUEUED", "EXPIRED", "EXECUTED"];
5086
+ const state = typeof stateNum === "number" && stateNum >= 0 && stateNum < stateMap.length ? stateMap[stateNum] : null;
5087
+ const BLOCK_TIME_SECONDS = 2;
5088
+ const votingDelaySeconds = votingDelay != null ? Number(votingDelay) * BLOCK_TIME_SECONDS : null;
5089
+ const votingPeriodSeconds = votingPeriod != null ? Number(votingPeriod) * BLOCK_TIME_SECONDS : null;
5090
+ const formatSXXX = (val) => {
5091
+ if (val == null) return null;
5092
+ const num = Number(val) / 1e18;
5093
+ if (num >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
5094
+ if (num >= 1e3) return `${(num / 1e3).toFixed(1)}K`;
5095
+ return num.toFixed(2);
5096
+ };
5097
+ const formatDuration = (seconds) => {
5098
+ if (seconds == null) return null;
5099
+ if (seconds < 60) return `${seconds} seconds`;
5100
+ if (seconds < 3600) return `${Math.round(seconds / 60)} minutes`;
5101
+ if (seconds < 86400) return `${(seconds / 3600).toFixed(1)} hours`;
5102
+ return `${(seconds / 86400).toFixed(1)} days`;
5103
+ };
5104
+ return {
5105
+ governor: addr,
5106
+ governorName: govName,
5107
+ proposalId: pid.toString(),
5108
+ state,
5109
+ stateNum: typeof stateNum === "number" ? stateNum : null,
5110
+ // Raw values (bigint as string for JSON serialization)
5111
+ votes: {
5112
+ for: forVotes.toString(),
5113
+ against: againstVotes.toString(),
5114
+ abstain: abstainVotes.toString(),
5115
+ total: totalVotes.toString()
5116
+ },
5117
+ quorum: quorum?.toString() || null,
5118
+ proposalThreshold: proposalThreshold?.toString() || null,
5119
+ snapshot: snapshot?.toString() || null,
5120
+ deadline: deadline?.toString() || null,
5121
+ votingDelay: votingDelay?.toString() || null,
5122
+ votingPeriod: votingPeriod?.toString() || null,
5123
+ // Computed status
5124
+ quorumMet,
5125
+ quorumProgress,
5126
+ // percentage (0-100+)
5127
+ passing: forVotes > againstVotes,
5128
+ // Formatted for display
5129
+ formatted: {
5130
+ forVotes: formatSXXX(forVotes),
5131
+ againstVotes: formatSXXX(againstVotes),
5132
+ abstainVotes: formatSXXX(abstainVotes),
5133
+ totalVotes: formatSXXX(totalVotes),
5134
+ quorum: formatSXXX(quorum),
5135
+ proposalThreshold: formatSXXX(proposalThreshold),
5136
+ votingDelay: formatDuration(votingDelaySeconds),
5137
+ votingPeriod: formatDuration(votingPeriodSeconds)
5138
+ },
5139
+ // Plain language summary
5140
+ summary: {
5141
+ quorumStatus: quorum != null ? quorumMet ? `Quorum reached (${formatSXXX(forVotes)} of ${formatSXXX(quorum)} SXXX required)` : `${formatSXXX(forVotes)} of ${formatSXXX(quorum)} SXXX needed for quorum (${quorumProgress?.toFixed(1)}%)` : "Quorum information unavailable",
5142
+ voteStatus: `${formatSXXX(forVotes)} SXXX for, ${formatSXXX(againstVotes)} SXXX against`,
5143
+ outcome: forVotes > againstVotes ? "Currently passing" : forVotes < againstVotes ? "Currently failing" : "Tied",
5144
+ votingPeriod: votingPeriodSeconds ? `Voting period: ${formatDuration(votingPeriodSeconds)}` : null
5145
+ }
5146
+ };
5147
+ }
5031
5148
  module2.exports = {
5032
5149
  getGovernorInfo,
5033
5150
  getProposal,
5151
+ getProposalVotingStatus,
5034
5152
  listProposals,
5035
5153
  getProposalMetadata,
5036
5154
  hashDescription,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sage-protocol/sdk",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
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",