tornado-instances/contracts/InstanceProposer.sol
T-Hax 8f085566cc init
Signed-off-by: T-Hax <>
2023-04-08 18:43:42 +00:00

119 lines
4.8 KiB
Solidity

// 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);
}
}