Skip to main content

Build a Telegram bot with Ghost

Β· 6 min read
Chris
Co-Founder of Ghost

Query Ghost data to power the data source for your telegram bot!

Overview​

This tutorial demonstrates how to create a Telegram bot that monitors and announces new deposits on the blockchain using Ghost. The bot sends beautifully formatted messages with transaction details and celebratory GIFs to your specified Telegram channels.

For the purposes of this example, we'll monitor deposits on Zeru Finance.

What is Ghost and Why Does It Matter?​

Ghost is a next-gen indexing stack that empowers developers to build GraphQL APIs for extracting, transforming, storing, and querying blockchain data.

What sets Ghost apart from other indexers:

  1. Write indexers in the same language as your smart contracts (Solidity) - Since smart contract developers emit critical events for indexers, they should be able to write the indexers themselves without needing to learn new languages or tools.

  2. Fully browser-based - Ghost requires no command-line tools or downloads. Everything is built and deployed directly from your browser, streamlining the developer experience.

  3. Blazing-fast performance - Deploy indexers (subgraphs) in seconds, drastically reducing setup time and increasing efficiency for real-time blockchain data access.

Ghost is designed to make blockchain data indexing more intuitive, seamless, and efficient for smart contract developers.

Prerequisites​

To follow along, you'll need:

  • A Ghost account to create our indexer for tracking events emitted by Zeru Finance's smart contract

Getting Started​

Navigate to the Ghost Graph dashboard and click the Create Graph button. This will open a modal where you can name your graph. Enter "Zeru Telegram" and select Bartio from the dropdown.

Create Ghost Graph

Click on the newly created graph to access the code editor.

Code Editor​

There are three essential files in the code editor that you need to understand:

  • Events.sol
  • Schema.sol
  • Indexer.sol

Events.sol​

Smart contracts emit events for users to track. In this file, we define the specific events we want to monitor. For this example, we'll track the Deposit event from Zeru's smart contract. Copy and paste this code into your events.sol file:

interface Events {
event Deposit(address indexed reserve, address user, address indexed onBehalfOf, uint256 amount, uint16 indexed referral);
}

Schema.sol​

The schema file allows us to define entities as Solidity structs. You can think of an entity as an object with multiple properties. These entities will be created and updated (covered in the next section) and can be queried after deployment.

For our purpose, we'll create a Deposit entity. Copy and paste this code:

struct Deposit {
string id;
address reserve;
address userId;
address onBehalfOf;
uint256 amount;
uint16 referral;
uint32 timestamp;
uint64 block;
bytes32 txHash;
}

Click the generate button to create the necessary files, including the indexer.sol file that you'll need to modify.

Indexer.sol​

After reviewing the events and schema, we'll work with indexer.sol, which contains the core indexing logic. Here, we register the contract addresses we want to monitor for events.

Register Zeru's smart contract using the graph.registerHandle function:

address constant ZERU_LENDING_POOL = 0x61dF6c455f1119d174F46B41a4BF39ac9Dedb4e4;

function registerHandles() external {
graph.registerHandle(ZERU_LENDING_POOL);
}

We also define transformation functions for each event type (as defined in events.sol). These functions allow us to retrieve an entity, update it using event data, and save the updated entity.

For example, when a Deposit event is emitted, we perform a transformation by creating a new Deposit object, populating it with event parameters, and storing it in the respective fields within a struct. Finally, we save the entity using graph.saveDeposit(deposit), making it available for future queries.

Add this code to your indexer.sol file:

function onDeposit(EventDetails memory details, DepositEvent memory ev) external {
Deposit memory deposit = graph.getDeposit(details.uniqueId());
deposit.reserve = ev.reserve;
deposit.userId = ev.user;
deposit.onBehalfOf = ev.onBehalfOf;
deposit.amount = ev.amount;
deposit.referral = ev.referral;
deposit.timestamp = details.timestamp;
deposit.block = details.block;
deposit.txHash = details.transactionHash;
graph.saveDeposit(deposit);
}

Compile and Deploy​

After reviewing all files, compile and deploy your Ghost Graph. Within seconds, your Ghost Graph should be fully deployed and synchronized with the chain. You'll see the number of entities stored in your graph and have access to various exploration options.

Click the playground button to launch your GraphQL playground.

GraphQL Playground​

Copy this query into your playground and click the play button to query your graph:

query GetLatestDeposits {
deposits(orderBy: "timestamp", orderDirection: "desc") {
items {
amount
block
id
onBehalfOf
referral
reserve
timestamp
txHash
userId
}
}
}

This will display all the latest deposits.

