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'