fork tested
Signed-off-by: T-Hax <>
This commit is contained in:
parent
a8495d76a4
commit
3863e951d8
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -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
188
test/all.test.js
Normal 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
43
test/utils.js
Normal 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,
|
||||
}
|
Loading…
Reference in New Issue
Block a user