Create and Configure Telegram Bot​

After setting up the Ghost Graph, let's configure the Telegram bot to announce new deposits.

  1. Clone the repository:
git clone https://github.com/tryghostxyz/ghost-tg-bot
cd ghost-tg-bot
  1. Create your environment configuration by copying the example file:
cp .env.example .env

Setting Up Environment Variables​

You'll need to configure three essential environment variables in your .env file:

  1. TELEGRAM_BOT_TOKEN

    • Create a new bot through @BotFather on Telegram
    • Send /newbot to BotFather and follow the prompts
    • Copy the provided token to TELEGRAM_BOT_TOKEN in your .env file
  2. GHOST_GRAPH_URL

    • Go to your deployed Ghost Graph
    • Click the "Deploy" button
    • Copy the provided query URL to GHOST_GRAPH_URL
  3. CHANNEL_IDS

    • Add your bot as an admin to your target Telegram channels
    • To get a channel's ID:
      • Open your channel in web.telegram.org
      • Find the channel ID in the URL after the hash (#)
      • Replace #- with -100
      • Example: #-1234564363 becomes -1001234564363
    • For multiple channels, separate IDs with commas

Your .env file should look like this:

TELEGRAM_BOT_TOKEN=1234567890:ABCdefGHIjklMNOpqrsTUVwxyz
GHOST_GRAPH_URL=https://api.ghostlogs.xyz/gg/pub/uniqueIdHere/ghostgraph
CHANNEL_IDS=<YOUR_CHANNEL_ID>

Starting the Bot​

  1. Install the required dependencies:
npm install
# or
yarn install
  1. Start the bot:
npm start
# or
yarn start

Verification and Troubleshooting​

Once running, the bot will monitor for new deposits and send notifications to your specified channels. Each notification includes:

  • Deposit amount in ETH
  • User address (with explorer link)
  • Reserve address (with explorer link)
  • Transaction hash (with explorer link)
  • Timestamp
  • A celebratory GIF

If you encounter issues:

  1. Bot not sending messages?

    • Verify the bot has admin privileges in the channel
    • Double-check your channel IDs
    • Confirm your TELEGRAM_BOT_TOKEN is correct
  2. No events being received?

    • Verify your Ghost Graph is properly deployed
    • Check that the GHOST_GRAPH_URL is correct
    • Ensure the query URL endpoint is accessible

For additional support, join the Ghost Telegram community.

Conclusion​

Congratulations! You've successfully created a system that tracks smart contract events using Ghost, queries the responses, and uses a Telegram bot to send new deposit notifications to your Telegram channel.

What other events will you track?

Join Ghost Community​

Twitter: @0xGhostLogs

Telegram: Ghost Devs TG Community

Index and Query Blockchain Data with Ghost using DYLI on Abstract

Β· 6 min read
Chris
Co-Founder of Ghost
Alex
Co-Founder of DYLI

DYLI_GHOST_ABSTRACT_Banner

Overview​

This guide is in collaboration with DYLI to showcase how they are using Ghost to index and query their smart contract data on Abstract Chain

What is Ghost and Why Does It Matter?​

Ghost is a next-generation indexing stack that empowers developers to build GraphQL APIs for extracting, transforming, storing, and querying blockchain data.

What sets Ghost apart from other indexers:

  1. Write indexers in the same language as your smart contracts (Solidity) - Since smart contract developers emit critical events for indexers, they should also have the ability to write the indexers themselves, without needing to learn new languages or tools.

  2. Fully browser-based - Ghost requires no command-line tools or downloads. Everything is built and deployed directly from your browser, streamlining the developer experience.

  3. Blazing-fast performance - Deploy indexers (subgraphs) in seconds, drastically reducing setup time and increasing efficiency for real-time blockchain data access.

Ghost is designed to make blockchain data indexing more intuitive, seamless, and efficient for smart contract developers.

What is DYLI?​

DYLI allows users to instantly launch, collect, and trade physical products, with the option to redeem them whenever they're ready.

A quick overview:

  1. Launch and Sell Drops: Quickly move from idea to product launchβ€”DYLI handles production, warehousing, and shipping

  2. Buy and Trade Instantly: Purchase limited drops directly from creators and trade them instantly on our marketplace, with creators earning royalties on every transaction.

  3. Redemption: Redeem your digital drop for the physical product whenever you're ready, with authenticity guaranteed.

  4. Unified Experience: DYLI merges primary and secondary markets, making the creating and collecting experiences more streamlined and enjoyable for everyone.

In this example, we will track on-chain events emitted by DYLI's marketplace contract. Using their testnet app, we'll take actions and verify the data indexed by Ghost in real-time.

Pre-requisites​

To follow along, you'll need to sign up for two accounts:

  • A Ghost account. This will be used to create our indexer for DYLI's marketplace contract.
  • A DYLI account. We'll use this to submit on-chain transactions on DYLI's testnet platform and verify that the transactions are being properly indexed.

Let's Get Started​

Log in to Ghost and navigate to the library.

Here, you’ll find all the Ghost Graphs created and shared by the community, including teams and other protocols.

Community Libray V1

Filter by Abstract Testnet and locate the dyliMarketplace Ghost Graph created by DYLI. Click the fork button to copy this graph into your own workspace.

Next, navigate to the Indexes tab and select the graph to open it in the code editor.

Code Editor​

There are three files that you need to familiarize yourself in the code editor.

  • Events.sol
  • Schema.sol
  • Indexer.sol

Events.sol​

Smart contracts emit events for users to track. In this case, we define the specific events we want to monitor.

While there are several events of interest, we'll focus on the OfferCreated event for this example.

Events sol

Whenever someone offers to purchase an item on DYLI, a transaction is submitted onchain and DYLI's marketplace smart contract will emit the OfferCreated event for anyone who wants to track this event.

Schema.sol​

The schema file allows us to define entities as Solidity structs. In this example, we have Listing and Offer entities. You can think of an entity as an object with multiple properties. These entities are what we'll create and keep updated (we'll cover how in the next section) and can be queried after deployment.

