2023-04-13 14:51:43 +03:00
// SPDX-License-Identifier: MIT
pragma solidity 0 . 7 . 6 ;
pragma abicoder v2 ;
import { SafeMath } from " @openzeppelin/contracts/math/SafeMath.sol " ;
import { Address } from " @openzeppelin/contracts/utils/Address.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 " ;
2023-04-14 22:16:26 +03:00
import { InstanceAdditionProposal , InstanceAdditionData } from " ./InstanceAdditionProposal.sol " ;
2023-04-13 14:51:43 +03:00
/**
* @ 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 InstanceProposalFactory {
using SafeMath for uint256 ;
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 minObservationCardinality ;
event NewMinimumObservationCardinalitySet ( uint256 newMinObservationCardinality ) ;
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 _wethAddress The address of the Wrapped Ether contract .
* @ param _minObservationCardinality The minimum TWAP cardinality for a pool which we will accept .
* /
constructor (
address _governance ,
address _instanceFactory ,
address _instanceRegistry ,
address _UniswapV3Factory ,
address _wethAddress ,
uint16 _minObservationCardinality
) {
governance = _governance ;
instanceFactory = _instanceFactory ;
instanceRegistry = _instanceRegistry ;
UniswapV3Factory = IUniswapV3Factory ( _UniswapV3Factory ) ;
minObservationCardinality = _minObservationCardinality ;
weth = _wethAddress ;
}
/**
* @ notice Create an instance addition proposal contract .
* @ 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 3 % , I do not consider it very beneficial for anyone to have it above this number .
* The statement of the function : " I have some tokens for which there exist good uniswap pools, for which I would like to create [total number denominations] instances, of denominations which may be divided by a common exponent characteristic for each token ' s denomination group, and with a protocol fee which is reasonable (under 3%). "
* @ param _tokenAddresses ERC20 token addresses to add to the proposal .
* @ param _uniswapPoolSwappingFees Swapping fees for each token which are used to determine whether the observation cardinality of the Uniswap Pool is large enough . ` 3000 ` means 0 . 3 % fee Uniswap pool .
* @ param _denomBase10DivExponents These are the exponents of the ( 10 ** exp ) divisor which is used to assure that the denomination may be represented as a number below 2 ^ 40 - 1 .
* @ param _denominations The denominations for each token . These will be stored as smallDenom = denomination / ( 10 ^ exponent )
* @ param _protocolFees List of protocol fees for each new instance . ` 100 ` means that instance withdrawal fee is 1 % of denomination .
* @ param _totalNumberDenominations This is the total number of denominations , the sum of the lengths of subarrays of denominations .
* /
function createProposalContract (
address [ ] calldata _tokenAddresses ,
uint24 [ ] calldata _uniswapPoolSwappingFees ,
uint16 [ ] calldata _denomBase10DivExponents ,
uint256 [ ] [ ] calldata _denominations ,
uint16 [ ] [ ] calldata _protocolFees ,
uint256 _totalNumberDenominations
) external returns ( address ) {
2023-04-14 22:16:26 +03:00
InstanceAdditionData [ ] memory toAdd = new InstanceAdditionData [ ] ( _totalNumberDenominations ) ;
2023-04-13 14:51:43 +03:00
uint256 toAddSize = 0 ;
require ( 0 < _tokenAddresses . length , " No tokens specified " ) ;
// 1️ ⃣ Each token must be ether, or a contract with a uniswap pool of a minimum cardinality.
for ( uint256 t = 0 ; t < _tokenAddresses . length ; t ++ ) {
address token = _tokenAddresses [ t ] ;
// 2️ ⃣ The swapping fees must have an element at t.
// We do not care if there is more swapping fees than tokens
uint24 swappingFee = _uniswapPoolSwappingFees [ t ] ;
2023-04-13 23:03:12 +03:00
// 3️ ⃣ If not eth the token is a contract, the swapping fee must give a good pool, the cardinality must be good.
2023-04-13 14:51:43 +03:00
if ( token != address ( 0 ) ) {
require ( token . isContract ( ) , " Token must be a contract if not addr(0) " ) ;
address poolAddress = UniswapV3Factory . getPool ( token , weth , swappingFee ) ;
require ( poolAddress != address ( 0 ) , " A Uniswap V3 pool does not exist for this token. " ) ;
( , , , , uint16 observationCardinalityNext , , ) = IUniswapV3PoolState ( poolAddress ) . slot0 ( ) ;
require ( minObservationCardinality <= observationCardinalityNext , " Uniswap Pool observation cardinality is low. " ) ;
}
2023-04-13 23:03:12 +03:00
// 4️ ⃣ Denominations, div exponent and protocol fees must have an element at t(oken). The exponent must be good.
2023-04-13 14:51:43 +03:00
uint256 [ ] memory denominations = _denominations [ t ] ;
uint16 exponent = _denomBase10DivExponents [ t ] ;
require ( 0 < exponent , " Exponent is 0. " ) ;
uint16 [ ] memory protocolFees = _protocolFees [ t ] ;
// 5️ ⃣ The denominations element must never have 0 length.
uint256 numDenoms = denominations . length ;
require ( 0 < numDenoms , " There is no denominations for some token " ) ;
// 6️ ⃣ All denominations divided by the exponent must not be 0, and there must be a reasonable protocol fee for each.
for ( uint256 d = 0 ; d < numDenoms ; d ++ ) {
// 7️ ⃣ protocolFee must have an element at d and be appropriate.
// We do not care if there is more fees than denominations
uint16 protocolFee = protocolFees [ d ] ;
require ( protocolFee <= 300 , " Protocol fee is more than 3%! " ) ;
// 8️ ⃣ The denomination must not be 0 post division.
uint40 smallDenomination = uint40 ( denominations [ d ] . div ( 10 ** exponent ) ) ;
require ( 0 < smallDenomination , " When divided, denom is 0 " ) ;
// 9️ ⃣ If all of the above are fine, add the packed struct to the memory array.
2023-04-14 22:16:26 +03:00
toAdd [ toAddSize ] = InstanceAdditionData ( {
2023-04-13 14:51:43 +03:00
tokenAddress : token ,
smallDenomination : smallDenomination ,
base10Exponent : exponent ,
uniPoolSwappingFee : swappingFee ,
protocolFee : protocolFee
} ) ;
toAddSize ++ ;
}
}
// 🔟 The number of elements added must be _totalNumberDenominations.
require ( toAddSize == _totalNumberDenominations , " Wrong total number of denominations. " ) ;
// 1️ ⃣1️ ⃣ Finish and return.
address proposal = address ( new InstanceAdditionProposal ( instanceFactory , instanceRegistry , toAdd ) ) ;
emit NewGovernanceProposalCreated ( proposal ) ;
return proposal ;
}
function setMinObservationCardinality ( uint16 _newMinObservationCardinality ) external onlyGovernance {
minObservationCardinality = _newMinObservationCardinality ;
emit NewMinimumObservationCardinalitySet ( _newMinObservationCardinality ) ;
}
}