123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- const { BN, expectRevert } = require('@openzeppelin/test-helpers');
- const ethereumjsUtil = require('ethereumjs-util');
- const { keccak256 } = ethereumjsUtil;
- const { expect } = require('chai');
- const UpgradeableBeacon = artifacts.require('UpgradeableBeacon');
- const BeaconProxy = artifacts.require('BeaconProxy');
- const DummyImplementation = artifacts.require('DummyImplementation');
- const DummyImplementationV2 = artifacts.require('DummyImplementationV2');
- const BadBeaconNoImpl = artifacts.require('BadBeaconNoImpl');
- const BadBeaconNotContract = artifacts.require('BadBeaconNotContract');
- function toChecksumAddress (address) {
- return ethereumjsUtil.toChecksumAddress('0x' + address.replace(/^0x/, '').padStart(40, '0').substr(-40));
- }
- const BEACON_LABEL = 'eip1967.proxy.beacon';
- const BEACON_SLOT = '0x' + new BN(keccak256(Buffer.from(BEACON_LABEL))).subn(1).toString(16);
- contract('BeaconProxy', function (accounts) {
- const [anotherAccount] = accounts;
- describe('bad beacon is not accepted', async function () {
- it('non-contract beacon', async function () {
- await expectRevert(
- BeaconProxy.new(anotherAccount, '0x'),
- 'BeaconProxy: beacon is not a contract',
- );
- });
- it('non-compliant beacon', async function () {
- const beacon = await BadBeaconNoImpl.new();
- await expectRevert.unspecified(
- BeaconProxy.new(beacon.address, '0x'),
- );
- });
- it('non-contract implementation', async function () {
- const beacon = await BadBeaconNotContract.new();
- await expectRevert(
- BeaconProxy.new(beacon.address, '0x'),
- 'BeaconProxy: beacon implementation is not a contract',
- );
- });
- });
- before('deploy implementation', async function () {
- this.implementationV0 = await DummyImplementation.new();
- this.implementationV1 = await DummyImplementationV2.new();
- });
- describe('initialization', function () {
- before(function () {
- this.assertInitialized = async ({ value, balance }) => {
- const beaconAddress = toChecksumAddress(await web3.eth.getStorageAt(this.proxy.address, BEACON_SLOT));
- expect(beaconAddress).to.equal(this.beacon.address);
- const dummy = new DummyImplementation(this.proxy.address);
- expect(await dummy.value()).to.bignumber.eq(value);
- expect(await web3.eth.getBalance(this.proxy.address)).to.bignumber.eq(balance);
- };
- });
- beforeEach('deploy beacon', async function () {
- this.beacon = await UpgradeableBeacon.new(this.implementationV0.address);
- });
- it('no initialization', async function () {
- const data = Buffer.from('');
- const balance = '10';
- this.proxy = await BeaconProxy.new(this.beacon.address, data, { value: balance });
- await this.assertInitialized({ value: '0', balance });
- });
- it('non-payable initialization', async function () {
- const value = '55';
- const data = this.implementationV0.contract.methods
- .initializeNonPayableWithValue(value)
- .encodeABI();
- this.proxy = await BeaconProxy.new(this.beacon.address, data);
- await this.assertInitialized({ value, balance: '0' });
- });
- it('payable initialization', async function () {
- const value = '55';
- const data = this.implementationV0.contract.methods
- .initializePayableWithValue(value)
- .encodeABI();
- const balance = '100';
- this.proxy = await BeaconProxy.new(this.beacon.address, data, { value: balance });
- await this.assertInitialized({ value, balance });
- });
- it('reverting initialization', async function () {
- const data = this.implementationV0.contract.methods.reverts().encodeABI();
- await expectRevert(
- BeaconProxy.new(this.beacon.address, data),
- 'DummyImplementation reverted',
- );
- });
- });
- it('upgrade a proxy by upgrading its beacon', async function () {
- const beacon = await UpgradeableBeacon.new(this.implementationV0.address);
- const value = '10';
- const data = this.implementationV0.contract.methods
- .initializeNonPayableWithValue(value)
- .encodeABI();
- const proxy = await BeaconProxy.new(beacon.address, data);
- const dummy = new DummyImplementation(proxy.address);
- // test initial values
- expect(await dummy.value()).to.bignumber.eq(value);
- // test initial version
- expect(await dummy.version()).to.eq('V1');
- // upgrade beacon
- await beacon.upgradeTo(this.implementationV1.address);
- // test upgraded version
- expect(await dummy.version()).to.eq('V2');
- });
- it('upgrade 2 proxies by upgrading shared beacon', async function () {
- const value1 = '10';
- const value2 = '42';
- const beacon = await UpgradeableBeacon.new(this.implementationV0.address);
- const proxy1InitializeData = this.implementationV0.contract.methods
- .initializeNonPayableWithValue(value1)
- .encodeABI();
- const proxy1 = await BeaconProxy.new(beacon.address, proxy1InitializeData);
- const proxy2InitializeData = this.implementationV0.contract.methods
- .initializeNonPayableWithValue(value2)
- .encodeABI();
- const proxy2 = await BeaconProxy.new(beacon.address, proxy2InitializeData);
- const dummy1 = new DummyImplementation(proxy1.address);
- const dummy2 = new DummyImplementation(proxy2.address);
- // test initial values
- expect(await dummy1.value()).to.bignumber.eq(value1);
- expect(await dummy2.value()).to.bignumber.eq(value2);
- // test initial version
- expect(await dummy1.version()).to.eq('V1');
- expect(await dummy2.version()).to.eq('V1');
- // upgrade beacon
- await beacon.upgradeTo(this.implementationV1.address);
- // test upgraded version
- expect(await dummy1.version()).to.eq('V2');
- expect(await dummy2.version()).to.eq('V2');
- });
- });
|