BeaconProxy.test.js 5.7 KB

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