Skip to content

Commit 215e045

Browse files
authored
Proposal for an unified interpretation data type (#78)
* Proposal for an unified interpretation data type * Add uniswapv3 swap interpretation * Add standard library of utilities to share across interpreters * Add default fallback interpreter * Use interpreters from the package in the web playground * Remove empty replace * Add an initial implementation for 1inch and hop-protocol * Add changeset
1 parent 84bc692 commit 215e045

File tree

19 files changed

+425
-281
lines changed

19 files changed

+425
-281
lines changed

.changeset/big-jobs-beg.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@3loop/transaction-interpreter': minor
3+
---
4+
5+
Add a common data type to return it from all interpreters. Additionally added a couple of more examples of interpretators and a fallback interpretator.

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,19 @@ Transaction decoding is a crucial component of many dApps. Our goal is to create
1919

2020
Currently, the available EVM transaction decoders require developers to use specific databases or provide a lower-level API that requires maintenance. The Loop Decoder, however, can be used as a plug-and-play component in any layer of dApp infrastructure.
2121

22+
The open-source nature of the Loop Decoder allows developers to also integrate new chains as they see fit. While using a third party closed-source decoder, developers are limited to the chains that the provider supports. This can be a significant burden for developers who want to support multiple emerging chains.
23+
24+
### Glossary
25+
26+
- **Transaction Decoder**: Decoder automates the decoding of any transaction data, event log and trace, in a unified format. We leverage multiple data sources for contract metadata and ABI resolution. With an unified interface to decode transactions across any protocol, developers don't have to manually understand and decode each individual transactions.
27+
- **Transaction Interpreter**: Interpreter is a flexible layer that allows developers to define custom interpretation of a decoded transactions. In context of this library, an interpretation is a transformation from a decoded transaction to a human-readable format. It is used to extract the most significant information that can be directly displayed to a non techincal user.
28+
2229
## Features
2330

2431
- [x] Can be used in any JavaScript environment
2532
- [x] Minimal external dependencies - connect your own storage
2633
- [x] Predefined ABI and Contract metadata resolvers
34+
- [x] Automatically resolves contract proxise and multicalls
2735
- [x] Flexible interpreter that allows you to define any custom interpretation of EVM transactions.
2836

2937
## Examples

apps/web/src/app/contract/[contract]/table.tsx

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,24 @@
11
'use client'
22
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
33
import React from 'react'
4-
import { DecodedTx } from '@3loop/transaction-decoder'
4+
import type { DecodedTx } from '@3loop/transaction-decoder'
55
import { findAndRunInterpreter, Interpretation } from '@/lib/interpreter'
6-
import { defaultInterpreters } from '@/lib/default-interpreters'
7-
import { Interpreter } from '@3loop/transaction-interpreter'
8-
9-
function getAvaliableinterpreters() {
10-
if (typeof window === 'undefined') return undefined
11-
12-
let res: Interpreter[] = []
13-
14-
for (const interpreter of defaultInterpreters) {
15-
const stored = window.localStorage.getItem(interpreter.id)
16-
if (stored) {
17-
const updatedSchema = JSON.parse(stored)
18-
19-
res.push({
20-
...interpreter,
21-
schema: updatedSchema,
22-
})
23-
} else {
24-
res.push(interpreter)
25-
}
26-
}
27-
28-
return res
29-
}
306

317
export default function TxTable({ txs }: { txs: DecodedTx[] }) {
328
const [result, setResult] = React.useState<Interpretation[]>([])
33-
const [interpreters] = React.useState(getAvaliableinterpreters)
349

3510
React.useEffect(() => {
3611
async function run() {
37-
if (interpreters == null) return
38-
3912
const withIntepretations = await Promise.all(
4013
txs.map((tx) => {
41-
return findAndRunInterpreter(tx, interpreters)
14+
return findAndRunInterpreter(tx)
4215
}),
4316
)
4417

4518
setResult(withIntepretations)
4619
}
4720
run()
48-
}, [interpreters, txs])
21+
}, [txs])
4922

