fork tested

Signed-off-by: T-Hax <>
This commit is contained in:
T-Hax 2023-04-14 19:16:26 +00:00
parent a8495d76a4
commit 3863e951d8
8 changed files with 344 additions and 41 deletions

View File

@ -8,8 +8,11 @@ module.exports = {
singletonFactory: '0xce0042B868300000d44A59004Da54A005ffdcf9f',
singletonFactoryVerboseWrapper: '0xCEe71753C9820f063b38FDbE4cFDAf1d3D928A80',
salt: '0x0000000000000000000000000000000000000000000000000000000047941987',
COMP: '0xc00e94Cb662C3520282E6f5717214004A7f26888',
TORN: '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
COMP: '0xc00e94Cb662C3520282E6f5717214004A7f26888',
LUSD: '0x5f98805a4e8be255a32880fdec7f6728c6568ba0',
RETH: '0xae78736cd615f374d3085123a210448e74fc6393',
FRXETH: '0x5E8422345238F34275888049021821E8E08CAa1f',
tornWhale: '0xF977814e90dA44bFA03b6295A0616a897441aceC',
compWhale: '0xF977814e90dA44bFA03b6295A0616a897441aceC',
creationFee: '200000000000000000000', // 200 TORN

View File

@ -8,40 +8,51 @@ import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import "./interfaces/IInstanceRegistry.sol";
import "./interfaces/IInstanceFactory.sol";
struct InstanceAdditionData {
address tokenAddress;
uint40 smallDenomination;
uint16 base10Exponent;
uint24 uniPoolSwappingFee;
uint16 protocolFee;
}
contract InstanceAdditionDataProvider {
InstanceAdditionData[] public toAddData;
constructor(InstanceAdditionData[] memory _toAdd) {
for (uint256 i = 0; i < _toAdd.length; i++) {
toAddData.push(_toAdd[i]);
}
}
function getAllDataToAdd() external view returns (InstanceAdditionData[] memory) {
return toAddData;
}
}
contract InstanceAdditionProposal {
using SafeMath for uint256;
InstanceAdditionDataProvider public immutable provider;
IInstanceFactory public immutable instanceFactory;
IInstanceRegistry public immutable instanceRegistry;
struct InstanceAdditionData {
address tokenAddress;
uint40 smallDenomination;
uint16 base10Exponent;
uint24 uniPoolSwappingFee;
uint16 protocolFee;
}
InstanceAdditionData[] public toAdd;
event AddInstanceForRegistry(address instance, address token, uint256 denomination);
constructor(address _instanceFactory, address _instanceRegistry, InstanceAdditionData[] memory _toAdd) {
provider = new InstanceAdditionDataProvider(_toAdd);
instanceFactory = IInstanceFactory(_instanceFactory);
instanceRegistry = IInstanceRegistry(_instanceRegistry);
// Copying structs is not implemented
for (uint256 i = 0; i < _toAdd.length; i++) {
toAdd.push(_toAdd[i]);
}
}
function executeProposal() external {
uint256 howMany = toAdd.length;
InstanceAdditionData[] memory _toAddData = provider.getAllDataToAdd();
uint256 howMany = _toAddData.length;
for (uint256 i = 0; i < howMany; i++) {
// Read out the data for the new instance
InstanceAdditionData memory data = toAdd[i];
InstanceAdditionData memory data = _toAddData[i];
// Safely calculate the denomination
uint256 denomination = uint256(data.smallDenomination).mul(10 ** uint256(data.base10Exponent));
@ -67,4 +78,13 @@ contract InstanceAdditionProposal {
emit AddInstanceForRegistry(address(instance), data.tokenAddress, denomination);
}
}
function getDataToAddByIndex(uint256 index) external view returns (InstanceAdditionData memory data) {
// The compiler is behaving weird here. Something to do with struct type inference.
return (provider.getAllDataToAdd())[index];
}
function getAllDataToAdd() external view returns (InstanceAdditionData[] memory) {
return provider.getAllDataToAdd();
}
}

View File

@ -9,7 +9,7 @@ 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";
import { InstanceAdditionProposal, InstanceAdditionData } from "./InstanceAdditionProposal.sol";
/**
* @notice Contract which creates instance addition proposals.
@ -87,9 +87,7 @@ contract InstanceProposalFactory {
uint16[][] calldata _protocolFees,
uint256 _totalNumberDenominations
) external returns (address) {
InstanceAdditionProposal.InstanceAdditionData[] memory toAdd = new InstanceAdditionProposal.InstanceAdditionData[](
_totalNumberDenominations
);
InstanceAdditionData[] memory toAdd = new InstanceAdditionData[](_totalNumberDenominations);
uint256 toAddSize = 0;
@ -153,7 +151,7 @@ contract InstanceProposalFactory {
// 9 If all of the above are fine, add the packed struct to the memory array.
toAdd[toAddSize] = InstanceAdditionProposal.InstanceAdditionData({
toAdd[toAddSize] = InstanceAdditionData({
tokenAddress: token,
smallDenomination: smallDenomination,
base10Exponent: exponent,

View File

@ -44,14 +44,14 @@ module.exports = {
hardhat: {
forking: {
url: process.env.MAINNET_RPC_URL,
blockNumber: 14250000,
//blockNumber: 14250000,
httpHeaders: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0' },
},
chainId: 1,
initialBaseFeePerGas: 5,
//initialBaseFeePerGas: 5,
loggingEnabled: false,
allowUnlimitedContractSize: false,
blockGasLimit: 50000000,
//blockGasLimit: 50000000,
},
rinkeby: {
url: process.env.RINKEBY_RPC_URL,

View File

@ -36,7 +36,7 @@
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"ethereum-waffle": "^3.4.0",
"hardhat": "^2.4.3",
"hardhat": ">=2.4.3",
"hardhat-contract-sizer": "^2.6.1",
"hardhat-log-remover": "^2.0.2",
"mocha-lcov-reporter": "^1.3.0",

View File

@ -1,6 +1,7 @@
require('dotenv').config()
const { ethers } = require('hardhat')
const hre = require('hardhat')
const { ethers } = hre
const config = require('../config')
const { createInterface } = require('readline')
@ -17,7 +18,7 @@ function idToNetwork(id) {
case 11155111:
return 'Sepolia'
default:
throw Error('\nChain id could not be recognized. What network are you using?\n')
throw Error('\nChain Id could not be recognized. What network are you using?\n')
}
}
@ -31,7 +32,7 @@ function _prompt(prompt, resolve) {
} else if (answer == 'n') {
userInput.close()
resolve(false)
} else wipeCachePrompt('', resolve)
} else _prompt('', resolve)
})
}
@ -40,44 +41,94 @@ function prompt(prompt) {
}
function happyDeployedMessage(name, chainId, address) {
return `\n${name} successfully deployed on ${idToNetwork(chainId)} at ${address} 🥳\n`
return `\n${name} successfully deployed on ${idToNetwork(chainId)} @ ${address} 🥳\n`
}
function happyVerifiedMessage(name) {
return `\n${name} successfully verified on Etherscan!`
function happyVerifiedMessage(name, address) {
return `\n${name} @ ${address} successfully verified on Etherscan! 🥳\n`
}
const promptMessageBase = (middle) => `\n${middle}\n\nAre you sure you would like to continue? 🧐 (y/n): `
async function main() {
const minFacFac = await ethers.getContractFactory('MinimalInstanceFactory')
const proposalFacFac = await ethers.getContractFactory('InstanceProposalFactory')
const minimalFactoryContractFactory = await ethers.getContractFactory('MinimalInstanceFactory')
const proposalFactoryContractFactory = await ethers.getContractFactory('InstanceProposalFactory')
const minFac = await minFacFac.deploy(config.verifier, config.hasher, config.merkleTreeHeight)
let minimalFactory, proposalFactory, nativeCloneableImplAddr, erc20CloneableImplAddr
console.log(happyDeployedMessage('MinimalInstanceFactory', minFac.address))
if (await prompt(promptMessageBase('Continuing to MinimalInstanceFactory deployment.'))) {
minimalFactory = await minimalFactoryContractFactory.deploy(
config.verifier,
config.hasher,
config.merkleTreeHeight,
)
} else {
return '\nDecided to stop at InstanceProposalFactory deployment.\n'
}
console.log(happyDeployedMessage('MinimalInstanceFactory', minimalFactory.address))
nativeCloneableImplAddr = await minimalFactory.nativeCurImpl()
erc20CloneableImplAddr = await minimalFactory.ERC20Impl()
console.log(happyDeployedMessage('ETHTornadoCloneable', nativeCloneableImplAddr))
console.log(happyDeployedMessage('ERC20TornadoCloneable', erc20CloneableImplAddr))
if (await prompt(promptMessageBase('Continuing to InstanceProposalFactory deployment.'))) {
const proposalFac = await proposalFacFac.deploy(
proposalFactory = await proposalFactoryContractFactory.deploy(
config.governance,
minFac.address,
minimalFactory.address,
config.instanceRegistry,
config.UniswapV3Factory,
config.WETH,
config.TWAPSlotsMin,
)
console.log(happyDeployedMessage('InstanceProposalFactory', proposalFac.address))
console.log(happyDeployedMessage('InstanceProposalFactory', proposalFactory.address))
} else {
return '\nDecided to stop at InstanceProposalFactory deployment.\n'
}
if (await prompt(promptMessageBase('Continuing to contract verification.'))) {
await hre.run('verify:verify', {
address: minimalFactory.address,
constructorArguments: [config.verifier, config.hasher, config.merkleTreeHeight],
})
console.log(happyVerifiedMessage('MinimalInstanceFactory', minimalFactory.address))
await hre.run('verify:verify', {
address: proposalFactory.address,
constructorArguments: [
config.governance,
minimalFactory.address,
config.instanceRegistry,
config.UniswapV3Factory,
config.WETH,
config.TWAPSlotsMin,
],
})
console.log(happyVerifiedMessage('InstanceProposalFactory', proposalFactory.address))
await hre.run('verify:verify', {
address: nativeCloneableImplAddr,
constructorArguments: [config.verifier, config.hasher],
})
console.log(happyVerifiedMessage('ETHTornadoCloneable', nativeCloneableImplAddr))
await hre.run('verify:verify', {
address: erc20CloneableImplAddr,
constructorArguments: [config.verifier, config.hasher],
})
console.log(happyVerifiedMessage('ERC20TornadoCloneable', erc20CloneableImplAddr))
} else {
return '\nDecided to stop at contract verification.\n'
}
}
main().then((res) => {
console.log(res ?? '\nScript succesfully finished.')
console.log(res ?? '\nScript succesfully finished.\n')
})

188
test/all.test.js Normal file
View File

@ -0,0 +1,188 @@
const hre = require('hardhat')
const config = require('../config')
const { ethers, waffle } = hre
const { loadFixture } = waffle
const { expect } = require('chai')
const { minewait } = require('./utils')
const { BigNumber } = require('@ethersproject/bignumber')
describe('Tests', () => {
let governanceSigner, proposer
let minimalFactory, proposalFactory, governance, torn
before(async () => {
minimalFactory = await (
await ethers.getContractFactory('MinimalInstanceFactory')
).deploy(config.verifier, config.hasher, config.merkleTreeHeight)
let gasUsed = await ethers.provider.estimateGas(minimalFactory.deployTransaction.data.toString())
console.log(`\nManaged to deploy the MinimalInstanceFactory. Gas used: ${gasUsed} 🏭\n`)
proposalFactory = await (
await ethers.getContractFactory('InstanceProposalFactory')
).deploy(
config.governance,
minimalFactory.address,
config.instanceRegistry,
config.UniswapV3Factory,
config.WETH,
config.TWAPSlotsMin,
)
gasUsed = await ethers.provider.estimateGas(minimalFactory.deployTransaction.data.toString())
console.log(`\nManaged to deploy the InstanceProposalFactory. Gas used: ${gasUsed} 🏭\n`)
await hre.network.provider.request({
method: 'hardhat_impersonateAccount',
params: [config.governance],
})
await hre.network.provider.request({
method: 'hardhat_setBalance',
params: [config.governance, ethers.utils.parseUnits('10').toHexString()],
})
governanceSigner = await ethers.getSigner(config.governance)
governance = await ethers.getContractAt(
'tornado-governance/contracts/v1/Governance.sol:Governance',
config.governance,
governanceSigner,
)
console.log('\nManaged to setup self-signing governance (just like IRL). 🏛️\n')
torn = await ethers.getContractAt('torn-token/contracts/TORN.sol:TORN', config.TORN, governanceSigner)
proposer = (await ethers.getSigners())[0]
const fundAmount = (await torn.balanceOf(governance.address)).div(2)
await expect(() => torn.transfer(proposer.address, fundAmount)).to.changeTokenBalance(
torn,
proposer,
fundAmount,
)
console.log(`\nFunded proposer with ${fundAmount.div(BigNumber.from(10).pow(18))} TORN 🌪️\n`)
})
it('Test the entire instance creation process.', async () => {
let response = await proposalFactory.createProposalContract(
[config.LUSD],
// This should work fine
[3000, 100000],
// This should work fine
[18, 3921],
[[ethers.utils.parseUnits('10000'), ethers.utils.parseUnits('1000'), ethers.utils.parseUnits('100')]],
// This should work fine
[[100, 100, 100, 312]],
3,
)
console.log('\nManaged to deploy an LUSD proposal factory. 🏭\n')
let receipt = await response.wait()
const proposalContractLUSD = await ethers.getContractAt(
'InstanceAdditionProposal',
receipt.events[0].args[0],
)
expect((await proposalContractLUSD.getAllDataToAdd()).length).to.equal(3)
let denominations = [
ethers.utils.parseUnits('1000'),
ethers.utils.parseUnits('100'),
ethers.utils.parseUnits('10'),
]
response = await proposalFactory.createProposalContract(
[config.RETH, config.LUSD],
// This should work fine
[500, 3000, 100000],
// This too
[18, 18, 3921],
[denominations, denominations],
// This should work fine
[
[100, 100, 100, 312],
[300, 300, 300, 312],
],
6,
)
console.log('\nManaged to deploy a RETH + LUSD proposal factory. 🏭\n')
receipt = await response.wait()
const proposalContractRETHLUSD = await ethers.getContractAt(
'InstanceAdditionProposal',
receipt.events[0].args[0],
)
const addedData = await proposalContractRETHLUSD.getAllDataToAdd()
expect(addedData.length).to.equal(6)
await expect(
proposalFactory.createProposalContract(
[config.RETH, config.FRXETH],
// Should be reverted because of frxeth
[500, 3000],
[18, 18, 3921],
[denominations, denominations],
[
[100, 100, 100, 312],
[100, 100, 100, 312],
],
6,
),
).to.be.reverted
console.log('\nInsufficient cardinality frxETH reverted. 🦄\n')
console.log('Starting proposal process on former proposal...\n')
governance = governance.connect(proposer)
torn = torn.connect(proposer)
const proposerBalance = await torn.balanceOf(proposer.address)
await torn.approve(governance.address, proposerBalance)
await expect(() => governance.lockWithApproval(proposerBalance)).to.changeTokenBalance(
torn,
proposer,
proposerBalance.mul('-1'),
)
await expect(governance.propose(proposalContractRETHLUSD.address, 'Add some random tokens.')).to.not.be
.reverted
const proposalId = await governance.latestProposalIds(proposer.address)
console.log(`\nSuccessfully proposed proposal with id ${proposalId}. 📜\n`)
const votingDelay = await governance.VOTING_DELAY()
// Mine the time necessary for the proposal to finish
await minewait(votingDelay.toNumber())
await expect(governance.castVote(proposalId, true)).to.not.be.reverted
const delay = await governance.EXECUTION_DELAY()
const period = await governance.VOTING_PERIOD()
// Mine the time necessary for the proposal to finish
await minewait(delay.add(period.add('43200')).toNumber())
await expect(governance.execute(proposalId)).to.not.be.reverted
console.log('\n Successfully executed the proposal! 🥳\n')
})
})

43
test/utils.js Normal file
View File

@ -0,0 +1,43 @@
/* global ethers, network */
async function setTime(timestamp) {
await ethers.provider.send('evm_setNextBlockTimestamp', [timestamp])
}
async function takeSnapshot() {
return await ethers.provider.send('evm_snapshot', [])
}
async function revertSnapshot(id) {
await ethers.provider.send('evm_revert', [id])
}
async function advanceTime(sec) {
const now = (await ethers.provider.getBlock('latest')).timestamp
await setTime(now + sec)
}
async function getSignerFromAddress(address) {
await network.provider.request({
method: 'hardhat_impersonateAccount',
params: [address],
})
let signer = await ethers.provider.getSigner(address)
signer.address = signer._address
return signer
}
async function minewait(time) {
await ethers.provider.send('evm_increaseTime', [time])
await ethers.provider.send('evm_mine', [])
}
module.exports = {
setTime,
advanceTime,
takeSnapshot,
revertSnapshot,
getSignerFromAddress,
minewait,
}