Skip to content

Commit fd553ac

Browse files
committed
Add experimental strategy to resolve erc20 abis
1 parent d8e71f7 commit fd553ac

File tree

5 files changed

+91
-35
lines changed

5 files changed

+91
-35
lines changed

.changeset/lucky-crabs-float.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@3loop/transaction-decoder': minor
3+
---
4+
5+
Add expermiental erc20 abi strategy and change how sql strategy is defined

apps/web/src/lib/decode.ts

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
import { getProvider, RPCProviderLive } from './rpc-provider'
2-
import { Effect, Layer, ManagedRuntime } from 'effect'
2+
import { Config, Effect, Layer, ManagedRuntime } from 'effect'
33
import {
44
DecodedTransaction,
55
DecodeResult,
66
decodeCalldata as calldataDecoder,
77
decodeTransactionByHash,
88
EtherscanV2StrategyResolver,
9-
FetchTransactionError,
9+
ExperimentalErc20AbiStrategyResolver,
1010
FourByteStrategyResolver,
1111
OpenchainStrategyResolver,
12-
RPCFetchError,
1312
SourcifyStrategyResolver,
14-
UnknownNetwork,
15-
UnsupportedEvent,
1613
AbiStore,
1714
AbiParams,
1815
ContractAbiResult,
1916
ContractMetaStore,
2017
ContractMetaParams,
2118
ContractMetaResult,
2219
PublicClient,
20+
ERC20RPCStrategyResolver,
21+
NFTRPCStrategyResolver,
22+
ProxyRPCStrategyResolver,
2323
} from '@3loop/transaction-decoder'
2424
import { SqlAbiStore, SqlContractMetaStore } from '@3loop/transaction-decoder/sql'
2525
import { Hex } from 'viem'
@@ -29,19 +29,33 @@ import { SqlClient } from '@effect/sql/SqlClient'
2929
import { ConfigError } from 'effect/ConfigError'
3030
import { SqlError } from '@effect/sql/SqlError'
3131

32-
const AbiStoreLive = SqlAbiStore.make({
33-
default: [
34-
EtherscanV2StrategyResolver({
35-
apikey: process.env.ETHERSCAN_API_KEY,
36-
}),
37-
SourcifyStrategyResolver(),
38-
OpenchainStrategyResolver(),
39-
FourByteStrategyResolver(),
40-
],
41-
})
32+
const AbiStoreLive = Layer.unwrapEffect(
33+
Effect.gen(function* () {
34+
const service = yield* PublicClient
35+
const apikey = yield* Config.withDefault(Config.string('ETHERSCAN_API_KEY'), undefined)
36+
return SqlAbiStore.make({
37+
default: [
38+
EtherscanV2StrategyResolver({
39+
apikey: apikey,
40+
}),
41+
ExperimentalErc20AbiStrategyResolver(service),
42+
OpenchainStrategyResolver(),
43+
SourcifyStrategyResolver(),
44+
FourByteStrategyResolver(),
45+
],
46+
})
47+
}),
48+
)
4249

43-
const MetaStoreLive = SqlContractMetaStore.make()
50+
const MetaStoreLive = Layer.unwrapEffect(
51+
Effect.gen(function* () {
52+
const service = yield* PublicClient
4453

54+
return SqlContractMetaStore.make({
55+
default: [ERC20RPCStrategyResolver(service), NFTRPCStrategyResolver(service), ProxyRPCStrategyResolver(service)],
56+
})
57+
}),
58+
)
4559
const DataLayer = Layer.mergeAll(RPCProviderLive, DatabaseLive)
4660
const LoadersLayer = Layer.mergeAll(AbiStoreLive, MetaStoreLive)
4761
const MainLayer = Layer.provideMerge(LoadersLayer, DataLayer) as Layer.Layer<
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import * as RequestModel from './request-model.js'
2+
import { Effect, RequestResolver } from 'effect'
3+
import { PublicClient } from '../public-client.js'
4+
import { erc20Abi, getAddress, getContract } from 'viem'
5+
6+
const getLocalFragments = (service: PublicClient, { address, chainID }: RequestModel.GetContractABIStrategy) =>
7+
Effect.gen(function* () {
8+
const client = yield* service
9+
.getPublicClient(chainID)
10+
.pipe(
11+
Effect.catchAll(() =>
12+
Effect.fail(new RequestModel.ResolveStrategyABIError('local-strategy', address, chainID)),
13+
),
14+
)
15+
16+
const inst = getContract({
17+
abi: erc20Abi,
18+
address: getAddress(address),
19+
client: client.client,
20+
})
21+
22+
const decimals = yield* Effect.tryPromise({
23+
try: () => inst.read.decimals(),
24+
catch: () => new RequestModel.ResolveStrategyABIError('local-strategy', address, chainID),
25+
})
26+
27+
if (decimals != null) {
28+
return [
29+
{
30+
type: 'address',
31+
address,
32+
chainID,
33+
abi: JSON.stringify(erc20Abi),
34+
},
35+
] as RequestModel.ContractABI[]
36+
}
37+
38+
return yield* Effect.fail(new RequestModel.ResolveStrategyABIError('local-strategy', address, chainID))
39+
})
40+
41+
export const ExperimentalErc20AbiStrategyResolver = (
42+
service: PublicClient,
43+
): RequestModel.ContractAbiResolverStrategy => {
44+
return {
45+
type: 'address',
46+
resolver: RequestResolver.fromEffect((req: RequestModel.GetContractABIStrategy) =>
47+
Effect.withSpan(getLocalFragments(service, req), 'AbiStrategy.ExperimentalErc20AbiStrategyResolver', {
48+
attributes: { chainID: req.chainID, address: req.address },
49+
}),
50+
),
51+
}
52+
}

packages/transaction-decoder/src/abi-strategy/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from './blockscout-abi.js'
22
export * from './etherscan-abi.js'
33
export * from './etherscanv2-abi.js'
4+
export * from './experimental-erc20.js'
45
export * from './fourbyte-abi.js'
56
export * from './openchain-abi.js'
67
export * from './request-model.js'

packages/transaction-decoder/src/sql/contract-meta-store.ts

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,14 @@
11
import { SqlClient } from '@effect/sql'
22
import { Effect, Layer } from 'effect'
3-
import {
4-
ContractData,
5-
ContractMetaStore,
6-
ERC20RPCStrategyResolver,
7-
NFTRPCStrategyResolver,
8-
ProxyRPCStrategyResolver,
9-
PublicClient,
10-
} from '../effect.js'
3+
import { ContractData, ContractMetaStore } from '../effect.js'
114

12-
export const make = () =>
5+
export const make = (strategies: ContractMetaStore['strategies']) =>
136
Layer.effect(
147
ContractMetaStore,
158
Effect.gen(function* () {
169
const sql = yield* SqlClient.SqlClient
17-
const publicClient = yield* PublicClient
18-
1910
const table = sql('_loop_decoder_contract_meta_')
2011

21-
// TODO; add timestamp to the table
2212
yield* sql`
2313
CREATE TABLE IF NOT EXISTS ${table} (
2414
address TEXT NOT NULL,
@@ -37,13 +27,7 @@ export const make = () =>
3727
)
3828

3929
return ContractMetaStore.of({
40-
strategies: {
41-
default: [
42-
ERC20RPCStrategyResolver(publicClient),
43-
NFTRPCStrategyResolver(publicClient),
44-
ProxyRPCStrategyResolver(publicClient),
45-
],
46-
},
30+
strategies,
4731
set: (key, value) =>
4832
Effect.gen(function* () {
4933
if (value.status === 'success') {

0 commit comments

Comments
 (0)