Solidity:如何将 bytes32 表示为字符串

chn*_*ski 11 ethereum solidity

这在其他语言中可能很简单,但我不知道如何在 Solidity 中做到这一点。
我有一个bytes32这样的0x05416460deb76d57af601be17e777b93592d8d4d4a4096c57876a91c84f4a712

不想将字节转换为字符串,而是只想将整个内容表示为字符串,例如“0x05416460deb76d57af601be17e777b93592d8d4d4a4096c57876a91c84f4a712”。
在 Solidity 中如何做到这一点?

更新:
为什么我需要这样做:基本上我连接到一个预言机,它在链外执行一些工作,最后将文件上传到 IPFS。我需要从预言机将内容标识符添加到我的合约中。预言机只能bytes32作为响应发送,因此我将其转换为多重哈希,并仅将来自预言机的digestas发送bytes32到合约。
到目前为止一切顺利,我可以在合约中重新创建多重哈希。问题是,在此之后我创建了一个ERC721(NFT)令牌,并且我必须在元数据中存储一些对 IPFS 文件的引用,该元数据只能是格式的string。这就是我现在被困住的地方。

Mik*_*rov 29

虽然 @Burt 的答案看起来是正确的(虽然没有测试),但有一种更有效的方法来解决相同的任务:

function toHex16 (bytes16 data) internal pure returns (bytes32 result) {
    result = bytes32 (data) & 0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000 |
          (bytes32 (data) & 0x0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000) >> 64;
    result = result & 0xFFFFFFFF000000000000000000000000FFFFFFFF000000000000000000000000 |
          (result & 0x00000000FFFFFFFF000000000000000000000000FFFFFFFF0000000000000000) >> 32;
    result = result & 0xFFFF000000000000FFFF000000000000FFFF000000000000FFFF000000000000 |
          (result & 0x0000FFFF000000000000FFFF000000000000FFFF000000000000FFFF00000000) >> 16;
    result = result & 0xFF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 |
          (result & 0x00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000) >> 8;
    result = (result & 0xF000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000) >> 4 |
          (result & 0x0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F00) >> 8;
    result = bytes32 (0x3030303030303030303030303030303030303030303030303030303030303030 +
           uint256 (result) +
           (uint256 (result) + 0x0606060606060606060606060606060606060606060606060606060606060606 >> 4 &
           0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F) * 7);
}

function toHex (bytes32 data) public pure returns (string memory) {
    return string (abi.encodePacked ("0x", toHex16 (bytes16 (data)), toHex16 (bytes16 (data << 128))));
}
Run Code Online (Sandbox Code Playgroud)

此代码产生大写输出。对于小写输出,只需将代码中的 7 更改为 39 即可。

解释

这个想法是使用二进制运算一次处理 16 个字节。

toHex16函数将表示为值的 16 个字节序列转换bytes16为表示为值的 32 个十六进制数字序列bytes32。该toHex函数将一个bytes32值拆分为两个bytes16块,通过toHex16函数将每个块转换为十六进制表示形式,最后0x使用函数将前缀与转换后的块连接起来abi.encodePacked

最复杂的部分是该toHex16函数的工作原理。我们逐句解释一下。

第一句话:

result = bytes32 (data) & 0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000 |
      (bytes32 (data) & 0x0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000) >> 64;
Run Code Online (Sandbox Code Playgroud)

这里我们将输入的最后 64 位右移 64 位,基本上是:

0123456789abcdeffedcba9876543210
\______________/\______________/
       |               |
       |               +---------------+
 ______V_______                  ______V_______
/              \                /              \
0123456789abcdef0000000000000000fedcba9876543210
Run Code Online (Sandbox Code Playgroud)

第二句话:

result = result & 0xFFFFFFFF000000000000000000000000FFFFFFFF000000000000000000000000 |
      (result & 0x00000000FFFFFFFF000000000000000000000000FFFFFFFF0000000000000000) >> 32;
Run Code Online (Sandbox Code Playgroud)

