Skip to content

Commit

Permalink
feat: move scores calculation to backend
Browse files Browse the repository at this point in the history
  • Loading branch information
andyesp committed Aug 25, 2023
1 parent 0b1e592 commit 63d86cf
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 85 deletions.
69 changes: 50 additions & 19 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"@dcl/ui-env": "1.2.1",
"@jparnaudo/react-crypto-icons": "^1.0.5",
"@otterspace-xyz/contracts": "^2.7.3",
"@snapshot-labs/snapshot.js": "0.4.52",
"@snapshot-labs/snapshot.js": "0.5.5",
"@tanstack/react-query": "^4.29.7",
"autoprefixer": "^10.4.4",
"chart.js": "^3.8.2",
Expand Down
10 changes: 10 additions & 0 deletions src/back/routes/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default routes((router) => {
router.post('/snapshot/proposals', handleAPI(getProposals))
router.post('/snapshot/proposals/pending', handleAPI(getPendingProposals))
router.get('/snapshot/vp-distribution/:address/:proposalSnapshotId?', handleAPI(getVpDistribution))
router.post('/snapshot/scores', handleAPI(getScores))
})

async function getStatusAndSpace(req: Request<{ spaceName?: string }>) {
Expand Down Expand Up @@ -65,3 +66,12 @@ async function getVpDistribution(req: Request<{ address: string; proposalSnapsho

return await SnapshotService.getVpDistribution(address, proposalSnapshotId)
}

async function getScores(req: Request) {
const addresses = req.body.addresses
if (!addresses || addresses.length === 0) {
throw new RequestError('Addresses missing', RequestError.BadRequest)
}

return await SnapshotService.getScores(addresses)
}
17 changes: 16 additions & 1 deletion src/clients/Governance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,14 @@ import { Vote, VotedProposal } from '../entities/Votes/types'
import Time from '../utils/date/Time'

import { TransparencyBudget } from './DclData'
import { SnapshotProposal, SnapshotSpace, SnapshotStatus, SnapshotVote, VpDistribution } from './SnapshotGraphqlTypes'
import {
DetailedScores,
SnapshotProposal,
SnapshotSpace,
SnapshotStatus,
SnapshotVote,
VpDistribution,
} from './SnapshotGraphqlTypes'
import { VestingInfo } from './VestingData'

type NewProposalMap = {
Expand Down Expand Up @@ -558,6 +565,14 @@ export class Governance extends API {
return response.data
}

async getScores(addresses: string[]) {
const response = await this.fetch<ApiResponse<DetailedScores>>(
'/snapshot/scores',
this.options().method('POST').json({ addresses })
)
return response.data
}

async getVestingContractData(addresses: string[]) {
const response = await this.fetch<ApiResponse<VestingInfo[]>>(
`/vesting`,
Expand Down
39 changes: 23 additions & 16 deletions src/clients/SnapshotApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ import { CancelProposal, ProposalType, Vote } from '@snapshot-labs/snapshot.js/d
import logger from 'decentraland-gatsby/dist/entities/Development/logger'
import env from 'decentraland-gatsby/dist/utils/env'

import { SNAPSHOT_ADDRESS, SNAPSHOT_PRIVATE_KEY, SNAPSHOT_SPACE } from '../entities/Snapshot/constants'
import {
SNAPSHOT_ADDRESS,
SNAPSHOT_API_KEY,
SNAPSHOT_PRIVATE_KEY,
SNAPSHOT_SPACE,
} from '../entities/Snapshot/constants'
import { getChecksumAddress } from '../entities/Snapshot/utils'
import { ProposalInCreation, ProposalLifespan } from '../services/ProposalService'
import Time from '../utils/date/Time'
import { getEnvironmentChainId } from '../utils/votes/utils'

import { SnapshotGraphql } from './SnapshotGraphql'
import { SnapshotStrategy } from './SnapshotGraphqlTypes'
import { trimLastForwardSlash } from './utils'

const SNAPSHOT_PROPOSAL_TYPE: ProposalType = 'single-choice' // Each voter may select only one choice
Expand Down Expand Up @@ -150,29 +154,32 @@ export class SnapshotApi {
return (await this.client.vote(account, address, voteMessage)) as SnapshotReceipt
}

async getScores(
addresses: string[],
blockNumber?: number | string,
space?: string,
networkId?: string,
proposalStrategies?: SnapshotStrategy[]
) {
async getScores(addresses: string[]) {
const formattedAddresses = addresses.map((address) => getChecksumAddress(address))
const network = networkId && networkId.length > 0 ? networkId : getEnvironmentChainId().toString()
const spaceName = space && space.length > 0 ? space : SnapshotApi.getSpaceName()
const strategies = proposalStrategies || (await SnapshotGraphql.get().getSpace(spaceName)).strategies
const spaceName = SnapshotApi.getSpaceName()
const network = getEnvironmentChainId().toString()
const strategies = (await SnapshotGraphql.get().getSpace(spaceName)).strategies
const scoreApiUrl = `https://score.snapshot.org/?apiKey=${SNAPSHOT_API_KEY}`

try {
const scores = await snapshot.utils.getScores(spaceName, strategies, network, formattedAddresses, blockNumber)
const scores = await snapshot.utils.getScores(
spaceName,
strategies,
network,
formattedAddresses,
undefined,
scoreApiUrl
)

return {
scores: scores,
strategies: strategies,
scores,
strategies,
}
} catch (e) {
logger.log(
`Space: ${spaceName}, Strategies: ${JSON.stringify(
strategies
)}, Network: ${network}, Addresses: ${formattedAddresses}, Block: ${blockNumber}`
)}, Network: ${network}, Addresses: ${formattedAddresses}`
)
throw new Error('Error fetching proposal scores', e as Error)
}
Expand Down
46 changes: 1 addition & 45 deletions src/entities/Votes/utils.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import isUUID from 'validator/lib/isUUID'

import { SnapshotApi } from '../../clients/SnapshotApi'
import { DetailedScores, SnapshotStrategy, SnapshotVote } from '../../clients/SnapshotGraphqlTypes'
import { isSameAddress } from '../Snapshot/utils'
import { SnapshotVote } from '../../clients/SnapshotGraphqlTypes'

import { ChoiceColor, Vote } from './types'

export type Scores = Record<string, number>
const DELEGATION_STRATEGY_NAME = 'delegation'

export function toProposalIds(ids?: undefined | null | string | string[]) {
if (!ids) {
Expand Down Expand Up @@ -166,44 +163,3 @@ export function abbreviateNumber(vp: number) {
function getFloorOrZero(number?: number) {
return Math.floor(number || 0)
}

export async function getScores(
addresses: string[],
block?: string | number,
space?: string,
networkId?: string,
proposalStrategies?: SnapshotStrategy[]
) {
const formattedAddresses = addresses.map((addr) => addr.toLowerCase())
const { scores, strategies } = await SnapshotApi.get().getScores(
formattedAddresses,
block,
space,
networkId,
proposalStrategies
)

const result: DetailedScores = {}
const delegationScores = scores[strategies.findIndex((s) => s.name === DELEGATION_STRATEGY_NAME)] || {}
for (const addr of formattedAddresses) {
result[addr] = {
ownVp: 0,
delegatedVp:
Math.round(delegationScores[Object.keys(delegationScores).find((key) => isSameAddress(key, addr)) || '']) || 0,
totalVp: 0,
}
}

for (const score of scores) {
for (const addr of Object.keys(score)) {
const address = addr.toLowerCase()
result[address].totalVp = (result[address].totalVp || 0) + Math.floor(score[addr] || 0)
}
}

for (const address of Object.keys(result)) {
result[address].ownVp = result[address].totalVp - result[address].delegatedVp
}

return result
}
1 change: 1 addition & 0 deletions src/hooks/useProposalOutcome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const useProposalOutcome = (snapshotId: ProposalAttributes['snapshot_id'], choic
return null
}

// TODO: Move this to backend scores/proposal/:snapshotId
return SnapshotGraphql.get().getProposalScores(snapshotId)
},
staleTime: FIVE_MINUTES_MS,
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useVotingPowerBalanceList.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useQuery } from '@tanstack/react-query'

import { getScores } from '../entities/Votes/utils'
import { Governance } from '../clients/Governance'

import { DEFAULT_QUERY_STALE_TIME } from './constants'

Expand All @@ -9,7 +9,7 @@ export default function useVotingPowerBalanceList(addresses: string[]) {
queryKey: [`votingPower#${JSON.stringify(addresses)}`],
queryFn: async () => {
if (addresses.length < 1) return {}
return await getScores(addresses)
return await Governance.get().getScores(addresses)
},
staleTime: DEFAULT_QUERY_STALE_TIME,
})
Expand Down
35 changes: 34 additions & 1 deletion src/services/SnapshotService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@ import isNumber from 'lodash/isNumber'

import { SnapshotApi, SnapshotReceipt } from '../clients/SnapshotApi'
import { SnapshotGraphql } from '../clients/SnapshotGraphql'
import { SnapshotProposal, SnapshotVote, VpDistribution } from '../clients/SnapshotGraphqlTypes'
import { DetailedScores, SnapshotProposal, SnapshotVote, VpDistribution } from '../clients/SnapshotGraphqlTypes'
import * as templates from '../entities/Proposal/templates'
import { proposalUrl, snapshotProposalUrl } from '../entities/Proposal/utils'
import { SNAPSHOT_SPACE } from '../entities/Snapshot/constants'
import { isSameAddress } from '../entities/Snapshot/utils'
import { inBackground } from '../helpers'
import { Avatar } from '../utils/Catalyst/types'

import { ProposalInCreation, ProposalLifespan } from './ProposalService'
import RpcService from './RpcService'

const DELEGATION_STRATEGY_NAME = 'delegation'

export class SnapshotService {
static async createProposal(
proposalInCreation: ProposalInCreation,
Expand Down Expand Up @@ -123,4 +126,34 @@ export class SnapshotService {
static async getVpDistribution(address: string, proposalSnapshotId?: string): Promise<VpDistribution> {
return await SnapshotGraphql.get().getVpDistribution(address, proposalSnapshotId)
}

static async getScores(addresses: string[]) {
const formattedAddresses = addresses.map((addr) => addr.toLowerCase())
const { scores, strategies } = await SnapshotApi.get().getScores(formattedAddresses)

const result: DetailedScores = {}
const delegationScores = scores[strategies.findIndex((s) => s.name === DELEGATION_STRATEGY_NAME)] || {}
for (const addr of formattedAddresses) {
result[addr] = {
ownVp: 0,
delegatedVp:
Math.round(delegationScores[Object.keys(delegationScores).find((key) => isSameAddress(key, addr)) || '']) ||
0,
totalVp: 0,
}
}

for (const score of scores) {
for (const addr of Object.keys(score)) {
const address = addr.toLowerCase()
result[address].totalVp = (result[address].totalVp || 0) + Math.floor(score[addr] || 0)
}
}

for (const address of Object.keys(result)) {
result[address].ownVp = result[address].totalVp - result[address].delegatedVp
}

return result
}
}

0 comments on commit 63d86cf

Please sign in to comment.