BeaconProxy.test.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. const { expectRevert } = require('@openzeppelin/test-helpers');
  2. const { getSlot, BeaconSlot } = require('../../helpers/erc1967');
  3. const { expectRevertCustomError } = require('../../helpers/customError');
  4. const { expect } = require('chai');
  5. const UpgradeableBeacon = artifacts.require('UpgradeableBeacon');
  6. const BeaconProxy = artifacts.require('BeaconProxy');
  7. const DummyImplementation = artifacts.require('DummyImplementation');
  8. const DummyImplementationV2 = artifacts.require('DummyImplementationV2');
  9. const BadBeaconNoImpl = artifacts.require('BadBeaconNoImpl');
  10. const BadBeaconNotContract = artifacts.require('BadBeaconNotContract');
  11. contract('BeaconProxy', function (accounts) {
  12. const [upgradeableBeaconAdmin, anotherAccount] = accounts;
  13. describe('bad beacon is not accepted', async function () {
  14. it('non-contract beacon', async function () {
  15. await expectRevertCustomError(BeaconProxy.new(anotherAccount, '0x'), 'ERC1967InvalidBeacon', [anotherAccount]);
  16. });
  17. it('non-compliant beacon', async function () {
  18. const beacon = await BadBeaconNoImpl.new();
  19. await expectRevert.unspecified(BeaconProxy.new(beacon.address, '0x'));
  20. });
  21. it('non-contract implementation', async function () {
  22. const beacon = await BadBeaconNotContract.new();
  23. const implementation = await beacon.implementation();
  24. await expectRevertCustomError(BeaconProxy.new(beacon.address, '0x'), 'ERC1967InvalidImplementation', [
  25. implementation,
  26. ]);
  27. });
  28. });
  29. before('deploy implementation', async function () {
  30. this.implementationV0 = await DummyImplementation.new();
  31. this.implementationV1 = await DummyImplementationV2.new();
  32. });
  33. describe('initialization', function () {
  34. before(function () {
  35. this.assertInitialized = async ({ value, balance }) => {
  36. const beaconSlot = await getSlot(this.proxy, BeaconSlot);
  37. const beaconAddress = web3.utils.toChecksumAddress(beaconSlot.substr(-40));
  38. expect(beaconAddress).to.equal(this.beacon.address);
  39. const dummy = new DummyImplementation(this.proxy.address);
  40. expect(await dummy.value()).to.bignumber.eq(value);
  41. expect(await web3.eth.getBalance(this.proxy.address)).to.bignumber.eq(balance);
  42. };
  43. });
  44. beforeEach('deploy beacon', async function () {
  45. this.beacon = await UpgradeableBeacon.new(this.implementationV0.address, upgradeableBeaconAdmin);
  46. });
  47. it('no initialization', async function () {
  48. const data = Buffer.from('');
  49. this.proxy = await BeaconProxy.new(this.beacon.address, data);
  50. await this.assertInitialized({ value: '0', balance: '0' });
  51. });
  52. it('non-payable initialization', async function () {
  53. const value = '55';
  54. const data = this.implementationV0.contract.methods.initializeNonPayableWithValue(value).encodeABI();
  55. this.proxy = await BeaconProxy.new(this.beacon.address, data);
  56. await this.assertInitialized({ value, balance: '0' });
  57. });
  58. it('payable initialization', async function () {
  59. const value = '55';
  60. const data = this.implementationV0.contract.methods.initializePayableWithValue(value).encodeABI();
  61. const balance = '100';
  62. this.proxy = await BeaconProxy.new(this.beacon.address, data, { value: balance });
  63. await this.assertInitialized({ value, balance });
  64. });
  65. it('reverting initialization due to value', async function () {
  66. const data = Buffer.from('');
  67. await expectRevertCustomError(
  68. BeaconProxy.new(this.beacon.address, data, { value: '1' }),
  69. 'ERC1967NonPayable',
  70. [],
  71. );
  72. });
  73. it('reverting initialization function', async function () {
  74. const data = this.implementationV0.contract.methods.reverts().encodeABI();
  75. await expectRevert(BeaconProxy.new(this.beacon.address, data), 'DummyImplementation reverted');
  76. });
  77. });
  78. it('upgrade a proxy by upgrading its beacon', async function () {
  79. const beacon = await UpgradeableBeacon.new(this.implementationV0.address, upgradeableBeaconAdmin);
  80. const value = '10';
  81. const data = this.implementationV0.contract.methods.initializeNonPayableWithValue(value).encodeABI();
  82. const proxy = await BeaconProxy.new(beacon.address, data);
  83. const dummy = new DummyImplementation(proxy.address);
  84. // test initial values
  85. expect(await dummy.value()).to.bignumber.eq(value);
  86. // test initial version
  87. expect(await dummy.version()).to.eq('V1');
  88. // upgrade beacon
  89. await beacon.upgradeTo(this.implementationV1.address, { from: upgradeableBeaconAdmin });
  90. // test upgraded version
  91. expect(await dummy.version()).to.eq('V2');
  92. });
  93. it('upgrade 2 proxies by upgrading shared beacon', async function () {
  94. const value1 = '10';
  95. const value2 = '42';
  96. const beacon = await UpgradeableBeacon.new(this.implementationV0.address, upgradeableBeaconAdmin);
  97. const proxy1InitializeData = this.implementationV0.contract.methods
  98. .initializeNonPayableWithValue(value1)
  99. .encodeABI();
  100. const proxy1 = await BeaconProxy.new(beacon.address, proxy1InitializeData);
  101. const proxy2InitializeData = this.implementationV0.contract.methods
  102. .initializeNonPayableWithValue(value2)
  103. .encodeABI();
  104. const proxy2 = await BeaconProxy.new(beacon.address, proxy2InitializeData);
  105. const dummy1 = new DummyImplementation(proxy1.address);
  106. const dummy2 = new DummyImplementation(proxy2.address);
  107. // test initial values
  108. expect(await dummy1.value()).to.bignumber.eq(value1);
  109. expect(await dummy2.value()).to.bignumber.eq(value2);
  110. // test initial version
  111. expect(await dummy1.version()).to.eq('V1');
  112. expect(await dummy2.version()).to.eq('V1');
  113. // upgrade beacon
  114. await beacon.upgradeTo(this.implementationV1.address, { from: upgradeableBeaconAdmin });
  115. // test upgraded version
  116. expect(await dummy1.version()).to.eq('V2');
  117. expect(await dummy2.version()).to.eq('V2');
  118. });
  119. });