Add Proxies from OpenZeppelin SDK (#2335)

This commit is contained in:
Francisco Giordano
2020-08-27 21:02:42 -03:00
committed by GitHub
parent 0b489f4d79
commit cb791a1b21
17 changed files with 1618 additions and 0 deletions

View File

@ -0,0 +1,81 @@
const { contract } = require('@openzeppelin/test-environment');
const { expectRevert } = require('@openzeppelin/test-helpers');
const { assert } = require('chai');
const InitializableMock = contract.fromArtifact('InitializableMock');
const SampleChild = contract.fromArtifact('SampleChild');
describe('Initializable', function () {
describe('basic testing without inheritance', function () {
beforeEach('deploying', async function () {
this.contract = await InitializableMock.new();
});
context('before initialize', function () {
it('initializer has not run', async function () {
assert.isFalse(await this.contract.initializerRan());
});
});
context('after initialize', function () {
beforeEach('initializing', async function () {
await this.contract.initialize();
});
it('initializer has run', async function () {
assert.isTrue(await this.contract.initializerRan());
});
it('initializer does not run again', async function () {
await expectRevert(this.contract.initialize(), 'Initializable: contract is already initialized');
});
});
context('after nested initialize', function () {
beforeEach('initializing', async function () {
await this.contract.initializeNested();
});
it('initializer has run', async function () {
assert.isTrue(await this.contract.initializerRan());
});
});
});
describe('complex testing with inheritance', function () {
const mother = 12;
const gramps = '56';
const father = 34;
const child = 78;
beforeEach('deploying', async function () {
this.contract = await SampleChild.new();
});
beforeEach('initializing', async function () {
await this.contract.initialize(mother, gramps, father, child);
});
it('initializes human', async function () {
assert.equal(await this.contract.isHuman(), true);
});
it('initializes mother', async function () {
assert.equal(await this.contract.mother(), mother);
});
it('initializes gramps', async function () {
assert.equal(await this.contract.gramps(), gramps);
});
it('initializes father', async function () {
assert.equal(await this.contract.father(), father);
});
it('initializes child', async function () {
assert.equal(await this.contract.child(), child);
});
});
});

View File

@ -0,0 +1,119 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const ImplV1 = contract.fromArtifact('DummyImplementation');
const ImplV2 = contract.fromArtifact('DummyImplementationV2');
const ProxyAdmin = contract.fromArtifact('ProxyAdmin');
const TransparentUpgradeableProxy = contract.fromArtifact('TransparentUpgradeableProxy');
describe('ProxyAdmin', function () {
const [proxyAdminOwner, newAdmin, anotherAccount] = accounts;
before('set implementations', async function () {
this.implementationV1 = await ImplV1.new();
this.implementationV2 = await ImplV2.new();
});
beforeEach(async function () {
const initializeData = Buffer.from('');
this.proxyAdmin = await ProxyAdmin.new({ from: proxyAdminOwner });
this.proxy = await TransparentUpgradeableProxy.new(
this.implementationV1.address,
this.proxyAdmin.address,
initializeData,
{ from: proxyAdminOwner },
);
});
it('has an owner', async function () {
expect(await this.proxyAdmin.owner()).to.equal(proxyAdminOwner);
});
describe('#getProxyAdmin', function () {
it('returns proxyAdmin as admin of the proxy', async function () {
const admin = await this.proxyAdmin.getProxyAdmin(this.proxy.address);
expect(admin).to.be.equal(this.proxyAdmin.address);
});
});
describe('#changeProxyAdmin', function () {
it('fails to change proxy admin if its not the proxy owner', async function () {
await expectRevert(
this.proxyAdmin.changeProxyAdmin(this.proxy.address, newAdmin, { from: anotherAccount }),
'caller is not the owner',
);
});
it('changes proxy admin', async function () {
await this.proxyAdmin.changeProxyAdmin(this.proxy.address, newAdmin, { from: proxyAdminOwner });
expect(await this.proxy.admin.call({ from: newAdmin })).to.eq(newAdmin);
});
});
describe('#getProxyImplementation', function () {
it('returns proxy implementation address', async function () {
const implementationAddress = await this.proxyAdmin.getProxyImplementation(this.proxy.address);
expect(implementationAddress).to.be.equal(this.implementationV1.address);
});
});
describe('#upgrade', function () {
context('with unauthorized account', function () {
it('fails to upgrade', async function () {
await expectRevert(
this.proxyAdmin.upgrade(this.proxy.address, this.implementationV2.address, { from: anotherAccount }),
'caller is not the owner',
);
});
});
context('with authorized account', function () {
it('upgrades implementation', async function () {
await this.proxyAdmin.upgrade(this.proxy.address, this.implementationV2.address, { from: proxyAdminOwner });
const implementationAddress = await this.proxyAdmin.getProxyImplementation(this.proxy.address);
expect(implementationAddress).to.be.equal(this.implementationV2.address);
});
});
});
describe('#upgradeAndCall', function () {
context('with unauthorized account', function () {
it('fails to upgrade', async function () {
const callData = new ImplV1('').contract.methods['initializeNonPayable(uint256)'](1337).encodeABI();
await expectRevert(
this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData,
{ from: anotherAccount }
),
'caller is not the owner',
);
});
});
context('with authorized account', function () {
context('with invalid callData', function () {
it('fails to upgrade', async function () {
const callData = '0x12345678';
await expectRevert.unspecified(
this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData,
{ from: proxyAdminOwner }
),
);
});
});
context('with valid callData', function () {
it('upgrades implementation', async function () {
const callData = new ImplV1('').contract.methods['initializeNonPayable(uint256)'](1337).encodeABI();
await this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData,
{ from: proxyAdminOwner }
);
const implementationAddress = await this.proxyAdmin.getProxyImplementation(this.proxy.address);
expect(implementationAddress).to.be.equal(this.implementationV2.address);
});
});
});
});
});

