Skip to content

Commit d058de3

Browse files
authored
In case we can't create tables with sql stores call die (#156)
* In case we can't create tables with sql stores call die instead of fail * Fetch abi and contract meta in parallel * Update effect to latest version * Use runtime in playground decoding
1 parent 7b1fedf commit d058de3

File tree

16 files changed

+139
-157
lines changed

16 files changed

+139
-157
lines changed

.changeset/new-ties-brush.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@3loop/transaction-interpreter': patch
3+
'@3loop/transaction-decoder': patch
4+
---
5+
6+
Bump effect version to latest, and remove the separate schema package
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@3loop/transaction-decoder': patch
3+
---
4+
5+
Fetch contract meta and contract abi in parallel when decoding a log

.changeset/sharp-rockets-fly.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@3loop/transaction-decoder': patch
3+
---
4+
5+
SQL stores will now die instead of failure if tables can't be created

apps/web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"autoprefixer": "10.4.15",
2929
"class-variance-authority": "^0.7.0",
3030
"clsx": "^2.1.1",
31-
"effect": "^3.10.9",
31+
"effect": "^3.10.18",
3232
"eslint": "^8.57.0",
3333
"eslint-config-next": "13.4.19",
3434
"jsonata": "^2.0.5",

apps/web/src/lib/decode.ts

