Skip to content

Commit

Permalink
test: add tests for proxy factory
Browse files Browse the repository at this point in the history
  • Loading branch information
heueristik committed Oct 23, 2023
1 parent d52643d commit f473fd4
Show file tree
Hide file tree
Showing 2 changed files with 200 additions and 0 deletions.
182 changes: 182 additions & 0 deletions contracts/test/utils/proxy/proxy-factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import {
ProxyFactory,
ProxyFactory__factory,
TestGovernanceERC20,
TestGovernanceERC20__factory,
} from '../../../typechain';
import {ProxyCreatedEvent} from '../../../typechain/src/utils/ProxyFactory';
import {findEvent} from '../../../utils/helpers';
import {
ERC1967_IMPLEMENTATION_SLOT,
OZ_INITIALIZED_SLOT_POSITION,
readStorage,
} from '../../../utils/storage';
import {SignerWithAddress} from '@nomiclabs/hardhat-ethers/signers';
import {expect} from 'chai';
import {ethers} from 'hardhat';

const MOCK_ADDRESS = `0x${'0123456789'.repeat(4)}`;
const DEFAULT_INITIALIZATION: [
string,
string,
string,
{receivers: string[]; amounts: number[]}
] = [
MOCK_ADDRESS,
'GOV',
'GOV',
{
receivers: [],
amounts: [],
},
];

describe.only('ProxyFactory', function () {
let deployer: SignerWithAddress;
let proxyFactory: ProxyFactory;
let logic: TestGovernanceERC20;

before(async () => {
deployer = (await ethers.getSigners())[0];

logic = await new TestGovernanceERC20__factory(deployer).deploy(
...DEFAULT_INITIALIZATION
);
proxyFactory = await new ProxyFactory__factory(deployer).deploy(
logic.address
);
});

describe('initialization', async () => {
it('points to the right logic contract', async () => {
expect(await proxyFactory.LOGIC()).to.equal(logic.address);
});
});

describe('deployUUPSProxy', async () => {
it('deploys the proxy unitialized if no data is provided', async () => {
const initData: any = [];

const tx = await proxyFactory.deployUUPSProxy(initData);
const event = await findEvent<ProxyCreatedEvent>(tx, 'ProxyCreated');
if (!event) {
throw new Error('Failed to get the event');
}

const tokenProxy = TestGovernanceERC20__factory.connect(
event.args.proxy,
deployer
);

// Check that the proxy points to the right logic contract.
expect(
await readStorage(tokenProxy.address, ERC1967_IMPLEMENTATION_SLOT, [
'address',
])
).to.equal(logic.address);

// Check that the proxy is not initialized.
expect(
await readStorage(tokenProxy.address, OZ_INITIALIZED_SLOT_POSITION, [
'uint8',
])
).to.equal(0);

// Check that the DAO address is not set.
expect(await tokenProxy.dao()).to.equal(ethers.constants.AddressZero);
});

it('deploys the proxy initialized if data is provided', async () => {
const initData =
TestGovernanceERC20__factory.createInterface().encodeFunctionData(
'initialize',
DEFAULT_INITIALIZATION
);

const tx = await proxyFactory.deployUUPSProxy(initData);
const event = await findEvent<ProxyCreatedEvent>(tx, 'ProxyCreated');
if (!event) {
throw new Error('Failed to get the event');
}

const tokenProxy = TestGovernanceERC20__factory.connect(
event.args.proxy,
deployer
);

// Check that the proxy points to the right logic contract.
expect(
await readStorage(tokenProxy.address, ERC1967_IMPLEMENTATION_SLOT, [
'address',
])
).to.equal(logic.address);

// Check that the proxy is initialized.
expect(
await readStorage(tokenProxy.address, OZ_INITIALIZED_SLOT_POSITION, [
'uint8',
])
).to.equal(1);

// Check that the DAO address is set.
expect(await tokenProxy.dao()).to.equal(MOCK_ADDRESS);
});
});

describe('deployMinimalProxy', async () => {
it('deploys the proxy unitialized if no data is provided', async () => {
const initData: any = [];

const tx = await proxyFactory.deployMinimalProxy(initData);

const event = await findEvent<ProxyCreatedEvent>(tx, 'ProxyCreated');
if (!event) {
throw new Error('Failed to get the event');
}

const tokenProxy = TestGovernanceERC20__factory.connect(
event.args.proxy,
deployer
);

// Check that the proxy is not initialized.
expect(
await readStorage(tokenProxy.address, OZ_INITIALIZED_SLOT_POSITION, [
'uint8',
])
).to.equal(0);

// Check that the DAO address is not set.
expect(await tokenProxy.dao()).to.equal(ethers.constants.AddressZero);
});

it('deploys the proxy initialized if data is provided', async () => {
const initData =
TestGovernanceERC20__factory.createInterface().encodeFunctionData(
'initialize',
DEFAULT_INITIALIZATION
);

const tx = await proxyFactory.deployMinimalProxy(initData);
const event = await findEvent<ProxyCreatedEvent>(tx, 'ProxyCreated');
if (!event) {
throw new Error('Failed to get the event');
}

const tokenProxy = TestGovernanceERC20__factory.connect(
event.args.proxy,
deployer
);

// Check that the proxy is initialized.
expect(
await readStorage(tokenProxy.address, OZ_INITIALIZED_SLOT_POSITION, [
'uint8',
])
).to.equal(1);

// Check that the DAO address is set.
expect(await tokenProxy.dao()).to.equal(MOCK_ADDRESS);
});
});
});
18 changes: 18 additions & 0 deletions contracts/utils/storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {defaultAbiCoder} from 'ethers/lib/utils';
import {ethers} from 'hardhat';

// See https://eips.ethereum.org/EIPS/eip-1967
export const ERC1967_IMPLEMENTATION_SLOT =
'0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc'; // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)

export const OZ_INITIALIZED_SLOT_POSITION = 0;

export async function readStorage(
contractAddress: string,
location: number | string,
types: string[]
): Promise<string> {
return await ethers.provider
.getStorageAt(contractAddress, location)
.then(encoded => defaultAbiCoder.decode(types, encoded)[0]);
}

0 comments on commit f473fd4

Please sign in to comment.