UpgradeableProxy.behaviour.js 7.4 KB

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