这里我们将两个 64 位块的最后 32 位右移 32 位:

0123456789abcdef0000000000000000fedcba9876543210
\______/\______/                \______/\______/
   |       |                       |       |
   |       +-------+               |       +-------+
 __V___          __V___          __V___          __V___
/      \        /      \        /      \        /      \
012345670000000089abcdef00000000fedcba980000000076543210
Run Code Online (Sandbox Code Playgroud)

下一句话:

result = result & 0xFFFF000000000000FFFF000000000000FFFF000000000000FFFF000000000000 |
      (result & 0x0000FFFF000000000000FFFF000000000000FFFF000000000000FFFF00000000) >> 16;
Run Code Online (Sandbox Code Playgroud)

做:

012345670000000089abcdef00000000fedcba980000000076543210
\__/\__/        \__/\__/        \__/\__/        \__/\__/
 |   |           |   |           |   |           |   |
 |   +---+       |   +---+       |   +---+       |   +---+
 V_      V_      V_      V_      V_      V_      V_      V_
/  \    /  \    /  \    /  \    /  \    /  \    /  \    /  \
012300004567000089ab0000cdef0000fedc0000ba980000765400003210
Run Code Online (Sandbox Code Playgroud)

下一个:

result = result & 0xFF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 |
      (result & 0x00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000) >> 8;
Run Code Online (Sandbox Code Playgroud)

做:

012300004567000089ab0000cdef0000fedc0000ba980000765400003210
\/\/    \/\/    \/\/    \/\/    \/\/    \/\/    \/\/    \/\/
| |     | |     | |     | |     | |     | |     | |     | |
| +-+   | +-+   | +-+   | +-+   | +-+   | +-+   | +-+   | +-+
V   V   V   V   V   V   V   V   V   V   V   V   V   V   V   V
/\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\
01002300450067008900ab00cd00ef00fe00dc00ba00980076005400320010
Run Code Online (Sandbox Code Playgroud)

本系列的最后一句话有点不同:

result = (result & 0xF000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000) >> 4 |
      (result & 0x0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F00) >> 8;
Run Code Online (Sandbox Code Playgroud)

它将奇数半字节右移 4 位,偶数半字节右移 8 位:

01002300450067008900ab00cd00ef00fe00dc00ba00980076005400320010
|\  |\  |\  |\  |\  |\  |\  |\  |\  |\  |\  |\  |\  |\  |\  |\
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \
 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \
 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
 V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
000102030405060708090a0b0c0d0e0f0f0e0d0c0b0a09080706050403020100
Run Code Online (Sandbox Code Playgroud)

因此,初始数据的所有半字节都分布在每个字节中。

现在,我们需要对每个字节x进行以下转换:

x` = x < 10 ? '0' + x : 'A' + (x - 10)
Run Code Online (Sandbox Code Playgroud)

让我们稍微重写一下这个公式:

x` = ('0' + x) + (x < 10 ? 0 : 'A' - '0' - 10)
x` = ('0' + x) + (x < 10 ? 0 : 1) * ('A' - '0' - 10)
Run Code Online (Sandbox Code Playgroud)

请注意,这(x < 10 ? 0 : 1)可以计算为((x + 6) >> 4),因此我们有:

x` = ('0' + x) + ((x + 6) >> 4) * ('A' - '0' - 10)
x` = (0x30 + x) + ((x + 0x06) >> 4) * 7
Run Code Online (Sandbox Code Playgroud)

最后声明:

result = bytes32 (0x3030303030303030303030303030303030303030303030303030303030303030 +
       uint256 (result) +
       (uint256 (result) + 0x0606060606060606060606060606060606060606060606060606060606060606 >> 4 &
       0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F) * 7);
Run Code Online (Sandbox Code Playgroud)

基本上对每个字节都执行上述计算。这

0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F
Run Code Online (Sandbox Code Playgroud)

需要右移后的掩码将原始公式中右移“丢弃”的位清零。

顺便说一句,最好在https://ethereum.stackexchange.com/上提出这样的问题