Contract Name:
GobsDataSource
Contract Source Code:
File 1 of 1 : GobsDataSource
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title GobsDataSource
* @notice Stores RGBA data, dynamically generates SVGs, and supports metadata traits.
*/
contract GobsDataSource {
address payable internal deployer;
bool private contractSealed = false;
struct Trait {
string traitType;
string value;
}
struct GobData {
bytes imageData; // Encoded RGBA data in row-major order
Trait[] traits; // Array of traits for the token
}
mapping(uint256 => GobData) private gobData; // Stores data for each token
modifier onlyDeployer() {
require(msg.sender == deployer, "Only deployer.");
_;
}
modifier unsealed() {
require(!contractSealed, "Contract is sealed.");
_;
}
constructor() {
deployer = payable(msg.sender);
}
/**
* @notice Seal the contract to prevent further modifications.
*/
function sealContract() external onlyDeployer unsealed {
contractSealed = true;
}
/**
* @notice Store RGBA pixel data for a token in row-major order.
* @param tokenId The ID of the token.
* @param imageData The RGBA data (row-major order, 29x29 pixels).
*/
function storePixelData(uint256 tokenId, bytes memory imageData) external onlyDeployer unsealed {
require(imageData.length == 29 * 29 * 4, "Invalid RGBA data length");
gobData[tokenId].imageData = imageData;
}
/**
* @notice Add traits for a token.
* @param tokenId The ID of the token.
* @param traits The traits to associate with the token.
*/
function storeTraits(uint256 tokenId, Trait[] memory traits) external onlyDeployer unsealed {
delete gobData[tokenId].traits; // Clear existing traits
for (uint256 i = 0; i < traits.length; i++) {
gobData[tokenId].traits.push(traits[i]);
}
}
/**
* @notice Retrieve RGBA pixel data for a token.
* @param tokenId The ID of the token.
* @return The RGBA pixel data.
*/
function getPixelData(uint256 tokenId) external view returns (bytes memory) {
require(gobData[tokenId].imageData.length > 0, "Image data not set");
return gobData[tokenId].imageData;
}
/**
* @notice Retrieve traits for a token.
* @param tokenId The ID of the token.
* @return The traits associated with the token.
*/
function getTraits(uint256 tokenId) external view returns (Trait[] memory) {
require(gobData[tokenId].traits.length > 0, "Traits not set");
return gobData[tokenId].traits;
}
/**
* @notice Dynamically generate SVG from RGBA data using bitwise operations.
* @param tokenId The ID of the token.
* @return The SVG representation of the token.
*/
function getSVG(uint256 tokenId) external view returns (string memory) {
bytes memory pixelData = gobData[tokenId].imageData;
require(pixelData.length > 0, "Image data not set");
string memory svgStart = '<svg xmlns="http://www.w3.org/2000/svg" width="29" height="29" viewBox="0 0 29 29">';
string memory rects;
uint256 index = 0;
for (uint256 y = 0; y < 29; y++) {
for (uint256 x = 0; x < 29; x++) {
uint32 rgba = toUint32(pixelData, index);
index += 4;
if ((rgba & 0xFF000000) != 0) { // Check alpha channel
string memory color = toHexColor(rgba);
rects = string(
abi.encodePacked(
rects,
'<rect x="', uint2str(x), '" y="', uint2str(y),
'" width="1" height="1" fill="', color, '" shape-rendering="crispEdges"/>'
)
);
}
}
}
return string(abi.encodePacked(svgStart, rects, "</svg>"));
}
/**
* @notice Convert a slice of bytes to a uint32.
* @param data The byte array.
* @param index The starting index.
* @return The uint32 representation.
*/
function toUint32(bytes memory data, uint256 index) internal pure returns (uint32) {
require(index + 4 <= data.length, "Index out of bounds");
return uint32(
uint8(data[index]) << 24 |
uint8(data[index + 1]) << 16 |
uint8(data[index + 2]) << 8 |
uint8(data[index + 3])
);
}
/**
* @notice Convert a uint32 RGBA value to a hex color string.
* @param rgba The RGBA value.
* @return The hex color string.
*/
function toHexColor(uint32 rgba) internal pure returns (string memory) {
bytes16 hexSymbols = "0123456789abcdef";
bytes memory buffer = new bytes(9); // '#RRGGBBAA'
buffer[0] = "#";
for (uint256 i = 0; i < 4; i++) {
uint8 value = uint8(rgba >> ((3 - i) * 8));
buffer[1 + i * 2] = hexSymbols[value >> 4];
buffer[2 + i * 2] = hexSymbols[value & 0xF];
}
return string(buffer);
}
/**
* @notice Utility function: Convert uint256 to string.
* @param value The value to convert.
* @return The string representation of the value.
*/
function uint2str(uint256 value) internal pure returns (string memory) {
if (value == 0) return "0";
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
}