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

chore: proposal projects deprecation #1890

Merged
merged 9 commits into from
Aug 19, 2024
25 changes: 0 additions & 25 deletions src/clients/Transparency.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { VestingStatus } from '../entities/Grant/types'
import { TokenInWallet } from '../entities/Transparency/types'
import { ErrorCategory } from '../utils/errorCategories'

Expand Down Expand Up @@ -53,20 +52,6 @@ export type TransparencyBudget = {
category_percentages: Record<string, number>
}

export type TransparencyVesting = {
proposal_id: string
token: string
vesting_address: string
vesting_released: number
vesting_releasable: number
vesting_start_at: string
vesting_finish_at: string
vesting_contract_token_balance: number
vesting_total_amount: number
vesting_status: VestingStatus
duration_in_months: number
}

const EMPTY_API: TransparencyData = {
balances: [],
income: {
Expand Down Expand Up @@ -108,14 +93,4 @@ export class Transparency {
return []
}
}

static async getVestings() {
try {
const response = (await (await fetch(`${API_URL}/vestings.json`)).json()) as TransparencyVesting[]
return response
} catch (error) {
ErrorClient.report('Failed to fetch transparency vestings data', { error, category: ErrorCategory.Transparency })
return []
}
}
}
4 changes: 3 additions & 1 deletion src/clients/VestingData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ export function getTokenSymbolFromAddress(tokenAddress: string) {
return 'USDC'
case '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2':
return 'WETH'
default:
console.log(`Unable to parse token contract address: ${tokenAddress}`)
return 'ETH'
}
throw new Error(`Unable to parse token contract address: ${tokenAddress}`)
}
22 changes: 15 additions & 7 deletions src/clients/VestingsSubgraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { VESTINGS_QUERY_ENDPOINT } from '../entities/Snapshot/constants'
import { SubgraphVesting } from './VestingSubgraphTypes'
import { trimLastForwardSlash } from './utils'

const OLDEST_INDEXED_BLOCK = 20463272