By storing Listings and Offers, we can track and query the latest events emitted on-chain, such as new listings or offers.

Schema sol

Indexer.sol​

Once you've reviewed the events and schema, we can move on to indexer.sol, where the core indexing logic resides. In this file, we register the contract addresses we want to listen to for events. Check out the registerHandle function for how we link these addresses.

We also define transformation functions for each event type (as defined in events.sol). These functions allow us to retrieve an entity, update it using event data, and then save the updated entity.

For example, whenever an OfferCreated event is emitted, we perform a transformation. Here, we create a new Offer object, populate it with the event parameters, and store it in the respective fields within a struct. Finally, we save the entity using graph.saveOffer(offer), enabling us to query this data later.

Indexer sol

Compile and Deploy​

After reviewing every file, compile and deploy your Ghost Graph. After a few seconds, your Ghost Graph should be fully deployed and synchronized with the chain. You'll see how many entities are stored in your graph and have access to various buttons for further exploration.

Next, click on the playground button to launch your GraphQL playground.

Deployed

GraphQL Playground​

Copy and paste the query below into your playground and click on the big pink play button to query your graph.

query LatestOffers {
offers(orderBy: "timestamp", orderDirection: "desc") {
items {
expiration
id
isActive
timestamp
tokenId
txHash
buyer
amount
offerId
}
}
}

LatestOffers

Let's keep this tab open as we will come back to it later.

Take Action on the DYLI Testnet​

Next, head to DYLI’s testnet and click the Place an offer button to make an offer.

DYLI Offer

This will trigger an on-chain transaction on the Abstract Chain that emits an OfferCreated event, which Ghost will automatically track and index!

Return to the GraphQL Playground​

Re-run the same query as before, and you'll see the latest offer appear at the top of the list. In this updated example, the latest offerId is 14, whereas it was previously 13.

LatestOffersV2

Conclusion​

That's it! You've successfully tracked smart contract events using Ghost. This example should inspire dApp developers building on Abstract, and we'd like to thank DYLI for sharing their indexer code with the Abstract community as a valuable reference for others.

Join Ghost and DYLI's social media and community​

Twitter: @0xGhostLogs // @DYLI

Telegram: Ghost Devs TG Community

How to run up a Berachain node (+ snapshot!)

Β· 3 min read
George
Co-Founder of Ghost

This is a guide on how to run a Berachain 🐻 ⛓️ node for the bArtio testnet.

Berachain nodes consist of a consensus client and an execution client. The consensus client is called beacon-kit and the execution client can be any of the ethereum clients (go-ethereum, reth, nethermind, etc). At Ghost, we run both go-ethereum and reth nodes, and for this guide we'll use reth.

You can run a node that syncs from genesis if you want, or you can use a snapshot that we created that'll help you sync much faster.

Install dependencies​

Make sure you your machine has go and rust installed because we'll be compiling beacon-kit and reth

  1. Install go (minimum version v1.22.4)
  2. Install rust (minimum version v1.79.0)

Download snapshot​

The Ghost team has created a snapshot you can use. The snapshot has both the consensus layer state (for beacon-kit) and the execution layer state (for reth)

