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

Real-world changes #1

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@

# IDE files
/.idea
constants.ts.*
39 changes: 39 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
FROM node:16-alpine AS node
FROM node AS node-with-gyp
RUN apk add g++ make python3
FROM node-with-gyp AS builder
WORKDIR /squid
ADD package.json .
ADD package-lock.json .
# remove if needed
ADD assets assets
# remove if needed
ADD db db
# remove if needed
ADD schema.graphql .
RUN npm ci
ADD tsconfig.json .
ADD src src
RUN npm run build
FROM node-with-gyp AS deps
WORKDIR /squid
ADD package.json .
ADD package-lock.json .
RUN npm ci --production
FROM node AS squid
WORKDIR /squid
COPY --from=deps /squid/package.json .
COPY --from=deps /squid/package-lock.json .
COPY --from=deps /squid/node_modules node_modules
COPY --from=builder /squid/lib lib
# remove if no assets folder
COPY --from=builder /squid/assets assets
# remove if no db folder
COPY --from=builder /squid/db db
# remove if no schema.graphql is in the root
COPY --from=builder /squid/schema.graphql schema.graphql
# remove if no commands.json is in the root
ADD commands.json .
RUN echo -e "loglevel=silent\\nupdate-notifier=false" > /squid/.npmrc
RUN npm i -g @subsquid/commands && mv $(which squid-commands) /usr/local/bin/sqd
ENV PROCESSOR_PROMETHEUS_PORT 3000
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
"name": "squid-evm-template",
"private": true,
"scripts": {
"generateProductionConstants": "NETWORK_ADDRESS='0x78163f593D1Fa151B4B7cacD146586aD2b686294' node ./utils/generateConstantsFile.js > ./src/utils/constants.ts",
"generateQAConstants": "NETWORK_ADDRESS='0x6a05DD32860C1b5351B97b4eCAAbFbc60edb102f' node ./src/utils/generateConstantsFile.js > ./src/utils/constants.ts",
"build": "rm -rf lib && tsc"
},
"dependencies": {
Expand Down
3 changes: 1 addition & 2 deletions src/handlers/domains/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@ export const handleDomainAdded = async (

const args = event.args.toObject();

const colonyContract = new ColonyContract(context, log.block, log.address);

/*
* @TODO Properly fetch the domain count
* As-is this won't work if multiple domains (within the same colony) are created
Expand All @@ -118,6 +116,7 @@ export const handleDomainAdded = async (
*/
let domainChainId = args.domainId;
if (!domainChainId) {
const colonyContract = new ColonyContract(context, log.block, log.address);
domainChainId = await colonyContract.getDomainCount();
}

Expand Down
19 changes: 7 additions & 12 deletions src/handlers/events/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { DataHandlerContext } from '@subsquid/evm-processor'
import { Store } from '@subsquid/typeorm-store'
import { Result } from 'ethers';

import { Event, Block, Transaction, Colony, Domain } from '../../model'
import { Event, Block, Transaction, Colony, Domain, ColonyExtension } from '../../model'
import { Log } from '../../types';

import { Contract as OneTxPaymentContract } from '../../abi/OneTxPayment';
import { Contract as IColonyExtension } from '../../abi/IColonyExtension';

import { getArbitraryReputationUpdateDomain } from '../domains';

Expand Down Expand Up @@ -62,12 +62,8 @@ export const handleEvent = async (

event.domain = domain;

for (const [key, value] of Object.entries(args)) {
if (typeof value === 'bigint') {
args[key] = value.toString();
}
}
event.args = JSON.stringify(args);
// For some reason, not smart enough to stringify bigints themselves
event.args = JSON.stringify(args, (key: string, value: any) => typeof value === 'bigint' ? value.toString() : value);

await context.store.insert(event);
};
Expand All @@ -78,15 +74,14 @@ export const handleExtensionEvent = async (
decodedLog: Result,
eventName: string,
) => {
// For now, can be any extension contract
const extension = new OneTxPaymentContract(context, log.block, log.address);
const colony = await extension.getColony();
const extension = await context.store.get(ColonyExtension, { where: { id: log.address.toLowerCase() }, relations: {'colony': true }});
if (!extension) { return; }

return handleEvent(
context,
log,
decodedLog,
eventName,
colony.toLowerCase(),
extension.colony.id
);
};
37 changes: 22 additions & 15 deletions src/handlers/extensions/oneTxPayment.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DataHandlerContext } from '@subsquid/evm-processor'
import { Store } from '@subsquid/typeorm-store'

import { Payment, OneTxPayment, Domain, Transaction } from '../../model'
import { Payment, OneTxPayment, Domain, Transaction, ColonyExtension } from '../../model'
import { Log } from '../../types';

import { abi as OneTxPaymentAbi, Contract as OneTxPaymentContract } from '../../abi/OneTxPayment';
Expand All @@ -19,12 +19,13 @@ export const handleOneTxPaymentMade = async (

const args = event.args.toObject();

const chainExtension = new OneTxPaymentContract(context, log.block, log.address);
const colonyAddress = await chainExtension.getColony();
const colonyContract = new ColonyContract(context, log.block, colonyAddress);
const extension = await context.store.get(ColonyExtension, { where: { id: log.address }, relations: {"colony": true} });
if (!extension) {
throw new Error("Extension not known, but should be");
}
const colonyAddress = extension.colony.id;

const colonySubsquidId = `${colonyAddress.toLowerCase()}`;
const rootDomainSubsquidId = `${colonySubsquidId}_domain_1`;
const oneTxPaymentSubsquidId = `${colonySubsquidId}_oneTxPayment_${args.nPayouts.toString()}_${args.fundamentalId.toString()}`;
const paymentSubsquidId = `${colonySubsquidId}_payment_${args.fundamentalId.toString()}`;

Expand All @@ -37,16 +38,22 @@ export const handleOneTxPaymentMade = async (
const transaction = await context.store.get(Transaction, { where: { id: log.transactionHash.toLowerCase() } });
oneTxPayment.transaction = transaction;

const rootDomain = await context.store.get(Domain, { where: { id: rootDomainSubsquidId } });
const payment = await context.store.get(Payment, { where: { id: paymentSubsquidId } });
const chainPayment = await colonyContract.getPayment(payment?.paymentChainId || args.fundamentalId);

// for some reason the Payment object from the database doesn't return neither the domain or the colony props
// so we need to fetch the domain id from the chain directly and fetch the Domain object like that
const paymentDomain = await context.store.get(Domain, { where: { id: `${colonySubsquidId}_domain_${chainPayment.domainId.toString()}` } });

oneTxPayment.payment = payment;
oneTxPayment.domain = paymentDomain || rootDomain;
const payment = await context.store.get(Payment, { where: { id: paymentSubsquidId }, relations: { "domain": true }});

if (!payment && args.nPayouts.toString() === '1') {
throw new Error("Payment not known, but should be");
}

if (!payment) {
// Then this used an expenditure. We go to the chain to get the corresponding domain
const colonyContract = new ColonyContract(context, log.block, colonyAddress);
const expenditure = await colonyContract.getExpenditure(args.fundamentalId);
const domain = await context.store.get(Domain, { where: { id: `${colonySubsquidId}_domain_${expenditure.domainId.toString()}` } });
oneTxPayment.domain = domain;
} else {
oneTxPayment.payment = payment;
oneTxPayment.domain = payment.domain;
}

await context.store.insert(oneTxPayment);
};
42 changes: 31 additions & 11 deletions src/handlers/extensions/votingReputation.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { DataHandlerContext } from '@subsquid/evm-processor'
import { Store } from '@subsquid/typeorm-store'

import { Motion, Transaction, Domain, Colony } from '../../model'
import { Motion, Transaction, Domain, Colony, ColonyExtension } from '../../model'
import { Log } from '../../types';

import { abi as VotingReputationAbi, Contract as VotingReputationContract } from '../../abi/VotingReputation';
import { bigint } from '../../model/generated/marshal';

export const handleMotionCreated = async (
context: DataHandlerContext<Store, {}>,
Expand All @@ -20,15 +21,27 @@ export const handleMotionCreated = async (

const votingReputationInstanceAddress = log.address.toLowerCase();
const chainExtension = new VotingReputationContract(context, log.block, votingReputationInstanceAddress);
const colonyAddress = await chainExtension.getColony();
const extension = await context.store.get(ColonyExtension, { where: { id: votingReputationInstanceAddress }, relations: {"colony": true} });
if (!extension) {
throw new Error("Extension not known, but should be");
}
const colonyAddress = extension.colony.id;
const chainMotion = await chainExtension.getMotion(args.motionId);
const totalStakeFraction = await chainExtension.getTotalStakeFraction();

const motionSubsquidId = `${colonyAddress.toLowerCase()}_motion_${votingReputationInstanceAddress}_${args.motionId.toString()}`;
const colonySubsquidId = `${colonyAddress.toLowerCase()}`;
const domainSubsquidId = `${colonySubsquidId}_domain_${args.domainId.toString()}`;

const motion = new Motion({ id: motionSubsquidId });
let motion = await context.store.get(Motion, { where: { id: motionSubsquidId } });
if ( motion ) {
// This was a motion created when we had the storage misalignment issue, causing
// motion ids to be reused. We returned to using the old counting, and tracked/
// restored all funds, so we can ignore this motion.
return;
}

motion = new Motion({ id: motionSubsquidId });
motion.fundamentalChainId = args.motionId;
motion.action = chainMotion.action;
motion.agent = args.creator.toLowerCase();
Expand Down Expand Up @@ -64,15 +77,20 @@ export const handleMotionStaked = async (
const args = event.args.toObject();

const votingReputationInstanceAddress = log.address.toLowerCase();
const chainExtension = new VotingReputationContract(context, log.block, votingReputationInstanceAddress);
const colonyAddress = await chainExtension.getColony();
const chainMotion = await chainExtension.getMotion(args.motionId);
const extension = await context.store.get(ColonyExtension, { where: { id: votingReputationInstanceAddress }, relations: {"colony": true} });
if (!extension) {
throw new Error("Extension not known, but should be");
}
const colonyAddress = extension.colony.id;

const motionSubsquidId = `${colonyAddress.toLowerCase()}_motion_${votingReputationInstanceAddress}_${args.motionId.toString()}`;

const motion = await context.store.get(Motion, { where: { id: motionSubsquidId } });
if (motion) {
motion.stakes = chainMotion.stakes.map((stake) => stake.toString());
if (!motion.stakes) {
throw new Error("Motion stakes not set, but should be");
}
motion.stakes[event.args.vote] = (BigInt(motion.stakes[event.args.vote]) + BigInt(event.args.amount)).toString();
await context.store.save(motion);
}
};
Expand All @@ -90,15 +108,17 @@ export const handleMotionEscalated = async (
const args = event.args.toObject();

const votingReputationInstanceAddress = log.address.toLowerCase();
const chainExtension = new VotingReputationContract(context, log.block, votingReputationInstanceAddress);
const colonyAddress = await chainExtension.getColony();
const chainMotion = await chainExtension.getMotion(args.motionId);
const extension = await context.store.get(ColonyExtension, { where: { id: votingReputationInstanceAddress }, relations: {"colony": true} });
if (!extension) {
throw new Error("Extension not known, but should be");
}
const colonyAddress = extension.colony.id;

const motionSubsquidId = `${colonyAddress.toLowerCase()}_motion_${votingReputationInstanceAddress}_${args.motionId.toString()}`;

const motion = await context.store.get(Motion, { where: { id: motionSubsquidId } });
if (motion) {
motion.escalated = chainMotion.escalated;
motion.escalated = true;
await context.store.save(motion);
}
};
77 changes: 34 additions & 43 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
} from './handlers';

import { checkIsColony, checkIsExtension, checkIsToken } from './utils';
import { COLONY_NETWORK_ADDRESS } from './utils/constants';

processor.run(new TypeormDatabase({ supportHotBlocks: true }), async (context) => {
// ******
Expand All @@ -50,51 +51,41 @@ processor.run(new TypeormDatabase({ supportHotBlocks: true }), async (context) =
for (const block of context.blocks) {
for (const log of block.logs as Array<Log>) {
const [topic] = log.topics;
if (topic) {
// If events are on 'untrusted' entities i.e. anything without a canonical single deployment,
// we check if it is actually something we want to pay attention to (e.g. that 'DomainAdded' was
// emitted by a Colony that was deployed with ColonyNetwork)
if (ColonyAbi.parseLog(log)){
if (!await checkIsColony(context, log.block.height, log.address)){
continue;
}
} else if (VotingReputationAbi.parseLog(log)) {
if (!await checkIsExtension(context, log.block.height, log.address)){
continue;
}
} else if (OneTxPaymentAbi.parseLog(log)) {
if (!await checkIsExtension(context, log.block.height, log.address)){
continue;
}
} else if (TokenAbi.parseLog(log)) {
if (!await checkIsToken(context, log.block.height, log.address)){
continue;
}
}
let event;

// handle the event first to save the event entity,
// transaction entity and block entity
const event = ColonyNetworkAbi.parseLog(log) || ColonyAbi.parseLog(log);
if (event) {
await handleEvent(
context,
log,
event.args,
event.signature,
log.address.toLowerCase(), // if set to colony address will add an associatedColony to the event enti
);
}
// Check event is on an object we know the interface of, before we
// try decoding. We have to do this first, before trying to parse any logs
if (
log.address !== COLONY_NETWORK_ADDRESS &&
!await checkIsColony(context, log.block.height, log.address) &&
!await checkIsExtension(context, log.block.height, log.address)
) {
continue;
}

// One TX Extension events
const extensionEvent = OneTxPaymentAbi.parseLog(log) || VotingReputationAbi.parseLog(log);
if (extensionEvent) {
await handleExtensionEvent(
context,
log,
extensionEvent.args,
extensionEvent.signature,
);
}
// We ignore 'metatransaction executed', which is on many of our contracts and significantly
// complicates things
if (topic === ColonyEvents.MetaTransactionExecuted.topic) {
continue;
}

if (topic) {
if ((event = ColonyAbi.parseLog(log)) && event){
await handleEvent(context, log, event.args, event.signature, log.address);
} else if ((event = ColonyNetworkAbi.parseLog(log)) && event) {
await handleEvent(context, log, event.args, event.signature);
} else if ((event = VotingReputationAbi.parseLog(log)) && event) {
await handleExtensionEvent(context, log, event.args, event.signature);
} else if ((event = OneTxPaymentAbi.parseLog(log)) && event) {
await handleExtensionEvent(context, log, event.args, event.signature);
}
// Tokens are a problem. ERC20 vs ERC721 transfer event with last parameter
// indexed or not means that this call might fail.
// } else if ((event = TokenAbi.parseLog(log)) && event) {
// if (!await checkIsToken(context, log.block.height, log.address)){
// continue;
// }
// }

// handle the rest of the custom events / handlers
switch (topic) {
Expand Down
Loading