1. 久十比特币首页
  2. 加密货币

如何使用 The Graph 的子图轻松访问 Tornado Cash 数据

大家好,我将分享我使用 The Graph 的经验,它可以帮助我通过编写几行代码来实时访问任何智能合约的数据。

首先,什么是The Graph?
The Graph 是一种分散式协议,可以访问由分散式“索引器”索引、由“策展人”策划并由“委托人”赞助的区块链(智能合约)数据。您可以在 thegraph.com 上阅读有关该协议的信息

币安Binance

币安Binance

全球顶级交易所,手机即可买比特币,10元买比特币,1元买NFT!!!

其次, Graph 也是一种有助于创建 ETL(提取转换加载)过程(称为“子图”)的技术,该过程将收集您需要的数据,将其存储在数据库中并通过 GraphQL 访问。

第三,您不需要运行任何特定的硬件、区块链存档节点、索引器等,因为您可以使用 The Graph 基础设施的现有提供者并将您的子图部署到其中之一。

所有这些点使得子图对 Web3 开发人员、分析师或研究人员具有吸引力。因此,让我们言归正传。

如何从子图开始

可以使用命令行实用程序 graph-cli 创建子图样板代码。要安装它运行

npm 安装-g @graphprotocol/graph-cli

或者

纱线全局添加@graphprotocol/graph-cli

然后,如果您使用指定的参数运行命令:

graph init tornado_subgraph /path/to/new/project/tornado --protocol=ethereum --product=hosted-service --allow-simple-name --contract-name TornadoContract --from-contract=0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF --索引事件--start-block=17000000 --network=mainnet

您还可以将参数“start-block”更改为您实际想要开始的块。例如,它可能是部署合约时的块。你可以去 Etherscan,通过地址 0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF 搜索这个合约,然后滚动到第一笔交易。块号是9117720。

作为此命令执行的结果,我们将获得可以部署到任何子图托管提供商的项目文件夹。但在这种情况下,数据将仅受事件发出的变量的限制。

“事件发出的变量”是什么意思

如果你打开这个智能合约的 solidity 代码,你会看到一些 Contract 类,其中包括一些功能,如存款取款:

function deposit(bytes32 _commitment) external payable nonReentrant { 
require(!commitments[_commitment], "承诺已提交");

uint32 insertedIndex = _insert(_commitment);
承诺[_commitment] = true;
_processDeposit();

发出存款(_commitment,insertedIndex,block.timestamp);
}
function withdraw(bytes calldata _proof, bytes32 _root, bytes32 _nullifierHash, address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) external payable nonReentrant {
require(_fee <= denomination, "费用超过转账价值");
require(!nullifierHashes[_nullifierHash], "该票据已被消费");
require(isKnownRoot(_root), "找不到你的默克尔根"); // 确保使用最近的
require(verifier.verifyProof(_proof, [uint256(_root), uint256(_nullifierHash), uint256(_recipient), uint256(_relayer), _fee, _refund]), "Invalid withdraw proof") ;

nullifierHashes[_nullifierHash] = true;
_processWithdraw(_recipient, _relayer, _fee, _refund);
发出提款(_recipient,_nullifierHash,_relayer,_fee);
}

您可以注意到,在这些函数的末尾,将发出存款/取款事件。这意味着括号中的变量将保存在我们全新的子图项目(我们刚刚生成的)可以轻松访问的日志中。这些事件按以下方式描述:

事件存款(bytes32 索引承诺,uint32 leafIndex,uint256 时间戳);
事件取款(地址,bytes32 nullifierHash,地址索引中继器,uint256 费用);

如果这些变量是您唯一需要的东西,您只需将子图部署到子图托管平台即可。您将获得可以使用以下查询调用的 GraphQL 端点:

{
取款(第一:10){
id

nullifierHash
中继器
费用
blockNumber
blockTimestamp
transactionHash
}
}

这是关于如何将子图部署到 Chainstack 平台的手册和演示,但您也可以使用任何其他平台。

现在是时候看看我们通过“graph init”命令实际生成的内容了。要控制子图行为,您需要使用 3 个文件。

首先——名为“subgraph.yaml”的清单。此代码将如下所示:

specVersion: 0.0.5 
schema:
file: ./schema.graphql
dataSources:
- kind: ethereum
name: TornadoContract
network: mainnet
source:
address: "0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF"
abi: TornadoContract
startBlock: 17000000
映射:
kind: ethereum/even ts
api版本:0.0。 7
语言:wasm/assemblyscript
实体:
-存款
-取款
abis:
-名称:TornadoContract
文件:./abis/TornadoContract.json
事件处理程序:
-事件:存款(索引字节 32、uint32、uint256)
处理程序:handleDeposit
- 事件:Withdrawal(地址,bytes32,索引地址,uint256)
处理程序:handleWithdrawal
文件:./src/tornado-contract.ts

重要的是:链/网络、startBlock、事件名称和源路径。一切都很直观。您可以按原样保留此文件。

其次,schema.graphql——这个文件描述了我们的事件数据将如何存储。该智能合约默认生成的文件如下所示:

type Deposit @entity(immutable: true) { 
id: Bytes!
承诺:字节!# bytes32
叶索引:BigInt!# uint32
时间戳:BigInt!# uint256
块号:BigInt!
块时间戳:BigInt!
transactionHash:字节!
}

type Withdrawal @entity(immutable: true) {
id: Bytes!
to: 字节!# 地址
nullifierHash:字节!# bytes32
中继器:字节!# 地址
费:BigInt!# uint256
块号:BigInt!
块时间戳:BigInt!
transactionHash:字节!
}

如果您想添加任何内容,可以直接在此文件中进行修改。但它只是描述了如何存储数据,而不是如何获取数据。在这里您可以找到解释子图模式的指南。

