Skip to content

Commit 1dedcee

Browse files
authored
feat: native decoder (#29)
1 parent 289d298 commit 1dedcee

File tree

6 files changed

+91
-12
lines changed

6 files changed

+91
-12
lines changed

microservices/tx/tx.routes.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
decodeTXHeadersSchema,
1212
type DecodeTXHeaders,
1313
} from "./tx.schema";
14-
import { decodeLogsfromTx, fetchTxFromHash } from "./tx.service";
14+
import { decodeLogsFromTx, fetchTxFromHash } from "./tx.service";
1515
import { type Chain } from "@covalenthq/client-sdk";
1616

1717
export const txRouter = Router();
@@ -39,7 +39,7 @@ const handleDecode = async (
3939
safe_details,
4040
...tx_metadata
4141
} = tx;
42-
const events = await decodeLogsfromTx(
42+
const events = await decodeLogsFromTx(
4343
chain_name as Chain,
4444
tx,
4545
covalentApiKey

microservices/tx/tx.service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const fetchTxFromHash = async (
3535
}
3636
};
3737

38-
export const decodeLogsfromTx = async (
38+
export const decodeLogsFromTx = async (
3939
chain_name: Chain,
4040
tx: Transaction,
4141
covalentApiKey: string

services/decoder/decoder.ts

+25-9
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,18 @@ import {
1313
type EventType,
1414
type DecoderConfig,
1515
type Fallbacks,
16+
type NativeDecodingFunction,
1617
} from "./decoder.types";
1718
import { encodeEventTopics, type Abi } from "viem";
1819

1920
export class GoldRushDecoder {
2021
private static configs: DecoderConfig = {};
2122
private static decoders: Decoders = {};
2223
private static fallbacks: Fallbacks = {};
24+
private static native_decoder: NativeDecodingFunction;
2325
private static decoding_functions: DecodingFunctions = [];
26+
private static fileExtension: "js" | "ts" =
27+
process.env.NODE_ENV !== "test" ? "js" : "ts";
2428

2529
public static initDecoder = () => {
2630
console.info("Initializing GoldrushDecoder Service...");
@@ -34,12 +38,10 @@ export class GoldRushDecoder {
3438
let configFile: string | null = null,
3539
decodersFile: string | null = null;
3640
files.forEach((file) => {
37-
const fileExtension =
38-
process.env.NODE_ENV !== "test" ? "js" : "ts";
39-
if (file.endsWith(`.configs.${fileExtension}`)) {
41+
if (file.endsWith(`.configs.${this.fileExtension}`)) {
4042
configFile = file;
4143
}
42-
if (file.endsWith(`.decoders.${fileExtension}`)) {
44+
if (file.endsWith(`.decoders.${this.fileExtension}`)) {
4345
decodersFile = file;
4446
}
4547
});
@@ -68,9 +70,7 @@ export class GoldRushDecoder {
6870
const files = readdirSync(fallbackPath);
6971
let fallbackFile: string | null = null;
7072
files.forEach((file) => {
71-
const fileExtension =
72-
process.env.NODE_ENV !== "test" ? "js" : "ts";
73-
if (file.endsWith(`.fallback.${fileExtension}`)) {
73+
if (file.endsWith(`.fallback.${this.fileExtension}`)) {
7474
fallbackFile = file;
7575
}
7676
});
@@ -80,6 +80,13 @@ export class GoldRushDecoder {
8080
}
8181
}
8282

83+
const nativeDecoderPath: string = join(
84+
__dirname,
85+
"native",
86+
`native.decoder.${this.fileExtension}`
87+
);
88+
require(join(nativeDecoderPath));
89+
8390
const decodersCount = Object.keys(this.decoding_functions).length;
8491
const configsCount = Object.values(this.configs).reduce(
8592
(chainCount, chain) => {
@@ -93,6 +100,7 @@ export class GoldRushDecoder {
93100
0
94101
);
95102

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

159+
public static native = (native_decoder: NativeDecodingFunction) => {
160+
this.native_decoder = native_decoder;
161+
};
162+
151163
public static decode = async (
152164
chain_name: Chain,
153165
tx: Transaction,
@@ -156,6 +168,10 @@ export class GoldRushDecoder {
156168
const covalent_client = new CovalentClient(covalent_api_key);
157169
const events: EventType[] = [];
158170
const logs = (tx.log_events ?? []).reverse();
171+
if (tx.value) {
172+
const nativeEvent = this.native_decoder(tx);
173+
events.push(nativeEvent);
174+
}
159175
for (const log of logs) {
160176
const {
161177
raw_log_topics: [topic0_hash],
@@ -169,10 +185,10 @@ export class GoldRushDecoder {
169185
this.decoders[chain_name]?.[contract_address]?.[topic0_hash];
170186
const fallback_index = this.fallbacks[topic0_hash];
171187
if (decoding_index !== undefined || fallback_index !== undefined) {
172-
const event = await this.decoding_functions[
188+
const logEvent = await this.decoding_functions[
173189
decoding_index ?? fallback_index
174190
](log, tx, chain_name, covalent_client);
175-
events.push(event);
191+
events.push(logEvent);
176192
}
177193
}
178194
return events;

services/decoder/decoder.types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ export type DecodingFunction = (
6464
covalent_client: CovalentClient
6565
) => Promise<EventType>;
6666

67+
export type NativeDecodingFunction = (tx: Transaction) => EventType;
68+
6769
export type DecoderConfig =
6870
| {
6971
[chain_name in Chain]: {
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { GoldRushDecoder } from "../decoder";
2+
import { type EventType } from "../decoder.types";
3+
4+
import { DECODED_ACTION, DECODED_EVENT_CATEGORY } from "../decoder.constants";
5+
6+
GoldRushDecoder.native((tx): EventType => {
7+
return {
8+
action: DECODED_ACTION.SWAPPED,
9+
category: DECODED_EVENT_CATEGORY.DEX,
10+
name: "Native Transfer",
11+
protocol: {
12+
logo: tx.gas_metadata.logo_url,
13+
name: tx.gas_metadata.contract_name,
14+
},
15+
details: [
16+
{
17+
heading: "From",
18+
value: tx.from_address,
19+
type: "address",
20+
},
21+
{
22+
heading: "To",
23+
value: tx.to_address,
24+
type: "address",
25+
},
26+
],
27+
tokens: [
28+
{
29+
heading: "Value",
30+
value: tx.value?.toString() || "0",
31+
decimals: tx.gas_metadata.contract_decimals,
32+
pretty_quote: tx.pretty_value_quote,
33+
ticker_logo: tx.gas_metadata.logo_url,
34+
ticker_symbol: tx.gas_metadata.contract_ticker_symbol,
35+
},
36+
],
37+
};
38+
});
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import request from "supertest";
2+
import app from "../../../api";
3+
import { type EventType } from "../decoder.types";
4+
5+
describe("Native", () => {
6+
test("Native Transfer", async () => {
7+
const res = await request(app)
8+
.post("/api/v1/tx/decode")
9+
.set({ "x-covalent-api-key": process.env.TEST_COVALENT_API_KEY })
10+
.send({
11+
chain_name: "eth-mainnet",
12+
tx_hash:
13+
"0xfa6d5bd3041f6d904e96e592d5d339907637d1c445b8464184dba92d728e7234",
14+
});
15+
const { events } = res.body as { events: EventType[] };
16+
const event = events.find(({ name }) => name === "Native Transfer");
17+
if (!event) {
18+
throw Error("Event not found");
19+
}
20+
expect(event.details?.length).toEqual(2);
21+
expect(event.tokens?.length).toEqual(1);
22+
});
23+
});

0 commit comments

Comments
 (0)