270 lines
7.3 KiB
JavaScript
270 lines
7.3 KiB
JavaScript
|
/* eslint-disable no-console */
|
||
|
/* eslint-disable import/order */
|
||
|
|
||
|
import { utils } from 'ethers'
|
||
|
import uniqBy from 'lodash/uniqBy'
|
||
|
import chunk from 'lodash/chunk'
|
||
|
|
||
|
import { lookupAddresses, createBatchRequestCallback } from '@/services'
|
||
|
import { CHUNK_COUNT_PER_BATCH_REQUEST } from '@/constants'
|
||
|
|
||
|
const { toWei, fromWei, toBN } = require('web3-utils')
|
||
|
|
||
|
const CACHE_TX = {}
|
||
|
const CACHE_BLOCK = {}
|
||
|
|
||
|
const parseComment = (calldata, govInstance) => {
|
||
|
const empty = { contact: '', message: '' }
|
||
|
if (!calldata || !govInstance) return empty
|
||
|
|
||
|
const methodLength = 4 // length of castDelegatedVote method
|
||
|
const result = utils.defaultAbiCoder.decode(
|
||
|
['address[]', 'uint256', 'bool'],
|
||
|
utils.hexDataSlice(calldata, methodLength)
|
||
|
)
|
||
|
const data = govInstance.methods.castDelegatedVote(...result).encodeABI()
|
||
|
const dataLength = utils.hexDataLength(data)
|
||
|
|
||
|
try {
|
||
|
const str = utils.defaultAbiCoder.decode(['string'], utils.hexDataSlice(calldata, dataLength))
|
||
|
const [contact, message] = JSON.parse(str)
|
||
|
return { contact, message }
|
||
|
} catch {
|
||
|
return empty
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const createProposalComment = (resultAll, votedEvent) => {
|
||
|
const { transactionHash, returnValues, blockNumber } = votedEvent
|
||
|
const { voter } = returnValues
|
||
|
|
||
|
const comment = parseComment()
|
||
|
|
||
|
const percentage =
|
||
|
toBN(votedEvent.returnValues.votes)
|
||
|
.mul(toBN(10000))
|
||
|
.divRound(resultAll)
|
||
|
.toNumber() / 100
|
||
|
|
||
|
return {
|
||
|
id: `${transactionHash}-${voter}`,
|
||
|
percentage,
|
||
|
...returnValues,
|
||
|
votes: fromWei(returnValues.votes),
|
||
|
transactionHash,
|
||
|
blockNumber,
|
||
|
|
||
|
...comment,
|
||
|
|
||
|
ens: {
|
||
|
delegator: null,
|
||
|
voter: null
|
||
|
},
|
||
|
delegator: null,
|
||
|
timestamp: null
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const createFetchCommentWithMessage = (web3, batch, govInstance) => async (proposalComment) => {
|
||
|
const { transactionHash, voter, blockNumber } = proposalComment
|
||
|
|
||
|
if (!CACHE_TX[transactionHash]) {
|
||
|
CACHE_TX[transactionHash] = new Promise((resolve, reject) => {
|
||
|
const callback = createBatchRequestCallback(resolve, reject)
|
||
|
batch.add(web3.eth.getTransaction.request(transactionHash, callback))
|
||
|
})
|
||
|
}
|
||
|
|
||
|
if (!CACHE_BLOCK[blockNumber]) {
|
||
|
CACHE_BLOCK[blockNumber] = new Promise((resolve, reject) => {
|
||
|
const callback = createBatchRequestCallback(resolve, reject)
|
||
|
batch.add(web3.eth.getBlock.request(blockNumber, callback))
|
||
|
})
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
const [tx, blockInfo] = await Promise.all([CACHE_TX[transactionHash], CACHE_BLOCK[blockNumber]])
|
||
|
|
||
|
const isMaybeHasComment = voter === tx.from
|
||
|
const comment = parseComment(isMaybeHasComment ? tx.input : null, govInstance)
|
||
|
|
||
|
return {
|
||
|
...proposalComment,
|
||
|
...comment,
|
||
|
|
||
|
delegator: voter === tx.from ? null : tx.from,
|
||
|
timestamp: blockInfo.timestamp
|
||
|
}
|
||
|
} catch (error) {
|
||
|
CACHE_TX[transactionHash] = null
|
||
|
CACHE_BLOCK[blockNumber] = null
|
||
|
return proposalComment
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const state = () => {
|
||
|
return {
|
||
|
isFetchingComments: false,
|
||
|
isFetchingMessages: false,
|
||
|
ensNames: {},
|
||
|
comments: []
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const getters = {
|
||
|
comments: (state) => {
|
||
|
const { ensNames } = state
|
||
|
let comments = state.comments.slice()
|
||
|
|
||
|
comments.sort((a, b) => b.blockNumber - a.blockNumber)
|
||
|
comments = uniqBy(comments, 'voter')
|
||
|
comments.sort((a, b) => b.percentage - a.percentage)
|
||
|
|
||
|
comments = comments.map((data) => ({
|
||
|
...data,
|
||
|
ens: {
|
||
|
delegator: ensNames[data.delegator],
|
||
|
voter: ensNames[data.voter]
|
||
|
}
|
||
|
}))
|
||
|
|
||
|
return comments
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const mutations = {
|
||
|
SAVE_FETCHING_COMMENTS(state, status) {
|
||
|
state.isFetchingComments = status
|
||
|
},
|
||
|
SAVE_FETCHING_MESSAGES(state, status) {
|
||
|
state.isFetchingMessages = status
|
||
|
},
|
||
|
SAVE_ENS_NAMES(state, ensNames) {
|
||
|
state.ensNames = { ...state.ensNames, ...ensNames }
|
||
|
},
|
||
|
SAVE_COMMENTS(state, comments) {
|
||
|
state.comments = comments
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const actions = {
|
||
|
async fetchComments(context, proposal) {
|
||
|
const { commit, dispatch, state } = context
|
||
|
let { comments } = state
|
||
|
let newComments = []
|
||
|
|
||
|
if (comments[0]?.id !== proposal.id) {
|
||
|
commit('SAVE_COMMENTS', [])
|
||
|
comments = []
|
||
|
}
|
||
|
|
||
|
commit('SAVE_FETCHING_COMMENTS', true)
|
||
|
newComments = await dispatch('fetchVotedEvents', { proposal, comments })
|
||
|
commit('SAVE_FETCHING_COMMENTS', false)
|
||
|
|
||
|
if (!newComments) return
|
||
|
commit('SAVE_COMMENTS', newComments.concat(comments))
|
||
|
dispatch('fetchEnsNames', { comments: newComments })
|
||
|
|
||
|
commit('SAVE_FETCHING_MESSAGES', true)
|
||
|
// TODO: TC-163 - add pagination
|
||
|
newComments = await dispatch('fetchCommentsMessages', { comments: newComments })
|
||
|
commit('SAVE_FETCHING_MESSAGES', false)
|
||
|
|
||
|
if (!newComments) return
|
||
|
commit('SAVE_COMMENTS', newComments.concat(comments))
|
||
|
},
|
||
|
async fetchVotedEvents(context, { proposal, comments }) {
|
||
|
const { rootGetters } = context
|
||
|
let { blockNumber: fromBlock } = proposal
|
||
|
|
||
|
const netId = rootGetters['metamask/netId']
|
||
|
const govInstance = rootGetters['governance/gov/govContract']({ netId })
|
||
|
|
||
|
if (comments[0]?.id === proposal.id) {
|
||
|
fromBlock = comments[0].blockNumber + 1
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
let votedEvents = await govInstance.getPastEvents('Voted', {
|
||
|
filter: {
|
||
|
// support: [false],
|
||
|
proposalId: proposal.id
|
||
|
},
|
||
|
fromBlock,
|
||
|
toBlock: 'latest'
|
||
|
})
|
||
|
|
||
|
console.log('fetchVotedEvents', votedEvents.length)
|
||
|
|
||
|
votedEvents = votedEvents.sort((a, b) => b.blockNumber - a.blockNumber)
|
||
|
votedEvents = uniqBy(votedEvents, 'returnValues.voter')
|
||
|
|
||
|
console.log('fetchVotedEvents uniq', votedEvents.length)
|
||
|
|
||
|
const resultAll = toBN(toWei(proposal.results.for)).add(toBN(toWei(proposal.results.against)))
|
||
|
let newComments = votedEvents.map((votedEvent) => createProposalComment(resultAll, votedEvent))
|
||
|
newComments = newComments.concat(comments)
|
||
|
return newComments
|
||
|
} catch (e) {
|
||
|
console.error('fetchVotedEvents', e.message)
|
||
|
return null
|
||
|
}
|
||
|
},
|
||
|
async fetchCommentsMessages(context, { comments }) {
|
||
|
const { rootGetters } = context
|
||
|
|
||
|
const netId = rootGetters['metamask/netId']
|
||
|
const govInstance = rootGetters['governance/gov/govContract']({ netId })
|
||
|
const web3 = rootGetters['governance/gov/getWeb3']({ netId })
|
||
|
const commentListChunks = chunk(comments, CHUNK_COUNT_PER_BATCH_REQUEST)
|
||
|
|
||
|
let results = []
|
||
|
|
||
|
try {
|
||
|
for await (const list of commentListChunks) {
|
||
|
const batch = new web3.BatchRequest()
|
||
|
const fetchCommentsWithMessages = createFetchCommentWithMessage(web3, batch, govInstance)
|
||
|
const promises = list.map(fetchCommentsWithMessages)
|
||
|
batch.execute()
|
||
|
const result = await Promise.all(promises)
|
||
|
|
||
|
results = results.concat(result)
|
||
|
}
|
||
|
|
||
|
return results
|
||
|
} catch (e) {
|
||
|
console.error('fetchCommentsMessages', e.message)
|
||
|
}
|
||
|
},
|
||
|
async fetchEnsNames(context, { comments }) {
|
||
|
const { rootGetters, commit } = context
|
||
|
|
||
|
const netId = rootGetters['metamask/netId']
|
||
|
const web3 = rootGetters['governance/gov/getWeb3']({ netId })
|
||
|
|
||
|
try {
|
||
|
const addresses = comments
|
||
|
.map((_) => _.voter)
|
||
|
.flat()
|
||
|
.filter(Boolean)
|
||
|
|
||
|
console.log('fetchEnsNames', addresses.length)
|
||
|
|
||
|
const ensNames = await lookupAddresses(addresses, web3)
|
||
|
|
||
|
commit('SAVE_ENS_NAMES', ensNames)
|
||
|
} catch (e) {
|
||
|
console.error('fetchEnsNames', e.message)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export default {
|
||
|
namespaced: true,
|
||
|
state,
|
||
|
getters,
|
||
|
mutations,
|
||
|
actions
|
||
|
}
|