# Technical Spec

## Working Examples

Working example using local references: <https://toniq-labs.github.io/bitgen-example/>

Working example Github repo: <https://github.com/Toniq-Labs/bitgen-example>

## Code Examples

Below is a fully functional version of each file type (final testing on Bitcoin mainnet in progress)

1. The Renderer JS
2. The Collection JS
3. The Collection JSON
4. The Asset Layer IMAGES
5. The Inscription HTML
6. The Provenance JSON
7. Content Security Policy
8. Rendering IFrames

## Renderer JS

```javascript
async function render(size, ...inscriptionIds) {
    try {
        const base64Images = await Promise.all(inscriptionIds.map(async (id) => await getBase64(await (await fetch(`/content/${id}`)).blob())));
        const finalImageUrl = generateCombinedImageUrl(size, base64Images, false);
        const resizeArtifactFix = `<img class="full-size" onload="this.remove()" src="${generateCombinedImageUrl(size, base64Images, true)}" />`;
        return `<style>body, html {margin: 0; padding: 0; overflow: hidden;} .full-size {position: absolute; opacity: 1; top: calc(100% - 1px); left: calc(100% - 1px);} img {display: block; width:100%;}</style>${resizeArtifactFix}<img src="${finalImageUrl}" />`;
    } catch (error) {
        return `<p style="color: red;">${error?.message || String(error)}</p>`;
    }
}
function generateCombinedImageUrl(size, base64Images, fullSize) {
    const innerImages = base64Images.map((base64Image) => `<foreignObject x="0" y="0" width="100%" height="100%"><svg ${fullSize ? 'class="full-size"' : ''} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${size.width} ${size.height}" width="${size.width}" height="${size.height}" style="image-rendering: pixelated; background: url(${base64Image}) no-repeat ${fullSize ? '' : 'center/contain'};"></svg></foreignObject>`);

    return URL.createObjectURL(new Blob([`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${size.width} ${size.height}" width="${size.width}" height="${size.height}">${innerImages.join('')}</svg>`], {type: 'image/svg+xml'}));
}
function getBase64(file) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => resolve(reader.result);
        reader.onerror = (error) => reject(error);
        reader.readAsDataURL(file);
    });
}

```

## Collection JS

```javascript
const collectionJsonInscriptionId = '{collection-json-inscription-id-here}';
const rendererJsInscriptionId = '{renderer-js-inscription-id-here}';
const renderSize = {width: 150, height: 150};

async function createInscriptionHtml() {
    const collectionMetadataPromise = fetch(
        `/content/${collectionJsonInscriptionId}`,
    ).then((response) => response.json());

    const inscriptionTraitsList = document.querySelector('script[t]').getAttribute('t').split(',');

    const rendererScript = document.createElement('script');
    rendererScript.setAttribute('async', '');
    rendererScript.src = `/content/${rendererJsInscriptionId}`;

    const renderPromise = new Promise((resolve, reject) => {
        rendererScript.addEventListener('load', async () => {
            try {
                const collectionMetadata = await collectionMetadataPromise;

                const traitInscriptionIds = inscriptionTraitsList.map(
                    (traitIndex, layerIndex) =>
                        collectionMetadata.layers[layerIndex].traits[traitIndex]?.inscriptionId,
                );

                resolve(await render(renderSize, ...traitInscriptionIds.filter(id => !!id)));
            } catch (error) {
                console.error(error);
                reject(error);
            }
        });
    });
    document.head.appendChild(rendererScript);

    return await renderPromise;
}

createInscriptionHtml().then((result) => (document.body.innerHTML = result));

```

## Collection JSON

```json
{
    "collection": {
        "name": "Large animals",
        "description": "A collection of large animals.",
        "creator": "Bob",
        "collectionImageInscriptionId": "96d87d7e59d75ebc0e6144b09fdd96355fcdaa86fd098d64c46f19a424012bbei0"
    },
    "layers": [
        {
            "type": "Background",
            "traits":[
                {
                    "name": "Blue",
                    "inscriptionId": "96d87d7e59d75ebc0e6144b09fdd96355fcdaa86fd098d64c46f19a424012bbei0"
                },
                {
                    "name": "Black",
                    "inscriptionId": "96d87d7e59d75ebc0e6144b09fdd96355fcdaa86fd098d64c46f19a424012bbei0"
                }
            ]
        },
        {
            "type": "Body",
            "traits":[
                {
                    "name": "Alligator",
                    "inscriptionId": "96d87d7e59d75ebc0e6144b09fdd96355fcdaa86fd098d64c46f19a424012bbei0"
                },
                {
                    "name": "Elephant",
                    "inscriptionId": "96d87d7e59d75ebc0e6144b09fdd96355fcdaa86fd098d64c46f19a424012bbei0"
                }
            ]
        }
    ]
}
```

## Asset Layer IMAGES

These inscriptions are simply inscribing each entire image file as its own inscription as bytes.

## Inscription HTML

<pre class="language-html"><code class="lang-html"><strong>&#x3C;script t="0,1,2" src="/content/{collection-js-inscription-id-here}">&#x3C;/script>
</strong></code></pre>

## Provenance JSON

```json
{
    "bitcoinAddress":"bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej",
    "collectionJsonInscriptionId":"9a42401296d87d7e59d75ebc0e6144b09fdd96355fcdaa86fd098d64c46f19a424012bbei0",
    "excludeInscriptions":[
        "96d87d7e59d75ebc0e6144b09fdd96355fcdaa86fd098d64c46f19a424012bbei0",
        "d75ebc0e61496d87d7e54b09fdd96355fcdaa86fd098d64c46f19a4240192bbei0"
    ]
}
```

## Content Security Policy

In order to view BitGen ordinal images on your website, we recommend the following [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) header:

{% code title="recommended CSP header" %}

```
default-src 'unsafe-inline' 'self' blob: data:
```

{% endcode %}

In addition the the CSP listed, you should also include the domain (or sub-domain) where you are hosting your iframe code (see IFrames section below on why this is important).

WebKit browsers (desktop Safari and all iOS browsers) have stricter CSP parsing compared to other browsers, so make sure to test whatever CSP headers you come up with on those browsers. (The above recommended header value has already been tested on WebKit browsers).

## IFrames

To display HTML ordinals on your website (such as those generated by the BitGen standard) you must load the inscription inside an iframe. For security purposes, make sure to host your iframe code on a different domain (or sub-domain) than your frontend to prevent inscription code from hijacking your website.

You might think that sandboxing the iframes on your frontend and whitelisting certain paths on your domain is enough, but in order to get inscriptions working on Safari/iPhone (webkit browsers) you have to allow scripts and cross origin on your iframes, which essentially removes any of the protections of sandboxing (a script could just remove sandboxing and access the parent window local storage or cookies). This is why we put our iframe code on a separate domain from our main website (it acts as sandboxing) without having to specify sandboxing in our iframes or having to whitelist certain paths.

Combine this with iframe sizing difficulties, and it can make displaying ordinals a daunting task. To help, we at Bioniq have created `toniq-nft-frame` to smooth out the process. [Check it out here](https://github.com/Toniq-Labs/toniq-nft-frame).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://bioniq-1.gitbook.io/bitgen-bitcoin-generative-ordinals-standard-alpha/technical-spec.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
