导言
本文通过详细的步骤介绍如何使用 Envio SDK 在 Berachain 网络上设置并部署一个本地索引器,帮助开发者快速掌握区块链数据的查询与分析技巧,提升开发效率。
索引 Berachain Artio 测试网与 Envio SDK
Bm 开发者小熊们!🐻
在这个简短的教程中,我们将学习如何部署一个支持 Berachain Artio 的索引器!
特别感谢 Evio 的 Denham 和 Sven 在 Github 上准备的初始资源!
为什么需要索引器? 索引器是区块链生态系统中的关键组件,承担着几个重要角色:
- 查询优化:它们将区块链数据重新结构化为便于查询的格式,大大提升了查询性能,相比区块链固有的顺序数据存储方式更为高效。
- 数据结构化:索引器将原始区块链数据转换为更易于理解和分析的格式,方便访问和分析。
- 可扩展性解决方案:通过将数据查询从主区块链基础设施中分离出来,索引器缓解了可扩展性限制,能够高效处理大数据量。
- 负载减轻:索引器减少了直接对区块链的查询负荷,保障网络性能和响应速度。
- 高级查询支持:索引器支持复杂的数据查询,满足了超出简单区块链交易的复杂操作需求。
- 实时数据提供:它们支持需要即时数据馈送的应用程序,确保数据的及时交付,而不会对主区块链网络造成负担。
- 历史数据分析:索引器组织并维护可访问的历史记录,简化了对过往数据的审查。
总的来说,索引器对于维持操作效率、促进复杂数据交互以及在复杂开发环境中提高区块链平台的整体实用性是不可或缺的。
🛠️ 我们正在构建什么?
本开发者指南将引导您设置一个索引器,使用 Envio 通过 GraphQL API 查询 Berachain 网络上的任意 ERC20 合约。
本指南将分析 WETH 合约发出的所有“批准”(approval)和“转账”(transfer)事件日志,从而实时获取有关 WETH 代币持有者、余额和转账等指标的洞察。
在本指南中,我们将通过查询代币持有者的余额,分析 WETH 持有量最多的前十个账户。
完整的 GitHub 代码库可以在该存储库的指南部分中找到,路径为Index ERC20 Contract Using Envio. 。
https://github.com/berachain/guides/tree/main/apps/envio-indexer-erc20
前置条件
在开始之前,请确保您已经在电脑上安装或设置了以下内容:
以下是使用 Envio 所需的前置软件包:
- Node.js (使用 v18 或更高版本)
- pnpm (使用 v8 或更高版本)
- Docker Desktop (Docker 专门用于在本地运行 Envio 索引器)
Berachain-Envio 设置
安装并设置 Envio
您可以通过运行以下命令来安装 Envio:
sudo npm i -g envio
# Password:
# added 2 packages in 3sLet’s start by initializing the indexer and generating a boilerplate to index all events emitted by the WETH ERC20 token contract on Berachain.
这是 WETH 合约地址:0x8239FBb3e3D0C2cDFd7888D8aF7701240Ac4DcA4。
1.在您选择的目录中打开终端并运行命令 envio init
。
2. 将索引器命名为您喜欢的名称(例如, weth-berachain-indexer
) ,并指定文件夹名称(例如,weth-berachain-indexer
)
3. 选择您喜欢的编程语言,选择 Template
,然后选择 Erc20
。
envio init
# > Name your indexer: weth-berachain-envio-test
# > Specify a folder name (ENTER to skip): weth-berachain-envio-test
# > Which language would you like to use? Typescript
# > Choose an initialization option Template
# > Which template would you like to use? Erc20
# Project template ready
# Running codegen
# installing packages...
# Checking for pnpm package...
# 8.14.1
# Package pnpm is already installed. Continuing...
# Scope: all 2 workspace projects
注意:Envio 上的索引器可以用 JavaScript、TypeScript 或 ReScript 编写。在本次演示中,我们选择使用 TypeScript 作为首选语言。
项目模板已经生成,包含运行索引器所需的所有文件。
对生成的代码进行修改
通过您选择的 IDE 打开现有代码,我们使用的是 VS Code(Visual Studio Code)。
文件:./config.yaml
该文件定义了我们在 Berachain 上要索引的网络、起始区块、合约地址和事件。
将网络、起始区块和合约地址的占位符值替换为正确的值,例如:
- 网络 ID = 80085
- 起始区块 = 0
- 合约地址 = 0x8239FBb3e3D0C2cDFd7888D8aF7701240Ac4DcA4
修改后,文件应如下所示:
文件:./config.yaml
name: weth-berachain-indexer
description: ERC-20 indexer
networks:
- id: 80085 # Berachain Artio Testnet
start_block: 0
contracts:
- name: ERC20
address: "0x8239FBb3e3D0C2cDFd7888D8aF7701240Ac4DcA4" #WETH
handler: src/EventHandlers.ts
events:
- event: "Approval(address indexed owner, address indexed spender, uint256 value)"
requiredEntities:
- name: "Account"
- name: "Approval"
- event: "Transfer(address indexed from, address indexed to, uint256 value)"
requiredEntities:
- name: "Account"
- name: "Approval"
上面使用的合约地址是 Berachain Artio 测试网上 WETH 的合约地址。可以在 Berachain Explorer 上查看:https://artio.beratrail.io/token/0x8239FBb3e3D0C2cDFd7888D8aF7701240Ac4DcA4。
2.文件:./Schema.graphql
该文件保存并定义了所选事件的数据结构,例如 Approval 事件。
## Pls note - there are no changes required below for this guide, but obv changes can be made based on requirements
type Account {
# id is the address of the account
id: ID!
# approvals are a list of approvals that this account has given
approvals: [Approval!]! @derivedFrom(field: "owner")
# account balance of tokens
balance: BigInt!
}
type Approval {
# id is the owner address and spender address [owner-spender]
id: ID!
# amount is the amount of tokens approved
amount: BigInt!
# owner is the account that approved the tokens
owner: Account!
# spender is the account that is approved to spend the tokens
spender: Account!
}
3.文件:./src/EventHandlers.ts
该文件定义了当事件被触发时会发生什么,并保存要运行的代码,允许对数据处理进行自定义。
import {
ERC20Contract_Approval_loader,
ERC20Contract_Approval_handler,
ERC20Contract_Transfer_loader,
ERC20Contract_Transfer_handler,
} from "../generated/src/Handlers.gen";
import { AccountEntity, ApprovalEntity } from "../generated/src/Types.gen";
ERC20Contract_Approval_loader(({ event, context }) => {
// loading the required Account entity
context.Account.load(event.params.owner.toString());
});
ERC20Contract_Approval_handler(({ event, context }) => {
// getting the owner Account entity
let ownerAccount = context.Account.get(event.params.owner.toString());
if (ownerAccount === undefined) {
// Usually an accoun that is being approved alreay has/has had a balance, but it is possible they havent.
// create the account
let accountObject: AccountEntity = {
id: event.params.owner.toString(),
balance: 0n,
};
context.Account.set(accountObject);
}
let approvalId =
event.params.owner.toString() + "-" + event.params.spender.toString();
let approvalObject: ApprovalEntity = {
id: approvalId,
amount: event.params.value,
owner_id: event.params.owner.toString(),
spender_id: event.params.spender.toString(),
};
// this is the same for create or update as the amount is overwritten
context.Approval.set(approvalObject);
});
ERC20Contract_Transfer_loader(({ event, context }) => {
context.Account.load(event.params.from.toString());
context.Account.load(event.params.to.toString());
});
ERC20Contract_Transfer_handler(({ event, context }) => {
let senderAccount = context.Account.get(event.params.from.toString());
if (senderAccount === undefined || senderAccount === null) {
// create the account
// This is likely only ever going to be the zero address in the case of the first mint
let accountObject: AccountEntity = {
id: event.params.from.toString(),
balance: 0n - event.params.value,
};
context.Account.set(accountObject);
} else {
// subtract the balance from the existing users balance
let accountObject: AccountEntity = {
id: senderAccount.id,
balance: senderAccount.balance - event.params.value,
};
context.Account.set(accountObject);
}
let receiverAccount = context.Account.get(event.params.to.toString());
if (receiverAccount === undefined || receiverAccount === null) {
// create new account
let accountObject: AccountEntity = {
id: event.params.to.toString(),
balance: event.params.value,
};
context.Account.set(accountObject);
} else {
// update existing account
let accountObject: AccountEntity = {
id: receiverAccount.id,
balance: receiverAccount.balance + event.params.value,
};
context.Account.set(accountObject);
}
});
部署本地索引器
启动索引器并探索索引数据。
现在,通过运行 envio dev
命令在本地启动我们的索引器。
Localhost Login Screen
您的浏览器会自动打开一个本地 Hasura 控制台,地址为 http://localhost:8080/console。
进入 Hasura 控制台,输入管理员密码:testing
。
您的本地页面应该类似于以下内容:
Localhost Homepage: Hasura
探索已索引的数据
1.进入 Hasura 控制台,输入管理员密码 testing
,并在上方的栏目中导航到“Data”以探索数据。例如,您可以:
- 查看“events_sync_state”表,以查看当前索引到的区块号,监控索引进度。
- 查看“chain_metadata”表,以查看链的区块高度。
- 查看“raw_events”表,以查看所有正在被索引的事件。
如果您查看“Account”表,您将看到一个名为“balance”(余额)的列。
2. 让我们分析数据,点击上方栏目中的“API”,以访问 GraphQL 端点并查询实时数据。
3. 从那里您可以运行查询,探索诸如 WETH ERC-20 代币持有者及其相应余额等详细信息。
就是这样!我们已经从现有的 WETH 合约中索引了所需的数据。到现在为止,您应该已经学会了在 Berachain 上部署本地索引器,并从 Berachain Artio 上的指定合约中查询数据!
完整代码和存储库
1.文件:./config.yaml
name: weth-berachain-indexer
description: ERC-20 indexer
networks:
- id: 80085 # Berachain Artio Testnet
start_block: 0
contracts:
- name: ERC20
address: "0x8239FBb3e3D0C2cDFd7888D8aF7701240Ac4DcA4" #WETH
handler: src/EventHandlers.ts
events:
- event: "Approval(address indexed owner, address indexed spender, uint256 value)"
requiredEntities:
- name: "Account"
- name: "Approval"
- event: "Transfer(address indexed from, address indexed to, uint256 value)"
requiredEntities:
- name: "Account"
- name: "Approval"
2. 文件:./Schema.graphql
## Pls note - there are no changes required below for this guide, but obv changes can be made based on requirements
type Account {
# id is the address of the account
id: ID!
# approvals are a list of approvals that this account has given
approvals: [Approval!]! @derivedFrom(field: "owner")
# account balance of tokens
balance: BigInt!
}
type Approval {
# id is the owner address and spender address [owner-spender]
id: ID!
# amount is the amount of tokens approved
amount: BigInt!
# owner is the account that approved the tokens
owner: Account!
# spender is the account that is approved to spend the tokens
spender: Account!
}
3. 文件:./src/EventHandlers.ts
import {
ERC20Contract_Approval_loader,
ERC20Contract_Approval_handler,
ERC20Contract_Transfer_loader,
ERC20Contract_Transfer_handler,
} from "../generated/src/Handlers.gen";
import { AccountEntity, ApprovalEntity } from "../generated/src/Types.gen";
ERC20Contract_Approval_loader(({ event, context }) => {
// loading the required Account entity
context.Account.load(event.params.owner.toString());
});
ERC20Contract_Approval_handler(({ event, context }) => {
// getting the owner Account entity
let ownerAccount = context.Account.get(event.params.owner.toString());
if (ownerAccount === undefined) {
// Usually an accoun that is being approved alreay has/has had a balance, but it is possible they havent.
// create the account
let accountObject: AccountEntity = {
id: event.params.owner.toString(),
balance: 0n,
};
context.Account.set(accountObject);
}
let approvalId =
event.params.owner.toString() + "-" + event.params.spender.toString();
let approvalObject: ApprovalEntity = {
id: approvalId,
amount: event.params.value,
owner_id: event.params.owner.toString(),
spender_id: event.params.spender.toString(),
};
// this is the same for create or update as the amount is overwritten
context.Approval.set(approvalObject);
});
ERC20Contract_Transfer_loader(({ event, context }) => {
context.Account.load(event.params.from.toString());
context.Account.load(event.params.to.toString());
});
ERC20Contract_Transfer_handler(({ event, context }) => {
let senderAccount = context.Account.get(event.params.from.toString());
if (senderAccount === undefined || senderAccount === null) {
// create the account
// This is likely only ever going to be the zero address in the case of the first mint
let accountObject: AccountEntity = {
id: event.params.from.toString(),
balance: 0n - event.params.value,
};
context.Account.set(accountObject);
} else {
// subtract the balance from the existing users balance
let accountObject: AccountEntity = {
id: senderAccount.id,
balance: senderAccount.balance - event.params.value,
};
context.Account.set(accountObject);
}
let receiverAccount = context.Account.get(event.params.to.toString());
if (receiverAccount === undefined || receiverAccount === null) {
// create new account
let accountObject: AccountEntity = {
id: event.params.to.toString(),
balance: event.params.value,
};
context.Account.set(accountObject);
} else {
// update existing account
let accountObject: AccountEntity = {
id: receiverAccount.id,
balance: receiverAccount.balance + event.params.value,
};
context.Account.set(accountObject);
}
});
Github Repository
上述指南的完整模板可以在以下地址获取:https://github.com/berachain/guides/tree/main/apps/envio-indexer-erc20 。
Envio Template on Berachain Repo
更多去中心化应用 (dApps) 以及如何获得支持?
如果您有兴趣了解并进一步开发 Berachain,您可以尝试官方指南仓库中提供的各种示例代码库——
https://github.com/berachain/guides/tree/main/apps
Berachain 的开发者文档中还包含多个入门指南,以及 Hardhat、Foundry 等实用工具的使用教程!请使用以下链接前往文档的开发者部分。
https://docs.berachain.com/developers/
如何获得开发者支持?
⚠️ 仍然遇到问题或有疑问?前往官方 Berachain Discord,提交工单!
我们的开发者关系团队将很乐意为您提供帮助!🤝
❤️ 别忘了为这篇文章点赞 👏🏼
原创文章,作者:Rama Ai,如若转载,请注明出处:https://www.dappchaser.com/indexing-on-berachain-artio-testnet-with-envio-sdk/