diff --git a/apps/docs/src/content/components/abi-store-interface.md b/apps/docs/src/content/components/abi-store-interface.md new file mode 100644 index 0000000..7af32b9 --- /dev/null +++ b/apps/docs/src/content/components/abi-store-interface.md @@ -0,0 +1,7 @@ +```ts title="vanilla.ts" +export interface VanillaAbiStore { + strategies?: readonly ContractAbiResolverStrategy[] + get: (key: AbiParams) => Promise + set: (key: AbiParams, val: ContractAbiResult) => Promise +} +``` diff --git a/apps/docs/src/content/components/meta-store-interface.md b/apps/docs/src/content/components/meta-store-interface.md new file mode 100644 index 0000000..b3b4e90 --- /dev/null +++ b/apps/docs/src/content/components/meta-store-interface.md @@ -0,0 +1,7 @@ +```ts title="vanilla.ts" +export interface VanillaContractMetaStore { + strategies?: readonly VanillaContractMetaStategy[] + get: (key: ContractMetaParams) => Promise + set: (key: ContractMetaParams, val: ContractMetaResult) => Promise +} +``` diff --git a/apps/docs/src/content/docs/guides/decode-transaction.mdx b/apps/docs/src/content/docs/guides/decode-transaction.mdx index bf59e59..98ec9d6 100644 --- a/apps/docs/src/content/docs/guides/decode-transaction.mdx +++ b/apps/docs/src/content/docs/guides/decode-transaction.mdx @@ -1,5 +1,5 @@ --- -title: How to decode an Ethereum Transaction +title: Decode an Ethereum Transaction (detailed) description: On this page you will provide a step-by-step guide on how to decode and interpret an Ethereum transaction using Loop Decoder. sidebar: order: 1 @@ -8,64 +8,87 @@ sidebar: import { Content as MemoryAbiLoader } from '../../components/memory-abi-loader.md' import { Content as MemoryContractLoader } from '../../components/memory-contract-loader.md' import { Content as RpcProvider } from '../../components/rpc-provider.md' +import { Content as AbiStoreInterface } from '../../components/abi-store-interface.md' +import { Content as MetaStoreInterface } from '../../components/meta-store-interface.md' -In this guide, we will go through the process of decoding an Ethereum transaction using Loop Decoder. For the simplicity of the example, we assume that that contract ABIs involved in the transaction are verified on Etherscan. +This guide explains how to decode Ethereum transactions using Loop Decoder. We'll cover: -We recomend to copy all snipepts to a typescript project and run it at the end of this guide, or or you can copy the whole example from this file: [Full Example Code](https://stackblitz.com/~/github.com/3loop/loop-decoder/tree/main/sandbox/quick-start). Do not forget to replace the placeholder `YourApiKeyToken` with your own free Etherscan API key. +- Setting up data loading strategies for ABIs and contract metadata +- Configuring data stores for Contract ABIs and metadata +- Decoding transactions -## Prerequisites +## Installation -### Create a new project - -Optionally, you can create a new project to follow along, or skip to [Required packages](#required-packages). - -1. Install Bun: - First, make sure you have Bun installed on your system. If you haven't installed it yet, you can do so using npm: - -```bash -npm install -g bun -``` - -1. Generate and initialize a new project: +Generate and initialize a new project: ```bash mkdir example-decode && cd example-decode bun init ``` -### Required packages - -For this guide, you will need to have the following packages installed: +Install required packages: ```bash bun install @3loop/transaction-decoder viem ``` -## Data Sources +## Setup Loop Decoder + +Loop Decoder requires three components: -Loop Decoder requires some data sources to be able to decode transactions. We will need an RPC provider, a data source to fetch Contracts ABIs and a data source to fetch contract meta-information, such as token name, decimals, symbol, etc. +1. RPC Provider: Fetches raw transaction data +2. ABI Data Store: Retrieves and caches contract ABIs +3. Contract Metadata Store: Retrieves and caches contract metadata (e.g., token name, symbol, decimals) -### RPC Provider +### 1. RPC Provider -We will start by creating a function which will return an object with PublicClient based on the chain ID. For the sake of this example, we will only support mainnet. +Create a `getPublicClient` function that accepts a chain ID and returns an object with [Viem](https://viem.sh/) `PublicClient`. -### ABI loader +For detailed configuration options and trace API settings, see the [RPC Provider](/reference/rpc-provider/) documentation. + +### 2. ABI Data Store + +The ABI Data Store handles: + +- Fetching ABIs using predefined strategies (e.g., Etherscan, 4byte). Some strategies like Etherscan require an API key. See the full list of strategies in [Data Loaders (ABI Strategies)](/reference/data-loaders/#abi-strategies) +- Caching fetched ABIs + +To create a custom ABI Data Store, implement the `VanillaAbiStore` interface: -To avoid making unecessary calls to third-party APIs, Loop Decoder uses an API that allows cache. For this example, we will keep it simple and use an in-memory cache. We will also use some strategies to download contract ABIs from Etherscan and 4byte.directory. You can find more information about the strategies in the [Strategies](/reference/data-loaders/) reference. + -Create a cache for contract ABI and add your free Etherscan API key instead of the placeholder `YourApiKeyToken`: +#### Example: an ABI data store with Etherscan and 4byte strategies and in-memory cache -### Contract Metadata loader +:::tip +You can use persistent data stores, like a database or file system, by redefining the `get` and `set` methods from the example above. +::: -Create an in-memory cache for contract meta-information. Using `ERC20RPCStrategyResolver` we will automatically retrieve token meta information from the contract such as token name, decimals, symbol, etc. +### 3. Contract Metadata Store + +The Contract Metadata Store handles: + +- Fetching contract metadata using predefined strategies (e.g., ERC20, NFT). See the full list of strategies in [Data Loaders (Contract Metadata)](/reference/data-loaders/#contract-metadata) +- Caching fetched contract metadata + +To create a custom Contract Metadata Store, implement the `VanillaContractMetaStore` interface: + + + +#### Example: a Contract Metadata Store with ERC20 strategy and in-memory cache -Finally, you can create a new instance of the LoopDecoder class: +:::tip +You can use persistent data stores, like a database or file system, by redefining the `get` and `set` methods from the example above. +::: + +### 4. Initializing Loop Decoder + +Finally, you can create a new instance of the TransactionDecoder class: ```ts import { TransactionDecoder } from '@3loop/transaction-decoder' @@ -77,9 +100,9 @@ const decoder = new TransactionDecoder({ }) ``` -## Decoding a Transaction +## Example: Decoding a Transaction -Now that we have all the necessary components, we can start decoding a transaction. For this example, we will use the following transaction: +Once the `TransactionDecoder` is set up, you can use it to decode a transaction by calling the `decodeTransaction` method: ```ts async function main() { @@ -98,12 +121,6 @@ async function main() { main() ``` -Run the script: - -```bash -bun run index.ts -``` - Expected output: ```json diff --git a/apps/docs/src/content/docs/reference/data-loaders.md b/apps/docs/src/content/docs/reference/data-loaders.md index 63789bd..4fbd876 100644 --- a/apps/docs/src/content/docs/reference/data-loaders.md +++ b/apps/docs/src/content/docs/reference/data-loaders.md @@ -25,8 +25,8 @@ Loop Decoder provides some strategies out of the box: ABI strategies will receive the contract address, and event or function signature as input and would return the ABI as a stringified JSON. Loop Decoder provides some strategies out of the box: -- `EtherscanStrategyResolver` - resolves the ABI from Etherscan -- `EtherscanV2StrategyResolver` - resolves the ABI from Etherscan v2 +- `EtherscanStrategyResolver` - resolves the ABI from Etherscan, requires an API key to work properly +- `EtherscanV2StrategyResolver` - resolves the ABI from Etherscan v2, requires an API key to work properly - `SourcifyStrategyResolver` - resolves the ABI from Sourcify - `FourByteStrategyResolver` - resolves the ABI from 4byte.directory - `OpenchainStrategyResolver` - resolves the ABI from Openchain diff --git a/apps/docs/src/content/docs/reference/proxy-resolution.md b/apps/docs/src/content/docs/reference/proxy-resolution.md index 058a904..ceb33eb 100644 --- a/apps/docs/src/content/docs/reference/proxy-resolution.md +++ b/apps/docs/src/content/docs/reference/proxy-resolution.md @@ -5,64 +5,48 @@ sidebar: order: 3 --- -Proxies are smart contracts that delegate calls to another contract, typically referred to as the implementation or logic contract. They allow the implementation to be updated without changing the proxy's address, ensuring that users continue interacting with the same address. +Proxy contracts are smart contracts that forward their calls to another contract - known as the implementation or logic contract. This pattern enables contract upgrades without changing the proxy's address, ensuring users can continue interacting with the same address. -## Types of Proxies +Loop Decoder automatically detects and handles proxy contracts when resolving ABIs and decoding function parameters. -#### 1. Standard Proxy +## Supported Proxy Types -Standard proxies are primarily used for upgradeable contracts, allowing the implementation contract to be updated without changing the address of the proxy contract. They have a higher deployment cost. +| Proxy Type | Description | +| ---------- | -------------------------------------------------- | +| EIP-1967 | The standard transparent proxy implementation. | +| EIP-1167 | Minimal proxy implementation. | +| Safe | Gnosis Safe multi-signature wallet implementation. | +| Zeppelin | OpenZeppelin's proxy implementation. | -Standard proxy contracts often use standardized storage slots to store the address of the implementation contract. The most common slot is the EIP-1967 standard, but there are also additional slots used by frameworks like OpenZeppelin or blockchain-specific implementations. - -#### 2. Minimal Proxy - -Minimal Proxies, also known as EIP-1167 proxies, are designed for efficient deployment of multiple instances of the same contract logic. They are not upgradeable and have a fixed implementation address. - -#### 3. UUPS (Universal Upgradeable Proxy Standard) - -UUPS proxies are an evolution of the standard proxy pattern. They move the upgrade functionality to the implementation contract, reducing the proxy contract's complexity and gas costs. - -#### 4. Beacon Proxy +## How Loop Decoder resolves proxy contracts -Beacon Proxies introduce an additional contract called a beacon, which stores the implementation address. Multiple proxy contracts can point to the same beacon, allowing for simultaneous upgrades of multiple proxies by updating the beacon's implementation address. +For each smart contract, Loop Decoder resolves the implementation address using standard JSON-RPC API calls and the following methods. -## Proxy Detection Methods +1. Bytecode analysis +2. Static slot-based detection +3. Call-based detection -#### 1. Static Slot-Based Detection +### 1. Bytecode analysis -This method checks specific storage slots in a contract (e.g., EIP-1967 or custom slots) to retrieve the implementation address. It's efficient but limited to proxies that follow standard storage patterns. +This method provides the most reliable and accurate way to detect proxy contracts. It examines the contract's bytecode to identify the delegatecall instruction. -#### 2. Call-Based Detection +For `EIP-1167` proxies, resolution occurs during this step. For other types, bytecode analysis generates a list of possible proxy implementations. -This approach involves analyzing how a contract responds to function calls, which can indicate whether it is delegating calls as a proxy. This method works for both standard and non-standard proxies but requires more computational resources. +### 2. Static slot-based detection -#### 3. Bytecode Analysis +This method examines specific storage slots in a contract (e.g., `EIP-1967` or custom slots) to retrieve the implementation address. While efficient, it only works with proxies that follow standard storage patterns. -Examining a contract’s bytecode for specific patterns, such as delegatecall instructions, can help identify proxy contracts. This method is particularly useful for detecting Minimal Proxies +### 3. Call-based detection -## How Loop Decoder resolves proxy contracts +This approach analyzes how a contract responds to function calls, determining whether it delegates calls as a proxy. The method works for both standard and non-standard proxies but consumes more computational resources. -Loop Decoder uses a static slot-based detection to identify proxy contracts. It checks for the presence of the implementation address in known storage slots by calling the `getStorageAt` RPC method. +## Example ```typescript -const storageSlots: Hex[] = [ - '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc', //eipEIP1967 - '0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3', //zeppelin - // ... and more -] - -storageSlots.map((slot) => { - const res = await publicClient.getStorageAt({ - address: contractAddress, - slot, - }) - - if (res === zeroSlot) { - // if the slot is empty it means that this contract is not a proxy - return undefined - } - - return res +import { getProxyImplementation } from '@3loop/transaction-decoder' + +const implementationAddress = await getProxyImplementation({ + address: '0x1234567890abcdef', + chainId: 1, }) ``` diff --git a/apps/docs/src/content/docs/welcome/getting-started.mdx b/apps/docs/src/content/docs/welcome/getting-started.mdx index 906f1b0..a5fdb9c 100644 --- a/apps/docs/src/content/docs/welcome/getting-started.mdx +++ b/apps/docs/src/content/docs/welcome/getting-started.mdx @@ -13,15 +13,15 @@ import { Content as RpcProvider } from '../../components/rpc-provider.md' npm i @3loop/transaction-decoder viem effect ``` -### Overview +## Quick Start -To begin using the Loop Decoder, you must provide three components: +Loop Decoder requires three components: -- RPC Provider -- ABI Loader -- Contract Metadata Loader +1. RPC Provider +2. ABI Data Store +3. Contract Metadata Store -This guide demonstrates setup using the default in-memory implementations for data loaders. For custom storage solutions, see our [How To Decode Transaction](/guides/decode-transaction/) guide. +This guide demonstrates setup using the default in-memory implementations for Data Stores. For custom storage solutions, see our [How To Decode Transaction](/guides/decode-transaction/) guide. ### 1. Set up your RPC Provider @@ -31,9 +31,9 @@ Create a `getPublicClient` function that accepts a chain ID and returns an objec For detailed configuration options and trace API settings, see the [RPC Provider](/reference/rpc-provider/) documentation. -### 2. Initialize ABI Loader +### 2. Initialize ABI Data Store -The `InMemoryAbiStoreLive` provides default ABI loading functionality: +The `InMemoryAbiStoreLive` provides default ABI loading and caching functionality: - Fetches ABIs from multiple sources (Etherscan, 4bytes, Openchain, Sourcify) - Caches results in memory @@ -48,9 +48,9 @@ const ABILoaderLayer = Layer.setConfigProvider(Config) const abiStore = InMemoryAbiStoreLive.pipe(Layer.provide(ABILoaderLayer)) ``` -For a custom implementation, see our [How To Decode Transaction, ABI Loader](/guides/decode-transaction/#abi-loader) guide. +For a custom implementation, see our [How To Decode Transaction (ABI Data Store)](/guides/decode-transaction/#2-abi-data-store) guide. -### 3. Initialize Contract Metadata Loader +### 3. Initialize Contract Metadata Store The `InMemoryContractMetaStoreLive` handles contract metadata resolution: @@ -63,9 +63,9 @@ import { InMemoryContractMetaStoreLive } from '@3loop/transaction-decoder/in-mem const contractMetaStore = InMemoryContractMetaStoreLive ``` -For a custom implementation, see our [How To Decode Transaction, Contract Metadata Loader](/guides/decode-transaction/#contract-metadata-loader) guide. +For a custom implementation, see our [How To Decode Transaction (Contract Metadata Store)](/guides/decode-transaction/#3-contract-metadata-store) guide. -### 4. Final Step +### 4. Decode a Transaction Finally, you can create a new instance of the LoopDecoder class and invoke `decodeTransaction` method with the transaction hash and chain ID: