diff --git a/lib/katana-v3-contracts b/lib/katana-v3-contracts index dd82739..a61849f 160000 --- a/lib/katana-v3-contracts +++ b/lib/katana-v3-contracts @@ -1 +1 @@ -Subproject commit dd827391e22db58687b27bfa3622f40ccf66661e +Subproject commit a61849f350314794a0bd8e44c77acc15277754d0 diff --git a/remappings.txt b/remappings.txt index aa6782f..ca33729 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,8 +1,5 @@ -@fdk/=lib/foundry-deployment-kit/script/ -@contract-libs/=lib/foundry-deployment-kit/lib/contract-libs/src/ -forge-std/=lib/foundry-deployment-kit/lib/forge-std/src/ -@solady/=lib/foundry-deployment-kit/lib/solady/src/ @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ +@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ solmate/=lib/solmate/ permit2/=lib/permit2/ @katana/v3-contracts/=lib/katana-v3-contracts/src/ \ No newline at end of file diff --git a/script/GeneralConfig.sol b/script/GeneralConfig.sol deleted file mode 100644 index 2b88335..0000000 --- a/script/GeneralConfig.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.23; - -import { BaseGeneralConfig } from "@fdk/BaseGeneralConfig.sol"; -import { Contract } from "./utils/Contract.sol"; -import { Network } from "./utils/Network.sol"; - -contract GeneralConfig is BaseGeneralConfig { - constructor() BaseGeneralConfig("", "deployments/") { } - - function _setUpNetworks() internal virtual override { - setNetworkInfo( - Network.Goerli.chainId(), - Network.Goerli.key(), - Network.Goerli.chainAlias(), - Network.Goerli.deploymentDir(), - Network.Goerli.envLabel(), - Network.Goerli.explorer() - ); - setNetworkInfo( - Network.EthMainnet.chainId(), - Network.EthMainnet.key(), - Network.EthMainnet.chainAlias(), - Network.EthMainnet.deploymentDir(), - Network.EthMainnet.envLabel(), - Network.EthMainnet.explorer() - ); - setNetworkInfo( - Network.RoninDevnet.chainId(), - Network.RoninDevnet.key(), - Network.RoninDevnet.chainAlias(), - Network.RoninDevnet.deploymentDir(), - Network.RoninDevnet.envLabel(), - Network.RoninDevnet.explorer() - ); - } - - function _setUpContracts() internal virtual override { - _mapContractName(Contract.Counter); - } - - function _mapContractName(Contract contractEnum) internal { - _contractNameMap[contractEnum.key()] = contractEnum.name(); - } -} diff --git a/script/Migration.s.sol b/script/Migration.s.sol deleted file mode 100644 index 4a5eb4e..0000000 --- a/script/Migration.s.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.23; - -import { BaseMigration } from "@fdk/BaseMigration.s.sol"; -import { DefaultNetwork } from "@fdk/utils/DefaultNetwork.sol"; -import { ISharedArgument } from "./interfaces/ISharedArgument.sol"; -import { Network } from "./utils/Network.sol"; - -contract Migration is BaseMigration { - ISharedArgument public constant vme = ISharedArgument(address(CONFIG)); - - function _configByteCode() internal virtual override returns (bytes memory) { - return vm.getCode("GeneralConfig.sol:GeneralConfig"); - } - - function _sharedArguments() internal virtual override returns (bytes memory rawArgs) { - ISharedArgument.SharedParameter memory param; - - if (network() == Network.Goerli.key()) { - // Undefined - } else if (network() == DefaultNetwork.RoninTestnet.key()) { - // Undefined - } else if (network() == DefaultNetwork.Local.key()) { - // Undefined - } else { - revert("Migration: Network Unknown Shared Parameters Unimplemented!"); - } - - rawArgs = abi.encode(param); - } -} diff --git a/script/SDK.sol b/script/SDK.sol deleted file mode 100644 index e82904b..0000000 --- a/script/SDK.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.23; - -import { console } from "forge-std/console.sol"; -import { StdStyle } from "forge-std/StdStyle.sol"; - -import { TNetwork, TContract } from "@fdk/types/Types.sol"; -import { DefaultNetwork } from "@fdk/utils/DefaultNetwork.sol"; -import { DefaultContract } from "@fdk/utils/DefaultContract.sol"; - -import { LibSig } from "@fdk/libraries/LibSig.sol"; -import { LibProxy } from "@fdk/libraries/LibProxy.sol"; -import { LibSharedAddress } from "@fdk/libraries/LibSharedAddress.sol"; - -import { ISharedArgument } from "./interfaces/ISharedArgument.sol"; -import { Network } from "./utils/Network.sol"; -import { Contract } from "./utils/Contract.sol"; - -import { GeneralConfig } from "./GeneralConfig.sol"; -import { Migration } from "./Migration.s.sol"; - -import { LibErrorHandler } from "@contract-libs/LibErrorHandler.sol"; - -import { LibString } from "@solady/utils/LibString.sol"; -import { GasBurnerLib } from "@solady/utils/GasBurnerLib.sol"; -import { JSONParserLib } from "@solady/utils/JSONParserLib.sol"; diff --git a/script/interfaces/ISharedArgument.sol b/script/interfaces/ISharedArgument.sol deleted file mode 100644 index cd6f7a9..0000000 --- a/script/interfaces/ISharedArgument.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.23; - -import { IGeneralConfig } from "@fdk/interfaces/IGeneralConfig.sol"; - -interface ISharedArgument is IGeneralConfig { - struct SharedParameter { - bool undefined; - } - - function sharedArguments() external view returns (SharedParameter memory param); -} diff --git a/script/utils/Contract.sol b/script/utils/Contract.sol deleted file mode 100644 index 2877156..0000000 --- a/script/utils/Contract.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.23; - -import { LibString, TContract } from "@fdk/types/Types.sol"; - -enum Contract { - Counter -} - -using { key, name } for Contract global; - -function key(Contract contractEnum) pure returns (TContract) { - return TContract.wrap(LibString.packOne(name(contractEnum))); -} - -function name(Contract contractEnum) pure returns (string memory) { - if (contractEnum == Contract.Counter) return "Counter"; - revert("Contract: Unknown contract"); -} diff --git a/script/utils/Network.sol b/script/utils/Network.sol deleted file mode 100644 index 28ad7fb..0000000 --- a/script/utils/Network.sol +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { LibString, TNetwork } from "@fdk/types/Types.sol"; - -enum Network { - Goerli, - EthMainnet, - RoninDevnet -} - -using { key, name, chainId, chainAlias, envLabel, deploymentDir, explorer } for Network global; - -function chainId(Network network) pure returns (uint256) { - if (network == Network.Goerli) return 5; - if (network == Network.EthMainnet) return 1; - if (network == Network.RoninDevnet) return 2022; - revert("Network: Unknown chain id"); -} - -function key(Network network) pure returns (TNetwork) { - return TNetwork.wrap(LibString.packOne(name(network))); -} - -function explorer(Network network) pure returns (string memory link) { - if (network == Network.Goerli) return "https://goerli.etherscan.io/"; - if (network == Network.EthMainnet) return "https://etherscan.io/"; -} - -function name(Network network) pure returns (string memory) { - if (network == Network.Goerli) return "Goerli"; - if (network == Network.RoninDevnet) return "RoninDevnet"; - if (network == Network.EthMainnet) return "EthMainnet"; - revert("Network: Unknown network name"); -} - -function deploymentDir(Network network) pure returns (string memory) { - if (network == Network.Goerli) return "goerli/"; - if (network == Network.EthMainnet) return "ethereum/"; - if (network == Network.RoninDevnet) return "ronin-devnet/"; - revert("Network: Unknown network deployment directory"); -} - -function envLabel(Network network) pure returns (string memory) { - if (network == Network.Goerli) return "TESTNET_PK"; - if (network == Network.RoninDevnet) return "DEVNET_PK"; - if (network == Network.EthMainnet) return "MAINNET_PK"; - revert("Network: Unknown private key env label"); -} - -function chainAlias(Network network) pure returns (string memory) { - if (network == Network.Goerli) return "goerli"; - if (network == Network.EthMainnet) return "ethereum"; - if (network == Network.RoninDevnet) return "ronin-devnet"; - revert("Network: Unknown network alias"); -} diff --git a/src/aggregate-router/interfaces/external/IKatanaV2Pair.sol b/src/aggregate-router/interfaces/external/IKatanaV2Pair.sol deleted file mode 100644 index c1b8b7c..0000000 --- a/src/aggregate-router/interfaces/external/IKatanaV2Pair.sol +++ /dev/null @@ -1,53 +0,0 @@ -pragma solidity >=0.5.0; - -interface IKatanaV2Pair { - event Approval(address indexed owner, address indexed spender, uint256 value); - event Transfer(address indexed from, address indexed to, uint256 value); - - function name() external pure returns (string memory); - function symbol() external pure returns (string memory); - function decimals() external pure returns (uint8); - function totalSupply() external view returns (uint256); - function balanceOf(address owner) external view returns (uint256); - function allowance(address owner, address spender) external view returns (uint256); - - function approve(address spender, uint256 value) external returns (bool); - function transfer(address to, uint256 value) external returns (bool); - function transferFrom(address from, address to, uint256 value) external returns (bool); - - function DOMAIN_SEPARATOR() external view returns (bytes32); - function PERMIT_TYPEHASH() external pure returns (bytes32); - function nonces(address owner) external view returns (uint256); - - function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) - external; - - event Mint(address indexed sender, uint256 amount0, uint256 amount1); - event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to); - event Swap( - address indexed sender, - uint256 amount0In, - uint256 amount1In, - uint256 amount0Out, - uint256 amount1Out, - address indexed to - ); - event Sync(uint112 reserve0, uint112 reserve1); - - function MINIMUM_LIQUIDITY() external pure returns (uint256); - function factory() external view returns (address); - function token0() external view returns (address); - function token1() external view returns (address); - function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); - function price0CumulativeLast() external view returns (uint256); - function price1CumulativeLast() external view returns (uint256); - function kLast() external view returns (uint256); - - function mint(address to) external returns (uint256 liquidity); - function burn(address to) external returns (uint256 amount0, uint256 amount1); - function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external; - function skim(address to) external; - function sync() external; - - function initialize(address, address) external; -} diff --git a/src/aggregate-router/modules/katana/v2/KatanaV2Library.sol b/src/aggregate-router/modules/katana/v2/KatanaV2Library.sol index dc8765b..d153352 100644 --- a/src/aggregate-router/modules/katana/v2/KatanaV2Library.sol +++ b/src/aggregate-router/modules/katana/v2/KatanaV2Library.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.0; -import { IKatanaV2Pair } from "../../../interfaces/external/IKatanaV2Pair.sol"; +import { IKatanaV2Pair } from "@katana/v3-contracts/periphery/interfaces/IKatanaV2Pair.sol"; /// @title Katana v2 Helper Library /// @notice Calculates the recipient address for a command diff --git a/src/aggregate-router/modules/katana/v2/V2SwapRouter.sol b/src/aggregate-router/modules/katana/v2/V2SwapRouter.sol index 0fdbb1c..269a562 100644 --- a/src/aggregate-router/modules/katana/v2/V2SwapRouter.sol +++ b/src/aggregate-router/modules/katana/v2/V2SwapRouter.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.17; -import { IKatanaV2Pair } from "../../../interfaces/external/IKatanaV2Pair.sol"; +import { IKatanaV2Pair } from "@katana/v3-contracts/periphery/interfaces/IKatanaV2Pair.sol"; import { KatanaV2Library } from "./KatanaV2Library.sol"; import { KatanaImmutables } from "../KatanaImmutables.sol"; import { Payments } from "../../Payments.sol"; diff --git a/src/governance/KatanaGovernance.sol b/src/governance/KatanaGovernance.sol new file mode 100644 index 0000000..9b3a28b --- /dev/null +++ b/src/governance/KatanaGovernance.sol @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import { IKatanaV2Factory } from "./interfaces/IKatanaV2Factory.sol"; +import { IKatanaV2Pair } from "@katana/v3-contracts/periphery/interfaces/IKatanaV2Pair.sol"; +import { IKatanaGovernance } from "@katana/v3-contracts/external/interfaces/IKatanaGovernance.sol"; + +contract KatanaGovernance is OwnableUpgradeable, IKatanaV2Factory, IKatanaGovernance { + using EnumerableSet for EnumerableSet.AddressSet; + + /// @inheritdoc IKatanaGovernance + address public immutable getPositionManager; + + /// @dev Revert error when the length of the array is invalid. + error InvalidLength(); + /// @dev Revert error when the caller is not authorized. + error Unauthorized(); + + /// @dev Gap for upgradeability. + uint256[50] private __gap; + + /// @dev Indicates the token is unauthorized for trade. + uint40 private constant UNAUTHORIZED = 0; + /// @dev Indicates the token is publicly allowed for trade. + uint40 private constant AUTHORIZED = type(uint40).max; + + /// @dev The factory contract. + IKatanaV2Factory private _factory; + /// @dev The mapping of token to permission. + mapping(address token => Permission) private _permission; + /// @dev The unique set of tokens. + EnumerableSet.AddressSet private _tokens; + /// @dev The router address + address private _router; + + /// @dev Only use this modifier for boolean-returned methods + modifier skipIfAllowedAllOrOwner(address account) { + _skipIfAllowedAllOrOwner(account); + _; + } + + constructor(address nonfungiblePositionManager) { + getPositionManager = nonfungiblePositionManager; + _disableInitializers(); + } + + function initialize(address admin, address factory) external initializer { + _setFactory(factory); + __Ownable_init_unchained(admin); + + IKatanaV2Pair pair; + uint40 until = AUTHORIZED; + bool[] memory statusesPlaceHolder; + address[] memory allowedPlaceHolder; + uint256 length = IKatanaV2Factory(factory).allPairsLength(); + + for (uint256 i; i < length; ++i) { + pair = IKatanaV2Pair(IKatanaV2Factory(factory).allPairs(i)); + _setPermission(pair.token0(), until, allowedPlaceHolder, statusesPlaceHolder); + _setPermission(pair.token1(), until, allowedPlaceHolder, statusesPlaceHolder); + } + } + + function initializeV2(address router) external reinitializer(2) { + _router = router; + } + + /// @inheritdoc IKatanaGovernance + function setRouter(address router) external onlyOwner { + _router = router; + } + + /** + * @inheritdoc IKatanaV2Factory + */ + function createPair(address tokenA, address tokenB) external returns (address pair) { + address sender = _msgSender(); + address[] memory tokens = new address[](2); + tokens[0] = tokenA; + tokens[1] = tokenB; + if (!this.isAuthorized(tokens, sender)) revert Unauthorized(); + + pair = _factory.createPair(tokenA, tokenB); + } + + /** + * @inheritdoc IKatanaGovernance + */ + function createPairAndSetPermission( + address tokenA, + address tokenB, + uint40 whitelistUntil, + address[] calldata alloweds, + bool[] calldata statuses + ) external onlyOwner returns (address pair) { + pair = _factory.createPair(tokenA, tokenB); + _setPermission(tokenA, whitelistUntil, alloweds, statuses); + _setPermission(tokenB, whitelistUntil, alloweds, statuses); + } + + /** + * @inheritdoc IKatanaGovernance + */ + function setPermission(address token, uint40 whitelistUntil, address[] calldata alloweds, bool[] calldata statuses) + external + onlyOwner + { + _setPermission(token, whitelistUntil, alloweds, statuses); + } + + /** + * @inheritdoc IKatanaV2Factory + */ + function setPairImplementation(address impl) external onlyOwner { + _factory.setPairImplementation(impl); + } + + /** + * @inheritdoc IKatanaV2Factory + */ + function setAllowedAll(bool shouldAllow) external onlyOwner { + _factory.setAllowedAll(shouldAllow); + } + + /** + * @inheritdoc IKatanaGovernance + */ + function setFactory(address factory) external onlyOwner { + _setFactory(factory); + } + + /** + * @inheritdoc IKatanaV2Factory + */ + function setTreasury(address newTreasury) external onlyOwner { + _factory.setTreasury(newTreasury); + } + + /** + * @inheritdoc IKatanaV2Factory + */ + function getPair(address tokenA, address tokenB) external view returns (address pair) { + return _factory.getPair(tokenA, tokenB); + } + + /** + * @inheritdoc IKatanaV2Factory + */ + function allPairs(uint256 index) external view returns (address pair) { + return _factory.allPairs(index); + } + + /** + * @inheritdoc IKatanaV2Factory + */ + function allPairsLength() external view returns (uint256) { + return _factory.allPairsLength(); + } + + /** + * @inheritdoc IKatanaV2Factory + */ + function treasury() external view returns (address) { + return _factory.treasury(); + } + + /** + * @inheritdoc IKatanaV2Factory + */ + function pairImplementation() external view returns (address) { + return _factory.pairImplementation(); + } + + /** + * @inheritdoc IKatanaV2Factory + */ + function INIT_CODE_PAIR_HASH() external view returns (bytes32) { + return _factory.INIT_CODE_PAIR_HASH(); + } + + /** + * @inheritdoc IKatanaGovernance + */ + function getRouter() external view returns (address) { + return _router; + } + + /** + * @inheritdoc IKatanaGovernance + */ + function getFactory() external view returns (address) { + return address(_factory); + } + + /** + * @inheritdoc IKatanaGovernance + */ + function isAuthorized(address token, address account) + external + view + skipIfAllowedAllOrOwner(account) + returns (bool authorized) + { + authorized = _isAuthorized(_permission[token], account); + } + + /** + * @inheritdoc IKatanaGovernance + */ + function isAuthorized(address[] calldata tokens, address account) + external + view + skipIfAllowedAllOrOwner(account) + returns (bool authorized) + { + uint256 length = tokens.length; + + for (uint256 i; i < length; ++i) { + if (!_isAuthorized(_permission[tokens[i]], account)) return false; + } + + return true; + } + + /** + * @inheritdoc IKatanaGovernance + */ + function getWhitelistUntil(address token) external view returns (uint40) { + return _permission[token].whitelistUntil; + } + + /** + * @inheritdoc IKatanaGovernance + */ + function getWhitelistedTokensFor(address account) + external + view + returns (address[] memory tokens, uint40[] memory whitelistUntils) + { + unchecked { + uint256 length = _tokens.length(); + tokens = new address[](length); + whitelistUntils = new uint40[](length); + uint256 count; + address token; + uint40 whitelistUntil; + Permission storage $; + + for (uint256 i; i < length; ++i) { + token = _tokens.at(i); + $ = _permission[token]; + whitelistUntil = $.whitelistUntil; + + if (block.timestamp < whitelistUntil && $.allowed[account]) { + tokens[count] = token; + whitelistUntils[count] = whitelistUntil; + ++count; + } + } + + assembly { + mstore(tokens, count) + mstore(whitelistUntils, count) + } + } + } + + /** + * @inheritdoc IKatanaGovernance + */ + function getManyTokensWhitelistInfo() + external + view + returns (address[] memory tokens, uint40[] memory whitelistedUntils) + { + tokens = _tokens.values(); + uint256 length = tokens.length; + whitelistedUntils = new uint40[](length); + + for (uint256 i; i < length; ++i) { + whitelistedUntils[i] = _permission[tokens[i]].whitelistUntil; + } + } + + /** + * @inheritdoc IKatanaV2Factory + */ + function allowedAll() public view returns (bool) { + return _factory.allowedAll(); + } + + /** + * @dev Sets the address of the factory contract. + * Can only be called by the contract owner. + */ + function _setFactory(address factory) private { + _factory = IKatanaV2Factory(factory); + + emit FactoryUpdated(_msgSender(), factory); + } + + /** + * @dev Sets the permission for a token. + * @param token The address of the token. + * @param whitelistUntil The end of the whitelist duration in seconds. + * @param alloweds The array of addresses to be allowed in whitelist duration. + * @param statuses The corresponding array of statuses (whether allowed or not). + */ + function _setPermission(address token, uint40 whitelistUntil, address[] memory alloweds, bool[] memory statuses) + private + { + uint256 length = alloweds.length; + if (length != statuses.length) revert InvalidLength(); + + Permission storage $ = _permission[token]; + $.whitelistUntil = whitelistUntil; + _tokens.add(token); + + for (uint256 i; i < length; ++i) { + $.allowed[alloweds[i]] = statuses[i]; + } + + emit PermissionUpdated(_msgSender(), token, whitelistUntil, alloweds, statuses); + } + + /** + * @dev Checks if an account is authorized. + * @param account The address of the account to check authorization for. + * @return A boolean indicating whether the account is authorized or not. + */ + function _isAuthorized(Permission storage $, address account) private view returns (bool) { + uint256 expiry = $.whitelistUntil; + if (expiry == UNAUTHORIZED) return false; + if (expiry == AUTHORIZED || block.timestamp > expiry) return true; + + return $.allowed[account]; + } + + /** + * @dev Skips the function if the caller is allowed all or the owner. + * WARNING: This function can return and exit current context and skip the function. + */ + function _skipIfAllowedAllOrOwner(address account) internal view { + if (allowedAll() || account == owner()) { + assembly ("memory-safe") { + mstore(0x0, true) + return(0x0, returndatasize()) + } + } + } +} diff --git a/src/governance/interfaces/IKatanaV2Factory.sol b/src/governance/interfaces/IKatanaV2Factory.sol new file mode 100644 index 0000000..dcc5aa3 --- /dev/null +++ b/src/governance/interfaces/IKatanaV2Factory.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.5.17 <0.9.0; + +/** + * @title IKatanaFactory + * + * @dev IKatanaFactory interface. + * @notice Imported from https://github.com/axieinfinity/contract-infinity/blob/main/contracts/interfaces/IKatanaFactory.sol + */ +interface IKatanaV2Factory { + /** + * @dev Emitted when a pair for `token0` token and `token1` token is created. + */ + event PairCreated(address indexed token0, address indexed token1, address pair, uint256 allPairsLength); + + /** + * @dev Emitted when pair implement is changed from `old` to `new`. + */ + event PairProxyUpdated(address indexed newImpl, address indexed oldImpl); + + function INIT_CODE_PAIR_HASH() external view returns (bytes32); + + /** + * @dev Flag whether allowed all users to call. + */ + function allowedAll() external view returns (bool); + + /** + * + * @dev Set allowed all. + * + * Requirements: + * + * - The method caller is admin. + * + */ + function setAllowedAll(bool) external; + + /** + * @dev Returns implementation address for pair token. + */ + function pairImplementation() external view returns (address); + + /** + * @dev Set implementation address for all pairs. + * + * Requirements: + * + * - The method caller is admin. + * + * Emit a {PairProxyUpdated} event. + */ + function setPairImplementation(address) external; + + /** + * @dev Returns treasury address. + */ + function treasury() external view returns (address); + + /** + * @dev Sets treasury address. + * + * Requirements: + * + * - The method caller is admin. + * + */ + function setTreasury(address addr) external; + + /** + * @dev Returns pair address for `tokenA` and `tokenB`. + */ + function getPair(address tokenA, address tokenB) external view returns (address pair); + + /** + * @dev Returns pair address at `index` position. + */ + function allPairs(uint256 index) external view returns (address pair); + + /** + * @dev Returns all pairs length. + */ + function allPairsLength() external view returns (uint256); + + /** + * @dev Create pair. + * + * Requirements: + * + * - The default method caller is contract admin. + * - All addresses is allowed if `allowedAll` is true. + * + */ + function createPair(address tokenA, address tokenB) external returns (address pair); +}