sudo apt-get install lz4

mkdir bera
cd bera

wget 'https://public-snapshots.ghostgraph.xyz/bera-testnet-20240815.tar.lz4'

tar --use-compress-program=lz4 -xvf bera-testnet-20240815.tar.lz4

ls bera-snapshot
## you should see:
## beacond-data reth-data

Build the clients​

Now lets build the consensus client (beacon-kit) and the execution client (we'll use reth)

## still in bera dir

git clone https://github.com/berachain/beacon-kit/
git clone https://github.com/paradigmxyz/reth

## build beacon-kit which will serve as the consensus client
cd beacon-kit
git checkout v0.2.0-alpha.2
make

## build reth which will serve as the execution client
cd ../reth
git checkout v1.0.1
cargo build --release

Run Reth​

cd ~/bera/reth/
./target/release/reth node \
--datadir ../bera-snapshot/reth-data \
--chain ../bera-snapshot/beacond-data/config/eth-genesis.json \
--instance 2 \
--http \
--ws

If you have cast installed you can confirm the node is using the snapshot data

$ cast bn -r 'http://localhost:8544'
1517942

Run Beacon-kit​

cd ~/bera/beacon-kit

## generate priv_validator_key.json and priv_validator_state.json files
## we'll use the beacon-kit init command
## in a temp location and extract just these files

./build/bin/beacond init --home tmp-bera --chain-id 80084 tmp-bartio
cp tmp-bera/config/priv_validator_key.json ../bera-snapshot/beacond-data/config/
cp tmp-bera/data/priv_validator_state.json ../bera-snapshot/beacond-data/data/

## now let's run the consensus client

export [email protected]:26656,[email protected]:26656,[email protected]:26656,[email protected]:26656,[email protected]:26656,[email protected]:26656,[email protected]:26656,[email protected]:26656,c28827cb96c14c905b127b92065a3fb4cd77d7f6@testnet-seeds.whispernode.com:25456,8a0fbd4a06050519b6bce88c03932bd0a57060bd@beacond-testnet.blacknodes.net:26656,d9903c2f9902243c88f2758fe2e81e305e737fb3@bera-testnet-seeds.nodeinfra.com:26656,[email protected]:26656,cf44af098820f50a1e96d51b7af6861bc961e706@berav2-seeds.staketab.org:5001,6b5040a0e1b29a2cca224b64829d8f3d8796a3e3@berachain-testnet-v2-2.seed.l0vd.com:21656,4f93da5553f0dfaafb620532901e082255ec3ad3@berachain-testnet-v2-1.seed.l0vd.com:61656,[email protected]:26656

./build/bin/beacond start \
--home ../bera-snapshot/beacond-data \
--beacon-kit.engine.jwt-secret-path ../bera-snapshot/reth-data/jwt.hex \
--beacon-kit.engine.rpc-dial-url http://127.0.0.1:8651 \
--p2p.seeds $P2P_SEEDS \
--p2p.seed_mode

Disclaimer​

This guide is being provided as is. The codebases (beacon-kit, reth) that it references are open source but are not written by this author. They may have bugs. The guide also may have bugs and may not work on your system. No guarantee, representation or warranty is being made, express or implied, as to the safety or correctness. It has not been audited and as such there can be no assurance it will work as intended, and users may experience delays, failures, errors, omissions or loss of transmitted information. Nothing in these docs should be construed as investment advice or legal advice for any particular facts or circumstances and is not meant to replace competent counsel. It is strongly advised for you to contact a reputable attorney in your jurisdiction for any questions or concerns with respect thereto. Author is not liable for any use of the foregoing, and users should proceed with caution and use at their own risk.

Connecting your GhostGraph to your Frontend

Β· 5 min read
Chris
Co-Founder of Ghost

Smart contracts store and make visible their onchain state. How can we read this state?

We can, of course, use an RPC endpoint to call the contract's view methods directly, and for many use-cases this is sufficient.

But in some cases we need to read some kind of derived state that isn't explicitly stored onchain. For example, let's say we wanted to create a leaderboard UI for an onchain app and rank by user PnL. A global sorted leaderboard isn't typically stored onchain but we can derive it by creating an indexer (using GhostGraph for example).

At a high level, the indexer ingests event data from smart contracts, transforms it, and stores it into a database that you can query. GhostGraph makes this super easy.

In this tutorial, we'll leverage GhostGraph to build an indexer that automatically ingests and transforms contract events and then we'll hook it up to a frontend UI.

Development Life Cycle

Project Context​

For this tutorial we'll reference SuperBlurBattlerz which is an onchain app that runs on Blast L2.

Pre-requisites​

If you haven't already, sign up for a new GhostGraph account here.

Need Help?​

If you are stuck at any time, feel free to join our Telegram for help.

Visual learner?​

Check out our YouTube video that walks you through the steps in less than 3 minutes!

All Set? Let's Go!​

Log in to GhostGraph. Once logged in, navigate to the Library tab. Here, you'll see existing community graphs that you can fork into your workspace as templates.

Library

Click on the fork button to create a copy of this GhostGraph in your own workspace.

Click_To_Editor

Go to the Indexes tab and click on the box to navigate to the code editor.

Code Editor​

Events.sol​

Smart contracts emit events for users to consume. Here, we define the events we want to track.

Events

Schema.sol​

The schema file allows us to define entities as Solidity structs. Here, we have User, Race, and Bet entities. Think of an entity as an object with many properties. Entities are the things that we'll create and keep updated (we'll see how exactly in the next section). Entities are what we can query after deployment. So for example, by storing users and their PnL we'll be able to use GraphQL to sort all users by descending PnL.

