197d593504
Signed-off-by: T-Hax <>
186 lines
8.0 KiB
Solidity
186 lines
8.0 KiB
Solidity
// 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️⃣ Fails if the token is not a contract or if there is no pool.
|
||
|
||
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).
|
||
|
||
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);
|
||
}
|
||
}
|