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

In case we can't create tables with sql stores call die #156

Merged
merged 4 commits into from
Nov 28, 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
6 changes: 6 additions & 0 deletions .changeset/new-ties-brush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@3loop/transaction-interpreter': patch
'@3loop/transaction-decoder': patch
---

Bump effect version to latest, and remove the separate schema package
5 changes: 5 additions & 0 deletions .changeset/popular-points-suffer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@3loop/transaction-decoder': patch
---

Fetch contract meta and contract abi in parallel when decoding a log
5 changes: 5 additions & 0 deletions .changeset/sharp-rockets-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@3loop/transaction-decoder': patch
---

SQL stores will now die instead of failure if tables can't be created
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"autoprefixer": "10.4.15",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"effect": "^3.10.9",
"effect": "^3.10.18",
"eslint": "^8.57.0",
"eslint-config-next": "13.4.19",
"jsonata": "^2.0.5",
Expand Down
69 changes: 32 additions & 37 deletions apps/web/src/lib/decode.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getProvider, RPCProviderLive } from './rpc-provider'
import { Effect, Layer } from 'effect'
import { Effect, Layer, ManagedRuntime } from 'effect'
import {
DecodedTransaction,
DecodeResult,
Expand All @@ -13,13 +13,21 @@ import {
SourcifyStrategyResolver,
UnknownNetwork,
UnsupportedEvent,
AbiStore,
AbiParams,
ContractAbiResult,
ContractMetaStore,
ContractMetaParams,
ContractMetaResult,
PublicClient,
} from '@3loop/transaction-decoder'
import { SqlAbiStore, SqlContractMetaStore } from '@3loop/transaction-decoder/sql'
import { Hex } from 'viem'
import { DatabaseLive } from './database'
import { SqlError } from '@effect/sql/SqlError'
import { PgClient } from '@effect/sql-pg/PgClient'
import { SqlClient } from '@effect/sql/SqlClient'
import { ConfigError } from 'effect/ConfigError'
import { ParseError } from 'effect/ParseResult'
import { SqlError } from '@effect/sql/SqlError'

const AbiStoreLive = SqlAbiStore.make({
default: [
Expand All @@ -36,7 +44,17 @@ const MetaStoreLive = SqlContractMetaStore.make()

const DataLayer = Layer.mergeAll(RPCProviderLive, DatabaseLive)
const LoadersLayer = Layer.mergeAll(AbiStoreLive, MetaStoreLive)
const MainLayer = Layer.provideMerge(LoadersLayer, DataLayer)
const MainLayer = Layer.provideMerge(LoadersLayer, DataLayer) as Layer.Layer<
| AbiStore<AbiParams, ContractAbiResult>
| ContractMetaStore<ContractMetaParams, ContractMetaResult>
| PublicClient
| PgClient
| SqlClient,
ConfigError | SqlError,
never
>

const runtime = ManagedRuntime.make(MainLayer)

export async function decodeTransaction({
chainID,
Expand All @@ -46,19 +64,9 @@ export async function decodeTransaction({
hash: string
}): Promise<DecodedTransaction | undefined> {
// NOTE: For unknonw reason the context of main layer is still missing the SqlClient in the type
const runnable = Effect.provide(decodeTransactionByHash(hash as Hex, chainID), MainLayer) as Effect.Effect<
DecodedTransaction,
| SqlError
| UnknownNetwork
| ConfigError
| SqlError
| RPCFetchError
| ParseError
| UnsupportedEvent
| FetchTransactionError,
never
>
return Effect.runPromise(runnable).catch((error: unknown) => {
const runnable = decodeTransactionByHash(hash as Hex, chainID)

return runtime.runPromise(runnable).catch((error: unknown) => {
console.error('Decode error', JSON.stringify(error, null, 2))
return undefined
})
Expand All @@ -73,26 +81,13 @@ export async function decodeCalldata({
data: string
contractAddress?: string
}): Promise<DecodeResult | undefined> {
const runnable = Effect.provide(
calldataDecoder({
data: data as Hex,
chainID,
contractAddress,
}),
MainLayer,
) as Effect.Effect<
DecodeResult,
| SqlError
| UnknownNetwork
| ConfigError
| SqlError
| RPCFetchError
| ParseError
| UnsupportedEvent
| FetchTransactionError,
never
>
return Effect.runPromise(runnable).catch((error: unknown) => {
const runnable = calldataDecoder({
data: data as Hex,
chainID,
contractAddress,
})

return runtime.runPromise(runnable).catch((error: unknown) => {
console.error('Decode error', JSON.stringify(error, null, 2))
return undefined
})
Expand Down
11 changes: 1 addition & 10 deletions apps/web/src/lib/rpc-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,18 @@ const providerConfigs: Record<string, (typeof supportedChains)[number]> = suppor
}
}, {})

const providers: Record<number, PublicClientObject> = {}

export function getProvider(chainID: number): PublicClientObject | null {
let provider = providers[chainID]
if (provider != null) {
return provider
}
const url = providerConfigs[chainID]?.rpcUrl

if (url != null) {
provider = {
return {
client: createPublicClient({
transport: http(url),
}),
config: {
traceAPI: providerConfigs[chainID]?.traceAPI,
},
}

providers[chainID] = provider
return provider
}

return null
Expand Down
6 changes: 2 additions & 4 deletions packages/transaction-decoder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,8 @@
"README.md"
],
"peerDependencies": {
"@effect/schema": "^0.66.8",
"@effect/sql": "^0.18.16",
"effect": "^3.10.9",
"effect": "^3.10.18",
"quickjs-emscripten": "^0.29.1",
"viem": "^2.7.22"
},
Expand All @@ -67,13 +66,12 @@
}
},
"devDependencies": {
"@effect/schema": "^0.66.16",
"@total-typescript/ts-reset": "^0.5.1",
"@types/node": "^20.16.3",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"@vitest/coverage-v8": "0.34.2",
"effect": "^3.10.9",
"effect": "^3.10.18",
"eslint": "^8.57.0",
"eslint-config-custom": "workspace:*",
"eslint-config-prettier": "^8.10.0",
Expand Down
47 changes: 22 additions & 25 deletions packages/transaction-decoder/src/decoding/log-decode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const decodedLog = (transaction: GetTransactionReturnType, logItem: Log) =>
Effect.gen(function* () {
const chainID = Number(transaction.chainId)

const address = logItem.address
const address = getAddress(logItem.address)
let abiAddress = address

const implementation = yield* getProxyStorageSlot({ address: getAddress(abiAddress), chainID })
Expand All @@ -31,11 +31,22 @@ const decodedLog = (transaction: GetTransactionReturnType, logItem: Log) =>
abiAddress = implementation.address
}

const abiItem = yield* getAndCacheAbi({
address: abiAddress,
event: logItem.topics[0],
chainID,
})
const [abiItem, contractData] = yield* Effect.all(
[
getAndCacheAbi({
address: abiAddress,
event: logItem.topics[0],
chainID,
}),
getAndCacheContractMeta({
address,
chainID: Number(transaction.chainId),
}),
],
{
concurrency: 'unbounded',
},
)

const { eventName, args: args_ } = yield* Effect.try({
try: () =>
Expand Down Expand Up @@ -98,34 +109,20 @@ const decodedLog = (transaction: GetTransactionReturnType, logItem: Log) =>
decoded: true,
}

return yield* transformLog(transaction, rawLog)
})

const transformLog = (transaction: GetTransactionReturnType, log: RawDecodedLog) =>
Effect.gen(function* () {
const events = Object.fromEntries(log.events.map((param) => [param.name, param.value]))

// NOTE: Can use a common parser with branded type evrywhere
const address = getAddress(log.address)

const contractData = yield* getAndCacheContractMeta({
address,
chainID: Number(transaction.chainId),
})

const events = Object.fromEntries(rawLog.events.map((param) => [param.name, param.value]))
return {
contractName: contractData?.contractName || null,
contractSymbol: contractData?.tokenSymbol || null,
contractAddress: address,
decimals: contractData?.decimals || null,
chainID: Number(transaction.chainId),
contractType: contractData?.type ?? 'OTHER',
signature: log.signature,
signature: rawLog.signature,
event: {
eventName: log.name,
logIndex: log.logIndex,
eventName: rawLog.name,
logIndex: rawLog.logIndex,
params: events,
...(!log.decoded && { decoded: log.decoded }),
...(!rawLog.decoded && { decoded: rawLog.decoded }),
},
} as Interaction
})
Expand Down
3 changes: 1 addition & 2 deletions packages/transaction-decoder/src/schema/trace.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as Schema from '@effect/schema/Schema'

import { Schema } from 'effect'
const CallType = Schema.Literal('call', 'delegatecall', 'callcode', 'staticcall')

const Address = Schema.String // NOTE: Probably we can use a branded type
Expand Down
21 changes: 11 additions & 10 deletions packages/transaction-decoder/src/sql/abi-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@ export const make = (strategies: AbiStore['strategies']) =>
const sql = yield* SqlClient.SqlClient

yield* sql`
CREATE TABLE IF NOT EXISTS contractAbi (
type TEXT NOT NULL,
address TEXT,
event TEXT,
signature TEXT,
chain INTEGER,
abi TEXT,
status TEXT NOT NULL
)
`
CREATE TABLE IF NOT EXISTS contractAbi (
type TEXT NOT NULL,
address TEXT,
event TEXT,
signature TEXT,
chain INTEGER,
abi TEXT,
status TEXT NOT NULL
)
`.pipe(Effect.catchAll(() => Effect.dieMessage('Failed to create contractAbi table')))

return AbiStore.of({
strategies,
set: (key, value) =>
Expand Down
20 changes: 10 additions & 10 deletions packages/transaction-decoder/src/sql/contract-meta-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ export const make = () =>
const publicClient = yield* PublicClient

yield* sql`
CREATE TABLE IF NOT EXISTS contractMeta (
address TEXT NOT NULL,
chain INTEGER NOT NULL,
contractName TEXT,
tokenSymbol TEXT,
decimals INTEGER,
type TEXT,
status TEXT NOT NULL
)
`
CREATE TABLE IF NOT EXISTS contractMeta (
address TEXT NOT NULL,
chain INTEGER NOT NULL,
contractName TEXT,
tokenSymbol TEXT,
decimals INTEGER,
type TEXT,
status TEXT NOT NULL
)
`.pipe(Effect.catchAll(() => Effect.dieMessage('Failed to create contractMeta table')))

return ContractMetaStore.of({
strategies: {
Expand Down
5 changes: 2 additions & 3 deletions packages/transaction-decoder/src/transaction-loader.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { Effect } from 'effect'
import * as Schema from '@effect/schema/Schema'
import { Effect, Schema } from 'effect'
import { RPCFetchError, PublicClient } from './public-client.js'
import type { TraceLog, TraceLogTree } from './schema/trace.js'
import { EthTrace } from './schema/trace.js'
import { transformTraceTree } from './helpers/trace.js'
import { GetTransactionReturnType, type Hash } from 'viem'
import { ParseError } from '@effect/schema/ParseResult'
import { ParseError } from 'effect/ParseResult'

export const getTransaction = (hash: Hash, chainID: number) =>
Effect.gen(function* () {
Expand Down
2 changes: 1 addition & 1 deletion packages/transaction-decoder/src/transformers/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ function getTokens(interactions: Interaction[]): Asset[] {
]
}

console.error('Unsupported type:', interaction)
console.warn('Unsupported type:', interaction)
// TODO: Batch transfers are not supported yet
return []
})
Expand Down
4 changes: 2 additions & 2 deletions packages/transaction-interpreter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"quickjs-emscripten": "^0.29.2"
},
"peerDependencies": {
"effect": "^3.10.9"
"effect": "^3.10.18"
},
"devDependencies": {
"@babel/core": "^7.25.2",
Expand All @@ -58,7 +58,7 @@
"chalk": "^5.3.0",
"chokidar": "^3.6.0",
"commander": "^12.1.0",
"effect": "^3.10.9",
"effect": "^3.10.18",
"esbuild": "^0.21.5",
"eslint": "^8.57.0",
"eslint-config-custom": "workspace:*",
Expand Down
Loading
Loading