// SPDX-License-Identifier: MIT pragma solidity 0.7.6; pragma abicoder v2; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import "./AddInstanceProposal.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IERC20Permit } from "@openzeppelin/contracts/drafts/IERC20Permit.sol"; import { IUniswapV3Factory } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; import { IUniswapV3PoolState } from "@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolState.sol"; /** * @notice Contract which creates instance addition proposals. * @dev In this version, no initializable is needed, and it was also a kind of bad initializable * in the first place, because the variables which were set, do not have to be pseudo-immutable. * * Also in this version, there is no "creationFee", this was a bad idea in the first place by * whoever implemented it, because anyone would just be able to fork this code in 1 hour as * I did, and redeploy a free version. */ contract InstanceProposer { using Address for address; address public immutable WETH; address public immutable governance; address public immutable instanceFactory; address public immutable instanceRegistry; IUniswapV3Factory public immutable UniswapV3Factory; uint16 public TWAPSlotsMin; event NewCreationFeeSet(uint256 newCreationFee); event NewTWAPSlotsMinSet(uint256 newTWAPSlotsMin); event NewGovernanceProposalCreated(address indexed proposal); /** * @dev Throws if called by any account other than the Governance. */ modifier onlyGovernance() { require(governance == msg.sender, "IF: caller is not the Governance"); _; } /** * @notice Contract constructor. * @param _governance The TC governance address. * @param _instanceFactory The factory address with which created proposals will use to deploy an instance. * @param _instanceRegistry The registry address with which created proposals will use to register an instance. * @param _UniswapV3Factory The UNIV3 Factory address with which we check TWAP cardinality. * @param _WETH The address of the Wrapped Ether contract. * @param _TWAPSlotsMin The minimum TWAP cardinality for a pool which we will accept. */ constructor( address _governance, address _instanceFactory, address _instanceRegistry, address _UniswapV3Factory, address _WETH, uint16 _TWAPSlotsMin ) { governance = _governance; instanceFactory = _instanceFactory; instanceRegistry = _instanceRegistry; UniswapV3Factory = IUniswapV3Factory(_UniswapV3Factory); WETH = _WETH; TWAPSlotsMin = _TWAPSlotsMin; } /** * @notice Create an instance addition proposal. * @dev Note that here, Uniswap is being used as an anti-shitcoin oracle. * Also, specifically in this version the upper limit on the protocol fee is 1%, I do not * consider it very beneficial for anyone to have it above this number. * @param _token address of ERC20 token for a new instance * @param _uniswapPoolSwappingFee fee value of Uniswap instance which will be used for `TORN/token` * price determination. `3000` means 0.3% fee Uniswap pool. * @param _denominations list of denominations for each new instance * @param _protocolFees list of protocol fees for each new instance. * `100` means that instance withdrawal fee is 1% of denomination. */ function createProposal( address _token, uint24 _uniswapPoolSwappingFee, uint256[] memory _denominations, uint32[] memory _protocolFees ) external returns (address) { require(_token == address(0) || _token.isContract(), "Token is not contract"); require(_denominations.length > 0, "Empty denominations"); require(_denominations.length == _protocolFees.length, "Incorrect denominations/fees length"); // check Uniswap Pool for (uint8 i = 0; i < _protocolFees.length; i++) { require(_protocolFees[i] <= 100, "Protocol fee is more than 1%"); if (_protocolFees[i] > 0 && _token != address(0)) { // pool exists address poolAddr = UniswapV3Factory.getPool(_token, WETH, _uniswapPoolSwappingFee); require(poolAddr != address(0), "Uniswap pool is not exist"); // TWAP slots (, , , , uint16 observationCardinalityNext, , ) = IUniswapV3PoolState(poolAddr).slot0(); require(observationCardinalityNext >= TWAPSlotsMin, "Uniswap pool TWAP slots number is low"); break; } } address proposal = address( new AddInstanceProposal(instanceFactory, instanceRegistry, _token, _uniswapPoolSwappingFee, _denominations, _protocolFees) ); emit NewGovernanceProposalCreated(proposal); return proposal; } function setTWAPSlotsMin(uint16 _TWAPSlotsMin) external onlyGovernance { TWAPSlotsMin = _TWAPSlotsMin; emit NewTWAPSlotsMinSet(_TWAPSlotsMin); } }