import type { Event } from "@/internal/types.js"; import { orderObject } from "@/utils/order.js"; import type { Column, Table } from "drizzle-orm"; import type { ProfilePattern, Row } from "./cache.js"; import { getCacheKey } from "./utils.js"; export const getProfilePatternKey = (pattern: ProfilePattern): string => { return JSON.stringify(orderObject(pattern), (_, value) => { if (typeof value === "bigint") { return value.toString(); } return value; }); }; const eq = (target: bigint | string | number | boolean, value: any) => { if (target === value) return true; if (target && value && target.toString() === value.toString()) return true; return false; }; const delimiters = ["-", "_", ":", "#", "$"]; export const recordProfilePattern = ( event: Event, table: Table, key: object, hints: ProfilePattern[], cache: Map, ): ProfilePattern | undefined => { globalThis.DISABLE_EVENT_PROXY = true; for (const hint of hints) { if ( getCacheKey(table, key, cache) === getCacheKey(table, recoverProfilePattern(hint, event), cache) ) { globalThis.DISABLE_EVENT_PROXY = false; return hint; } } const result: ProfilePattern = {}; for (const [js] of cache.get(table)!) { // @ts-ignore const value = key[js]!; const pattern = matchEventParameters(event, value); if (pattern === undefined) { globalThis.DISABLE_EVENT_PROXY = false; return undefined; } result[js] = pattern; } globalThis.DISABLE_EVENT_PROXY = false; return result; }; const matchEventParameters = ( event: Event, value: any, ): ProfilePattern[keyof ProfilePattern] | undefined => { if (eq(event.chain.id, value)) { return { type: "derived", value: ["chainId"] }; } if (eq(event.event.id, value)) { return { type: "derived", value: ["id"] }; } switch (event.type) { case "block": { if (eq(event.event.block.hash, value)) { return { type: "derived", value: ["block", "hash"] }; } if (eq(event.event.block.number, value)) { return { type: "derived", value: ["block", "number"] }; } if (eq(event.event.block.timestamp, value)) { return { type: "derived", value: ["block", "timestamp"] }; } if (eq(event.event.block.timestamp / 60n, value)) { return { type: "derived", value: ["block", "timestamp"], fn: (value) => (value as bigint) / 60n, }; } if (eq(event.event.block.timestamp / 3600n, value)) { return { type: "derived", value: ["block", "timestamp"], fn: (value) => (value as bigint) / 3600n, }; } if (eq(event.event.block.timestamp / 86400n, value)) { return { type: "derived", value: ["block", "timestamp"], fn: (value) => (value as bigint) / 86400n, }; } if (event.event.block.miner && eq(event.event.block.miner, value)) { return { type: "derived", value: ["block", "miner"] }; } break; } case "transaction": { if (eq(event.event.block.hash, value)) { return { type: "derived", value: ["block", "hash"] }; } if (eq(event.event.block.number, value)) { return { type: "derived", value: ["block", "number"] }; } if (eq(event.event.block.timestamp, value)) { return { type: "derived", value: ["block", "timestamp"] }; } if (eq(event.event.block.timestamp / 60n, value)) { return { type: "derived", value: ["block", "timestamp"], fn: (value) => (value as bigint) / 60n, }; } if (eq(event.event.block.timestamp / 3600n, value)) { return { type: "derived", value: ["block", "timestamp"], fn: (value) => (value as bigint) / 3600n, }; } if (eq(event.event.block.timestamp / 86400n, value)) { return { type: "derived", value: ["block", "timestamp"], fn: (value) => (value as bigint) / 86400n, }; } if (event.event.block.miner && eq(event.event.block.miner, value)) { return { type: "derived", value: ["block", "miner"] }; } if (eq(event.event.transaction.hash, value)) { return { type: "derived", value: ["transaction", "hash"] }; } if (eq(event.event.transaction.from, value)) { return { type: "derived", value: ["transaction", "from"] }; } if (event.event.transaction.to && eq(event.event.transaction.to, value)) { return { type: "derived", value: ["transaction", "to"] }; } if (eq(event.event.transaction.transactionIndex, value)) { return { type: "derived", value: ["transaction", "transactionIndex"] }; } if ( event.event.transactionReceipt?.contractAddress && eq(event.event.transactionReceipt.contractAddress, value) ) { return { type: "derived", value: ["transactionReceipt", "contractAddress"], }; } break; } case "log": { // Note: explicitly skip profiling args if they are an array if ( event.event.args !== undefined && Array.isArray(event.event.args) === false ) { for (const argKey of Object.keys(event.event.args)) { const argValue = (event.event.args as { [key: string]: unknown })[ argKey ] as string | bigint | number | boolean; if (typeof argValue !== "object" && eq(argValue, value)) { return { type: "derived", value: ["args", argKey] }; } } } if (eq(event.event.log.address, value)) { return { type: "derived", value: ["log", "address"] }; } if (eq(event.event.log.logIndex, value)) { return { type: "derived", value: ["log", "logIndex"] }; } if (eq(event.event.block.hash, value)) { return { type: "derived", value: ["block", "hash"] }; } if (eq(event.event.block.number, value)) { return { type: "derived", value: ["block", "number"] }; } if (eq(event.event.block.timestamp, value)) { return { type: "derived", value: ["block", "timestamp"] }; } if (eq(event.event.block.timestamp / 60n, value)) { return { type: "derived", value: ["block", "timestamp"], fn: (value) => (value as bigint) / 60n, }; } if (eq(event.event.block.timestamp / 3600n, value)) { return { type: "derived", value: ["block", "timestamp"], fn: (value) => (value as bigint) / 3600n, }; } if (eq(event.event.block.timestamp / 86400n, value)) { return { type: "derived", value: ["block", "timestamp"], fn: (value) => (value as bigint) / 86400n, }; } if (event.event.block.miner && eq(event.event.block.miner, value)) { return { type: "derived", value: ["block", "miner"] }; } if (eq(event.event.transaction.hash, value)) { return { type: "derived", value: ["transaction", "hash"] }; } if (eq(event.event.transaction.from, value)) { return { type: "derived", value: ["transaction", "from"] }; } if (event.event.transaction.to && eq(event.event.transaction.to, value)) { return { type: "derived", value: ["transaction", "to"] }; } if (eq(event.event.transaction.transactionIndex, value)) { return { type: "derived", value: ["transaction", "transactionIndex"] }; } if ( event.event.transactionReceipt?.contractAddress && eq(event.event.transactionReceipt.contractAddress, value) ) { return { type: "derived", value: ["transactionReceipt", "contractAddress"], }; } break; } case "trace": { // Note: explicitly skip profiling args if they are an array if ( event.event.args !== undefined && Array.isArray(event.event.args) === false ) { for (const argKey of Object.keys(event.event.args)) { const argValue = (event.event.args as { [key: string]: unknown })[ argKey ] as string | bigint | number | boolean; if (typeof argValue !== "object" && eq(argValue, value)) { return { type: "derived", value: ["args", argKey] }; } } } // Note: explicitly skip profiling result if it is an array if ( event.event.result !== undefined && Array.isArray(event.event.result) === false ) { for (const argKey of Object.keys(event.event.result)) { const argValue = (event.event.result as { [key: string]: unknown })[ argKey ] as string | bigint | number | boolean; if (typeof argValue !== "object" && eq(argValue, value)) { return { type: "derived", value: ["result", argKey] }; } } } if (eq(event.event.trace.from, value)) { return { type: "derived", value: ["trace", "from"] }; } if (event.event.trace.to && eq(event.event.trace.to, value)) { return { type: "derived", value: ["trace", "to"] }; } if (eq(event.event.block.hash, value)) { return { type: "derived", value: ["block", "hash"] }; } if (eq(event.event.block.number, value)) { return { type: "derived", value: ["block", "number"] }; } if (eq(event.event.block.timestamp, value)) { return { type: "derived", value: ["block", "timestamp"] }; } if (eq(event.event.block.timestamp / 60n, value)) { return { type: "derived", value: ["block", "timestamp"], fn: (value) => (value as bigint) / 60n, }; } if (eq(event.event.block.timestamp / 3600n, value)) { return { type: "derived", value: ["block", "timestamp"], fn: (value) => (value as bigint) / 3600n, }; } if (eq(event.event.block.timestamp / 86400n, value)) { return { type: "derived", value: ["block", "timestamp"], fn: (value) => (value as bigint) / 86400n, }; } if (event.event.block.miner && eq(event.event.block.miner, value)) { return { type: "derived", value: ["block", "miner"] }; } if (eq(event.event.transaction.hash, value)) { return { type: "derived", value: ["transaction", "hash"] }; } if (eq(event.event.transaction.from, value)) { return { type: "derived", value: ["transaction", "from"] }; } if (event.event.transaction.to && eq(event.event.transaction.to, value)) { return { type: "derived", value: ["transaction", "to"] }; } if (eq(event.event.transaction.transactionIndex, value)) { return { type: "derived", value: ["transaction", "transactionIndex"] }; } if ( event.event.transactionReceipt?.contractAddress && eq(event.event.transactionReceipt.contractAddress, value) ) { return { type: "derived", value: ["transactionReceipt", "contractAddress"], }; } break; } case "transfer": { if (eq(event.event.transfer.from, value)) { return { type: "derived", value: ["transfer", "from"] }; } if (eq(event.event.transfer.to, value)) { return { type: "derived", value: ["transfer", "to"] }; } if (eq(event.event.trace.from, value)) { return { type: "derived", value: ["trace", "from"] }; } if (event.event.trace.to && eq(event.event.trace.to, value)) { return { type: "derived", value: ["trace", "to"] }; } if (eq(event.event.block.hash, value)) { return { type: "derived", value: ["block", "hash"] }; } if (eq(event.event.block.number, value)) { return { type: "derived", value: ["block", "number"] }; } if (eq(event.event.block.timestamp, value)) { return { type: "derived", value: ["block", "timestamp"] }; } if (eq(event.event.block.timestamp / 60n, value)) { return { type: "derived", value: ["block", "timestamp"], fn: (value) => (value as bigint) / 60n, }; } if (eq(event.event.block.timestamp / 3600n, value)) { return { type: "derived", value: ["block", "timestamp"], fn: (value) => (value as bigint) / 3600n, }; } if (eq(event.event.block.timestamp / 86400n, value)) { return { type: "derived", value: ["block", "timestamp"], fn: (value) => (value as bigint) / 86400n, }; } if (event.event.block.miner && eq(event.event.block.miner, value)) { return { type: "derived", value: ["block", "miner"] }; } if (eq(event.event.transaction.hash, value)) { return { type: "derived", value: ["transaction", "hash"] }; } if (eq(event.event.transaction.from, value)) { return { type: "derived", value: ["transaction", "from"] }; } if (event.event.transaction.to && eq(event.event.transaction.to, value)) { return { type: "derived", value: ["transaction", "to"] }; } if (eq(event.event.transaction.transactionIndex, value)) { return { type: "derived", value: ["transaction", "transactionIndex"] }; } if ( event.event.transactionReceipt?.contractAddress && eq(event.event.transactionReceipt.contractAddress, value) ) { return { type: "derived", value: ["transactionReceipt", "contractAddress"], }; } break; } } if (typeof value === "string") { del: for (const delimiter of delimiters) { const subValues = value.split(delimiter); if (subValues.length > 1) { const result: ProfilePattern[keyof ProfilePattern] = { type: "delimeter", values: [], delimiter, }; for (const subValue of subValues) { const match = matchEventParameters(event, subValue); if (match?.type === "derived") { result.values.push(match); continue; } continue del; } return result; } } } return undefined; }; export const recoverProfilePattern = ( pattern: ProfilePattern, event: Event, ): Row => { const result: Row = {}; for (const [key, _pattern] of Object.entries(pattern)) { if (_pattern.type === "derived") { const { value, fn } = _pattern; if (value[0] === "chainId") { result[key] = event.chain.id; } else { let _result: unknown = event.event; for (const prop of value) { // @ts-ignore _result = _result[prop]; } if (fn) { _result = fn(_result); } result[key] = _result; } } else { const { values, delimiter } = _pattern; result[key] = values .map(({ value, fn }) => { if (value[0] === "chainId") { return event.chain.id; } else { let _result: unknown = event.event; for (const prop of value) { // @ts-ignore _result = _result[prop]; } if (fn) { _result = fn(_result); } return _result; } }) .join(delimiter); } } return result; };