View File

@ -0,0 +1,436 @@
const { contract, web3 } = require('@openzeppelin/test-environment');
const { BN, expectRevert, expectEvent, constants } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
const { toChecksumAddress, keccak256 } = require('ethereumjs-util');
const { expect } = require('chai');
const Proxy = contract.fromArtifact('Proxy');
const Implementation1 = contract.fromArtifact('Implementation1');
const Implementation2 = contract.fromArtifact('Implementation2');
const Implementation3 = contract.fromArtifact('Implementation3');
const Implementation4 = contract.fromArtifact('Implementation4');
const MigratableMockV1 = contract.fromArtifact('MigratableMockV1');
const MigratableMockV2 = contract.fromArtifact('MigratableMockV2');
const MigratableMockV3 = contract.fromArtifact('MigratableMockV3');
const InitializableMock = contract.fromArtifact('InitializableMock');
const DummyImplementation = contract.fromArtifact('DummyImplementation');
const ClashingImplementation = contract.fromArtifact('ClashingImplementation');
const IMPLEMENTATION_LABEL = 'eip1967.proxy.implementation';
const ADMIN_LABEL = 'eip1967.proxy.admin';
module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createProxy, accounts) {
const [proxyAdminAddress, proxyAdminOwner, anotherAccount] = accounts;
before(async function () {
this.implementationV0 = (await DummyImplementation.new()).address;
this.implementationV1 = (await DummyImplementation.new()).address;
});
beforeEach(async function () {
const initializeData = Buffer.from('');
this.proxy = await createProxy(this.implementationV0, proxyAdminAddress, initializeData, {
from: proxyAdminOwner,
});
this.proxyAddress = this.proxy.address;
});
describe('implementation', function () {
it('returns the current implementation address', async function () {
const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress });
expect(implementation).to.be.equal(this.implementationV0);
});
it('delegates to the implementation', async function () {
const dummy = new DummyImplementation(this.proxyAddress);
const value = await dummy.get();
expect(value).to.equal(true);
});
});
describe('upgradeTo', function () {
describe('when the sender is the admin', function () {
const from = proxyAdminAddress;
describe('when the given implementation is different from the current one', function () {
it('upgrades to the requested implementation', async function () {
await this.proxy.upgradeTo(this.implementationV1, { from });
const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress });
expect(implementation).to.be.equal(this.implementationV1);
});
it('emits an event', async function () {
expectEvent(
await this.proxy.upgradeTo(this.implementationV1, { from }),
'Upgraded', {
implementation: this.implementationV1,
},
);
});
});
describe('when the given implementation is the zero address', function () {
it('reverts', async function () {
await expectRevert(
this.proxy.upgradeTo(ZERO_ADDRESS, { from }),
'UpgradeableProxy: new implementation is not a contract',
);
});
});
});
describe('when the sender is not the admin', function () {
const from = anotherAccount;
it('reverts', async function () {
await expectRevert.unspecified(
this.proxy.upgradeTo(this.implementationV1, { from })
);
});
});
});
describe('upgradeToAndCall', function () {
describe('without migrations', function () {
beforeEach(async function () {
this.behavior = await InitializableMock.new();
});
describe('when the call does not fail', function () {
const initializeData = new InitializableMock('').contract.methods['initializeWithX(uint256)'](42).encodeABI();
describe('when the sender is the admin', function () {
const from = proxyAdminAddress;
const value = 1e5;
beforeEach(async function () {
this.receipt = await this.proxy.upgradeToAndCall(this.behavior.address, initializeData, { from, value });
});
it('upgrades to the requested implementation', async function () {
const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress });
expect(implementation).to.be.equal(this.behavior.address);
});
it('emits an event', function () {
expectEvent(this.receipt, 'Upgraded', { implementation: this.behavior.address });
});
it('calls the initializer function', async function () {
const migratable = new InitializableMock(this.proxyAddress);
const x = await migratable.x();
expect(x).to.be.bignumber.equal('42');
});
it('sends given value to the proxy', async function () {
const balance = await web3.eth.getBalance(this.proxyAddress);
expect(balance.toString()).to.be.bignumber.equal(value.toString());
});
it.skip('uses the storage of the proxy', async function () {
// storage layout should look as follows:
// - 0: Initializable storage
// - 1-50: Initailizable reserved storage (50 slots)
// - 51: initializerRan
// - 52: x
const storedValue = await Proxy.at(this.proxyAddress).getStorageAt(52);
expect(parseInt(storedValue)).to.eq(42);
});
});
describe('when the sender is not the admin', function () {
it('reverts', async function () {
await expectRevert.unspecified(
this.proxy.upgradeToAndCall(this.behavior.address, initializeData, { from: anotherAccount }),
);
});
});
});
describe('when the call does fail', function () {
const initializeData = new InitializableMock('').contract.methods.fail().encodeABI();
it('reverts', async function () {
await expectRevert.unspecified(
this.proxy.upgradeToAndCall(this.behavior.address, initializeData, { from: proxyAdminAddress }),
);
});
});
});
describe('with migrations', function () {
describe('when the sender is the admin', function () {
const from = proxyAdminAddress;
const value = 1e5;
describe('when upgrading to V1', function () {
const v1MigrationData = new MigratableMockV1('').contract.methods.initialize(42).encodeABI();
beforeEach(async function () {
this.behaviorV1 = await MigratableMockV1.new();
this.balancePreviousV1 = new BN(await web3.eth.getBalance(this.proxyAddress));
this.receipt = await this.proxy.upgradeToAndCall(this.behaviorV1.address, v1MigrationData, { from, value });
});
it('upgrades to the requested version and emits an event', async function () {
const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress });
expect(implementation).to.be.equal(this.behaviorV1.address);
expectEvent(this.receipt, 'Upgraded', { implementation: this.behaviorV1.address });
});
it('calls the \'initialize\' function and sends given value to the proxy', async function () {
const migratable = new MigratableMockV1(this.proxyAddress);
const x = await migratable.x();
expect(x).to.be.bignumber.equal('42');
const balance = await web3.eth.getBalance(this.proxyAddress);
expect(new BN(balance)).to.be.bignumber.equal(this.balancePreviousV1.addn(value));
});
describe('when upgrading to V2', function () {
const v2MigrationData = new MigratableMockV2('').contract.methods.migrate(10, 42).encodeABI();
beforeEach(async function () {
this.behaviorV2 = await MigratableMockV2.new();
this.balancePreviousV2 = new BN(await web3.eth.getBalance(this.proxyAddress));
this.receipt =
await this.proxy.upgradeToAndCall(this.behaviorV2.address, v2MigrationData, { from, value });
});
it('upgrades to the requested version and emits an event', async function () {
const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress });
expect(implementation).to.be.equal(this.behaviorV2.address);
expectEvent(this.receipt, 'Upgraded', { implementation: this.behaviorV2.address });
});
it('calls the \'migrate\' function and sends given value to the proxy', async function () {
const migratable = new MigratableMockV2(this.proxyAddress);
const x = await migratable.x();
expect(x).to.be.bignumber.equal('10');
const y = await migratable.y();
expect(y).to.be.bignumber.equal('42');
const balance = new BN(await web3.eth.getBalance(this.proxyAddress));
expect(balance).to.be.bignumber.equal(this.balancePreviousV2.addn(value));
});
describe('when upgrading to V3', function () {
const v3MigrationData = new MigratableMockV3('').contract.methods['migrate()']().encodeABI();
beforeEach(async function () {
this.behaviorV3 = await MigratableMockV3.new();
this.balancePreviousV3 = new BN(await web3.eth.getBalance(this.proxyAddress));
this.receipt =
await this.proxy.upgradeToAndCall(this.behaviorV3.address, v3MigrationData, { from, value });
});
it('upgrades to the requested version and emits an event', async function () {
const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress });
expect(implementation).to.be.equal(this.behaviorV3.address);
expectEvent(this.receipt, 'Upgraded', { implementation: this.behaviorV3.address });
});
it('calls the \'migrate\' function and sends given value to the proxy', async function () {
const migratable = new MigratableMockV3(this.proxyAddress);
const x = await migratable.x();
expect(x).to.be.bignumber.equal('42');
const y = await migratable.y();
expect(y).to.be.bignumber.equal('10');
const balance = new BN(await web3.eth.getBalance(this.proxyAddress));
expect(balance).to.be.bignumber.equal(this.balancePreviousV3.addn(value));
});
});
});
});
});
describe('when the sender is not the admin', function () {
const from = anotherAccount;
it('reverts', async function () {
const behaviorV1 = await MigratableMockV1.new();
const v1MigrationData = new MigratableMockV1('').contract.methods.initialize(42).encodeABI();
await expectRevert.unspecified(
this.proxy.upgradeToAndCall(behaviorV1.address, v1MigrationData, { from }),
);
});
});
});
});
describe('changeAdmin', function () {
describe('when the new proposed admin is not the zero address', function () {
const newAdmin = anotherAccount;
describe('when the sender is the admin', function () {
beforeEach('transferring', async function () {
this.receipt = await this.proxy.changeAdmin(newAdmin, { from: proxyAdminAddress });
});
it('assigns new proxy admin', async function () {
const newProxyAdmin = await this.proxy.admin.call({ from: newAdmin });
expect(newProxyAdmin).to.be.equal(anotherAccount);
});
it('emits an event', function () {
expectEvent(this.receipt, 'AdminChanged', {
previousAdmin: proxyAdminAddress,
newAdmin: newAdmin,
});
});
});
describe('when the sender is not the admin', function () {
it('reverts', async function () {
await expectRevert.unspecified(this.proxy.changeAdmin(newAdmin, { from: anotherAccount }));
});
});
});
describe('when the new proposed admin is the zero address', function () {
it('reverts', async function () {
await expectRevert(
this.proxy.changeAdmin(ZERO_ADDRESS, { from: proxyAdminAddress }),
'TransparentUpgradeableProxy: new admin is the zero address',
);
});
});
});
describe('storage', function () {
it('should store the implementation address in specified location', async function () {
const slot = '0x' + new BN(keccak256(Buffer.from(IMPLEMENTATION_LABEL))).subn(1).toString(16);
const implementation = toChecksumAddress(await web3.eth.getStorageAt(this.proxyAddress, slot));
expect(implementation).to.be.equal(this.implementationV0);
});
it('should store the admin proxy in specified location', async function () {
const slot = '0x' + new BN(keccak256(Buffer.from(ADMIN_LABEL))).subn(1).toString(16);
const proxyAdmin = toChecksumAddress(await web3.eth.getStorageAt(this.proxyAddress, slot));
expect(proxyAdmin).to.be.equal(proxyAdminAddress);
});
});
describe('transparent proxy', function () {
beforeEach('creating proxy', async function () {
const initializeData = Buffer.from('');
this.impl = await ClashingImplementation.new();
this.proxy = await createProxy(this.impl.address, proxyAdminAddress, initializeData, { from: proxyAdminOwner });
this.clashing = new ClashingImplementation(this.proxy.address);
});
it('proxy admin cannot call delegated functions', async function () {
await expectRevert(
this.clashing.delegatedFunction({ from: proxyAdminAddress }),
'TransparentUpgradeableProxy: admin cannot fallback to proxy target',
);
});
context('when function names clash', function () {
it('when sender is proxy admin should run the proxy function', async function () {
const value = await this.proxy.admin.call({ from: proxyAdminAddress });
expect(value).to.be.equal(proxyAdminAddress);
});
it('when sender is other should delegate to implementation', async function () {
const value = await this.proxy.admin.call({ from: anotherAccount });
expect(value).to.be.equal('0x0000000000000000000000000000000011111142');
});
});
});
describe('regression', () => {
const initializeData = Buffer.from('');
it('should add new function', async () => {
const instance1 = await Implementation1.new();
const proxy = await createProxy(instance1.address, proxyAdminAddress, initializeData, { from: proxyAdminOwner });
const proxyInstance1 = new Implementation1(proxy.address);
await proxyInstance1.setValue(42);
const instance2 = await Implementation2.new();
await proxy.upgradeTo(instance2.address, { from: proxyAdminAddress });
const proxyInstance2 = new Implementation2(proxy.address);
const res = await proxyInstance2.getValue();
expect(res.toString()).to.eq('42');
});
it('should remove function', async () => {
const instance2 = await Implementation2.new();
const proxy = await createProxy(instance2.address, proxyAdminAddress, initializeData, { from: proxyAdminOwner });
const proxyInstance2 = new Implementation2(proxy.address);
await proxyInstance2.setValue(42);
const res = await proxyInstance2.getValue();
expect(res.toString()).to.eq('42');
const instance1 = await Implementation1.new();
await proxy.upgradeTo(instance1.address, { from: proxyAdminAddress });
const proxyInstance1 = new Implementation2(proxy.address);
await expectRevert.unspecified(proxyInstance1.getValue());
});
it('should change function signature', async () => {
const instance1 = await Implementation1.new();
const proxy = await createProxy(instance1.address, proxyAdminAddress, initializeData, { from: proxyAdminOwner });
const proxyInstance1 = new Implementation1(proxy.address);
await proxyInstance1.setValue(42);
const instance3 = await Implementation3.new();
await proxy.upgradeTo(instance3.address, { from: proxyAdminAddress });
const proxyInstance3 = new Implementation3(proxy.address);
const res = await proxyInstance3.getValue(8);
expect(res.toString()).to.eq('50');
});
it('should add fallback function', async () => {
const initializeData = Buffer.from('');
const instance1 = await Implementation1.new();
const proxy = await createProxy(instance1.address, proxyAdminAddress, initializeData, { from: proxyAdminOwner });
const instance4 = await Implementation4.new();
await proxy.upgradeTo(instance4.address, { from: proxyAdminAddress });
const proxyInstance4 = new Implementation4(proxy.address);
const data = '';
await web3.eth.sendTransaction({ to: proxy.address, from: anotherAccount, data });
const res = await proxyInstance4.getValue();
expect(res.toString()).to.eq('1');
});
it('should remove fallback function', async () => {
const instance4 = await Implementation4.new();
const proxy = await createProxy(instance4.address, proxyAdminAddress, initializeData, { from: proxyAdminOwner });
const instance2 = await Implementation2.new();
await proxy.upgradeTo(instance2.address, { from: proxyAdminAddress });
const data = '';
await expectRevert.unspecified(
web3.eth.sendTransaction({ to: proxy.address, from: anotherAccount, data }),
);
const proxyInstance2 = new Implementation2(proxy.address);
const res = await proxyInstance2.getValue();
expect(res.toString()).to.eq('0');
});
});
};

