4

I try to hash a tokenId with a seed in my smart contract. For simplicity and to avoid other errors I leave the seed out for now. I basically just want to hash a number on my contract and hash the same number on my javascript code and receive the same output. Code looks something like this on Solidity:

  function _tokenURI(uint256 tokenId) internal view returns (string memory) {
    string memory currentBaseURI = _baseURI();
    bytes32 hashedToken = keccak256(abi.encodePacked(tokenId));

    return
      bytes(currentBaseURI).length > 0
        ? string(abi.encodePacked(currentBaseURI, hashedToken, baseExtension))
        : "";
  }

which also leads to an error on client side invalid codepoint at offset. To tackle this I tried to cast bit32 to string using these functions

  function _bytes32ToString(bytes32 _bytes32)
    private
    pure
    returns (string memory)
  {
    uint8 i = 0;
    bytes memory bytesArray = new bytes(64);
    for (i = 0; i < bytesArray.length; i++) {
      uint8 _f = uint8(_bytes32[i / 2] & 0x0f);
      uint8 _l = uint8(_bytes32[i / 2] >> 4);

      bytesArray[i] = _toByte(_f);
      i = i + 1;
      bytesArray[i] = _toByte(_l);
    }
    return string(bytesArray);
  }

  function _toByte(uint8 _uint8) private pure returns (bytes1) {
    if (_uint8 < 10) {
      return bytes1(_uint8 + 48);
    } else {
      return bytes1(_uint8 + 87);
    }
  }

though I'm not sure if this is equivalent. Code on the frontend looks like:

const hashed = web3.utils.soliditySha3(
          { type: "uint256", value: tokenId}
        );

What do I need to change in order to receive the exact same output? And what does

invalid codepoint at offset

mean?

TylerH
  • 20,799
  • 66
  • 75
  • 101

2 Answers2

0

Maybe issue is that tokenId is not uint256 or Web3, Solidity version? I did few tests with Remix IDE and I recieved the same results.
Solidity code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
 
contract Hash {
    function getHashValue_1() public view returns(bytes32){
        return keccak256(abi.encodePacked(uint256(234)));
    }
    // bytes32: 0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2
    
    function getHashValue_3() public view returns(bytes32){
        return keccak256(abi.encodePacked(uint256(10),string('StringSecretValue')));
    }
    // bytes32: 0x5938b4caf29ac4903ee34628c3dc1eb5c670a6bd392a006d0cb91f1fc5db3819
}

JS code:

(async () => {
    try {
        console.log('Web3 version is '+ Web3.version);
        // Web3 version is 1.3.0
        
        let theValueYouNeed = web3.utils.soliditySha3("234");
        theValueYouNeed = web3.utils.soliditySha3({type: 'uint256', value: '234'});
        theValueYouNeed = web3.utils.soliditySha3({t: 'uint256', v: '234'});
        // above hashed value is 0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2
        console.log('Hashed value 1 is '+theValueYouNeed);
        
        theValueYouNeed = web3.utils.soliditySha3({t: 'uint256', v: '10'},{t: 'string', v: 'StringSecretValue'});
        console.log('Hashed value 2 is '+theValueYouNeed);
        // above hashed value is 0x5938b4caf29ac4903ee34628c3dc1eb5c670a6bd392a006d0cb91f1fc5db3819
        
    } catch (e) {
        console.log(e.message)
    }
})()

I'm not sure but invalid codepoint at offset should mean that a designated value does not fall within the range or set of allowed values... So maybe there is something wrong with tokenId and you could do some tests with hardcoded values?

Peteris
  • 418
  • 2
  • 13
0

You get the invalid codepoint error because you mix string and byte data when you call abi.encodePacked(currentBaseURI, hashedToken, baseExtension)).

When Javascript gets the return value from the contract it expects a UTF8 string, but inside your hashedToken you have byte values that are not valid for a UTF8-encoded string.

This kind of error might be "intermittent". It might happen in just some cases. You're lucky to see it during development and not in production.

How to fix it?

You are on the right track converting the hash result to a string. There is an alternative way to do it in this answer which uses less gas by using only bitwise operations.

To convert the hex value in Javascript you can use web3.utils.hexToNumberString().

Iñigo González
  • 3,735
  • 1
  • 11
  • 27