本文介绍了多种从智能合约地址获取交易的方法,包括手动查询、使用中心化服务和构建自定义解决方案。通过示例代码,读者将学习如何通过web3.py和ethers.js等库实现区块链上交易的提取,并使用Etherscan、Dune Analytics、Alchemy和QuickNode等服务来简化这一过程,适合希望深入了解区块链数据查询的开发者。
在本教程中,你将学习几种查询和检索合约地址的所有交易的技术。
如果你不知道从哪里开始,获取智能合约地址的所有交易可能会很繁琐且耗时。在本教程中,你将学习几种查询和检索合约地址的所有交易的技术。
我们将研究手动获取智能合约交易,使用集中服务如 Etherscan 或 Dune Analytics,甚至构建你自己的解决方案,使用由像 Alchemy(Alchemy SDK)和 Quicknode 等公司提供的流行 SDK 和库。
如果你是这一切的初学者,可以查看我们在 Cyfrin Updraft 上的 Solidity 智能合约开发课程,完全免费。
让我们开始吧!
由于区块链存储数据的方式,直接查询节点以获取智能合约地址的所有交易是困难的,但这是Web3 开发者需要学习的一项关键技能。以太坊等区块链本身并不是关系数据库,因此不设计为易于查询。它们是一系列相互连接的区块,类似于链表。
因此,如果你只有一个节点,你需要“穷举”遍历每个区块,收集所有交易的清单,并检查交易是否来自或发送到你正在查找的地址。此外,你还需要一个存档节点- 一个配置为构建所有历史状态的以太坊客户端实例。
不过幸运的是,许多服务已经构建了优化的解决方案,将这些交易存储和索引在数据库中,方便你进行查询。
我们将查看每个示例,以查询智能合约地址的交易,并在出现新交易时获得通知。
我们已将所有示例整理到一个GitHub 仓库供你探索!
在一些情况下,智能合约审计员和安全研究人员需要此信息以了解更多有关协议的内容:
还有一些情况下,Web3 dapps 开发人员需要向其用户展示此信息,或者 DeX 需要此信息以在其平台上执行特定操作。
无论使用场景如何,从智能合约地址获取历史和当前交易都是处理区块链数据时非常有用的。
让我们开始吧!
在本例中,我们将使用以太坊主网 Aave 代币,因为它是一个代理合约,我们还可以展示一些事件查询方面的内容,比如“被治理升级了多少次”。
首先,让我们了解如果我们手动进行操作,如何获取区块链交易往返于智能合约。
如前所述,直接查询区块链获取智能合约交易无疑是我们在本文中探索的所有解决方案中耗时最长的,但在理解交易的工作机制时也是最具有启发性的。
以下是使用 web3.py 和 ethers.js 进行交易穷举的示例 - 这两种是迄今为止最受欢迎的 web3 库。
让我们开始加入起始区块和结束区块,以避免对你的节点发出过多的调用。
const { ethers } = require("ethers");
require('dotenv').config();
const START_BLOCK = 17950195;
const END_BLOCK = 17950197;
async function getAllTransactionsForContract(){
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL);
for (let blockNumber = START_BLOCK; blockNumber < END_BLOCK; blockNumber++){
const block = await provider.getBlock(blockNumber);
const transactions = block.transactions
for (const transaction of transactions){
console.log(transaction.hash)
}
}
}
getAllTransactionsForContract()
使用 web3.py 我们可以做到同样的事情:
import os
from web3 import Web3
START_BLOCK = 17950195
END_BLOCK = 17950197
def main():
w3 = Web3(Web3.HTTPProvider(os.getenv("RPC_URL")))
for block in range(START_BLOCK, END_BLOCK):
block = w3.eth.get_block(block, full_transactions=True)
for transaction in block.transactions:
print(transaction["hash"])
if __name__ == "__main__":
main()
正如我们上面所看到的,"穷举"方法包括逐块逐个交易的查找,直到你找到所有所需的事件,这可能需要很长时间。
而且,最糟糕的是这份清单并不详尽!如果你的地址是一个合约,合约可以在一次交易中相互进行交易,但其“接收”或“发送”地址并不会包含你感兴趣的合约。让我们看看为什么:
首先,让我们快速回顾一下一次交易对象的样子
{
"Transaction": {
"hash": "example_hash_value", // DataHexString< 32 >: 交易哈希,keccak256序列化RLP编码的交易表示。
"to": "example_address_to", // Address: 交易目标地址。
"from": "example_address_from", // Address: 交易来源地址。
"nonce": 12345, // number: 交易的nonce,确保顺序和不可重放性。
"gasLimit": "example_big_number", // BigNumber: 交易的gas限额,未使用时会退款,或不足时会消耗。
"gasPrice": null, // null | BigNumber: 每单位gas的价格,EIP-1559交易为null。
"maxFeePerGas": "example_big_number", // BigNumber: 每单位gas的最高价格,对于非EIP-1559交易为null。
"maxPriorityFeePerGas": "example_big_number", // BigNumber: 每单位gas优先费用价格,对于非EIP-1559交易为null。
"data": "example_data", // BytesLike: 交易的数据,表示合约中的调用数据。
"value": "example_big_number", // BigNumber: 交易的wei值。
"chainId": 1, // number: 交易的链ID,用于EIP-155防止重放攻击。
"r": "example_r_value", // DataHexString< 32 >: 交易的椭圆曲线签名的r部分。
"s": "example_s_value", // DataHexString< 32 >: 交易的椭圆曲线签名的s部分。
"v": 27 // number: 椭圆曲线签名的v部分,精确x坐标点并在EIP-155中编码链ID。
}
}
正如我们所看到的,我们有“来源”和“目标”字段,包含了交易的起始地址和结束地址 - 我们关心的是每个包含我们合约地址的交易。
问题是在于:
假设一个钱包与合约的功能“doStuff()”进行交互,我们称其为合约A。在这种情况下,“来源”字段将是钱包地址,“目标”字段将是我们的合约地址 - 很棒!
现在,假设在 doStuff() 函数中,合约将与另一个合约交互,称为“合约B” - 在这种情况下,“来源”和“目标”依然只是A和B,即使我们与C进行了交互。
上面的代码将错过这个合约到合约的调用!我们将会遗漏一个交易。
为了获取所有这些交易,你需要模拟每个交易,运行类似于debug_traceTransaction的东西,跟踪栈,并跟踪与其他合约交互的操作码。
这是一项非常繁琐且耗时的过程,最终使用上述代码只能为我们提供一部分所有智能合约交易的清单。
你可以编写这一切,但大多数人选择将交易索引到一个更易于查询的数据库中,或者使用使其更容易访问的服务。
幸运的是,有一些服务使我们在智能合约或地址中检索所有交易的工作变得轻松。让我们来探索它们,并理解各自的权衡。
一个来自 etherscan 的内部交易示例
获取交易(及内部交易)列表的最简单方法之一是使用像 etherscan 的区块浏览器。
对于我们的Aave代币,查看交易列表非常简单。如果我们选择internal
并切换到高级
,我们可以看到与 Etherscan 交互的合约列表。
在 Etherscan 的后端,它们正在为我们索引所有这些数据,以便我们更容易“查看”它。
它们还提供了一个高级过滤器模式,但不幸的是,我们仍然缺乏一些细节,并且获取确切的交易信息可能有点棘手。
让我们看看 Dune Analytics 如何部分解决这一问题。
Dune Analytics 是一个允许用户创建、分享和探索以太坊数据仪表板的平台。它通过允许用户查询和可视化数据,以用户友好的方式提供见解。
简单来说,它索引区块链的“信息”,使你能够像查询 SQL 数据库一样进行查询。例如,我们可以轻松查看我们 Aave 代币的所有交易。
首先,如果你还没有创建账户,请导航到 Dune analytics 并创建一个新账户。
然后,转到“查询”标签,并点击“新查询”按钮。
这将打开一个像控制台的编辑器,你可以在其中编写 SQL 查询。
复制并粘贴以下代码到文本编辑器中,然后单击“运行”:
SELECT
*
FROM ethereum.traces
WHERE
"from" = 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9
or "to" = 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9
几秒钟后,你应该会在下面的表格中看到所有交易 - 超过 345,000 条!
Dune 配备了一个称为 ethereum.traces 的表格,其中包括合约中的所有交易。上述查询将找到每一笔与 Aave 代币相关的交易,不管是发送到 Aave 代币,还是 Aave 代币发送的交易。帮助你免去之前通过最初的穷举法而必须模拟每笔交易的麻烦!
现在,如果你想快速查看一个智能合约或钱包地址的所有交易,这非常棒。你还可以使用Dune API 将其实现到代码中 - 但如果我们想要更深入的细节和灵活性呢?
在这种情况下,我们可能想使用像Alchemy SDK或Quicknode SDK。
在此,我们将使用流行的Alchemy SDK - 提供了一组方法和 API,让我们轻松获取区块链数据。
其中之一是 Alchemy 的Transfer API,它允许你轻松获取以太坊及支持的 L2(包括 Polygon、Arbitrum 和 Optimism)的任何地址的历史交易。
getAssetTransfers 方法确实类似于我们之前使用 Dune Analytics 的工作 - 再加上它将为你提供更多对所需数据的细节 - 比如外部交易和内部交易的对比,或者按合约标准过滤。
要获取我们 Aave 代币智能合约的所有交易,你可以复制以下代码:
// 设置: npm install alchemy-sdk
import { Alchemy, Network } from "alchemy-sdk";
const config = {
apiKey: "demo",
network: Network.ETH_MAINNET,
};
const alchemy = new Alchemy(config);
// Aave token on ETH Mainnet
const aaveTokenAddress = "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9";
const res = await alchemy.core.getAssetTransfers({
fromBlock: "17950195",
toAddress: toAddress,
});
console.log(res);
这将在几毫秒内打印区块 0 到最新可用区块的所有交易列表!
顺便说一句,对于此,你还可以使用Python SDK,Patrick Collins 制作的 SDK,让你更容易用 Python 操作 Alchemy API,以下是一个示例:
import os
from alchemy_sdk_py import Alchemy
ALCHEMY_API_KEY = os.getenv("ALCHEMY_API_KEY")
CHAIN_ID = 1
MY_ADDRESS = "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9"
def main():
alchemy = Alchemy(network=CHAIN_ID)
transfers = alchemy.get_asset_transfers(to_address=MY_ADDRESS)
# 这是分页版本,我们可以添加 `get_all_flag=True` 但会进行大量API调用!
print(transfers)
if __name__ == "__main__":
main()
QuickNode SDK - 提供各种模块,每个模块都具有使获取区块链数据比直接获取原始数据然后手动过滤更容易的方法。
其中一个模块称为API 模块,让你利用QuickNode Graph API进行 GraphQL 查询,只返回有价值的数据。目前,它在以太坊和 Polygon 上受支持,而 核心 RPC 函数 在所有 EVM 链上受支持。
events.getByContract 查询返回提供合约地址的智能合约的事件;查询可以格式化以根据区块号、钱包地址、类型等过滤交易。响应是分页的,使其更易于导航大型负载。
要获取我们 Aave 代币智能合约的所有交易,你可以复制以下代码:
// 设置: npm install @quicknode/sdk
import API from "@quicknode/sdk/api";
const qn = new QuickNode.API({
graphApiKey: 'QUICKNODE_GRAPH_API_KEY',
});
qn.events
.getByContract({
contractAddress: '0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9', // 在 ETH Mainnet 上的 Aave 代币
filter: {
blockNumber: {
gte: 1
},
type: {
in: ['TRANSFER'],
},
}
})
.then((response) => console.log(response));
这将在几毫秒内打印从区块号大于或等于 1 到最新可用区块的所有交易列表(以分页格式)。
恭喜你!你刚刚学习了如何从合约地址获取所有交易!
在本指南中,你学习了如何从智能合约或钱包地址获取所有交易的信息。如果你想深入了解且学习 Web3 开发,请确保查看我们在 Cyfrin Updraft 提供的智能合约开发和审计课程,完全免费!
- 原文链接: cyfrin.io/blog/how-to-ge...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!