|Home|

4th.Energy Asset Assemblies

What's all this then?

This is the final version of proof of concept 3.5. It shows a template driven HTML generator using all on chain assets, generated all onchain, and displayed directly from a call on this web page to an eth node.

What problem are we trying to solve?

The problem I am trying to solve is onchain composition of code libraries that can also be extended after the main template is deployed. I have used plain old javascript, a compiled dart library for the drunken bishop math that turns a hash into a unique sparse matrix, and an HTML template so as to prove the general case (ie. this is not a CNFT - it is plain HTML to show that this is a generally useful way to do onchain code deployment and composition. The assembler code is standard EVM assembler put through the 4th.energy EVM assembler tool). The original HTML project I used as the basis for this POC is explained here.

The ideas developed in this POC will be carried forward into work for onchain 4th.Energy code deployment, composition and library management.

The Proof of Concept

What you are seeing here is an HTML page generated all onchain and displayed in an iframe. The iframe content is fetched directly from an "eth_call" from this web page as per POC3.

Press "Next" to see the next random hash. If you enter any plain old text into the edit box and press "View", we hash and display it for you and show you the resulting image. There are 7 draw algorithms - click Next a few times to see.

Hash:

This POC will work on any device because it's all generated on chain and returned as an SVG inside an HTML page.

How does it work?

The proof of concept introduces some tools and techniques:

The Tools

The Techniques

Details

This was quite a complex project and it is beyond a simple code walk through, however the main components are shown below. The are:

Resource File Contract Assembly

            {
                "code-list": {
                    "draw.e4ml": [
                        "0x...contract code...",
                        "${user-hash}",
                        "0x...contract code...",
                        "${make-chip}",
                        "0x...contract code...",
                        "${draw-chip}",
                        "0x...contract code..."
                    ],
                    "draw.js": [
                        "0x...contract code...[22k]",
                        "0x...contract code...[22k]",
                        "0x...contract code...[22k]",
                        "0x...[remainder]"
                    ],
                    "gray.js": [
                        "0x...contract code..."
                    ],
                    "grid.js": [
                        "0x...contract code..."
                    ],
                    "text.js": [
                        "0x...contract code..."
                    ],
                    "cols.js": [
                        "0x...contract code..."
                    ],
                    "dots.js": [
                        "0x...contract code..."
                    ],
                    "line.js": [
                        "0x...contract code..."
                    ],
                    "spot.js": [
                        "0x...contract code..."
                    ]
                },
                "hash-list": {
                    "draw.e4ml": [
                        "0x1aC902DfB6A2a12AB5E5f881c4c75C9D11FAe2A2",
                        "0x8f509DA0F29690102A1652213B732b5aF39508d9",
                        "0xECbB2B73a73cFd5C1f96A06c5c9b406929752Ddd",
                        "0x25dB6AF57Bf91C20A8d400873eFd0BCe69333E86"
                    ],
                    "draw.js": [
                        "0x31447132A5c12f19e94525b4183986f2430a31bE",
                        "0x0e3cD80a3F4b47035bE49a47df2155cc4182A08B",
                        "0xA10ca7e46f486DdEEFa4ccb222db881e00F7b45C",
                        "0x0AE8C3374BD9C09d7321a2b37281197B56390a2D"
                    ],
                    "gray.js": ["0xC28c48D72ab450b127e774306Ca08e9712e48F8c"],
                    "grid.js": ["0x8dF9Db4C8880f074adDaB5C4D4EDBFB8a706FC55"],
                    "text.js": ["0x8F4e3A23b6F7c7249F72e2d212a0d6281a5972E8"],
                    "cols.js": ["0xd998627Fb834df02A8F9AA27b4680d5DD140400e"],
                    "dots.js": ["0xd7eE9883240B26EC210fcB917eaBfA62D8d9b38E"],
                    "line.js": ["0x89EbF11d92174269f2879362CBd720f8aA3CbdcB"],
                    "spot.js": ["0x64EdCC7af6C16E903bbB33c0bc2d220b5e38DaE1"]
                }
            }        
        

Real-time Template EVM Assembly Code

        # 
# 4th.energy on-chain HTML POC 
#
# A contract that takes the user hash and draw file selection contract hash
# as call data and pulls it all together.  
#
# Written using the 4th Energy EVM macro assembler.
#
PUSH2 &main JUMP