Schema

Indexer.sol​

Once you've reviewed the events and schema, we can move on to indexer.sol where the core indexing logic will reside. In this file, we register the contract addresses that we want to listen to events from. Take a look at the registerHandles function.

We also write transformations that are called for each different type of event (remember we defined events in events.sol). Inside these functions we can get an entity, update it based on the values from the event and then save the entity.

Go ahead and review the transformations to understand them.

Indexer

Compile and Deploy​

After reviewing every file, compile and deploy your GhostGraph. After a few seconds, your GhostGraph should be fully deployed and synchronized with the chain. You'll see how many entities are stored in your graph and have access to various buttons for further exploration.

Next, click on the playground button to launch your GraphQL playground.

Deploy

GraphQL Playground​

Copy and paste the query below into your playground and click on the big pink play button to query your graph.

If you'd like, explore more and change queries to query different entities stored in the graph.

query GetAllUsers {
users(
orderBy: "winningAmount",
orderDirection:"desc",
limit: 100
) {
items {
totalBetAmount
winningAmount
id
}
}
}

Playground

Now that we have our backend set up with our indexer/graph ready, let's hook it up with our frontend.

Frontend Setup​

  • Download the frontend repo on GitHub
  • Open the command line and run git clone https://github.com/chrischang/gg-graphql-template.git
  • Follow the README.md to install the dependencies and clone the environment

Environment Variables​

There are two environmental variables that you need:

  1. REACT_APP_GHOST_GRAPH_URL
  2. REACT_APP_GHOST_GRAPH_KEY

REACT_APP_GHOST_GRAPH_URL​

In the code editor, after the indexer has been deployed and synced, click on the Query button to get your query URL.

Query

REACT_APP_GHOST_GRAPH_KEY​

Generate your key on the main GhostGraph dashboard.

key

All Together​

Your .env file should look like this:

REACT_APP_GHOST_GRAPH_URL=`https://api.ghostlogs.xyz/gg/pub/<queryUrl>/ghostgraph`
REACT_APP_GHOST_GRAPH_KEY=`<apiKey>`

(Optional) If You Made Modifications to Your GhostGraph, Follow These Steps​

Copy schema.graphql​

In the GhostGraph code editor, find the schema.graphql file. Copy the entire file and replace the content of the schema.graphql file in your frontend repository.

graphql_schema

Regenerate queries.tsx​

Generate the queries.tsx file by running npm run codegen. This will generate a queries.tsx file in the graphql/generated folder, which includes:

  • Typings
  • React hooks

You can use these to build your frontend.

Let's run the app​

Run PORT=3001 npm run start in the command line of the frontend repo. This should automatically open a browser tab and navigate to http://localhost:3001. If not, manually open a tab and navigate there.

Voila​

Congratulations! You've successfully created an application connecting GhostGraph to your frontend, showcasing the PNL Leaderboard.

leaderboard

Closing Comments​

What other features or improvements would you like to see on this leaderboard?

Join our Telegram and share your ideas with us!.

Welcome To Ghost

Β· One min read
Chris
Co-Founder of Ghost
George
Co-Founder of Ghost

Welcome to Ghost! We are rebuilding the indexing stack including two products:

  1. GhostGraph: Our blazingly fast way to spin up an index (subgraph) for smart contracts. All in Solidity!

  2. GhostLogs: Permissionless edit mode that enables anyone to define and emit custom gasless events even after contract deployment.

We are glad that you found us because you are still early