// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {IFxERC1155} from "../../tokens/IFxERC1155.sol"; import {ERC1155Holder} from "../../lib/ERC1155Holder.sol" ; import { Create2 } from "../..//lib/Create2.sol"; import { FxBaseChildTunnel } from "../../tunnel/FxBaseChildTunnel.sol"; contract FxERC1155ChildTunnel is FxBaseChildTunnel, Create2, ERC1155Holder { bytes32 public constant DEPOSIT = keccak256("DEPOSIT"); bytes32 public constant DEPOSIT_BATCH = keccak256("DEPOSIT_BATCH"); bytes32 public constant WITHDRAW = keccak256("WITHDRAW"); bytes32 public constant WITHDRAW_BATCH = keccak256("WITHDRAW_BATCH"); bytes32 public constant MAP_TOKEN = keccak256("MAP_TOKEN"); //string public constant URI = "FXERC1155URI" ; event TokenMapped(address indexed rootToken, address indexed childToken); // root to child token mapping(address => address) public rootToChildToken; address public tokenTemplate; constructor(address _fxChild, address _tokenTemplate) FxBaseChildTunnel(_fxChild) { tokenTemplate = _tokenTemplate; require(_isContract(_tokenTemplate), "Token template is not contract"); } function withdraw(address childToken, uint256 id, uint256 amount, bytes memory data) public { IFxERC1155 childTokenContract = IFxERC1155(childToken); address rootToken = childTokenContract.connectedToken(); require( childToken != address(0x0) && rootToken != address(0x0) && childToken == rootToChildToken[rootToken], "FxERC1155ChildTunnel: NO_MAPPED_TOKEN" ); childTokenContract.burn(msg.sender, id, amount); bytes memory message = abi.encode(WITHDRAW, abi.encode(rootToken, childToken, msg.sender, id, amount, data)); _sendMessageToRoot(message); } function withdrawBatch(address childToken, uint256[] memory ids, uint256[] memory amounts, bytes memory data) public { IFxERC1155 childTokenContract = IFxERC1155(childToken); address rootToken = childTokenContract.connectedToken(); require( childToken != address(0x0) && rootToken != address(0x0) && childToken == rootToChildToken[rootToken], "FxERC1155ChildTunnel: NO_MAPPED_TOKEN" ); childTokenContract.burnBatch(msg.sender, ids, amounts); bytes memory message = abi.encode(WITHDRAW_BATCH, abi.encode(rootToken, childToken, msg.sender, ids, amounts, data)); _sendMessageToRoot(message); } function _processMessageFromRoot(uint256 /* stateId */, address sender, bytes memory data) internal override validateSender(sender) { (bytes32 syncType, bytes memory syncData) = abi.decode(data, (bytes32, bytes)); if(syncType == MAP_TOKEN) { _mapToken(syncData); } else if(syncType == DEPOSIT) { _syncDeposit(syncData); } else if(syncType == DEPOSIT_BATCH) { _syncDepositBatch(syncData); } else { revert("FxERC1155ChildTunnel: INVALID_SYNC_TYPE"); } } function _mapToken(bytes memory syncData) internal returns (address) { (address rootToken, string memory uri) = abi.decode(syncData , (address, string)); address childToken = rootToChildToken[rootToken]; require(childToken == address(0x0), "FxERC1155ChildTunnel: ALREADY_MAPPED"); bytes32 salt = keccak256(abi.encodePacked(rootToken)); childToken = createClone(salt, tokenTemplate); IFxERC1155(childToken).initialize(address(this),rootToken, string(abi.encodePacked(uri))); rootToChildToken[rootToken] = childToken; emit TokenMapped(rootToken, childToken); return childToken; } function _syncDeposit(bytes memory syncData) internal { (address rootToken, address depositor, address user, uint256 id, uint256 amount, bytes memory data) = abi.decode(syncData, (address, address, address, uint256, uint256, bytes)); address childToken = rootToChildToken[rootToken]; IFxERC1155 childTokenContract = IFxERC1155(childToken); childTokenContract.mint(user, id, amount, data); } function _syncDepositBatch(bytes memory syncData) internal { (address rootToken, address depositor, address user, uint256[] memory ids, uint256[] memory amounts, bytes memory data) = abi.decode(syncData, (address, address, address, uint256[], uint256[], bytes)); address childToken = rootToChildToken[rootToken]; IFxERC1155 childTokenContract = IFxERC1155(childToken); childTokenContract.mintBatch(user, ids, amounts, data); } function _isContract(address _addr) private view returns (bool){ uint32 size; assembly { size := extcodesize(_addr) } return (size > 0); } }