From ddd028b05ca93c1a4ab8381ee2ea4885b06ff6d9 Mon Sep 17 00:00:00 2001 From: Gheorghe Pinzaru Date: Thu, 25 Apr 2024 20:20:13 +0200 Subject: [PATCH 1/2] Update effect to stable version 3 --- .prettierignore | 4 + .prettierrc.json | 10 +- .vscode/extensions.json | 6 +- apps/docs/package.json | 2 +- .../src/content/docs/guides/effect-api.md | 4 +- apps/docs/tsconfig.json | 2 +- apps/web/components.json | 2 +- apps/web/package.json | 2 +- apps/web/src/lib/contract-loader.ts | 106 ++++++------ package.json | 6 +- packages/eslint-config-custom/package.json | 2 +- packages/transaction-decoder/README.md | 4 +- packages/transaction-decoder/package.json | 154 +++++++++--------- .../transaction-decoder/src/abi-loader.ts | 66 ++++---- .../src/contract-meta-loader.ts | 60 ++++--- .../src/decoding/log-decode.ts | 124 +++++++------- .../src/decoding/proxies.ts | 17 +- .../src/decoding/trace-decode.ts | 58 +++---- .../src/interpreters/index.ts | 8 +- .../src/meta-strategy/erc20-rpc-strategy.ts | 38 ++--- .../src/meta-strategy/nft-rpc-strategy.ts | 72 ++++---- .../transaction-decoder/src/schema/trace.ts | 70 ++++---- .../src/transaction-decoder.ts | 146 ++++++++--------- .../src/transaction-loader.ts | 112 ++++++------- packages/transaction-decoder/src/vanilla.ts | 4 +- .../transaction-decoder/test/constants.ts | 2 +- .../test/mocks/abi-loader-mock.ts | 37 ++--- .../test/transaction-decoder.test.ts | 4 +- .../transaction-decoder/tsconfig.build.json | 34 ++-- packages/transaction-decoder/tsconfig.json | 4 +- packages/tsconfig/base.json | 6 +- packages/tsconfig/universal-esm.json | 7 +- pnpm-lock.yaml | 24 +-- turbo.json | 13 +- 34 files changed, 558 insertions(+), 652 deletions(-) diff --git a/.prettierignore b/.prettierignore index 1b763b1b..df9c894d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,5 @@ CHANGELOG.md +**/node_modules +**/dist +**/test/**/*.json + diff --git a/.prettierrc.json b/.prettierrc.json index 860b46e5..07bd1f27 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,7 +1,7 @@ { - "semi": false, - "tabWidth": 2, - "printWidth": 120, - "singleQuote": true, - "trailingComma": "all" + "semi": false, + "tabWidth": 2, + "printWidth": 120, + "singleQuote": true, + "trailingComma": "all" } diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 4d457c1a..ee9be9e3 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,8 +1,4 @@ { - "recommendations": [ - "astro-build.astro-vscode", - "dbaeumer.vscode-eslint", - "esbenp.prettier-vscode" - ], + "recommendations": ["astro-build.astro-vscode", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode"], "unwantedRecommendations": [] } diff --git a/apps/docs/package.json b/apps/docs/package.json index 672626bb..b83950c7 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -27,4 +27,4 @@ "eslint-plugin-astro": "^0.31.0", "eslint-plugin-mdx": "^2.3.2" } -} \ No newline at end of file +} diff --git a/apps/docs/src/content/docs/guides/effect-api.md b/apps/docs/src/content/docs/guides/effect-api.md index 5341ee6b..61d3e528 100644 --- a/apps/docs/src/content/docs/guides/effect-api.md +++ b/apps/docs/src/content/docs/guides/effect-api.md @@ -106,11 +106,11 @@ const MainLayer = Layer.provideMerge(PublicClientLive, LoadersLayer) 5. Fetch and decode a transaction ```ts -const program = Effect.gen(function* (_) { +const program = Effect.gen(function* () { const hash = '0xab701677e5003fa029164554b81e01bede20b97eda0e2595acda81acf5628f75' const chainID = 5 - return yield* _(decodeTransactionByHash(hash, chainID)) + return yield* decodeTransactionByHash(hash, chainID) }) ``` diff --git a/apps/docs/tsconfig.json b/apps/docs/tsconfig.json index 1a46b476..d78f81ec 100644 --- a/apps/docs/tsconfig.json +++ b/apps/docs/tsconfig.json @@ -1,3 +1,3 @@ { "extends": "astro/tsconfigs/base" -} \ No newline at end of file +} diff --git a/apps/web/components.json b/apps/web/components.json index e4e8e1a7..0c8f2fe8 100644 --- a/apps/web/components.json +++ b/apps/web/components.json @@ -13,4 +13,4 @@ "components": "@/components", "utils": "@/lib/utils" } -} \ No newline at end of file +} diff --git a/apps/web/package.json b/apps/web/package.json index 7b212142..92d6e37e 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -27,7 +27,7 @@ "autoprefixer": "10.4.15", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", - "effect": "^2.4.18", + "effect": "^3.0.5", "eslint": "^8.47.0", "eslint-config-next": "13.4.19", "jsonata": "^2.0.3", diff --git a/apps/web/src/lib/contract-loader.ts b/apps/web/src/lib/contract-loader.ts index f5f48ca9..bad7940f 100644 --- a/apps/web/src/lib/contract-loader.ts +++ b/apps/web/src/lib/contract-loader.ts @@ -33,45 +33,41 @@ export const AbiStoreLive = Layer.succeed( ], }, set: ({ address = {} }) => - Effect.gen(function* (_) { + Effect.gen(function* () { const addressMatches = Object.entries(address) // Cache all addresses - yield* _( - Effect.all( - addressMatches.map(([key, value]) => - Effect.promise(() => - prisma.contractAbi - .create({ - data: { - address: key, - abi: value, - }, - }) - .catch((e) => { - console.error('Failed to cache abi', e) - return null - }), - ), + yield* Effect.all( + addressMatches.map(([key, value]) => + Effect.promise(() => + prisma.contractAbi + .create({ + data: { + address: key, + abi: value, + }, + }) + .catch((e) => { + console.error('Failed to cache abi', e) + return null + }), ), - { - concurrency: 'inherit', - batching: 'inherit', - }, ), + { + concurrency: 'inherit', + batching: 'inherit', + }, ) }), get: ({ address }) => - Effect.gen(function* (_) { + Effect.gen(function* () { const normAddress = address.toLowerCase() - const cached = yield* _( - Effect.promise(() => - prisma.contractAbi.findFirst({ - where: { - address: normAddress, - }, - }), - ), + const cached = yield* Effect.promise(() => + prisma.contractAbi.findFirst({ + where: { + address: normAddress, + }, + }), ) if (cached != null) { @@ -85,8 +81,8 @@ export const AbiStoreLive = Layer.succeed( // TODO: Provide context of RPCProvider export const ContractMetaStoreLive = Layer.effect( ContractMetaStore, - Effect.gen(function* (_) { - const publicClient = yield* _(PublicClient) + Effect.gen(function* () { + const publicClient = yield* PublicClient const erc20Loader = ERC20RPCStrategyResolver(publicClient) const nftLoader = NFTRPCStrategyResolver(publicClient) @@ -95,38 +91,34 @@ export const ContractMetaStoreLive = Layer.effect( default: [erc20Loader, nftLoader], }, get: ({ address, chainID }) => - Effect.gen(function* (_) { + Effect.gen(function* () { const normAddress = address.toLowerCase() - const data = yield* _( - Effect.tryPromise( - () => - prisma.contractMeta.findFirst({ - where: { - address: normAddress, - chainID: chainID, - }, - }) as Promise, - ).pipe(Effect.catchAll((_) => Effect.succeed(null))), - ) + const data = yield* Effect.tryPromise( + () => + prisma.contractMeta.findFirst({ + where: { + address: normAddress, + chainID: chainID, + }, + }) as Promise, + ).pipe(Effect.catchAll((_) => Effect.succeed(null))) return data }), set: ({ address, chainID }, contractMeta) => - Effect.gen(function* (_) { + Effect.gen(function* () { const normAddress = address.toLowerCase() - yield* _( - Effect.tryPromise(() => - prisma.contractMeta.create({ - data: { - ...contractMeta, - decimals: contractMeta.decimals ?? 0, - address: normAddress, - chainID: chainID, - }, - }), - ).pipe(Effect.catchAll((_) => Effect.succeed(null))), - ) + yield* Effect.tryPromise(() => + prisma.contractMeta.create({ + data: { + ...contractMeta, + decimals: contractMeta.decimals ?? 0, + address: normAddress, + chainID: chainID, + }, + }), + ).pipe(Effect.catchAll((_) => Effect.succeed(null))) }), }) }), diff --git a/package.json b/package.json index 22280b33..bf50ad9a 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "dev": "turbo run dev", "lint": "turbo run lint", "fix": "turbo run fix", - "check": "prettier --check \"**/*.{ts,tsx,md}\"", - "format": "prettier --write \"**/*.{ts,tsx,md}\"", + "check": "prettier --check \"**/*.{ts,tsx,json,md}\"", + "format": "prettier --write \"**/*.{ts,tsx,json,md}\"", "version": "changeset version && pnpm install --no-frozen-lockfile && pnpm format", "publish-packages": "pnpm run build && changeset publish", "test": "turbo run test", @@ -21,4 +21,4 @@ }, "packageManager": "pnpm@8.6.10", "name": "@3loop/decoder-monorepo" -} \ No newline at end of file +} diff --git a/packages/eslint-config-custom/package.json b/packages/eslint-config-custom/package.json index 9db8aafc..b7d28990 100644 --- a/packages/eslint-config-custom/package.json +++ b/packages/eslint-config-custom/package.json @@ -10,4 +10,4 @@ "eslint-config-prettier": "^8.10.0", "eslint-plugin-sort-export-all": "^1.4.1" } -} \ No newline at end of file +} diff --git a/packages/transaction-decoder/README.md b/packages/transaction-decoder/README.md index 7b10459d..939cc8b3 100644 --- a/packages/transaction-decoder/README.md +++ b/packages/transaction-decoder/README.md @@ -180,11 +180,11 @@ const MainLayer = Layer.provideMerge(PublicClientLive, LoadersLayer) 5. Fetch and decode a transaction ```ts -const program = Effect.gen(function* (_) { +const program = Effect.gen(function* () { const hash = '0xab701677e5003fa029164554b81e01bede20b97eda0e2595acda81acf5628f75' const chainID = 5 - return yield* _(decodeTransactionByHash(hash, chainID)) + return yield* decodeTransactionByHash(hash, chainID) }) ``` diff --git a/packages/transaction-decoder/package.json b/packages/transaction-decoder/package.json index a12527ed..fde1aaf9 100644 --- a/packages/transaction-decoder/package.json +++ b/packages/transaction-decoder/package.json @@ -1,81 +1,81 @@ { - "name": "@3loop/transaction-decoder", - "version": "0.7.0", - "description": "A library for decoding Ethereum transactions", - "types": "dist/index.d.ts", - "main": "dist/index.cjs", - "license": "GPL-3.0-only", - "type": "module", - "exports": { - ".": { - "require": "./dist/index.cjs", - "import": "./dist/index.js", - "types": "./dist/index.d.ts" - }, - "./*": { - "import": { - "types": "./dist/*.d.ts", - "default": "./dist/*.js" - }, - "require": { - "types": "./dist/*.d.cts", - "default": "./dist/*.cjs" - } - } + "name": "@3loop/transaction-decoder", + "version": "0.7.0", + "description": "A library for decoding Ethereum transactions", + "types": "dist/index.d.ts", + "main": "dist/index.cjs", + "license": "GPL-3.0-only", + "type": "module", + "exports": { + ".": { + "require": "./dist/index.cjs", + "import": "./dist/index.js", + "types": "./dist/index.d.ts" }, - "scripts": { - "dev": "tsup --watch", - "coverage": "vitest run --config ./vitest.config.mts --coverage", - "build": "rm -rf dist && tsup", - "check": "tsc --noEmit", - "lint": "eslint --exit-on-fatal-error --ignore-path .eslintignore --ext .ts .", - "fix": "eslint --ignore-path .eslintignore --ext .ts . --fix", - "test": "vitest run --config ./vitest.config.mts", - "test:watch": "vitest run --config ./vitest.config.mts --watch", - "gen:mock": "node test/mocks/create-mock.js" - }, - "files": [ - "dist/**/*", - "src/**/*", - "README.md" - ], - "peerDependencies": { - "@effect/schema": "^0.64.20", - "effect": "^2.4.18", - "quickjs-emscripten": "^0.29.1", - "viem": "^2.7.22" - }, - "devDependencies": { - "@effect/schema": "^0.64.20", - "@total-typescript/ts-reset": "^0.5.1", - "@types/node": "^20.6.0", - "@types/traverse": "^0.6.35", - "@typescript-eslint/eslint-plugin": "^5.59.6", - "@typescript-eslint/parser": "^5.59.6", - "@vitest/coverage-v8": "0.34.2", - "effect": "2.4.18", - "eslint": "^8.49.0", - "eslint-config-custom": "workspace:*", - "eslint-config-prettier": "^8.10.0", - "prettier": "^2.8.8", - "quickjs-emscripten": "^0.29.1", - "ts-node": "^10.9.1", - "tsconfig": "workspace:*", - "tsup": "^7.2.0", - "typescript": "5.1.3", - "viem": "^2.7.22", - "vite": "4.4.9", - "vite-tsconfig-paths": "4.2.0", - "vitest": "0.34.2" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.16" - }, - "sideEffects": false, - "dependencies": { - "traverse": "^0.6.7" + "./*": { + "import": { + "types": "./dist/*.d.ts", + "default": "./dist/*.js" + }, + "require": { + "types": "./dist/*.d.cts", + "default": "./dist/*.cjs" + } } + }, + "scripts": { + "dev": "tsup --watch", + "coverage": "vitest run --config ./vitest.config.mts --coverage", + "build": "rm -rf dist && tsup", + "check": "tsc --noEmit", + "lint": "eslint --exit-on-fatal-error --ignore-path .eslintignore --ext .ts .", + "fix": "eslint --ignore-path .eslintignore --ext .ts . --fix", + "test": "vitest run --config ./vitest.config.mts", + "test:watch": "vitest run --config ./vitest.config.mts --watch", + "gen:mock": "node test/mocks/create-mock.js" + }, + "files": [ + "dist/**/*", + "src/**/*", + "README.md" + ], + "peerDependencies": { + "@effect/schema": "^0.66.8", + "effect": "^3.0.5", + "quickjs-emscripten": "^0.29.1", + "viem": "^2.7.22" + }, + "devDependencies": { + "@effect/schema": "^0.66.8", + "@total-typescript/ts-reset": "^0.5.1", + "@types/node": "^20.6.0", + "@types/traverse": "^0.6.35", + "@typescript-eslint/eslint-plugin": "^5.59.6", + "@typescript-eslint/parser": "^5.59.6", + "@vitest/coverage-v8": "0.34.2", + "effect": "3.0.5", + "eslint": "^8.49.0", + "eslint-config-custom": "workspace:*", + "eslint-config-prettier": "^8.10.0", + "prettier": "^2.8.8", + "quickjs-emscripten": "^0.29.1", + "ts-node": "^10.9.1", + "tsconfig": "workspace:*", + "tsup": "^7.2.0", + "typescript": "5.1.3", + "viem": "^2.7.22", + "vite": "4.4.9", + "vite-tsconfig-paths": "4.2.0", + "vitest": "0.34.2" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.16" + }, + "sideEffects": false, + "dependencies": { + "traverse": "^0.6.7" + } } diff --git a/packages/transaction-decoder/src/abi-loader.ts b/packages/transaction-decoder/src/abi-loader.ts index 7707da2c..6a6dc05e 100644 --- a/packages/transaction-decoder/src/abi-loader.ts +++ b/packages/transaction-decoder/src/abi-loader.ts @@ -1,4 +1,4 @@ -import { Context, Effect, Either, RequestResolver, Request, ReadonlyArray } from 'effect' +import { Context, Effect, Either, RequestResolver, Request, Array } from 'effect' import { ContractABI, GetContractABIStrategy } from './abi-strategy/request-model.js' export interface GetAbiParams { @@ -14,7 +14,7 @@ export interface AbiStore[]> readonly set: (value: SetValue) => Effect.Effect readonly get: (arg: Key) => Effect.Effect - readonly getMany?: (arg: ReadonlyArray) => Effect.Effect, never> + readonly getMany?: (arg: Array) => Effect.Effect, never> } export const AbiStore = Context.GenericTag('@3loop-decoder/AbiStore') @@ -34,17 +34,17 @@ function makeKey(key: AbiLoader) { } const AbiLoaderRequestResolver = RequestResolver.makeBatched((requests: Array) => - Effect.gen(function* (_) { + Effect.gen(function* () { if (requests.length === 0) return - const abiStore = yield* _(AbiStore) + const abiStore = yield* AbiStore const strategies = abiStore.strategies // NOTE: We can further optimize if we have match by Address by avoid extra requests for each signature // but might need to update the Loader public API - const groups = ReadonlyArray.groupBy(requests, makeKey) + const groups = Array.groupBy(requests, makeKey) const uniqueRequests = Object.values(groups).map((group) => group[0]) - const getMany = (requests: ReadonlyArray) => { + const getMany = (requests: Array) => { if (abiStore.getMany != null) { return abiStore.getMany(requests) } else { @@ -64,10 +64,9 @@ const AbiLoaderRequestResolver = RequestResolver.makeBatched((requests: Array { + Array.partitionMap((resp, i) => { return resp == null ? Either.left(uniqueRequests[i]) : Either.right([uniqueRequests[i], resp] as const) }), ), @@ -75,35 +74,32 @@ const AbiLoaderRequestResolver = RequestResolver.makeBatched((requests: Array { - const group = groups[makeKey(request)] - return Effect.forEach(group, (req) => Request.succeed(req, result), { discard: true }) - }, - { - discard: true, - }, - ), + yield* Effect.forEach( + results, + ([request, result]) => { + const group = groups[makeKey(request)] + return Effect.forEach(group, (req) => Request.succeed(req, result), { discard: true }) + }, + { + discard: true, + }, ) // Load the ABI from the strategies - yield* _( - Effect.forEach(remaining, ({ chainID, address, event, signature }) => { - const strategyRequest = GetContractABIStrategy({ - address, - event, - signature, - chainID, - }) - - const allAvailableStrategies = [...(strategies[chainID] ?? []), ...strategies.default] - - return Effect.validateFirst(allAvailableStrategies, (strategy) => - Effect.request(strategyRequest, strategy), - ).pipe(Effect.orElseSucceed(() => null)) - }), + yield* Effect.forEach(remaining, ({ chainID, address, event, signature }) => { + const strategyRequest = GetContractABIStrategy({ + address, + event, + signature, + chainID, + }) + + const allAvailableStrategies = [...(strategies[chainID] ?? []), ...strategies.default] + + return Effect.validateFirst(allAvailableStrategies, (strategy) => Effect.request(strategyRequest, strategy)).pipe( + Effect.orElseSucceed(() => null), + ) + }).pipe( Effect.flatMap((results) => Effect.forEach( results, diff --git a/packages/transaction-decoder/src/contract-meta-loader.ts b/packages/transaction-decoder/src/contract-meta-loader.ts index 41055e4d..ea9a3a92 100644 --- a/packages/transaction-decoder/src/contract-meta-loader.ts +++ b/packages/transaction-decoder/src/contract-meta-loader.ts @@ -1,4 +1,4 @@ -import { Context, Effect, RequestResolver, Request, ReadonlyArray, Either } from 'effect' +import { Context, Effect, RequestResolver, Request, Array, Either } from 'effect' import { ContractData } from './types.js' import { GetContractMetaStrategy } from './meta-strategy/request-model.js' import { Address } from 'viem' @@ -15,7 +15,7 @@ export interface ContractMetaStore[]> readonly set: (arg: Key, value: Value) => Effect.Effect readonly get: (arg: Key) => Effect.Effect - readonly getMany?: (arg: ReadonlyArray) => Effect.Effect, never> + readonly getMany?: (arg: Array) => Effect.Effect, never> } export const ContractMetaStore = Context.GenericTag('@3loop-decoder/ContractMetaStore') @@ -33,14 +33,14 @@ function makeKey(key: ContractMetaLoader) { } const ContractMetaLoaderRequestResolver = RequestResolver.makeBatched((requests: Array) => - Effect.gen(function* (_) { - const contractMetaStore = yield* _(ContractMetaStore) + Effect.gen(function* () { + const contractMetaStore = yield* ContractMetaStore const strategies = contractMetaStore.strategies - const groups = ReadonlyArray.groupBy(requests, makeKey) + const groups = Array.groupBy(requests, makeKey) const uniqueRequests = Object.values(groups).map((group) => group[0]) - const getMany = (requests: ReadonlyArray) => { + const getMany = (requests: Array) => { if (contractMetaStore.getMany != null) { return contractMetaStore.getMany(requests) } else { @@ -58,10 +58,9 @@ const ContractMetaLoaderRequestResolver = RequestResolver.makeBatched((requests: return result ? contractMetaStore.set({ chainID, address }, result) : Effect.succeed(null) } - const [remaining, results] = yield* _( - getMany(uniqueRequests), + const [remaining, results] = yield* getMany(uniqueRequests).pipe( Effect.map( - ReadonlyArray.partitionMap((resp, i) => { + Array.partitionMap((resp, i) => { return resp == null ? Either.left(uniqueRequests[i]) : Either.right([uniqueRequests[i], resp] as const) }), ), @@ -69,32 +68,29 @@ const ContractMetaLoaderRequestResolver = RequestResolver.makeBatched((requests: ) // Resolve ContractMeta from the store - yield* _( - Effect.forEach( - results, - ([request, result]) => { - const group = groups[makeKey(request)] - return Effect.forEach(group, (req) => Request.succeed(req, result), { discard: true }) - }, - { - discard: true, - }, - ), + yield* Effect.forEach( + results, + ([request, result]) => { + const group = groups[makeKey(request)] + return Effect.forEach(group, (req) => Request.succeed(req, result), { discard: true }) + }, + { + discard: true, + }, ) // Resolve ContractMeta from the strategies - yield* _( - Effect.forEach(remaining, ({ chainID, address }) => { - const strategyRequest = GetContractMetaStrategy({ - address, - chainID, - }) - const allAvailableStrategies = [...(strategies[chainID] ?? []), ...strategies.default] - - return Effect.validateFirst(allAvailableStrategies, (strategy) => - Effect.request(strategyRequest, strategy), - ).pipe(Effect.orElseSucceed(() => null)) - }), + yield* Effect.forEach(remaining, ({ chainID, address }) => { + const strategyRequest = GetContractMetaStrategy({ + address, + chainID, + }) + const allAvailableStrategies = [...(strategies[chainID] ?? []), ...strategies.default] + + return Effect.validateFirst(allAvailableStrategies, (strategy) => Effect.request(strategyRequest, strategy)).pipe( + Effect.orElseSucceed(() => null), + ) + }).pipe( Effect.flatMap((results) => Effect.forEach( results, diff --git a/packages/transaction-decoder/src/decoding/log-decode.ts b/packages/transaction-decoder/src/decoding/log-decode.ts index 8cc9b90e..de457efd 100644 --- a/packages/transaction-decoder/src/decoding/log-decode.ts +++ b/packages/transaction-decoder/src/decoding/log-decode.ts @@ -7,50 +7,46 @@ import { getAndCacheContractMeta } from '../contract-meta-loader.js' import * as AbiDecoder from './abi-decode.js' const decodedLog = (transaction: GetTransactionReturnType, logItem: Log) => - Effect.gen(function* (_) { + Effect.gen(function* () { const chainID = Number(transaction.chainId) const address = logItem.address let abiAddress = address - const implementation = yield* _(getProxyStorageSlot({ address: abiAddress, chainID })) + const implementation = yield* getProxyStorageSlot({ address: abiAddress, chainID }) if (implementation) { - yield* _(Effect.logDebug(`Proxy implementation found for ${abiAddress} at ${implementation}`)) + yield* Effect.logDebug(`Proxy implementation found for ${abiAddress} at ${implementation}`) abiAddress = implementation } - const abiItem_ = yield* _( - getAndCacheAbi({ - address: abiAddress, - event: logItem.topics[0], - chainID, - }), - ) + const abiItem_ = yield* getAndCacheAbi({ + address: abiAddress, + event: logItem.topics[0], + chainID, + }) if (abiItem_ == null) { - return yield* _(Effect.fail(new AbiDecoder.MissingABIError(abiAddress, logItem.topics[0]!, chainID))) + return yield* Effect.fail(new AbiDecoder.MissingABIError(abiAddress, logItem.topics[0]!, chainID)) } const abiItem = JSON.parse(abiItem_) as Abi[] - const { eventName, args: args_ } = yield* _( - Effect.try({ - try: () => - decodeEventLog({ - abi: abiItem, - topics: logItem.topics, - data: logItem.data, - strict: false, - }), - catch: () => { - Effect.logWarning(`Could not decode log ${abiAddress} ${JSON.stringify(logItem)}`) - }, - }), - ) + const { eventName, args: args_ } = yield* Effect.try({ + try: () => + decodeEventLog({ + abi: abiItem, + topics: logItem.topics, + data: logItem.data, + strict: false, + }), + catch: () => { + Effect.logWarning(`Could not decode log ${abiAddress} ${JSON.stringify(logItem)}`) + }, + }) if (args_ == null || eventName == null) { - return yield* _(Effect.fail(new AbiDecoder.DecodeError(`Could not decode log ${abiAddress}`))) + return yield* Effect.fail(new AbiDecoder.DecodeError(`Could not decode log ${abiAddress}`)) } const args = args_ as any @@ -58,35 +54,31 @@ const decodedLog = (transaction: GetTransactionReturnType, logItem: Log) => const fragment = getAbiItem({ abi: abiItem, name: eventName }) if (fragment == null) { - return yield* _( - Effect.fail(new AbiDecoder.DecodeError(`Could not find fragment in ABI ${abiAddress} ${eventName}`)), - ) + return yield* Effect.fail(new AbiDecoder.DecodeError(`Could not find fragment in ABI ${abiAddress} ${eventName}`)) } - const decodedParams = yield* _( - Effect.try({ - try: () => { - if ('inputs' in fragment && fragment.inputs != null) { - return fragment.inputs.map((input, i) => { - if (input.name == null) return null - - const arg = Array.isArray(args) ? args[i] : args[input.name] - const value = Array.isArray(arg) ? arg.map((item) => item?.toString()) : arg.toString() - - return { - type: input.type, - name: input.name, - value, - } as DecodedLogEvent - }) - } - return [] - }, - catch: () => { - Effect.logError(`Could not decode log params ${JSON.stringify(logItem)}`) - }, - }), - ) + const decodedParams = yield* Effect.try({ + try: () => { + if ('inputs' in fragment && fragment.inputs != null) { + return fragment.inputs.map((input, i) => { + if (input.name == null) return null + + const arg = Array.isArray(args) ? args[i] : args[input.name] + const value = Array.isArray(arg) ? arg.map((item) => item?.toString()) : arg.toString() + + return { + type: input.type, + name: input.name, + value, + } as DecodedLogEvent + }) + } + return [] + }, + catch: () => { + Effect.logError(`Could not decode log params ${JSON.stringify(logItem)}`) + }, + }) const rawLog: RawDecodedLog = { events: decodedParams.filter((x) => x != null) as DecodedLogEvent[], @@ -96,22 +88,20 @@ const decodedLog = (transaction: GetTransactionReturnType, logItem: Log) => decoded: true, } - return yield* _(transformLog(transaction, rawLog)) + return yield* transformLog(transaction, rawLog) }) const transformLog = (transaction: GetTransactionReturnType, log: RawDecodedLog) => - Effect.gen(function* (_) { + 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 contractData = yield* getAndCacheContractMeta({ + address, + chainID: Number(transaction.chainId), + }) return { contractName: contractData?.contractName || null, @@ -130,15 +120,13 @@ const transformLog = (transaction: GetTransactionReturnType, log: RawDecodedLog) }) export const decodeLogs = ({ logs, transaction }: { logs: readonly Log[]; transaction: GetTransactionReturnType }) => - Effect.gen(function* (_) { + Effect.gen(function* () { const effects = logs.filter((log) => log.topics.length > 0).map((logItem) => decodedLog(transaction, logItem)) const eithers = effects.map((e) => Effect.either(e)) - return yield* _( - Effect.all(eithers, { - concurrency: 'inherit', - batching: 'inherit', - }), - ) + return yield* Effect.all(eithers, { + concurrency: 'inherit', + batching: 'inherit', + }) }) diff --git a/packages/transaction-decoder/src/decoding/proxies.ts b/packages/transaction-decoder/src/decoding/proxies.ts index 63b6ced8..6b1ae0c7 100644 --- a/packages/transaction-decoder/src/decoding/proxies.ts +++ b/packages/transaction-decoder/src/decoding/proxies.ts @@ -18,9 +18,9 @@ export interface GetStorageSlot extends Request.Request
('GetStorageSlot') export const GetStorageSlotResolver = RequestResolver.fromEffect((request: GetStorageSlot) => - Effect.gen(function* (_) { - const service = yield* _(PublicClient) - const { client: publicClient } = yield* _(service.getPublicClient(request.chainID)) + Effect.gen(function* () { + const service = yield* PublicClient + const { client: publicClient } = yield* service.getPublicClient(request.chainID) // NOTE: Should we make this recursive when we have a Proxy of a Proxy? const effects = storageSlots.map((slot) => Effect.tryPromise({ @@ -40,12 +40,11 @@ export const GetStorageSlotResolver = RequestResolver.fromEffect((request: GetSt }), ) - const res = yield* _( - Effect.all(effects, { - concurrency: 'inherit', - batching: 'inherit', - }), - ) + const res = yield* Effect.all(effects, { + concurrency: 'inherit', + batching: 'inherit', + }) + return res.find((x) => x != null) }), ).pipe(RequestResolver.contextFromEffect) diff --git a/packages/transaction-decoder/src/decoding/trace-decode.ts b/packages/transaction-decoder/src/decoding/trace-decode.ts index 1bb00761..5726f6d4 100644 --- a/packages/transaction-decoder/src/decoding/trace-decode.ts +++ b/packages/transaction-decoder/src/decoding/trace-decode.ts @@ -29,45 +29,41 @@ function replacer(key: unknown, value: unknown) { } const decodeTraceLog = (call: TraceLog, transaction: GetTransactionReturnType) => - Effect.gen(function* (_) { + Effect.gen(function* () { if ('to' in call.action && 'input' in call.action) { const { to, input, from } = call.action const chainID = Number(transaction.chainId) const signature = call.action.input.slice(0, 10) const contractAddress = to - const abi_ = yield* _( - getAndCacheAbi({ - address: contractAddress, - signature, - chainID, - }), - ) + const abi_ = yield* getAndCacheAbi({ + address: contractAddress, + signature, + chainID, + }) if (abi_ == null) { - return yield* _(Effect.fail(new MissingABIError(contractAddress, signature, chainID))) + return yield* Effect.fail(new MissingABIError(contractAddress, signature, chainID)) } const abi = JSON.parse(abi_) as Abi - return yield* _( - Effect.try({ - try: () => { - const method = decodeMethod(input as Hex, abi) - return { - ...method, - from, - to, - } as DecodeTraceResult - }, - catch: (e) => { - return new DecodeError(e) - }, - }), - ) + return yield* Effect.try({ + try: () => { + const method = decodeMethod(input as Hex, abi) + return { + ...method, + from, + to, + } as DecodeTraceResult + }, + catch: (e) => { + return new DecodeError(e) + }, + }) } - return yield* _(Effect.fail(new DecodeError(`Could not decode trace log ${JSON.stringify(call, replacer)}`))) + return yield* Effect.fail(new DecodeError(`Could not decode trace log ${JSON.stringify(call, replacer)}`)) }) export const decodeTransactionTrace = ({ @@ -77,7 +73,7 @@ export const decodeTransactionTrace = ({ trace: TraceLog[] transaction: GetTransactionReturnType }) => - Effect.gen(function* (_) { + Effect.gen(function* () { if (trace.length === 0) { return [] } @@ -92,12 +88,10 @@ export const decodeTransactionTrace = ({ const eithers = effects.map((e) => Effect.either(e)) - const result = yield* _( - Effect.all(eithers, { - concurrency: 'inherit', - batching: 'inherit', - }), - ) + const result = yield* Effect.all(eithers, { + concurrency: 'inherit', + batching: 'inherit', + }) return result }) diff --git a/packages/transaction-decoder/src/interpreters/index.ts b/packages/transaction-decoder/src/interpreters/index.ts index b64dbcd4..5799d0f5 100644 --- a/packages/transaction-decoder/src/interpreters/index.ts +++ b/packages/transaction-decoder/src/interpreters/index.ts @@ -47,8 +47,8 @@ async function newRuntime(module: QuickJSWASMModule, config: RuntimeConfig): Pro } const newContext = () => - Effect.gen(function* (_) { - const { runtime } = yield* _(QuickJSVM) + Effect.gen(function* () { + const { runtime } = yield* QuickJSVM const vm: QuickJSContext = runtime.newContext() // `console.log` @@ -69,10 +69,10 @@ const newContext = () => }) export const interpretTx = ({ decodedTx, interpreter }: { decodedTx: DecodedTx; interpreter: Interpreter }) => - Effect.gen(function* (_) { + Effect.gen(function* () { const input = JSON.stringify(decodedTx) const code = interpreter.schema - const vm = yield* _(newContext()) + const vm = yield* newContext() const result = vm.unwrapResult(vm.evalCode(code + '\n' + 'transformEvent(' + input + ')')) const ok = vm.dump(result) 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 31100ebf..886b703d 100644 --- a/packages/transaction-decoder/src/meta-strategy/erc20-rpc-strategy.ts +++ b/packages/transaction-decoder/src/meta-strategy/erc20-rpc-strategy.ts @@ -6,9 +6,9 @@ import { erc20Abi, getContract } from 'viem' export const ERC20RPCStrategyResolver = (publicClientLive: PublicClient) => RequestResolver.fromEffect(({ chainID, address }: RequestModel.GetContractMetaStrategy) => - Effect.gen(function* (_) { - const service = yield* _(PublicClient) - const { client } = yield* _(service.getPublicClient(chainID)) + Effect.gen(function* () { + const service = yield* PublicClient + const { client } = yield* service.getPublicClient(chainID) const inst = getContract({ abi: erc20Abi, @@ -18,29 +18,25 @@ export const ERC20RPCStrategyResolver = (publicClientLive: PublicClient) => const fail = new RequestModel.ResolveStrategyMetaError('ERC20RPCStrategy', address, chainID) - const decimals = yield* _( - Effect.tryPromise({ - try: () => inst.read.decimals(), - catch: () => fail, - }), - ) + const decimals = yield* Effect.tryPromise({ + try: () => inst.read.decimals(), + catch: () => fail, + }) if (decimals == null) { - return yield* _(Effect.fail(fail)) + return yield* Effect.fail(fail) } //NOTE: keep for now to support blur pools - const [symbol, name] = yield* _( - Effect.all( - [ - Effect.tryPromise({ try: () => inst.read.symbol().catch(() => ''), catch: () => fail }), - Effect.tryPromise({ try: () => inst.read.name().catch(() => ''), catch: () => fail }), - ], - { - concurrency: 'inherit', - batching: 'inherit', - }, - ), + const [symbol, name] = yield* Effect.all( + [ + Effect.tryPromise({ try: () => inst.read.symbol().catch(() => ''), catch: () => fail }), + Effect.tryPromise({ try: () => inst.read.name().catch(() => ''), catch: () => fail }), + ], + { + concurrency: 'inherit', + batching: 'inherit', + }, ) const meta: ContractData = { 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 6150c0b5..325c2dff 100644 --- a/packages/transaction-decoder/src/meta-strategy/nft-rpc-strategy.ts +++ b/packages/transaction-decoder/src/meta-strategy/nft-rpc-strategy.ts @@ -7,9 +7,9 @@ import { ERC1155InterfaceId, ERC721InterfaceId, erc165Abi } from './constants.js export const NFTRPCStrategyResolver = (publicClientLive: PublicClient) => RequestResolver.fromEffect(({ chainID, address }: RequestModel.GetContractMetaStrategy) => - Effect.gen(function* (_) { - const service = yield* _(PublicClient) - const { client } = yield* _(service.getPublicClient(chainID)) + Effect.gen(function* () { + const service = yield* PublicClient + const { client } = yield* service.getPublicClient(chainID) const inst = getContract({ abi: erc165Abi, @@ -19,26 +19,24 @@ export const NFTRPCStrategyResolver = (publicClientLive: PublicClient) => const fail = new RequestModel.ResolveStrategyMetaError('NFTRPCStrategy', address, chainID) - const [isERC721, isERC1155] = yield* _( - Effect.all( - [ - Effect.tryPromise({ - try: () => inst.read.supportsInterface([ERC721InterfaceId]), - catch: () => fail, - }), - Effect.tryPromise({ - try: () => inst.read.supportsInterface([ERC1155InterfaceId]), - catch: () => fail, - }), - ], - { - concurrency: 'inherit', - batching: 'inherit', - }, - ), + const [isERC721, isERC1155] = yield* Effect.all( + [ + Effect.tryPromise({ + try: () => inst.read.supportsInterface([ERC721InterfaceId]), + catch: () => fail, + }), + Effect.tryPromise({ + try: () => inst.read.supportsInterface([ERC1155InterfaceId]), + catch: () => fail, + }), + ], + { + concurrency: 'inherit', + batching: 'inherit', + }, ) - if (!isERC721 && !isERC1155) return yield* _(Effect.fail(fail)) + if (!isERC721 && !isERC1155) return yield* Effect.fail(fail) const erc721inst = getContract({ abi: erc721Abi, @@ -46,23 +44,21 @@ export const NFTRPCStrategyResolver = (publicClientLive: PublicClient) => client, }) - const [name, symbol] = yield* _( - Effect.all( - [ - Effect.tryPromise({ - try: () => erc721inst.read.name(), - catch: () => fail, - }), - Effect.tryPromise({ - try: () => erc721inst.read.symbol(), - catch: () => fail, - }), - ], - { - concurrency: 'inherit', - batching: 'inherit', - }, - ), + const [name, symbol] = yield* Effect.all( + [ + Effect.tryPromise({ + try: () => erc721inst.read.name(), + catch: () => fail, + }), + Effect.tryPromise({ + try: () => erc721inst.read.symbol(), + catch: () => fail, + }), + ], + { + concurrency: 'inherit', + batching: 'inherit', + }, ) const type: ContractType = isERC1155 ? 'ERC1155' : 'ERC721' diff --git a/packages/transaction-decoder/src/schema/trace.ts b/packages/transaction-decoder/src/schema/trace.ts index fee29c23..e86b12bc 100644 --- a/packages/transaction-decoder/src/schema/trace.ts +++ b/packages/transaction-decoder/src/schema/trace.ts @@ -1,88 +1,86 @@ import * as Schema from '@effect/schema/Schema' -const CallType = Schema.literal('call', 'delegatecall', 'callcode', 'staticcall') +const CallType = Schema.Literal('call', 'delegatecall', 'callcode', 'staticcall') -const Address = Schema.string // NOTE: Probably we can use a branded type +const Address = Schema.String // NOTE: Probably we can use a branded type -export const bigintFromString = Schema.transform( - Schema.string, - Schema.bigintFromSelf, - (s) => BigInt(s), - (b) => String(b), -) +export const bigintFromString = Schema.transform(Schema.String, Schema.BigIntFromSelf, { + decode: (value) => BigInt(value), + encode: (value) => value.toString(), +}) -const EthTraceActionCall = Schema.struct({ +const EthTraceActionCall = Schema.Struct({ callType: CallType, from: Address, to: Address, gas: bigintFromString, - input: Schema.string, + input: Schema.String, value: bigintFromString, }) -const EthTraceActionCreate = Schema.struct({ +const EthTraceActionCreate = Schema.Struct({ from: Address, gas: bigintFromString, value: bigintFromString, }) -const EthTraceActionSuicide = Schema.struct({ +const EthTraceActionSuicide = Schema.Struct({ address: Address, refundAddress: Address, balance: bigintFromString, }) -const EthTraceActionReward = Schema.struct({ +const EthTraceActionReward = Schema.Struct({ author: Address, - rewardType: Schema.literal('block', 'uncle', 'emptyStep', 'external'), + rewardType: Schema.Literal('block', 'uncle', 'emptyStep', 'external'), value: bigintFromString, }) -const EthTraceResult = Schema.struct({ +const EthTraceResult = Schema.Struct({ gasUsed: bigintFromString, - output: Schema.string, + output: Schema.String, }) -const EthTraceBase = Schema.struct({ +const EthTraceBase = Schema.Struct({ result: Schema.optional(EthTraceResult), - subtraces: Schema.number, - traceAddress: Schema.array(Schema.number), - error: Schema.optional(Schema.string), + subtraces: Schema.Number, + traceAddress: Schema.Array(Schema.Number), + error: Schema.optional(Schema.String), }) const CallTrace = Schema.extend( EthTraceBase, - Schema.struct({ + Schema.Struct({ action: EthTraceActionCall, - type: Schema.literal('call'), + type: Schema.Literal('call'), }), ) const CreateTrace = Schema.extend( EthTraceBase, - Schema.struct({ + Schema.Struct({ action: EthTraceActionCreate, - type: Schema.literal('create'), + type: Schema.Literal('create'), }), ) const RewardTrace = Schema.extend( EthTraceBase, - Schema.struct({ + Schema.Struct({ action: EthTraceActionReward, - type: Schema.literal('reward'), + type: Schema.Literal('reward'), }), ) const SuicideTrace = Schema.extend( EthTraceBase, - Schema.struct({ + Schema.Struct({ action: EthTraceActionSuicide, - type: Schema.literal('suicide'), + type: Schema.Literal('suicide'), }), ) -const DebugCallType = Schema.literal( +const DebugCallType = Schema.Literal( 'CALL', 'DELEGATECALL', 'CALLCODE', @@ -92,15 +90,15 @@ const DebugCallType = Schema.literal( 'REWARD', ) -export const EthDebugTraceBase = Schema.struct({ - gas: Schema.string, +export const EthDebugTraceBase = Schema.Struct({ + gas: Schema.String, to: Address, from: Address, - gasUsed: Schema.string, - input: Schema.string, + gasUsed: Schema.String, + input: Schema.String, type: DebugCallType, - value: Schema.optional(Schema.string), - output: Schema.string, + value: Schema.optional(Schema.String), + output: Schema.String, }) type DebugTraceLog = Schema.Schema.Type @@ -109,7 +107,7 @@ export type TraceLogTree = { calls?: Array } & DebugTraceLog -export const EthTrace = Schema.union(CallTrace, CreateTrace, RewardTrace, SuicideTrace) +export const EthTrace = Schema.Union(CallTrace, CreateTrace, RewardTrace, SuicideTrace) export type TraceLog = Schema.Schema.Type export type CallTraceLog = Schema.Schema.Type diff --git a/packages/transaction-decoder/src/transaction-decoder.ts b/packages/transaction-decoder/src/transaction-decoder.ts index 31a0498c..eca6940e 100644 --- a/packages/transaction-decoder/src/transaction-decoder.ts +++ b/packages/transaction-decoder/src/transaction-decoder.ts @@ -41,13 +41,13 @@ export class FetchTransactionError { } export const decodeMethod = ({ transaction }: { transaction: GetTransactionReturnType }) => - Effect.gen(function* (_) { + Effect.gen(function* () { if (transaction.to == null) { - return yield* _(Effect.die(new UnsupportedEvent('Contract creation'))) + return yield* Effect.die(new UnsupportedEvent('Contract creation')) } if (!('input' in transaction)) { - return yield* _(Effect.die(new UnsupportedEvent('Unsupported transaction'))) + return yield* Effect.die(new UnsupportedEvent('Unsupported transaction')) } const data = transaction.input @@ -57,38 +57,34 @@ export const decodeMethod = ({ transaction }: { transaction: GetTransactionRetur let abiAddress = transaction.to //if contract is a proxy, get the implementation address - const implementation = yield* _(getProxyStorageSlot({ address: abiAddress, chainID })) + const implementation = yield* getProxyStorageSlot({ address: abiAddress, chainID }) if (implementation) { abiAddress = implementation } - const abi_ = yield* _( - getAndCacheAbi({ - address: abiAddress, - signature, - chainID, - }), - ) + const abi_ = yield* getAndCacheAbi({ + address: abiAddress, + signature, + chainID, + }) if (!abi_) { - return yield* _(Effect.fail(new AbiDecoder.MissingABIError(abiAddress, signature, chainID))) + return yield* Effect.fail(new AbiDecoder.MissingABIError(abiAddress, signature, chainID)) } const abi = JSON.parse(abi_) as Abi // TODO: Pass the error message, so we can easier debug - const decoded = yield* _( - Effect.try({ - try: () => AbiDecoder.decodeMethod(data, abi), - catch: (e) => { - return new AbiDecoder.DecodeError(e) - }, - }), - ) + const decoded = yield* Effect.try({ + try: () => AbiDecoder.decodeMethod(data, abi), + catch: (e) => { + return new AbiDecoder.DecodeError(e) + }, + }) if (decoded == null) { - return yield* _(Effect.fail(new AbiDecoder.DecodeError(`Failed to decode method: ${transaction.input}`))) + return yield* Effect.fail(new AbiDecoder.DecodeError(`Failed to decode method: ${transaction.input}`)) } return decoded @@ -101,23 +97,19 @@ export const decodeLogs = ({ transaction: GetTransactionReturnType receipt: TransactionReceipt }) => - Effect.gen(function* (_) { - return yield* _( - LogDecoder.decodeLogs({ - logs: receipt.logs, - transaction, - }), - ) + Effect.gen(function* () { + return yield* LogDecoder.decodeLogs({ + logs: receipt.logs, + transaction, + }) }) export const decodeTrace = ({ trace, transaction }: { trace: TraceLog[]; transaction: GetTransactionReturnType }) => - Effect.gen(function* (_) { - return yield* _( - TraceDecoder.decodeTransactionTrace({ - trace, - transaction, - }), - ) + Effect.gen(function* () { + return yield* TraceDecoder.decodeTransactionTrace({ + trace, + transaction, + }) }) const collectAllAddresses = ({ @@ -159,19 +151,17 @@ export const decodeTransaction = ({ trace: TraceLog[] timestamp: number }) => - Effect.gen(function* (_) { - const { decodedData, decodedTrace, decodedLogs } = yield* _( - Effect.all( - { - decodedData: decodeMethod({ transaction }), - decodedTrace: decodeTrace({ trace, transaction }), - decodedLogs: decodeLogs({ receipt, transaction }), - }, - { - batching: true, - concurrency: 'unbounded', - }, - ), + Effect.gen(function* () { + const { decodedData, decodedTrace, decodedLogs } = yield* Effect.all( + { + decodedData: decodeMethod({ transaction }), + decodedTrace: decodeTrace({ trace, transaction }), + decodedLogs: decodeLogs({ receipt, transaction }), + }, + { + batching: true, + concurrency: 'unbounded', + }, ) const decodedLogsRight = decodedLogs.filter(Either.isRight).map((r) => r.right) @@ -179,19 +169,17 @@ export const decodeTransaction = ({ const logsErrors = decodedLogs.filter(Either.isLeft).map((r) => r.left) if (logsErrors.length > 0) { - yield* _(Effect.logError(`Logs decode errors: ${JSON.stringify(logsErrors)}`)) + yield* Effect.logError(`Logs decode errors: ${JSON.stringify(logsErrors)}`) } const traceErrors = decodedTrace.filter(Either.isLeft).map((r) => r.left) if (traceErrors.length > 0) { - yield* _(Effect.logError(`Trace decode errors: ${JSON.stringify(traceErrors)}`)) + yield* Effect.logError(`Trace decode errors: ${JSON.stringify(traceErrors)}`) } - const interpreterMap = yield* _( - getAndCacheContractMeta({ - address: receipt.to!, - chainID: Number(transaction.chainId), - }), - ) + const interpreterMap = yield* getAndCacheContractMeta({ + address: receipt.to!, + chainID: Number(transaction.chainId), + }) const interactions: Interaction[] = TraceDecoder.augmentTraceLogs(transaction, decodedLogsRight, trace) @@ -232,38 +220,34 @@ export const decodeTransaction = ({ }) export const decodeTransactionByHash = (hash: Hash, chainID: number) => - Effect.gen(function* (_) { - const { transaction, receipt, trace } = yield* _( - Effect.all( - { - transaction: getTransaction(hash, chainID), - receipt: getTransactionReceipt(hash, chainID), - trace: getTrace(hash, chainID), - }, - { - concurrency: 'unbounded', - batching: true, - }, - ), + Effect.gen(function* () { + const { transaction, receipt, trace } = yield* Effect.all( + { + transaction: getTransaction(hash, chainID), + receipt: getTransactionReceipt(hash, chainID), + trace: getTrace(hash, chainID), + }, + { + concurrency: 'unbounded', + batching: true, + }, ) if (!receipt || !transaction || !trace) { - return yield* _(Effect.fail(new FetchTransactionError({ hash, chainID }))) + return yield* Effect.fail(new FetchTransactionError({ hash, chainID })) } - const timestamp = yield* _(getBlockTimestamp(receipt.blockNumber, chainID)) + const timestamp = yield* getBlockTimestamp(receipt.blockNumber, chainID) if (!receipt.to) { - return yield* _(Effect.fail(new UnsupportedEvent('Contract creation'))) + return yield* Effect.fail(new UnsupportedEvent('Contract creation')) } else if (transaction.input === '0x') { - return yield* _(Effect.sync(() => transferDecode({ transaction, receipt }))) + return yield* Effect.sync(() => transferDecode({ transaction, receipt })) } - return yield* _( - decodeTransaction({ - transaction, - receipt, - trace, - timestamp: Number(timestamp), - }), - ) + return yield* decodeTransaction({ + transaction, + receipt, + trace, + timestamp: Number(timestamp), + }) }) diff --git a/packages/transaction-decoder/src/transaction-loader.ts b/packages/transaction-decoder/src/transaction-loader.ts index d4369b8e..28d7aae7 100644 --- a/packages/transaction-decoder/src/transaction-loader.ts +++ b/packages/transaction-decoder/src/transaction-loader.ts @@ -8,76 +8,66 @@ import { type Hash } from 'viem' import { ParseError } from '@effect/schema/ParseResult' export const getTransaction = (hash: Hash, chainID: number) => - Effect.gen(function* (_) { - const service = yield* _(PublicClient) - const { client } = yield* _(service.getPublicClient(chainID)) - return yield* _( - Effect.tryPromise({ - try: () => client.getTransaction({ hash }), - catch: () => new RPCFetchError('Get transaction'), - }), - ) + Effect.gen(function* () { + const service = yield* PublicClient + const { client } = yield* service.getPublicClient(chainID) + return yield* Effect.tryPromise({ + try: () => client.getTransaction({ hash }), + catch: () => new RPCFetchError('Get transaction'), + }) }) export const getTransactionReceipt = (hash: Hash, chainID: number) => - Effect.gen(function* (_) { - const service = yield* _(PublicClient) - const { client } = yield* _(service.getPublicClient(chainID)) - return yield* _( - Effect.tryPromise({ - try: () => client.getTransactionReceipt({ hash }), - catch: () => new RPCFetchError('Get transaction receipt'), - }), - ) + Effect.gen(function* () { + const service = yield* PublicClient + const { client } = yield* service.getPublicClient(chainID) + return yield* Effect.tryPromise({ + try: () => client.getTransactionReceipt({ hash }), + catch: () => new RPCFetchError('Get transaction receipt'), + }) }) export const getTrace = (hash: Hash, chainID: number) => - Effect.gen(function* (_) { - const service = yield* _(PublicClient) - const { client, config } = yield* _(service.getPublicClient(chainID)) + Effect.gen(function* () { + const service = yield* PublicClient + const { client, config } = yield* service.getPublicClient(chainID) const traceAPISupport = config?.supportTraceAPI ?? true if (traceAPISupport) { - const trace = yield* _( - Effect.tryPromise({ - try: async () => { - const trace = await (client.request({ - method: 'trace_transaction' as any, - params: [hash], - }) as Promise) - if (trace == null) return [] - return trace - }, - catch: () => new RPCFetchError('Get trace'), - }), - ) + const trace = yield* Effect.tryPromise({ + try: async () => { + const trace = await (client.request({ + method: 'trace_transaction' as any, + params: [hash], + }) as Promise) + if (trace == null) return [] + return trace + }, + catch: () => new RPCFetchError('Get trace'), + }) const effects: Effect.Effect[] = trace.map((log: string) => { return Schema.decodeUnknown(EthTrace)(log) }) - const results = yield* _( - Effect.all(effects, { - concurrency: 'inherit', - batching: 'inherit', - }), - ) + const results = yield* Effect.all(effects, { + concurrency: 'inherit', + batching: 'inherit', + }) return results } else { - const trace = yield* _( - Effect.tryPromise({ - try: async () => { - const trace = await client.request({ - method: 'debug_traceTransaction', - params: [hash, { tracer: 'callTracer' }], - } as any) - if (trace == null) return [] - return trace - }, - catch: (e) => new RPCFetchError(e), - }), - ) + const trace = yield* Effect.tryPromise({ + try: async () => { + const trace = await client.request({ + method: 'debug_traceTransaction', + params: [hash, { tracer: 'callTracer' }], + } as any) + if (trace == null) return [] + return trace + }, + catch: (e) => new RPCFetchError(e), + }) const transformedTrace = transformTraceTree(trace as unknown as TraceLogTree) @@ -86,15 +76,13 @@ export const getTrace = (hash: Hash, chainID: number) => }) export const getBlockTimestamp = (blockNumber: bigint, chainID: number) => - Effect.gen(function* (_) { - const service = yield* _(PublicClient) - const { client } = yield* _(service.getPublicClient(chainID)) - const block = yield* _( - Effect.tryPromise({ - try: () => client.getBlock({ blockNumber }), - catch: () => new RPCFetchError('Block number'), - }), - ) + Effect.gen(function* () { + const service = yield* PublicClient + const { client } = yield* service.getPublicClient(chainID) + const block = yield* Effect.tryPromise({ + try: () => client.getBlock({ blockNumber }), + catch: () => new RPCFetchError('Block number'), + }) if (block) { return block.timestamp diff --git a/packages/transaction-decoder/src/vanilla.ts b/packages/transaction-decoder/src/vanilla.ts index 2e4455a4..af12f0fb 100644 --- a/packages/transaction-decoder/src/vanilla.ts +++ b/packages/transaction-decoder/src/vanilla.ts @@ -75,8 +75,8 @@ export class TransactionDecoder { } decodeTransaction({ chainID, hash }: { chainID: number; hash: string }) { - const program = Effect.gen(function* (_) { - return yield* _(decodeTransactionByHash(hash as Hex, chainID)) + const program = Effect.gen(function* () { + return yield* decodeTransactionByHash(hash as Hex, chainID) }).pipe(Logger.withMinimumLogLevel(this.logging ? LogLevel.Debug : LogLevel.Error)) const runnable = Effect.provide(program, this.context) diff --git a/packages/transaction-decoder/test/constants.ts b/packages/transaction-decoder/test/constants.ts index f8709ace..da3226d7 100644 --- a/packages/transaction-decoder/test/constants.ts +++ b/packages/transaction-decoder/test/constants.ts @@ -1,6 +1,6 @@ import { Hex } from 'viem' -type TXS = { +type TXS = readonly { hash: Hex chainID: number }[] diff --git a/packages/transaction-decoder/test/mocks/abi-loader-mock.ts b/packages/transaction-decoder/test/mocks/abi-loader-mock.ts index b90e7432..fcda3f5d 100644 --- a/packages/transaction-decoder/test/mocks/abi-loader-mock.ts +++ b/packages/transaction-decoder/test/mocks/abi-loader-mock.ts @@ -13,50 +13,41 @@ export const MockedAbiStoreLive = Layer.succeed( ], }, set: ({ address = {}, func = {}, event = {} }) => - Effect.gen(function* (_) { + Effect.gen(function* () { const addressMatches = Object.entries(address) const sigMatches = Object.entries(func).concat(Object.entries(event)) // Cache all addresses - yield* _( - Effect.all( - addressMatches.map(([key, value]) => - Effect.sync(() => fs.writeFileSync(`./test/mocks/abi/${key.toLowerCase()}.json`, value)), - ), + yield* Effect.all( + addressMatches.map(([key, value]) => + Effect.sync(() => fs.writeFileSync(`./test/mocks/abi/${key.toLowerCase()}.json`, value)), ), ) // Cache all signatures - yield* _( - Effect.all( - sigMatches.map(([key, value]) => - Effect.sync(() => fs.writeFileSync(`./test/mocks/abi/${key.toLowerCase()}.json`, value)), - ), + yield* Effect.all( + sigMatches.map(([key, value]) => + Effect.sync(() => fs.writeFileSync(`./test/mocks/abi/${key.toLowerCase()}.json`, value)), ), ) }), get: ({ address, signature, event }) => - Effect.gen(function* (_) { - const addressExists = yield* _( - Effect.sync(() => fs.existsSync(`./test/mocks/abi/${address.toLowerCase()}.json`)), - ) + Effect.gen(function* () { + const addressExists = yield* Effect.sync(() => fs.existsSync(`./test/mocks/abi/${address.toLowerCase()}.json`)) if (addressExists) { - return yield* _( - Effect.sync(() => fs.readFileSync(`./test/mocks/abi/${address.toLowerCase()}.json`)?.toString()), - ) + return yield* Effect.sync(() => fs.readFileSync(`./test/mocks/abi/${address.toLowerCase()}.json`)?.toString()) } const sig = signature ?? event if (sig != null) { - const signatureExists = yield* _( - Effect.sync(() => fs.existsSync(`./test/mocks/abi/${sig.toLowerCase()}.json`)), - ) + const signatureExists = yield* Effect.sync(() => fs.existsSync(`./test/mocks/abi/${sig.toLowerCase()}.json`)) if (signatureExists) { - const signatureAbi = yield* _( - Effect.sync(() => fs.readFileSync(`./test/mocks/abi/${sig.toLowerCase()}.json`)?.toString()), + const signatureAbi = yield* Effect.sync( + () => fs.readFileSync(`./test/mocks/abi/${sig.toLowerCase()}.json`)?.toString(), ) + return `[${signatureAbi}]` } } diff --git a/packages/transaction-decoder/test/transaction-decoder.test.ts b/packages/transaction-decoder/test/transaction-decoder.test.ts index c32614af..4df70c98 100644 --- a/packages/transaction-decoder/test/transaction-decoder.test.ts +++ b/packages/transaction-decoder/test/transaction-decoder.test.ts @@ -9,8 +9,8 @@ import { MockedMetaStoreLive } from './mocks/meta-loader-mock.js' describe('Transaction Decoder', () => { test.each(TEST_TRANSACTIONS)('Resolve and decode transaction', async ({ hash, chainID }) => { - const program = Effect.gen(function* (_) { - return yield* _(decodeTransactionByHash(hash, chainID)) + const program = Effect.gen(function* () { + return yield* decodeTransactionByHash(hash, chainID) }) const LoadersLayer = Layer.provideMerge(MockedAbiStoreLive, MockedMetaStoreLive) diff --git a/packages/transaction-decoder/tsconfig.build.json b/packages/transaction-decoder/tsconfig.build.json index ece2a4f9..db0b2be0 100644 --- a/packages/transaction-decoder/tsconfig.build.json +++ b/packages/transaction-decoder/tsconfig.build.json @@ -1,19 +1,19 @@ { - "extends": "tsconfig/universal-esm.json", - "compilerOptions": { - "outDir": "dist", - "allowJs": true, - "baseUrl": ".", - "isolatedModules": true, - "stripInternal": true, - "noErrorTruncation": true, - "target": "es2016", - "module": "commonjs", - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "skipLibCheck": true, - "paths": { "@/*": ["src/*"] } - }, - "include": ["src"], - "exclude": ["dist", "example", "node_modules"] + "extends": "tsconfig/universal-esm.json", + "compilerOptions": { + "outDir": "dist", + "allowJs": true, + "baseUrl": ".", + "isolatedModules": true, + "stripInternal": true, + "noErrorTruncation": true, + "target": "es2016", + "module": "commonjs", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "paths": { "@/*": ["src/*"] } + }, + "include": ["src"], + "exclude": ["dist", "example", "node_modules"] } diff --git a/packages/transaction-decoder/tsconfig.json b/packages/transaction-decoder/tsconfig.json index b97756ae..59a39788 100644 --- a/packages/transaction-decoder/tsconfig.json +++ b/packages/transaction-decoder/tsconfig.json @@ -1,4 +1,4 @@ { - "extends": "./tsconfig.build.json", - "include": ["src/**/*", "test/**/*", "examples/**/*", "tsup.config.ts"] + "extends": "./tsconfig.build.json", + "include": ["src/**/*", "test/**/*", "examples/**/*", "tsup.config.ts"] } diff --git a/packages/tsconfig/base.json b/packages/tsconfig/base.json index ce11bb5c..837bec34 100644 --- a/packages/tsconfig/base.json +++ b/packages/tsconfig/base.json @@ -17,7 +17,5 @@ "strict": true, "strictNullChecks": true }, - "exclude": [ - "node_modules" - ] -} \ No newline at end of file + "exclude": ["node_modules"] +} diff --git a/packages/tsconfig/universal-esm.json b/packages/tsconfig/universal-esm.json index cc797207..ed537b88 100644 --- a/packages/tsconfig/universal-esm.json +++ b/packages/tsconfig/universal-esm.json @@ -4,12 +4,9 @@ "extends": "./base.json", "compilerOptions": { "jsx": "react-jsx", - "lib": [ - "dom", - "esnext" - ], + "lib": ["dom", "esnext"], "module": "ESNext", "moduleResolution": "nodenext", "target": "ES2021" } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d8689c88..3a2133ba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -115,8 +115,8 @@ importers: specifier: ^2.0.0 version: 2.0.0 effect: - specifier: ^2.4.18 - version: 2.4.18 + specifier: ^3.0.5 + version: 3.0.5 eslint: specifier: ^8.47.0 version: 8.47.0 @@ -198,8 +198,8 @@ importers: version: 0.6.7 devDependencies: '@effect/schema': - specifier: ^0.64.20 - version: 0.64.20(effect@2.4.18)(fast-check@3.15.0) + specifier: ^0.66.8 + version: 0.66.8(effect@3.0.5)(fast-check@3.15.0) '@total-typescript/ts-reset': specifier: ^0.5.1 version: 0.5.1 @@ -219,8 +219,8 @@ importers: specifier: 0.34.2 version: 0.34.2(vitest@0.34.2) effect: - specifier: 2.4.18 - version: 2.4.18 + specifier: 3.0.5 + version: 3.0.5 eslint: specifier: ^8.49.0 version: 8.49.0 @@ -1131,13 +1131,13 @@ packages: engines: {node: '>=10'} dev: false - /@effect/schema@0.64.20(effect@2.4.18)(fast-check@3.15.0): - resolution: {integrity: sha512-oSphgsP9j7yHy31RxTQ0txWq76u1F/H1Hqw+GVcpjnK4n/fuZo7MNYU/qpG+TLJWj4+yUku5I3Rr0Vr7gVoMeQ==} + /@effect/schema@0.66.8(effect@3.0.5)(fast-check@3.15.0): + resolution: {integrity: sha512-TJ2wGt1O4WqNrZtz9bRd+Mo8sitS33Ifj1u9TqXAg8FlWn7OA+MtULQmktdDVAlWw18QmamKX7grKeatL+0iCg==} peerDependencies: - effect: ^2.4.18 + effect: ^3.0.5 fast-check: ^3.13.2 dependencies: - effect: 2.4.18 + effect: 3.0.5 fast-check: 3.15.0 dev: true @@ -4919,8 +4919,8 @@ packages: /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - /effect@2.4.18: - resolution: {integrity: sha512-8nNzkhSzFLhG9YxowXKawlk4+b3zwwOsbZwaaYhy1OjZGcEgcE6i0ywMFieXNSfOtsQ8swV1+5hvF+VBEtwwqg==} + /effect@3.0.5: + resolution: {integrity: sha512-cXAKns46iWEQkZHgRU+ehgfV2Au34HopVIBkWwTbD+iRR50zMOHNEHEjXGnyJPtWyYlLBQtB9AZXFoc72ChwPA==} /electron-to-chromium@1.4.498: resolution: {integrity: sha512-4LODxAzKGVy7CJyhhN5mebwe7U2L29P+0G+HUriHnabm0d7LSff8Yn7t+Wq+2/9ze2Fu1dhX7mww090xfv7qXQ==} diff --git a/turbo.json b/turbo.json index f034d824..fef99fdf 100644 --- a/turbo.json +++ b/turbo.json @@ -1,17 +1,10 @@ { "$schema": "https://turbo.build/schema.json", - "globalDependencies": [ - "**/.env.*local" - ], + "globalDependencies": ["**/.env.*local"], "pipeline": { "build": { - "dependsOn": [ - "^build" - ], - "outputs": [ - ".next/**", - "!.next/cache/**" - ] + "dependsOn": ["^build"], + "outputs": [".next/**", "!.next/cache/**"] }, "test": { "outputs": [] From 813b5a96350fdb463a0cf1637e4450cfe858e29b Mon Sep 17 00:00:00 2001 From: Gheorghe Pinzaru Date: Fri, 26 Apr 2024 09:55:58 +0200 Subject: [PATCH 2/2] changeset --- .changeset/tall-bulldogs-jog.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tall-bulldogs-jog.md diff --git a/.changeset/tall-bulldogs-jog.md b/.changeset/tall-bulldogs-jog.md new file mode 100644 index 00000000..f13c4be2 --- /dev/null +++ b/.changeset/tall-bulldogs-jog.md @@ -0,0 +1,5 @@ +--- +'@3loop/transaction-decoder': minor +--- + +Bump effect version to stable 3