Frame
  • Overview
  • Reference
    • Deployed Contracts
    • Technical Reference
      • Frame
      • FrameDataStore
      • FrameFactory
      • FrameDataStoreFactory
    • Contract Source
      • Frame
      • FrameDataStore
      • FrameFactory
      • FrameDataStoreFactory
      • FrameDataStoreOwnable
Powered by GitBook
On this page
  1. Reference
  2. Contract Source

FrameDataStore

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

contract FrameDataStore {
    struct ContractData {
        address rawContract;
        uint128 size;
        uint128 offset;
    }

    struct ContractDataPages {
        uint256 maxPageNumber;
        bool exists;
        mapping(uint256 => ContractData) pages;
    }

    mapping(string => ContractDataPages) internal _contractDataPages;

    mapping(address => bool) internal _controllers;

    bool public isLocked = false;

    string public name;
    string public version;

    constructor() {
    }

    function saveData(
        string memory _key,
        uint128 _pageNumber,
        bytes memory _b
    ) public {
        require(
            _b.length < 24576,
            "FrameDataStore: Exceeded 24,576 bytes max contract size"
        );

        require(
            !hasKey(_key) || getMaxPageNumber(_key) == _pageNumber - 1, 
            "FrameDataStore: Cannot overwrite page or skip next page for key"
        );

        require(
            !isLocked, 
            "FrameDataStore: Contract locked"
        );

        // Create the header for the contract data
        bytes memory init = hex"610000_600e_6000_39_610000_6000_f3";
        bytes1 size1 = bytes1(uint8(_b.length));
        bytes1 size2 = bytes1(uint8(_b.length >> 8));
        init[2] = size1;
        init[1] = size2;
        init[10] = size1;
        init[9] = size2;

        // Prepare the code for storage in a contract
        bytes memory code = abi.encodePacked(init, _b);

        // Create the contract
        address dataContract;
        assembly {
            dataContract := create(0, add(code, 32), mload(code))
            if eq(dataContract, 0) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
        }

        // Store the record of the contract
        _saveDataForDeployedContract(
            _key,
            _pageNumber,
            dataContract,
            uint128(_b.length),
            0
        );
    }

    function _saveDataForDeployedContract(
        string memory _key,
        uint256 _pageNumber,
        address dataContract,
        uint128 _size,
        uint128 _offset
    ) internal {
        // Pull the current data for the contractData
        ContractDataPages storage _cdPages = _contractDataPages[_key];

        // Store the maximum page
        if (_cdPages.maxPageNumber < _pageNumber) {
            _cdPages.maxPageNumber = _pageNumber;
        }

        // Keep track of the existance of this key
        _cdPages.exists = true;

        // Add the page to the location needed
        _cdPages.pages[_pageNumber] = ContractData(
            dataContract,
            _size,
            _offset
        );
    }

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

    function setVersion(string memory _version) public {
        require(bytes(version).length < 3, "FrameDataStore: Version already set");
        version = _version;
    }

    function getSizeBetweenPages(
        string memory _key,
        uint256 _startPage,
        uint256 _endPage
    ) public view returns (uint256) {
        // For all data within the contract data pages, iterate over and compile them
        ContractDataPages storage _cdPages = _contractDataPages[_key];

        // Determine the total size
        uint256 totalSize;
        for (uint256 idx = _startPage; idx <= _endPage; idx++) {
            totalSize += _cdPages.pages[idx].size;
        }

        return totalSize;
    }

    function getMaxPageNumber(string memory _key)
        public
        view
        returns (uint256)
    {
        return _contractDataPages[_key].maxPageNumber;
    }

    function getData(
        string memory _key,
        uint256 _startPage,
        uint256 _endPage
    ) public view returns (bytes memory) {
        // Get the total size
        uint256 totalSize = getSizeBetweenPages(_key, _startPage, _endPage);

        // Create a region large enough for all of the data
        bytes memory _totalData = new bytes(totalSize);

        // Retrieve the pages
        ContractDataPages storage _cdPages = _contractDataPages[_key];

        uint256 endPageNumber = _endPage;

        // For each page, pull and compile
        uint256 currentPointer = 32;
        for (uint256 idx = _startPage; idx <= endPageNumber; idx++) {
            ContractData storage dataPage = _cdPages.pages[idx];
            address dataContract = dataPage.rawContract;
            uint256 size = uint256(dataPage.size);
            uint256 offset = uint256(dataPage.offset);

            // Copy directly to total data
            assembly {
                extcodecopy(
                    dataContract,
                    add(_totalData, currentPointer),
                    offset,
                    size
                )
            }

            // Update the current pointer
            currentPointer += size;
        }

        return _totalData;
    }

    function getAllDataFromPage(
        string memory _key,
        uint256 _startPage
    ) public view returns (bytes memory) {
        ContractDataPages storage _cdPages = _contractDataPages[_key];
        return getData(_key, _startPage, _cdPages.maxPageNumber);
    }

    function hasKey(string memory _key) public view returns (bool) {
        return _contractDataPages[_key].exists;
    }

    function lock() public {
        require(!isLocked, "FrameDataStore: Contract already locked");
        isLocked = true;
    }
}
PreviousFrameNextFrameFactory

Last updated 2 years ago