mirror of
https://github.com/tornadocash/tornado-pool-factory
synced 2024-02-02 15:04:08 +01:00
add creation fee logic
This commit is contained in:
parent
ecbed3dd29
commit
238363d348
@ -9,4 +9,5 @@ module.exports = {
|
|||||||
COMP: '0xc00e94Cb662C3520282E6f5717214004A7f26888',
|
COMP: '0xc00e94Cb662C3520282E6f5717214004A7f26888',
|
||||||
TORN: '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
|
TORN: '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
|
||||||
tornWhale: '0xF977814e90dA44bFA03b6295A0616a897441aceC',
|
tornWhale: '0xF977814e90dA44bFA03b6295A0616a897441aceC',
|
||||||
|
creationFee: '200000000000000000000', // 200 TORN
|
||||||
}
|
}
|
||||||
|
@ -70,19 +70,25 @@ contract AddInstanceProposal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function denominationByIndex(uint256 _index) public view returns (uint256) {
|
function denominationByIndex(uint256 _index) public view returns (uint256) {
|
||||||
if (_index == 0) { return denomination0; }
|
if (_index == 0) {
|
||||||
else if (_index == 1) { return denomination1; }
|
return denomination0;
|
||||||
else if (_index == 2) { return denomination2; }
|
} else if (_index == 1) {
|
||||||
else {
|
return denomination1;
|
||||||
|
} else if (_index == 2) {
|
||||||
|
return denomination2;
|
||||||
|
} else {
|
||||||
revert("Invalid instance index");
|
revert("Invalid instance index");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function protocolFeeByIndex(uint256 _index) public view returns (uint32) {
|
function protocolFeeByIndex(uint256 _index) public view returns (uint32) {
|
||||||
if (_index == 0) { return protocolFee0; }
|
if (_index == 0) {
|
||||||
else if (_index == 1) { return protocolFee1; }
|
return protocolFee0;
|
||||||
else if (_index == 2) { return protocolFee2; }
|
} else if (_index == 1) {
|
||||||
else {
|
return protocolFee1;
|
||||||
|
} else if (_index == 2) {
|
||||||
|
return protocolFee2;
|
||||||
|
} else {
|
||||||
revert("Invalid instance index");
|
revert("Invalid instance index");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ contract ERC20TornadoCloneable is ERC20Tornado {
|
|||||||
address _token
|
address _token
|
||||||
) external {
|
) external {
|
||||||
require(denomination == 0 && levels == 0, "already initialized");
|
require(denomination == 0 && levels == 0, "already initialized");
|
||||||
|
|
||||||
token = IERC20(_token);
|
token = IERC20(_token);
|
||||||
require(_denomination > 0, "denomination should be greater than 0");
|
require(_denomination > 0, "denomination should be greater than 0");
|
||||||
denomination = _denomination;
|
denomination = _denomination;
|
||||||
|
@ -9,22 +9,26 @@ import "@openzeppelin/contracts/proxy/Clones.sol";
|
|||||||
import "./ERC20TornadoCloneable.sol";
|
import "./ERC20TornadoCloneable.sol";
|
||||||
import "./AddInstanceProposal.sol";
|
import "./AddInstanceProposal.sol";
|
||||||
import "./interfaces/IGovernance.sol";
|
import "./interfaces/IGovernance.sol";
|
||||||
|
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
import { IERC20Permit } from "@openzeppelin/contracts/drafts/IERC20Permit.sol";
|
||||||
|
|
||||||
contract InstanceFactory is Ownable {
|
contract InstanceFactory is Ownable {
|
||||||
using Clones for address;
|
using Clones for address;
|
||||||
using Address for address;
|
using Address for address;
|
||||||
|
|
||||||
address public immutable governance;
|
address public immutable governance;
|
||||||
|
address public immutable torn;
|
||||||
address public immutable instanceRegistry;
|
address public immutable instanceRegistry;
|
||||||
address public implementation;
|
address public implementation;
|
||||||
address public verifier;
|
address public verifier;
|
||||||
address public hasher;
|
address public hasher;
|
||||||
uint32 public merkleTreeHeight;
|
uint32 public merkleTreeHeight;
|
||||||
|
uint256 public creationFee;
|
||||||
|
|
||||||
event NewVerifierSet(address indexed newVerifier);
|
event NewVerifierSet(address indexed newVerifier);
|
||||||
event NewHasherSet(address indexed newHasher);
|
event NewHasherSet(address indexed newHasher);
|
||||||
event NewTreeHeightSet(uint32 indexed newTreeHeight);
|
event NewTreeHeightSet(uint32 indexed newTreeHeight);
|
||||||
|
event NewCreationFeeSet(uint256 indexed newCreationFee);
|
||||||
event NewImplementationSet(address indexed newImplemenentation);
|
event NewImplementationSet(address indexed newImplemenentation);
|
||||||
event NewInstanceCloneCreated(address indexed clone);
|
event NewInstanceCloneCreated(address indexed clone);
|
||||||
event NewGovernanceProposalCreated(address indexed proposal);
|
event NewGovernanceProposalCreated(address indexed proposal);
|
||||||
@ -34,13 +38,17 @@ contract InstanceFactory is Ownable {
|
|||||||
address _hasher,
|
address _hasher,
|
||||||
uint32 _merkleTreeHeight,
|
uint32 _merkleTreeHeight,
|
||||||
address _governance,
|
address _governance,
|
||||||
address _instanceRegistry
|
address _instanceRegistry,
|
||||||
|
address _torn,
|
||||||
|
uint256 _creationFee
|
||||||
) {
|
) {
|
||||||
verifier = _verifier;
|
verifier = _verifier;
|
||||||
hasher = _hasher;
|
hasher = _hasher;
|
||||||
merkleTreeHeight = _merkleTreeHeight;
|
merkleTreeHeight = _merkleTreeHeight;
|
||||||
governance = _governance;
|
governance = _governance;
|
||||||
instanceRegistry = _instanceRegistry;
|
instanceRegistry = _instanceRegistry;
|
||||||
|
torn = _torn;
|
||||||
|
creationFee = _creationFee;
|
||||||
|
|
||||||
ERC20TornadoCloneable implContract = new ERC20TornadoCloneable(_verifier, _hasher);
|
ERC20TornadoCloneable implContract = new ERC20TornadoCloneable(_verifier, _hasher);
|
||||||
implementation = address(implContract);
|
implementation = address(implContract);
|
||||||
@ -65,25 +73,46 @@ contract InstanceFactory is Ownable {
|
|||||||
return implementation.predictDeterministicAddress(salt);
|
return implementation.predictDeterministicAddress(salt);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNewProposal(
|
function createProposalApprove(
|
||||||
address _token,
|
address _token,
|
||||||
uint24 _uniswapPoolSwappingFee,
|
uint24 _uniswapPoolSwappingFee,
|
||||||
uint256[] memory _denominations,
|
uint256[] memory _denominations,
|
||||||
uint32[] memory _protocolFees
|
uint32[] memory _protocolFees
|
||||||
) external returns (address) {
|
) external returns (address) {
|
||||||
|
require(IERC20(torn).transferFrom(msg.sender, governance, creationFee));
|
||||||
|
return _createProposal(_token, _uniswapPoolSwappingFee, _denominations, _protocolFees);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createProposalPermit(
|
||||||
|
address _token,
|
||||||
|
uint24 _uniswapPoolSwappingFee,
|
||||||
|
uint256[] memory _denominations,
|
||||||
|
uint32[] memory _protocolFees,
|
||||||
|
address creater,
|
||||||
|
uint256 deadline,
|
||||||
|
uint8 v,
|
||||||
|
bytes32 r,
|
||||||
|
bytes32 s
|
||||||
|
) external returns (address) {
|
||||||
|
IERC20Permit(torn).permit(creater, address(this), creationFee, deadline, v, r, s);
|
||||||
|
require(IERC20(torn).transferFrom(creater, governance, creationFee));
|
||||||
|
return _createProposal(_token, _uniswapPoolSwappingFee, _denominations, _protocolFees);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _createProposal(
|
||||||
|
address _token,
|
||||||
|
uint24 _uniswapPoolSwappingFee,
|
||||||
|
uint256[] memory _denominations,
|
||||||
|
uint32[] memory _protocolFees
|
||||||
|
) internal returns (address) {
|
||||||
require(_token.isContract(), "Token is not contract"); // TODO should we check that such instance already exist?
|
require(_token.isContract(), "Token is not contract"); // TODO should we check that such instance already exist?
|
||||||
require(_uniswapPoolSwappingFee > 0, "uniswapPoolSwappingFee is zero"); // TODO should we check > 0 ?
|
require(_uniswapPoolSwappingFee > 0, "uniswapPoolSwappingFee is zero"); // TODO should we check > 0 ?
|
||||||
require(_denominations.length > 0, "Empty denominations");
|
require(_denominations.length > 0, "Empty denominations");
|
||||||
require(_denominations.length == _protocolFees.length, "Incorrect denominations/fees length");
|
require(_denominations.length == _protocolFees.length, "Incorrect denominations/fees length");
|
||||||
|
|
||||||
address proposal = address(new AddInstanceProposal(
|
address proposal = address(
|
||||||
address(this),
|
new AddInstanceProposal(address(this), instanceRegistry, _token, _uniswapPoolSwappingFee, _denominations, _protocolFees)
|
||||||
instanceRegistry,
|
);
|
||||||
_token,
|
|
||||||
_uniswapPoolSwappingFee,
|
|
||||||
_denominations,
|
|
||||||
_protocolFees
|
|
||||||
));
|
|
||||||
emit NewGovernanceProposalCreated(proposal);
|
emit NewGovernanceProposalCreated(proposal);
|
||||||
|
|
||||||
return proposal;
|
return proposal;
|
||||||
@ -104,6 +133,11 @@ contract InstanceFactory is Ownable {
|
|||||||
emit NewTreeHeightSet(merkleTreeHeight);
|
emit NewTreeHeightSet(merkleTreeHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setCreationFee(uint256 _creationFee) external onlyOwner {
|
||||||
|
creationFee = _creationFee;
|
||||||
|
emit NewCreationFeeSet(_creationFee);
|
||||||
|
}
|
||||||
|
|
||||||
function setImplementation(address _newImplementation) external onlyOwner {
|
function setImplementation(address _newImplementation) external onlyOwner {
|
||||||
implementation = _newImplementation;
|
implementation = _newImplementation;
|
||||||
emit NewImplementationSet(implementation);
|
emit NewImplementationSet(implementation);
|
||||||
|
@ -6,8 +6,10 @@ pragma abicoder v2;
|
|||||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
|
||||||
interface IInstanceRegistry {
|
interface IInstanceRegistry {
|
||||||
|
enum InstanceState {
|
||||||
enum InstanceState { DISABLED, ENABLED}
|
DISABLED,
|
||||||
|
ENABLED
|
||||||
|
}
|
||||||
|
|
||||||
struct Instance {
|
struct Instance {
|
||||||
bool isERC20;
|
bool isERC20;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
pragma solidity 0.6.12 || 0.7.6;
|
pragma solidity 0.6.12||0.7.6;
|
||||||
|
|
||||||
import { Governance } from "tornado-governance/contracts/v1/Governance.sol";
|
import { Governance } from "tornado-governance/contracts/v1/Governance.sol";
|
||||||
import { InstanceRegistry } from "tornado-relayer-registry/contracts/tornado-proxy/InstanceRegistry.sol";
|
import { InstanceRegistry } from "tornado-relayer-registry/contracts/tornado-proxy/InstanceRegistry.sol";
|
||||||
|
44
scripts/permit.js
Normal file
44
scripts/permit.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
const { EIP712Signer } = require('@ticket721/e712')
|
||||||
|
|
||||||
|
const Permit = [
|
||||||
|
{ name: 'owner', type: 'address' },
|
||||||
|
{ name: 'spender', type: 'address' },
|
||||||
|
{ name: 'value', type: 'uint256' },
|
||||||
|
{ name: 'nonce', type: 'uint256' },
|
||||||
|
{ name: 'deadline', type: 'uint256' },
|
||||||
|
]
|
||||||
|
|
||||||
|
class PermitSigner extends EIP712Signer {
|
||||||
|
constructor(_domain, _permitArgs) {
|
||||||
|
super(_domain, ['Permit', Permit])
|
||||||
|
this.permitArgs = _permitArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)
|
||||||
|
setPermitInfo(_permitArgs) {
|
||||||
|
this.permitArgs = _permitArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
getPayload() {
|
||||||
|
return this.generatePayload(this.permitArgs, 'Permit')
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSignature(privateKey) {
|
||||||
|
let payload = this.getPayload()
|
||||||
|
payload.message.owner = payload.message.owner.address
|
||||||
|
const { hex, v, r, s } = await this.sign(privateKey, payload)
|
||||||
|
return {
|
||||||
|
hex,
|
||||||
|
v,
|
||||||
|
r: '0x' + r,
|
||||||
|
s: '0x' + s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getSignerAddress(permitArgs, signature) {
|
||||||
|
const original_payload = this.generatePayload(permitArgs, 'Permit')
|
||||||
|
return this.verify(original_payload, signature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { PermitSigner }
|
@ -7,7 +7,7 @@ const { rbigint, createDeposit, toHex, generateProof, initialize } = require('to
|
|||||||
const MixerContractABI = require('tornado-cli/build/contracts/Mixer.abi.json')
|
const MixerContractABI = require('tornado-cli/build/contracts/Mixer.abi.json')
|
||||||
const config = require('../config')
|
const config = require('../config')
|
||||||
const { getSignerFromAddress, minewait } = require('./utils')
|
const { getSignerFromAddress, minewait } = require('./utils')
|
||||||
|
const { PermitSigner } = require('../scripts/permit.js')
|
||||||
|
|
||||||
describe('Instance Factory Tests', () => {
|
describe('Instance Factory Tests', () => {
|
||||||
const ProposalState = {
|
const ProposalState = {
|
||||||
@ -26,14 +26,11 @@ describe('Instance Factory Tests', () => {
|
|||||||
const [sender, deployer, multisig] = await ethers.getSigners()
|
const [sender, deployer, multisig] = await ethers.getSigners()
|
||||||
|
|
||||||
const tornWhale = await getSignerFromAddress(config.tornWhale)
|
const tornWhale = await getSignerFromAddress(config.tornWhale)
|
||||||
|
|
||||||
const gov = await ethers.getContractAt(
|
const gov = await ethers.getContractAt('Governance', config.governance)
|
||||||
'Governance',
|
|
||||||
config.governance,
|
|
||||||
)
|
|
||||||
|
|
||||||
const tornToken = await ethers.getContractAt(
|
const tornToken = await ethers.getContractAt(
|
||||||
'@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20',
|
'@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20',
|
||||||
config.TORN,
|
config.TORN,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -43,7 +40,7 @@ describe('Instance Factory Tests', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
instanceRegistry = await ethers.getContractAt(
|
instanceRegistry = await ethers.getContractAt(
|
||||||
'tornado-relayer-registry/contracts/tornado-proxy/InstanceRegistry.sol:InstanceRegistry',
|
'tornado-relayer-registry/contracts/tornado-proxy/InstanceRegistry.sol:InstanceRegistry',
|
||||||
config.instanceRegistry,
|
config.instanceRegistry,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -54,11 +51,23 @@ describe('Instance Factory Tests', () => {
|
|||||||
config.hasher,
|
config.hasher,
|
||||||
config.merkleTreeHeight,
|
config.merkleTreeHeight,
|
||||||
config.governance,
|
config.governance,
|
||||||
config.instanceRegistry
|
config.instanceRegistry,
|
||||||
|
config.TORN,
|
||||||
|
config.creationFee,
|
||||||
)
|
)
|
||||||
await instanceFactory.deployed()
|
await instanceFactory.deployed()
|
||||||
|
|
||||||
return { sender, deployer, multisig, tornWhale, gov, tornToken, compToken, instanceRegistry, instanceFactory }
|
return {
|
||||||
|
sender,
|
||||||
|
deployer,
|
||||||
|
multisig,
|
||||||
|
tornWhale,
|
||||||
|
gov,
|
||||||
|
tornToken,
|
||||||
|
compToken,
|
||||||
|
instanceRegistry,
|
||||||
|
instanceFactory,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
it('Should have initialized all successfully', async function () {
|
it('Should have initialized all successfully', async function () {
|
||||||
@ -73,11 +82,13 @@ describe('Instance Factory Tests', () => {
|
|||||||
it('Should set correct params for factory', async function () {
|
it('Should set correct params for factory', async function () {
|
||||||
const { instanceFactory } = await loadFixture(fixture)
|
const { instanceFactory } = await loadFixture(fixture)
|
||||||
|
|
||||||
expect( await instanceFactory.governance()).to.be.equal(config.governance)
|
expect(await instanceFactory.governance()).to.be.equal(config.governance)
|
||||||
expect( await instanceFactory.verifier()).to.be.equal(config.verifier)
|
expect(await instanceFactory.verifier()).to.be.equal(config.verifier)
|
||||||
expect( await instanceFactory.hasher()).to.be.equal(config.hasher)
|
expect(await instanceFactory.hasher()).to.be.equal(config.hasher)
|
||||||
expect( await instanceFactory.merkleTreeHeight()).to.be.equal(config.merkleTreeHeight)
|
expect(await instanceFactory.merkleTreeHeight()).to.be.equal(config.merkleTreeHeight)
|
||||||
expect(await instanceFactory.implementation()).to.exist
|
expect(await instanceFactory.implementation()).to.exist
|
||||||
|
expect(await instanceFactory.creationFee()).to.be.equal(config.creationFee)
|
||||||
|
expect(await instanceFactory.torn()).to.be.equal(config.TORN)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Governance should be able to set factory params', async function () {
|
it('Governance should be able to set factory params', async function () {
|
||||||
@ -87,48 +98,58 @@ describe('Instance Factory Tests', () => {
|
|||||||
|
|
||||||
const govSigner = await getSignerFromAddress(gov.address)
|
const govSigner = await getSignerFromAddress(gov.address)
|
||||||
instanceFactory = await instanceFactory.connect(govSigner)
|
instanceFactory = await instanceFactory.connect(govSigner)
|
||||||
|
|
||||||
await instanceFactory.setVerifier(addressZero)
|
await instanceFactory.setVerifier(addressZero)
|
||||||
await instanceFactory.setHasher(addressZero)
|
await instanceFactory.setHasher(addressZero)
|
||||||
await instanceFactory.setMerkleTreeHeight(1)
|
await instanceFactory.setMerkleTreeHeight(1)
|
||||||
|
await instanceFactory.setCreationFee(0)
|
||||||
|
|
||||||
expect( await instanceFactory.verifier()).to.be.equal(addressZero)
|
expect(await instanceFactory.verifier()).to.be.equal(addressZero)
|
||||||
expect( await instanceFactory.hasher()).to.be.equal(addressZero)
|
expect(await instanceFactory.hasher()).to.be.equal(addressZero)
|
||||||
expect( await instanceFactory.merkleTreeHeight()).to.be.equal(1)
|
expect(await instanceFactory.merkleTreeHeight()).to.be.equal(1)
|
||||||
|
expect(await instanceFactory.creationFee()).to.be.equal(0)
|
||||||
|
|
||||||
await instanceFactory.setVerifier(config.verifier)
|
await instanceFactory.setVerifier(config.verifier)
|
||||||
await instanceFactory.setHasher(config.hasher)
|
await instanceFactory.setHasher(config.hasher)
|
||||||
await instanceFactory.setMerkleTreeHeight(config.merkleTreeHeight)
|
await instanceFactory.setMerkleTreeHeight(config.merkleTreeHeight)
|
||||||
|
await instanceFactory.setCreationFee(config.creationFee)
|
||||||
|
|
||||||
expect( await instanceFactory.verifier()).to.be.equal(config.verifier)
|
expect(await instanceFactory.verifier()).to.be.equal(config.verifier)
|
||||||
expect( await instanceFactory.hasher()).to.be.equal(config.hasher)
|
expect(await instanceFactory.hasher()).to.be.equal(config.hasher)
|
||||||
expect( await instanceFactory.merkleTreeHeight()).to.be.equal(config.merkleTreeHeight)
|
expect(await instanceFactory.merkleTreeHeight()).to.be.equal(config.merkleTreeHeight)
|
||||||
|
expect(await instanceFactory.creationFee()).to.be.equal(config.creationFee)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should successfully deploy/propose/execute proposal - add instance', async function () {
|
it('Should successfully deploy/propose/execute proposal - add instance', async function () {
|
||||||
let { instanceFactory, gov, instanceRegistry, tornWhale, tornToken } = await loadFixture(fixture)
|
let { sender, instanceFactory, gov, instanceRegistry, tornWhale, tornToken } = await loadFixture(fixture)
|
||||||
|
|
||||||
// deploy proposal ----------------------------------------------
|
// deploy proposal ----------------------------------------------
|
||||||
let tx = await instanceFactory.createNewProposal(
|
await tornToken.connect(tornWhale).transfer(sender.address, config.creationFee)
|
||||||
config.COMP,
|
await tornToken.approve(instanceFactory.address, config.creationFee)
|
||||||
3000,
|
|
||||||
[ethers.utils.parseEther('100')],
|
|
||||||
[30]
|
|
||||||
)
|
|
||||||
let receipt = await tx.wait()
|
|
||||||
|
|
||||||
const proposal = await ethers.getContractAt(
|
await expect(() =>
|
||||||
'AddInstanceProposal',
|
instanceFactory
|
||||||
receipt.events[0].args[0],
|
.connect(sender)
|
||||||
|
.createProposalApprove(config.COMP, 3000, [ethers.utils.parseEther('100')], [30]),
|
||||||
|
).to.changeTokenBalances(
|
||||||
|
tornToken,
|
||||||
|
[sender, gov],
|
||||||
|
[BigNumber.from(0).sub(config.creationFee), config.creationFee],
|
||||||
)
|
)
|
||||||
|
|
||||||
expect( await proposal.instanceFactory()).to.be.equal(instanceFactory.address)
|
let logs = await ethers.provider.getLogs(instanceFactory.filters.NewGovernanceProposalCreated())
|
||||||
expect( await proposal.instanceRegistry()).to.be.equal(instanceRegistry.address)
|
const proposal = await ethers.getContractAt(
|
||||||
expect( await proposal.token()).to.be.equal(config.COMP)
|
'AddInstanceProposal',
|
||||||
expect( await proposal.uniswapPoolSwappingFee()).to.be.equal(3000)
|
ethers.utils.getAddress('0x' + logs[0].topics[1].slice(26)),
|
||||||
expect( await proposal.numInstances()).to.be.equal(1)
|
)
|
||||||
expect( await proposal.protocolFeeByIndex(0)).to.be.equal(30)
|
|
||||||
expect( await proposal.denominationByIndex(0)).to.be.equal(ethers.utils.parseEther('100'))
|
expect(await proposal.instanceFactory()).to.be.equal(instanceFactory.address)
|
||||||
|
expect(await proposal.instanceRegistry()).to.be.equal(instanceRegistry.address)
|
||||||
|
expect(await proposal.token()).to.be.equal(config.COMP)
|
||||||
|
expect(await proposal.uniswapPoolSwappingFee()).to.be.equal(3000)
|
||||||
|
expect(await proposal.numInstances()).to.be.equal(1)
|
||||||
|
expect(await proposal.protocolFeeByIndex(0)).to.be.equal(30)
|
||||||
|
expect(await proposal.denominationByIndex(0)).to.be.equal(ethers.utils.parseEther('100'))
|
||||||
|
|
||||||
// propose proposal ---------------------------------------------
|
// propose proposal ---------------------------------------------
|
||||||
let response, id, state
|
let response, id, state
|
||||||
@ -147,7 +168,7 @@ describe('Instance Factory Tests', () => {
|
|||||||
expect(args.target).to.be.equal(proposal.address)
|
expect(args.target).to.be.equal(proposal.address)
|
||||||
expect(args.description).to.be.equal('COMP token instance proposal')
|
expect(args.description).to.be.equal('COMP token instance proposal')
|
||||||
expect(state).to.be.equal(ProposalState.Pending)
|
expect(state).to.be.equal(ProposalState.Pending)
|
||||||
|
|
||||||
// execute proposal ---------------------------------------------
|
// execute proposal ---------------------------------------------
|
||||||
await minewait((await gov.VOTING_DELAY()).add(1).toNumber())
|
await minewait((await gov.VOTING_DELAY()).add(1).toNumber())
|
||||||
await expect(gov.castVote(id, true)).to.not.be.reverted
|
await expect(gov.castVote(id, true)).to.not.be.reverted
|
||||||
@ -161,7 +182,7 @@ describe('Instance Factory Tests', () => {
|
|||||||
.toNumber(),
|
.toNumber(),
|
||||||
)
|
)
|
||||||
expect(await gov.state(id)).to.be.equal(ProposalState.AwaitingExecution)
|
expect(await gov.state(id)).to.be.equal(ProposalState.AwaitingExecution)
|
||||||
|
|
||||||
tx = await gov.execute(id)
|
tx = await gov.execute(id)
|
||||||
|
|
||||||
expect(await gov.state(id)).to.be.equal(ProposalState.Executed)
|
expect(await gov.state(id)).to.be.equal(ProposalState.Executed)
|
||||||
@ -169,25 +190,92 @@ describe('Instance Factory Tests', () => {
|
|||||||
// check instance initialization --------------------------------
|
// check instance initialization --------------------------------
|
||||||
receipt = await tx.wait()
|
receipt = await tx.wait()
|
||||||
const instanceAddr = '0x' + receipt.events[0].topics[1].toString().slice(-40)
|
const instanceAddr = '0x' + receipt.events[0].topics[1].toString().slice(-40)
|
||||||
const instance = await ethers.getContractAt(
|
const instance = await ethers.getContractAt('ERC20TornadoCloneable', instanceAddr)
|
||||||
'ERC20TornadoCloneable',
|
|
||||||
instanceAddr,
|
|
||||||
)
|
|
||||||
|
|
||||||
expect( await instance.token()).to.be.equal(config.COMP)
|
expect(await instance.token()).to.be.equal(config.COMP)
|
||||||
expect( await instance.verifier()).to.be.equal(config.verifier)
|
expect(await instance.verifier()).to.be.equal(config.verifier)
|
||||||
expect( await instance.hasher()).to.be.equal(config.hasher)
|
expect(await instance.hasher()).to.be.equal(config.hasher)
|
||||||
expect( await instance.levels()).to.be.equal(config.merkleTreeHeight)
|
expect(await instance.levels()).to.be.equal(config.merkleTreeHeight)
|
||||||
expect( await instance.denomination()).to.equal(ethers.utils.parseEther('100'))
|
expect(await instance.denomination()).to.equal(ethers.utils.parseEther('100'))
|
||||||
|
|
||||||
const instanceData = await instanceRegistry.instances(instance.address)
|
const instanceData = await instanceRegistry.instances(instance.address)
|
||||||
expect(instanceData.isERC20).to.be.equal(true)
|
expect(instanceData.isERC20).to.be.equal(true)
|
||||||
expect(instanceData.token).to.be.equal(config.COMP)
|
expect(instanceData.token).to.be.equal(config.COMP)
|
||||||
expect(instanceData.state).to.be.equal(1)
|
expect(instanceData.state).to.be.equal(1)
|
||||||
expect(instanceData.uniswapPoolSwappingFee).to.be.equal(3000)
|
expect(instanceData.uniswapPoolSwappingFee).to.be.equal(3000)
|
||||||
expect(instanceData.protocolFeePercentage).to.be.equal(30)
|
expect(instanceData.protocolFeePercentage).to.be.equal(30)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should successfully deploy proposal with permit', async function () {
|
||||||
|
let { instanceFactory, gov, instanceRegistry, tornWhale, tornToken } = await loadFixture(fixture)
|
||||||
|
|
||||||
|
const privateKey = '0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3'
|
||||||
|
const publicKey = '0x' + ethers.utils.computeAddress(Buffer.from(privateKey.slice(2), 'hex'))
|
||||||
|
const sender = await ethers.getSigner(publicKey.slice(2))
|
||||||
|
|
||||||
|
await expect(() =>
|
||||||
|
tornToken.connect(tornWhale).transfer(sender.address, config.creationFee),
|
||||||
|
).to.changeTokenBalances(
|
||||||
|
tornToken,
|
||||||
|
[tornWhale, sender],
|
||||||
|
[BigNumber.from(0).sub(config.creationFee), config.creationFee],
|
||||||
|
)
|
||||||
|
|
||||||
|
// prepare permit data
|
||||||
|
const domain = {
|
||||||
|
name: await tornToken.name(),
|
||||||
|
version: '1',
|
||||||
|
chainId: 1,
|
||||||
|
verifyingContract: tornToken.address,
|
||||||
|
}
|
||||||
|
|
||||||
|
const curTimestamp = Math.trunc(new Date().getTime() / 1000)
|
||||||
|
const args = {
|
||||||
|
owner: sender,
|
||||||
|
spender: instanceFactory.address,
|
||||||
|
value: config.creationFee,
|
||||||
|
nonce: 0,
|
||||||
|
deadline: curTimestamp + 1000,
|
||||||
|
}
|
||||||
|
|
||||||
|
const permitSigner = new PermitSigner(domain, args)
|
||||||
|
const signature = await permitSigner.getSignature(privateKey)
|
||||||
|
const signer = await permitSigner.getSignerAddress(args, signature.hex)
|
||||||
|
expect(signer).to.equal(sender.address)
|
||||||
|
|
||||||
|
await expect(() =>
|
||||||
|
instanceFactory.createProposalPermit(
|
||||||
|
config.COMP,
|
||||||
|
3000,
|
||||||
|
[ethers.utils.parseEther('100')],
|
||||||
|
[30],
|
||||||
|
sender.address,
|
||||||
|
args.deadline.toString(),
|
||||||
|
signature.v,
|
||||||
|
signature.r,
|
||||||
|
signature.s,
|
||||||
|
),
|
||||||
|
).to.changeTokenBalances(
|
||||||
|
tornToken,
|
||||||
|
[sender, gov],
|
||||||
|
[BigNumber.from(0).sub(config.creationFee), config.creationFee],
|
||||||
|
)
|
||||||
|
|
||||||
|
let logs = await ethers.provider.getLogs(instanceFactory.filters.NewGovernanceProposalCreated())
|
||||||
|
const proposal = await ethers.getContractAt(
|
||||||
|
'AddInstanceProposal',
|
||||||
|
ethers.utils.getAddress('0x' + logs[0].topics[1].slice(26)),
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(await proposal.instanceFactory()).to.be.equal(instanceFactory.address)
|
||||||
|
expect(await proposal.instanceRegistry()).to.be.equal(instanceRegistry.address)
|
||||||
|
expect(await proposal.token()).to.be.equal(config.COMP)
|
||||||
|
expect(await proposal.uniswapPoolSwappingFee()).to.be.equal(3000)
|
||||||
|
expect(await proposal.numInstances()).to.be.equal(1)
|
||||||
|
expect(await proposal.protocolFeeByIndex(0)).to.be.equal(30)
|
||||||
|
expect(await proposal.denominationByIndex(0)).to.be.equal(ethers.utils.parseEther('100'))
|
||||||
|
})
|
||||||
|
|
||||||
// it('Should prepare data for instance deposit/withdraw tests', async () => {
|
// it('Should prepare data for instance deposit/withdraw tests', async () => {
|
||||||
// const RAITokenAddress = '0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919'
|
// const RAITokenAddress = '0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919'
|
||||||
// await sendr('hardhat_impersonateAccount', ['0x46a0B4Fa58141ABa23185e79f7047A7dFd0FF100'])
|
// await sendr('hardhat_impersonateAccount', ['0x46a0B4Fa58141ABa23185e79f7047A7dFd0FF100'])
|
||||||
|
Loading…
Reference in New Issue
Block a user