瀏覽代碼

Add Initialized event (#3294)

Co-authored-by: Francisco Giordano <frangio.1@gmail.com>
Hadrien Croubois 3 年之前
父節點
當前提交
e029096ca4
共有 3 個文件被更改,包括 46 次插入4 次删除
  1. 2 0
      CHANGELOG.md
  2. 7 0
      contracts/proxy/utils/Initializable.sol
  3. 37 4
      test/proxy/utils/Initializable.test.js

+ 2 - 0
CHANGELOG.md

@@ -16,6 +16,8 @@
  * `ERC20Wrapper`: the `decimals()` function now tries to fetch the value from the underlying token instance. If that calls revert, then the default value is used. ([#3259](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3259))
  * `Governor`: Implement `IERC721Receiver` and `IERC1155Receiver` to improve token custody by governors. ([#3230](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3230))
  * `TimelockController`: Implement `IERC721Receiver` and `IERC1155Receiver` to improve token custody by timelocks. ([#3230](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3230))
+ * `Initializable`: add a reinitializer modifier that enables the initialization of new modules, added to already initialized contracts through upgradeability. ([#3232](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3232))
+ * `Initializable`: add an Initialized event that tracks initialized version numbers. ([#3294](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3294))
 
 ### Breaking changes
 

+ 7 - 0
contracts/proxy/utils/Initializable.sol

@@ -66,6 +66,11 @@ abstract contract Initializable {
      */
     bool private _initializing;
 
+    /**
+     * @dev Triggered when the contract has been initialized or reinitialized.
+     */
+    event Initialized(uint8 version);
+
     /**
      * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
      * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
@@ -78,6 +83,7 @@ abstract contract Initializable {
         _;
         if (isTopLevelCall) {
             _initializing = false;
+            emit Initialized(1);
         }
     }
 
@@ -101,6 +107,7 @@ abstract contract Initializable {
         _;
         if (isTopLevelCall) {
             _initializing = false;
+            emit Initialized(version);
         }
     }
 

+ 37 - 4
test/proxy/utils/Initializable.test.js

@@ -1,4 +1,4 @@
-const { expectRevert } = require('@openzeppelin/test-helpers');
+const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
 const { expect } = require('chai');
 
 const InitializableMock = artifacts.require('InitializableMock');
@@ -12,13 +12,13 @@ contract('Initializable', function (accounts) {
       this.contract = await InitializableMock.new();
     });
 
-    context('before initialize', function () {
+    describe('before initialize', function () {
       it('initializer has not run', async function () {
         expect(await this.contract.initializerRan()).to.equal(false);
       });
     });
 
-    context('after initialize', function () {
+    describe('after initialize', function () {
       beforeEach('initializing', async function () {
         await this.contract.initialize();
       });
@@ -32,7 +32,7 @@ contract('Initializable', function (accounts) {
       });
     });
 
-    context('nested under an initializer', function () {
+    describe('nested under an initializer', function () {
       it('initializer modifier reverts', async function () {
         await expectRevert(this.contract.initializerNested(), 'Initializable: contract is already initialized');
       });
@@ -108,6 +108,39 @@ contract('Initializable', function (accounts) {
     });
   });
 
+  describe('events', function () {
+    it('constructor initialization emits event', async function () {
+      const contract = await ConstructorInitializableMock.new();
+
+      await expectEvent.inTransaction(contract.transactionHash, contract, 'Initialized', { version: '1' });
+    });
+
+    it('initialization emits event', async function () {
+      const contract = await ReinitializerMock.new();
+
+      const { receipt } = await contract.initialize();
+      expect(receipt.logs.filter(({ event }) => event === 'Initialized').length).to.be.equal(1);
+      expectEvent(receipt, 'Initialized', { version: '1' });
+    });
+
+    it('reinitialization emits event', async function () {
+      const contract = await ReinitializerMock.new();
+
+      const { receipt } = await contract.reinitialize(128);
+      expect(receipt.logs.filter(({ event }) => event === 'Initialized').length).to.be.equal(1);
+      expectEvent(receipt, 'Initialized', { version: '128' });
+    });
+
+    it('chained reinitialization emits multiple events', async function () {
+      const contract = await ReinitializerMock.new();
+
+      const { receipt } = await contract.chainReinitialize(2, 3);
+      expect(receipt.logs.filter(({ event }) => event === 'Initialized').length).to.be.equal(2);
+      expectEvent(receipt, 'Initialized', { version: '2' });
+      expectEvent(receipt, 'Initialized', { version: '3' });
+    });
+  });
+
   describe('complex testing with inheritance', function () {
     const mother = '12';
     const gramps = '56';