#
# function - make the call with what's on the stack and return the data into memory
#
:callCopy JUMPDEST
    # TOS -> arg count size and contract
    # Unknown ret size
    PUSH1 00 
    # Return buffer is current data size @ C0 + E0 address
    PUSH1 C0 MLOAD PUSH1 E0 ADD
    # Call
    # Arg size is arg count at 20 chars. Push on 00 to swap 
    PUSH1 00 SWAP4 PUSH1 14 MUL
    # Arg mem offset address is 00
    PUSH1 00
    # Zero value send
    PUSH1 00 
    # Contract address arg is now at 6 - Push on 00 to swap 
    PUSH1 00 SWAP6
    # All our gas & call
    GAS
    CALL
    # Just assume it works so drop status code
    POP
    # First copy the first size word to the buffer
    # Return buffer is current data size @ 0x120 + 0x140 address
    PUSH1 C0 MLOAD PUSH1 E0 ADD DUP1 DUP1
    # Load first word size
    PUSH1 00 PUSH1 20 SWAP2 RETURNDATACOPY
    # Load the size
    MLOAD DUP1
    # Save it 
    PUSH1 C0 MLOAD ADD PUSH1 C0 MSTORE
    # Copy the data
    SWAP1 PUSH1 20 SWAP1 RETURNDATACOPY
    # Pop of 00 swap placeholders and return to return address
    POP POP
JUMP

# 
# function - convert the word at 0 to unicode text to be used as our emitted script hash.
#
:hashText JUMPDEST
        PUSH1 00
        :hashLoop JUMPDEST
            DUP1 PUSH1 20 EQ PUSH2 &doneLoop JUMPI 
            DUP1 PUSH1 01 ADD SWAP1
            # Last index value is now TOS - get byte at index
            PUSH1 00 MLOAD SWAP1 BYTE 
            # The byte has to be made into two printable chars - 4 bytes.
            # first get first and second nibbles on stack 
            DUP1 PUSH1 04 SHR  
            SWAP1 PUSH1 0F AND
            # we basically add NIBBLE GT * 57 + NIBBLE LT 30 to find the right offset to add
            DUP1 PUSH1 09 SWAP1 GT PUSH1 57 MUL DUP1 PUSH1 09 SWAP1 LT PUSH1 30 MUL ADD ADD
            SWAP1 
            DUP1 PUSH1 09 SWAP1 GT PUSH1 57 MUL DUP1 PUSH1 09 SWAP1 LT PUSH1 30 MUL ADD ADD
            # put at current result memory location E0 + *C0 then add 2 to C0
            PUSH1 C0 MLOAD PUSH1 E0 ADD MSTORE8
            PUSH1 C0 MLOAD PUSH1 E1 ADD MSTORE8
            PUSH1 C0 MLOAD PUSH1 02 ADD PUSH1 C0 MSTORE
            PUSH2 &hashLoop JUMP
        :doneLoop JUMPDEST
        # Pop index
        POP
JUMP