export class VestingsSubgraph {
static Cache = new Map<string, VestingsSubgraph>()
private readonly queryEndpoint: string
Expand Down Expand Up @@ -72,7 +74,7 @@ export class VestingsSubgraph {
}
`

const variables = { address }
const variables = { address: address.toLowerCase() }
const response = await fetch(this.queryEndpoint, {
method: 'post',
headers: { 'Content-Type': 'application/json' },
Expand All @@ -86,10 +88,15 @@ export class VestingsSubgraph {
return body?.data?.vestings[0] || {}
}

async getVestings(addresses: string[]): Promise<SubgraphVesting[]> {
async getVestings(addresses?: string[]): Promise<SubgraphVesting[]> {
const queryAddresses = addresses && addresses.length > 0
const addressesQuery = queryAddresses
? `where: { id_in: $addresses }`
: 'block: {number_gte: $blockNumber}, first: 1000'
const addressesParam = queryAddresses ? `$addresses: [String]!` : '$blockNumber: Int!'
const query = `
query getVestings($addresses: [String]!) {
vestings(where: { id_in: $addresses }){
query getVestings(${addressesParam}) {
vestings(${addressesQuery}){
id
version
duration
Expand Down Expand Up @@ -122,14 +129,15 @@ export class VestingsSubgraph {
}
}
`

const variables = { addresses }
const variables = queryAddresses
? { addresses: addresses.map((address) => address.toLowerCase()) }
: { blockNumber: OLDEST_INDEXED_BLOCK }
const response = await fetch(this.queryEndpoint, {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query,
variables: variables,
variables,
}),
})

Expand Down
26 changes: 0 additions & 26 deletions src/entities/Proposal/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -469,32 +469,6 @@ export default class ProposalModel extends Model<ProposalAttributes> {
return proposals.map(this.parse)
}

static async getProjectList(): Promise<ProposalWithProject[]> {
const status = [ProposalStatus.Passed, ProposalStatus.Enacted].map((status) => SQL`${status}`)
const types = [ProposalType.Bid, ProposalType.Grant].map((type) => SQL`${type}`)

const proposals = await this.namedQuery(
'get_project_list',
SQL`
SELECT prop.*,
proj.id as project_id,
COALESCE(json_agg(DISTINCT to_jsonb(pe.*)) FILTER (WHERE pe.id IS NOT NULL), '[]') as personnel,
COALESCE(array_agg(co.address) FILTER (WHERE co.address IS NOT NULL), '{}') AS coauthors
FROM ${table(ProposalModel)} prop
LEFT OUTER JOIN ${table(ProjectModel)} proj on prop.id = proj.proposal_id
LEFT JOIN ${table(PersonnelModel)} pe ON proj.id = pe.project_id AND pe.deleted = false
LEFT JOIN ${table(CoauthorModel)} co ON prop.id = co.proposal_id AND co.status = ${CoauthorStatus.APPROVED}
WHERE prop."deleted" = FALSE
AND prop."type" IN (${join(types)})
AND prop."status" IN (${join(status)})
GROUP BY prop.id, proj.id
ORDER BY prop."created_at" DESC
`
)

return proposals.map(this.parseProposalWithProject)
}

private static parseTimeframe(timeFrame?: string | null) {
const date = Time.utc()
switch (timeFrame) {
Expand Down
22 changes: 1 addition & 21 deletions src/entities/Proposal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -803,27 +803,7 @@ export type ProjectFunding = {
vesting?: Vesting
}

export type ProposalProject = {
id: string
project_id?: string | null
status: ProjectStatus
title: string
user: string
coAuthors?: string[]
personnel: PersonnelAttributes[]
size: number
type: ProposalType
about: string
created_at: number
updated_at: number
configuration: {
category: ProposalGrantCategory
tier: string
}
funding?: ProjectFunding
}

export type ProposalProjectWithUpdate = ProposalProject & {
export type LatestUpdate = {
update?: IndexedUpdate | null
update_timestamp?: number
}
Expand Down
3 changes: 0 additions & 3 deletions src/entities/Proposal/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ export const SITEMAP_ITEMS_PER_PAGE = 100
export const DEFAULT_CHOICES = ['yes', 'no', 'abstain']
export const REGEX_NAME = new RegExp(`^([a-zA-Z0-9]){${MIN_NAME_SIZE},${MAX_NAME_SIZE}}$`)

//TODO: avoid manually calculating cliff, use subgraph or contract method instead
export const CLIFF_PERIOD_IN_DAYS = 29

export function formatBalance(value: number | bigint) {
return numeral(value).format('0,0')
}
Expand Down
2 changes: 1 addition & 1 deletion src/entities/Updates/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export type UpdateAttributes = Partial<UpdateGeneralSection> &
discourse_topic_slug?: string
}

export type IndexedUpdate = UpdateAttributes & {
export type IndexedUpdate = Partial<UpdateAttributes> & {
index: number
}

Expand Down
122 changes: 120 additions & 2 deletions src/models/Project.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { Model } from 'decentraland-gatsby/dist/entities/Database/model'
import { SQL, table } from 'decentraland-gatsby/dist/entities/Database/utils'
import { SQL, conditional, table } from 'decentraland-gatsby/dist/entities/Database/utils'
import isEthereumAddress from 'validator/lib/isEthereumAddress'
import isUUID from 'validator/lib/isUUID'

import CoauthorModel from '../entities/Coauthor/model'
import { CoauthorStatus } from '../entities/Coauthor/types'
import { ProjectStatus } from '../entities/Grant/types'
import ProposalModel from '../entities/Proposal/model'
import { ProjectFunding } from '../entities/Proposal/types'
import { LatestUpdate, ProjectFunding, ProposalAttributes, ProposalType } from '../entities/Proposal/types'
import UpdateModel from '../entities/Updates/model'
import { UpdateAttributes } from '../entities/Updates/types'

import PersonnelModel, { PersonnelAttributes } from './Personnel'
import ProjectLinkModel, { ProjectLink } from './ProjectLink'
Expand Down Expand Up @@ -35,6 +37,30 @@ export type Project = ProjectAttributes & {
funding?: ProjectFunding
}

export type ProjectInList = Pick<Project, 'id' | 'proposal_id' | 'status' | 'title' | 'author' | 'funding'> &
Pick<ProposalAttributes, 'type' | 'configuration'> & {
latest_update?: LatestUpdate
created_at: number
updated_at: number
}

type ProposalDataForProject = Pick<
ProposalAttributes,
'enacting_tx' | 'enacted_description' | 'vesting_addresses' | 'type' | 'configuration'
> & {
proposal_created_at: Date
proposal_updated_at: Date
}

export type ProjectQueryResult = Pick<Project, 'id' | 'proposal_id' | 'status' | 'title' | 'author'> &
ProposalDataForProject & { updates?: UpdateAttributes[] }

export type UserProject = Pick<
Project,
'id' | 'proposal_id' | 'status' | 'title' | 'author' | 'personnel' | 'coauthors' | 'funding'
> &
ProposalDataForProject

export default class ProjectModel extends Model<ProjectAttributes> {
static tableName = 'projects'
static withTimestamps = false
Expand Down Expand Up @@ -96,4 +122,96 @@ export default class ProjectModel extends Model<ProjectAttributes> {
const result = await this.namedQuery<{ exists: boolean }>(`is_author_or_coauthor`, query)
return result[0]?.exists || false
}

static async getProjectsWithUpdates(from?: Date, to?: Date): Promise<ProjectQueryResult[]> {
const query = SQL`
SELECT
pr.id,
pr.proposal_id,
pr.status,
pr.title,
p.type,
p.enacting_tx,
p.enacted_description,
p.configuration,
p.user as author,
p.vesting_addresses,
p.created_at as proposal_created_at,
p.updated_at as proposal_updated_at,
COALESCE(json_agg(DISTINCT to_jsonb(ordered_updates.*)) FILTER (WHERE ordered_updates.id IS NOT NULL), '[]') as updates
FROM ${table(ProjectModel)} pr
JOIN ${table(ProposalModel)} p ON pr.proposal_id = p.id
LEFT JOIN (SELECT * FROM ${table(UpdateModel)} up ORDER BY up.created_at DESC) ordered_updates
ON pr.id = ordered_updates.project_id
WHERE 1=1
${conditional(!!from, SQL`AND pr.created_at >= ${from}`)}
${conditional(!!to, SQL`AND pr.created_at <= ${to}`)}
GROUP BY
pr.id,
pr.proposal_id,
pr.status,
pr.title,
p.created_at,
p.updated_at,
p.type,
p.enacting_tx,
p.enacted_description,
p.configuration,
p.user,
p.vesting_addresses,
p.updated_at
ORDER BY p.created_at DESC;
`

const result = await this.namedQuery<ProjectQueryResult>(`get_projects`, query)
return result || []
}

static async getUserProjects(userAddress: string): Promise<UserProject[]> {
const query = SQL`
SELECT
pr.id,
pr.proposal_id,
pr.status,
pr.title,
COALESCE(json_agg(DISTINCT to_jsonb(pe.*)) FILTER (WHERE pe.id IS NOT NULL), '[]') as personnel,
COALESCE(array_agg(co.address) FILTER (WHERE co.address IS NOT NULL), '{}') AS coauthors,
p.enacting_tx,
p.enacted_description,
p.vesting_addresses,
p.type,
p.configuration,
p.user as author,
p.created_at as proposal_created_at,
p.updated_at as proposal_updated_at
FROM ${table(ProjectModel)} pr
JOIN ${table(ProposalModel)} p ON pr.proposal_id = p.id
LEFT JOIN ${table(PersonnelModel)} pe ON pr.id = pe.project_id AND pe.deleted = false
LEFT JOIN ${table(CoauthorModel)} co ON pr.proposal_id = co.proposal_id AND co.status = ${
CoauthorStatus.APPROVED
}
WHERE
p.type = ${ProposalType.Grant} AND
(lower(p.user) = lower(${userAddress}) OR
lower(co.address) = lower(${userAddress}) OR
lower(pe.address) = lower(${userAddress}))
GROUP BY
pr.id,
pr.proposal_id,
pr.status,
pr.title,
p.enacting_tx,
p.enacted_description,
p.vesting_addresses,
p.type,
p.configuration,
p.user,
p.created_at,
p.updated_at
ORDER BY p.created_at DESC;
`

const result = await this.namedQuery<UserProject>(`get_user_projects`, query)
return result || []
}
}
Loading
Loading