// SPDX-License-Identifier: MIT pragma solidity =0.8.18; import "@openzeppelin/contracts-upgradeable/metatx/ERC2771ContextUpgradeable.sol"; import "../../SomaAccessControl/utils/Accessible.sol"; import "../../TemplateFactory/TemplateFactoryLibrary.sol"; import "../../TemplateFactory/TemplateDeployer.sol"; import "./interfaces/ISomaSwapFactory.sol"; import "./interfaces/ISomaSwapPair.sol"; import "./SomaSwapPair.sol"; /** * @notice Implementation of the {ISomaSwapFactory} interface. */ contract SomaSwapFactory is ISomaSwapFactory, TemplateDeployer, Accessible { using TemplateFactoryLibrary for ITemplateFactory; /** * @notice Returns the name of the contract. */ string public constant name = "SomaSwapFactory"; /** * @inheritdoc ISomaSwapFactory */ bytes32 public constant override FEE_SETTER_ROLE = keccak256("SomaSwapFactory.FEE_SETTER_ROLE"); /** * @inheritdoc ISomaSwapFactory */ bytes32 public constant override CREATE_PAIR_ROLE = keccak256("SomaSwapFactory.CREATE_PAIR_ROLE"); /** * @inheritdoc ISomaSwapFactory */ bytes32 public constant override MANAGE_ROUTER_ROLE = keccak256("SomaSwapFactory.MANAGE_ROUTER_ROLE"); /** * @inheritdoc ISomaSwapFactory */ address public override feeTo; /** * @inheritdoc ISomaSwapFactory */ mapping(address => mapping(address => address)) public override getPair; /** * @inheritdoc ISomaSwapFactory */ mapping(address => bool) public override isRouter; /** * @inheritdoc ISomaSwapFactory */ address[] public override allPairs; /** * @notice Constructor of the contract * @param templateVersion The template version to construct. * @custom:requirement The template must not be deprecated. */ constructor(uint256 templateVersion) TemplateDeployer(bytes32("SomaSwapPair"), templateVersion) {} /** * @notice Checks if SomaSwapFactory inherits a given contract interface. * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(ISomaSwapFactory).interfaceId || super.supportsInterface(interfaceId); } /** * @inheritdoc ISomaSwapFactory */ function allPairsLength() external view override returns (uint256) { return allPairs.length; } /** * @inheritdoc ISomaSwapFactory */ function addRouter(address router) external override onlyRole(MANAGE_ROUTER_ROLE) { isRouter[router] = true; emit RouterAdded(router, _msgSender()); } /** * @inheritdoc ISomaSwapFactory */ function removeRouter(address router) external override onlyRole(MANAGE_ROUTER_ROLE) { isRouter[router] = false; emit RouterRemoved(router, _msgSender()); } /** * @inheritdoc ISomaSwapFactory */ function createPair(address tokenA, address tokenB) external override whenNotPaused onlyRole(CREATE_PAIR_ROLE) returns (address pair) { require(tokenA != tokenB, "SomaSwapFactory: IDENTICAL_ADDRESSES"); (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); require(token0 != address(0), "SomaSwapFactory: ZERO_ADDRESS"); require(getPair[token0][token1] == address(0), "SomaSwapFactory: PAIR_EXISTS"); // single check is sufficient // slither-disable-next-line reentrancy-no-eth pair = _deploy(keccak256(abi.encodePacked(token0, token1))); // slither-disable-next-line reentrancy-no-eth ISomaSwapPair(pair).initialize(token0, token1); getPair[token0][token1] = pair; getPair[token1][token0] = pair; // populate mapping in the reverse direction allPairs.push(pair); emit PairCreated(token0, token1, pair, allPairs.length); } /** * @inheritdoc ISomaSwapFactory */ function setFeeTo(address _feeTo) external override onlyRole(FEE_SETTER_ROLE) whenNotPaused { emit FeeToUpdated(feeTo, _feeTo, _msgSender()); feeTo = _feeTo; } }