Skip to content

Commit

Permalink
Merge pull request #108 from artgenexyz/autopr/107
Browse files Browse the repository at this point in the history
Add onchain storage for generative art
  • Loading branch information
caffeinum authored Jun 27, 2023
2 parents 1ff3ecd + d5fe391 commit 17219cc
Show file tree
Hide file tree
Showing 20 changed files with 1,157 additions and 14 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,7 @@ wiki/

generate-vanity.sh

.parcel-cache/
dist/


57 changes: 57 additions & 0 deletions contracts/extensions/onchain-art/ArtgeneCodeStorage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "solady/src/utils/Base64.sol";

import "../../interfaces/INFTExtension.sol";
import "../../interfaces/IRenderer.sol";
import "../base/NFTExtension.sol";

import "./ArtgeneScript.sol";

abstract contract ArtgeneCodeStorage is
NFTExtension,
INFTURIExtension,
IRenderer
{
Artgene_js public immutable script;

constructor(
address _nft,
address _artgeneScriptAddress
) NFTExtension(_nft) {
script = Artgene_js(_artgeneScriptAddress);
}

function tokenURI(
uint256 tokenId
)
public
view
virtual
override(INFTURIExtension, IRenderer)
returns (string memory)
{}

function render(
uint256 tokenId,
bytes memory optional
) public view virtual override returns (string memory) {}

function tokenHTML(
uint256 tokenId,
bytes32 dna,
bytes memory optional
) public view virtual override returns (string memory) {}

function supportsInterface(
bytes4 interfaceId
) public view override(IERC165, NFTExtension) returns (bool) {
return
interfaceId == type(INFTURIExtension).interfaceId ||
interfaceId == type(IRenderer).interfaceId ||
super.supportsInterface(interfaceId);
}

}
187 changes: 187 additions & 0 deletions contracts/extensions/onchain-art/ArtgeneScript.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/Strings.sol";
import "solady/src/utils/Base64.sol";

// See full template: https://github.com/artgenexyz/generative-template
//
// ((window) => {
// let dna =
// new URLSearchParams(window.location.search).get("dna") ||
// window.dna;

// if (!dna) {
// // example: 0xde4b0d963091d3b0a9c9604784c0d9df49e4261df639643cc07185e78bb930ab
// // random 64 chars of abcd...1234 in hex
// dna =
// "0x" +
// Array(64)
// .fill(0)
// .map(() => "0123456789abcdef"[(Math.random() * 16) | 0])
// .join("");
// }

// // Seeded PRNG
// function xmur3(str) {
// for (var i = 0, h = 1779033703 ^ str.length; i < str.length; i++) {
// h = Math.imul(h ^ str.charCodeAt(i), 3432918353);
// h = (h << 13) | (h >>> 19);
// }
// return function () {
// h = Math.imul(h ^ (h >>> 16), 2246822507);
// h = Math.imul(h ^ (h >>> 13), 3266489909);
// return (h ^= h >>> 16) >>> 0;
// };
// }

// function sfc32(a, b, c, d) {
// return function () {
// a |= 0;
// b |= 0;
// c |= 0;
// d |= 0;
// var 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;
// };
// }

// const hash = xmur3(dna);

// // Pad seed with Phi, Pi and E.
// // https://en.wikipedia.org/wiki/Nothing-up-my-sleeve_number
// const _rand = sfc32(0x9e3779b9, 0x243f6a88, 0xb7e15162, hash());

