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

chore: update docs #58

Merged
merged 3 commits into from
Jul 30, 2024
Merged
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 .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
NODE_ENV?
TEST_GOLDRUSH_API_KEY?
PORT?
3 changes: 2 additions & 1 deletion .prettierrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
"vueIndentScriptAndStyle": false,
"endOfLine": "lf",
"embeddedLanguageFormatting": "auto",
"singleAttributePerLine": false
"singleAttributePerLine": false,
"plugins": ["@trivago/prettier-plugin-sort-imports"]
}
72 changes: 15 additions & 57 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<div align="center">
<img alt="GoldRush Kit Logo" src="assets/goldrush-decoder-banner.png" style="max-width: 100%;"/>
<img alt="GoldRush Kit Logo" src="./assets/goldrush-decoder-banner.png" style="max-width: 100%;"/>
</div>

<p align="center">
<img src="https://img.shields.io/github/license/covalenthq/goldrush-decoder" alt="MIT">
</p>

<h1 align="center">
Decode unstructured, raw event logs into structured data with a simple API.
Decode unstructured, raw event logs into structured data with a simple API.
</h1>

<div align="center">
Open-source. Public Good. 200+ Chains.
Open-source. Public Good. 200+ Chains.
</div>

<br>
Expand Down Expand Up @@ -39,9 +39,9 @@ This repository contains the logic for decoding a `raw_log_event` of a transacti
```ts
GoldRushDecoder.on(
"<protocol-name>:<EventName>",
["<chain_name_1>", "<chain_name_2>"],
["<chain_name_1>", "<chain_name_2>", ...],
ABI as Abi,
async (log_event, tx, chain_name, covalent_client): Promise<EventType> => {
async (log_event, tx, chain_name, covalent_client, options): Promise<EventType> => {
<!-- decoding logic -->
}
);
Expand All @@ -52,33 +52,28 @@ This repository contains the logic for decoding a `raw_log_event` of a transacti
1. **Event Id**: A case-sensitive string concatenation of the `protocol name` with the `event name` by a `:`.
2. **Chain Names**: An array of all the chains the defined decoding function will run for.
3. **ABI**: The ABI of the contract on which the event exists.
4. **Decoding Function**: The actual decoding function, it has 3 arguments passed to it:
4. **Decoding Function**: The actual decoding function, it has 5 arguments passed to it:
1. `log_event`: The raw log event that is being decoded.
2. `tx`: The transaction object that generated this log.
3. `chain_name`: Name of the chain to which the log belongs to.
4. `covalent_client`: The covalent client created with your covalent API key.
5. `options`: Query parameters attached to the request for refining the response. These are of the following types
1. `raw_logs`: A `boolean` that attaches the raw log of the event along with the decoded response
2. `min_usd`: A minimum number value for a transaction to have to be decoded

3. `fallback`: Creates a fallback function for the specified event name. This function is not linked to any chain or contract. Its declaration is:

```ts
GoldRushDecoder.fallback(
"EventName",
ABI as Abi,
async (log_event, tx, chain_name, covalent_client): Promise<EventType> => {
async (log_event, tx, chain_name, covalent_client, options): Promise<EventType> => {
<!-- decoding logic -->
}
);
```

The method has 3 arguments:

1. **Event Name**: A case-sensitive name of the event to be decoded.
2. **ABI**: The ABI of the contract on which the event exists.
3. **Decoding Function**: The actual decoding function, it has 3 arguments passed to it:
1. `log_event`: The raw log event that is being decoded.
2. `tx`: The transaction object that generated this log.
3. `chain_name`: Name of the chain to which the log belongs to.
4. `covalent_client`: The covalent client created with your covalent API key.
The fallback method has all the same arguments as the [`on`](./README.md#knowledge-primer) arguments excluding the `Chain Names` array

4. `decode`: The function that chooses which decoding function needs to be called for which log event. It collects all the decoded events for a transaction and returns them in an array of structured data. It is run when the API server receives a request.

Expand All @@ -92,7 +87,7 @@ Follow the following steps to start the development server of the **GoldRush Dec
yarn install
```

2. Setup the environmental variables. Refer to [.env.example](.env.example) for the list of environmental variables and store them in `.env` at the root level of the repository.
2. Setup the environmental variables. Refer to [.env.example](./.env.example) for the list of environmental variables and store them in `.env` at the root level of the repository.

3. Start the server

Expand All @@ -104,7 +99,7 @@ Follow the following steps to start the development server of the **GoldRush Dec

### 2. API Endpoints

1. `/api/v1`: The default endpoint for the v1 of the server. A header of the key `x-covalent-api-key` with the value as the [Covalent API key](https://www.covalenthq.com/platform/apikey/) is **mandatory** for the Decoder to work.
1. `/api/v1`: The default endpoint for the v1 of the server. A header of the key `x-goldrush-api-key` with the value as the [GoldRush API key](https://goldrush.dev/platform/apikey/) is **mandatory** for the Decoder to work.

1. `/tx/decode`: Decodes a transaction of a chain.

Expand All @@ -115,7 +110,7 @@ Follow the following steps to start the development server of the **GoldRush Dec

```bash
curl --location 'http://localhost:<PORT>/api/v1/tx/decode' \
--header 'x-covalent-api-key: <COVALENT_API_KEY>' \
--header 'x-goldrush-api-key: <GOLDRUSH_API_KEY>' \
--header 'Content-Type: application/json' \
--data '{
"chain_name": "<CHAIN_NAME>",
Expand All @@ -140,44 +135,7 @@ Follow the following steps to add a Decoding logic for an event from a contract

This will modify the configs added to the [Protocols](services/protocols) folder. A config will be added to `${protocol_name}.configs.ts`. A sample decoder with a dummy event name (`<EVENT NAME>`) will be added to `${protocol_name}.decoders.ts`. Along with this, a test file `${protocol_name}.test.ts` will also be created which needs to be fixed so that the test passes.

4. In `${protocol_name}.decoders.ts`, a decoding logic declaration (`Decoder.on(...) {...}`) will be exposed wherein the decoding logic needs to be implemented. The return type of the decoding function expects:

```ts
export interface EventType {
category: DECODED_EVENT_CATEGORY;
action: DECODED_ACTION;
name: string;
protocol?: {
name: string;
logo: string;
};
tokens?: {
heading: string;
value: string;
decimals: number;
ticker_symbol: string | null;
ticker_logo: string | null;
pretty: string;
}[];
nfts?: {
heading: string;
collection_name: string | null;
token_identifier: string | null;
collection_address: string;
images: {
default: string | null;
256: string | null;
512: string | null;
1024: string | null;
};
}[];
details?: {
heading: string;
value: string;
type: "address" | "text";
}[];
}
```
4. In `${protocol_name}.decoders.ts`, a decoding logic declaration (`Decoder.on(...)`) will be exposed wherein the decoding logic needs to be implemented. The return type of the decoding function expects the return type [`EventType`](./services/decoder/decoder.types.ts):

## Contributing

Expand Down
10 changes: 5 additions & 5 deletions api/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { txRouter } from "../microservices/tx/tx.routes";
import { GoldRushDecoder } from "../services";
import { timestampParser } from "../utils/functions";
import cors from "cors";
import { config as dotenvConfig } from "dotenv";
import express, {
type Express,
type Request,
type Response,
type NextFunction,
} from "express";
import cors from "cors";
import { config as dotenvConfig } from "dotenv";
import { GoldRushDecoder } from "../services";
import { txRouter } from "../microservices/tx/tx.routes";
import { timestampParser } from "../utils/functions";

dotenvConfig();

Expand Down
Binary file modified assets/goldrush-decoder-banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 12 additions & 12 deletions microservices/tx/tx.routes.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import {
Router,
type Request,
type Response,
type NextFunction,
} from "express";
import { validateQuery } from "../../middlewares";
import {
type DecodeTXRequest,
decodeTXBodySchema,
decodeTXHeadersSchema,
type DecodeTXHeaders,
decodeTXQuerySchema,
type DecodeTXHeaders,
type DecodeTXQuery,
type DecodeTXRequest,
} from "./tx.schema";
import { decodeLogsFromTx, fetchTxFromHash } from "./tx.service";
import { type Chain } from "@covalenthq/client-sdk";
import {
Router,
type NextFunction,
type Request,
type Response,
} from "express";

export const txRouter = Router();

Expand All @@ -24,16 +24,16 @@ const handleDecode = async (
next: NextFunction
) => {
try {
const covalentApiKey = (req.headers as DecodeTXHeaders)[
"x-covalent-api-key"
const goldrushApiKey = (req.headers as DecodeTXHeaders)[
"x-goldrush-api-key"
];
const raw_logs = (req.query as DecodeTXQuery)["raw_logs"] === "true";
const min_usd = (req.query as DecodeTXQuery)["min_usd"] ?? 0;
const { chain_name, tx_hash } = req.body as DecodeTXRequest;
const tx = await fetchTxFromHash(
chain_name as Chain,
tx_hash,
covalentApiKey
goldrushApiKey
);
const {
log_events,
Expand All @@ -46,7 +46,7 @@ const handleDecode = async (
const events = await decodeLogsFromTx(
chain_name as Chain,
tx,
covalentApiKey,
goldrushApiKey,
{
raw_logs,
min_usd,
Expand Down
4 changes: 2 additions & 2 deletions microservices/tx/tx.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ export const decodeTXBodySchema = yup.object({
export type DecodeTXRequest = yup.InferType<typeof decodeTXBodySchema>;

export const decodeTXHeadersSchema = yup.object({
"x-covalent-api-key": yup
"x-goldrush-api-key": yup
.string()
.trim()
.required("x-covalent-api-key is required"),
.required("x-goldrush-api-key is required"),
});

export type DecodeTXHeaders = yup.InferType<typeof decodeTXHeadersSchema>;
Expand Down
6 changes: 3 additions & 3 deletions microservices/tx/tx.service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { GoldRushDecoder } from "../../services";
import { type QueryOptions } from "../../services/decoder/decoder.types";
import {
CovalentClient,
type Chain,
type Transaction,
} from "@covalenthq/client-sdk";
import { type QueryOptions } from "../../services/decoder/decoder.types";

export const fetchTxFromHash = async (
chain_name: Chain,
Expand Down Expand Up @@ -39,13 +39,13 @@ export const fetchTxFromHash = async (
export const decodeLogsFromTx = async (
chain_name: Chain,
tx: Transaction,
covalentApiKey: string,
apiKey: string,
options: QueryOptions
) => {
const events = await GoldRushDecoder.decode(
chain_name,
tx,
covalentApiKey,
apiKey,
options
);
return events;
Expand Down
4 changes: 2 additions & 2 deletions middlewares/validate-query.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

import { type ObjectSchema, type ValidationError } from "yup";
import { type Request, type Response, type NextFunction } from "express";
import { type ObjectSchema, type ValidationError } from "yup";

type RequestLocations = "query" | "body" | "params" | "headers";

/**
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@covalent/goldrush-decoder",
"version": "1.0.0",
"description": "",
"description": "Decode unstructured, raw event logs into structured data with a simple API. Open-source. Public Good. 200+ Chains.",
"scripts": {
"add-config": "ts-node ./scripts/add-config.ts",
"build": "tsc",
Expand All @@ -24,6 +24,7 @@
"yup": "^1.3.2"
},
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/cors": "^2.8.14",
"@types/express": "^4.17.19",
"@types/jest": "^29.5.8",
Expand Down
50 changes: 17 additions & 33 deletions scripts/add-config.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,21 @@
import prettierConfig from "../.prettierrc.json";
import { type Configs } from "../services/decoder/decoder.types";
import { slugify } from "../utils/functions";
import { Chains, type Chain } from "@covalenthq/client-sdk";
import { prompt } from "enquirer";
import { existsSync, mkdirSync, writeFileSync } from "fs";
import { join } from "path";
import { format, type Options } from "prettier";
import prettierConfig from "../.prettierrc.json";
import { slugify } from "../utils/functions";
import { type Configs } from "../services/decoder/decoder.types";
import * as yup from "yup";

const writeInFile = async (
path: string,
name: string,
content: string,
pretty: boolean
) => {
const writeInFile = async (path: string, name: string, content: string) => {
if (!existsSync(path)) {
mkdirSync(path);
}
const formattedContent = pretty
? await format(content, {
parser: "typescript",
...(prettierConfig as Options),
})
: content;
const formattedContent = await format(content, {
parser: "typescript",
...(prettierConfig as Options),
});
writeFileSync(join(path, name), formattedContent, "utf-8");
};

Expand Down Expand Up @@ -146,33 +139,25 @@ const chainNameSchema = yup

if (!exists) {
const eventName: string = "<EVENT NAME>";
const abiContent: string = `[]`;
const abiContent: string = `export const ABI = [] as const`;
const configsContent: string = `import{type Configs}from"../../decoder.types";\n\nconst configs:Configs=[{address:"${address}",is_factory:${is_factory},protocol_name:"${protocol_name}",chain_name:"${chain_name}"}];\n\nexport default configs;`;
const decodersContent: string = `import{GoldRushDecoder}from"../../decoder";import{type EventType}from"../../decoder.types";import{DECODED_ACTION,DECODED_EVENT_CATEGORY}from"../../decoder.constants";import{decodeEventLog,type Abi}from"viem";import ABI from "./abis/${protocol_name}.abi.json";\n\nGoldRushDecoder.on("${protocol_name}:${eventName}",["${chain_name}"],ABI as Abi,async(log_event,tx,chain_name,covalent_client,options):Promise<EventType> =>{const{raw_log_data,raw_log_topics}=log_event;\n\nconst{args:decoded}=decodeEventLog({abi:ABI,topics:raw_log_topics as[],data:raw_log_data as \`0x\${string}\`,eventName:"${eventName}"})as{eventName:"${eventName}";args:{}};\n\nreturn{action:DECODED_ACTION.SWAPPED,category:DECODED_EVENT_CATEGORY.DEX,name:"${eventName}",protocol:{logo:log_event.sender_logo_url as string,name:log_event.sender_name as string},...(options.raw_logs?{raw_log:log_event}:{})};});`;
const testContent: string = `import request from"supertest";import app from"../../../../api";import{type EventType}from"../../decoder.types";\n\ndescribe("${protocol_name}",()=>{test("${chain_name}:${eventName}",async()=>{const res=await request(app).post("/api/v1/tx/decode").set({"x-covalent-api-key":process.env.TEST_COVALENT_API_KEY}).send({chain_name:"${chain_name}",tx_hash:"<ENTER TX HASH FOR TESTING>"});const{events}=res.body as{events:EventType[]};const event=events.find(({name})=>name==="${eventName}");if(!event){throw Error("Event not found")}const testAdded:boolean=false;expect(testAdded).toEqual(true)})});`;
const decodersContent: string = `import{GoldRushDecoder}from"../../decoder";import{DECODED_ACTION,DECODED_EVENT_CATEGORY}from"../../decoder.constants";import{type EventType}from"../../decoder.types";import{ABI}from"./abis/${protocol_name}.abi";import{decodeEventLog,type Abi}from"viem";\n\nGoldRushDecoder.on(":${eventName}",["${chain_name}"],ABI as Abi,async(log_event,tx,chain_name,covalent_client,options):Promise<EventType> =>{const{raw_log_data,raw_log_topics}=log_event;\n\nconst{args:decoded}=decodeEventLog({abi:ABI,topics:raw_log_topics as[],data:raw_log_data as \`0x\${string}\`,eventName:"${eventName}"})as{eventName:"${eventName}";args:{}};\n\nreturn{action:DECODED_ACTION.SWAPPED,category:DECODED_EVENT_CATEGORY.DEX,name:"${eventName}",protocol:{logo:log_event.sender_logo_url as string,name:log_event.sender_name as string},...(options.raw_logs?{raw_log:log_event}:{})};});`;
const testContent: string = `import app from"../../../../api";import{type EventType}from"../../decoder.types";import request from"supertest";\n\ndescribe("${protocol_name}",()=>{test("${chain_name}:${eventName}",async()=>{const res=await request(app).post("/api/v1/tx/decode").set({"x-goldrush-api-key":process.env.TEST_GOLDRUSH_API_KEY}).send({chain_name:"${chain_name}",tx_hash:"<ENTER TX HASH FOR TESTING>"});const{events}=res.body as{events:EventType[]};const event=events.find(({name})=>name==="${eventName}");if(!event){throw Error("Event not found")}const testAdded:boolean=false;expect(testAdded).toEqual(true)})});`;
await writeInFile(
protocolDir,
`${protocol_name}.decoders.ts`,
decodersContent,
true
decodersContent
);
await writeInFile(
protocolDir,
`${protocol_name}.configs.ts`,
configsContent,
true
);
await writeInFile(
protocolDir,
`${protocol_name}.test.ts`,
testContent,
true
configsContent
);
await writeInFile(protocolDir, `${protocol_name}.test.ts`, testContent);
await writeInFile(
join(protocolDir, "abis"),
`${protocol_name}.abi.json`,
abiContent,
false
`${protocol_name}.abi.ts`,
abiContent
);
customLog(`Created '${protocol_name}' successfully!`, "success");
} else {
Expand All @@ -190,8 +175,7 @@ const chainNameSchema = yup
await writeInFile(
protocolDir,
`${protocol_name}.configs.ts`,
configsContent,
true
configsContent
);
customLog(
`Added a config to '${protocol_name}' successfully!`,
Expand Down
Loading
Loading