pragma solidity ^0.5.1; import { IERC20 } from "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; import { ConditionalTokens } from "@gnosis.pm/conditional-tokens-contracts/contracts/ConditionalTokens.sol"; import { CTHelpers } from "@gnosis.pm/conditional-tokens-contracts/contracts/CTHelpers.sol"; import { Create2CloneFactory } from "./Create2CloneFactory.sol"; import { FixedProductMarketMaker, FixedProductMarketMakerData } from "./FixedProductMarketMaker.sol"; import { ERC1155TokenReceiver } from "@gnosis.pm/conditional-tokens-contracts/contracts/ERC1155/ERC1155TokenReceiver.sol"; contract FPMMDeterministicFactory is Create2CloneFactory, FixedProductMarketMakerData, ERC1155TokenReceiver { event FixedProductMarketMakerCreation( address indexed creator, FixedProductMarketMaker fixedProductMarketMaker, ConditionalTokens conditionalTokens, IERC20 collateralToken, bytes32[] conditionIds, uint fee ); FixedProductMarketMaker public implementationMaster; address internal currentFunder; constructor() public { implementationMaster = new FixedProductMarketMaker(); } function cloneConstructor(bytes calldata consData) external { ( ConditionalTokens _conditionalTokens, IERC20 _collateralToken, bytes32[] memory _conditionIds, uint _fee ) = abi.decode(consData, (ConditionalTokens, IERC20, bytes32[], uint)); _supportedInterfaces[_INTERFACE_ID_ERC165] = true; _supportedInterfaces[ ERC1155TokenReceiver(0).onERC1155Received.selector ^ ERC1155TokenReceiver(0).onERC1155BatchReceived.selector ] = true; conditionalTokens = _conditionalTokens; collateralToken = _collateralToken; conditionIds = _conditionIds; fee = _fee; uint atomicOutcomeSlotCount = 1; outcomeSlotCounts = new uint[](conditionIds.length); for (uint i = 0; i < conditionIds.length; i++) { uint outcomeSlotCount = conditionalTokens.getOutcomeSlotCount(conditionIds[i]); atomicOutcomeSlotCount *= outcomeSlotCount; outcomeSlotCounts[i] = outcomeSlotCount; } require(atomicOutcomeSlotCount > 1, "conditions must be valid"); collectionIds = new bytes32[][](conditionIds.length); _recordCollectionIDsForAllConditions(conditionIds.length, bytes32(0)); require(positionIds.length == atomicOutcomeSlotCount, "position IDs construction failed!?"); } function _recordCollectionIDsForAllConditions(uint conditionsLeft, bytes32 parentCollectionId) private { if(conditionsLeft == 0) { positionIds.push(CTHelpers.getPositionId(collateralToken, parentCollectionId)); return; } conditionsLeft--; uint outcomeSlotCount = outcomeSlotCounts[conditionsLeft]; collectionIds[conditionsLeft].push(parentCollectionId); for(uint i = 0; i < outcomeSlotCount; i++) { _recordCollectionIDsForAllConditions( conditionsLeft, CTHelpers.getCollectionId( parentCollectionId, conditionIds[conditionsLeft], 1 << i ) ); } } function onERC1155Received( address operator, address from, uint256 id, uint256 value, bytes calldata data ) external returns (bytes4) { ConditionalTokens(msg.sender).safeTransferFrom(address(this), currentFunder, id, value, data); return this.onERC1155Received.selector; } function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external returns (bytes4) { ConditionalTokens(msg.sender).safeBatchTransferFrom(address(this), currentFunder, ids, values, data); return this.onERC1155BatchReceived.selector; } function create2FixedProductMarketMaker( uint saltNonce, ConditionalTokens conditionalTokens, IERC20 collateralToken, bytes32[] calldata conditionIds, uint fee, uint initialFunds, uint[] calldata distributionHint ) external returns (FixedProductMarketMaker) { FixedProductMarketMaker fixedProductMarketMaker = FixedProductMarketMaker( create2Clone(address(implementationMaster), saltNonce, abi.encode( conditionalTokens, collateralToken, conditionIds, fee )) ); emit FixedProductMarketMakerCreation( msg.sender, fixedProductMarketMaker, conditionalTokens, collateralToken, conditionIds, fee ); if (initialFunds > 0) { currentFunder = msg.sender; collateralToken.transferFrom(msg.sender, address(this), initialFunds); collateralToken.approve(address(fixedProductMarketMaker), initialFunds); fixedProductMarketMaker.addFunding(initialFunds, distributionHint); fixedProductMarketMaker.transfer(msg.sender, fixedProductMarketMaker.balanceOf(address(this))); currentFunder = address(0); } return fixedProductMarketMaker; } }