Technical requirements for projects on Art Blocks.
<aside> 🚨 This document is intended for artists who have already entered the pipeline to launch a project on Art Blocks. If you have not yet applied, this documentation serves as a guide on what to expect.
</aside>
The Art Blocks platform hosts generative projects for the production of verifiably deterministic outputs. A generative script (for example, p5js) is stored immutably on the Ethereum blockchain for each project. When a user wants to purchase an iteration of a project hosted on the platform, they purchase an ERC721 compliant "non-fungible" token, also stored on the Ethereum blockchain, containing a provably unique "seed" which controls variables in the generative script. These variables, in turn, control the way the output looks and operates.
Each "seed," also known as a "hash string," is a hexadecimal string generated in a pseudo-random manner at the time the token is minted. Each character (0-9, a-f) represents a value from 0-15 and each pair of characters ("aa", or "f2") represents a value from 0-255.
For example:
"0x11ac128f8b54949c12d04102cfc01960fc496813cbc3495bf77aeed738579738";
This hash will be the source of entropy or variation use to determine the output of your algorithm. When your art is rendered on Art Blocks, your script will have access to a global variable called tokenData
. One of the first lines in your script should be to read in the hash from this variable.
let hash = tokenData.hash;
Included in the tokenData
is also the tokenId
. The tokenId
encodes both the project and mint.
<aside>
🚨 If you include using the tokenId
as a way to determine the outputs of your script you must disclose this as part of the release communications for your project.
</aside>
tokenId = (projectNumber * 1000000) + mintNumber
If your script needs to know the mint number or project number, it can do so like this:
let projectNumber = Math.floor(parseInt(tokenData.tokenId) / 1000000);
let mintNumber = parseInt(tokenData.tokenId) % 1000000;
When you are testing locally, this variable obviously will not be defined in your browser environment. This here is a simple function to generate valid hashes and tokenIds.
function genTokenData(projectNum) {
let data = {};
let hash = "0x";
for (var i = 0; i < 64; i++) {
hash += Math.floor(Math.random() * 16).toString(16);
}
data.hash = hash;
data.tokenId = (
projectNum * 1000000 +
Math.floor(Math.random() * 1000)
).toString();
return data;
}
let tokenData = genTokenData(123);
Art Blocks requires that all artists use an instance of the following Random class to feed all of their project's randomness. If you need to modify/customize the Random class, please flag this to our team during the review process.
class Random {
constructor() {
this.useA = false;
let sfc32 = function (uint128Hex) {
let a = parseInt(uint128Hex.substring(0, 8), 16);
let b = parseInt(uint128Hex.substring(8, 16), 16);
let c = parseInt(uint128Hex.substring(16, 24), 16);
let d = parseInt(uint128Hex.substring(24, 32), 16);
return function () {
a |= 0;
b |= 0;
c |= 0;
d |= 0;
let t = (((a + b) | 0) + d) | 0;
d = (d + 1) | 0;
a = b ^ (b >>> 9);
b = (c + (c << 3)) | 0;
c = (c << 21) | (c >>> 11);
c = (c + t) | 0;
return (t >>> 0) / 4294967296;
};
};
// seed prngA with first half of tokenData.hash
this.prngA = new sfc32(tokenData.hash.substring(2, 34));
// seed prngB with second half of tokenData.hash
this.prngB = new sfc32(tokenData.hash.substring(34, 66));
for (let i = 0; i < 1e6; i += 2) {
this.prngA();
this.prngB();
}
}
// random number between 0 (inclusive) and 1 (exclusive)
random_dec() {
this.useA = !this.useA;
return this.useA ? this.prngA() : this.prngB();
}
// random number between a (inclusive) and b (exclusive)
random_num(a, b) {
return a + (b - a) * this.random_dec();
}
// random integer between a (inclusive) and b (inclusive)
// requires a < b for proper probability distribution
random_int(a, b) {
return Math.floor(this.random_num(a, b + 1));
}
// random boolean with p as percent liklihood of true
random_bool(p) {
return this.random_dec() < p;
}
// random value in an array of items
random_choice(list) {
return list[this.random_int(0, list.length - 1)];
}
}
<aside> 🚨 Note that the class uses the prng algorithm sfc32, which was designed and coded by Chris Doty-Humphry and is public domain. More information can be found at https://pracrand.sourceforge.net
</aside>