diff --git a/assets/javascripts/discourse/components/profile-summary-votes.js b/assets/javascripts/discourse/components/profile-summary-votes.js
index c1bcad0..72c21e5 100644
--- a/assets/javascripts/discourse/components/profile-summary-votes.js
+++ b/assets/javascripts/discourse/components/profile-summary-votes.js
@@ -32,6 +32,7 @@ export default Component.extend({
async fetchVotes() {
set(this, "fetched", false);
+ set(this, "votes", []);
const votes = await VotingHistory.start(this.profile, {
SiteSettings: this.siteSettings,
});
@@ -42,8 +43,8 @@ export default Component.extend({
async init() {
this._super(...arguments);
this.daoName = this.oldDaoName = window.selectedDao;
+ const cli = new KarmaApiClient(this.daoName, "");
if (this.session) {
- const cli = new KarmaApiClient(this.daoName, "");
try {
const { allowance } = await cli.isApiAllowed(this.session.csrfToken);
set(this, "hasSetApiKey", !!allowance);
diff --git a/assets/javascripts/discourse/templates/components/karma-stats.hbs b/assets/javascripts/discourse/templates/components/karma-stats.hbs
index 84df85e..6dd9e0d 100644
--- a/assets/javascripts/discourse/templates/components/karma-stats.hbs
+++ b/assets/javascripts/discourse/templates/components/karma-stats.hbs
@@ -22,7 +22,7 @@
Governance Stats
- {{#if availableDaos.length}}
+ {{#if (gt availableDaos.length 1)}}
{{#each availableDaos as |dao|}}
{{#if (eq daoName dao.name)}}
diff --git a/assets/javascripts/discourse/templates/components/proposal-banner.hbs b/assets/javascripts/discourse/templates/components/proposal-banner.hbs
index 716c25a..bb7a70d 100644
--- a/assets/javascripts/discourse/templates/components/proposal-banner.hbs
+++ b/assets/javascripts/discourse/templates/components/proposal-banner.hbs
@@ -1,5 +1,5 @@
{{#if (and siteSettings.Show_proposal_banner shouldShow)}}
- {{#if availableDaos.length}}
+ {{#if (gt availableDaos.length 1)}}
{{#each availableDaos as |dao|}}
{{#if (eq daoName dao.name)}}
diff --git a/assets/javascripts/lib/karma-api-client.js b/assets/javascripts/lib/karma-api-client.js
index 7d6a6a0..e9b0c49 100644
--- a/assets/javascripts/lib/karma-api-client.js
+++ b/assets/javascripts/lib/karma-api-client.js
@@ -105,6 +105,51 @@ class KarmaApiClient {
"X-CSRF-Token": csrfToken,
});
}
+
+
+ /**
+ * @param {import('karma-score').KarmaApiVotesSummaryRes} summary
+ * @returns {import('karma-score').ParsedProposal[]}
+ */
+ #parseVotingSummary = (summary) => {
+ console.info('voting summary', summary)
+ const { proposals, votes } = summary;
+ const parsedVotes = [];
+
+ votes.sort().forEach((vote) => {
+ const [id, version] = vote.proposalId.split('-');
+ console.log('id', id, 'version', version)
+ const proposal = proposals.find(p => p.id === +id && p.version === version);
+ console.log('proposal', proposal)
+ if (!proposal) {
+ return;
+ }
+
+ parsedVotes.push({
+ title: proposal?.title,
+ proposalId: proposal.id,
+ voteMethod: "Off-chain",
+ proposal: proposal?.title,
+ choice: vote.reason,
+ executed: moment(proposal.endDate).format("MMMM D, YYYY"),
+ });
+ })
+
+ return parsedVotes.sort((a, b) => moment(a.executed).isBefore(moment(b.executed)) ? 1 : -1);
+ }
+
+ /**
+ * Get voting summary for moonbeam and moonriver ONLY
+ * @returns {Promise
+ */
+ async fetchVoteSummary() {
+ console.info('fetching voting summary')
+ if (!['moonbeam', 'moonriver', 'moonbase'].includes(this.daoName.toLowerCase())) {
+ return { proposals: [], votes: [] };
+ }
+ const url = `${karmaUrl}/delegate/${this.daoName}/${this.publicAddress}/voting-history`.toLowerCase();
+ return await request(url, null, "GET");
+ }
}
export default KarmaApiClient;
diff --git a/assets/javascripts/lib/voting-history/gql/off-chain-fetcher.js b/assets/javascripts/lib/voting-history/gql/off-chain-fetcher.js
index 868bd8e..720ff0d 100644
--- a/assets/javascripts/lib/voting-history/gql/off-chain-fetcher.js
+++ b/assets/javascripts/lib/voting-history/gql/off-chain-fetcher.js
@@ -10,6 +10,7 @@ const subgraphUrl = new URL("https://hub.snapshot.org/graphql");
* Concat proposal and votes into a common interface
* @param proposals
* @param votes
+ * @returns {import("karma-score").ParsedProposal[])}
*/
function parseVotes(votes = []) {
const array = [];
diff --git a/assets/javascripts/lib/voting-history/index.js b/assets/javascripts/lib/voting-history/index.js
index 764bab3..0c1fbc6 100644
--- a/assets/javascripts/lib/voting-history/index.js
+++ b/assets/javascripts/lib/voting-history/index.js
@@ -1,6 +1,8 @@
import { fetchDaoSnapshotAndOnChainIds } from "../fetch-snapshot-onchain-ids";
+import KarmaApiClient from "../karma-api-client";
import { fetchOffChainProposalVotes } from "./gql/off-chain-fetcher";
import { fetchOnChainProposalVotes } from "./gql/on-chain-fetcher";
+import { moonriverFetcher } from "./moonbeam/moonbeam";
import template from "./template";
const karma = "https://karmahq.xyz/profile";
@@ -15,6 +17,13 @@ const VotingHistory = {
return 0;
},
+ /**
+ *
+ * @param {*} profile
+ * @param {*} ctx
+ * @param {*} wrapperId
+ * @returns {Promise}
+ */
async start(profile, ctx, wrapperId = ".__karma-stats") {
if (!ctx || !ctx.SiteSettings || !profile) {
return;
@@ -30,10 +39,15 @@ const VotingHistory = {
const daoName = window.selectedDao;
const amount = this.shouldShowVotingHistory(ctx);
+ if (['moonbeam', 'moonriver', 'moonbase'].includes(daoName.toLowerCase())) {
+ console.info('voting history for' + daoName)
+ const votes = await moonriverFetcher(daoName, profile.address);
+ return votes.slice(0, amount);
+ }
+
// TODO fix this workaround by refactoring this code into components
this.daoIds = (await fetchDaoSnapshotAndOnChainIds(daoName));
-
let onChain = [];
if (this.daoIds.onChain?.length) {
onChain = await fetchOnChainProposalVotes(
diff --git a/assets/javascripts/lib/voting-history/moonbeam/moonbeam.js b/assets/javascripts/lib/voting-history/moonbeam/moonbeam.js
new file mode 100644
index 0000000..5262c57
--- /dev/null
+++ b/assets/javascripts/lib/voting-history/moonbeam/moonbeam.js
@@ -0,0 +1,140 @@
+import KarmaApiClient from "../../karma-api-client";
+
+const getVoteReason = (vote) => {
+ if (!vote.reason || typeof vote.reason === 'boolean') return 'Did not vote';
+ if (vote.reason.toLowerCase() === 'for') return 1;
+ if (vote.reason.toLowerCase() === 'abstain') return 'ABSTAIN';
+ return 0;
+};
+
+/**
+ * Concat proposal and votes into a common interface
+ * @param proposals
+ * @param votes
+ */
+function concatOnChainProposals(proposals, votes) {
+ const array = [];
+
+ votes.forEach((vote) => {
+ const { proposal } = vote;
+ const original = proposals.find(item => +item.id === +proposal);
+ array.push({
+ voteMethod: 'On-chain',
+ proposal: original?.description || `Proposal ${proposal}`,
+ choice: getVoteReason(vote),
+ solution: vote?.solution,
+ reason: vote?.reason,
+ executed: moment
+ .unix(original?.timestamp || Math.round(Date.now() / 1000))
+ .format('MMMM D, YYYY'),
+ executedTimestamp: original?.timestamp || Math.round(Date.now() / 1000),
+ voteId: proposal,
+ trackId: Number(original?.trackId),
+ version: original?.version,
+ });
+ });
+
+ proposals.forEach(proposal => {
+ if (!array.find(item => item.voteId && +item.voteId === +proposal.id))
+ array.push({
+ voteMethod: 'On-chain',
+ proposal: proposal.description,
+ choice: -1,
+ solution: null,
+ executed: moment.unix(proposal.timestamp).format('MMMM D, YYYY'),
+ executedTimestamp: proposal.timestamp,
+ voteId: proposal.id.toString(),
+ finished: proposal.finished,
+ trackId: Number(proposal?.trackId),
+ version: proposal?.version,
+ });
+ });
+
+ return array.sort((a, b) => b.executedTimestamp - a.executedTimestamp);
+}
+
+async function proposalsWithMetadata(daoName) {
+ console.log('proposals with metadata')
+ const url = `https://dapp.karmahq.xyz/api/proposals?dao=${daoName?.toLowerCase()}&source=on-chain`;
+ const data = await fetch(url, {
+ method: "GET",
+ }).then(async (res) => await res.json());
+ console.info('cu de saco', data)
+ return data;
+}
+
+async function getDaoProposals(
+ cachedProposals = [],
+ daoName = 'moonbeam'
+) {
+ const proposals = await proposalsWithMetadata(daoName);
+ const proposalsMap = proposals.map(proposal => {
+ const status = Object.entries(proposal.information)[0];
+ const matchedProposal = cachedProposals.find(
+ pr =>
+ +pr.id === +proposal.proposalId &&
+ (proposal.trackId === null) === (pr.version === 'V1')
+ );
+ const timestamp =
+ (cachedProposals.find(
+ pr =>
+ +pr.id === +proposal.proposalId &&
+ (proposal.trackId === null) === (pr.version === 'V1')
+ )?.startDate || 0) / 1000;
+
+ return {
+ proposal: proposal.proposalId,
+ id: `${proposal.proposalId}`,
+ description:
+ proposal.proposal || `Proposal ${proposal.proposalId.toString()}`,
+ timestamp: Math.round(timestamp),
+ trackId: proposal.trackId,
+ finished: !status ? true : status[0] !== 'ongoing',
+ version: matchedProposal?.version,
+ };
+ });
+
+ // eslint-disable-next-line id-length
+ return proposalsMap.sort((a, b) => b.timestamp - a.timestamp);
+}
+
+async function fetchOnChainVotes(daoName, address) {
+ if (!daoName || !address) return [];
+ try {
+ daoName = [daoName].flat()[0]
+ const cli = new KarmaApiClient([daoName].flat()[0], address);
+ const { votes, proposals: cachedProposals } = await cli.fetchVoteSummary();
+ console.info('deu n vote', votes, cachedProposals)
+
+ const voteList = votes.map(vote => ({
+ proposal: vote.proposalId.split('-')[0],
+ openGov: vote.proposalId.split('-')[1] === 'V2',
+ reason: vote.reason,
+ }));
+ if (voteList && Array.isArray(voteList)) {
+ const proposals = await getDaoProposals(cachedProposals, daoName);
+
+ return concatOnChainProposals(proposals, voteList);
+ }
+ } catch (error) {
+ console.info(error)
+ return [];
+ }
+ return [];
+}
+
+export async function moonriverFetcher(
+ daoName,
+ address
+) {
+ console.info('fetching moonriver')
+ try {
+ const votes = await fetchOnChainVotes(daoName, address);
+ console.info('deu vote', votes)
+ return votes;
+ } catch (error) {
+ console.info('deu error')
+ console.info(error)
+ return [];
+ }
+}
diff --git a/assets/javascripts/lib/voting-history/template.js b/assets/javascripts/lib/voting-history/template.js
index 8a91e8a..cba3a76 100644
--- a/assets/javascripts/lib/voting-history/template.js
+++ b/assets/javascripts/lib/voting-history/template.js
@@ -9,7 +9,7 @@ function getIcon(choice = "not vote") {
return voteIcon.empty;
}
if (
- choice.toLocaleLowerCase().substring(0, 2) === "no" ||
+ choice?.toLowerCase?.().substring(0, 2) === "no" ||
/agai+nst/gi.test(choice)
) {
return voteIcon.no;
diff --git a/spec/types.d.ts b/spec/types.d.ts
index f140eae..c53140e 100644
--- a/spec/types.d.ts
+++ b/spec/types.d.ts
@@ -42,4 +42,26 @@ declare module "karma-score" {
event: string;
properties: Record;
}
+
+ declare interface KarmaApiVotesSummaryRes {
+ proposals: {
+ id: number;
+ version: "V1" | "V2";
+ endDate: number;
+ startDate: number;
+ }[];
+ votes: {
+ proposalId: string;
+ reason: string;
+ }[];
+ }
+
+ declare interface ParsedProposal {
+ title: string;
+ proposalId: string;
+ voteMethod: string;
+ proposal: string;
+ choice: string | number;
+ executed: string;
+ }
}