Frame

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;

interface IFrameDataStore {
    function getData(
        string memory _key,
        uint256 _startPage,
        uint256 _endPage
    ) external view returns (bytes memory);

    function getMaxPageNumber(string memory _key)
        external
        view
        returns (uint256);

    function getAllDataFromPage(
        string memory _key,
        uint256 _startPage
    ) external view returns (bytes memory);
}

contract Frame {
    struct Asset {
        string wrapperKey;
        string key;
        address wrapperStore;
        address store;
    }

    string public name = "";
    bool public initSuccess = false;
    
    mapping(uint256 => Asset) public depsList;
    uint256 public depsCount;

    IFrameDataStore public pageWrapStore;
    IFrameDataStore public sourceStore;

    uint256 public renderPagesCount;
    mapping(uint256 => uint256[4]) public renderMap;

    constructor() {}

    function init(
        Asset[] calldata _deps,
        address _sourceStore,
        address _pageWrapStore,
        uint256[4][] calldata _renderMap
    ) public {
        require(!initSuccess, "Frame: Can't re-init contract");

        _setDeps(_deps);
        _setRenderMap(_renderMap);

        sourceStore = IFrameDataStore(_sourceStore);
        pageWrapStore = IFrameDataStore(_pageWrapStore);
        
        initSuccess = true;
    }

    function setName(string memory _name) public {
        require(bytes(name).length < 3, "Frame: Name already set");
        name = _name;
    }

    // Internal 

    function _setDeps(Asset[] calldata _deps) internal {
        for (uint256 dx; dx < _deps.length; dx++) {
            depsList[dx] = _deps[dx];
        }
        depsCount = _deps.length;
    }

    function _setRenderMap(uint256[4][] calldata _map) internal {
        for (uint256 idx; idx < _map.length; idx++) {
            renderPagesCount++;
            renderMap[idx] = _map[idx];
        }
        renderPagesCount = _map.length;
    }

    function _compareStrings(string memory _a, string memory _b) internal pure returns (bool) {
        return (keccak256(abi.encodePacked((_a))) == keccak256(abi.encodePacked((_b))));
    }

    function _isB64JsWrapperString(string memory _a) internal pure returns (bool) {
        return _compareStrings("[email protected]", _a);
    }

    function _isImportmapWrapperString(string memory _a) internal pure returns (bool) {
        return _compareStrings("[email protected]", _a);
    }

    function _isAssetDep(uint256 _index) internal view returns (bool) {
        return _index < depsCount;
    }

    function _getAssetWithWrapperString(
        IFrameDataStore _assetStorage,
        IFrameDataStore _wrapperStorage,
        Asset memory _asset, 
        uint256 _fromPage, 
        uint256 _toPage
    ) internal view returns (string memory) {
        string memory result = "";
        if (_fromPage == 0) {
            result = string(
                abi.encodePacked(
                    _wrapperStorage.getData(_asset.wrapperKey, 0, 0)
                )
            );
        }
        result = string.concat(
            result,
            string(
                abi.encodePacked(
                    _assetStorage.getData(_asset.key, _fromPage, _toPage)
                )
            )
        );
        if (_toPage == _assetStorage.getMaxPageNumber(_asset.key)) {
            result = string.concat(
                result,
                string(
                    abi.encodePacked(
                        _wrapperStorage.getData(_asset.wrapperKey, 1, 1)
                    )
                )
            );
        }
        return result;
    }

    // Read-only

    function renderPage(uint256 _rpage) public view returns (string memory) {
        // Index item format: [startAsset, endAsset, startAssetPage, endAssetPage]
        uint256[4] memory indexItem = renderMap[_rpage];
        uint256 startAtAssetIndex = indexItem[0];
        uint256 endAtAssetIndex = indexItem[1];
        uint256 startAtPage = indexItem[2];
        uint256 endAtPage = indexItem[3];
        string memory result = "";

        // Iterate over assets in the index item
        for (uint256 idx = startAtAssetIndex; idx < endAtAssetIndex + 1; idx++) {
            Asset memory idxAsset = depsList[idx];
            IFrameDataStore idxStorage = IFrameDataStore(idxAsset.store);
            IFrameDataStore idxWrapStorage = IFrameDataStore(idxAsset.wrapperStore);

            bool isIdxAtEndAssetIndex = idx == endAtAssetIndex;
            uint256 startPage = idx == startAtAssetIndex ? startAtPage : 0;
            uint256 endPage = isIdxAtEndAssetIndex
                ? endAtPage
                : idxStorage.getMaxPageNumber(idxAsset.key);

            string memory newStuff = _getAssetWithWrapperString(idxStorage, idxWrapStorage, idxAsset, startPage, endPage);
            result = string.concat(result, newStuff);

            address sourceStoreAddr = address(sourceStore);
            bool isIdxAssetLastDep = address(idxAsset.store) != sourceStoreAddr && address(depsList[idx + 1].store) == sourceStoreAddr;
            bool hasCompletedAsset = endPage == idxStorage.getMaxPageNumber(idxAsset.key);
            bool isNextAssetImportMap = _isImportmapWrapperString(depsList[idx + 1].wrapperKey);

            if (_isB64JsWrapperString(idxAsset.wrapperKey) && hasCompletedAsset) {
              if (isIdxAssetLastDep) {
                result = string.concat(
                    result, 
                    string(
                        abi.encodePacked(
                            pageWrapStore.getData("[email protected]", 1, 1),
                            pageWrapStore.getData("[email protected]", 0, 0)
                        )
                    )
                );
              } 
              if (isNextAssetImportMap) {
                  string memory importKeysJsString = string(
                      abi.encodePacked(
                          pageWrapStore.getData("[email protected]", 0, 0)
                      )
                  );

                  // Inject a list of import key names to the page
                  for (uint256 dx = 0; dx < depsCount; dx++) {
                      if(_isImportmapWrapperString(depsList[dx].wrapperKey)) {
                          importKeysJsString = string.concat(
                              string.concat(importKeysJsString, '"'), 
                              string.concat(depsList[dx].key, '"')
                          );

                          if (dx != depsCount - 1) {
                              importKeysJsString = string.concat(importKeysJsString, ',');
                          }
                      }
                  }

                  importKeysJsString = string.concat(
                      string.concat(
                          importKeysJsString, 
                          string(
                              abi.encodePacked(
                                  pageWrapStore.getData("[email protected]", 1, 1)
                              )
                          )
                      ),
                      string(
                          abi.encodePacked(
                              pageWrapStore.getData("[email protected]", 0, 0)
                          )
                      )
                  );

                  result = string.concat(result, importKeysJsString);
              } 
            }
            
            // Finishing deps
            if (isIdxAssetLastDep && hasCompletedAsset) {
                if(_isImportmapWrapperString(idxAsset.wrapperKey)){
                    result = string.concat(
                        result, 
                        string(
                            abi.encodePacked(
                                pageWrapStore.getData("[email protected]", 1, 1),
                                pageWrapStore.getData("[email protected]", 1, 1),
                                pageWrapStore.getData("[email protected]", 0, 0)
                            )
                        )
                    );
                } else {
                    result = string.concat(
                        result, 
                        string(
                            abi.encodePacked(
                                pageWrapStore.getData("[email protected]", 1, 1),
                                pageWrapStore.getData("[email protected]", 0, 0)
                            )
                        )
                    );
                }
                
            }

        }

        if (_rpage == 0) {
            result = string.concat(
                string(
                    abi.encodePacked(
                        pageWrapStore.getData("[email protected]", 0, 0),
                        pageWrapStore.getData("[email protected]", 0, 0)
                    )
                ),
            result);
        }
        
        if (_rpage == (renderPagesCount - 1)) {
            result = string.concat(
                result,
                string(
                    abi.encodePacked(
                        pageWrapStore.getData("[email protected]", 1, 1), 
                        pageWrapStore.getData("[email protected]", 1, 1)
                    )
                )
            );
        }

        return result;
    }
}

Last updated