Ethereum: 用Python链上查询 Uniswap V3 ETH/USDC 资金池资产
直接从链上获取数据,是去中心化世界最“Trustless”的方式。绕开中心化的API,不仅能获得最原始、最可信的数据,还能避免单点故障。
今天,我们就来深入探讨一下,如何像一个真正的“链上侦探”一样,直接通过代码自动化地查询Uniswap V3的ETH/USDC资金池(HLP)资产状况。
为什么选择直接与区块链交互?
在DeFi的世界里,数据就是金钱。无论是进行套利、数据分析还是风险监控,及时准确地获取资金池数据都至关重要。
- 可靠性:官方网站或第三方API可能会出现延迟、服务中断甚至数据错误。而区块链本身是最终的“事实来源”。
- 去中心化:这符合Web3的核心精神。不依赖任何中心化实体,我们的应用将更加健壮。
- 自动化:通过编写脚本,我们可以实现7x24小时不间断的数据监控,并将其集成到更复杂的交易策略或分析工具中。
核心思路:与智能合约对话
要获取链上资金池的资产状况,我们本质上是在与智能合约进行“对话”。流动性池本身就是一个智能合约,它掌管着用户存入的代币。因此,我们的目标是查询这个合约地址所持有的特定代币(ETH和USDC)的数量。
对于Uniswap V3,由于其“集中流动性”的特性,情况比V2要复杂一些。V2池子可以直接调用getReserves()
函数。但在V3中,最直接可靠的方法是查询资金池合约地址本身持有的代币余额。
整个流程可以分解为以下几个步骤:
- 连接到以太坊网络:我们需要一个节点作为我们与区块链沟通的桥梁。
- 定位目标合约:找到Uniswap V3 ETH/USDC资金池的合约地址,以及ETH(通常是WETH)和USDC的代币合约地址。
- 使用ERC-20 ABI:利用标准的ERC-20接口规范,调用代币合约的
balanceOf
函数。 - 执行查询并解析结果:传入资金池合约地址作为参数,获取返回值并进行解析。
下面是这个流程的可视化表示:
实战演练:用Python和Web3.py查询
现在,让我们用最受欢迎的以太坊Python库web3.py
来动手实践。
准备工作
首先,我们需要安装web3.py
库:
pip install web3
我们还需要一个以太坊节点的URL。我们可以使用Infura、Alchemy等服务免费获取,或者如果我们自己运行了节点,也可以使用本地地址。
关键地址信息
在主网上,一个常用的Uniswap V3 ETH/USDC (0.3%费率) 池的地址和相关代币地址如下:
- Uniswap V3 Pool (ETH/USDC 0.3%):
0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640
- WETH Token:
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
- USDC Token:
0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
核心代码
from web3 import Web3# 1. 准备工作
# 替换成我们自己的节点URL
infura_url = "https://mainnet.infura.io/v3/72a0c21cf2854a9d810d8986b88b9"
web3 = Web3(Web3.HTTPProvider(infura_url))# 检查连接
if not web3.is_connected():raise ConnectionError("无法连接到以太坊节点")print("成功连接到以太坊节点!")# 2. 定义地址和简化的ERC-20 ABI
pool_address = web3.to_checksum_address("0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640")
weth_address = web3.to_checksum_address("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")
usdc_address = web3.to_checksum_address("0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")# 标准的ERC-20 balanceOf函数ABI
erc20_abi = [{"constant": True,"inputs": [{"name": "_owner", "type": "address"}],"name": "balanceOf","outputs": [{"name": "balance", "type": "uint256"}],"type": "function",},{"constant": True,"inputs": [],"name": "decimals","outputs": [{"name": "", "type": "uint8"}],"type": "function",},{"constant": True,"inputs": [],"name": "symbol","outputs": [{"name": "", "type": "string"}],"type": "function",},
]# 3. 创建合约实例
weth_contract = web3.eth.contract(address=weth_address, abi=erc20_abi)
usdc_contract = web3.eth.contract(address=usdc_address, abi=erc20_abi)# 4. 查询余额
weth_balance_raw = weth_contract.functions.balanceOf(pool_address).call()
usdc_balance_raw = usdc_contract.functions.balanceOf(pool_address).call()# 5. 处理精度
weth_decimals = weth_contract.functions.decimals().call()
usdc_decimals = usdc_contract.functions.decimals().call()weth_balance = weth_balance_raw / (10**weth_decimals)
usdc_balance = usdc_balance_raw / (10**usdc_decimals)# 6. 获取代币符号
weth_symbol = weth_contract.functions.symbol().call()
usdc_symbol = usdc_contract.functions.symbol().call()# 7. 打印结果时
print(f" {weth_symbol} 余额: {weth_balance:.4f}")
print(f" {usdc_symbol} 余额: {usdc_balance:.2f}")
代码解析
web3.to_checksum_address
: 这是一个好习惯,可以防止因大小写错误导致的地址问题。erc20_abi
: 我们不需要完整的代币合约ABI。对于查询余额,只需要balanceOf
和decimals
这两个函数的接口定义就足够了。.call()
: 这个方法用于执行“只读”操作,它不会创建交易,因此不需要花费Gas。- 处理精度: 链上返回的余额是一个没有小数点的整数。我们需要查询代币的
decimals
(小数位数),然后进行换算,才能得到我们日常习惯的数值。WETH通常是18位,USDC是6位。
结论与实用建议
通过上述方法,我们已经掌握了直接从链上自动化监控DEX资金池资产的核心技能。这不仅让我们摆脱了对中心化服务的依赖,也为构建更高级的链上工具打下了坚实的基础。
实用建议:
- 扩展到其他资金池:这个方法是通用的。我们只需要找到目标资金池的合约地址和相应代币的地址,就可以查询任何ERC-20代币对的流动性。我们可以去Etherscan或DEX的官方分析页面查找这些地址。
- 考虑历史数据:如果我们想查询某个历史区块的资产状况,可以在
.call()
函数中传入block_identifier
参数,例如balanceOf(pool_address).call(block_identifier=18000000)
。 - 节点服务的选择:对于生产环境的应用,建议使用付费的、高可用的节点服务,并设置好备用节点,以防主节点出现问题。
- 保持学习:DeFi领域发展迅速,Uniswap V4也即将到来。保持对新技术的好奇心,持续学习,才能在浪潮中立于不败之地。