instances/contracts/InstanceProposalFactory.sol

186 lines
8.1 KiB
Solidity
Raw Normal View History

// 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";
import { InstanceAdditionProposal } from "./InstanceAdditionProposal.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 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) {
InstanceAdditionProposal.InstanceAdditionData[] memory toAdd = new InstanceAdditionProposal.InstanceAdditionData[](
_totalNumberDenominations
);
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];
// 3⃣ If not eth the token is a contract, the swapping fee must give a good pool, the cardinality must be good.
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.");
}
// 4⃣ Denominations, div exponent and protocol fees must have an element at t(oken). The exponent must be good.
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.
toAdd[toAddSize] = InstanceAdditionProposal.InstanceAdditionData({
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);
}
}