-
Notifications
You must be signed in to change notification settings - Fork 8
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
Proposal: Arb-subgraph refactoring #585
base: main
Are you sure you want to change the base?
Conversation
…ture/si-1316-refactor-subgraph-forta-alert
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Firstly, thank you of your job.
This bot - it's really simple bot. I'd say the most simple bot around all bots, and based on this idea I'd suggest some recommendations or improvements.
Recommendations:
- Move your business logic (Balance.checker.ts) directly into handleBlock. There aren't different classes with business logic in arb-subgraph project.
-
- Few months ago I tried to remove any external dependencies like forta for providing pure tech design that would not contain anything extra deps. Only that we needed for bot working. Here I found fastify 'dependency' as http router and DI container. Speaking about performance: what kind of performance we speak about?! - '/metrics, /health' - handlers. - Strange to think about performance here.
- Speaking about DI - -higher, I texted that the bot is so simple, and it's ok to move business logic directly into HandlerBlock. From this perspective, I believe in the idea that any extra DI system is superfluous here. Also, I found that each file in project for supporting fastigy.DI has to have extra code for declaring module and creating instances of classes. They have(creating instances, of the classes) moved from main.ts into classes.
So, in arb-subgraph is added external dependency 'satisfy', but I don't see real benefits. It does not matter on real performance and creating instances. Everything would work without that extra "deps". Of course, if your team has a habit to work with that library, it might be OK. But I personally don't split up this vision. All authors of programming languages advise to have to use only that what we need and nothing more extra. I do agree with them; do not use extra, and simplify as much as possible.
Improvements:
Once I shared a picture, the bot that works under the l2 network might have time lags. So, in reality, forta-softaware has lags with l2 networks. For us, it means that bots have to iterate under l1 network.
Speaking about this bot. Bot can listen up L1 network that fetch latest l2 block and make some checks.
Also, our local infra would not support interating over l2 network. If your team is going to deploy the bot into local infra - bot must iterate over the l1 network.
Approve/Request changes - I could not be a blocker for your team. I shared my opinion about this PR, and it could also be improved. Have a good hack =)
@@ -1,14 +1,13 @@ | |||
# Build stage: compile Typescript to Javascript | |||
FROM node:16.17.1-alpine3.16 AS base | |||
FROM node:20.9.0-alpine3.18 AS base |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest using the latest node.
FROM node:20.15.1-alpine3.20 AS base
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was left as is. The refactoring only affected the code of the bot itself. Thanks - I'll fix it!
COPY --from=builder /app/node_modules ./node_modules | ||
COPY --from=builder /app/dist ./src | ||
COPY version.json ./ | ||
|
||
ENTRYPOINT ["/sbin/tini", "--"] | ||
CMD ["yarn", "run", "start:prod"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that it would work. Check pls.
If it does not work - here is a working combination:
CMD ["yarn", "run", "start:docker:prod"]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ill check it
"build": "tsc && yarn run copy-version && mkdir dist/generated/proto && make gen_proto_prod", | ||
"copy-version": "cp version.json dist", | ||
"start": "ts-node src/main.ts", | ||
"start:dev": "nodemon --watch src --watch forta.config.json -e js,ts,json --exec \"yarn run build && yarn run copy-version && forta-agent run\"", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Useless command. This commit removes forta-agent dependency
}, | ||
"packageManager": "yarn@3.3.0" | ||
"packageManager": "yarn@1.22.21" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest upgrading all deps in package.json
@@ -0,0 +1,6 @@ | |||
|
|||
# The URL of the Arbitrum RPC endpoint | |||
ARBITRUM_RPC_URL=https://arbitrum.drpc.com |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found that https://arbitrum-one.publicnode.com works better. Especially for reading some data in the past.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a sample for envs. I doubt that these URLs will be used in any work of the bot
import { FastifyPluginAsync } from 'fastify' | ||
import fp from 'fastify-plugin' | ||
import { sendUnaryData, ServerUnaryCall } from '@grpc/grpc-js' | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Empty line. If it made local "import-formatter", pls share settings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#585 (comment)
I apparently didn't find your formatting settings. I'll look again.
const event = call.request.getEvent() | ||
const block = event?.getBlock() | ||
|
||
if (!block) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe do assert(block)
(or any other a bit more appropriate check) before the logic block? To save a level of nesting and localize the error if block
happens to be undefined
l2blockDtoEvent.number, | ||
) | ||
if (finding) { | ||
findings.push(finding) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forgot to read onAppInitFindings
import { HealthCheckRequest, HealthCheckResponse, ResponseStatus, Error } from '../../generated/proto/agent_pb' | ||
import { ENVS } from '../../env' | ||
|
||
const BORDER_TIME = 15 * 60 * 1000 // 15 minutes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would suggest naming for const: 'MINUTES_15'
return null | ||
} | ||
|
||
const subgraphBillingContract = Billing__factory.connect(BILLING_ADDRESS, this.fastify.provider) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inject Billing__factory, BILLING_ADDRESS like dependencies please
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I didn't understand what you meant.
I guess the main point of the Fastify change proposal is the following (numbered third in the reasoning section):
@arwer13 please take a look |
@sergeyWh1te
|
If there are concerns about the vendor lock on fastify, then you can implement similar logic on pure ts. And can leave Express. type ServiceFactory<T, R> = (services: T) => R
type Factories<T> = {
[K in keyof T]: ServiceFactory<T, T[K]>
}
export class ServiceProvider<T> {
public services: T
constructor(services: Factories<T>) {
this.services = {} as T
const serviceNames = Object.keys(services) as (keyof T)[]
serviceNames.forEach((key: keyof T) => {
this.services[key] = services[key](this.services)
})
}
}
export type ServicesInstance = {
logger: ReturnType<typeof LoggerInit>
metrics: ReturnType<typeof MetricsInit>
alerts: ReturnType<typeof AlertsInit>
provider: ReturnType<typeof ProviderInit>
BalanceChecker: BalanceChecker
}
const services = {
logger: LoggerInit,
metrics: MetricsInit,
BalanceChecker: BalanceCheckerInit,
alerts: AlertsInit,
provider: ProviderInit,
}
const serviceProvider = new ServiceProvider(services)
// The metric's service is available here
provider.services.metrics.buildInfo.set({ commitHash: Version.commitHash }, 1)
export class BalanceChecker {
private services: Services
constructor(services: ServicesInstance) {
this.services = services
}
public async checkSubgraphBalance() {
// The provider's service is available here
console.log(this.services.provider)
// The alert's service is available here
return this.services.alerts.lowBalance()
}
}
export const BalanceCheckerInit = (services: ServicesInstance) => {
// The logger's service is available here
const { logger } = services
logger.info('BalanceChecker plugin initialized')
return new BalanceChecker(services)
} Well, or, as a last resort, completely abandon the concept of a single instance that unites all services under one namespace and leave the project structure, which will allow you to reuse most of the bot code =) |
From the point of (L2) bots unification, I don't see much difference if to use fastify or not. Anyway, duplication is removed at the level of imported files by extracting common code, persistent parameterization, placing in common files, and linking together in the main of a specific bot. There doesn't seem to be anything fundamentally different in fastify approach in comparison with the current unification approach of mine: #558 I think it is a bit better with fastify: IMO, through the pre-prepared framework structure, the structure becomes clearer and more understandable for people with a web backend background. |
Disclaimer: This is a proposal for the architectural restructuring and refactoring of some alerts-related code. I may lack complete context or understanding of the issues surrounding alert implementation.
Key Changes:
Some classes in the plugins (e.g., metrics) have been used as-is. However, any code structure can be used as needed. There is no strict requirement for using classes.
What is Fastify?
Learn more about Fastify here.
Why Choose Fastify Over Express:
Structure: arb-subgraph
plugins
folder.plugins/common
.Transferred from
l2-bridge-arbitrum
As-Is:Example of Plugin Connection:
Fastify also allows for convenient registration of server routes with custom handlers through plugins.
Example of Route Registration:
Example Logs:
This pull request aims to improve the architecture and performance of the alerts system by leveraging Fastify's advanced features and plugin system.
Your feedback and suggestions are welcome.