diff --git a/apps/api/src/currentConfig.ts b/apps/api/src/currentConfig.ts index 6191e3762..06a431238 100644 --- a/apps/api/src/currentConfig.ts +++ b/apps/api/src/currentConfig.ts @@ -4,11 +4,13 @@ import { networkNodeUrl, networkProperties } from './overrrides'; export default { ...baseConfig, network_node_url: networkNodeUrl, - sources: [ - { - ...baseConfig.sources[0], + sources: baseConfig.sources.map((source, i) => { + if (i !== 0) return source; + + return { + ...source, contract: networkProperties.factoryAddress, start: networkProperties.startBlock - } - ] + }; + }) }; diff --git a/apps/api/src/utils.ts b/apps/api/src/utils.ts index 92d35f552..0494fd941 100644 --- a/apps/api/src/utils.ts +++ b/apps/api/src/utils.ts @@ -1,5 +1,5 @@ import fetch from 'cross-fetch'; -import { CallData, Contract, Provider, hash, shortString } from 'starknet'; +import { BigNumberish, CallData, Contract, Provider, hash, shortString } from 'starknet'; import { Contract as EthContract } from '@ethersproject/contracts'; import { JsonRpcProvider } from '@ethersproject/providers'; import { faker } from '@faker-js/faker'; @@ -17,6 +17,11 @@ import SimpleQuorumExecutionStrategyAbi from './abis/l1/SimpleQuorumExecutionStr import { networkNodeUrl, networkProperties } from './overrrides'; import { handleStrategiesParsedMetadata } from './ipfs'; +type StrategyConfig = { + address: BigNumberish; + params: BigNumberish[]; +}; + const ethProvider = new JsonRpcProvider( process.env.L1_NETWORK_NODE_URL ?? 'https://rpc.brovider.xyz/5' ); @@ -32,7 +37,7 @@ export function getCurrentTimestamp() { return Math.floor(Date.now() / 1000); } -export function toAddress(bn) { +export function toAddress(bn: any) { try { return getAddress(BigNumber.from(bn).toHexString()); } catch (e) { @@ -40,7 +45,7 @@ export function toAddress(bn) { } } -export function getUrl(uri, gateway = 'pineapple.fyi') { +export function getUrl(uri: string, gateway = 'pineapple.fyi') { const ipfsGateway = `https://${gateway}`; if (!uri) return null; if ( @@ -56,12 +61,14 @@ export function getUrl(uri, gateway = 'pineapple.fyi') { return uri; } -export async function getJSON(uri) { +export async function getJSON(uri: string) { const url = getUrl(uri); + if (!url) throw new Error('Invalid URI'); + return fetch(url).then(res => res.json()); } -export function getSpaceName(address) { +export function getSpaceName(address: string) { const seed = parseInt(hash.getSelectorFromName(address).toString().slice(0, 12)); faker.seed(seed); const noun = faker.word.noun(6); @@ -109,6 +116,7 @@ export async function handleExecutionStrategy(address: string, payload: string[] quorum = await executionContract.quorum(); } else if (executionStrategyType === 'EthRelayer') { const [l1Destination] = payload; + if (!l1Destination) throw new Error('Invalid payload for EthRelayer execution strategy'); const SimpleQuorumExecutionStrategyContract = new EthContract( l1Destination, @@ -154,10 +162,11 @@ export async function updateProposaValidationStrategy( if (Object.keys(parsed).length !== 0) { space.proposal_threshold = parsed.proposal_threshold; space.voting_power_validation_strategy_strategies = parsed.allowed_strategies.map( - strategy => `0x${strategy.address.toString(16)}` + (strategy: StrategyConfig) => `0x${strategy.address.toString(16)}` ); space.voting_power_validation_strategy_strategies_params = parsed.allowed_strategies.map( - strategy => strategy.params.map(param => `0x${param.toString(16)}`).join(',') + (strategy: StrategyConfig) => + strategy.params.map(param => `0x${param.toString(16)}`).join(',') ); } @@ -182,6 +191,7 @@ export async function handleStrategiesMetadata( ) { for (let i = 0; i < metadataUris.length; i++) { const metadataUri = metadataUris[i]; + if (!metadataUri) continue; const index = startingIndex + i; const uniqueId = `${spaceId}/${index}/${dropIpfs(metadataUri)}`; diff --git a/apps/api/src/writer.ts b/apps/api/src/writer.ts index cc4ace338..d22516c35 100644 --- a/apps/api/src/writer.ts +++ b/apps/api/src/writer.ts @@ -15,6 +15,11 @@ import { registerProposal } from './utils'; +type Strategy = { + address: string; + params: string[]; +}; + export const handleSpaceDeployed: CheckpointWriter = async ({ blockNumber, event, instance }) => { console.log('Handle space deployed'); @@ -31,9 +36,13 @@ export const handleSpaceCreated: CheckpointWriter = async ({ block, tx, event }) if (!event || !tx.transaction_hash) return; - const strategies: string[] = event.voting_strategies.map(strategy => strategy.address); - const strategiesParams = event.voting_strategies.map(strategy => strategy.params.join(',')); // different format than sx-evm - const strategiesMetadataUris = event.voting_strategy_metadata_uris.map(array => + const strategies: string[] = event.voting_strategies.map( + (strategy: Strategy) => strategy.address + ); + const strategiesParams = event.voting_strategies.map((strategy: Strategy) => + strategy.params.join(',') + ); // different format than sx-evm + const strategiesMetadataUris = event.voting_strategy_metadata_uris.map((array: string[]) => longStringToText(array) ); @@ -207,9 +216,13 @@ export const handleVotingStrategiesAdded: CheckpointWriter = async ({ rawEvent, const initialNextStrategy = space.next_strategy_index; - const strategies = event.voting_strategies.map(strategy => strategy.address); - const strategiesParams = event.voting_strategies.map(strategy => strategy.params.join(',')); - const strategiesMetadataUris = event.voting_strategy_metadata_uris.map(array => + const strategies: string[] = event.voting_strategies.map( + (strategy: Strategy) => strategy.address + ); + const strategiesParams = event.voting_strategies.map((strategy: Strategy) => + strategy.params.join(',') + ); + const strategiesMetadataUris = event.voting_strategy_metadata_uris.map((array: string[]) => longStringToText(array) ); @@ -364,7 +377,11 @@ export const handlePropose: CheckpointWriter = async ({ block, tx, rawEvent, eve for (const herodotusStrategy of herodotusStrategiesIndicies) { const [strategy, i] = herodotusStrategy; - const [l1TokenAddress] = space.strategies_params[i].split(','); + const params = space.strategies_params[i]; + if (!params) continue; + + const [l1TokenAddress] = params.split(','); + if (!l1TokenAddress) continue; try { await registerProposal({ @@ -495,8 +512,8 @@ export const handleVote: CheckpointWriter = async ({ block, tx, rawEvent, event if (proposal) { proposal.vote_count += 1; proposal.scores_total = (BigInt(proposal.scores_total) + BigInt(vote.vp)).toString(); - proposal[`scores_${vote.choice}`] = ( - BigInt(proposal[`scores_${vote.choice}`]) + BigInt(vote.vp) + proposal[`scores_${choice}`] = ( + BigInt(proposal[`scores_${choice}`]) + BigInt(vote.vp) ).toString(); await proposal.save(); } diff --git a/apps/api/test/unit/writer.test.ts b/apps/api/test/unit/writer.test.ts index d66d421ba..0576d44f2 100644 --- a/apps/api/test/unit/writer.test.ts +++ b/apps/api/test/unit/writer.test.ts @@ -1,9 +1,14 @@ import { describe, it, expect } from 'vitest'; import { mockDeep } from 'vitest-mock-extended'; -import { AsyncMySqlPool } from '@snapshot-labs/checkpoint'; +import Checkpoint, { CheckpointWriter, AsyncMySqlPool } from '@snapshot-labs/checkpoint'; import { validateAndParseAddress } from 'starknet'; -async function handleDeploy({ source, block, tx, mysql }) { +type Block = Parameters[0]['block']; +type Transaction = Parameters[0]['tx']; + +const handleDeploy: CheckpointWriter = async ({ source, block, tx, mysql }) => { + if (!source || !block) return; + console.log('Handle deploy'); const item = { id: validateAndParseAddress(source.contract), @@ -18,29 +23,33 @@ async function handleDeploy({ source, block, tx, mysql }) { }; const query = `INSERT IGNORE INTO spaces SET ?;`; await mysql.queryAsync(query, [item]); -} +}; describe('handleDeploy', () => { it('should create space with correct parameters', async () => { // TODO: Replace inputs with typed mocks const source = { - contract: '0x0625dc1290b6e936be5f1a3e963cf629326b1f4dfd5a56738dea98e1ad31b7f' + contract: '0x0625dc1290b6e936be5f1a3e963cf629326b1f4dfd5a56738dea98e1ad31b7f', + start: 1, + events: [] }; - const block = { + const block = mockDeep({ timestamp: 1652650963200 - }; - - const tx = { + }); + const tx = mockDeep({ transaction_hash: '0xabbb5161c49978ffc76369df76cdb40683e33c09145a9681553fe8e9c9a96a46' - }; - + }); + const mockInstance = mockDeep(); const mockMysql = mockDeep(); await handleDeploy({ source, block, + blockNumber: 0, + pg: null, + instance: mockInstance, tx, mysql: mockMysql }); diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json index 08adb5860..d2d3ce1f7 100644 --- a/apps/api/tsconfig.json +++ b/apps/api/tsconfig.json @@ -1,14 +1,10 @@ { + "extends": "../../tsconfig.json", "compilerOptions": { "target": "esnext", "module": "commonjs", - "rootDir": "./", - "outDir": "./dist", - "esModuleInterop": true, - "strict": true, - "noImplicitAny": false, - "resolveJsonModule": true, "moduleResolution": "Node", - "skipLibCheck": true + "rootDir": "./", + "outDir": "./dist" } }