tornado-instances/contracts/InstanceProposalFactory.sol
T-Hax 3863e951d8 fork tested
Signed-off-by: T-Hax <>
2023-04-14 19:16:26 +00:00

184 lines
8.0 KiB
Solidity
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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, InstanceAdditionData } 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) {
InstanceAdditionData[] memory toAdd = new 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] = 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);
}
}