2024-03-30 09:10:48 +03:00
|
|
|
import fs from 'fs';
|
|
|
|
import path from 'path';
|
|
|
|
import hardhat, { ethers } from 'hardhat';
|
|
|
|
import {
|
|
|
|
type ERC20Mock,
|
|
|
|
type InstanceRegistry,
|
|
|
|
ERC20Mock__factory,
|
|
|
|
TORN__factory,
|
|
|
|
Governance__factory,
|
|
|
|
ETHTornado__factory,
|
|
|
|
ERC20Tornado__factory,
|
|
|
|
Vesting__factory,
|
|
|
|
TestnetGovernanceProxy__factory,
|
|
|
|
TestnetAdminProxy__factory,
|
|
|
|
GasCompensationVault__factory,
|
|
|
|
InstanceRegistry__factory,
|
|
|
|
GovernanceProposalStateUpgrade__factory,
|
|
|
|
RelayerRegistry__factory,
|
|
|
|
TornadoStakingRewards__factory,
|
|
|
|
TornadoRouter__factory,
|
|
|
|
TornadoVault__factory,
|
|
|
|
Echoer__factory,
|
|
|
|
TestnetFeeManager__factory,
|
|
|
|
} from '../typechain-types';
|
|
|
|
import type { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers';
|
|
|
|
const {
|
|
|
|
ZeroAddress,
|
|
|
|
parseEther,
|
|
|
|
deployContract
|
|
|
|
} = ethers;
|
|
|
|
|
|
|
|
type contracts = {
|
|
|
|
[key in string]: {
|
|
|
|
DAI: string,
|
2024-03-30 12:40:05 +03:00
|
|
|
ENS: string,
|
2024-03-30 09:10:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const contracts: contracts = {
|
|
|
|
hardhat: {
|
|
|
|
DAI: '',
|
2024-03-30 12:40:05 +03:00
|
|
|
ENS: '',
|
2024-03-30 09:10:48 +03:00
|
|
|
},
|
|
|
|
develop: {
|
|
|
|
DAI: '',
|
2024-03-30 12:40:05 +03:00
|
|
|
ENS: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
|
2024-03-30 09:10:48 +03:00
|
|
|
},
|
|
|
|
sepolia: {
|
|
|
|
// https://staging.aave.com/faucet/
|
|
|
|
DAI: '0xFF34B3d4Aee8ddCd6F9AFFFB6Fe49bD371b8a357',
|
2024-03-30 12:40:05 +03:00
|
|
|
ENS: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
|
2024-03-30 09:10:48 +03:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
|
|
const toFixedHex = (number: number, length = 32) => {
|
|
|
|
return '0x' + BigInt(number).toString(16).padStart(length * 2, '0');
|
|
|
|
};
|
|
|
|
|
|
|
|
async function deployHasher(owner: SignerWithAddress) {
|
|
|
|
const bytecode = fs.readFileSync(path.join(__dirname, './hasherBytecode.txt'), { encoding: 'utf8' });
|
|
|
|
|
|
|
|
const txReceipt = await owner.sendTransaction({ data: bytecode }).then(t => t.wait());
|
|
|
|
|
|
|
|
return txReceipt?.contractAddress as unknown as string;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function deployInstances() {
|
|
|
|
const [owner] = await ethers.getSigners();
|
|
|
|
const { DAI: daiAddress } = contracts[hardhat.network.name];
|
|
|
|
|
|
|
|
let DAI: ERC20Mock;
|
|
|
|
|
|
|
|
if (!daiAddress) {
|
|
|
|
DAI = await new ERC20Mock__factory(owner).deploy();
|
|
|
|
await DAI.waitForDeployment();
|
|
|
|
|
|
|
|
await DAI.mint(owner.address, parseEther('100000000')).then(t => t.wait());
|
|
|
|
} else {
|
|
|
|
DAI = ERC20Mock__factory.connect(daiAddress, owner);
|
|
|
|
}
|
|
|
|
|
|
|
|
const Hasher = await deployHasher(owner);
|
|
|
|
|
|
|
|
const Verifier = await deployContract('Verifier');
|
|
|
|
await Verifier.waitForDeployment();
|
|
|
|
|
|
|
|
const ETHTornadoFactory = new ETHTornado__factory(owner);
|
|
|
|
|
|
|
|
const ETHTornado1 = await ETHTornadoFactory.deploy(Verifier.target, Hasher, parseEther('0.1'), 20);
|
|
|
|
await ETHTornado1.waitForDeployment();
|
|
|
|
const ETHTornado2 = await ETHTornadoFactory.deploy(Verifier.target, Hasher, parseEther('1'), 20);
|
|
|
|
await ETHTornado2.waitForDeployment();
|
|
|
|
const ETHTornado3 = await ETHTornadoFactory.deploy(Verifier.target, Hasher, parseEther('10'), 20);
|
|
|
|
await ETHTornado3.waitForDeployment();
|
|
|
|
const ETHTornado4 = await ETHTornadoFactory.deploy(Verifier.target, Hasher, parseEther('100'), 20);
|
|
|
|
await ETHTornado4.waitForDeployment();
|
|
|
|
|
|
|
|
// Check if deposit works
|
|
|
|
// console.log(await ETHTornado1.deposit(toFixedHex(42), { value: parseEther('0.1') }).then(t => t.wait()))
|
|
|
|
|
|
|
|
const DAITornadoFactory = new ERC20Tornado__factory(owner);
|
|
|
|
|
|
|
|
const DAITornado1 = await DAITornadoFactory.deploy(Verifier.target, Hasher, parseEther('100'), 20, DAI.target);
|
|
|
|
await DAITornado1.waitForDeployment();
|
|
|
|
const DAITornado2 = await DAITornadoFactory.deploy(Verifier.target, Hasher, parseEther('1000'), 20, DAI.target);
|
|
|
|
await DAITornado2.waitForDeployment();
|
|
|
|
const DAITornado3 = await DAITornadoFactory.deploy(Verifier.target, Hasher, parseEther('10000'), 20, DAI.target);
|
|
|
|
await DAITornado3.waitForDeployment();
|
|
|
|
const DAITornado4 = await DAITornadoFactory.deploy(Verifier.target, Hasher, parseEther('100000'), 20, DAI.target);
|
|
|
|
await DAITornado4.waitForDeployment();
|
|
|
|
|
|
|
|
const ethInstance = {
|
|
|
|
isERC20: false,
|
|
|
|
token: ZeroAddress,
|
|
|
|
state: 1,
|
|
|
|
uniswapPoolSwappingFee: 0,
|
|
|
|
protocolFeePercentage: 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
const daiInstance = {
|
|
|
|
isERC20: true,
|
|
|
|
token: DAI.target,
|
|
|
|
state: 1,
|
|
|
|
uniswapPoolSwappingFee: 3000,
|
|
|
|
protocolFeePercentage: 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
const instances = [
|
|
|
|
{
|
|
|
|
addr: ETHTornado1.target,
|
|
|
|
instance: ethInstance,
|
|
|
|
},
|
|
|
|
{
|
2024-03-30 12:40:05 +03:00
|
|
|
addr: ETHTornado2.target,
|
2024-03-30 09:10:48 +03:00
|
|
|
instance: {
|
|
|
|
...ethInstance,
|
|
|
|
protocolFeePercentage: 30,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
addr: ETHTornado3.target,
|
|
|
|
instance: {
|
|
|
|
...ethInstance,
|
|
|
|
protocolFeePercentage: 30,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
addr: ETHTornado4.target,
|
|
|
|
instance: {
|
|
|
|
...ethInstance,
|
|
|
|
protocolFeePercentage: 30,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
addr: DAITornado1.target,
|
|
|
|
instance: daiInstance,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
addr: DAITornado2.target,
|
|
|
|
instance: daiInstance,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
addr: DAITornado3.target,
|
|
|
|
instance: {
|
|
|
|
...daiInstance,
|
|
|
|
protocolFeePercentage: 30,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
addr: DAITornado4.target,
|
|
|
|
instance: {
|
|
|
|
...daiInstance,
|
|
|
|
protocolFeePercentage: 30,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
console.log({
|
|
|
|
DAI: DAI.target,
|
|
|
|
Hasher: Hasher,
|
|
|
|
Verifier: Verifier.target,
|
|
|
|
instances,
|
|
|
|
});
|
|
|
|
|
|
|
|
return {
|
|
|
|
DAI,
|
|
|
|
Hasher,
|
|
|
|
Verifier,
|
|
|
|
instances,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
async function deployGovernance(instances: InstanceRegistry.TornadoStruct[]): Promise<void> {
|
|
|
|
const [owner] = await ethers.getSigners();
|
|
|
|
const { ENS: ensAddress } = contracts[hardhat.network.name];
|
|
|
|
|
|
|
|
// Deploy V1 logic contact first
|
|
|
|
const GovernanceV1 = await new Governance__factory(owner).deploy();
|
|
|
|
await GovernanceV1.waitForDeployment();
|
|
|
|
|
|
|
|
// This is what almost every contract would need for constructor args
|
|
|
|
const GovernanceProxy = await new TestnetGovernanceProxy__factory(owner).deploy(GovernanceV1.target, '0x');
|
|
|
|
await GovernanceProxy.waitForDeployment();
|
|
|
|
|
|
|
|
const TORN = await new TORN__factory(owner).deploy(
|
|
|
|
GovernanceProxy.target,
|
|
|
|
0,
|
|
|
|
[
|
|
|
|
{
|
|
|
|
to: owner.address,
|
|
|
|
amount: parseEther('10000000')
|
|
|
|
}
|
|
|
|
]
|
|
|
|
);
|
|
|
|
await TORN.waitForDeployment();
|
|
|
|
|
|
|
|
// Initialize governance v1 with TORN address
|
|
|
|
await (Governance__factory.connect(GovernanceProxy.target as string, owner)).initialize(TORN.target).then(t => t.wait());
|
|
|
|
|
|
|
|
// Create governance vesting contract
|
|
|
|
const GovernanceVesting = await new Vesting__factory(owner).deploy(
|
|
|
|
TORN.target,
|
|
|
|
GovernanceProxy.target,
|
|
|
|
0,
|
|
|
|
3,
|
|
|
|
60
|
|
|
|
);
|
|
|
|
await GovernanceVesting.waitForDeployment();
|
|
|
|
|
|
|
|
await TORN.transfer(GovernanceVesting.target, parseEther('2500000')).then(t => t.wait());
|
|
|
|
|
|
|
|
// Deploy other governance contracts
|
|
|
|
const ProxyFactory = new TestnetAdminProxy__factory(owner);
|
|
|
|
|
|
|
|
// Deploy gas compensation vault
|
|
|
|
const GasCompensationVault = await new GasCompensationVault__factory(owner).deploy(GovernanceProxy.target);
|
|
|
|
await GasCompensationVault.waitForDeployment();
|
|
|
|
|
|
|
|
// Deploy user vault
|
|
|
|
const TornadoVault = await new TornadoVault__factory(owner).deploy(TORN.target, GovernanceProxy.target);
|
|
|
|
await TornadoVault.waitForDeployment();
|
|
|
|
|
|
|
|
// Deploy instance registry
|
|
|
|
const InstanceRegistryImpl = await new InstanceRegistry__factory(owner).deploy(GovernanceProxy.target);
|
|
|
|
await InstanceRegistryImpl.waitForDeployment();
|
|
|
|
|
|
|
|
const InstanceRegistryProxy = await ProxyFactory.deploy(InstanceRegistryImpl.target, GovernanceProxy.target, '0x');
|
|
|
|
await InstanceRegistryProxy.waitForDeployment();
|
|
|
|
|
|
|
|
// Deploy FeeManager
|
|
|
|
const FeeManagerImpl = await new TestnetFeeManager__factory(owner).deploy(TORN.target, GovernanceProxy.target, InstanceRegistryProxy.target);
|
|
|
|
await FeeManagerImpl.waitForDeployment();
|
|
|
|
|
|
|
|
const FeeManagerProxy = await ProxyFactory.deploy(FeeManagerImpl.target, GovernanceProxy.target, '0x');
|
|
|
|
await FeeManagerProxy.waitForDeployment();
|
|
|
|
|
|
|
|
// Deploy RelayerRegistry & TornadoStakingRewards
|
|
|
|
const RelayerRegistryMock = await new RelayerRegistry__factory(owner).deploy(
|
|
|
|
TORN.target,
|
|
|
|
GovernanceProxy.target,
|
|
|
|
ensAddress || ZeroAddress,
|
|
|
|
ZeroAddress, // Use this as zero address as we don't know the proxy address yet
|
|
|
|
FeeManagerProxy.target,
|
|
|
|
);
|
|
|
|
await RelayerRegistryMock.waitForDeployment();
|
|
|
|
|
|
|
|
const RelayerRegistryProxy = await ProxyFactory.deploy(RelayerRegistryMock.target, GovernanceProxy.target, '0x');
|
|
|
|
await RelayerRegistryProxy.waitForDeployment();
|
|
|
|
|
|
|
|
const TornadoStakingRewardsImpl = await new TornadoStakingRewards__factory(owner).deploy(
|
|
|
|
GovernanceProxy.target,
|
|
|
|
TORN.target,
|
|
|
|
RelayerRegistryProxy.target
|
|
|
|
);
|
|
|
|
await TornadoStakingRewardsImpl.waitForDeployment();
|
|
|
|
|
|
|
|
const TornadoStakingRewardsProxy = await ProxyFactory.deploy(TornadoStakingRewardsImpl.target, GovernanceProxy.target, '0x');
|
|
|
|
await TornadoStakingRewardsProxy.waitForDeployment();
|
|
|
|
|
|
|
|
const RelayerRegistryImpl = await new RelayerRegistry__factory(owner).deploy(
|
|
|
|
TORN.target,
|
|
|
|
GovernanceProxy.target,
|
|
|
|
ensAddress || ZeroAddress,
|
|
|
|
TornadoStakingRewardsProxy.target,
|
|
|
|
FeeManagerProxy.target,
|
|
|
|
);
|
|
|
|
await RelayerRegistryImpl.waitForDeployment();
|
|
|
|
|
|
|
|
await (TestnetAdminProxy__factory.connect(RelayerRegistryProxy.target as string, owner)).upgradeToOwner(RelayerRegistryImpl.target).then(t => t.wait());
|
|
|
|
|
|
|
|
// Deploy Echoer
|
|
|
|
const Echoer = await new Echoer__factory(owner).deploy();
|
|
|
|
await Echoer.waitForDeployment();
|
|
|
|
|
|
|
|
// Deploy TornadoRouter
|
|
|
|
const TornadoRouter = await new TornadoRouter__factory(owner).deploy(
|
|
|
|
GovernanceProxy.target,
|
|
|
|
InstanceRegistryProxy.target,
|
|
|
|
RelayerRegistryProxy.target
|
|
|
|
);
|
|
|
|
await TornadoRouter.waitForDeployment();
|
|
|
|
|
|
|
|
// Initialize InstanceRegistry
|
|
|
|
await (InstanceRegistry__factory.connect(InstanceRegistryProxy.target as string, owner)).initialize(
|
|
|
|
instances,
|
|
|
|
TornadoRouter.target
|
|
|
|
).then(t => t.wait());
|
|
|
|
|
|
|
|
// Initialize RelayerRegistry
|
|
|
|
await (RelayerRegistry__factory.connect(RelayerRegistryProxy.target as string, owner)).initialize(
|
|
|
|
TornadoRouter.target
|
|
|
|
).then(t => t.wait());
|
|
|
|
|
|
|
|
// Upgrade Governance
|
|
|
|
const GovernanceV5 = await new GovernanceProposalStateUpgrade__factory(owner).deploy(
|
|
|
|
TornadoStakingRewardsProxy.target,
|
|
|
|
GasCompensationVault.target,
|
|
|
|
TornadoVault.target
|
|
|
|
);
|
|
|
|
await GovernanceV5.waitForDeployment();
|
|
|
|
|
|
|
|
await (TestnetGovernanceProxy__factory.connect(GovernanceProxy.target as string, owner)).upgradeToOwner(GovernanceV5.target).then(t => t.wait());
|
|
|
|
|
|
|
|
// Finalize contracts
|
|
|
|
await owner.sendTransaction({ to: GasCompensationVault.target, value: parseEther('0.1') }).then(t => t.wait());
|
|
|
|
|
|
|
|
console.log({
|
|
|
|
TORN: TORN.target,
|
|
|
|
GovernanceProxy: GovernanceProxy.target,
|
|
|
|
GovernanceV1: GovernanceV1.target,
|
|
|
|
GovernanceV5: GovernanceV5.target,
|
|
|
|
GovernanceVesting: GovernanceVesting.target,
|
|
|
|
GasCompensationVault: GasCompensationVault.target,
|
|
|
|
TornadoVault: TornadoVault.target,
|
|
|
|
InstanceRegistryProxy: InstanceRegistryProxy.target,
|
|
|
|
InstanceRegistryImpl: InstanceRegistryImpl.target,
|
|
|
|
FeeManagerProxy: FeeManagerProxy.target,
|
|
|
|
FeeManagerImpl: FeeManagerImpl.target,
|
|
|
|
RelayerRegistryProxy: RelayerRegistryProxy.target,
|
|
|
|
RelayerRegistryImpl: RelayerRegistryImpl.target,
|
|
|
|
RelayerRegistryMock: RelayerRegistryMock.target,
|
|
|
|
TornadoStakingRewardsProxy: TornadoStakingRewardsProxy.target,
|
|
|
|
TornadoStakingRewardsImpl: TornadoStakingRewardsImpl.target,
|
|
|
|
TornadoRouter: TornadoRouter.target,
|
|
|
|
Echoer: Echoer.target,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async function deploy() {
|
|
|
|
const { instances } = await deployInstances();
|
|
|
|
await deployGovernance(instances);
|
|
|
|
}
|
|
|
|
|
|
|
|
deploy();
|