Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added apps/docs/src/assets/polymarket-bot.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion apps/docs/src/content/components/memory-abi-loader.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
// Create an in-memory cache for the ABIs
const abiCache = new Map<string, ContractABI>()

// ABI store implementation with caching and multiple resolution strategies
const abiStore: VanillaAbiStore = {
strategies: [
// List of stratagies to resolve new ABIs
Expand Down
5 changes: 2 additions & 3 deletions apps/docs/src/content/components/memory-contract-loader.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
```ts title="index.ts"
import type { ContractData, VanillaContractMetaStore } from '@3loop/transaction-decoder'
import { ERC20RPCStrategyResolver } from '@3loop/transaction-decoder'
import { ERC20RPCStrategyResolver, NFTRPCStrategyResolver } from '@3loop/transaction-decoder'

// Create an in-memory cache for the contract meta-information
const contractMetaCache = new Map<string, ContractData>()

// Contract metadata store implementation with in-memory caching
const contractMetaStore: VanillaContractMetaStore = {
strategies: [ERC20RPCStrategyResolver],
strategies: [ERC20RPCStrategyResolver, NFTRPCStrategyResolver],

get: async ({ address, chainID }) => {
const key = `${address}-${chainID}`.toLowerCase()
Expand Down
255 changes: 255 additions & 0 deletions apps/docs/src/content/docs/recipes/farcaster-bot.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
---
title: Build a Farcaster Bot for On-Chain Alerts
description: Create a Farcaster bot that monitors blockchain transactions and posts human-readable casts
sidebar:
order: 2
---

import { Content as MemoryAbiLoader } from '../../components/memory-abi-loader.md'

Check warning on line 8 in apps/docs/src/content/docs/recipes/farcaster-bot.mdx

View workflow job for this annotation

GitHub Actions / pr

'MemoryAbiLoader' is defined but never used
import { Content as MemoryContractLoader } from '../../components/memory-contract-loader.md'
import { Steps } from '@astrojs/starlight/components'

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.

:::tip
Jump to the repo to view the full code example [3loop/farcaster-onchain-alerts-bot](https://github.com/3loop/farcaster-onchain-alerts-bot)
:::

:::note
This example tracks AAVE trades on Base Mainnet. You can easily adapt it to monitor any contract on any EVM chain.
:::

![Final result](../../../assets/fc-bot.png)

## Guide

### Step 0: Prerequisites

- Bun installed (see installation guide [here](https://bun.sh/docs/installation))
- Alchemy account (sign up [here](https://www.alchemy.com/))
- Basescan API Key (sign up [here](https://basescan.org/register))
- Farcaster account (can be yours or a separate one for your bot)

### Step 1: Clone the Repository

Clone the bot [repository](https://github.com/3loop/farcaster-onchain-alerts-bot) and install dependencies:

```bash
git clone https://github.com/3loop/farcaster-onchain-alerts-bot
cd farcaster-onchain-alerts-bot
bun i
```

### Step 2: Configure Environment Variables

Copy the `.env.example` file to `.env` and add your API keys:

```bash
cp .env.example .env
vim .env
```

For the Farcaster bot you need to specify:

- `ALCHEMY_API_KEY` - Alchemy API key to monitor new transactions via WebSocket
- `ETHERSCAN_API_KEY` - Basescan API key, used to fetch and cache ABIs
- `ARCHIVE_RPC_URL` - Archive RPC URL for Base (required for transaction tracing)
- `SIGNER_PRIVATE_KEY` and `ACCOUNT_FID` - Farcaster credentials (see Step 3)

### Step 3: Create a Farcaster Account Key (Signer)

A Farcaster signer is a separate Ed25519 public and private key pair connected to your Farcaster account that you need for posting messages on your behalf. To connect the key pair, you have to send a transaction from your Farcaster wallet to the Key Registry Farcaster smart contract. At the moment of writing this guide, there was no simple way to create and connect the signer without using 3rd party APIs. So we made a script to generate the required transaction, and to run it you need to do the following:

<Steps>

1. **Fund your Farcaster custody wallet on Optimism:**: You need some ETH on the Optimism chain to pay for the gas. A few dollars would be enough. Click on the 3 dots near your profile, press "About," and there you will find your custody address.
2. **Get your Farcaster recovery phrase**: On your phone, go to settings -> advanced -> recovery phrase, and write this recovery phrase into the `MNEMONIC` variable in the `scripts/create-signer.ts` file.
3. **Run the script**: Run the following command `bun run scripts/create-signer.ts`. The result of this script will be an Optimism transaction like [this](https://optimistic.etherscan.io/tx/0x9eecacefceb6f120c3ef50222eabb15d86fd5feac6dae3fdf09dccb7687c70d4), and a public and private key printed in the console. Do not share the private key.
4. **Add env variables**: Add the private key generated from the script and the bot's account FID into the `SIGNER_PRIVATE_KEY` and `ACCOUNT_FID` variables.

</Steps>

### Step 4: Setup the Transaction Decoder

Loop Decoder requires three components: an RPC provider, ABI store, and contract metadata store. Let's set up each one:

#### RPC Provider

Configure your RPC provider in `constants.ts` for Base Mainnet (chain ID 8453). We use `traceAPI: 'geth'` for transaction tracing:

```ts title="src/constants.ts"
export const RPC = {
8453: {
archiveUrl: process.env.ARCHIVE_RPC_URL,
traceAPI: 'geth',
},
}
```

```ts title="src/decoder/decoder.ts"
const getPublicClient = (chainId: number) => {
const rpc = RPC[chainId as keyof typeof RPC]
if (!rpc) throw new Error(`Missing RPC provider for chain ID ${chainId}`)

return {
client: createPublicClient({ transport: http(rpc.archiveUrl) }),
config: { traceAPI: rpc.traceAPI },
}
}
```

#### ABI Store

Set up an in-memory ABI cache with Basescan and 4byte.directory strategies:

<MemoryAbiLoader />

#### Contract Metadata Store

Set up contract metadata resolution for token or NFT information (name, decimals, symbol):

<MemoryContractLoader />

#### Create Decoder Instance

Combine all components into a `TransactionDecoder` instance:

```ts title="src/decoder/decoder.ts"
import { TransactionDecoder } from '@3loop/transaction-decoder'

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

### Step 5: Decode and Interpret Transactions

With the decoder set up, you can now decode transactions and make them human-readable:

```ts title="src/index.ts"
// 1. Decode the transaction
const decoded = await decoder.decodeTransaction({
chainID: CHAIN_ID,
hash: txHash,
})

// 2. Interpret it (make it human-readable)
const interpreted = interpretTx(decoded)

// 3. Use the result
console.log(interpreted.action) // e.g., "Alice bought 5 shares of Bob for 0.1 ETH"
```

View a [decoded AAVE transaction example](https://loop-decoder-web.vercel.app/interpret/1/0xc0bd04d7e94542e58709f51879f64946ff4a744e1c37f5f920cea3d478e115d7) in our playground. You can test the `interpretTx` function by pasting it into the Interpretation field.

### Step 6: Monitor AAVE Transactions

Set up real-time monitoring for AAVE trades. Configure the contract address in `constants.ts`:

```ts title="src/constants.ts"
export const CONTRACT_ADDRESS = '0xa238dd80c259a72e81d7e4664a9801593f98d1c5'
export const CHAIN_ID = 8453
```

Subscribe to new transactions and process them:

```ts title="src/index.ts"
const wsClient = createPublicClient({
transport: webSocket(ALCHEMY_WS_RPC_URL),
})

// Subscribe to AAVE transactions
wsClient.transport.subscribe({
method: 'eth_subscribe',
params: [
'alchemy_minedTransactions',
{
addresses: [{ to: CONTRACT_ADDRESS }],
includeRemoved: false,
hashesOnly: true,
},
],
onData: (data: any) => {
const hash = data?.result?.transaction?.hash
if (hash) handleTransaction(hash)
},
})

// Process each transaction
async function handleTransaction(txHash: string) {
try {
// 1. Decode
const decoded = await decoder.decodeTransaction({
chainID: CHAIN_ID,
hash: txHash,
})

if (!decoded) return

// 2. Interpret
const interpreted = interpretTx(decoded)

// 3. Format message
const text = `New trade: ${interpreted.trader} ${interpreted.isBuy ? 'Bought' : 'Sold'} ${
interpreted.shareAmount
} shares of ${interpreted.subject} for ${interpreted.price} ETH`

// 4. Post to Farcaster
await publishToFarcaster({
text,
url: `https://basescan.org/tx/${txHash}`,
})
} catch (e) {
console.error(e)
}
}
```

### Step 7: Publish to Farcaster

Use the `@standard-crypto/farcaster-js-hub-rest` package to publish casts:

```ts title="src/index.ts"
async function publishToFarcaster(cast: { text: string; url: string }) {
await client.submitCast(
{
text: cast.text,
embeds: [{ url: cast.url }],
},
Number(fid),
signerPrivateKey,
)
}
```

### Step 8: Run the Bot

Start the bot locally:

```bash
bun run src/index.ts
```

The bot will now monitor AAVE transactions and post casts to your Farcaster account.

## Next Steps

You've built a Farcaster bot that:

- Monitors specific contracts in real-time
- Decodes transactions automatically
- Generates human-readable descriptions
- Posts alerts to Farcaster

**Customize it further:**

- Track different contracts by updating `CONTRACT_ADDRESS`
- Modify the cast format in `handleTransaction`
- Add filters for specific transaction types or amounts
- Deploy to a server for 24/7 monitoring

---

Need help? Reach out on X/Twitter [@3loop_io](https://x.com/3loop_io) or check the [full code example](https://github.com/3loop/farcaster-onchain-alerts-bot).
Loading
Loading