// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; import {Ownable} from "@openzeppelin/contracts@4.8.2/access/Ownable.sol"; import {IERC165} from "@openzeppelin/contracts@4.8.2/utils/introspection/IERC165.sol"; contract L2Storage is Ownable, IERC165 { function supportsInterface(bytes4 x) external pure returns (bool) { return x == type(IERC165).interfaceId; // 0x01ffc9a7 } event AdminChanged(address indexed op, bool indexed admin); event TokenDataChanged(uint256 tokenId, string indexed key, string value, uint256 nonce); event AddressDataChanged(uint256 addressId, string indexed key, string value, uint256 nonce); mapping (uint256 => mapping(string => string)) _tokens; mapping (uint256 => mapping(string => string)) _addresses; mapping (uint256 => uint256) _tokenNonce; mapping (uint256 => uint256) _addressNonce; mapping (address => bool) _admin; modifier onlyAdmin { require(_admin[msg.sender], "not admin"); _; } constructor() { _admin[msg.sender] = true; } function setAdmin(address op, bool admin) onlyOwner external { _admin[op] = admin; emit AdminChanged(op, admin); } function setTokenData(uint256 tokenId, string memory key, string memory v) onlyAdmin public { _tokens[tokenId][key] = _tokens[tokenId][key] = v; uint256 nonce = ++_tokenNonce[tokenId]; emit TokenDataChanged(tokenId, key, v, nonce); } function setAddressData(uint256 addressId, string memory key, string memory v) onlyAdmin public { _tokens[addressId][key] = _tokens[addressId][key] = v; uint256 nonce = ++_addressNonce[addressId]; emit TokenDataChanged(addressId, key, v, nonce); } function getTokenData(uint256 tokenId, string calldata key) external view returns (string memory v) { v = _tokens[tokenId][key]; } function getAddressData(uint256 addressId, string calldata key) external view returns (string memory v) { v = _addresses[addressId][key]; } function getTokenNonce(uint256 tokenId) external view returns (uint256 nonce) { nonce = _tokenNonce[tokenId]; } function getAddressNonce(uint256 addressId) external view returns (uint256 nonce) { nonce = _addressNonce[addressId]; } function getTokenBatchData(uint256 tokenId, string[] calldata keys) external view returns (uint256 nonce, string[] memory vs) { vs = new string[](keys.length); for (uint256 i = 0; i < keys.length; i++) { vs[i] = _tokens[tokenId][keys[i]]; } nonce = _tokenNonce[tokenId]; } function getAddressBatchData(uint256 addressId, string[] calldata keys) external view returns (uint256 nonce, string[] memory vs) { vs = new string[](keys.length); for (uint256 i = 0; i < keys.length; i++) { vs[i] = _addresses[addressId][keys[i]]; } nonce = _addressNonce[addressId]; } function multicall(bytes[] calldata calls) external returns (bytes[] memory results) { results = new bytes[](calls.length); for(uint i = 0; i < calls.length; i++) { (bool success, bytes memory result) = address(this).delegatecall(calls[i]); if (!success) { assembly { let ptr := mload(0x40) mstore(ptr, returndatasize()) returndatacopy(ptr, 0, returndatasize()) revert(ptr, returndatasize()) } } results[i] = result; } return results; } }