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

Ezequiel Challenge Solution #3

Open
wants to merge 2 commits into
base: main
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
build
.env
22 changes: 22 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM node:12.18-alpine as builder
RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
WORKDIR /home/node/app
COPY package*.json ./
RUN npm config set unsafe-perm true
RUN npm install -g typescript
RUN npm install -g ts-node
USER node
RUN npm install
COPY --chown=node:node . .
RUN npm run build

FROM node:12.18-alpine
RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
WORKDIR /home/node/app
COPY package*.json ./
USER node
RUN npm install --production
COPY --from=builder /home/node/app/build ./build
COPY --chown=node:node .env .
EXPOSE 8000
CMD [ "node", "build/index.js" ]
62 changes: 19 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,19 @@

## Cryptologic web3/backend application challenge.

The main goal of this challenge is to create a backend application writen in nodejs/typescript that consumes, processes and store blockchain data.

To achieve this, the following items are required.

- Create a fork of the current repository, once the challenge is done submit a proper PR.
- Instantiate a web3 o etherjs object with an public RPC (in this case we will use Avalanche EVM C-Chain)
- create a MongoDB database connection withing the runtime

Having this transaction as a starting point [0x508217c172c3cfe006ee9ca5bef621ba11a359461bacfc0494f1449a7d00f443](https://snowtrace.io/tx/0x508217c172c3cfe006ee9ca5bef621ba11a359461bacfc0494f1449a7d00f443) it is required to:

- Use web3.js or ethers.js proper methods to fetch transaction data details

- Fetch and save main contract ABI and bytecode and save them into a file and database

- Understand and parse the event log to show in a básic REST response the decoded events and its parameters

- Create a basic REST endpoint that once queried saves the parsed data into a MongoDB document.

- Create a basic REST endpoint to get the previously saved data.


Must have:

- Basic error handling
- Basic README file
- Basic explaination of the work done

Extra points:
- unit testing
- Docker build

#### References:

[Event logs](https://medium.com/mycrypto/understanding-event-logs-on-the-ethereum-blockchain-f4ae7ba50378)

[Web3 doc](https://web3js.readthedocs.io/en/1.0/web3-eth.html)

[EthersJS dock](https://docs.ethers.io/v5/)

[topics & getPastLogs](https://ethereum.stackexchange.com/questions/61585/how-to-setup-topics-for-function-getpastlogs)
## Ezequiel web3/backend application challenge Solution.

Project Structure

- Dependency Injection for instanciating classes
- Here we have a di-container that will register, in singleton scope, Services, Repositories and External Providers. Advantage: Responsible for instantiation in one place.
- API
- We have routes and middlewares for the REST API. Inside of API we will find 2 endpoints:
* Get All Transactions (This endpoint has some limitations, returns everything it finds in database. One improvement will be adding pagination and query params. Other improvemnt is to add Json Validation schema for POST methods).
- Middlewares: We have an error handler middleware responsible for capturing and formating errors.
- Services
- We have services to be consumed from API and Workers.
- Services has services.interface responsible for declaring all services contracts.
- Smart Contract Component
- Here we have a package responsible for interacting with Ethereum Network, create contracts and retrieve transaction information from the network.
- We have ABI provider that creates a contract with SmartContractService. ABI provider has 2 providers that get abi information.
- Workers
- Here we register for events. In this case only have the possibility to register event for Joe Doe contract address. Events will be saved to DB in order to be consumed by API
- Further enhancement: Have the possibility to register events for a lot of addresses.
74 changes: 74 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import express, { Express, Request, Response } from "express";
import dotenv from "dotenv";
import cors from "cors";
import helmet from "helmet";
import bodyParser from "body-parser";
import morgan from "morgan";
import fs from "fs";
import path from "path";

import diContainer from "./src/di-container";
import { IContractConsumer } from "./src/workers/contractConsumer.interface";
import { transactionRoutes } from "./src/api/routes/transaction.routes";
import { initDb } from "./src/db/db";
import errorHandler from "./src/api/middlewares/error-handler.middleware";
import AppConfig from "./src/config/app.config";
import swaggerUi from "swagger-ui-express";

dotenv.config();

var accessLogStream = fs.createWriteStream(
path.join(path.resolve("./"), "access.log"),
{
flags: "a",
}
);

const app: Express = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.use(helmet());
app.use(cors());

//Using logger middleware
app.use(morgan("combined", { stream: accessLogStream }));

app.use(transactionRoutes);
app.use(errorHandler);

//Initialize DB
initDb();

app.get("/health", (req: Request, res: Response) => {
res.send("Server is running ok");
});

const server = app.listen(AppConfig.PORT, () => {
console.log(`Server is running at https://localhost:${AppConfig.PORT}`);
});

//Start Consumer
diContainer.get<IContractConsumer>("IContractConsumer").start();

app.use(
"/docs",
swaggerUi.serve,
swaggerUi.setup(undefined, {
swaggerOptions: {
url: "./swagger.json",
},
})
);

//Todo: Move this to termination signals.
process.on("exit", () => {
console.log("Server is shutting down");
server.close();
});

process.on("SIGINT", () => {
console.log("Server is shutting down");
server.close();
});
Loading