文章介绍了Solidity中的ABI编码和解码,包括abi.encode
、abi.decode
和abi.encodeWithSignature
的使用,并通过实例演示了如何在智能合约中处理函数调用和数据传递。
我们需要在介绍下一个信息之前,再进行一个看似随机的插曲。
但我希望你理解以下内容是什么
abi.encode
abi.decode
abi.encodeWithSignature
为了说明它们的重要性,让我们创建另一个智能合约,打开“调试”下拉菜单,获取某些信息。
contract ExampleContract {
function meaningOfLifeAndAllExistence()
public
pure
returns (uint256) {
return 42;
}
}
当我们复制那个时,我们得到
0x92d62db5
这到底是什么? 这是 “meaningOfLifeAndAllExistence()” 的 函数签名。我们稍后会学习这如何得出。
每当你“调用”一个智能合约时,你实际上是发送一个附带一些数据的以太坊交易,以便智能合约知道要执行哪个函数。
让我们从另一个角度来看信息。
contract ExampleContract {
function meaningOfLifeAndAllExistence()
public
pure
returns (bytes memory) {
return msg.data;
}
}
我们将返回类型更改为“bytes memory”(不必担心我们之前没有见过这个),并返回一个名为 msg.data 的变量(同样不必担心我们之前没有见过这个)。
重要的是要注意到,我们得到的返回数据字节序列是相同的!
那么发生了什么呢?
当你调用智能合约中的一个函数时,你实际上并不是在进行一个“函数调用”,而是向合约发送数据,并附带关于应该执行哪个函数的信息。
这样理解对吗?当你启动你的浏览器钱包并进行 ERC20 代币交易时,无法“远程调用” ERC20 合约的函数。函数调用只发生在同一执行上下文内。但是,将交易描述为函数是很方便的。但我们需要看清幕后发生的事情,以真正理解 Solidity。
当你“调用智能合约”时,你是在向合约发送数据,并附带执行的指令。
有许多数据编码方式,如 json、xml、protobuf 等。 Solidity 和以太坊使用 ABI 编码。
我们在这里不深入讨论 ABI 的规范。但你需要知道的是,它总是看起来像一系列字节。
函数被识别为四个字节的序列。我们最初的字节序列 (0x92d62db5) 有四个字节:92, d6, 2d, b5。
请记住,一个字节是 8 位,并且 8 位的最大值可以是 255(2^8 - 1)。一个字节在十六进制中可以从 0x00 到 0xff 转换。将 0xff 转换为十进制,希望这会让你明白。
当一个函数不带参数时,发送表示该函数的四个字节指示智能合约执行该函数。
但是如果数据要带一个参数,它的样子会是什么?
contract ExampleContract {
function takeOneArg(uint256 x)
public
pure
returns (bytes memory) {
// 我们不会对 x 做任何事情
return msg.data;
}
}
我们得到
0xf8689fd30000000000000000000000000000000000000000000000000000000000000007
这样的返回。f8689fd3 部分意味着调用函数 “takeOneArg”,而前面很多零的7则意味着传递数字7。
如果我们必须手动进行这项操作,情况将会非常混乱。
幸运的是,我们不需要。
看看这个。
contract ExampleContract {
function getEncoding(uint x)
public
pure
returns (bytes memory) {
return abi.encodeWithSignature("takeOneArg()", x);
}
function takeOneArg(uint256 x)
public
pure
returns (bytes memory) {
return msg.data;
}
}
现在我们不需要担心 ABI 编码的规范,让我们先适应它的使用。
考虑以下示例。
contract ExampleContract {
function encodingXY(uint x, uint256 y)
public
pure
returns (bytes memory) {
return abi.encode(x, y);
}
function getATuple(bytes memory encoding)
public
pure
returns (uint256, uint256) {
(uint256 x, uint256 y) = abi.decode(encoding,
(uint256, uint256));
return(x, y);
}
}
请注意我们在使用“abi.encode”和“abi.decode”。 “withSignature” 部分是在涉及函数时使用,但在这里不是这种情况。
在这个例子中,变量 x 和 y 被编码为
0x0000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000f
十进制数字被转换为十六进制,因此“5”仍然是“5”,但“15”则变成了“f”。
如果我们提前知道这是一对 uint256,我们可以使用上面截图的函数将其“解码”回一对数据。
出现在 abi.decode 第二个参数的元组是解码数据的说明。如果在这里提供了错误的数据类型或错误的元组长度,你将得到错误的结果,或者代码将会撤销。
练习题
参见 Solidity 练习营, 以了解更多关于智能合约开发和代币标准的信息。
- 原文链接: rareskills.io/learn-soli...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!