View File

@ -0,0 +1,17 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const shouldBehaveLikeUpgradeableProxy = require('./UpgradeableProxy.behaviour');
const shouldBehaveLikeTransparentUpgradeableProxy = require('./TransparentUpgradeableProxy.behaviour');
const TransparentUpgradeableProxy = contract.fromArtifact('TransparentUpgradeableProxy');
describe('TransparentUpgradeableProxy', function () {
const [proxyAdminAddress, proxyAdminOwner] = accounts;
const createProxy = async function (logic, admin, initData, opts) {
return TransparentUpgradeableProxy.new(logic, admin, initData, opts);
};
shouldBehaveLikeUpgradeableProxy(createProxy, proxyAdminAddress, proxyAdminOwner);
shouldBehaveLikeTransparentUpgradeableProxy(createProxy, accounts);
});

View File

@ -0,0 +1,216 @@
const { contract, web3 } = require('@openzeppelin/test-environment');
const { BN, expectRevert } = require('@openzeppelin/test-helpers');
const { toChecksumAddress, keccak256 } = require('ethereumjs-util');
const { expect } = require('chai');
const DummyImplementation = contract.fromArtifact('DummyImplementation');
const IMPLEMENTATION_LABEL = 'eip1967.proxy.implementation';
module.exports = function shouldBehaveLikeUpgradeableProxy (createProxy, proxyAdminAddress, proxyCreator) {
it('cannot be initialized with a non-contract address', async function () {
const nonContractAddress = proxyCreator;
const initializeData = Buffer.from('');
await expectRevert.unspecified(
createProxy(nonContractAddress, proxyAdminAddress, initializeData, {
from: proxyCreator,
}),
);
});
before('deploy implementation', async function () {
this.implementation = web3.utils.toChecksumAddress((await DummyImplementation.new()).address);
});
const assertProxyInitialization = function ({ value, balance }) {
it('sets the implementation address', async function () {
const slot = '0x' + new BN(keccak256(Buffer.from(IMPLEMENTATION_LABEL))).subn(1).toString(16);
const implementation = toChecksumAddress(await web3.eth.getStorageAt(this.proxy, slot));
expect(implementation).to.be.equal(this.implementation);
});
it('initializes the proxy', async function () {
const dummy = new DummyImplementation(this.proxy);
expect(await dummy.value()).to.be.bignumber.equal(value.toString());
});
it('has expected balance', async function () {
expect(await web3.eth.getBalance(this.proxy)).to.be.bignumber.equal(balance.toString());
});
};
describe('without initialization', function () {
const initializeData = Buffer.from('');
describe('when not sending balance', function () {
beforeEach('creating proxy', async function () {
this.proxy = (
await createProxy(this.implementation, proxyAdminAddress, initializeData, {
from: proxyCreator,
})
).address;
});
assertProxyInitialization({ value: 0, balance: 0 });
});
describe('when sending some balance', function () {
const value = 10e5;
beforeEach('creating proxy', async function () {
this.proxy = (
await createProxy(this.implementation, proxyAdminAddress, initializeData, {
from: proxyCreator,
value,
})
).address;
});
assertProxyInitialization({ value: 0, balance: value });
});
});
describe('initialization without parameters', function () {
describe('non payable', function () {
const expectedInitializedValue = 10;
const initializeData = new DummyImplementation('').contract.methods['initializeNonPayable()']().encodeABI();
describe('when not sending balance', function () {
beforeEach('creating proxy', async function () {
this.proxy = (
await createProxy(this.implementation, proxyAdminAddress, initializeData, {
from: proxyCreator,
})
).address;
});
assertProxyInitialization({
value: expectedInitializedValue,
balance: 0,
});
});
describe('when sending some balance', function () {
const value = 10e5;
it('reverts', async function () {
await expectRevert.unspecified(
createProxy(this.implementation, proxyAdminAddress, initializeData, { from: proxyCreator, value }),
);
});
});
});
describe('payable', function () {
const expectedInitializedValue = 100;
const initializeData = new DummyImplementation('').contract.methods['initializePayable()']().encodeABI();
describe('when not sending balance', function () {
beforeEach('creating proxy', async function () {
this.proxy = (
await createProxy(this.implementation, proxyAdminAddress, initializeData, {
from: proxyCreator,
})
).address;
});
assertProxyInitialization({
value: expectedInitializedValue,
balance: 0,
});
});
describe('when sending some balance', function () {
const value = 10e5;
beforeEach('creating proxy', async function () {
this.proxy = (
await createProxy(this.implementation, proxyAdminAddress, initializeData, {
from: proxyCreator,
value,
})
).address;
});
assertProxyInitialization({
value: expectedInitializedValue,
balance: value,
});
});
});
});
describe('initialization with parameters', function () {
describe('non payable', function () {
const expectedInitializedValue = 10;
const initializeData = new DummyImplementation('').contract
.methods['initializeNonPayable(uint256)'](expectedInitializedValue).encodeABI();
describe('when not sending balance', function () {
beforeEach('creating proxy', async function () {
this.proxy = (
await createProxy(this.implementation, proxyAdminAddress, initializeData, {
from: proxyCreator,
})
).address;
});
assertProxyInitialization({
value: expectedInitializedValue,
balance: 0,
});
});
describe('when sending some balance', function () {
const value = 10e5;
it('reverts', async function () {
await expectRevert.unspecified(
createProxy(this.implementation, proxyAdminAddress, initializeData, { from: proxyCreator, value }),
);
});
});
});
describe('payable', function () {
const expectedInitializedValue = 42;
const initializeData = new DummyImplementation('').contract
.methods['initializePayable(uint256)'](expectedInitializedValue).encodeABI();
describe('when not sending balance', function () {
beforeEach('creating proxy', async function () {
this.proxy = (
await createProxy(this.implementation, proxyAdminAddress, initializeData, {
from: proxyCreator,
})
).address;
});
assertProxyInitialization({
value: expectedInitializedValue,
balance: 0,
});
});
describe('when sending some balance', function () {
const value = 10e5;
beforeEach('creating proxy', async function () {
this.proxy = (
await createProxy(this.implementation, proxyAdminAddress, initializeData, {
from: proxyCreator,
value,
})
).address;
});
assertProxyInitialization({
value: expectedInitializedValue,
balance: value,
});
});
});
});
};

View File

@ -0,0 +1,15 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const shouldBehaveLikeUpgradeableProxy = require('./UpgradeableProxy.behaviour');
const UpgradeableProxy = contract.fromArtifact('UpgradeableProxy');
describe('UpgradeableProxy', function () {
const [proxyAdminOwner] = accounts;
const createProxy = async function (implementation, _admin, initData, opts) {
return UpgradeableProxy.new(implementation, initData, opts);
};
shouldBehaveLikeUpgradeableProxy(createProxy, undefined, proxyAdminOwner);
});