Displaying IPFS and Arweave TxIDs With Stored Bytes

Kevin
Coinmonks
4 min readFeb 7, 2022

--

In our previous article I covered the 21MMpixels contract view functions that encode IPFS Base32 content identifiers (IPFS cidV1) and Arweave Base64URL transaction IDs into the structure we use to store image links for the NFT tiles in 21 million pixels. Next I will briefly explain the contracts view functions that translate the stored information back to human readable format.

The 21MMpixels contract tokenImg function is the entry point to view the image set for any of the NFTs composing the 21MMpixels image. This function begins by reading the RedeemedStruct for a given tile. The bytes1 multibase field of the RedeemedStruct determines the function we use to decode the image link. Today we’ll focus on two of the contract defined multibase possibilities, IPFS which is encoded as 0x62 (Base32 Uppercase) or 0x42 (Base32 Lowercase), and Arweave transaction IDs, encoded as 0x01.

In the case of an IPFS image link, we first call our byteArraysToBase32String function with the two bytes30 digests, the multibase, and the stored length. If the length used exceeds 240 bits (30 bytes * 8 bits / byte), we will call _bytes30ToString twice, first with the first digest, a length of 48 (240 bits divided by 5 bits / character) and the multibase, and next with the second digest, a length of (stored length minus 240) divided by 5, and the multibase. If the length used is less than 241 bits we will simply use the result of _bytes30ToString with the first digest, a size of length divided by 5, and the multibase.

The _bytes30ToString function called with 3 variables returns a bytes array representing a string in UTF8 encoding. The function creates a new bytes variable (bytesArray) with a length equal to the called length, which represents the number of characters. For Base32, we define a uint8 addand based on the multibase, equal to 65 for Base32Lower and 97 for Base32Upper. We then enter a for loop which utilizes two functions for an i from 0 to length. In each loop, we first call the function _get5BitsAsUint with the bytes30 input, and i.

The _get5BitsAsUint function creates a temporary variable from the bytes30 input (temp), left shifts (<<) the temp variable by the i from the calling function, named as position in this function, multiplied by 5. We then create a bytes30 mask of 0xf8 followed by twenty-eight zeroes, which in binary would be five ones followed by 235 zeros. We use the bitwise “and” operator (&) to combine the mask and the temp variable. Finally, we right shift (>>) temp by 235, and return a uint8 with uint8(uint240((temp))).

The result of the _get5BitsAsUint function is used to call _uintToChar with the result and the addand. This _uintToChar function returns a bytes1 representing the UTF8 character code. If the converted uint8 is less than 26 (representing a letter), it returns the uint8 plus the addand. If the converted uint8 is greater than 25 (representing a digit), it returns the uint8 plus 24. The result returned by _uintToChar is added to the bytesArray variable at a byte position of i.

The bytes returned from _bytes30ToString are converted to UTF8 strings with the function string(bytes.concat(_bytes30ToString result)).

Conversion of stored Arweave Transaction IDs functions very similarly to the IPFS conversion. I will briefly cover the differences.

The byteArrayToBase64String function uses a different _bytes30ToString function, this one called with the bytes30 digest and a length. This function calls two functions, _get6BitsAsUint, and _uintToChar with a single variable.

The _get6BitsAsUint function differs from the _get5BitsAsUint function in that it first left shifts by the position multiplied by 6 (Base64 uses 6 bits per character), the bytes30 mask starts with 0xfc, representing six ones followed by 234 zeroes, and the final right shift is by 234.

The _uintToChar function used for the Arweave Base64 conversion is called with the uint8 result (_conv) of _get6BitsAsUint and does not have an addand. This function returns bytes1 of the _conv + 65 for _conv < 26 (alpha characters “a” — “z”), _conv + 71 for _conv < 52 (alpha characters “A” — “Z”), _conv — 4 (digits “0” — “9”), _conv — 17 for _conv == 62 (“-”), and _conv + 32 for _conv == 63 (“_”).

Since all the functions used in the 21MMpixels contract for Base32 and Base64 operations are view functions, we have not necessarily gas optimized the functions. We’re happy to answer any questions and welcome any feedback.

Join Coinmonks Telegram Channel and Youtube Channel learn about crypto trading and investing

Also Read

--

--