diff --git a/.changeset/swift-taxis-impress.md b/.changeset/swift-taxis-impress.md new file mode 100644 index 0000000..308bbea --- /dev/null +++ b/.changeset/swift-taxis-impress.md @@ -0,0 +1,5 @@ +--- +'@3loop/transaction-decoder': minor +--- + +Add an id to each strategy to make each request to a strategy unique. Effect will cache the request in a single global cache, thus to avoid the same request of being cached across different strategies we added an unique id that will identify each request. diff --git a/packages/transaction-decoder/src/abi-loader.ts b/packages/transaction-decoder/src/abi-loader.ts index 312f8ec..fd17c6a 100644 --- a/packages/transaction-decoder/src/abi-loader.ts +++ b/packages/transaction-decoder/src/abi-loader.ts @@ -214,17 +214,22 @@ const AbiLoaderRequestResolver: Effect.Effect< const response = yield* Effect.forEach( remaining, (req) => { - const strategyRequest = new GetContractABIStrategy({ - address: req.address, - chainID: req.chainID, - }) - const allAvailableStrategies = Array.prependAll(strategies.default, strategies[req.chainID] ?? []).filter( (strategy) => strategy.type === 'address', ) return Effect.validateFirst(allAvailableStrategies, (strategy) => { - return pipe(Effect.request(strategyRequest, strategy.resolver), Effect.withRequestCaching(false)) + return pipe( + Effect.request( + new GetContractABIStrategy({ + address: req.address, + chainId: req.chainID, + strategyId: strategy.id, + }), + strategy.resolver, + ), + Effect.withRequestCaching(true), + ) }).pipe( Effect.map(Either.left), Effect.orElseSucceed(() => Either.right(req)), @@ -239,23 +244,35 @@ const AbiLoaderRequestResolver: Effect.Effect< const [addressStrategyResults, notFound] = Array.partitionMap(response, (res) => res) // NOTE: Secondly we request strategies to fetch fragments - const fragmentStrategyResults = yield* Effect.forEach(notFound, ({ chainID, address, event, signature }) => { - const strategyRequest = new GetContractABIStrategy({ - address, - chainID, - event, - signature, - }) - - const allAvailableStrategies = Array.prependAll(strategies.default, strategies[chainID] ?? []).filter( - (strategy) => strategy.type === 'fragment', - ) + const fragmentStrategyResults = yield* Effect.forEach( + notFound, + ({ chainID, address, event, signature }) => { + const allAvailableStrategies = Array.prependAll(strategies.default, strategies[chainID] ?? []).filter( + (strategy) => strategy.type === 'fragment', + ) - // TODO: Distinct the errors and missing data, so we can retry on errors - return Effect.validateFirst(allAvailableStrategies, (strategy) => - pipe(Effect.request(strategyRequest, strategy.resolver), Effect.withRequestCaching(false)), - ).pipe(Effect.orElseSucceed(() => null)) - }) + // TODO: Distinct the errors and missing data, so we can retry on errors + return Effect.validateFirst(allAvailableStrategies, (strategy) => + pipe( + Effect.request( + new GetContractABIStrategy({ + address, + chainId: chainID, + event, + signature, + strategyId: strategy.id, + }), + strategy.resolver, + ), + Effect.withRequestCaching(true), + ), + ).pipe(Effect.orElseSucceed(() => null)) + }, + { + concurrency: 'unbounded', + batching: true, + }, + ) const strategyResults = Array.appendAll(addressStrategyResults, fragmentStrategyResults) @@ -299,7 +316,7 @@ export const getAndCacheAbi = (params: AbiParams) => }).pipe( Effect.withSpan('AbiLoader.GetAndCacheAbi', { attributes: { - chainID: params.chainID, + chainId: params.chainID, address: params.address, event: params.event, signature: params.signature, diff --git a/packages/transaction-decoder/src/abi-strategy/blockscout-abi.ts b/packages/transaction-decoder/src/abi-strategy/blockscout-abi.ts index aae6527..e79ad42 100644 --- a/packages/transaction-decoder/src/abi-strategy/blockscout-abi.ts +++ b/packages/transaction-decoder/src/abi-strategy/blockscout-abi.ts @@ -2,7 +2,7 @@ import { Effect, RequestResolver } from 'effect' import * as RequestModel from './request-model.js' async function fetchContractABI( - { address, chainID }: RequestModel.GetContractABIStrategy, + { address, chainId }: RequestModel.GetContractABIStrategy, config: { apikey?: string; endpoint: string }, ): Promise { const endpoint = config.endpoint @@ -25,7 +25,7 @@ async function fetchContractABI( if (json.status === '1') { return [ { - chainID, + chainID: chainId, address, abi: json.result, type: 'address', @@ -33,7 +33,7 @@ async function fetchContractABI( ] } - throw new Error(`Failed to fetch ABI for ${address} on chain ${chainID}`) + throw new Error(`Failed to fetch ABI for ${address} on chain ${chainId}`) } export const BlockscoutStrategyResolver = (config: { @@ -41,15 +41,16 @@ export const BlockscoutStrategyResolver = (config: { endpoint: string }): RequestModel.ContractAbiResolverStrategy => { return { + id: 'blockscout-strategy', type: 'address', resolver: RequestResolver.fromEffect((req: RequestModel.GetContractABIStrategy) => Effect.withSpan( Effect.tryPromise({ try: () => fetchContractABI(req, config), - catch: () => new RequestModel.ResolveStrategyABIError('Blockscout', req.address, req.chainID), + catch: () => new RequestModel.ResolveStrategyABIError('Blockscout', req.address, req.chainId), }), 'AbiStrategy.BlockscoutStrategyResolver', - { attributes: { chainID: req.chainID, address: req.address } }, + { attributes: { chainId: req.chainId, address: req.address } }, ), ), } diff --git a/packages/transaction-decoder/src/abi-strategy/etherscan-abi.ts b/packages/transaction-decoder/src/abi-strategy/etherscan-abi.ts index 17bdb1c..f6bbae8 100644 --- a/packages/transaction-decoder/src/abi-strategy/etherscan-abi.ts +++ b/packages/transaction-decoder/src/abi-strategy/etherscan-abi.ts @@ -48,10 +48,10 @@ const endpoints: { [k: number]: string } = { } async function fetchContractABI( - { address, chainID }: RequestModel.GetContractABIStrategy, + { address, chainId }: RequestModel.GetContractABIStrategy, config?: { apikey?: string; endpoint?: string }, ): Promise { - const endpoint = config?.endpoint ?? endpoints[chainID] + const endpoint = config?.endpoint ?? endpoints[chainId] const params: Record = { module: 'contract', action: 'getabi', @@ -72,13 +72,13 @@ async function fetchContractABI( { type: 'address', address, - chainID, + chainID: chainId, abi: json.result, }, ] } - throw new Error(`Failed to fetch ABI for ${address} on chain ${chainID}`) + throw new Error(`Failed to fetch ABI for ${address} on chain ${chainId}`) } export const EtherscanStrategyResolver = (config?: { @@ -86,15 +86,16 @@ export const EtherscanStrategyResolver = (config?: { endpoint?: string }): RequestModel.ContractAbiResolverStrategy => { return { + id: 'etherscan-strategy', type: 'address', resolver: RequestResolver.fromEffect((req: RequestModel.GetContractABIStrategy) => Effect.withSpan( Effect.tryPromise({ try: () => fetchContractABI(req, config), - catch: () => new RequestModel.ResolveStrategyABIError('etherscan', req.address, req.chainID), + catch: () => new RequestModel.ResolveStrategyABIError('etherscan', req.address, req.chainId), }), 'AbiStrategy.EtherscanStrategyResolver', - { attributes: { chainID: req.chainID, address: req.address } }, + { attributes: { chainId: req.chainId, address: req.address } }, ), ), } diff --git a/packages/transaction-decoder/src/abi-strategy/etherscanv2-abi.ts b/packages/transaction-decoder/src/abi-strategy/etherscanv2-abi.ts index df8eb33..8f65885 100644 --- a/packages/transaction-decoder/src/abi-strategy/etherscanv2-abi.ts +++ b/packages/transaction-decoder/src/abi-strategy/etherscanv2-abi.ts @@ -4,13 +4,13 @@ import * as RequestModel from './request-model.js' const endpoint = 'https://api.etherscan.io/v2/api' async function fetchContractABI( - { address, chainID }: RequestModel.GetContractABIStrategy, + { address, chainId }: RequestModel.GetContractABIStrategy, config?: { apikey?: string }, ): Promise { const params: Record = { module: 'contract', action: 'getabi', - chainId: chainID.toString(), + chainId: chainId.toString(), address, } @@ -28,26 +28,27 @@ async function fetchContractABI( { type: 'address', address, - chainID, + chainID: chainId, abi: json.result, }, ] } - throw new Error(`Failed to fetch ABI for ${address} on chain ${chainID}`) + throw new Error(`Failed to fetch ABI for ${address} on chain ${chainId}`) } export const EtherscanV2StrategyResolver = (config?: { apikey?: string }): RequestModel.ContractAbiResolverStrategy => { return { + id: 'etherscanV2-strategy', type: 'address', resolver: RequestResolver.fromEffect((req: RequestModel.GetContractABIStrategy) => Effect.withSpan( Effect.tryPromise({ try: () => fetchContractABI(req, config), - catch: () => new RequestModel.ResolveStrategyABIError('etherscanV2', req.address, req.chainID), + catch: () => new RequestModel.ResolveStrategyABIError('etherscanV2', req.address, req.chainId), }), 'AbiStrategy.EtherscanV2StrategyResolver', - { attributes: { chainID: req.chainID, address: req.address } }, + { attributes: { chainId: req.chainId, address: req.address } }, ), ), } diff --git a/packages/transaction-decoder/src/abi-strategy/experimental-erc20.ts b/packages/transaction-decoder/src/abi-strategy/experimental-erc20.ts index 4d4156b..3f3222c 100644 --- a/packages/transaction-decoder/src/abi-strategy/experimental-erc20.ts +++ b/packages/transaction-decoder/src/abi-strategy/experimental-erc20.ts @@ -3,13 +3,13 @@ import { Effect, RequestResolver } from 'effect' import { PublicClient } from '../public-client.js' import { erc20Abi, getAddress, getContract } from 'viem' -const getLocalFragments = (service: PublicClient, { address, chainID }: RequestModel.GetContractABIStrategy) => +const getLocalFragments = (service: PublicClient, { address, chainId }: RequestModel.GetContractABIStrategy) => Effect.gen(function* () { const client = yield* service - .getPublicClient(chainID) + .getPublicClient(chainId) .pipe( Effect.catchAll(() => - Effect.fail(new RequestModel.ResolveStrategyABIError('local-strategy', address, chainID)), + Effect.fail(new RequestModel.ResolveStrategyABIError('local-strategy', address, chainId)), ), ) @@ -21,7 +21,7 @@ const getLocalFragments = (service: PublicClient, { address, chainID }: RequestM const decimals = yield* Effect.tryPromise({ try: () => inst.read.decimals(), - catch: () => new RequestModel.ResolveStrategyABIError('local-strategy', address, chainID), + catch: () => new RequestModel.ResolveStrategyABIError('local-strategy', address, chainId), }) if (decimals != null) { @@ -29,23 +29,24 @@ const getLocalFragments = (service: PublicClient, { address, chainID }: RequestM { type: 'address', address, - chainID, + chainID: chainId, abi: JSON.stringify(erc20Abi), }, ] as RequestModel.ContractABI[] } - return yield* Effect.fail(new RequestModel.ResolveStrategyABIError('local-strategy', address, chainID)) + return yield* Effect.fail(new RequestModel.ResolveStrategyABIError('local-strategy', address, chainId)) }) export const ExperimentalErc20AbiStrategyResolver = ( service: PublicClient, ): RequestModel.ContractAbiResolverStrategy => { return { + id: 'experimental-erc20-strategy', type: 'address', resolver: RequestResolver.fromEffect((req: RequestModel.GetContractABIStrategy) => Effect.withSpan(getLocalFragments(service, req), 'AbiStrategy.ExperimentalErc20AbiStrategyResolver', { - attributes: { chainID: req.chainID, address: req.address }, + attributes: { chainId: req.chainId, address: req.address }, }), ), } diff --git a/packages/transaction-decoder/src/abi-strategy/fourbyte-abi.ts b/packages/transaction-decoder/src/abi-strategy/fourbyte-abi.ts index 33b0c29..65ef48c 100644 --- a/packages/transaction-decoder/src/abi-strategy/fourbyte-abi.ts +++ b/packages/transaction-decoder/src/abi-strategy/fourbyte-abi.ts @@ -24,7 +24,7 @@ async function fetchABI({ address, event, signature, - chainID, + chainId, }: RequestModel.GetContractABIStrategy): Promise { if (signature != null) { const full_match = await fetch(`${endpoint}/signatures/?hex_signature=${signature}`) @@ -34,7 +34,7 @@ async function fetchABI({ return json.results.map((result) => ({ type: 'func', address, - chainID, + chainID: chainId, abi: parseFunctionSignature(result.text_signature), signature, })) @@ -48,27 +48,28 @@ async function fetchABI({ return json.results.map((result) => ({ type: 'event', address, - chainID, + chainID: chainId, abi: parseEventSignature(result.text_signature), event, })) } } - throw new Error(`Failed to fetch ABI for ${address} on chain ${chainID}`) + throw new Error(`Failed to fetch ABI for ${address} on chain ${chainId}`) } export const FourByteStrategyResolver = (): RequestModel.ContractAbiResolverStrategy => { return { + id: 'fourbyte-strategy', type: 'fragment', resolver: RequestResolver.fromEffect((req: RequestModel.GetContractABIStrategy) => Effect.withSpan( Effect.tryPromise({ try: () => fetchABI(req), - catch: () => new RequestModel.ResolveStrategyABIError('4byte.directory', req.address, req.chainID), + catch: () => new RequestModel.ResolveStrategyABIError('4byte.directory', req.address, req.chainId), }), 'AbiStrategy.FourByteStrategyResolver', - { attributes: { chainID: req.chainID, address: req.address } }, + { attributes: { chainId: req.chainId, address: req.address } }, ), ), } diff --git a/packages/transaction-decoder/src/abi-strategy/openchain-abi.ts b/packages/transaction-decoder/src/abi-strategy/openchain-abi.ts index f71b35d..6714555 100644 --- a/packages/transaction-decoder/src/abi-strategy/openchain-abi.ts +++ b/packages/transaction-decoder/src/abi-strategy/openchain-abi.ts @@ -40,7 +40,7 @@ function parseEventSignature(signature: string): string { async function fetchABI({ address, - chainID, + chainId, signature, event, }: RequestModel.GetContractABIStrategy): Promise { @@ -52,7 +52,7 @@ async function fetchABI({ return json.result.function[signature].map((f) => ({ type: 'func', address, - chainID, + chainID: chainId, abi: parseFunctionSignature(f.name), signature, })) @@ -66,27 +66,28 @@ async function fetchABI({ return json.result.event[event].map((e) => ({ type: 'event', address, - chainID, + chainID: chainId, abi: parseEventSignature(e.name), event, })) } } - throw new Error(`Failed to fetch ABI for ${address} on chain ${chainID}`) + throw new Error(`Failed to fetch ABI for ${address} on chain ${chainId}`) } export const OpenchainStrategyResolver = (): RequestModel.ContractAbiResolverStrategy => { return { + id: 'openchain-strategy', type: 'fragment', resolver: RequestResolver.fromEffect((req: RequestModel.GetContractABIStrategy) => Effect.withSpan( Effect.tryPromise({ try: () => fetchABI(req), - catch: () => new RequestModel.ResolveStrategyABIError('openchain', req.address, req.chainID), + catch: () => new RequestModel.ResolveStrategyABIError('openchain', req.address, req.chainId), }), 'AbiStrategy.OpenchainStrategyResolver', - { attributes: { chainID: req.chainID, address: req.address } }, + { attributes: { chainId: req.chainId, address: req.address } }, ), ), } diff --git a/packages/transaction-decoder/src/abi-strategy/request-model.ts b/packages/transaction-decoder/src/abi-strategy/request-model.ts index 610d806..8bcb5bc 100644 --- a/packages/transaction-decoder/src/abi-strategy/request-model.ts +++ b/packages/transaction-decoder/src/abi-strategy/request-model.ts @@ -43,6 +43,7 @@ export type ContractABI = FunctionFragmentABI | EventFragmentABI | AddressABI export interface ContractAbiResolverStrategy { type: 'address' | 'fragment' + id: string resolver: RequestResolver.RequestResolver } @@ -51,13 +52,14 @@ export class GetContractABIStrategy extends Schema.TaggedRequest { const normalisedAddress = getAddress(address) - const full_match = await fetch(`${endpoint}/full_match/${chainID}/${normalisedAddress}/metadata.json`) + const full_match = await fetch(`${endpoint}/full_match/${chainId}/${normalisedAddress}/metadata.json`) if (full_match.status === 200) { const json = (await full_match.json()) as SourcifyResponse @@ -25,39 +25,40 @@ async function fetchContractABI({ { type: 'address', address, - chainID, + chainID: chainId, abi: JSON.stringify(json.output.abi), }, ] } - const partial_match = await fetch(`${endpoint}/partial_match/${chainID}/${normalisedAddress}/metadata.json`) + const partial_match = await fetch(`${endpoint}/partial_match/${chainId}/${normalisedAddress}/metadata.json`) if (partial_match.status === 200) { const json = (await partial_match.json()) as SourcifyResponse return [ { type: 'address', address, - chainID, + chainID: chainId, abi: JSON.stringify(json.output.abi), }, ] } - throw new Error(`Failed to fetch ABI for ${address} on chain ${chainID}`) + throw new Error(`Failed to fetch ABI for ${address} on chain ${chainId}`) } export const SourcifyStrategyResolver = (): RequestModel.ContractAbiResolverStrategy => { return { + id: 'sourcify-strategy', type: 'address', resolver: RequestResolver.fromEffect((req: RequestModel.GetContractABIStrategy) => Effect.withSpan( Effect.tryPromise({ try: () => fetchContractABI(req), - catch: () => new RequestModel.ResolveStrategyABIError('sourcify', req.address, req.chainID), + catch: () => new RequestModel.ResolveStrategyABIError('sourcify', req.address, req.chainId), }), 'AbiStrategy.SourcifyStrategyResolver', - { attributes: { chainID: req.chainID, address: req.address } }, + { attributes: { chainId: req.chainId, address: req.address } }, ), ), } diff --git a/packages/transaction-decoder/src/contract-meta-loader.ts b/packages/transaction-decoder/src/contract-meta-loader.ts index 7417016..5c118ac 100644 --- a/packages/transaction-decoder/src/contract-meta-loader.ts +++ b/packages/transaction-decoder/src/contract-meta-loader.ts @@ -1,6 +1,6 @@ import { Context, Effect, RequestResolver, Request, Array, Either, pipe, Schema, PrimaryKey, SchemaAST } from 'effect' import { ContractData } from './types.js' -import { GetContractMetaStrategy } from './meta-strategy/request-model.js' +import { ContractMetaResolverStrategy, GetContractMetaStrategy } from './meta-strategy/request-model.js' import { Address } from 'viem' export interface ContractMetaParams { @@ -28,7 +28,7 @@ export type ContractMetaResult = ContractMetaSuccess | ContractMetaNotFound | Co type ChainOrDefault = number | 'default' export interface ContractMetaStore { - readonly strategies: Record[]> + readonly strategies: Record readonly set: (arg: Key, value: Value) => Effect.Effect /** * The `get` function might return 3 states: @@ -156,16 +156,21 @@ const ContractMetaLoaderRequestResolver = RequestResolver.makeBatched((requests: // Fetch ContractMeta from the strategies const strategyResults = yield* Effect.forEach(remaining, ({ chainID, address }) => { - const strategyRequest = new GetContractMetaStrategy({ - address, - chainID, - }) - const allAvailableStrategies = Array.prependAll(strategies.default, strategies[chainID] ?? []) // TODO: Distinct the errors and missing data, so we can retry on errors return Effect.validateFirst(allAvailableStrategies, (strategy) => - pipe(Effect.request(strategyRequest, strategy), Effect.withRequestCaching(false)), + pipe( + Effect.request( + new GetContractMetaStrategy({ + address, + chainId: chainID, + strategyId: strategy.id, + }), + strategy.resolver, + ), + Effect.withRequestCaching(true), + ), ).pipe(Effect.orElseSucceed(() => null)) }) diff --git a/packages/transaction-decoder/src/meta-strategy/erc20-rpc-strategy.ts b/packages/transaction-decoder/src/meta-strategy/erc20-rpc-strategy.ts index cdbd1e2..aa99235 100644 --- a/packages/transaction-decoder/src/meta-strategy/erc20-rpc-strategy.ts +++ b/packages/transaction-decoder/src/meta-strategy/erc20-rpc-strategy.ts @@ -5,13 +5,16 @@ import { PublicClient } from '../public-client.js' import { erc20Abi } from 'viem' import { MULTICALL3_ADDRESS } from '../decoding/constants.js' -export const ERC20RPCStrategyResolver = (publicClientLive: PublicClient) => - RequestResolver.fromEffect(({ chainID, address }: RequestModel.GetContractMetaStrategy) => +export const ERC20RPCStrategyResolver = ( + publicClientLive: PublicClient, +): RequestModel.ContractMetaResolverStrategy => ({ + id: 'erc20-rpc-strategy', + resolver: RequestResolver.fromEffect(({ chainId, address }: RequestModel.GetContractMetaStrategy) => Effect.gen(function* () { const service = yield* PublicClient - const { client } = yield* service.getPublicClient(chainID) + const { client } = yield* service.getPublicClient(chainId) - const fail = new RequestModel.ResolveStrategyMetaError('ERC20RPCStrategy', address, chainID) + const fail = new RequestModel.ResolveStrategyMetaError('ERC20RPCStrategy', address, chainId) const [symbolResponse, decimalsResponse, nameResponse] = yield* Effect.tryPromise({ try: () => @@ -51,13 +54,14 @@ export const ERC20RPCStrategyResolver = (publicClientLive: PublicClient) => tokenSymbol: symbolResponse.result, decimals: Number(decimalsResponse.result), type: 'ERC20' as ContractType, - chainID, + chainID: chainId, } return meta - }).pipe(Effect.withSpan('MetaStrategy.ERC20RPCStrategyResolver', { attributes: { chainID, address } })), + }).pipe(Effect.withSpan('MetaStrategy.ERC20RPCStrategyResolver', { attributes: { chainId, address } })), ).pipe( RequestResolver.contextFromServices(PublicClient), Effect.provideService(PublicClient, publicClientLive), Effect.runSync, - ) + ), +}) diff --git a/packages/transaction-decoder/src/meta-strategy/nft-rpc-strategy.ts b/packages/transaction-decoder/src/meta-strategy/nft-rpc-strategy.ts index 7633b66..0defad1 100644 --- a/packages/transaction-decoder/src/meta-strategy/nft-rpc-strategy.ts +++ b/packages/transaction-decoder/src/meta-strategy/nft-rpc-strategy.ts @@ -5,11 +5,12 @@ import { PublicClient } from '../public-client.js' import { erc721Abi, getContract } from 'viem' import { ERC1155InterfaceId, ERC721InterfaceId, erc165Abi } from './constants.js' -export const NFTRPCStrategyResolver = (publicClientLive: PublicClient) => - RequestResolver.fromEffect(({ chainID, address }: RequestModel.GetContractMetaStrategy) => +export const NFTRPCStrategyResolver = (publicClientLive: PublicClient): RequestModel.ContractMetaResolverStrategy => ({ + id: 'nft-rpc-strategy', + resolver: RequestResolver.fromEffect(({ chainId, address }: RequestModel.GetContractMetaStrategy) => Effect.gen(function* () { const service = yield* PublicClient - const { client } = yield* service.getPublicClient(chainID) + const { client } = yield* service.getPublicClient(chainId) // const proxyResult = yield* getProxyStorageSlot({ address, chainID }) // const { address: implementationAddress } = proxyResult ?? {} @@ -21,7 +22,7 @@ export const NFTRPCStrategyResolver = (publicClientLive: PublicClient) => client, }) - const fail = new RequestModel.ResolveStrategyMetaError('NFTRPCStrategy', contractAddress, chainID) + const fail = new RequestModel.ResolveStrategyMetaError('NFTRPCStrategy', contractAddress, chainId) const [isERC721, isERC1155] = yield* Effect.all( [ @@ -73,13 +74,14 @@ export const NFTRPCStrategyResolver = (publicClientLive: PublicClient) => contractName: name, tokenSymbol: symbol, type, - chainID, + chainID: chainId, } return meta - }).pipe(Effect.withSpan('MetaStrategy.NFTRPCStrategyResolver', { attributes: { chainID, address } })), + }).pipe(Effect.withSpan('MetaStrategy.NFTRPCStrategyResolver', { attributes: { chainId, address } })), ).pipe( RequestResolver.contextFromServices(PublicClient), Effect.provideService(PublicClient, publicClientLive), Effect.runSync, - ) + ), +}) diff --git a/packages/transaction-decoder/src/meta-strategy/proxy-rpc-strategy.ts b/packages/transaction-decoder/src/meta-strategy/proxy-rpc-strategy.ts index 50eb68e..cdddf94 100644 --- a/packages/transaction-decoder/src/meta-strategy/proxy-rpc-strategy.ts +++ b/packages/transaction-decoder/src/meta-strategy/proxy-rpc-strategy.ts @@ -4,13 +4,14 @@ import { Effect, RequestResolver } from 'effect' import { getProxyStorageSlot } from '../decoding/proxies.js' import { PublicClient } from '../public-client.js' -export const ProxyRPCStrategyResolver = (publicClientLive: PublicClient) => - RequestResolver.fromEffect(({ chainID, address }: RequestModel.GetContractMetaStrategy) => +export const ProxyRPCStrategyResolver = (publicClientLive: PublicClient) => ({ + id: 'proxy-rpc-strategy', + resolver: RequestResolver.fromEffect(({ chainId, address }: RequestModel.GetContractMetaStrategy) => Effect.gen(function* () { - const proxyResult = yield* getProxyStorageSlot({ address, chainID }) + const proxyResult = yield* getProxyStorageSlot({ address, chainID: chainId }) const { address: implementationAddress, type: proxyType } = proxyResult ?? {} - const fail = new RequestModel.ResolveStrategyMetaError('ProxyRPCStrategy', address, chainID) + const fail = new RequestModel.ResolveStrategyMetaError('ProxyRPCStrategy', address, chainId) if (!implementationAddress || !proxyType) { return yield* Effect.fail(fail) @@ -23,7 +24,7 @@ export const ProxyRPCStrategyResolver = (publicClientLive: PublicClient) => address, contractAddress: address, type: 'SAFE-PROXY' as ContractType, - chainID, + chainID: chainId, } } @@ -42,9 +43,10 @@ export const ProxyRPCStrategyResolver = (publicClientLive: PublicClient) => } return meta - }).pipe(Effect.withSpan('MetaStrategy.ProxyRPCStrategyResolver', { attributes: { chainID, address } })), + }).pipe(Effect.withSpan('MetaStrategy.ProxyRPCStrategyResolver', { attributes: { chainId, address } })), ).pipe( RequestResolver.contextFromServices(PublicClient), Effect.provideService(PublicClient, publicClientLive), Effect.runSync, - ) + ), +}) diff --git a/packages/transaction-decoder/src/meta-strategy/request-model.ts b/packages/transaction-decoder/src/meta-strategy/request-model.ts index fe10cf9..0e8478f 100644 --- a/packages/transaction-decoder/src/meta-strategy/request-model.ts +++ b/packages/transaction-decoder/src/meta-strategy/request-model.ts @@ -1,11 +1,12 @@ import { UnknownNetwork } from '../public-client.js' import { ContractData } from '../types.js' -import { PrimaryKey, Schema, SchemaAST } from 'effect' +import { PrimaryKey, RequestResolver, Schema, SchemaAST } from 'effect' import { Address } from 'viem' export interface FetchMetaParams { - readonly chainID: number + readonly chainId: number readonly address: Address + readonly strategyId: string } export class ResolveStrategyMetaError { @@ -17,6 +18,11 @@ export class ResolveStrategyMetaError { ) {} } +export interface ContractMetaResolverStrategy { + id: string + resolver: RequestResolver.RequestResolver +} + class SchemaAddress extends Schema.make
(SchemaAST.stringKeyword) {} class SchemaContractData extends Schema.make(SchemaAST.objectKeyword) {} export class GetContractMetaStrategy extends Schema.TaggedRequest()( @@ -25,12 +31,13 @@ export class GetContractMetaStrategy extends Schema.TaggedRequest PublicClientObject | undefined @@ -26,7 +26,7 @@ export interface VanillaAbiStore { set: (key: AbiParams, val: ContractAbiResult) => Promise } -type VanillaContractMetaStategy = (client: PublicClient) => RequestResolver.RequestResolver +type VanillaContractMetaStategy = (client: PublicClient) => ContractMetaResolverStrategy export interface VanillaContractMetaStore { strategies?: readonly VanillaContractMetaStategy[]