// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {ERC1155} from "../../lib/ERC1155.sol"; import {ERC1155Holder} from "../../lib/ERC1155Holder.sol" ; import { Create2 } from "../../lib/Create2.sol"; import { FxBaseRootTunnel } from "../../tunnel/FxBaseRootTunnel.sol"; contract FxERC1155RootTunnel is FxBaseRootTunnel, 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"); event TokenMappedERC1155(address indexed rootToken, address indexed childToken); event FxWithdrawERC1155(address indexed rootToken, address indexed childToken, address indexed userAddress, uint256 id, uint256 amount); event FxDepositERC1155(address indexed rootToken, address indexed depositor, address indexed userAddress, uint256 id, uint256 amount); event FxWithdrawBatchERC1155(address indexed rootToken, address indexed childToken, address indexed userAddress, uint256[] ids, uint256[] amounts); event FxDepositBatchERC1155(address indexed rootToken, address indexed userAddress, uint256[] ids, uint256[] amounts); mapping(address => address) public rootToChildTokens; bytes32 public childTokenTemplateCodeHash; constructor(address _checkpointManager, address _fxRoot, address _fxERC1155Token) FxBaseRootTunnel(_checkpointManager, _fxRoot) { childTokenTemplateCodeHash = keccak256(minimalProxyCreationCode(_fxERC1155Token)); } function mapToken(address rootToken) public { require(rootToChildTokens[rootToken] == address(0x0), "FxERC1155RootTunnel: ALREADY_MAPPED"); ERC1155 rootTokenContract = ERC1155(rootToken); string memory uri = rootTokenContract.uri(0); //token Id? // MAP_TOKEN, encode(rootToken,uri) bytes memory message = abi.encode(MAP_TOKEN, abi.encode(rootToken,uri)); _sendMessageToChild(message); // compute child token address before deployment using create2 bytes32 salt = keccak256(abi.encodePacked(rootToken)); address childToken = computedCreate2Address(salt, childTokenTemplateCodeHash, fxChildTunnel); // add into mapped tokens rootToChildTokens[rootToken] = childToken; emit TokenMappedERC1155(rootToken, childToken); } function deposit(address rootToken, address user, uint256 id, uint256 amount, bytes memory data) public { // map token if not mapped if (rootToChildTokens[rootToken] == address(0x0)) { mapToken(rootToken); } // transfer from depositor to this contract ERC1155(rootToken).safeTransferFrom( msg.sender, // depositor address(this), // manager contract id, amount, data ); // DEPOSIT, encode(rootToken, depositor, user, id, amount, extra data) bytes memory message = abi.encode(DEPOSIT, abi.encode(rootToken, msg.sender, user, id, amount, data)); _sendMessageToChild(message); emit FxDepositERC1155(rootToken, msg.sender, user, id, amount); } function depositBatch(address rootToken, address user, uint256[] memory ids, uint256[] memory amounts, bytes memory data) public { // map token if not mapped if (rootToChildTokens[rootToken] == address(0x0)) { mapToken(rootToken); } // transfer from depositor to this contract ERC1155(rootToken).safeBatchTransferFrom( msg.sender, // depositor address(this), // manager contract ids, amounts, data ); // DEPOSIT_BATCH, encode(rootToken, depositor, user, id, amount, extra data) bytes memory message = abi.encode(DEPOSIT_BATCH, abi.encode(rootToken, msg.sender, user, ids, amounts, data)); _sendMessageToChild(message); emit FxDepositBatchERC1155(rootToken, user, ids, amounts); } function _processMessageFromChild(bytes memory data) internal override { (bytes32 syncType, bytes memory syncData) = abi.decode(data, (bytes32, bytes)); if(syncType == WITHDRAW) { _syncWithdraw(syncData); } else if(syncType == WITHDRAW_BATCH) { _syncBatchWithdraw(syncData); } else { revert("FxERC1155RootTunnel: INVALID_SYNC_TYPE"); } } function _syncWithdraw(bytes memory syncData) internal { (address rootToken, address childToken, address user, uint256 id, uint256 amount, bytes memory data) = abi.decode(syncData, (address, address, address, uint256, uint256, bytes)); require(rootToChildTokens[rootToken] == childToken, "FxERC1155RootTunnel: INVALID_MAPPING_ON_EXIT"); ERC1155(rootToken).safeTransferFrom(address(this), user, id, amount, data); emit FxWithdrawERC1155(rootToken, childToken, user, id, amount); } function _syncBatchWithdraw(bytes memory syncData) internal { (address rootToken, address childToken, address user, uint256[] memory ids, uint256[] memory amounts, bytes memory data) = abi.decode(syncData, (address, address, address, uint256[], uint256[], bytes)); require(rootToChildTokens[rootToken] == childToken, "FxERC1155RootTunnel: INVALID_MAPPING_ON_EXIT"); ERC1155(rootToken).safeBatchTransferFrom(address(this), user, ids, amounts, data); emit FxWithdrawBatchERC1155(rootToken, childToken, user, ids, amounts); } }