5023
return (
5124
<Table>

apps/web/src/app/tx/[chainID]/[hash]/form.tsx

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,32 @@ import { DecodedTx } from '@3loop/transaction-decoder'
1212
import { Interpretation, applyInterpreter } from '@/lib/interpreter'
1313
import CodeBlock from '@/components/ui/code-block'
1414
import { NetworkSelect } from '@/components/ui/network-select'
15-
import { Interpreter } from '@3loop/transaction-interpreter'
15+
import { fallbackInterpreter, getInterpreterForContract } from '@3loop/transaction-interpreter'
1616

1717
interface FormProps {
1818
currentChainID: number
1919
decoded?: DecodedTx
20-
defaultInterpreter?: Interpreter
2120
currentHash?: string
2221
}
2322

24-
export default function DecodingForm({ decoded, defaultInterpreter, currentHash, currentChainID }: FormProps) {
23+
export default function DecodingForm({ decoded, currentHash, currentChainID }: FormProps) {
2524
const [result, setResult] = React.useState<Interpretation>()
26-
const [schema, setSchema] = useLocalStorage(defaultInterpreter?.id ?? 'unknown', defaultInterpreter?.schema)
25+
const [persistedSchema, setSchema] = useLocalStorage(decoded?.toAddress ?? 'unknown', '')
26+
27+
const schema = React.useMemo(() => {
28+
if (persistedSchema !== '') return persistedSchema
29+
30+
if (decoded?.toAddress == null) return null
31+
32+
const schema = getInterpreterForContract({
33+
address: decoded.toAddress,
34+
chain: decoded.chainID,
35+
})
36+
37+
if (schema != null) return schema
38+
39+
return fallbackInterpreter
40+
}, [decoded?.chainID, decoded?.toAddress, persistedSchema])
2741

2842
const router = useRouter()
2943

@@ -34,24 +48,24 @@ export default function DecodingForm({ decoded, defaultInterpreter, currentHash,
3448
}
3549

3650
const onRun = React.useCallback(() => {
37-
if (schema && defaultInterpreter != null && decoded != null) {
51+
if (schema && decoded != null) {
3852
const newInterpreter = {
39-
...defaultInterpreter,
53+
id: decoded.toAddress ?? 'unknown',
4054
schema: schema,
4155
}
4256

4357
applyInterpreter(decoded, newInterpreter).then((res) => {
4458
setResult(res)
4559
})
4660
}
47-
}, [schema, decoded, defaultInterpreter])
61+
}, [schema, decoded])
4862

4963
// Run the interpreter on page load
5064
React.useEffect(() => {
51-
if (schema && defaultInterpreter != null && decoded != null && result == null) {
65+
if (schema && decoded != null && result == null) {
5266
onRun()
5367
}
54-
}, [schema, decoded, defaultInterpreter, result, onRun])
68+
}, [schema, decoded, result, onRun])
5569

5670
return (
5771
<div className="grid h-full items-stretch gap-6 grid-cols-1 lg:grid-cols-[1fr_200px]">
@@ -86,7 +100,7 @@ export default function DecodingForm({ decoded, defaultInterpreter, currentHash,
86100

87101
<CodeBlock
88102
language="javascript"
89-
value={schema}
103+
value={schema ?? ''}
90104
onChange={(value) => setSchema(value)}
91105
lineNumbers={true}
92106
readonly={false}
Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,16 @@
11
import * as React from 'react'
22
import DecodingForm from './form'
33
import { decodeTransaction } from '@/lib/decode'
4-
import { emptyInterpreter, findInterpreter } from '@/lib/interpreter'
5-
import { defaultInterpreters } from '@/lib/default-interpreters'
64

75
export default async function TransactionPage({ params }: { params: { hash: string; chainID: number } }) {
86
const decoded = await decodeTransaction({
97
hash: params.hash,
108
chainID: params.chainID,
119
})
1210

13-
if (!decoded) {
11+
if (!decoded || !decoded.toAddress) {
1412
return <DecodingForm currentHash={params.hash} currentChainID={params.chainID} />
1513
}
1614

17-
const interpreter = findInterpreter({
18-
decodedTx: decoded,
19-
interpreters: defaultInterpreters,
20-
})
21-
22-
return (
23-
<DecodingForm
24-
decoded={decoded}
25-
defaultInterpreter={interpreter || emptyInterpreter}
26-
currentHash={params.hash}
27-
currentChainID={params.chainID}
28-
/>
29-
)
15+
return <DecodingForm decoded={decoded} currentHash={params.hash} currentChainID={params.chainID} />
3016
}

apps/web/src/lib/default-interpreters.ts

Lines changed: 0 additions & 143 deletions
This file was deleted.

0 commit comments

Comments
 (0)