第三,src/tornado-contract.ts——这个文件包含了如何从事件中获取数据(不仅仅是事件!)以及如何将其放入我们上面刚刚描述的表中的实际逻辑。该文件如下所示:

import { 
Deposit as DepositEvent,
Withdrawal as WithdrawalEvent
} from "../generated/TornadoContract/TornadoContract"
import { Deposit, Withdrawal } from "../generated/schema"
import { Address, BigInt } from "@graphprotocol/graph-ts "
import { TornadoContract } from "../generated/TornadoContract/TornadoContract"

导出函数 handleDeposit(event: DepositEvent): void {
let entity = new Deposit(
event.transaction.hash.concatI32(event.logIndex.toI32())
)

entity.commitment = event.params.commitment
entity.leafIndex = event.params.leafIndex
entity.timestamp = event.params.timestamp

entity.blockNumber = event.block.number
entity.blockTimestamp = event.block.timestamp
entity.transactionHash = event.transaction.hash

entity.save()
}

导出函数 handleWithdrawal(event: WithdrawalEvent): void {
let entity = new Withdrawal(
event.transaction.hash.concatI32(event .logIndex.toI32())
)
entity.to = event.params.to
entity.nullifierHash = event.params.nullifierHash
entity.relayer = event.params.relayer
entity.fee = event.params.fee

entity.blockNumber = event. block.number
entity.blockTimestamp = event.block.timestamp
entity.transactionHash = event.transaction.hash

entity.save()
}

如您所见,它只是将“事件”变量字段中的数据复制到存款/取款对象中的适当占位符中。所有这些代码都已生成,无需任何更改即可部署!

但是,如果我们需要与 Tornado Cash 相关的每笔交易的更多信息怎么办?例如,没有关于将他们的“钱”发送到 Tornado Cash 智能合约的地址的信息。让我们在几行代码中添加它!

如何使用 The Graph 的子图轻松访问 Tornado Cash 数据

你需要在这里知道一件事。当您获得“事件”变量时,它还包含除了发出的参数之外的更多信息。可以轻松提取的完整数据实体是:

类事件 {
地址:地址
logIndex:BigInt
transactionLogIndex:BigInt
logType:字符串 | 空
块:块
事务:事务
参数:Array<EventParam>
收据:TransactionReceipt | null
}

class Block {
hash: Bytes
parentHash: Bytes
unclesHash: Bytes
author: Address
stateRoot: Bytes
transactionsRoot: Bytes
receiptsRoot: Bytes
number: BigInt
gasUsed: BigInt
gasLimit: BigInt
时间戳: BigInt
难度: BigInt
totalDifficulty: BigInt
大小: BigInt | 空
baseFeePerGas: BigInt | 无效的
}

class Transaction {
hash: Bytes
index: BigInt
from: Address
to: Address | 空
值:BigInt
gasLimit:BigInt
gasPrice:BigInt
输入:Bytes
nonce:BigInt
}

class TransactionReceipt {
transactionHash:Bytes
transactionIndex:BigInt
blockHash:Bytes
blockNumber:BigInt
cumulativeGasUsed:BigInt
gasUsed:BigInt
contractAddress:Address
logs:Array<Log>
status:BigInt
root: Bytes
logsBloom: Bytes
}

class Log {
address: Address
topics: Array<Bytes>
数据:Bytes
blockHash: 字节
blockNumber: 字节
transactionHash: 字节
transactionIndex: BigInt
logIndex: BigInt
transactionLogIndex: BigInt
logType: string
removed: bool | 空
}

我想从 Transaction 实体中添加“from”和“value”。为此,我在 src/tornado-contract.ts 中添加了两行代码:

  entity.blockNumber = event.block.number 
entity.blockTimestamp = event.block.timestamp
entity.transactionHash = event.transaction.hash

// LINE#1 触发事件的地址可以通过event.transaction.from
entity.from_ = event.transaction.from // LINE#2 可以通过event.transaction.value entity.value_ = event.transaction.value entity.save()

访问Wei中交易的值


另外,我需要在 schema.graphql 文件中添加两行:

type Deposit @entity(immutable: true) { 
id: Bytes!
来自_:字节!# LINE#1
value_: BigInt!# LINE#2
承诺:字节!# bytes32
叶索引:BigInt!# uint32
时间戳:BigInt!# uint256
块号:BigInt!
块时间戳:BigInt!
transactionHash:字节!
}

是的,这很容易。现在你可以用这样一行来部署你的子图:

图部署——节点 https://api.graph-eu.p2pify.com/3a57099edc73524c2807cafeefaa82e1/deploy——ipfs https://api.graph-eu.p2pify.com/3a57099edc36235c2807cafeefaa82e1/ipfs tornado_subgraph

并从 UI 中查询数据,如下所示:

{ 
deposits(first: 10) {
id
commitment
leafIndex
timestamp
transactionHash
from_
value_
}
}

或者在命令行中:

curl -g \\ 
-X POST \\
-H "Content-Type: application/json" \\
-d '{"query":"{deposits(first: 10) { id commitment leafIndex timestamp transactionHash from_ value_}}" }' \\
https://ethereum-mainnet.graph-eu.p2pify.com/3c6e0b8a9c432532a8228b9a98ca1531d/tornado_subgraph

但是,如果您需要将一些智能合约调用结果保存为一个值怎么办?也可以从子图中运行 eth_call。我会把它留给你玩,只是添加这个名为“索引 ERC-20 代币余额”的教程,它也涵盖了这方面。

文章来源投稿,发布:Yori;文章内容仅供参考,不构成投资建议;投资者据此操作,风险自担;如若转载,请注明出处:http://www.910btc.com/1476.html

发表评论

邮箱地址不会被公开。 必填项已用*标注