#
# MAIN
#
:main JUMPDEST
    # Get the user hash call data into 00
    PUSH1 14 PUSH1 00 PUSH1 00 CALLDATACOPY

    # Put the draw chips into memory for args from 20
    # start accumulator
    PUSH1 14

    #
    DUP1 PUSH20 
    ${cols.js.0}
    PUSH1 0C PUSH1 08 MUL SHL SWAP1 MSTORE PUSH1 14 ADD 

    DUP1 PUSH20 
    ${dots.js.0}
    PUSH1 0C PUSH1 08 MUL SHL SWAP1 MSTORE PUSH1 14 ADD 

    DUP1 PUSH20 
    ${gray.js.0}
    PUSH1 0C PUSH1 08 MUL SHL SWAP1 MSTORE PUSH1 14 ADD 

    DUP1 PUSH20 
    ${grid.js.0}
    PUSH1 0C PUSH1 08 MUL SHL SWAP1 MSTORE PUSH1 14 ADD 

    DUP1 PUSH20 
    ${line.js.0}
    PUSH1 0C PUSH1 08 MUL SHL SWAP1 MSTORE PUSH1 14 ADD 

    DUP1 PUSH20 
    ${spot.js.0}
    PUSH1 0C PUSH1 08 MUL SHL SWAP1 MSTORE PUSH1 14 ADD 

    DUP1 PUSH20 
    ${text.js.0}
    PUSH1 0C PUSH1 08 MUL SHL SWAP1 MSTORE PUSH1 14 ADD 

    # remove accumulator
    POP 

    # CO for output size accumlator, and E0 as start of output.
    # Set output size to 0
    PUSH1 00 PUSH1 C0 MSTORE

    # 0. main html frag
    PUSH2 &draw.e4ml.0.done
    PUSH1 00 PUSH20 ${draw.e4ml.0}
    PUSH2 &callCopy JUMP

    :draw.e4ml.0.done JUMPDEST
        # Convert user hash to unicode, add to data
        PUSH2 &doneHash
        PUSH2 &hashText JUMP
        :doneHash JUMPDEST

    # 1. main html frag
    PUSH2 &draw.e4ml.1.done
    PUSH1 00 PUSH20 ${draw.e4ml.1}
    PUSH2 &callCopy JUMP
    :draw.e4ml.1.done JUMPDEST

        # Each of our js draw library file
        PUSH2 &draw.js.0.done
        PUSH1 00 PUSH20 ${draw.js.0}
        PUSH2 &callCopy JUMP
        :draw.js.0.done JUMPDEST

        PUSH2 &draw.js.1.done
        PUSH1 00 PUSH20 ${draw.js.1}
        PUSH2 &callCopy JUMP
        :draw.js.1.done JUMPDEST

        PUSH2 &draw.js.2.done
        PUSH1 00 PUSH20 ${draw.js.2}
        PUSH2 &callCopy JUMP
        :draw.js.2.done JUMPDEST

        PUSH2 &draw.js.3.done
        PUSH1 00 PUSH20 ${draw.js.3}
        PUSH2 &callCopy JUMP
        :draw.js.3.done JUMPDEST

        PUSH2 &draw.js.4.done
        PUSH1 00 PUSH20 ${draw.js.4}
        PUSH2 &callCopy JUMP
        :draw.js.4.done JUMPDEST

        PUSH2 &draw.js.5.done
        PUSH1 00 PUSH20 ${draw.js.5}
        PUSH2 &callCopy JUMP
        :draw.js.5.done JUMPDEST

        PUSH2 &draw.js.6.done
        PUSH1 00 PUSH20 ${draw.js.6}
        PUSH2 &callCopy JUMP
        :draw.js.6.done JUMPDEST

        PUSH2 &draw.js.7.done
        PUSH1 00 PUSH20 ${draw.js.7}
        PUSH2 &callCopy JUMP
        :draw.js.7.done JUMPDEST

    # 2. main html frag
    PUSH2 &draw.e4ml.2.done
    PUSH1 00 PUSH20 ${draw.e4ml.2}
    PUSH2 &callCopy JUMP
    :draw.e4ml.2.done JUMPDEST

        # Chip frag selector
        PUSH2 &frag.done
        # get the first word as our random number and mod by 7
        PUSH1 07 PUSH1 00 MLOAD MOD
        # add 1 (user address) + mod * 20 (14) to get address
        PUSH1 01 ADD PUSH1 14 MUL MLOAD 
        PUSH1 0C PUSH1 08 MUL SHR
        PUSH1 00 SWAP1 PUSH2 &callCopy JUMP
        :frag.done JUMPDEST

    # 3. main html frag
    PUSH2 &draw.e4ml.3.done
    PUSH1 00 PUSH20 ${draw.e4ml.3}
    PUSH2 &callCopy JUMP
    :draw.e4ml.3.done JUMPDEST
    
# Main returns
PUSH1 C0 MLOAD PUSH1 E0 RETURN

        

Draw Module

const spot = function(grid, rows, cols) {
    const hues = [
        "#00db96",
        "#49297e",
        "#90dcff",
        "#e10086",
        "#fdfb76",
    ];

    function set(node, name, text) {
        node.setAttributeNS(null, name, text);
    }
    var node = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    var size = 20;
    set(node, "viewBox", "0 0 " + (rows * size) + " " + (cols * size));
    set(node, "width", (cols * size) + "");
    set(node, "height", (rows * size) + "");
    var r = 0;
    var s = 0;
    grid.forEach(row => {
        var c = 0;
        var x = r * size;
        r++;
        row.forEach(cell => {
            var y = c * size;
            c++;
            var ring = document.createElementNS("http://www.w3.org/2000/svg", "circle");;
            var rad = cell * size / 2;
            set(ring, 'cx', x);
            set(ring, 'cy', y);
            set(ring, 'r', rad);
            s += cell;
            ring.style.fill = hues[s % hues.length];
            node.append(ring);
        });
    });
    return node;
};
window['makeChip'] = spot;
        

Load Code

async function showView() {
    var node = new ethers.providers.JsonRpcProvider('https://4th.energy/geth/test');
    var data = await node.call({
        to: '0x093F8F9e9D24Cde9e382b599dC364811cADAB256',
        data: ethers.utils.hashMessage(hash.value).substring(0, 42)
    });
    chip.srcdoc = readByteText(data);
}        
        

What's next?

A breather. The mathcastles testnet. An NFT mint process using the above. Xmas. And then some discussions on what to do next with the learning and 4th.energy