应用二进制接口编码(ABI编码)

文章介绍了Solidity中的ABI编码和解码,包括abi.encodeabi.decodeabi.encodeWithSignature的使用,并通过实例演示了如何在智能合约中处理函数调用和数据传递。

我们需要在介绍下一个信息之前,再进行一个看似随机的插曲。

但我希望你理解以下内容是什么

  • abi.encode

  • abi.decode

  • abi.encodeWithSignature

为了说明它们的重要性,让我们创建另一个智能合约,打开“调试”下拉菜单,获取某些信息。

https://static.wixstatic.com/media/61a666_57ac790548bd46f293823e0574f3e152~mv2.png


contract ExampleContract {

    function meaningOfLifeAndAllExistence()
        public
        pure
        returns (uint256) {
            return 42;
    }
}

当我们复制那个时,我们得到

0x92d62db5

这到底是什么? 这是 “meaningOfLifeAndAllExistence()” 的 函数签名。我们稍后会学习这如何得出。

每当你“调用”一个智能合约时,你实际上是发送一个附带一些数据的以太坊交易,以便智能合约知道要执行哪个函数。

让我们从另一个角度来看信息。

https://static.wixstatic.com/media/61a666_38d5431035ee47bca02b56df9b3f92f7~mv2.png


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 转换为十进制,希望这会让你明白。

当一个函数不带参数时,发送表示该函数的四个字节指示智能合约执行该函数。

但是如果数据要带一个参数,它的样子会是什么?

https://static.wixstatic.com/media/61a666_2afb4e91b2f0423ba0d719a3d8b382ec~mv2.png


contract ExampleContract {
    function takeOneArg(uint256 x)
        public
        pure
        returns (bytes memory) {
            // 我们不会对 x 做任何事情
            return msg.data;
    }
}

我们得到

0xf8689fd30000000000000000000000000000000000000000000000000000000000000007

这样的返回。f8689fd3 部分意味着调用函数 “takeOneArg”,而前面很多零的7则意味着传递数字7。

如果我们必须手动进行这项操作,情况将会非常混乱。

幸运的是,我们不需要。

看看这个。

https://static.wixstatic.com/media/61a666_687ca50bc74449e28295e2641620d6c1~mv2.png


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 编码的规范,让我们先适应它的使用。

考虑以下示例。

https://static.wixstatic.com/media/61a666_ef35fbdaa086479aa76b7ce06edceb08~mv2.png


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 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
RareSkills
RareSkills
https://www.rareskills.io/