// window.rendered = false;
// const preview = () => {
// window.rendered = true;
// };
//
// const rand = (a, b) => {
// if (Array.isArray(a) && b === undefined) {
// return a[Math.floor(_rand() * a.length)];
// }
// if (a === undefined) {
// return _rand();
// } else if (b === undefined) {
// return _rand() * a;
// } else {
// return _rand() * (b - a) + a;
// }
// };
//
// window.rand = rand;
// window.preview = preview;
//
// window.fxrand = rand;
// window.fxpreview = preview;
//
// window.genome = [];
//
// const evolve = (name, value) => {
// const genome = window.genome;
// const gene = genome.find((g) => g.name === name);
//
// if (!gene) {
// genome.push({
// name,
// value,
// });
// } else {
// gene.value = value;
// }
//
// return {
// name,
// value,
// };
// };
//
// window.dna = dna;
//
// window.Artgene = {
// dna,
// genome: window.genome,
// rand,
// preview,
// evolve,
// };
// })(window);

// ((document) => {
// // <title>artgene.xyz</title>
// // <style>
// // html,
// // body {
// // margin: 0;
// // padding: 0;
// // }

// // main {
// // width: 100vw;
// // height: 100vh;
// // display: flex;
// // }

// // canvas {
// // display: block;
// // margin: auto;
// // max-width: 100%;
// // max-height: 100%;
// // /* for canvases bigger than screen size */
// // object-fit: contain;
// // }
// // </style>

// // Create the title element
// var title = document.createElement("title");
// title.textContent = "artgene.xyz";
// document.head.appendChild(title);

// // Create the style element
// var style = document.createElement("style");
// style.textContent = `html,body{margin:0;padding:0}main{width:100vw;height:100vh;display:flex}canvas{display:block;margin:auto;max-width:100%;max-height:100%;object-fit:contain}`;
// document.head.appendChild(style);
// })(document);

contract Artgene_js {
// minified, gunzip and hex encoded
bytes private constant _SCRIPT =
hex"1f8b08085f908764000361727467656e652e6d696e2e6a73006d54db6ee33610fd1557c02ec84a5175b36c49a18120e85b0b142dfab4c862196b64692b91063dbe55d6bf7728d949bac90b450e8767ce191d928158f52de04c0905c7d9df7ffef61748b3aeff9046763b067eabd7121badfcdd18e7fe069039a5920ebf5cc0a749d154ec27c57b259ce0e4b80fc6c8334b13ee574ddbb280fb9ddc32c6b858394118c5c93c5d2c33f9bc2ea172befc2eb1f68d54a5ee18ff394c2fc113e7fe77dd28e6387ca8f66a6dabcf9001ef2b6dd8411ae21a7828c2c5220be27811c45f8926a80dd685ba7f9dba2eef518c059a6edf32a4b4752dcda32ee10199e25e9cc451162ee379cc0b14787f1fc6175cad56613618c0bd51b35b7df623d498967a5194a4cb289a070b8bf02e21f6e2284d93659605192f264cda13d3694e6330bc6a940c3ce5a12779ffbe3c5c4450283ba01da41d6c2f8c60e0aa4bc05d79a1b0906e485f10eaab22f8ac50025d46d2624ee15164144e2243bb740d85afc48ce5c37f49a22cc9d24594a5c3b0d66a8754c3b6ab98168d902c4ae74912cf1769e6a5c1328ce74bea451c2c932c4bb334f20ce3bc00df802ac140292ad9eee07abe14d60afd9b5d347b18aebb5ad826d80472d56825bfd94d9602fef9b31242ece958d528285fda04938daa5693411af2d1cd04fc692018f8f010e50d40b46694f131ec8834e6bc8d307507dc8561b002c9b842d3646be0d0c0519434af4e2fe1eaf47663034a7720be3c5db5b682d1efb65aa7b514b79ce2d677499748958c24acc057b2b35214f5d65e39c37be96ff7bb9af5762757de41b67bc871e057d2fe18107875f38f695601dd5fa1e8fb60904a83e8294029138dfcc6c7b38a72ed5dd5e4a50707dd1e206f8781b32351d4475e5896fd743fe9a6199008bfb6d081a207031b6cc1e185f2114ef8a815525838722aeb9fceff3ac4a20659fa72bb25673cd64d5b5ad759407c0fb8c3f30888ff03fc5663d77acfba3cf79d349b46e541b19565d9a84d1e0c9d6c547f6c4aacf330080ec7a2866653e3b8a88bb2d96d5b79ceab164ec35aaa83dcf5b7d8333d83ff145748b9474df3d3dd0bd4a771f98af6a9d0cfdf618d77558339fd4ba4c2c3b78f0422a706967abdb7aa78f11f70fed8eb8f050000";

function version() external pure returns (string memory) {
return "0.1.0";
}

function getBase64() public pure returns (bytes memory) {
return bytes(Base64.encode(_SCRIPT));
}

function getString() public pure returns (string memory) {
return string(_SCRIPT);
}

function getInjectScript(
uint256 tokenId,
bytes32 dna
) public pure returns (bytes memory) {
// window.dna = "$dna"; window.tokenId = "$tokenId";
return
bytes(
string.concat(
'window.dna = "',
Strings.toHexString(uint256(dna), 32),
'";',
'window.tokenId = "',
Strings.toString(tokenId),
'";'
)
);
}
}
144 changes: 144 additions & 0 deletions contracts/extensions/onchain-art/InformationAge_js.sol

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions contracts/extensions/onchain-art/Nests_js.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./ScriptyOnchainArt.sol";

