UpgradeableProxy.behaviour.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. const { BN, expectRevert } = require('@openzeppelin/test-helpers');
  2. const ethereumjsUtil = require('ethereumjs-util');
  3. const { expect } = require('chai');
  4. const DummyImplementation = artifacts.require('DummyImplementation');
  5. const IMPLEMENTATION_LABEL = 'eip1967.proxy.implementation';
  6. function toChecksumAddress (address) {
  7. return ethereumjsUtil.toChecksumAddress('0x' + address.replace(/^0x/, '').padStart(40, '0'));
  8. }
  9. module.exports = function shouldBehaveLikeUpgradeableProxy (createProxy, proxyAdminAddress, proxyCreator) {
  10. it('cannot be initialized with a non-contract address', async function () {
  11. const nonContractAddress = proxyCreator;
  12. const initializeData = Buffer.from('');
  13. await expectRevert.unspecified(
  14. createProxy(nonContractAddress, proxyAdminAddress, initializeData, {
  15. from: proxyCreator,
  16. }),
  17. );
  18. });
  19. before('deploy implementation', async function () {
  20. this.implementation = web3.utils.toChecksumAddress((await DummyImplementation.new()).address);
  21. });
  22. const assertProxyInitialization = function ({ value, balance }) {
  23. it('sets the implementation address', async function () {
  24. const slot = '0x' + new BN(ethereumjsUtil.keccak256(Buffer.from(IMPLEMENTATION_LABEL))).subn(1).toString(16);
  25. const implementation = toChecksumAddress(await web3.eth.getStorageAt(this.proxy, slot));
  26. expect(implementation).to.be.equal(this.implementation);
  27. });
  28. it('initializes the proxy', async function () {
  29. const dummy = new DummyImplementation(this.proxy);
  30. expect(await dummy.value()).to.be.bignumber.equal(value.toString());
  31. });
  32. it('has expected balance', async function () {
  33. expect(await web3.eth.getBalance(this.proxy)).to.be.bignumber.equal(balance.toString());
  34. });
  35. };
  36. describe('without initialization', function () {
  37. const initializeData = Buffer.from('');
  38. describe('when not sending balance', function () {
  39. beforeEach('creating proxy', async function () {
  40. this.proxy = (
  41. await createProxy(this.implementation, proxyAdminAddress, initializeData, {
  42. from: proxyCreator,
  43. })
  44. ).address;
  45. });
  46. assertProxyInitialization({ value: 0, balance: 0 });
  47. });
  48. describe('when sending some balance', function () {
  49. const value = 10e5;
  50. beforeEach('creating proxy', async function () {
  51. this.proxy = (
  52. await createProxy(this.implementation, proxyAdminAddress, initializeData, {
  53. from: proxyCreator,
  54. value,
  55. })
  56. ).address;
  57. });
  58. assertProxyInitialization({ value: 0, balance: value });
  59. });
  60. });
  61. describe('initialization without parameters', function () {
  62. describe('non payable', function () {
  63. const expectedInitializedValue = 10;
  64. const initializeData = new DummyImplementation('').contract.methods['initializeNonPayable()']().encodeABI();
  65. describe('when not sending balance', function () {
  66. beforeEach('creating proxy', async function () {
  67. this.proxy = (
  68. await createProxy(this.implementation, proxyAdminAddress, initializeData, {
  69. from: proxyCreator,
  70. })
  71. ).address;
  72. });
  73. assertProxyInitialization({
  74. value: expectedInitializedValue,
  75. balance: 0,
  76. });
  77. });
  78. describe('when sending some balance', function () {
  79. const value = 10e5;
  80. it('reverts', async function () {
  81. await expectRevert.unspecified(
  82. createProxy(this.implementation, proxyAdminAddress, initializeData, { from: proxyCreator, value }),
  83. );
  84. });
  85. });
  86. });
  87. describe('payable', function () {
  88. const expectedInitializedValue = 100;
  89. const initializeData = new DummyImplementation('').contract.methods['initializePayable()']().encodeABI();
  90. describe('when not sending balance', function () {
  91. beforeEach('creating proxy', async function () {
  92. this.proxy = (
  93. await createProxy(this.implementation, proxyAdminAddress, initializeData, {
  94. from: proxyCreator,
  95. })
  96. ).address;
  97. });
  98. assertProxyInitialization({
  99. value: expectedInitializedValue,
  100. balance: 0,
  101. });
  102. });
  103. describe('when sending some balance', function () {
  104. const value = 10e5;
  105. beforeEach('creating proxy', async function () {
  106. this.proxy = (
  107. await createProxy(this.implementation, proxyAdminAddress, initializeData, {
  108. from: proxyCreator,
  109. value,
  110. })
  111. ).address;
  112. });
  113. assertProxyInitialization({
  114. value: expectedInitializedValue,
  115. balance: value,
  116. });
  117. });
  118. });
  119. });
  120. describe('initialization with parameters', function () {
  121. describe('non payable', function () {
  122. const expectedInitializedValue = 10;
  123. const initializeData = new DummyImplementation('').contract
  124. .methods.initializeNonPayableWithValue(expectedInitializedValue).encodeABI();
  125. describe('when not sending balance', function () {
  126. beforeEach('creating proxy', async function () {
  127. this.proxy = (
  128. await createProxy(this.implementation, proxyAdminAddress, initializeData, {
  129. from: proxyCreator,
  130. })
  131. ).address;
  132. });
  133. assertProxyInitialization({
  134. value: expectedInitializedValue,
  135. balance: 0,
  136. });
  137. });
  138. describe('when sending some balance', function () {
  139. const value = 10e5;
  140. it('reverts', async function () {
  141. await expectRevert.unspecified(
  142. createProxy(this.implementation, proxyAdminAddress, initializeData, { from: proxyCreator, value }),
  143. );
  144. });
  145. });
  146. });
  147. describe('payable', function () {
  148. const expectedInitializedValue = 42;
  149. const initializeData = new DummyImplementation('').contract
  150. .methods.initializePayableWithValue(expectedInitializedValue).encodeABI();
  151. describe('when not sending balance', function () {
  152. beforeEach('creating proxy', async function () {
  153. this.proxy = (
  154. await createProxy(this.implementation, proxyAdminAddress, initializeData, {
  155. from: proxyCreator,
  156. })
  157. ).address;
  158. });
  159. assertProxyInitialization({
  160. value: expectedInitializedValue,
  161. balance: 0,
  162. });
  163. });
  164. describe('when sending some balance', function () {
  165. const value = 10e5;
  166. beforeEach('creating proxy', async function () {
  167. this.proxy = (
  168. await createProxy(this.implementation, proxyAdminAddress, initializeData, {
  169. from: proxyCreator,
  170. value,
  171. })
  172. ).address;
  173. });
  174. assertProxyInitialization({
  175. value: expectedInitializedValue,
  176. balance: value,
  177. });
  178. });
  179. });
  180. describe('reverting initialization', function () {
  181. const initializeData = new DummyImplementation('').contract
  182. .methods.reverts().encodeABI();
  183. it('reverts', async function () {
  184. await expectRevert(
  185. createProxy(this.implementation, proxyAdminAddress, initializeData, { from: proxyCreator }),
  186. 'DummyImplementation reverted',
  187. );
  188. });
  189. });
  190. });
  191. };