Skip to content

Latest commit

 

History

History
200 lines (163 loc) · 5.21 KB

1-03.md

File metadata and controls

200 lines (163 loc) · 5.21 KB
Step 3: Make Metadata

Create a file called ./contracts/Metadata.sol and add contract basics

touch ./contracts/Metadata.sol
pragma solidity ^0.5.0;
/**
* Metadata contract is upgradeable and returns metadata about Token
*/
contract Metadata {

}

Create a file called ./contracts/helpers/strings.sol and add this modified strings library from Nick Johnson

mkdir ./contracts/helpers
touch ./contracts/helpers/strings.sol
/*
 * @title String & slice utility library for Solidity contracts.
 * @author Nick Johnson <arachnid@notdot.net>
 */

pragma solidity ^0.5.0;

library strings {
    struct slice {
        uint _len;
        uint _ptr;
    }

    function memcpy(uint dest, uint src, uint len) private pure {
        // Copy word-length chunks while possible
        for (; len >= 32; len -= 32) {
            assembly {
                mstore(dest, mload(src))
            }
            dest += 32;
            src += 32;
        }

        // Copy remaining bytes
        uint mask = 256 ** (32 - len) - 1;
        assembly {
            let srcpart := and(mload(src), not(mask))
            let destpart := and(mload(dest), mask)
            mstore(dest, or(destpart, srcpart))
        }
    }

    /*
     * @dev Returns a slice containing the entire string.
     * @param self The string to make a slice from.
     * @return A newly allocated slice containing the entire string.
     */
    function toSlice(string memory self) internal pure returns (slice memory) {
        uint ptr;
        assembly {
            ptr := add(self, 0x20)
        }
        return slice(bytes(self).length, ptr);
    }

    /*
     * @dev Returns a newly allocated string containing the concatenation of
     *      `self` and `other`.
     * @param self The first slice to concatenate.
     * @param other The second slice to concatenate.
     * @return The concatenation of the two strings.
     */
    function concat(slice memory self, slice memory other) internal pure returns (string memory) {
        string memory ret = new string(self._len + other._len);
        uint retptr;
        assembly {
            retptr := add(ret, 32)
        }
        memcpy(retptr, self._ptr, self._len);
        memcpy(retptr + self._len, other._ptr, other._len);
        return ret;
    }
}

Import the library into the Metadata.sol and use the strings library for all types

pragma solidity ^0.5.0;
/**
* Metadata contract is upgradeable and returns metadata about Token
*/

import "./helpers/strings.sol";

contract Metadata {
    using strings for *;
}

Add the tokenURI function that accepts a uint256 tokenId and returns a string

function tokenURI(uint _tokenId) public pure returns (string memory _infoUrl) {
    string memory base = "https://domain.com/metadata/";
    string memory id = uint2str(_tokenId);
    return base.toSlice().concat(id.toSlice());
}

Now add the function uint2str modified from oraclize

function uint2str(uint i) internal pure returns (string memory) {
    if (i == 0) return "0";
    uint j = i;
    uint length;
    while (j != 0) {
        length++;
        j /= 10;
    }
    bytes memory bstr = new bytes(length);
    uint k = length - 1;
    while (i != 0) {
        uint _uint = 48 + i % 10;
        bstr[k--] = toBytes(_uint)[31];
        i /= 10;
    }
    return string(bstr);
}
function toBytes(uint256 x) public pure returns (bytes memory b) {
    b = new bytes(32);
    assembly { mstore(add(b, 32), x) }
}

What's happening here is that we're taking a number and converting it into the UTF8 string value of that number. You can see the part that says 48 + i % 10, that's where the magic is happening. The modulo operation (%) will convert the number to a single digit. Then that digit is added to the number 48, which is where the number characters begin inside of the UTF8 character encoding. To see what I mean check here.

Run yarn compile to make sure there are no errors. Your final Metadata.sol should look like this:

pragma solidity ^0.5.0;
/**
* Metadata contract is upgradeable and returns metadata about Token
*/

import "./helpers/strings.sol";

contract Metadata {
    using strings for *;

    function tokenURI(uint _tokenId) public pure returns (string memory _infoUrl) {
        string memory base = "https://domain.com/metadata/";
        string memory id = uint2str(_tokenId);
        return base.toSlice().concat(id.toSlice());
    }

    function uint2str(uint i) internal pure returns (string memory) {
        if (i == 0) return "0";
        uint j = i;
        uint length;
        while (j != 0) {
            length++;
            j /= 10;
        }
        bytes memory bstr = new bytes(length);
        uint k = length - 1;
        while (i != 0) {
            uint _uint = 48 + i % 10;
            bstr[k--] = toBytes(_uint)[31];
            i /= 10;
        }
        return string(bstr);
    }

    function toBytes(uint256 x) public pure returns (bytes memory b) {
        b = new bytes(32);
        assembly { mstore(add(b, 32), x) }
    }
}

Commit your changes

git add . && git commit -m 'Step 3: Make Metadata'

Go to step 4