Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use snapshot api key in all snapshot requests #1211

Merged
merged 3 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
32 changes: 24 additions & 8 deletions src/back/routes/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ import { Request } from 'express'

import { SnapshotVote } from '../../clients/SnapshotGraphqlTypes'
import { SnapshotService } from '../../services/SnapshotService'
import { validateAddress, validateDates, validateFields } from '../utils/validations'
import { validateAddress, validateDates, validateFields, validateProposalSnapshotId } from '../utils/validations'

export default routes((router) => {
router.get('/snapshot/status-space/:spaceName', handleAPI(getStatusAndSpace))
router.post('/snapshot/votes', handleAPI(getAddressesVotes))
router.get('/snapshot/votes/:id', handleAPI(getProposalVotes))
router.get('/snapshot/votes/:proposalSnapshotId', handleAPI(getProposalVotes))
router.post('/snapshot/votes/all', handleAPI(getAllVotesBetweenDates))
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))
router.get('/snapshot/proposal-scores/:proposalSnapshotId', handleAPI(getProposalScores))
})

async function getStatusAndSpace(req: Request<{ spaceName?: string }>) {
Expand All @@ -27,13 +29,11 @@ async function getAddressesVotes(req: Request) {
return await SnapshotService.getAddressesVotes(addresses)
}

async function getProposalVotes(req: Request<{ id?: string }>) {
const { id } = req.params
if (!id || id.length === 0) {
throw new RequestError('Invalid snapshot id')
}
async function getProposalVotes(req: Request<{ proposalSnapshotId?: string }>) {
const { proposalSnapshotId } = req.params
validateProposalSnapshotId(proposalSnapshotId)

return await SnapshotService.getProposalVotes(id!)
return await SnapshotService.getProposalVotes(proposalSnapshotId!)
}

async function getAllVotesBetweenDates(req: Request): Promise<SnapshotVote[]> {
Expand Down Expand Up @@ -65,3 +65,19 @@ 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)
}

async function getProposalScores(req: Request<{ proposalSnapshotId?: string }>) {
const { proposalSnapshotId } = req.params
validateProposalSnapshotId(proposalSnapshotId)

return await SnapshotService.getProposalScores(proposalSnapshotId!)
}
6 changes: 6 additions & 0 deletions src/back/utils/validations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,9 @@ export function validateUniqueAddresses(addresses: string[]): boolean {

return uniqueSet.size === addresses.length
}

export function validateProposalSnapshotId(proposalSnapshotId?: string) {
if (!proposalSnapshotId || proposalSnapshotId.length === 0) {
throw new RequestError('Invalid snapshot id')
}
}
25 changes: 24 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 @@ -551,13 +558,29 @@ export class Governance extends API {
return response.data
}

async getProposalScores(proposalSnapshotId: string) {
const response = await this.fetch<ApiResponse<number[]>>(
`/snapshot/proposal-scores/${proposalSnapshotId}`,
this.options().method('GET')
)
return response.data
}

async getVpDistribution(address: string, proposalSnapshotId?: string) {
const snapshotId = proposalSnapshotId ? `/${proposalSnapshotId}` : ''
const url = `/snapshot/vp-distribution/${address}${snapshotId}`
const response = await this.fetch<ApiResponse<VpDistribution>>(url, this.options().method('GET'))
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
1 change: 1 addition & 0 deletions src/clients/SnapshotGraphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export class SnapshotGraphql extends API {
strategies {
name
params
network
}
}
}
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
}
Loading
Loading