使用ethers.js直接读取智能合约中插槽内容
Posted Zero_Nothing
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用ethers.js直接读取智能合约中插槽内容相关的知识,希望对你有一定的参考价值。
我在上一篇《代理/实现模式下合约插槽索引计算》中的最后,提到了一个合约示例,(BSC 区块链,地址为:0x4BfE9489937d6C0d7cD6911F1102c25c7CBc1B5A)不知道有没有读者有兴趣去看一下,在那个示例合约中,有一段代码我摘出来:
/**
* @return adm The admin slot.
*/
function _admin() internal view returns (address adm) {
bytes32 slot = ADMIN_SLOT;
assembly {
adm := sload(slot)
}
}
modifier ifAdmin() {
if (msg.sender == _admin()) {
_;
} else {
_fallback();
}
}
/**
* @return The address of the proxy admin.
*/
function admin() external ifAdmin returns (address) {
return _admin();
}
/**
* @return The address of the implementation.
*/
function implementation() external ifAdmin returns (address) {
return _implementation();
}
可以看到,这时我们是没有办法知道它的admin地址及implement合约地址的(既没有提供view类型函数,同时还加了一个ifAdmin
限制)。怎么办呢?不急,区块链本身就是一个公开透明世界计算机,这就意味着它的数据是对所有人公开的,没有私有这个说法。私有这个限制只是对于链上合约之间交互而言,它并不代表无法从链下获取(毕竟,区块最终是存在节点计算机的硬盘上的)。
ethers.js 中有一个getStorageAt
函数,就可以直接读取插槽内容。上一篇文章提到过,智能合约的底层数据库为K/V型,不管是K还是V,都是32字节(256位)大小的。因此,我们读出来的数据也是32字节(256位),需要根据实际情况判断它到底是一个地址,一个整数还是一个布尔值。很明显,我们这两个插槽内容都是地址。
我们直接上代码:
//此文件用来获取某合约的插槽信息
const { ethers,utils } = require("ethers");
const bsc_rpc_url = "https://bsc-dataseed2.defibit.io"
const provider = new ethers.providers.JsonRpcProvider(bsc_rpc_url)
const proxy_address = "0x4BfE9489937d6C0d7cD6911F1102c25c7CBc1B5A"
const admin_slot = ethers.BigNumber.from("0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103")
const impl_slot = ethers.BigNumber.from("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc")
async function start() {
const admin_info = await provider.getStorageAt(proxy_address , admin_slot)
console.log("admin_info:",admin_info)
const admin_address = utils.getAddress("0x" + admin_info.substring(26))
console.log("admin_address:",admin_address)
console.log()
const impl_info = await provider.getStorageAt(proxy_address , impl_slot)
console.log("impl_info:",impl_info)
const impl_address = utils.getAddress("0x" + impl_info.substring(26))
console.log("impl_address:",impl_address)
}
start()
运行后输出的结果为:
admin_info: 0x0000000000000000000000005379f32c8d5f663eacb61eef63f722950294f452
admin_address: 0x5379F32C8D5F663EACb61eeF63F722950294f452
impl_info: 0x000000000000000000000000cac73a0f24968e201c2cc326edbc92a87666b430
impl_address: 0xcac73A0f24968e201c2cc326edbC92A87666b430
这里的代码我稍微解释一下,admin_slot
与impl_slot
均是从源合约中直接复制过来的(见上一篇文章的插槽索引计算)。getStorageAt
函数返回的其实是一个16进制字符串,它的长度是多少呢?上面提到读取K/V时返回的V是32字节(256 bit)大小,因此返回的字符串长度是32 * 2 + 2 = 66
(一个16进制字符串是4bit ,因此一个字节字符串的长度就是2,最后 +2 是因为前面多了一个0x)。
而我们的地址是长度是多少呢,是40位16进制(160bit),因此我们从长度索引第26开始,后面的就是地址的值了。最后我们使用getAddress
函数将它转换成一个校验后的地址(同时验证我们从字符串得到的是一个格式正确的地址,如果格式不正确转换会抛出错误)。
那我们得到的这两个地址到底对不对呢?我们下次再进行验证!!!
以上是关于使用ethers.js直接读取智能合约中插槽内容的主要内容,如果未能解决你的问题,请参考以下文章