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

feat: native decoder #29

Merged
merged 2 commits into from
Feb 27, 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
4 changes: 2 additions & 2 deletions microservices/tx/tx.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
decodeTXHeadersSchema,
type DecodeTXHeaders,
} from "./tx.schema";
import { decodeLogsfromTx, fetchTxFromHash } from "./tx.service";
import { decodeLogsFromTx, fetchTxFromHash } from "./tx.service";
import { type Chain } from "@covalenthq/client-sdk";

export const txRouter = Router();
Expand Down Expand Up @@ -39,7 +39,7 @@ const handleDecode = async (
safe_details,
...tx_metadata
} = tx;
const events = await decodeLogsfromTx(
const events = await decodeLogsFromTx(
chain_name as Chain,
tx,
covalentApiKey
Expand Down
2 changes: 1 addition & 1 deletion microservices/tx/tx.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const fetchTxFromHash = async (
}
};

export const decodeLogsfromTx = async (
export const decodeLogsFromTx = async (
chain_name: Chain,
tx: Transaction,
covalentApiKey: string
Expand Down
34 changes: 25 additions & 9 deletions services/decoder/decoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ import {
type EventType,
type DecoderConfig,
type Fallbacks,
type NativeDecodingFunction,
} from "./decoder.types";
import { encodeEventTopics, type Abi } from "viem";

export class GoldRushDecoder {
private static configs: DecoderConfig = {};
private static decoders: Decoders = {};
private static fallbacks: Fallbacks = {};
private static native_decoder: NativeDecodingFunction;
private static decoding_functions: DecodingFunctions = [];
private static fileExtension: "js" | "ts" =
process.env.NODE_ENV !== "test" ? "js" : "ts";

public static initDecoder = () => {
console.info("Initializing GoldrushDecoder Service...");
Expand All @@ -34,12 +38,10 @@ export class GoldRushDecoder {
let configFile: string | null = null,
decodersFile: string | null = null;
files.forEach((file) => {
const fileExtension =
process.env.NODE_ENV !== "test" ? "js" : "ts";
if (file.endsWith(`.configs.${fileExtension}`)) {
if (file.endsWith(`.configs.${this.fileExtension}`)) {
configFile = file;
}
if (file.endsWith(`.decoders.${fileExtension}`)) {
if (file.endsWith(`.decoders.${this.fileExtension}`)) {
decodersFile = file;
}
});
Expand Down Expand Up @@ -68,9 +70,7 @@ export class GoldRushDecoder {
const files = readdirSync(fallbackPath);
let fallbackFile: string | null = null;
files.forEach((file) => {
const fileExtension =
process.env.NODE_ENV !== "test" ? "js" : "ts";
if (file.endsWith(`.fallback.${fileExtension}`)) {
if (file.endsWith(`.fallback.${this.fileExtension}`)) {
fallbackFile = file;
}
});
Expand All @@ -80,6 +80,13 @@ export class GoldRushDecoder {
}
}

const nativeDecoderPath: string = join(
__dirname,
"native",
`native.decoder.${this.fileExtension}`
);
require(join(nativeDecoderPath));

const decodersCount = Object.keys(this.decoding_functions).length;
const configsCount = Object.values(this.configs).reduce(
(chainCount, chain) => {
Expand All @@ -93,6 +100,7 @@ export class GoldRushDecoder {
0
);

console.info("native decoder added");
console.info(`${protocolsCount.toLocaleString()} protocols found`);
console.info(`${configsCount.toLocaleString()} configs generated`);
console.info(`${decodersCount.toLocaleString()} decoders generated`);
Expand Down Expand Up @@ -148,6 +156,10 @@ export class GoldRushDecoder {
this.fallbacks[topic0_hash] = decoding_function_index;
};

public static native = (native_decoder: NativeDecodingFunction) => {
this.native_decoder = native_decoder;
};

public static decode = async (
chain_name: Chain,
tx: Transaction,
Expand All @@ -156,6 +168,10 @@ export class GoldRushDecoder {
const covalent_client = new CovalentClient(covalent_api_key);
const events: EventType[] = [];
const logs = (tx.log_events ?? []).reverse();
if (tx.value) {
const nativeEvent = this.native_decoder(tx);
events.push(nativeEvent);
}
for (const log of logs) {
const {
raw_log_topics: [topic0_hash],
Expand All @@ -169,10 +185,10 @@ export class GoldRushDecoder {
this.decoders[chain_name]?.[contract_address]?.[topic0_hash];
const fallback_index = this.fallbacks[topic0_hash];
if (decoding_index !== undefined || fallback_index !== undefined) {
const event = await this.decoding_functions[
const logEvent = await this.decoding_functions[
decoding_index ?? fallback_index
](log, tx, chain_name, covalent_client);
events.push(event);
events.push(logEvent);
}
}
return events;
Expand Down
2 changes: 2 additions & 0 deletions services/decoder/decoder.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ export type DecodingFunction = (
covalent_client: CovalentClient
) => Promise<EventType>;

export type NativeDecodingFunction = (tx: Transaction) => EventType;

export type DecoderConfig =
| {
[chain_name in Chain]: {
Expand Down
38 changes: 38 additions & 0 deletions services/decoder/native/native.decoder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { GoldRushDecoder } from "../decoder";
import { type EventType } from "../decoder.types";

import { DECODED_ACTION, DECODED_EVENT_CATEGORY } from "../decoder.constants";

GoldRushDecoder.native((tx): EventType => {
return {
action: DECODED_ACTION.SWAPPED,
category: DECODED_EVENT_CATEGORY.DEX,
name: "Native Transfer",
protocol: {
logo: tx.gas_metadata.logo_url,
name: tx.gas_metadata.contract_name,
},
details: [
{
heading: "From",
value: tx.from_address,
type: "address",
},
{
heading: "To",
value: tx.to_address,
type: "address",
},
],
tokens: [
{
heading: "Value",
value: tx.value?.toString() || "0",
decimals: tx.gas_metadata.contract_decimals,
pretty_quote: tx.pretty_value_quote,
ticker_logo: tx.gas_metadata.logo_url,
ticker_symbol: tx.gas_metadata.contract_ticker_symbol,
},
],
};
});
23 changes: 23 additions & 0 deletions services/decoder/native/native.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import request from "supertest";
import app from "../../../api";
import { type EventType } from "../decoder.types";

describe("Native", () => {
test("Native Transfer", 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: "eth-mainnet",
tx_hash:
"0xfa6d5bd3041f6d904e96e592d5d339907637d1c445b8464184dba92d728e7234",
});
const { events } = res.body as { events: EventType[] };
const event = events.find(({ name }) => name === "Native Transfer");
if (!event) {
throw Error("Event not found");
}
expect(event.details?.length).toEqual(2);
expect(event.tokens?.length).toEqual(1);
});
});
Loading