Lines changed: 32 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getProvider, RPCProviderLive } from './rpc-provider'
2-
import { Effect, Layer } from 'effect'
2+
import { Effect, Layer, ManagedRuntime } from 'effect'
33
import {
44
DecodedTransaction,
55
DecodeResult,
@@ -13,13 +13,21 @@ import {
1313
SourcifyStrategyResolver,
1414
UnknownNetwork,
1515
UnsupportedEvent,
16+
AbiStore,
17+
AbiParams,
18+
ContractAbiResult,
19+
ContractMetaStore,
20+
ContractMetaParams,
21+
ContractMetaResult,
22+
PublicClient,
1623
} from '@3loop/transaction-decoder'
1724
import { SqlAbiStore, SqlContractMetaStore } from '@3loop/transaction-decoder/sql'
1825
import { Hex } from 'viem'
1926
import { DatabaseLive } from './database'
20-
import { SqlError } from '@effect/sql/SqlError'
27+
import { PgClient } from '@effect/sql-pg/PgClient'
28+
import { SqlClient } from '@effect/sql/SqlClient'
2129
import { ConfigError } from 'effect/ConfigError'
22-
import { ParseError } from 'effect/ParseResult'
30+
import { SqlError } from '@effect/sql/SqlError'
2331

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

3745
const DataLayer = Layer.mergeAll(RPCProviderLive, DatabaseLive)
3846
const LoadersLayer = Layer.mergeAll(AbiStoreLive, MetaStoreLive)
39-
const MainLayer = Layer.provideMerge(LoadersLayer, DataLayer)
47+
const MainLayer = Layer.provideMerge(LoadersLayer, DataLayer) as Layer.Layer<
48+
| AbiStore<AbiParams, ContractAbiResult>
49+
| ContractMetaStore<ContractMetaParams, ContractMetaResult>
50+
| PublicClient
51+
| PgClient
52+
| SqlClient,
53+
ConfigError | SqlError,
54+
never
55+
>
56+
57+
const runtime = ManagedRuntime.make(MainLayer)
4058

4159
export async function decodeTransaction({
4260
chainID,
@@ -46,19 +64,9 @@ export async function decodeTransaction({
4664
hash: string
4765
}): Promise<DecodedTransaction | undefined> {
4866
// NOTE: For unknonw reason the context of main layer is still missing the SqlClient in the type
49-
const runnable = Effect.provide(decodeTransactionByHash(hash as Hex, chainID), MainLayer) as Effect.Effect<
50-
DecodedTransaction,
51-
| SqlError
52-
| UnknownNetwork
53-
| ConfigError
54-
| SqlError
55-
| RPCFetchError
56-
| ParseError
57-
| UnsupportedEvent
58-
| FetchTransactionError,
59-
never
60-
>
61-
return Effect.runPromise(runnable).catch((error: unknown) => {
67+
const runnable = decodeTransactionByHash(hash as Hex, chainID)
68+
69+
return runtime.runPromise(runnable).catch((error: unknown) => {
6270
console.error('Decode error', JSON.stringify(error, null, 2))
6371
return undefined
6472
})
@@ -73,26 +81,13 @@ export async function decodeCalldata({
7381
data: string
7482
contractAddress?: string
7583
}): Promise<DecodeResult | undefined> {
76-
const runnable = Effect.provide(
77-
calldataDecoder({
78-
data: data as Hex,
79-
chainID,
80-
contractAddress,
81-
}),
82-
MainLayer,
83-
) as Effect.Effect<
84-
DecodeResult,
85-
| SqlError
86-
| UnknownNetwork
87-
| ConfigError
88-
| SqlError
89-
| RPCFetchError
90-
| ParseError
91-
| UnsupportedEvent
92-
| FetchTransactionError,
93-
never
94-
>
95-
return Effect.runPromise(runnable).catch((error: unknown) => {
84+
const runnable = calldataDecoder({
85+
data: data as Hex,
86+
chainID,
87+
contractAddress,
88+
})
89+
90+
return runtime.runPromise(runnable).catch((error: unknown) => {
9691
console.error('Decode error', JSON.stringify(error, null, 2))
9792
return undefined
9893
})

apps/web/src/lib/rpc-provider.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,18 @@ const providerConfigs: Record<string, (typeof supportedChains)[number]> = suppor
1010
}
1111
}, {})
1212

13-
const providers: Record<number, PublicClientObject> = {}
14-
1513
export function getProvider(chainID: number): PublicClientObject | null {
16-
let provider = providers[chainID]
17-
if (provider != null) {
18-
return provider
19-
}
2014
const url = providerConfigs[chainID]?.rpcUrl
2115

2216
if (url != null) {
23-
provider = {
17+
return {
2418
client: createPublicClient({
2519
transport: http(url),
2620
}),
2721
config: {
2822
traceAPI: providerConfigs[chainID]?.traceAPI,
2923
},
3024
}
31-
32-
providers[chainID] = provider
33-
return provider
3425
}
3526

3627
return null

packages/transaction-decoder/package.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,8 @@
5555
"README.md"
5656
],
5757
"peerDependencies": {
58-
"@effect/schema": "^0.66.8",
5958
"@effect/sql": "^0.18.16",
60-
"effect": "^3.10.9",
59+
"effect": "^3.10.18",
6160
"quickjs-emscripten": "^0.29.1",
6261
"viem": "^2.7.22"
6362
},
@@ -67,13 +66,12 @@
6766
}
6867
},
6968
"devDependencies": {
70-
"@effect/schema": "^0.66.16",
7169
"@total-typescript/ts-reset": "^0.5.1",
7270
"@types/node": "^20.16.3",
7371
"@typescript-eslint/eslint-plugin": "^5.62.0",
7472
"@typescript-eslint/parser": "^5.62.0",
7573
"@vitest/coverage-v8": "0.34.2",
76-
"effect": "^3.10.9",
74+
"effect": "^3.10.18",
7775
"eslint": "^8.57.0",
7876
"eslint-config-custom": "workspace:*",
7977
"eslint-config-prettier": "^8.10.0",

packages/transaction-decoder/src/decoding/log-decode.ts

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const decodedLog = (transaction: GetTransactionReturnType, logItem: Log) =>
2121
Effect.gen(function* () {
2222
const chainID = Number(transaction.chainId)
2323

24-
const address = logItem.address
24+
const address = getAddress(logItem.address)
2525
let abiAddress = address
2626

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

34-
const abiItem = yield* getAndCacheAbi({
35-
address: abiAddress,
36-
event: logItem.topics[0],
37-
chainID,
38-
})
34+
const [abiItem, contractData] = yield* Effect.all(
35+
[
36+
getAndCacheAbi({
37+
address: abiAddress,
38+
event: logItem.topics[0],
39+
chainID,
40+
}),
41+
getAndCacheContractMeta({
42+
address,
43+
chainID: Number(transaction.chainId),
44+
}),
45+
],
46+
{
47+
concurrency: 'unbounded',
48+
},
49+
)
3950

4051
const { eventName, args: args_ } = yield* Effect.try({
4152
try: () =>
@@ -99,34 +110,20 @@ const decodedLog = (transaction: GetTransactionReturnType, logItem: Log) =>
99110
decoded: true,
100111
}
101112

102-
return yield* transformLog(transaction, rawLog)
103-
})
104-
105-
const transformLog = (transaction: GetTransactionReturnType, log: RawDecodedLog) =>
106-
Effect.gen(function* () {
107-
const events = Object.fromEntries(log.events.map((param) => [param.name, param.value]))
108-
109-
// NOTE: Can use a common parser with branded type evrywhere
110-
const address = getAddress(log.address)
111-
112-
const contractData = yield* getAndCacheContractMeta({
113-
address,
114-
chainID: Number(transaction.chainId),
115-
})
116-
113+
const events = Object.fromEntries(rawLog.events.map((param) => [param.name, param.value]))
117114
return {
118115
contractName: contractData?.contractName || null,
119116
contractSymbol: contractData?.tokenSymbol || null,
120117
contractAddress: address,
121118
decimals: contractData?.decimals || null,
122119
chainID: Number(transaction.chainId),
123120
contractType: contractData?.type ?? 'OTHER',
124-
signature: log.signature,
121+
signature: rawLog.signature,
125122
event: {
126-
eventName: log.name,
127-
logIndex: log.logIndex,
123+
eventName: rawLog.name,
124+
logIndex: rawLog.logIndex,
128125
params: events,
129-
...(!log.decoded && { decoded: log.decoded }),
126+
...(!rawLog.decoded && { decoded: rawLog.decoded }),
130127
},
131128
} as Interaction
132129
})

packages/transaction-decoder/src/schema/trace.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import * as Schema from '@effect/schema/Schema'
2-
1+
import { Schema } from 'effect'
32
const CallType = Schema.Literal('call', 'delegatecall', 'callcode', 'staticcall')
43

54
const Address = Schema.String // NOTE: Probably we can use a branded type

packages/transaction-decoder/src/sql/abi-store.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,17 @@ export const make = (strategies: AbiStore['strategies']) =>
99
const sql = yield* SqlClient.SqlClient
1010

1111
yield* sql`
12-
CREATE TABLE IF NOT EXISTS contractAbi (
13-
type TEXT NOT NULL,
14-
address TEXT,
15-
event TEXT,
16-
signature TEXT,
17-
chain INTEGER,
18-
abi TEXT,
19-
status TEXT NOT NULL
20-
)
21-
`
12+
CREATE TABLE IF NOT EXISTS contractAbi (
13+
type TEXT NOT NULL,
14+
address TEXT,
15+
event TEXT,
16+
signature TEXT,
17+
chain INTEGER,
18+
abi TEXT,
19+
status TEXT NOT NULL
20+
)
21+
`.pipe(Effect.catchAll(() => Effect.dieMessage('Failed to create contractAbi table')))
22+
2223
return AbiStore.of({
2324
strategies,
2425
set: (key, value) =>

0 commit comments

Comments
 (0)