Skip to content

Commit bc453bd

Browse files
committed
Update custom data stores examples
1 parent f4ff11a commit bc453bd

File tree

3 files changed

+53
-77
lines changed

3 files changed

+53
-77
lines changed

apps/docs/src/content/components/memory-abi-loader.md

Lines changed: 30 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,60 +9,46 @@ import {
99
// Create an in-memory cache for the ABIs
1010
const abiCache = new Map<string, ContractABI>()
1111

12+
// ABI store implementation with caching and multiple resolution strategies
1213
const abiStore: VanillaAbiStore = {
13-
// Define the strategies to use for fetching the ABIs
1414
strategies: [
15-
EtherscanStrategyResolver({
16-
apikey: 'YourApiKeyToken',
15+
// List of stratagies to resolve new ABIs
16+
EtherscanV2StrategyResolver({
17+
apikey: process.env.ETHERSCAN_API_KEY || '',
1718
}),
1819
FourByteStrategyResolver(),
1920
],
2021

21-
// Get the ABI from the cache
22-
// Get it by contract address, event name or signature hash
22+
// Get ABI from memory by address, event or signature
23+
// Can be returned the list of all possible ABIs
2324
get: async ({ address, event, signature }) => {
24-
const value = abiCache.get(address)
25-
if (value) {
26-
return {
27-
status: 'success',
28-
result: value,
29-
}
30-
} else if (event) {
31-
const value = abiCache.get(event)
32-
if (value) {
33-
return {
34-
status: 'success',
35-
result: value,
36-
}
37-
}
38-
} else if (signature) {
39-
const value = abiCache.get(signature)
40-
if (value) {
41-
return {
42-
status: 'success',
43-
result: value,
44-
}
45-
}
46-
}
25+
const key = address?.toLowerCase() || event || signature
26+
if (!key) return []
4727

48-
return {
49-
status: 'empty',
50-
result: null,
51-
}
28+
const cached = abiCache.get(key)
29+
return cached
30+
? [
31+
{
32+
...cached,
33+
id: key,
34+
source: 'etherscan',
35+
status: 'success',
36+
},
37+
]
38+
: []
5239
},
5340

54-
// Set the ABI in the cache
55-
// Store it by contract address, event name or signature hash
56-
set: async (_key, value) => {
57-
if (value.status === 'success') {
58-
if (value.result.type === 'address') {
59-
abiCache.set(value.result.address, value.result)
60-
} else if (value.result.type === 'event') {
61-
abiCache.set(value.result.event, value.result)
62-
} else if (value.result.type === 'func') {
63-
abiCache.set(value.result.signature, value.result)
64-
}
65-
}
41+
set: async (_key, abi) => {
42+
const key =
43+
abi.type === 'address'
44+
? abi.address.toLowerCase()
45+
: abi.type === 'event'
46+
? abi.event
47+
: abi.type === 'func'
48+
? abi.signature
49+
: null
50+
51+
if (key) abiCache.set(key, abi)
6652
},
6753
}
6854
```

apps/docs/src/content/components/memory-contract-loader.md

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,19 @@ import { ERC20RPCStrategyResolver } from '@3loop/transaction-decoder'
55
// Create an in-memory cache for the contract meta-information
66
const contractMetaCache = new Map<string, ContractData>()
77

8+
// Contract metadata store implementation with in-memory caching
89
const contractMetaStore: VanillaContractMetaStore = {
9-
// Define the strategies to use for fetching the contract meta-information
1010
strategies: [ERC20RPCStrategyResolver],
1111

12-
// Get the contract meta-information from the cache
1312
get: async ({ address, chainID }) => {
1413
const key = `${address}-${chainID}`.toLowerCase()
15-
const value = contractMetaCache.get(key)
16-
17-
if (value) {
18-
return {
19-
status: 'success',
20-
result: value,
21-
}
22-
}
23-
24-
return {
25-
status: 'empty',
26-
result: null,
27-
}
14+
const cached = contractMetaCache.get(key)
15+
return cached ? { status: 'success', result: cached } : { status: 'empty', result: null }
2816
},
2917

30-
// Set the contract meta-information in the cache
3118
set: async ({ address, chainID }, result) => {
32-
const key = `${address}-${chainID}`.toLowerCase()
33-
3419
if (result.status === 'success') {
35-
contractMetaCache.set(key, result.result)
20+
contractMetaCache.set(`${address}-${chainID}`.toLowerCase(), result.result)
3621
}
3722
},
3823
}

apps/docs/src/content/docs/recipes/fc-bot.mdx

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import { Steps } from '@astrojs/starlight/components'
1010

1111
In this guide, you will learn how to create a Farcaster bot that sends human-readable alerts about transactions happening on-chain. You can customize this bot for any EVM-compatible blockchain, and you don't need any specific knowledge about EVM transaction decoding and interpretation.
1212

13+
:::tip
14+
Jump to the repo to view the full code example [3loop/farcaster-onchain-alerts-bot](https://github.com/3loop/farcaster-onchain-alerts-bot)
15+
:::
16+
1317
:::note
1418
For the demo, we've used a [friend.tech](https://www.friend.tech) smart contract to track trading activity on the Base Mainnet network. By modifying the contract address and environment variables, you can create a bot to track smart contracts on any other EVM chain.
1519
:::
@@ -72,8 +76,12 @@ The `constants.ts` file contains the RPC URL and configuration. The Base RPCs do
7276
```ts title="src/constants.ts"
7377
export const RPC = {
7478
8453: {
75-
url: `wss://base-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`,
76-
traceAPI: 'none',
79+
// Transaciton decoder by default needs archive node for transaction tracing
80+
// becasue Alchemy free plan does not provide archive nodes, we use a different RPC here
81+
archiveUrl: process.env.ARCHIVE_RPC_URL,
82+
83+
// Provide "none" when transaciton tracing is not needed
84+
traceAPI: 'geth',
7785
},
7886
}
7987
```
@@ -85,18 +93,11 @@ import { createPublicClient } from 'viem'
8593

8694
const getPublicClient = (chainId: number) => {
8795
const rpc = RPC[chainId as keyof typeof RPC]
88-
89-
if (!rpc) {
90-
throw new Error(`Missing RPC provider for chain ID ${chainId}`)
91-
}
96+
if (!rpc) throw new Error(`Missing RPC provider for chain ID ${chainId}`)
9297

9398
return {
94-
client: createPublicClient({
95-
transport: webSocket(rpc.url),
96-
}),
97-
config: {
98-
traceAPI: rpc.traceAPI,
99-
},
99+
client: createPublicClient({ transport: http(rpc.archiveUrl) }),
100+
config: { traceAPI: rpc.traceAPI },
100101
}
101102
}
102103
```
@@ -140,7 +141,7 @@ const decoded = await decoder.decodeTransaction({
140141
})
141142
```
142143

143-
The `decodeTransaction` method returns a `DecodedTransaction` object, which you can inspect using our [playground](https://loop-decoder-web.vercel.app/). [Here](https://loop-decoder-web.vercel.app/tx/8453/0x3bc635e07e1cec2b73a896e6d130cffde6b3eeb419f91ea79028927fb7a66a07) is an example of the Friend.tech transaction.
144+
The `decodeTransaction` method returns a `DecodedTransaction` object, which you can inspect using our [playground](https://loop-decoder-web.vercel.app/). [Here](https://loop-decoder-web.vercel.app/interpret/8453/0x3bc635e07e1cec2b73a896e6d130cffde6b3eeb419f91ea79028927fb7a66a07) is an example of the Friend.tech transaction.
144145

145146
To interpret a decoded transaction, the `interpreter.ts` file contains a `transformEvent` function that transforms the `DecodedTransaction` and adds an action description. You can test the `transformEvent` function by putting it into the Interpretation field on the playground with the Friend.tech [example](https://loop-decoder-web.vercel.app/tx/8453/0x3bc635e07e1cec2b73a896e6d130cffde6b3eeb419f91ea79028927fb7a66a07), and pressing "Interpret".
146147

@@ -158,8 +159,12 @@ export const CHAIN_ID = 8453
158159
In the program's entry point, we start a WebSocket subscription for new transactions using Alchemy's WebSocket RPC.
159160

160161
```ts title="src/index.ts"
162+
const wsClient = createPublicClient({
163+
transport: webSocket(ALCHEMY_WS_RPC_URL),
164+
})
165+
161166
async function createSubscription(address: string) {
162-
await publicClient.transport.subscribe({
167+
await wsClient.transport.subscribe({
163168
method: 'eth_subscribe',
164169
params: [
165170
//@ts-ignore

0 commit comments

Comments
 (0)