contract Nests_js is ScriptyOnchainArt {
/* (minified, gunzipped, hex-encoded) */
bytes private constant artScript =
hex"1f8b08085b799a640003736b657463682e6a7300cd58596fdb46107ef7afd83e14a02d5916294a8a21a70f715124408f206a51178161acc995b50d450a2475b885ff7b67660fee92921df7a1a89044e4ecdcc7b7a36c79c9b282a7327f9827a510397bcb163cabc4ec64b1c9935a1639ab44bd5907a7ecef13c68087d7e29ae75b5e0561f466d867d368783a83a3b5dc8bec7b9157b27e0c6222254556943f15a908decfdff5d96802ece150fd030c4f8d8db4e43b6d422e58f08de793a2337671417cc66156d1a939aa77f2819e3351d3cbcd5d08d1943c4f839d4ceb25bb60113b6731d877dfb5fb8ddc1f8ddc52c887654d8c3d60ec33877040f2e62eea5a5482ee7bdcb518fd6b8b2323691ce8b1519f352f61d7982782b6cf8d887ae988dcdcc52f599974acc46d2b3dd78a2352d565f14504d42c01f508fc8148f5715efc20b32cf0987fa7a4049126def3e4cb43596cc098af654c5a94633217dafbd0b811da10ac63268c86bb3933dc368cd1d770c786121b57a0590783013d8a2c93eb4a780d1afa55ef51c7823ae35b47e695ecd414c76554442d4ca8cb8d40754fa0ae12ad79cc45551321c9042f4da5f0e86738096c1d7f2c8ab57a7b6acfbe6224bda0958ac8eaa55809206cb97e360d65014481853afb8e5d4e0c5074fa21c2f8a61863a4da4ac7e18a43af1c136f376547fa8ac5478563c8eb74da67939127fc82a9a9317582999bab11210ae4a7918279e0b54c80bc808c05387df715e46938c3ef2b3619e2075f7a3de32132ed9d5caac63867eed43f3ae7ba517c86aa927f6141c286c4b3f5923b72c341644bef0600bee240ebcae878e10fc99ba93f5c194fd4a48a6e144ff8c96bfc7d9f3df695c3fa4b67562553e6b2963ca32e862b8567d873180f1eccf1ddc63488c618d674acc6a4235cf3b266eb42e6b5a3e2635119058037ea626ce8c01be8b7c34acb022aecbaf489084e9e210f4ab4064a95c16160871ddaf2cc5873479ebca060031b26d194b9a03164bd5221e22ea09dc937ab7b51feb2f854ec6c84d8ec66365be718a94b32cd0ca6b3625313886acd9af4214fc5dec91d05a5160b9f0155bb24ebf3aad8420be5354d87e0c912e2db6923e6ecd7c7b540ff3edfcedc315aa9295ac110b95e03c59f24578d5f6774003709b8b238e61df003de52c91fa066f0946ccaad203d9e2b9f57b73a24976cc1c0c0a38e62b9117321d24e3be84422e73b3898ab69251edc2ade0c6d8ea8e288c37c0588623a57f501ec6e722145694569d646c361239e3c02ea575a2a55eb9f65871b5831dab472ce5562f1e10a96cdac8051d672a7446ef26ba5f64a660fc5a02680c7864b27025802938d1eb05ef8d19fb26f3135334724c12210942c358a8cf12e34898c26064f744a807e63423bc71bc52e720d83023af7a6b22cdeead2f0b6ce83c412fccd87b2cdd33f37558d4d512144723b3d12fb5ef3217642c6de7a63d2644be70bdcc8d0956bba6c4d260ca836911dc762fc1c51134fe0c21b3758a03e4ff649c7ea4b3b9c07d73c8dd93a1530c91ad09c12a4722b5391be27fa6f0062e091453d6f8e5b752b5673426f3579ce0a7f06801f5f5e9a6b125fc7c3d0ef8c8c573576865db1cca16d60295507c3771b4fa4745b5943abd8a13ed2ebd7e1d9ba2a968e0d727a34f23959ab9f23b5dfcc1c1667cd39ac7332795e676b461c9dcf4985e84ad8f6c56922fb889b21ca5c2392ba33376ab7af8fb052de621a5b19c1df900ac447b0ec34e8ac3e6e493448dcb80e127fe085e5f2928b7defb8dbab67d81f47f949ddd7a8f079d0f197a502b21c9e76655f1780d1e34a39422e1834dd75b03ad1b1eae06f24bc455f511cfa81f6df27f3d9c00f0717427066613812e0ff2030fc3130181f09af41320d8ba8bb4170f5adf7cc8f1fd8456bd7707678fcbb2ec5568a5d40ff5ff40fd968fa0eaf120000";

constructor(
address _nft,
address _artgeneScriptAddress,
address _ethfsFileStorageAddress,
address _scriptyStorageAddress,
address _scriptyBuilderAddress
)
ScriptyOnchainArt(
_nft,
_artgeneScriptAddress,
_ethfsFileStorageAddress,
_scriptyStorageAddress,
_scriptyBuilderAddress
)
{}

function _getArtScript() internal pure override returns (bytes memory) {
/* (minified, gunzipped, hex-encoded) */
return artScript;
}
}
52 changes: 52 additions & 0 deletions contracts/extensions/onchain-art/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
## GunZip Onchain Art




## Artgene Script


Contract `ArtgeneScript` is a storage for artgene.js

How to generate it:

```bash
npx terser -m --ecma 8 scripts/artgene.js -o scripts/artgene.min.js
gzip -c scripts/artgene.min.js | xxd -p | tr -d '\n' | pbcopy
```

This copies the hex string in your clipboard. Paste it into:

```solidity
contract ArtgeneScript {
...
bytes private constant _SCRIPT =
hex'deadbeef'
}
```

`ArtgeneScript` is deployed once and most probably never updated. Other projects can connect to it and fetch its script directly.

Also see `compress.sh` for automated script doing that

## User Script

Contract `ScriptyOnchainArt` uses `scripty.sol` to fetch dependencies. This contract is an onchain code storage used per-project.

To generate hex version of your custom code, use same algorithm as for artgene.js:

```bash
gzip -c scripts/user-script.js | xxd -p | tr -d '\n' | pbcopy
```

Also see `scripts/onchain/gzip-hex.sh`

## Deploying and testing gas

```bash
FORK=mainnet hh run scripts/deploy-onchain-ext.ts && open scripts/onchain/output.html
```




Loading

0 comments on commit 17219cc

Please sign in to comment.