diff --git a/apps/api/src/ipfs.ts b/apps/api/src/ipfs.ts index 6c73c6f90..0d9aebd6d 100644 --- a/apps/api/src/ipfs.ts +++ b/apps/api/src/ipfs.ts @@ -22,6 +22,7 @@ export async function handleSpaceMetadata(space: string, metadataUri: string) { spaceMetadataItem.wallet = ''; spaceMetadataItem.executors = []; spaceMetadataItem.executors_types = []; + spaceMetadataItem.treasuries = []; spaceMetadataItem.delegations = []; const metadata: any = metadataUri ? await getJSON(metadataUri) : {}; @@ -34,6 +35,11 @@ export async function handleSpaceMetadata(space: string, metadataUri: string) { if (metadata.properties) { if (metadata.properties.cover) spaceMetadataItem.cover = metadata.properties.cover; + if (metadata.properties.treasuries) { + spaceMetadataItem.treasuries = metadata.properties.treasuries.map((treasury: any) => + JSON.stringify(treasury) + ); + } if (metadata.properties.delegations) { spaceMetadataItem.delegations = metadata.properties.delegations.map((delegation: any) => JSON.stringify(delegation) @@ -45,9 +51,6 @@ export async function handleSpaceMetadata(space: string, metadataUri: string) { if (metadata.properties.voting_power_symbol) { spaceMetadataItem.voting_power_symbol = metadata.properties.voting_power_symbol; } - if (metadata.properties.wallets && metadata.properties.wallets.length > 0) { - spaceMetadataItem.wallet = metadata.properties.wallets[0]; - } if ( metadata.properties.execution_strategies && metadata.properties.execution_strategies_types diff --git a/apps/api/src/schema.gql b/apps/api/src/schema.gql index dc1586f02..b51926148 100644 --- a/apps/api/src/schema.gql +++ b/apps/api/src/schema.gql @@ -39,6 +39,7 @@ type SpaceMetadataItem { avatar: String! cover: String! external_url: String! + treasuries: [String]! delegations: [String]! github: String! twitter: String! @@ -47,6 +48,8 @@ type SpaceMetadataItem { wallet: String! executors: [String]! executors_types: [String]! + # this is dummy field, we should add support for many-to-many in the future + executors_strategies: [ExecutionStrategy]! @derivedFrom(field: "id") } type VotingPowerValidationStrategiesParsedMetadataItem { @@ -73,6 +76,16 @@ type StrategiesParsedMetadataDataItem { payload: String } +type ExecutionStrategy { + id: String! + type: String! + quorum: BigDecimalVP! + treasury: String + treasury_chain: String + timelock_veto_guardian: String + timelock_delay: BigInt! +} + type Proposal { id: String! proposal_id: Int! diff --git a/apps/subgraph-api/schema.graphql b/apps/subgraph-api/schema.graphql index 67d6a8e32..13d022228 100644 --- a/apps/subgraph-api/schema.graphql +++ b/apps/subgraph-api/schema.graphql @@ -34,6 +34,7 @@ type SpaceMetadata @entity { avatar: String! cover: String! external_url: String! + treasuries: [String!]! delegations: [String!]! github: String! twitter: String! @@ -42,6 +43,7 @@ type SpaceMetadata @entity { wallet: String! executors: [Bytes!]! executors_types: [String!]! + executors_strategies: [ExecutionStrategy!]! } type StrategiesParsedMetadata @entity { @@ -72,6 +74,8 @@ type ExecutionStrategy @entity { id: ID! type: String! quorum: BigDecimal! + treasury: Bytes + treasury_chain: Int timelock_veto_guardian: Bytes timelock_delay: BigInt! } diff --git a/apps/subgraph-api/src/ipfs.ts b/apps/subgraph-api/src/ipfs.ts index 51969e5b5..e7301dc72 100644 --- a/apps/subgraph-api/src/ipfs.ts +++ b/apps/subgraph-api/src/ipfs.ts @@ -18,21 +18,36 @@ export function handleSpaceMetadata(content: Bytes): void { spaceMetadata.about = description ? description.toString() : '' spaceMetadata.avatar = avatar ? avatar.toString() : '' spaceMetadata.external_url = externalUrl ? externalUrl.toString() : '' + spaceMetadata.wallet = '' + spaceMetadata.treasuries = [] spaceMetadata.delegations = [] if (properties) { const propertiesObj = properties.toObject() + let treasuries = propertiesObj.get('treasuries') let delegations = propertiesObj.get('delegations') let cover = propertiesObj.get('cover') let github = propertiesObj.get('github') let twitter = propertiesObj.get('twitter') let discord = propertiesObj.get('discord') let votingPowerSymbol = propertiesObj.get('voting_power_symbol') - let wallets = propertiesObj.get('wallets') let executionStrategies = propertiesObj.get('execution_strategies') let executionStrategiesTypes = propertiesObj.get('execution_strategies_types') + if (treasuries) { + let jsonObj: JSON.Obj = JSON.parse(content) + let jsonPropertiesObj = jsonObj.getObj('properties') + if (jsonPropertiesObj) { + let jsonTreasuriesArr = jsonPropertiesObj.getArr('treasuries') + if (jsonTreasuriesArr) { + spaceMetadata.treasuries = jsonTreasuriesArr._arr.map((treasury) => + treasury.toString() + ) + } + } + } + if (delegations) { let jsonObj: JSON.Obj = JSON.parse(content) let jsonPropertiesObj = jsonObj.getObj('properties') @@ -51,8 +66,6 @@ export function handleSpaceMetadata(content: Bytes): void { spaceMetadata.twitter = twitter ? twitter.toString() : '' spaceMetadata.discord = discord ? discord.toString() : '' spaceMetadata.voting_power_symbol = votingPowerSymbol ? votingPowerSymbol.toString() : 'VP' - spaceMetadata.wallet = - wallets && wallets.toArray().length > 0 ? wallets.toArray()[0].toString() : '' if (executionStrategies && executionStrategiesTypes) { spaceMetadata.executors = executionStrategies @@ -61,18 +74,22 @@ export function handleSpaceMetadata(content: Bytes): void { spaceMetadata.executors_types = executionStrategiesTypes .toArray() .map((type) => type.toString()) + spaceMetadata.executors_strategies = executionStrategies + .toArray() + .map((strategy) => strategy.toString()) } else { spaceMetadata.executors = [] spaceMetadata.executors_types = [] + spaceMetadata.executors_strategies = [] } } else { spaceMetadata.cover = '' spaceMetadata.github = '' spaceMetadata.twitter = '' spaceMetadata.discord = '' - spaceMetadata.wallet = '' spaceMetadata.executors = [] spaceMetadata.executors_types = [] + spaceMetadata.executors_strategies = [] } spaceMetadata.save() diff --git a/apps/subgraph-api/src/mapping.ts b/apps/subgraph-api/src/mapping.ts index 14f16f731..c9a08c142 100644 --- a/apps/subgraph-api/src/mapping.ts +++ b/apps/subgraph-api/src/mapping.ts @@ -1,4 +1,11 @@ -import { Address, BigDecimal, BigInt, Bytes, dataSource } from '@graphprotocol/graph-ts' +import { + Address, + BigDecimal, + BigInt, + Bytes, + DataSourceContext, + dataSource, +} from '@graphprotocol/graph-ts' import { ProxyDeployed } from '../generated/ProxyFactory/ProxyFactory' import { AvatarExecutionStrategy } from '../generated/ProxyFactory/AvatarExecutionStrategy' import { TimelockExecutionStrategy } from '../generated/ProxyFactory/TimelockExecutionStrategy' @@ -39,18 +46,31 @@ const MASTER_SIMPLE_QUORUM_TIMELOCK = Address.fromString( '0xf2A1C2f2098161af98b2Cc7E382AB7F3ba86Ebc4' ) +const CHAIN_IDS = new Map() +CHAIN_IDS.set('mainnet', 1) +CHAIN_IDS.set('goerli', 5) +CHAIN_IDS.set('sepolia', 11155111) +CHAIN_IDS.set('matic', 137) +CHAIN_IDS.set('arbitrum-one', 42161) +CHAIN_IDS.set('linea-testnet', 59140) + export function handleProxyDeployed(event: ProxyDeployed): void { + let network = dataSource.network() + if (event.params.implementation.equals(MASTER_SPACE)) { SpaceTemplate.create(event.params.proxy) } else if (event.params.implementation.equals(MASTER_SIMPLE_QUORUM_AVATAR)) { let executionStrategyContract = AvatarExecutionStrategy.bind(event.params.proxy) let typeResult = executionStrategyContract.try_getStrategyType() let quorumResult = executionStrategyContract.try_quorum() - if (typeResult.reverted || quorumResult.reverted) return + let targetAddress = executionStrategyContract.try_target() + if (typeResult.reverted || quorumResult.reverted || targetAddress.reverted) return let executionStrategy = new ExecutionStrategy(event.params.proxy.toHexString()) executionStrategy.type = typeResult.value executionStrategy.quorum = new BigDecimal(quorumResult.value) + if (CHAIN_IDS.has(network)) executionStrategy.treasury_chain = CHAIN_IDS.get(network) + executionStrategy.treasury = targetAddress.value executionStrategy.timelock_delay = new BigInt(0) executionStrategy.save() } else if (event.params.implementation.equals(MASTER_SIMPLE_QUORUM_TIMELOCK)) { @@ -71,6 +91,8 @@ export function handleProxyDeployed(event: ProxyDeployed): void { let executionStrategy = new ExecutionStrategy(event.params.proxy.toHexString()) executionStrategy.type = typeResult.value executionStrategy.quorum = new BigDecimal(quorumResult.value) + if (CHAIN_IDS.has(network)) executionStrategy.treasury_chain = CHAIN_IDS.get(network) + executionStrategy.treasury = event.params.proxy executionStrategy.timelock_veto_guardian = timelockVetoGuardianResult.value executionStrategy.timelock_delay = timelockDelayResult.value executionStrategy.save() diff --git a/apps/ui/src/components/App/Nav.vue b/apps/ui/src/components/App/Nav.vue index da1f5cc18..2aba00c48 100644 --- a/apps/ui/src/components/App/Nav.vue +++ b/apps/ui/src/components/App/Nav.vue @@ -24,8 +24,6 @@ const space = computed(() => : null ); -const { treasury } = useTreasury(space); - const isController = computed(() => space.value ? compareAddresses(space.value.controller, web3.value.account) : false ); @@ -47,7 +45,7 @@ const navigationConfig = computed(() => ({ } } : undefined), - ...(treasury.value + ...(space.value?.treasuries?.length ? { treasury: { name: 'Treasury', diff --git a/apps/ui/src/components/EditorExecution.vue b/apps/ui/src/components/EditorExecution.vue index f53ba144d..850032a35 100644 --- a/apps/ui/src/components/EditorExecution.vue +++ b/apps/ui/src/components/EditorExecution.vue @@ -2,19 +2,19 @@ import Draggable from 'vuedraggable'; import { getNetwork } from '@/networks'; import { simulate } from '@/helpers/tenderly'; -import { Transaction as TransactionType, Space, SelectedStrategy } from '@/types'; +import { Transaction as TransactionType, Space, SpaceMetadataTreasury } from '@/types'; const model = defineModel({ required: true }); const props = defineProps<{ - space?: Space; - selectedExecutionStrategy: SelectedStrategy; + space: Space; + treasuryData: SpaceMetadataTreasury; }>(); const uiStore = useUiStore(); -const { treasury } = useTreasury(toRef(props, 'space')); +const { treasury } = useTreasury(props.treasuryData); const editedTx: Ref = ref(null); const modalState: Ref<{ @@ -31,15 +31,6 @@ const simulationState: Ref<'SIMULATING' | 'SIMULATION_SUCCEDED' | 'SIMULATION_FA ref(null); const network = computed(() => (props.space ? getNetwork(props.space.network) : null)); -const currentTreasury = computed(() => - props.selectedExecutionStrategy.type === 'SimpleQuorumTimelock' && network.value - ? { - wallet: props.selectedExecutionStrategy.address, - network: network.value.baseChainId, - networkId: props.space?.network - } - : treasury.value -); function addTx(tx: TransactionType) { const newValue = [...model.value]; @@ -72,15 +63,11 @@ function editTx(index: number) { } async function handleSimulateClick() { - if (simulationState.value !== null || !currentTreasury.value) return; + if (simulationState.value !== null || !treasury.value) return; simulationState.value = 'SIMULATING'; - const valid = await simulate( - currentTreasury.value.network, - currentTreasury.value.wallet, - model.value - ); + const valid = await simulate(treasury.value.network, treasury.value.wallet, model.value); if (valid) { simulationState.value = 'SIMULATION_SUCCEDED'; @@ -98,11 +85,11 @@ watch(model.value, () => {