Skip to content

Commit

Permalink
Update aastro config and decode calldata (#198)
Browse files Browse the repository at this point in the history
  • Loading branch information
anastasiarods authored Jan 12, 2025
1 parent 7970424 commit 8775211
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 93 deletions.
7 changes: 7 additions & 0 deletions apps/docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ export default defineConfig({
github: 'https://github.com/3loop/loop-decoder',
twitter: 'https://x.com/3loop_io',
},
editLink: {
baseUrl: 'https://github.com/3loop/loop-decoder/edit/main/apps/docs/',
},
components: {
Header: './src/components/Header.astro',
},
lastUpdated: true,
sidebar: [
{
label: 'Welcome',
Expand Down
129 changes: 129 additions & 0 deletions apps/docs/src/components/Header.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
---
import config from 'virtual:starlight/user-config';
import LanguageSelect from 'virtual:starlight/components/LanguageSelect';
import Search from 'virtual:starlight/components/Search';
import SiteTitle from 'virtual:starlight/components/SiteTitle';
import SocialIcons from 'virtual:starlight/components/SocialIcons';
import ThemeSelect from 'virtual:starlight/components/ThemeSelect';
/**
* Render the `Search` component if Pagefind is enabled or the default search component has been overridden.
*/
const isHomePage = Astro.url.pathname === '/';
const shouldRenderSearch =
!isHomePage &&
(config.pagefind || config.components.Search !== '@astrojs/starlight/components/Search.astro');
---

<div class="header sl-flex">
<div class="title-wrapper sl-flex">
<SiteTitle {...Astro.props}/>
</div>
<div class="sl-flex">
{shouldRenderSearch &&
<Search {...Astro.props}/>}
</div>
<div class="sl-hidden md:sl-flex right-group">
<div class="external-link">
<a class="external-link-text" href="https://loop-decoder-web.vercel.app" data-color-scheme="no-preference: light; light: light; dark: dark;">
<span>Playground</span>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
<polyline points="15 3 21 3 21 9"></polyline>
<line x1="10" y1="14" x2="21" y2="3"></line>
</svg>
</a>
</div>
<div class="sl-flex social-icons">
<SocialIcons {...Astro.props}/>
</div>
<ThemeSelect {...Astro.props}/>
<LanguageSelect {...Astro.props}/>
<!-- Place this tag where you want the button to render. -->

</div>
</div>

<style>
.header {
gap: var(--sl-nav-gap);
justify-content: space-between;
align-items: center;
height: 100%;
}

.title-wrapper {
/* Prevent long titles overflowing and covering the search and menu buttons on narrow viewports. */
overflow: clip;
/* Avoid clipping focus ring around link inside title wrapper. */
padding: 0.25rem;
margin: -0.25rem;
}

.right-group,
.social-icons {
gap: 1rem;
align-items: center;
}
.social-icons::after {
content: '';
height: 2rem;
border-inline-end: 1px solid var(--sl-color-gray-5);
}

.external-link {
height: 28px;
}

@media (min-width: 50rem) {
:global(:root[data-has-sidebar]) {
--__sidebar-pad: calc(2 * var(--sl-nav-pad-x));
}
:global(:root:not([data-has-toc])) {
--__toc-width: 0rem;
}
.header {
--__sidebar-width: max(0rem, var(--sl-content-inline-start, 0rem) - var(--sl-nav-pad-x));
--__main-column-fr: calc(
(
100% + var(--__sidebar-pad, 0rem) - var(--__toc-width, var(--sl-sidebar-width)) -
(2 * var(--__toc-width, var(--sl-nav-pad-x))) - var(--sl-content-inline-start, 0rem) -
var(--sl-content-width)
) / 2
);
display: grid;
grid-template-columns:
/* 1 (site title): runs up until the main content column’s left edge or the width of the title, whichever is the largest */
minmax(
calc(var(--__sidebar-width) + max(0rem, var(--__main-column-fr) - var(--sl-nav-gap))),
auto
)
/* 2 (search box): all free space that is available. */
1fr
/* 3 (right items): use the space that these need. */
auto;
align-content: center;
}
}

.external-link-text {
display: inline-flex;
align-items: center;
gap: 0.4rem;
color: var(--sl-color-text-accent);
text-decoration: none;
font-weight: 500;
transition: color 0.2s ease;
}

.external-link-text:hover {
color: var(--sl-color-text);
}

.external-link-text svg {
width: 0.9em;
height: 0.9em;
}
</style>
10 changes: 7 additions & 3 deletions apps/docs/src/content/docs/guides/decode-transaction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ To create a custom ABI Data Store, implement the `VanillaAbiStore` interface:

<AbiStoreInterface />

#### Example: an ABI data store with Etherscan and 4byte strategies and in-memory cache
#### Example: an ABI data store with Etherscan and 4byte data loaders and in-memory cache

<MemoryAbiLoader />

Expand All @@ -78,7 +78,7 @@ To create a custom Contract Metadata Store, implement the `VanillaContractMetaSt

<MetaStoreInterface />

#### Example: a Contract Metadata Store with ERC20 strategy and in-memory cache
#### Example: a Contract Metadata Store with ERC20 data loader and in-memory cache

<MemoryContractLoader />

Expand Down Expand Up @@ -121,7 +121,7 @@ async function main() {
main()
```

Expected output:
Check the full expected output in our [Playground](https://loop-decoder-web.vercel.app/decode/1/0xc0bd04d7e94542e58709f51879f64946ff4a744e1c37f5f920cea3d478e115d7) or see it below:

```json
{
Expand Down Expand Up @@ -161,3 +161,7 @@ Expected output:
// ...
}
```

## Try it live

Try decoding the above or any other transactions in the our playground [here](https://loop-decoder-web.vercel.app/decode).
Original file line number Diff line number Diff line change
@@ -1,85 +1,26 @@
---
title: How to decode a Calldata
description: On this page you will provide a step-by-step guide on how to decode and interpret a Calldata using Loop Decoder.
title: Decoding Calldata
description: On this page you will provide a guide on how to decode and interpret a Calldata using Loop Decoder.
sidebar:
order: 2
---

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'
Loop Decoder provides a `decodeCalldata` method that decodes a transaction calldata. This method handles special cases:

In this guide, we will go through the process of decoding EVM Calldata using Loop Decoder. As an example we will decode a Calldata for Gnosis Safe Multisend transaction that has deep nested calls.
- Gnosis Safe Multisend or Multy Transfer transactions
- Multicalls and deeply nested calls
- Proxy contract resolutions

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 Loop Decoder

## Prerequisites
To use `decodeCalldata` method, you need to setup a `TransactionDecoder` instance with the necessary data stores. For setup instructions, refer to:

### Create a new project
- Setup with default in-memory data stores (the quickest way): [Getting Started](/welcome/getting-started/)
- Setup with customizable data stores: [How To Decode Transaction (detailed)](/guides/decode-transaction/)

Optionally, you can create a new project to follow along, or skip to [Required packages](#required-packages).
### Decoding Calldata

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:

```bash
mkdir example-decode && cd example-decode
bun init
```

### Required packages

For this guide, you will need to have the following packages installed:

```bash
bun install @3loop/transaction-decoder viem
```

## Data Sources

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.

### 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.

<RpcProvider />

### ABI loader

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`:

<MemoryAbiLoader />

### Contract Metadata loader

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.

<MemoryContractLoader />

Finally, you can create a new instance of the LoopDecoder class:

```ts
import { TransactionDecoder } from '@3loop/transaction-decoder'

const decoder = new TransactionDecoder({
getPublicClient: getPublicClient,
abiStore: abiStore,
contractMetaStore: contractMetaStore,
})
```

## Decoding a Calldata

Now that we have all the necessary components, we can start decoding a Calldata. For this example, we will use the following Calldata:
In the example below, we will decode a transaction calldata for for Gnosis Safe Multisend transaction that has deep nested calls.

```ts
async function main() {
Expand All @@ -99,12 +40,6 @@ async function main() {
main()
```

Run the script:

```bash
bun run index.ts
```

Expected output:

```json
Expand Down Expand Up @@ -140,15 +75,9 @@ Expected output:
"type": "address",
"value": "0x2D8880BcC0618DBCC5d516640015A69e28fdC406"
},
{
"name": "value",
"type": "uint256",
"value": "0"
},
{
"name": "dataLength",
"type": "uint256",
"value": "1544"
},
//...
```

### Try it live

Try decoding the above or any other calldata in our [Playground](https://loop-decoder-web.vercel.app/calldata).
2 changes: 1 addition & 1 deletion apps/docs/src/content/docs/welcome/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Loop Decoder requires three components:
2. ABI Data Store
3. Contract Metadata Store

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.
This guide demonstrates setup using the default in-memory implementations for Data Stores (see and run [Full Example Code](https://stackblitz.com/github/3loop/loop-decoder/tree/main/sandbox/quick-start?file=src/decoder.ts) from this page). For custom storage solutions, see our [How To Decode Transaction](/guides/decode-transaction/) guide.

### 1. Set up your RPC Provider

Expand Down
8 changes: 6 additions & 2 deletions apps/docs/src/content/docs/welcome/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Loop Decoder is a TypeScript library with minimal dependencies that transforms b

- Work in any JavaScript environment
- Minimal external dependencies - you only need an RPC; everything else can be fetched from your own storage.
- Highly customizable - we don't force you to use a specific API or storage; instead, we provide data loaders.
- Highly customizable - we don't force you to use a specific API or storage; instead, we provide composable and customizable modules.

![Loop Decoder architecture](../../../assets/diagram.png)

Expand All @@ -30,7 +30,11 @@ The minimal configuration for the Transaction Decoder requires an RPC URL, ABIs,

### Transaction Interpreter

Transaction Interpretation allows you to understand what really happened in any transaction. In Loop Decoder, this is a JavaScript function that takes a Decoded Transaction and selects the most important fields and parameters based on the type of contract interacted with. The goal is to abstract away blockchain and smart contract nuances, producing an object understandable for both technical and non-technical users.
Transaction Interpretation allows you to understand what really happened in any transaction. In Loop Decoder, this is a JavaScript function that takes a Decoded Transaction and selects the most important fields and parameters based on the type of contract interacted with.

:::caution
The Transaction Interpreter is still under development and breaking changes are expected.
:::

For example, the interpretation can construct a one-line description of the transaction:

Expand Down

0 comments on commit 8775211

Please sign in to comment.