文件 1 的 10:AssetStore.sol
pragma solidity ^0.8.6;
import { Ownable } from '@openzeppelin/contracts/access/Ownable.sol';
import { IAssetStoreRegistry, IAssetStore } from './interfaces/IAssetStore.sol';
import { IStringValidator } from './interfaces/IStringValidator.sol';
import "@openzeppelin/contracts/utils/Strings.sol";
import './libs/StringValidator.sol';
import './libs/StringSet.sol';
import './libs/SVGPathDecoder.sol';
abstract contract AssetStoreCore is Ownable, IAssetStoreRegistry {
using StringSet for StringSet.Set;
using Strings for uint16;
using Strings for uint256;
struct Asset {
uint32 groupId;
uint32 categoryId;
string name;
string minter;
bytes metadata;
address soulbound;
uint256[] partsIds;
}
IStringValidator public validator;
IPathDecoder public decoder;
mapping(uint256 => Asset) private assets;
uint256 private nextAssetIndex = 1;
mapping(uint256 => Part) private parts;
uint256 private nextPartIndex = 1;
StringSet.Set internal groupSet;
mapping(uint32 => StringSet.Set) internal categorySets;
struct AssetCatalog {
mapping(uint32 => uint256) assetIds;
uint32 nextAssetIndex;
mapping(string => uint256) assetNameToId;
}
mapping(uint32 => mapping(uint32 => AssetCatalog)) internal assetCatalogs;
constructor() {
validator = new StringValidator();
decoder = new SVGPathDecoder();
}
function _getGroupId(string memory group) private returns(uint32) {
(uint32 id, bool created) = groupSet.getOrCreateId(group, validator);
if (created) {
emit GroupAdded(group);
}
return id;
}
function _getCategoryId(string memory group, uint32 groupId, string memory category) private returns(uint32) {
StringSet.Set storage categorySet = categorySets[groupId];
(uint32 id, bool created) = categorySet.getOrCreateId(category, validator);
if (created) {
emit CategoryAdded(group, category);
}
return id;
}
function _registerPart(Part memory _part) private returns(uint256) {
parts[nextPartIndex++] = _part;
return nextPartIndex-1;
}
modifier validateAsset(AssetInfo memory _assetInfo) {
uint size = _assetInfo.parts.length;
uint i;
for (i=0; i < size; i++) {
Part memory part = _assetInfo.parts[i];
require(validator.validate(bytes(part.color)), "Invalid AssetData Color");
}
require(validator.validate(bytes(_assetInfo.name)), "Invalid AssetData Name");
_;
}
function _registerAsset(AssetInfo memory _assetInfo) internal validateAsset(_assetInfo) returns(uint256) {
uint32 groupId = _getGroupId(_assetInfo.group);
uint32 categoryId = _getCategoryId(_assetInfo.group, groupId, _assetInfo.category);
uint size = _assetInfo.parts.length;
uint256[] memory partsIds = new uint256[](size);
uint i;
for (i=0; i<size; i++) {
partsIds[i] = _registerPart(_assetInfo.parts[i]);
}
uint256 assetId = nextAssetIndex++;
Asset storage asset = assets[assetId];
asset.name = _assetInfo.name;
asset.soulbound = _assetInfo.soulbound;
uint minterLength = bytes(_assetInfo.minter).length;
if (minterLength > 0) {
require(minterLength <= 32, "AssetSgore: _registerAsset, minter name is too long.");
asset.minter = _assetInfo.minter;
}
if (_assetInfo.metadata.length > 0) {
asset.metadata = _assetInfo.metadata;
}
asset.groupId = groupId;
asset.categoryId = categoryId;
asset.partsIds = partsIds;
AssetCatalog storage assetCatalog = assetCatalogs[groupId][categoryId];
require(assetCatalog.assetNameToId[_assetInfo.name] == 0, "Asset already exists with the same group, category and name");
assetCatalog.assetIds[assetCatalog.nextAssetIndex++] = assetId;
assetCatalog.assetNameToId[_assetInfo.name] = assetId;
emit AssetRegistered(msg.sender, assetId);
return assetId;
}
function getAssetCount() external view returns(uint256) {
return nextAssetIndex - 1;
}
modifier assetExists(uint256 _assetId) {
require(_assetId > 0 && _assetId < nextAssetIndex, "AssetStore: assetId is out of range");
_;
}
modifier partExists(uint256 _partId) {
require(_partId > 0 && _partId < nextPartIndex, "partId is out of range");
_;
}
function _getAsset(uint256 _assetId) internal view assetExists(_assetId) returns(Asset memory) {
return assets[_assetId];
}
function _getPart(uint256 _partId) internal view partExists(_partId) returns(Part memory) {
return parts[_partId];
}
}
abstract contract AssetStoreAdmin is AssetStoreCore {
address public admin;
mapping(address => bool) whitelist;
bool disableWhitelist = false;
mapping(uint256 => bool) disabled;
constructor() {
whitelist[msg.sender] = true;
admin = owner();
}
modifier onlyAdmin() {
require(owner() == _msgSender() || admin == _msgSender(), "AssetStoreAdmin: caller is not the admin");
_;
}
function setAdmin(address _admin) external onlyOwner {
admin = _admin;
}
function setWhitelistStatus(address _address, bool _status) external onlyAdmin {
whitelist[_address] = _status;
}
function setDisabled(uint256 _assetId, bool _status) external assetExists(_assetId) onlyAdmin {
disabled[_assetId] = _status;
}
function setDisableWhitelist(bool _disable) external onlyAdmin {
disableWhitelist = _disable;
}
function setValidator(IStringValidator _validator) external onlyAdmin {
validator = _validator;
}
function setPathDecoder(IPathDecoder _decoder) external onlyAdmin {
decoder = _decoder;
}
function getRawAsset(uint256 _assetId) external view onlyAdmin returns(Asset memory) {
return _getAsset(_assetId);
}
function getRawPart(uint256 _partId) external view onlyAdmin returns(Part memory) {
return _getPart(_partId);
}
}
contract AppStoreRegistory is AssetStoreAdmin {
modifier onlyWhitelist {
require(disableWhitelist || whitelist[msg.sender], "AssetStore: The sender must be in the white list.");
_;
}
function registerAsset(AssetInfo memory _assetInfo) external override onlyWhitelist returns(uint256) {
return _registerAsset(_assetInfo);
}
function registerAssets(AssetInfo[] memory _assetInfos) external override onlyWhitelist {
uint i;
for (i=0; i<_assetInfos.length; i++) {
_registerAsset(_assetInfos[i]);
}
}
}
contract AssetStore is AppStoreRegistory, IAssetStore {
using Strings for uint16;
using Strings for uint256;
using StringSet for StringSet.Set;
modifier enabled(uint256 _assetId) {
require(disabled[_assetId] != true, "AssetStore: this asset is diabled");
_;
}
function getGroupCount() external view override returns(uint32) {
return groupSet.getCount();
}
function getGroupNameAtIndex(uint32 _groupIndex) external view override returns(string memory) {
return groupSet.nameAtIndex(_groupIndex);
}
function getCategoryCount(string memory _group) external view override returns(uint32) {
return categorySets[groupSet.getId(_group)].getCount();
}
function getCategoryNameAtIndex(string memory _group, uint32 _categoryIndex) external view override returns(string memory) {
return categorySets[groupSet.getId(_group)].nameAtIndex(_categoryIndex);
}
function getAssetCountInCategory(string memory _group, string memory _category) external view override returns(uint32) {
uint32 groupId = groupSet.getId(_group);
StringSet.Set storage categorySet = categorySets[groupId];
return assetCatalogs[groupId][categorySet.getId(_category)].nextAssetIndex;
}
function getAssetIdInCategory(string memory _group, string memory _category, uint32 _assetIndex) external view override returns(uint256) {
uint32 groupId = groupSet.getId(_group);
StringSet.Set storage categorySet = categorySets[groupId];
AssetCatalog storage assetCatalog = assetCatalogs[groupId][categorySet.getId(_category)];
require(_assetIndex < assetCatalog.nextAssetIndex, "The assetIndex is out of range");
return assetCatalog.assetIds[_assetIndex];
}
function getAssetIdWithName(string memory _group, string memory _category, string memory _name) external override view returns(uint256) {
uint32 groupId = groupSet.getId(_group);
StringSet.Set storage categorySet = categorySets[groupId];
return assetCatalogs[groupId][categorySet.getId(_category)].assetNameToId[_name];
}
function _getDescription(Asset memory asset) internal view returns(bytes memory) {
string memory group = groupSet.nameAtIndex(asset.groupId - 1);
return abi.encodePacked(group, '/', categorySets[asset.groupId].nameAtIndex(asset.categoryId - 1), '/', asset.name);
}
function _tagForAsset(uint256 _assetId) internal pure returns(string memory) {
return string(abi.encodePacked('asset', _assetId.toString()));
}
function _safeGenerateSVGPart(uint256 _assetId, string memory _tag) internal view returns(bytes memory) {
Asset memory asset = _getAsset(_assetId);
uint256[] memory indeces = asset.partsIds;
bytes memory pack = abi.encodePacked(' <g id="', _tag, '" desc="', _getDescription(asset), '">\n');
uint i;
for (i=0; i<indeces.length; i++) {
Part memory part = _getPart(indeces[i]);
bytes memory color;
if (bytes(part.color).length > 0) {
color = abi.encodePacked(' fill="', part.color ,'"');
}
pack = abi.encodePacked(pack, ' <path d="', decoder.decodePath(part.body), '"', color,' />\n');
}
pack = abi.encodePacked(pack, ' </g>\n');
return pack;
}
function generateSVGPart(uint256 _assetId, string memory _tag) external override view enabled(_assetId) returns(string memory) {
return string(_safeGenerateSVGPart(_assetId, _tag));
}
function generateSVG(uint256 _assetId) external override view enabled(_assetId) returns(string memory) {
bytes memory pack = abi.encodePacked(
'<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">\n',
_safeGenerateSVGPart(_assetId, _tagForAsset(_assetId)),
'</svg>');
return string(pack);
}
function getAttributes(uint256 _assetId) external view override returns(AssetAttributes memory) {
Asset memory asset = _getAsset(_assetId);
AssetAttributes memory attr;
attr.name = asset.name;
attr.tag = _tagForAsset(_assetId);
attr.soulbound = asset.soulbound;
attr.minter = asset.minter;
attr.metadata = asset.metadata;
attr.group = groupSet.nameAtIndex(asset.groupId - 1);
attr.category = categorySets[asset.groupId].nameAtIndex(asset.categoryId - 1);
attr.width = 1024;
attr.height = 1024;
return attr;
}
function getStringValidator() external override view returns(IStringValidator) {
return validator;
}
}
文件 2 的 10:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 3 的 10:IAssetStore.sol
pragma solidity ^0.8.6;
import { IStringValidator } from './IStringValidator.sol';
interface IAssetStore {
function getGroupCount() external view returns(uint32);
function getGroupNameAtIndex(uint32 _groupIndex) external view returns(string memory);
function getCategoryCount(string memory _group) external view returns(uint32);
function getCategoryNameAtIndex(string memory _group, uint32 _categoryIndex) external view returns(string memory);
function getAssetCountInCategory(string memory _group, string memory _category) external view returns(uint32);
function getAssetIdInCategory(string memory _group, string memory _category, uint32 _assetIndex) external view returns(uint256);
function getAssetIdWithName(string memory _group, string memory _category, string memory _name) external view returns(uint256);
struct AssetAttributes {
string group;
string category;
string name;
string tag;
string minter;
address soulbound;
bytes metadata;
uint16 width;
uint16 height;
}
function generateSVG(uint256 _assetId) external view returns(string memory);
function generateSVGPart(uint256 _assetId, string memory _tag) external view returns(string memory);
function getAttributes(uint256 _assetId) external view returns(AssetAttributes memory);
function getStringValidator() external view returns(IStringValidator);
}
interface IAssetStoreRegistry {
struct Part {
bytes body;
string color;
}
struct AssetInfo {
string group;
string category;
string name;
string minter;
address soulbound;
bytes metadata;
Part[] parts;
}
event AssetRegistered(address from, uint256 assetId);
event GroupAdded(string group);
event CategoryAdded(string group, string category);
function registerAsset(AssetInfo memory _assetInfo) external returns(uint256);
function registerAssets(AssetInfo[] memory _assetInfos) external;
}
文件 4 的 10:IPathDecoder.sol
pragma solidity ^0.8.6;
interface IPathDecoder {
function decodePath(bytes memory body) external pure returns (bytes memory);
}
文件 5 的 10:IStringValidator.sol
pragma solidity ^0.8.6;
interface IStringValidator {
function validate(bytes memory str) external pure returns (bool);
function sanitizeJason(string memory _str) external pure returns(bytes memory);
}
文件 6 的 10:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 7 的 10:SVGPathDecoder.sol
import "@openzeppelin/contracts/utils/Strings.sol";
import "../interfaces/IPathDecoder.sol";
pragma solidity ^0.8.6;
contract SVGPathDecoder is IPathDecoder {
using Strings for uint16;
function decodePath(bytes memory body) external pure override returns (bytes memory) {
bytes memory ret;
uint16 i;
uint16 length = (uint16(body.length) * 2)/ 3;
for (i = 0; i < length; i++) {
uint16 offset = i / 2 * 3;
uint8 low;
uint8 high;
if (i % 2 == 0) {
low = uint8(body[offset]);
high = uint8(body[offset + 1]) % 0x10;
} else {
low = uint8(body[offset + 2]);
high = uint8(body[offset + 1]) / 0x10;
}
if (high == 0) {
if ((low >=65 && low<=90) || (low >= 97 && low <= 122)) {
ret = abi.encodePacked(ret, low);
}
} else {
uint16 value = uint16(high) * 0x100 + uint16(low) - 0x100;
if (value >= 1024) {
ret = abi.encodePacked(ret, (value - 1024).toString(), " ");
} else {
ret = abi.encodePacked(ret, "-", (1024 - value).toString(), " ");
}
}
}
return ret;
}
}
文件 8 的 10:StringSet.sol
pragma solidity ^0.8.6;
import { IStringValidator } from '../interfaces/IStringValidator.sol';
library StringSet {
struct Set {
mapping(uint32 => string) names;
uint32 nextIndex;
mapping(string => uint32) ids;
}
function getOrCreateId(Set storage _set, string memory _name, IStringValidator _validator) internal returns(uint32, bool) {
uint32 id = _set.ids[_name];
if (id > 0) {
return (id, false);
}
require(_validator.validate(bytes(_name)), "StringSet.getId: Invalid String");
_set.names[_set.nextIndex++] = _name;
id = _set.nextIndex;
_set.ids[_name] = id;
return (id, true);
}
function getId(Set storage _set, string memory _name) internal view returns (uint32) {
uint32 id = _set.ids[_name];
require(id > 0, "StringSet: the specified name does not exist");
return id;
}
function getCount(Set storage _set) internal view returns (uint32) {
return _set.nextIndex;
}
function nameAtIndex(Set storage _set, uint32 _index) internal view returns(string memory) {
require(_index < _set.nextIndex, "StringSet.nameAtIndex: The index is out of range");
return _set.names[_index];
}
}
文件 9 的 10:StringValidator.sol
import { IStringValidator } from '../interfaces/IStringValidator.sol';
pragma solidity ^0.8.6;
contract StringValidator is IStringValidator {
function validate(bytes memory str) external pure override returns (bool) {
for(uint i; i < str.length; i++){
bytes1 char = str[i];
if(!(
(char >= 0x30 && char <= 0x39) ||
(char >= 0x41 && char <= 0x5A) ||
(char >= 0x61 && char <= 0x7A) ||
(char == 0x20) ||
(char == 0x23) ||
(char == 0x28) ||
(char == 0x29) ||
(char == 0x2C) ||
(char == 0x2D) ||
(char == 0x2E)
)) {
return false;
}
}
return true;
}
function sanitizeJason(string memory _str) external override pure returns(bytes memory) {
bytes memory src = bytes(_str);
bytes memory res;
uint i;
for (i=0; i<src.length; i++) {
uint8 b = uint8(src[i]);
if (b >= 0x20) {
if (b == 0x5c || b == 0x22) {
res = abi.encodePacked(res, bytes1(0x5c));
}
res = abi.encodePacked(res, b);
}
}
return res;
}
}
文件 10 的 10:Strings.sol
pragma solidity ^0.8.0;
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
function toString(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);
}
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
{
"compilationTarget": {
"contracts/AssetStore.sol": "AssetStore"
},
"evmVersion": "berlin",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"assetId","type":"uint256"}],"name":"AssetRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"group","type":"string"},{"indexed":false,"internalType":"string","name":"category","type":"string"}],"name":"CategoryAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"group","type":"string"}],"name":"GroupAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decoder","outputs":[{"internalType":"contract IPathDecoder","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assetId","type":"uint256"}],"name":"generateSVG","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assetId","type":"uint256"},{"internalType":"string","name":"_tag","type":"string"}],"name":"generateSVGPart","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAssetCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_group","type":"string"},{"internalType":"string","name":"_category","type":"string"}],"name":"getAssetCountInCategory","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_group","type":"string"},{"internalType":"string","name":"_category","type":"string"},{"internalType":"uint32","name":"_assetIndex","type":"uint32"}],"name":"getAssetIdInCategory","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_group","type":"string"},{"internalType":"string","name":"_category","type":"string"},{"internalType":"string","name":"_name","type":"string"}],"name":"getAssetIdWithName","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assetId","type":"uint256"}],"name":"getAttributes","outputs":[{"components":[{"internalType":"string","name":"group","type":"string"},{"internalType":"string","name":"category","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"tag","type":"string"},{"internalType":"string","name":"minter","type":"string"},{"internalType":"address","name":"soulbound","type":"address"},{"internalType":"bytes","name":"metadata","type":"bytes"},{"internalType":"uint16","name":"width","type":"uint16"},{"internalType":"uint16","name":"height","type":"uint16"}],"internalType":"struct IAssetStore.AssetAttributes","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_group","type":"string"}],"name":"getCategoryCount","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_group","type":"string"},{"internalType":"uint32","name":"_categoryIndex","type":"uint32"}],"name":"getCategoryNameAtIndex","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGroupCount","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_groupIndex","type":"uint32"}],"name":"getGroupNameAtIndex","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assetId","type":"uint256"}],"name":"getRawAsset","outputs":[{"components":[{"internalType":"uint32","name":"groupId","type":"uint32"},{"internalType":"uint32","name":"categoryId","type":"uint32"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"minter","type":"string"},{"internalType":"bytes","name":"metadata","type":"bytes"},{"internalType":"address","name":"soulbound","type":"address"},{"internalType":"uint256[]","name":"partsIds","type":"uint256[]"}],"internalType":"struct AssetStoreCore.Asset","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_partId","type":"uint256"}],"name":"getRawPart","outputs":[{"components":[{"internalType":"bytes","name":"body","type":"bytes"},{"internalType":"string","name":"color","type":"string"}],"internalType":"struct IAssetStoreRegistry.Part","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStringValidator","outputs":[{"internalType":"contract IStringValidator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"group","type":"string"},{"internalType":"string","name":"category","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"minter","type":"string"},{"internalType":"address","name":"soulbound","type":"address"},{"internalType":"bytes","name":"metadata","type":"bytes"},{"components":[{"internalType":"bytes","name":"body","type":"bytes"},{"internalType":"string","name":"color","type":"string"}],"internalType":"struct IAssetStoreRegistry.Part[]","name":"parts","type":"tuple[]"}],"internalType":"struct IAssetStoreRegistry.AssetInfo","name":"_assetInfo","type":"tuple"}],"name":"registerAsset","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"group","type":"string"},{"internalType":"string","name":"category","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"minter","type":"string"},{"internalType":"address","name":"soulbound","type":"address"},{"internalType":"bytes","name":"metadata","type":"bytes"},{"components":[{"internalType":"bytes","name":"body","type":"bytes"},{"internalType":"string","name":"color","type":"string"}],"internalType":"struct IAssetStoreRegistry.Part[]","name":"parts","type":"tuple[]"}],"internalType":"struct IAssetStoreRegistry.AssetInfo[]","name":"_assetInfos","type":"tuple[]"}],"name":"registerAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_disable","type":"bool"}],"name":"setDisableWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assetId","type":"uint256"},{"internalType":"bool","name":"_status","type":"bool"}],"name":"setDisabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IPathDecoder","name":"_decoder","type":"address"}],"name":"setPathDecoder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IStringValidator","name":"_validator","type":"address"}],"name":"setValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"bool","name":"_status","type":"bool"}],"name":"setWhitelistStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"validator","outputs":[{"internalType":"contract IStringValidator","name":"","type":"address"}],"stateMutability":"view","type":"function"}]