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