assets:Big Map 289583
metadata:Big Map 289584
renderer:8 items
bootloader:#!/bin/bash
ASSET_ADDR='KT1CNzPAzNXAgJy8bdASzZXPn2mydjhmRyTS'
ASSET_DETAILS=$(curl -s "https://api.tzkt.io/v1/contracts/${ASSET_ADDR}/storage?path=renderer")
case "$OSTYPE" in
cygwin*)
alias open="cmd /c start"
;;
linux*)
alias open="xdg-open"
;;
esac
mkdir -p viewer
cd viewer
echo "${ASSET_DETAILS}" | node -pe "JSON.parse(require('fs').readFileSync('/dev/stdin')).font" | xxd -r -p | gunzip -d > SHIFT437.ttf
echo "${ASSET_DETAILS}" | node -pe "JSON.parse(require('fs').readFileSync('/dev/stdin')).index" > index.html
echo "${ASSET_DETAILS}" | node -pe "JSON.parse(require('fs').readFileSync('/dev/stdin')).style" > index.css
echo "${ASSET_DETAILS}" | node -pe "JSON.parse(require('fs').readFileSync('/dev/stdin')).readme" > readme.md
echo "${ASSET_DETAILS}" | node -pe "JSON.parse(require('fs').readFileSync('/dev/stdin')).script" > index.js
cd - > /dev/null
echo "Done installing 0x5E1F1E on-chain viewer. Open viewer? [Y/n]"
read opn
if [[ "${opn}" != 'n' && "${opn}" != 'N' ]]; then
open viewer/index.html
fi
font:1f8b080885f11263000353484946543433372e74746600ed3d0d905d5579df39f7de77df7bbbc9becdee269b6c7e76d9dd6c7e960081861d048c1aa240ab2941c9221337928460036620621a30c6d1389994b68c038ca599295319c652ea6cabd560ad32402d76686ba9a315d1024e913a8c662464ffdebbb7e79cef7ce7e7befbde3ea0ed38d3e672f7dd9f73bfef3bdffff9ceb9176000b0108e420013efdb7edec68fffe1f6bf0060fbc4d5891b6fd975e0fd7bfe781c20bc475c7be78d771cec2f1f5a781c209e10f717ef3d70d32d856d27370044df13f76fbf69d7ed076009940076778bfb959bf6ffeede23f71efc0140e928c08e33fbf6ecdacd771dab897baf8a7dd33e71a17bb6fdc3e2d921713eb4ef9683873eb7fbca2bc5f9d300fce9fd1fbd71d7158fdd5016f80e89f3e22dbb0e1d08decb2e13f7df23daf7dfbaeb963d9fbeaff73f013e7c505c3b7ae0a3b71f4c3f2d3b74e31979ffc06d7b0e2c4f3f71523c2fdb2f06794becc3efb96be4431d97be0e6d1590fffee69ca797c8df7f1fd9f7c374ba2a3ad02e6f148103fe13cfb443557609d269901d64e0ffdb25afb0805d0a9be909f14f504e20202815d93d10c92bf25ae8fc1641b52b6143c1aaf3c4cf55f4e0f6ebdeb10d36336029d2d02e9ee907d62911c5e24cf61458f443382e0fa3f74b2a05f129022cfd7e9a9620ada963b573fdcbf42f707b4fef5f0486d799dbce792eef7a76e70dda307d8f67cee57120f6742e435f933d70f0b4425396be56db36ea07c22926e94c494bdac8db280157bfeef5fa7f4cf424ab52fe7dd6f4bec5e662f29f61cfd85681f7a44b5be0fde55a4bb3f473712f30d7085ad8902aff2f53fd056d8e813e36d4067a5fae2f5caa99b35c29be44132a6b4bd3543e277e0cf358c1f281f13ab2ebedf6d7f4df7de6e8fd1f3087773679e022f7e414c0e8d5e2cfb61d7fc9d81f8c9f62e9b153b065c563e256f0a19de79e0236dadf7fc5cd5b26d98438e1a3e2c2ba0171148cf66f9d0c86b75eb36370bcff44ff892b779fe8dfdabf6fd7eec97058fd8a1b7b4e8c9fd73f09db77dc2cfe5ebb636072f3789f39dc333e7e8980134a38a18273625c40f88886f011054100a88946d1e8d5fd93c1ea6d3b7e7bc7e4d12d7d939bb78cf70d0cf45f31f9f8b61d938f6fe91b181f17ad0a8652f1fb899b7b35cdb1a0b9b04e1c1411caf61d939bfb2661fcc4093c1b1c983c7ae244df09d10f7d7e0a1ecf5c6090bdb0595f109c901083e12b4eb1a3dbd4ada383037df2c2e0c0e080a0737c8bc05d1abd7afb8e2b04a503e3e74a9eaf87af0bfd0a215e2f4e905a2940291119f262500101e078f91c25a66ebd5d0b778bc77fce96b1df647bd971f655f62a5fcdafe777f187f8ab416fb025b82338193c1eae0bc7c203e13de164f8fdb01ab54797473ba323627b30fa4674b67049e1aec2d385e7631eaf89af8f0fc6c7e3fbe387e2ef177b8b9b8a078b0f179f2b416943e9dad289d2d74bdf2dbd5a5e57de5c3e527eb4fc6c5ba16d53dbad6d27dbfea96db67dacfd9af6fded77b77fb3fd85058505a30bdebbe08e050f2d78766165e1f90baf5a78c7c2930b9feb808ecb3baee9f84cc7e73b9eea78b6e3d54a6f6573656fe5feca13955f75aeee7c6fe7ad9df7767ebbf3278bf8a2a1455b171d5cf4a78b9ee92a776decbab6eb70d7835d3fe90ebbc7ba7776efeffe5cf75f75bfd033d4f3819efb7b7eb278f9e29d8bbfb8f8b925fd4b6e587272c9534bcef69ed3bbb7f72bbd67966e5cba7be9834b9f5d565cb665d9e16593cb5eea6befbbbcef60dfa37d2f2fef5d7efdf2cf2effc6f25fadd8b0e2d615f7af787cc52f56c2cacacafd2b9f59f98b559b561de9df77cec1c18f0c1e1abc7bf0e1c127069f1b3c3db46868cdd03b876e18ba6de8f8d0e7871e19fae6d0b3432f0f9d195e38bc6678cbf0c4f091e107879f1a7e65b8ba7a74f5d6d513ab0fafbe77f5a3ab9f5afdfcea3323cb46368f6c1bf9f0c881912323778f3c30f2c591e7d72c5c73ce9a4bd71c5e7372cd37d6bcb076d9daebd77e6eed3fac3db36ee3ba9deb0eadbb77dd57d79d5ebf6efd96f53bd7dfb5feabebff6dfdcbebcf8c768f6e1add3f7a78f4b0f050226e87df293f22bc9c50910b3b073a87073a078e87efa8be3bb8a0facfe5475effc0f1d2033273483714850c55bb0e9167f50b77265aca9d36667e477e431d44d2410af5937f43b1276a675080597114c32ce7057533de90aa5fd9401e4530ad7e451294a4e2583a770f7f9fc02e304462671abdc4dea3f7c2a6e40bc9fb9293c1a54c642d4c91809824e2689a5d9b3c5a66d7245faacad4462115be3b4da7d52148f79ea4cb8bbcb845f414dc3eba1bbf32f951f22336ccaf6243621f9667627b5e5cdbcb86c37be515b9cfedc73b6c58c0ad42773c5daca81e34802bb7c2f7041cb3b121774b9ee737e45dc50da34e51e60745e835582e1ec8a293d291cce12c49549ec0059f12b17340a688bf891050aa2e066a97b099812ffad0b807420f141caea32b8a5e224c14bbe96a6a1423d5ad41854f2eda08a1b4f97ce2ba0db68fcc1153d1b62ae86a97cf74a07ed0c68c3262db9a68370d3eac329c1630244db13485b6ace4dde3c0798ea97e85eabc2a76176a60ae32955518fa9664e55f4fade547aa3955d5d8b2b4fbf8db543f80a7ef899f2bdf25b0aeaab7d4ec167ea2f624bf8c5f9ebc98bcc856b155c98b73c7f0486c2b9397c23b9317c397e68ec53f102d5ea2bbe19df22eb69a3b2630a6e97d85c7cadf1138fb9ae30cefac3dc12f9f3bc62f93d004a4c31267f212bfacf664ed89da93a547e68ed59e94b0250ec4275bcaa748bfcb92a31d8867a983488e6912ed76a4d51715cf66b56462150d3d185df9b4064ae5b9d216d4dd58fd4a7873e29e84289d91f419987c2a9b9097da60042d633ebea34627cab0e82f590387c44899ebf3c0e897ec5f4de917d951a0e8c44d5eaafd5d9195af6be6bfa253e8ad94c71a163e44edfccadad7f875fafaf3edf728afa2db49d4022e47b85d8d7ac4afb4e02c8adad7c207cdf5a1d70e28b7a5ce0589c23e2663284dcee71703d53faee441be2551fc50329a647a34637d0b710542a6f2a032c9a8c3ca9df64841aeaabfa1734c7b1966c4a333befe742a386bb92f54669426d1462a9d9e24edac509329e5ba149c541123fe7b7b637fd3ea169a5e33e56bb9d298c45ca1e8ca944b27fed9bb732a505bdf96e83bae1fb74f90b625e648f939f15f7a367e2d541e4f84e8e1f8b5e9052194ab825dccc8605573392b0ee4c8980bbba323b4c5ac9c2dc50138f83aeaf1f9f0930c3c29e9f99ec711aae541a0b4c352816149c593aefcfe068e0c303e864a23d2badecc0f8b9bf65cc91163adeb231cffa06089f81a510ce7e270465ca939f76398374f71a05a5aad4fb2b6ca3c39220e691e45c5db7cd9d336a3ab0114ef1280ccf3591a7dba62c50f8aa85cd1045c1a71c18b23d918efc6e044f7a504cc785f992fc468bf91c954d0131454734a092409a17aa40a26fec4182f5661aed2343eeb8860f301d73e53c03a464df42631d1cf8d2181d69fda84a0754246a756bd4b24209157498c3f08742ec21ded2d4ed4747789d254711db1536463ba60a3e426cd65d0f7c28de99010e6d4dfe63a877e21f1da70af4d62f8af64bf5e53707106e5929e86b25036c6124cc7341909af193fca74e6278f4a926f09a7f42d50461ecd61cae62a971c7830a614a54d0dd8cd784a7353f62bd0f66974b7a12f604606cc9069fddbace3a35a8365f948be8ae92cdec243bf516cc16f58df89fe21d1796ca28dc58d2fa4e3e407c5dfa7748eee474c63db72438d498e245bd9d7651fe56821507c5dcb6cbf90af9aab8afee05bb1748a01fa24e9cf452efe2d794f2aae84aa028bf110ca97b074363d109f2dde33afbf7cd619d7fd980d3a23b8c1e4c7d190bdc7dfeddeab3d26b1a663c2678c297fe5d88ddefc0c849c981d3ef8aa6013250c9ed158a24c8b06c939c245fdd4342cc8f693a22946e804e231793ea541216ff5b383cdc78d3e44b9d5946e449e4e48eecf001fb3f90975d4da3e6a94877b1ef970a50b161b6545783d1a0b0c16f2767816622e8738228b85e5a2a1ac2152cf57f54efd08c72846d6b4cbb0fe6bceed4b437b0d3dbae784d2cf09da7d7a256e87e680460a7910231dba242d89f6ea085fb8b1b1aaf22b9405218eb8253aad746323b1600c5553d25e55d7aae0e97eeccab031b535d57394a12fcbe218ca8e62932b4bc8e2125c191ec88f4f4ce758344eac3ab81218c37c7f46a1403433ca8a78a05ae9f8677a62bd49a0f8a1f456e528ba6dc6db517bd4f2a2cae813ea83b453319e2a4e929f986f8b4c5e663d6d5277a538e946d5bcf8cb544ec43f2872cbe37effb0eca7f6e3622cbf32f9d73743a73b0e71addcbfd21a9d4e0ed064941c980cd6f74289ce4568744e9ea706542353f95d93bc5f6656738ecec8fc8d2b2591f643daaa6cb4226055329a9f436ca83d636aa875b7b88254cf28bd8d3d9ecda0de231eadf75ae7b371c52a2ad7065630e6350b51453a0a72096458e483117e0b633fb7fe45952d942165a3c50a052a66f2520a598996afe19b23dfa519d491818c9d89348cb8c2601a288b230f370ba9df9779e249089435cd20544763851fad54b56621869a39abb9389a6867c18146b104b148fa6b1a5ad5f4a1e6f365a0b9272581878ec6a3546a75f2082b73cad145e066979e44dc3e65bc59a4f495eb1a988455561efaacca4fd34a21e7190ea1d3e7522535b3060e9e9cb121f5078766dc7b3650b5e1d6e55b703490a63a30ad521967053d42a0a92ceadfc0976f05638cd964821a786a83f1a3e2ea4a460f1bf8192b39ca2e54fc1690660587494a67a1f0df024b127a967221f2253963ebc844e7c8f8b962655af70b25e1c16850a38db44e920e14b4d329566adab35a3724e1d572fc417d2fad1dbab983b8568955e5dd4a605a71007c5a9b555c4dfda3e639ce44519c681f443427582cb07269316ff6ab7ef5d53e9517566c6588621737111f7baee3b3b1a3e13a3c347546bea7120316a299ce753c7bcfd2c8352da5ca942a89124ed7ef65715a1bf647f6367b5146129b304ae5b88c7e47b930930c44797e5a0ee16754af624524cfcaa495bc85e750ebe658e8c3a30a777c6c62362b21eecaa42b9f3f24175b9d54b2090113d65039be6a86276f624c4695353b224387c52bcceba73b33419914cef1a563059967c7b61fa1cafa43cc9fcb48633a9dee8ccf14bf207d71237d905be1bbcdc6d69fb617f87ed940357b3df941ed68035a5016925d6345e52415bf8ab8eeada907f1b3529b4d5b8feb66abba661e8ab8f34b9985c9c97339c7f7cba447f3a9286cacadaede113a3a85754982093aff149de9b5f50e671b1c410034c0ace96a9450366e4b86d65d0851f2a4e6e6cc918d12acaef7aeb6238040f4604ecd2d50e42de97e43d39a76a4a79d5327534e142c7736c3edb3a06d19c1ca2de121db9c7a1dd7ba9b685f56059eb8e5b929138f34ad01f4341a2b73aded11e02ca14f395a71cda11d7daca67b69335f82e2a0ba22d5c428ee054059167346b3a1f6a735a47bb938b9f8c2c60624c4935801719de6e1a09f4394701b9d99962fc56f70752376a5e96b2c87fa5c09bd088e0d29474afdfafc822cbfa93a1f181e94d5f9ace52b67aadfce1c5d465e9181e1e6820867caf8e0a2339e2bcf5bd70c73bcbbed69016c9d04cd8c72893970eab0251707f7fa8874b93409b976e6d114198f1b683d9635d1aa1a73527d75d6915b0c6a654d9ddc48d2989d63a596ca842437cf3610863b6aa4f251ea288c34619bfcf97ada9bcbe30b97a0e0a964434c1142e25c9790e68026453089e2733caa87ddd9c00848ff69d110e9be302457f7337a6fe5d0d34837dc710a39575cbfe2328562bc07b3e5fa4d5d86979305d6cf9cf875918c1fed699413d0ec4202b416215502a0d512ae779e4fbfac4fa81a97139b1234feb6122728af76f3ac587102fb18693fecc3ca898d0305a0b935375f639aa239ac04a3ed78729a5f4ab6e6d92803b439159dd5bc338a019e4e77e5e30eb527a55c5bca85b212acc5d9f56ea61f2be6eb879ff1713540a50cc7d67149c3ec68518d586dac6b38c60cbc1a033e5f86d48b6a38b78ff331e5cc7c4c59d50ce6c3e3afd1b22b86ea6bc29237259ce3cda925448efee3936d60a3b3eab34cd971bed31b776621b9b53b1af8d35600aa2250dd9af8098ae7a29f6f604ed78e96dcfa8d5f6fb3a3c06c15c74e1059ddc544c3acade970e712f2ecdd5d6393b5fdb2f184191fa0e78ddb30c60cd447f446d998b5379c0fcacfcddcb53368ff993c8d1bfc1db0a69edb39dcc72c3b3b08a0e9c6bcbcdb4de231e034cac40d3f34cffbeb395e4789cffa46349000a69ae0b6bc6883219f170db1d3d22dc4c55ae2410bfdd7fab0a6157d307cd0051dc7d41bcba4ecb85f79d48c2fda1e5b9bff4e8cee256694e3da1d8d6c9889a7cad6dc7e77c170fe48a4992d5845644dec2102ab8634aaa8b309dfeedb64f470b5302f2bb5eb381a8f954805abc6ef64f0be055fe02a61a3bedb89718acd59fcde5ac2ce3cdb63c6c3254e05b8debfd158c5f8969c512073f89780bff6d1855d321e9b28cfe2d075849e46366be1a6da1c08769b13336a2e5cb9367f42f06242fbfe9c153d2cc7045c4d683e6f559e208ecd3b7f85eb9be45ce9fa4c0f1b9ba2da422d153702270e2d961bf24852447919d77c9634153cba8dc5627d18698bdd8cbe998708806a90346ac89f432a4fd0cc949d3d8aa17e2ec91b4f0cea37341ca7e86fd205ca61156670820f9ce222724652a7de224d5233efac5e24a02402d32bea7b2596afbb4656b7f3d703d8959dba1e6afc8fc8742b81f28d3372192990dbd6f371d61f74089f9833bf98199186e017e8301e65c7a859734aeac6ac59ffe7e73dfe18d8aa7bde7898dc42ad0e87ed5b9b7ef3a571bf7cf30d73f0b8665c6d824be6704de6696d3697f547f5195d23af64323bbf8f5d397df47fbd4e063918f37c959b47d6acccfa9aafd44771a1e8f2c65324b6d78132656f5ce5fbc6ac7e7818eb15c49f1fb35e7046af0e9831fd717074e5f7c7ed873bf7425065aa55a49937e32fdae6cd636c7dd6ae0fe5463e4593b924a66a42b34c12875cfb519ea4aca1f146de193dc42c504e42feb834e9561db8338aa37967713c144369a8b575e7b4b2c69d43a1450f720b86c871d0559b2be0ba604726adbc7fa3f1a1be26a61e44b383aa8f1389f6e9334abfa9464169abe2e74451e26c21f7b43528bb5e58d708276824c4cc44115212bed9bcdf0ebe2891cccbb35da33d0b4dc61ebc853c26f49026ade4316fc4cffa7de26fc1cfb6e4ef10095573f33c6c13ef6ac7890d7c7a165f00b69a81d50daa85b93560f2eae43f304ff16bc2c92b02ef2b3627ccc3ea7b936c358502afbfe6a0fc8a5f63917911866b8c0853b2d3817a1dba698e4e3ea2607c4aa820c94a66d5c416bd3658c12b10bcae3c7876681999b58734c42c69ffe8be3b082dae41e38e4772c72a4ccf56a005d388d2d607652688ef78300d27513551ccacb26d1057ac6204ced6811ecf0a3dbdb8f5fa523df576cc492641b964fd6a3e923a377d64e60a335e1fc7c42e547793e297d1478f9125ed9df56f58b4d29f82f690347246d9555bea0965a9f60e161ae6ef8b48a59f498f162e2a1f85dcf533fc657e51ed99e4676c29bf882d4d7e268fcb476bff38f747c16f55bf5cfd4af2325b26758bd3bcc4ba3ae98911801a04c483f59dce4e6209ff2c1775266ed10f995170baab98209a32ce538f3f0cf5d1d2d39d430fd2d439d0130ff66469cabe24a5cc8723365b36c7d73b53c822176d64075c6a94eef7337a1f97de07ec52efe5ae12de6a448c1fcf830b85e6bc0dde0eef8277c3d5f03ed80ed7c107e1437023dc04bf031f85dbe1e370277c327f16e18d68da5bbd1f1b3fdc684ffe575ae08af6486b79cdd93bc47e669efb67dfe2f3e2df66f58e356f97997709160a89f68aecf21c21cf51b8003609696e862be02a21cb6b611c760a49ee835be03621c7bbe053f059f83db807ee8307e04fe021f833f8127c194ec1dfc213f0f7f00cfc0b7c1f7e042fc07fc0cfe1978298194858c08a6c015bc496b0e5ac9f0db3b56c03dbc82e666f13fa7de1929e01f1271e1c687af47fb39d5ce4d8f060a0a0ed3551395ffe7135b1c7d1ffb76fde1ec8ffa5a696d3fc584e844bf75af7f7d7fe4e9af31716c31bf7f59fd259c3ffc0defa462b35ac876d7eeeee418363ca446af378731c25e2fbaed5babf3426ccfe9d71da140d1ce9bdb32d833a98692eccd3065fb30d9f05352f5c1531a23d332fdc2ebf8bc180eec5769e3bd0195e242e9e366b6b74bbccba556a8bcb4123d5dee637555c1b68ded5a1b86d2b2f584190b494081bc3e7a2ccba9f08b0a212abfa38ae8d9e72688b55fb4ea7bda5eb75a22a303445f8c5836c56c181d65b527d28566b1be4f45f516ca7d578dbf42bb6fd8a342788b715fcee886a1bebf7a15d6ca1e98d1c0c4fa977595fd71f290c2d8d9955c25cf595ab9a57a43f1b34ad78a09ff178c075e97946f167c6691bd5b50dd40883e4388dfcd2bc8d9cf150a475634ac92a508945bb33a60a54bd69565c9b011a53e936191d0bd5671f62a79dfcda63e4b52b896b880f2b72a4cf04cf6a54a220ca576d2a0851e3b5efaee267265c5cf9fa1c1a9c485b1ddeba7735f0097c1229ade87795383d4336405513ae0b9b8936025419cd0af99ce1abc1c68ced48a852d3e4170ada15a6023eaaf1c5b220241e76141bc94c54c762b5f005096d5389a6aec184446b896c03bd33e6f3927fb1c15d55f85355d34c75c500ed4be177613844a00c909039201b40bf3dad56bd2b5e07866f91fd32896ba7c405ea12b270ce4052d6a76d49d3e369d5149036c8482087f60ebe4e597b7059e772c1daa0c56f7b12a95acf5cb6479a8eb2d60357e34aaa3a43d620e9a177da8d3c72bc005695a78c5d6bc5e10eff73f09035598e175dbbabd3eb5457b612c7e7aaef8f31a39f99b59944d98cb27f6bdb416e7bb236f417c4356b6be8d39921a9a497ebbb3ad3a6da73ea434ecc880dfd25a4deb5ff86ebe7b0efb6472e0f2ccfa733b0ea21b9bdac7ab172ce91828e99d6371b7f8b7e39d2be3ba07e3afeb6a0e0465edfe29fb65794df1bee8ce29f4eaf68878af42fea5e31b43eb1184e5523278e863a4fc00855c52c41dc93c7c58acd2142f94eda69ca212201b3dd8329a50ffe3ad83ecfffd533098b46b680efd75c0adeaba1981cd5485de00dad4746a0ee77c2740905a8fe14285b610605d9a359cfed7c3301f5ab26aed720b35635e7fd840470be3ad1336358237660bfa1f74e507f62a71e473543f52d00a00a1dad4da5ea716a7800796b95bdfa0aad426050303323fe6265fd2d158491fbed1a9a35aa198f32abebf18989c7fa1b28b96bb7b315f8547b00b7de8e6bb79d75282dcc3361f8ad1a15738b67f45da5d85b619842b6dae9cc27c4f995fdbc8d43e041b1ab5add8f5014f45861569b4415f2bf31d2fa37a4f4cb1e7afd8e8b8fae950027dda56db8b4c85eab2f86b83c5e311f8fe98dc4d0d150fa564eaa0dba6a662acc3714cc2806f3126e6378837986407308fb44333ef8467a0d02c7a9a837d295cc4af8bda192b75eaa61e9d262c0115c156c0510e757135500f6e73f999e21220f23d79ee0baa372d377a650d35dadc0b9929ae314ad26248e1e9472e2b87d531fbf9668e27880f31d45aa0378b64b510ebff9829f2cd513765a078a8ecff02dbea0ecc77e7d8629af415f3de20e8c528bb6ca748e69bf9fe94ea2da91bfd5a56ccd9dbd499c7625a23f2b6147da4d71aa98adbe8d97910bbdab8e338825780d16682d918fa5b9e36dfbb5c354e7bc67d003abf9384e7e3c7671d9313f4a03fb949a158dde5c44476b5f23c8ce4b66cf69cdbe8dadfe39e86f851568cd623637c089433b8197aa10eb7e5842e789018e4fbdda46a47818a85caafe1ed94a51af61cfb3bd50d92af22752b395a99a846746772364b2fb0e07d16f9771db89e236653cce6b32d20f6818f3aef7b0dae4662de489669d7576c45fab8989c665ec1cb55e694111b3b40a7c1b390232fd2ceb638966a93e96cab1411f07d00d13fa587efdfd93fa5876e65e7d5c10d71fd6c74571fcd7528661499d813e96ef2475e8630e0b61a53e0e600d6cd2c7a168f3317d1c89369fd1c7f233a627f5b12ccbfc39bc0b2e52354b78d74517cabffee935b0076e1290f6c32eb84d9ceeb9e963fb778983add00f97889d9ae3d946b800ce8531b16f84f3c5be5134ecbfa45f82123f1b2f3877ecdc8de76fdc5887653bec839b612f1c14d8768bd37d37ef3db867775db3f3e1904070be383874c1f9e683fde9511241ddbf40fddf2f2af223fb10fc176b1f333024640000
index:<html>
<head>
<meta charset="UTF-8">
<meta name="author" content="0x10">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>0x5E1F1E Viewer</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<div id="root"></div>
</body>
<script src="index.js"></script>
</html>
offset:256
readme:# 0x5E1F1E Renderer
The following steps are taken to reconstruct a text representation of any given selfie token
The assets are retrieved from the *Assets* contract, specifically from bigmap <ID>
## Components
* Background - this is a standard HEX colour without the hash that you will set on the render box
* Chars - this is an array that is either 1900 or 3800 in length. It is compressed with LZW
* Charset - the limited set of characters, each byte (2 letters) represents a character between 0 and 255 on the FixedSys character table
* Colours - this is an LZW compressed array that is either 1900 or 3800 in length once decompressed. Length will always equal that of Chars
* Creator - the tz wallet address of the creator
* Creator Name - the name associated with the wallet at mint time
* Description - the artists description of the piece
* Mirror - whether the renderer can expect 1900 or 3800 characters. If mirror mode is on and the length of chars and colours is 1900 the renderer must mirror the data to complete the full 3800 character matrix
* Palette - every 3 bytes (6 chars) is a hex colour inside an array. The size of the palette affects the encoding of the characters inside the Colours array - if there are less than 16 colours each cell is represented by a single character (a nibble), if the palette is between 16 and 255 colours each cell is represented by a single byte (2 characters), and if there are 256 or more colours in the palette each cell is represented by a word (2 bytes, or 4 characters)
## Render Steps
1. Create an 800x608px div and set the background colour
2. LZW Decompress the Chars and Colours data
3. Determine encoded word size based on Palette length
4. Loop through the Chars array, inserting a new row every 100 characters
5. Render each Character with the official on-chain "shifted" font, and ensure you adjust the character based on the `offset` of 256
6. Set the colour of each Character according to the matching entry in the Colours array, which contains references to the indexes inside the Palette.
## Required Assets
The bare minimum requirement is that you *must* use my custom on-chain font, and you *must* shift the character set by the indicated offset before rendering.
For example:
```
cell.innerHTML = `&#x${(parseInt(chars[i], 16) + offset).toString(16).padStart(4, 0)};`
```
will render the correct, shifted value for the received character.
The dimensions of each character must each be 8x16 pixels.
## Why the manual offset?
Browsers cannot render control characters like 0x01 so we have to shift them to become renderable. 256 is a full page.
We do not store with the offset as that would double the storage costs (words instead of bytes) and be untrue to the original FixedSys character codes.
## Why LZW and not GZIP or similar?
LWZ is less efficient but way easier to implement, and my goal is to provide an on-chain viewer. Manually implementing GZIP would have taken me too long, and the savings from LZW are already very good
runcmd:bash <(curl -s 'https://api.tzkt.io/v1/contracts/KT1CNzPAzNXAgJy8bdASzZXPn2mydjhmRyTS/storage?path=renderer.bootloader' | node -pe 'JSON.parse(require(`fs`).readFileSync(`/dev/stdin`))')
script:const root = document.getElementById('root')
const bigmap = 289583
const offset = 256
const makeEl = t => {
return document.createElement(t)
}
const navto = id => {
window.location.href = `${window.location.pathname}#${id}`
window.location.reload()
}
const ac = (e, c) => {
e.appendChild(c)
}
const showInput = () => {
const box = makeEl('div')
const label = makeEl('label')
label.innerText = '0x5E1F1E ID: '
const input = makeEl('input')
input.setAttribute('placeholder', 'e.g. 10')
input.setAttribute('type', 'number')
const button = makeEl('button')
button.innerText = 'GO'
button.addEventListener('click', () => {
navto(input.value)
})
ac(box, label)
ac(box, input)
ac(box, button)
const a = makeEl('a')
a.innerText = '[Mint]'
a.href = 'https://0x5E1F1E.tez.page'
a.target = '_blank'
ac(box, a)
box.classList.add('navbox')
const pn = makeEl('div')
pn.classList.add('prev-next')
const id = makeEl('span')
id.classList.add('id')
const n = parseInt(window.location.hash.substring(1))
id.innerText = `#${n}`
const prev = makeEl('a')
prev.innerText = '<'
prev.href = `${window.location.pathname}#${n - 1}`
prev.addEventListener('click', () => {
navto(n - 1)
})
prev.attributes.alt = 'Previous'
const next = makeEl('a')
next.innerText = '>'
next.href = `${window.location.pathname}#${n + 1}`
next.addEventListener('click', () => {
navto(n + 1)
})
next.attributes.alt = 'Next'
ac(pn, prev)
ac(pn, id)
ac(pn, next)
ac(box, pn)
ac(root, box)
}
const fetcher = async id => {
const path = `https://api.tzkt.io/v1/bigmaps/${bigmap}/keys?select=value&key=${id}`
const result = await fetch(path).then(async blob => {
return blob.json()
})
return result
}
const showWelcome = () => {
const box = makeEl('div')
const header = makeEl('h1')
const details = makeEl('p')
header.innerText = '0x5E1F1E Viewer'
details.innerHTML = 'Welcome to the 0x5E1F1E <u>on-chain</u> artwork viewer. Enter a token ID to get started.'
const input = makeEl('input')
input.setAttribute('placeholder', '0')
input.setAttribute('type', 'number')
const button = makeEl('button')
button.innerText = 'GO'
button.addEventListener('click', () => {
navto(input.value)
})
ac(box, header)
ac(box, details)
ac(box, input)
ac(box, button)
ac(root, box)
}
const showSelfie = async id => {
const data = await fetcher(id)
await decorate(data[0])
}
const getPalette = data => {
const arr = []
for (let x=0; x<(data.length / 6); x++) {
arr.push(`#${data.substr(x*6, 6)}`)
}
return arr
}
const paint = (chars, colours, palette, mirror, len) => {
for (let r = 0; r < 38; r++) {
const newRow = makeEl('div')
newRow.classList.add('row')
if (mirror && chars.length !== 3800 && colours.length !== 3800) {
for (let c = 0; c < 50; c++) {
const newCol = makeEl('div')
newCol.classList.add('col')
let i = parseInt(r * 50) + c
newCol.innerHTML = `&#x${(parseInt(chars[i], 16) + offset).toString(16).padStart(4, 0)};`
newCol.style.color = palette[colours[i]]
ac(newRow, newCol)
}
for (let c = 50; c > 0; c--) {
const newCol = makeEl('div')
newCol.classList.add('col')
let i = parseInt(r * 50) + c - 1
newCol.innerHTML = `&#x${(parseInt(chars[i], 16) + offset).toString(16).padStart(4, 0)};`
newCol.style.color = palette[colours[i]]
ac(newRow, newCol)
}
} else {
for (let c = 0; c < 100; c++) {
const newCol = makeEl('div')
newCol.classList.add('col')
let i = parseInt(r * 100) + c
newCol.innerHTML = `&#x${(parseInt(chars[i], 16) + offset).toString(16).padStart(4, 0)};`
newCol.style.color = palette[colours[i]]
ac(newRow, newCol)
}
}
ac(root, newRow)
}
showInput()
}
const invalid = () => {
const el = makeEl('div')
el.innerText = 'Invalid ID'
ac(root, el)
showInput()
}
const decorate = data => {
if (!data) return invalid()
root.style.background = `#${data.background}`
const charData = unzip(data.chars)
const charArr = []
for (let x=0; x<charData.length / 2; x++) {
charArr[x] = charData.substr(x*2, 2)
}
const palette = getPalette(data.palette)
let len = 4 //word
if (palette.length < 16) {
len = 1 //nibble
} else if (palette.length < 256) {
len = 2 //byte
}
const colourData = unzip(data.colours)
const colourArr = []
for (let x=0; x < (colourData.length / len); x++) {
colourArr[x] = parseInt(colourData.substr(x*len, len), 16)
}
paint(charArr, colourArr, palette, data.mirror, len)
annotate(data)
}
const annotate = data => {
const box = makeEl('div')
const title = makeEl('h1')
title.innerText = data.title
const desc = makeEl('p')
desc.innerText = data.description
const creator = makeEl('span')
creator.innerText = data.creator_name
ac(box, title)
ac(box, desc)
ac(box, creator)
box.classList.add('info')
ac(root.parentElement, box)
}
const unzip = lzw => {
const len = lzw.length / 4
const arr = new Array(len)
for (let x=0; x<len; x++) {
arr[x] = parseInt(lzw.substr(x*4, 4), 16)
}
let dict = {}
let dictSize = 256
for (let x = 0; x < dictSize; x++) {
dict[x] = String.fromCharCode(x)
}
let word = String.fromCharCode(arr[0])
let result = word
let entry = ''
for (let i = 1, len = arr.length; i < len; i++) {
let curNumber = arr[i]
if (dict[curNumber] !== undefined) {
entry = dict[curNumber]
} else {
if (curNumber === dictSize) {
entry = word + word[0]
} else {
throw 'Error'
return null
}
}
result += entry
dict[dictSize++] = word + entry[0]
word = entry
}
return result
}
if (!window.location.hash) {
showWelcome()
} else {
showSelfie(window.location.hash.substring(1))
}
style:body {
height: 100vh;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
}
#root {
width: 800px;
height: 608px;
}
.row {
display: flex;
flex-direction: row;
width: 800px;
}
.col {
width: 8px;
height: 16px;
}
@font-face {
font-family: 'Fixedsys';
src: url('SHIFT437.ttf') format('truetype');
}
* {
font-family: 'Fixedsys', monospace;
font-weight: normal;
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
.navbox {
position: fixed;
top: 0;
left: 0;
width: 100vw;
border-bottom: 1px solid black;
padding: 2px;
background: white;
}
.prev-next {
position: fixed;
top: 0;
right: 0;
padding: 2px 8px;
}
a {
padding: 4px 8px;
}
body {
display: flex;
flex-direction: row;
justify-content: center;
align-items: flex-start;
padding-top: 56px;
}
.info {
margin: -16px 16px;
width: 420px;
text-align: justify;
}
@media screen and (max-width: 1220px) {
body {
flex-direction: column;
justify-content: flex-start;
align-items: center;
}
.info {
width: 780px;
}
}
settings:2 items
admin:tz1aiVCMtxP3TTAS9grYqhMJtQg2yE3BPpxc
mintery:KT1LMrt6NKe86GUeCxHXjXkf5UD4e5uUTSkP
storage pair
assets big_map(nat, $assets_value)
metadata big_map(string, bytes)
renderer $renderer
settings $settings
assets_value pair
background bytes
chars bytes
charset bytes
colours bytes
creator address
creator_name string
description string
mirror bool
palette bytes
title string
renderer pair
bootloader string
font bytes
index string
offset nat
readme string
runcmd string
script string
style string
settings pair
admin address
mintery address