Solidity keccak256与web3 soliditySha3结果对齐及编码报错问题问询
问题解答
1. invalid codepoint at offset报错含义
你最初的代码直接将bytes32类型的哈希值拼接进字符串返回,bytes32的原始字节中包含大量不符合UTF-8编码规范的数值,客户端尝试将这段内容解析为字符串时,会碰到无法识别的Unicode码位,就会抛出这个错误。你后续增加的bytes32转十六进制字符串的思路是对的,只是转换逻辑存在错误导致和前端输出不一致。
2. 两端哈希对齐的修改方案
2.1 调整Solidity端_bytes32ToString逻辑
你当前的转换函数把十六进制的高低位顺序搞反了:单个字节的十六进制表示是高4位在前,低4位在后,你的代码先写入了低4位、再写入高4位,导致最终输出的十六进制字符串和标准实现顺序相反。修改后的函数如下:
function _bytes32ToString(bytes32 _bytes32) private pure returns (string memory) { uint8 i = 0; bytes memory bytesArray = new bytes(64); for (i = 0; i < 32; i++) { uint8 _l = uint8(_bytes32[i] >> 4); // 高4位 uint8 _f = uint8(_bytes32[i] & 0x0f); // 低4位 bytesArray[i*2] = _toByte(_l); bytesArray[i*2 + 1] = _toByte(_f); } return string(bytesArray); } function _toByte(uint8 _uint8) private pure returns (bytes1) { if (_uint8 < 10) { return bytes1(_uint8 + 48); } else { return bytes1(_uint8 + 87); } }
2.2 修改_tokenURI拼接逻辑
把原来直接拼接hashedToken的逻辑,替换为调用转换函数输出十六进制字符串:
function _tokenURI(uint256 tokenId) internal view returns (string memory) { string memory currentBaseURI = _baseURI(); bytes32 hashedToken = keccak256(abi.encodePacked(tokenId)); string memory hashedStr = _bytes32ToString(hashedToken); // 新增转换步骤 return bytes(currentBaseURI).length > 0 ? string(abi.encodePacked(currentBaseURI, hashedStr, baseExtension)) : ""; }
2.3 前端输出对齐
web3.utils.soliditySha3默认返回带0x前缀的66位十六进制字符串,而Solidity端输出的是无0x前缀的64位字符串,你只需要在前端去掉前缀即可和合约输出完全一致:
const hashed = web3.utils.soliditySha3( { type: "uint256", value: tokenId} ).slice(2); // 去掉前两位的0x前缀
3. 验证逻辑
拿tokenId = 1举例:
- Solidity端
keccak256(abi.encodePacked(uint256(1)))的正确十六进制输出为5fe7f977e71d3317419859cbff11873c1604bf42b761e69b1375f9f1b4518743 - 前端
web3.utils.soliditySha3({type:'uint256',value:1}).slice(2)输出和上面完全一致,两端对齐完成。
内容的提问来源于stack exchange,提问作者ccc1da70-5e63-4879-a4cb-adb604
相关产品推荐
相关产品推荐

