From 5bc5eabdf30d6d3966f2b4178ab25c55b240aa82 Mon Sep 17 00:00:00 2001 From: JulissaDantes Date: Wed, 16 Nov 2022 15:22:16 -0400 Subject: [PATCH 01/13] Remove admin and implementation getters --- contracts/proxy/transparent/ProxyAdmin.sol | 30 ----------- .../TransparentUpgradeableProxy.sol | 26 --------- test/proxy/transparent/ProxyAdmin.test.js | 30 ++--------- .../TransparentUpgradeableProxy.behaviour.js | 53 ------------------- 4 files changed, 4 insertions(+), 135 deletions(-) diff --git a/contracts/proxy/transparent/ProxyAdmin.sol b/contracts/proxy/transparent/ProxyAdmin.sol index 839534298b9..7f340f62935 100644 --- a/contracts/proxy/transparent/ProxyAdmin.sol +++ b/contracts/proxy/transparent/ProxyAdmin.sol @@ -11,36 +11,6 @@ import "../../access/Ownable.sol"; * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}. */ contract ProxyAdmin is Ownable { - /** - * @dev Returns the current implementation of `proxy`. - * - * Requirements: - * - * - This contract must be the admin of `proxy`. - */ - function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) { - // We need to manually run the static call since the getter cannot be flagged as view - // bytes4(keccak256("implementation()")) == 0x5c60da1b - (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b"); - require(success); - return abi.decode(returndata, (address)); - } - - /** - * @dev Returns the current admin of `proxy`. - * - * Requirements: - * - * - This contract must be the admin of `proxy`. - */ - function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) { - // We need to manually run the static call since the getter cannot be flagged as view - // bytes4(keccak256("admin()")) == 0xf851a440 - (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440"); - require(success); - return abi.decode(returndata, (address)); - } - /** * @dev Changes the admin of `proxy` to `newAdmin`. * diff --git a/contracts/proxy/transparent/TransparentUpgradeableProxy.sol b/contracts/proxy/transparent/TransparentUpgradeableProxy.sol index 4de85075ac1..57417296d6e 100644 --- a/contracts/proxy/transparent/TransparentUpgradeableProxy.sol +++ b/contracts/proxy/transparent/TransparentUpgradeableProxy.sol @@ -50,32 +50,6 @@ contract TransparentUpgradeableProxy is ERC1967Proxy { } } - /** - * @dev Returns the current admin. - * - * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}. - * - * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the - * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. - * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` - */ - function admin() external ifAdmin returns (address admin_) { - admin_ = _getAdmin(); - } - - /** - * @dev Returns the current implementation. - * - * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}. - * - * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the - * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. - * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` - */ - function implementation() external ifAdmin returns (address implementation_) { - implementation_ = _implementation(); - } - /** * @dev Changes the admin of the proxy. * diff --git a/test/proxy/transparent/ProxyAdmin.test.js b/test/proxy/transparent/ProxyAdmin.test.js index 07adba6ad69..1e8afb9b3bb 100644 --- a/test/proxy/transparent/ProxyAdmin.test.js +++ b/test/proxy/transparent/ProxyAdmin.test.js @@ -30,17 +30,6 @@ contract('ProxyAdmin', function (accounts) { expect(await this.proxyAdmin.owner()).to.equal(proxyAdminOwner); }); - describe('#getProxyAdmin', function () { - it('returns proxyAdmin as admin of the proxy', async function () { - const admin = await this.proxyAdmin.getProxyAdmin(this.proxy.address); - expect(admin).to.be.equal(this.proxyAdmin.address); - }); - - it('call to invalid proxy', async function () { - await expectRevert.unspecified(this.proxyAdmin.getProxyAdmin(this.implementationV1.address)); - }); - }); - describe('#changeProxyAdmin', function () { it('fails to change proxy admin if its not the proxy owner', async function () { await expectRevert( @@ -55,17 +44,6 @@ contract('ProxyAdmin', function (accounts) { }); }); - describe('#getProxyImplementation', function () { - it('returns proxy implementation address', async function () { - const implementationAddress = await this.proxyAdmin.getProxyImplementation(this.proxy.address); - expect(implementationAddress).to.be.equal(this.implementationV1.address); - }); - - it('call to invalid proxy', async function () { - await expectRevert.unspecified(this.proxyAdmin.getProxyImplementation(this.implementationV1.address)); - }); - }); - describe('#upgrade', function () { context('with unauthorized account', function () { it('fails to upgrade', async function () { @@ -79,8 +57,8 @@ contract('ProxyAdmin', function (accounts) { context('with authorized account', function () { it('upgrades implementation', async function () { await this.proxyAdmin.upgrade(this.proxy.address, this.implementationV2.address, { from: proxyAdminOwner }); - const implementationAddress = await this.proxyAdmin.getProxyImplementation(this.proxy.address); - expect(implementationAddress).to.be.equal(this.implementationV2.address); + const dummy = await new ImplV2(this.proxy.address); + expect(await dummy.version()).to.be.equals("V2"); }); }); }); @@ -116,8 +94,8 @@ contract('ProxyAdmin', function (accounts) { await this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData, { from: proxyAdminOwner }, ); - const implementationAddress = await this.proxyAdmin.getProxyImplementation(this.proxy.address); - expect(implementationAddress).to.be.equal(this.implementationV2.address); + const dummy = await new ImplV2(this.proxy.address); + expect(await dummy.version()).to.be.equals("V2"); }); }); }); diff --git a/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js b/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js index 33fef6f4119..ef57066a61c 100644 --- a/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js +++ b/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js @@ -33,12 +33,6 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro }); describe('implementation', function () { - it('returns the current implementation address', async function () { - const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress }); - - expect(implementation).to.be.equal(this.implementationV0); - }); - it('delegates to the implementation', async function () { const dummy = new DummyImplementation(this.proxyAddress); const value = await dummy.get(); @@ -52,13 +46,6 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro const from = proxyAdminAddress; describe('when the given implementation is different from the current one', function () { - it('upgrades to the requested implementation', async function () { - await this.proxy.upgradeTo(this.implementationV1, { from }); - - const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress }); - expect(implementation).to.be.equal(this.implementationV1); - }); - it('emits an event', async function () { expectEvent( await this.proxy.upgradeTo(this.implementationV1, { from }), @@ -107,11 +94,6 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro this.receipt = await this.proxy.upgradeToAndCall(this.behavior.address, initializeData, { from, value }); }); - it('upgrades to the requested implementation', async function () { - const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress }); - expect(implementation).to.be.equal(this.behavior.address); - }); - it('emits an event', function () { expectEvent(this.receipt, 'Upgraded', { implementation: this.behavior.address }); }); @@ -172,12 +154,6 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro this.receipt = await this.proxy.upgradeToAndCall(this.behaviorV1.address, v1MigrationData, { from, value }); }); - it('upgrades to the requested version and emits an event', async function () { - const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress }); - expect(implementation).to.be.equal(this.behaviorV1.address); - expectEvent(this.receipt, 'Upgraded', { implementation: this.behaviorV1.address }); - }); - it('calls the \'initialize\' function and sends given value to the proxy', async function () { const migratable = new MigratableMockV1(this.proxyAddress); @@ -198,12 +174,6 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro await this.proxy.upgradeToAndCall(this.behaviorV2.address, v2MigrationData, { from, value }); }); - it('upgrades to the requested version and emits an event', async function () { - const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress }); - expect(implementation).to.be.equal(this.behaviorV2.address); - expectEvent(this.receipt, 'Upgraded', { implementation: this.behaviorV2.address }); - }); - it('calls the \'migrate\' function and sends given value to the proxy', async function () { const migratable = new MigratableMockV2(this.proxyAddress); @@ -227,12 +197,6 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro await this.proxy.upgradeToAndCall(this.behaviorV3.address, v3MigrationData, { from, value }); }); - it('upgrades to the requested version and emits an event', async function () { - const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress }); - expect(implementation).to.be.equal(this.behaviorV3.address); - expectEvent(this.receipt, 'Upgraded', { implementation: this.behaviorV3.address }); - }); - it('calls the \'migrate\' function and sends given value to the proxy', async function () { const migratable = new MigratableMockV3(this.proxyAddress); @@ -273,11 +237,6 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro this.receipt = await this.proxy.changeAdmin(newAdmin, { from: proxyAdminAddress }); }); - it('assigns new proxy admin', async function () { - const newProxyAdmin = await this.proxy.admin.call({ from: newAdmin }); - expect(newProxyAdmin).to.be.equal(anotherAccount); - }); - it('emits an event', function () { expectEvent(this.receipt, 'AdminChanged', { previousAdmin: proxyAdminAddress, @@ -332,18 +291,6 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro 'TransparentUpgradeableProxy: admin cannot fallback to proxy target', ); }); - - context('when function names clash', function () { - it('when sender is proxy admin should run the proxy function', async function () { - const value = await this.proxy.admin.call({ from: proxyAdminAddress }); - expect(value).to.be.equal(proxyAdminAddress); - }); - - it('when sender is other should delegate to implementation', async function () { - const value = await this.proxy.admin.call({ from: anotherAccount }); - expect(value).to.be.equal('0x0000000000000000000000000000000011111142'); - }); - }); }); describe('regression', () => { From 07920a370471fab4f57588e00044f73c5ba237a4 Mon Sep 17 00:00:00 2001 From: JulissaDantes Date: Thu, 17 Nov 2022 09:07:05 -0400 Subject: [PATCH 02/13] create changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dafdf499018..e10b376a512 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased (Breaking) * `TimelockController`: Changed the role architecture to use `DEFAULT_ADMIN_ROLE` as the admin for all roles, instead of the bespoke `TIMELOCK_ADMIN_ROLE` that was used previously. This aligns with the general recommendation for `AccessControl` and makes the addition of new roles easier. Accordingly, the `admin` parameter and timelock will now be granted `DEFAULT_ADMIN_ROLE` instead of `TIMELOCK_ADMIN_ROLE`. ([#3799](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3799)) + * `TransparentUpgradeableProxy`: The `admin()` and `implementation()` getters were removed, this impacts the `ProxyAdmin` contract as well, removing the `getProxyAdmin` and `getProxyImplementation` getters. ([#](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/)) ## Unreleased From 0f238ea46a63289d8411b4c161e0bcc7c61a3c02 Mon Sep 17 00:00:00 2001 From: JulissaDantes Date: Thu, 17 Nov 2022 09:12:17 -0400 Subject: [PATCH 03/13] Include PR number --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e10b376a512..854dd0e2be0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## Unreleased (Breaking) * `TimelockController`: Changed the role architecture to use `DEFAULT_ADMIN_ROLE` as the admin for all roles, instead of the bespoke `TIMELOCK_ADMIN_ROLE` that was used previously. This aligns with the general recommendation for `AccessControl` and makes the addition of new roles easier. Accordingly, the `admin` parameter and timelock will now be granted `DEFAULT_ADMIN_ROLE` instead of `TIMELOCK_ADMIN_ROLE`. ([#3799](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3799)) - * `TransparentUpgradeableProxy`: The `admin()` and `implementation()` getters were removed, this impacts the `ProxyAdmin` contract as well, removing the `getProxyAdmin` and `getProxyImplementation` getters. ([#](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/)) + * `TransparentUpgradeableProxy`: The `admin()` and `implementation()` getters were removed, this impacts the `ProxyAdmin` contract as well, removing the `getProxyAdmin` and `getProxyImplementation` getters. ([#3820](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3820)) ## Unreleased From 9032c1874796c07b77dcc9160168c92666cf387d Mon Sep 17 00:00:00 2001 From: JulissaDantes Date: Thu, 17 Nov 2022 09:12:59 -0400 Subject: [PATCH 04/13] Update test --- test/proxy/transparent/ProxyAdmin.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/proxy/transparent/ProxyAdmin.test.js b/test/proxy/transparent/ProxyAdmin.test.js index 1e8afb9b3bb..7f852c1f99c 100644 --- a/test/proxy/transparent/ProxyAdmin.test.js +++ b/test/proxy/transparent/ProxyAdmin.test.js @@ -58,7 +58,7 @@ contract('ProxyAdmin', function (accounts) { it('upgrades implementation', async function () { await this.proxyAdmin.upgrade(this.proxy.address, this.implementationV2.address, { from: proxyAdminOwner }); const dummy = await new ImplV2(this.proxy.address); - expect(await dummy.version()).to.be.equals("V2"); + expect(await dummy.version()).to.be.equals('V2'); }); }); }); @@ -95,7 +95,7 @@ contract('ProxyAdmin', function (accounts) { { from: proxyAdminOwner }, ); const dummy = await new ImplV2(this.proxy.address); - expect(await dummy.version()).to.be.equals("V2"); + expect(await dummy.version()).to.be.equals('V2'); }); }); }); From 7b08af96973cce90d3d5806a63d2d90a46f2c592 Mon Sep 17 00:00:00 2001 From: JulissaDantes Date: Thu, 17 Nov 2022 11:33:58 -0400 Subject: [PATCH 05/13] Update test --- test/proxy/transparent/ProxyAdmin.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/proxy/transparent/ProxyAdmin.test.js b/test/proxy/transparent/ProxyAdmin.test.js index 7f852c1f99c..0d20de4084f 100644 --- a/test/proxy/transparent/ProxyAdmin.test.js +++ b/test/proxy/transparent/ProxyAdmin.test.js @@ -40,7 +40,8 @@ contract('ProxyAdmin', function (accounts) { it('changes proxy admin', async function () { await this.proxyAdmin.changeProxyAdmin(this.proxy.address, newAdmin, { from: proxyAdminOwner }); - expect(await this.proxy.admin.call({ from: newAdmin })).to.eq(newAdmin); + // Allows new admin to call ifAdmin functions + await this.proxy.changeAdmin(newAdmin, { from: newAdmin });// expect to not revert }); }); From 3974b81b99555a9f18961177f61546d6fcc994f3 Mon Sep 17 00:00:00 2001 From: JulissaDantes Date: Fri, 25 Nov 2022 08:39:17 -0400 Subject: [PATCH 06/13] Update CHANGELOG.md Co-authored-by: Francisco --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 854dd0e2be0..0755699566d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ ## Unreleased (Breaking) * `TimelockController`: Changed the role architecture to use `DEFAULT_ADMIN_ROLE` as the admin for all roles, instead of the bespoke `TIMELOCK_ADMIN_ROLE` that was used previously. This aligns with the general recommendation for `AccessControl` and makes the addition of new roles easier. Accordingly, the `admin` parameter and timelock will now be granted `DEFAULT_ADMIN_ROLE` instead of `TIMELOCK_ADMIN_ROLE`. ([#3799](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3799)) - * `TransparentUpgradeableProxy`: The `admin()` and `implementation()` getters were removed, this impacts the `ProxyAdmin` contract as well, removing the `getProxyAdmin` and `getProxyImplementation` getters. ([#3820](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3820)) + * `TransparentUpgradeableProxy`: Removed `admin` and `implementation` getters, which were only callable by the proxy owner and thus not very useful. ([#3820](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3820)) + * `ProxyAdmin`: Removed `getProxyAdmin` and `getProxyImplementation` getters. ([#3820](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3820)) ## Unreleased From 1da26023b8363327707a8104b1aeceaa7633d2c8 Mon Sep 17 00:00:00 2001 From: JulissaDantes Date: Fri, 25 Nov 2022 09:11:07 -0400 Subject: [PATCH 07/13] Update ProxyAdmin tests --- test/proxy/transparent/ProxyAdmin.test.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/test/proxy/transparent/ProxyAdmin.test.js b/test/proxy/transparent/ProxyAdmin.test.js index 0d20de4084f..417081d914d 100644 --- a/test/proxy/transparent/ProxyAdmin.test.js +++ b/test/proxy/transparent/ProxyAdmin.test.js @@ -1,7 +1,7 @@ const { expectRevert } = require('@openzeppelin/test-helpers'); const { expect } = require('chai'); - +const { network } = require('hardhat'); const ImplV1 = artifacts.require('DummyImplementation'); const ImplV2 = artifacts.require('DummyImplementationV2'); const ProxyAdmin = artifacts.require('ProxyAdmin'); @@ -40,8 +40,9 @@ contract('ProxyAdmin', function (accounts) { it('changes proxy admin', async function () { await this.proxyAdmin.changeProxyAdmin(this.proxy.address, newAdmin, { from: proxyAdminOwner }); - // Allows new admin to call ifAdmin functions - await this.proxy.changeAdmin(newAdmin, { from: newAdmin });// expect to not revert + + const admin = '0x'+(await network.provider.send('eth_getStorageAt', [this.proxy.address,'0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103','latest'])).substring(26); + expect(admin.toUpperCase()).to.be.eq(newAdmin.toUpperCase()); }); }); @@ -58,8 +59,9 @@ contract('ProxyAdmin', function (accounts) { context('with authorized account', function () { it('upgrades implementation', async function () { await this.proxyAdmin.upgrade(this.proxy.address, this.implementationV2.address, { from: proxyAdminOwner }); - const dummy = await new ImplV2(this.proxy.address); - expect(await dummy.version()).to.be.equals('V2'); + + const implementation = '0x'+(await network.provider.send('eth_getStorageAt', [this.proxy.address,'0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc','latest'])).substring(26); + expect(implementation.toUpperCase()).to.be.eq(this.implementationV2.address.toUpperCase()); }); }); }); @@ -95,8 +97,8 @@ contract('ProxyAdmin', function (accounts) { await this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData, { from: proxyAdminOwner }, ); - const dummy = await new ImplV2(this.proxy.address); - expect(await dummy.version()).to.be.equals('V2'); + const implementation = '0x'+(await network.provider.send('eth_getStorageAt', [this.proxy.address,'0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc','latest'])).substring(26); + expect(implementation.toUpperCase()).to.be.eq(this.implementationV2.address.toUpperCase()); }); }); }); From 6f773c76749dee49b277ca466adfe1ba7fa20f57 Mon Sep 17 00:00:00 2001 From: JulissaDantes Date: Fri, 25 Nov 2022 09:43:59 -0400 Subject: [PATCH 08/13] Use helper function --- test/proxy/transparent/ProxyAdmin.test.js | 17 +++--- .../TransparentUpgradeableProxy.behaviour.js | 61 ++++++++++++++----- 2 files changed, 57 insertions(+), 21 deletions(-) diff --git a/test/proxy/transparent/ProxyAdmin.test.js b/test/proxy/transparent/ProxyAdmin.test.js index 417081d914d..34aa02323a7 100644 --- a/test/proxy/transparent/ProxyAdmin.test.js +++ b/test/proxy/transparent/ProxyAdmin.test.js @@ -1,5 +1,5 @@ const { expectRevert } = require('@openzeppelin/test-helpers'); - +const { getSlot, ImplementationSlot, AdminSlot } = require('../../helpers/erc1967'); const { expect } = require('chai'); const { network } = require('hardhat'); const ImplV1 = artifacts.require('DummyImplementation'); @@ -40,9 +40,10 @@ contract('ProxyAdmin', function (accounts) { it('changes proxy admin', async function () { await this.proxyAdmin.changeProxyAdmin(this.proxy.address, newAdmin, { from: proxyAdminOwner }); + const proxyAdminSlot = await getSlot(this.proxy, AdminSlot); + const proxyAdminAddress = web3.utils.toChecksumAddress(proxyAdminSlot.substr(-40)); - const admin = '0x'+(await network.provider.send('eth_getStorageAt', [this.proxy.address,'0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103','latest'])).substring(26); - expect(admin.toUpperCase()).to.be.eq(newAdmin.toUpperCase()); + expect(proxyAdminAddress).to.be.eq(newAdmin); }); }); @@ -60,8 +61,9 @@ contract('ProxyAdmin', function (accounts) { it('upgrades implementation', async function () { await this.proxyAdmin.upgrade(this.proxy.address, this.implementationV2.address, { from: proxyAdminOwner }); - const implementation = '0x'+(await network.provider.send('eth_getStorageAt', [this.proxy.address,'0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc','latest'])).substring(26); - expect(implementation.toUpperCase()).to.be.eq(this.implementationV2.address.toUpperCase()); + const implementationSlot = await getSlot(this.proxy, ImplementationSlot); + const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + expect(implementationAddress).to.be.eq(this.implementationV2.address); }); }); }); @@ -97,8 +99,9 @@ contract('ProxyAdmin', function (accounts) { await this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData, { from: proxyAdminOwner }, ); - const implementation = '0x'+(await network.provider.send('eth_getStorageAt', [this.proxy.address,'0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc','latest'])).substring(26); - expect(implementation.toUpperCase()).to.be.eq(this.implementationV2.address.toUpperCase()); + const implementationSlot = await getSlot(this.proxy, ImplementationSlot); + const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + expect(implementationAddress).to.be.eq(this.implementationV2.address); }); }); }); diff --git a/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js b/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js index ef57066a61c..3180a4030e0 100644 --- a/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js +++ b/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js @@ -33,6 +33,12 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro }); describe('implementation', function () { + it('returns the current implementation address', async function () { + const implementation = '0x'+(await network.provider.send('eth_getStorageAt', [this.proxy.address,'0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc','latest'])).substring(26); + + expect(web3.utils.toChecksumAddress(implementation)).to.be.equal(this.implementationV0); + }); + it('delegates to the implementation', async function () { const dummy = new DummyImplementation(this.proxyAddress); const value = await dummy.get(); @@ -46,6 +52,14 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro const from = proxyAdminAddress; describe('when the given implementation is different from the current one', function () { + it('upgrades to the requested implementation', async function () { + await this.proxy.upgradeTo(this.implementationV1, { from }); + + const implementationSlot = await getSlot(this.proxy, ImplementationSlot); + const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + expect(implementationAddress).to.be.equal(this.implementationV1); + }); + it('emits an event', async function () { expectEvent( await this.proxy.upgradeTo(this.implementationV1, { from }), @@ -94,6 +108,12 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro this.receipt = await this.proxy.upgradeToAndCall(this.behavior.address, initializeData, { from, value }); }); + it('upgrades to the requested implementation', async function () { + const implementationSlot = await getSlot(this.proxy, ImplementationSlot); + const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + expect(implementationAddress).to.be.equal(this.behavior.address); + }); + it('emits an event', function () { expectEvent(this.receipt, 'Upgraded', { implementation: this.behavior.address }); }); @@ -154,6 +174,13 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro this.receipt = await this.proxy.upgradeToAndCall(this.behaviorV1.address, v1MigrationData, { from, value }); }); + it('upgrades to the requested version and emits an event', async function () { + const implementationSlot = await getSlot(this.proxy, ImplementationSlot); + const implementation = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + expect(implementation).to.be.equal(this.behaviorV1.address); + expectEvent(this.receipt, 'Upgraded', { implementation: this.behaviorV1.address }); + }); + it('calls the \'initialize\' function and sends given value to the proxy', async function () { const migratable = new MigratableMockV1(this.proxyAddress); @@ -174,6 +201,13 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro await this.proxy.upgradeToAndCall(this.behaviorV2.address, v2MigrationData, { from, value }); }); + it('upgrades to the requested version and emits an event', async function () { + const implementationSlot = await getSlot(this.proxy, ImplementationSlot); + const implementation = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + expect(implementation).to.be.equal(this.behaviorV2.address); + expectEvent(this.receipt, 'Upgraded', { implementation: this.behaviorV2.address }); + }); + it('calls the \'migrate\' function and sends given value to the proxy', async function () { const migratable = new MigratableMockV2(this.proxyAddress); @@ -197,6 +231,13 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro await this.proxy.upgradeToAndCall(this.behaviorV3.address, v3MigrationData, { from, value }); }); + it('upgrades to the requested version and emits an event', async function () { + const implementationSlot = await getSlot(this.proxy, ImplementationSlot); + const implementation = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + expect(implementation).to.be.equal(this.behaviorV3.address); + expectEvent(this.receipt, 'Upgraded', { implementation: this.behaviorV3.address }); + }); + it('calls the \'migrate\' function and sends given value to the proxy', async function () { const migratable = new MigratableMockV3(this.proxyAddress); @@ -237,6 +278,12 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro this.receipt = await this.proxy.changeAdmin(newAdmin, { from: proxyAdminAddress }); }); + it('assigns new proxy admin', async function () { + const proxyAdminSlot = await getSlot(this.proxy, AdminSlot); + const newProxyAdmin = web3.utils.toChecksumAddress(proxyAdminSlot.substr(-40)); + expect(newProxyAdmin).to.be.equal(anotherAccount); + }); + it('emits an event', function () { expectEvent(this.receipt, 'AdminChanged', { previousAdmin: proxyAdminAddress, @@ -262,20 +309,6 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro }); }); - describe('storage', function () { - it('should store the implementation address in specified location', async function () { - const implementationSlot = await getSlot(this.proxy, ImplementationSlot); - const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); - expect(implementationAddress).to.be.equal(this.implementationV0); - }); - - it('should store the admin proxy in specified location', async function () { - const proxyAdminSlot = await getSlot(this.proxy, AdminSlot); - const proxyAdminAddress = web3.utils.toChecksumAddress(proxyAdminSlot.substr(-40)); - expect(proxyAdminAddress).to.be.equal(proxyAdminAddress); - }); - }); - describe('transparent proxy', function () { beforeEach('creating proxy', async function () { const initializeData = Buffer.from(''); From 55527a0813be11e1ef8541e59f6985e4bfc61d58 Mon Sep 17 00:00:00 2001 From: JulissaDantes Date: Fri, 25 Nov 2022 09:49:41 -0400 Subject: [PATCH 09/13] Update documentation --- contracts/proxy/ERC1967/ERC1967Proxy.sol | 5 +++++ contracts/proxy/ERC1967/ERC1967Upgrade.sol | 5 +++++ test/proxy/transparent/ProxyAdmin.test.js | 5 ++--- .../TransparentUpgradeableProxy.behaviour.js | 12 ++++++------ 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/contracts/proxy/ERC1967/ERC1967Proxy.sol b/contracts/proxy/ERC1967/ERC1967Proxy.sol index a04d701ce19..9bb0370f2d7 100644 --- a/contracts/proxy/ERC1967/ERC1967Proxy.sol +++ b/contracts/proxy/ERC1967/ERC1967Proxy.sol @@ -25,6 +25,11 @@ contract ERC1967Proxy is Proxy, ERC1967Upgrade { /** * @dev Returns the current implementation address. + * + * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the + * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. + * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` + * */ function _implementation() internal view virtual override returns (address impl) { return ERC1967Upgrade._getImplementation(); diff --git a/contracts/proxy/ERC1967/ERC1967Upgrade.sol b/contracts/proxy/ERC1967/ERC1967Upgrade.sol index 77fbdd16565..b780dd95eb2 100644 --- a/contracts/proxy/ERC1967/ERC1967Upgrade.sol +++ b/contracts/proxy/ERC1967/ERC1967Upgrade.sol @@ -112,6 +112,11 @@ abstract contract ERC1967Upgrade { /** * @dev Returns the current admin. + * + * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the + * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. + * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` + * */ function _getAdmin() internal view returns (address) { return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; diff --git a/test/proxy/transparent/ProxyAdmin.test.js b/test/proxy/transparent/ProxyAdmin.test.js index 34aa02323a7..bcce479462c 100644 --- a/test/proxy/transparent/ProxyAdmin.test.js +++ b/test/proxy/transparent/ProxyAdmin.test.js @@ -1,7 +1,6 @@ const { expectRevert } = require('@openzeppelin/test-helpers'); const { getSlot, ImplementationSlot, AdminSlot } = require('../../helpers/erc1967'); const { expect } = require('chai'); -const { network } = require('hardhat'); const ImplV1 = artifacts.require('DummyImplementation'); const ImplV2 = artifacts.require('DummyImplementationV2'); const ProxyAdmin = artifacts.require('ProxyAdmin'); @@ -42,7 +41,7 @@ contract('ProxyAdmin', function (accounts) { await this.proxyAdmin.changeProxyAdmin(this.proxy.address, newAdmin, { from: proxyAdminOwner }); const proxyAdminSlot = await getSlot(this.proxy, AdminSlot); const proxyAdminAddress = web3.utils.toChecksumAddress(proxyAdminSlot.substr(-40)); - + expect(proxyAdminAddress).to.be.eq(newAdmin); }); }); @@ -60,7 +59,7 @@ contract('ProxyAdmin', function (accounts) { context('with authorized account', function () { it('upgrades implementation', async function () { await this.proxyAdmin.upgrade(this.proxy.address, this.implementationV2.address, { from: proxyAdminOwner }); - + const implementationSlot = await getSlot(this.proxy, ImplementationSlot); const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); expect(implementationAddress).to.be.eq(this.implementationV2.address); diff --git a/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js b/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js index 3180a4030e0..21e8de97ce7 100644 --- a/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js +++ b/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js @@ -34,9 +34,9 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro describe('implementation', function () { it('returns the current implementation address', async function () { - const implementation = '0x'+(await network.provider.send('eth_getStorageAt', [this.proxy.address,'0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc','latest'])).substring(26); - - expect(web3.utils.toChecksumAddress(implementation)).to.be.equal(this.implementationV0); + const implementationSlot = await getSlot(this.proxy, ImplementationSlot); + const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + expect(implementationAddress).to.be.equal(this.implementationV0); }); it('delegates to the implementation', async function () { @@ -56,10 +56,10 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro await this.proxy.upgradeTo(this.implementationV1, { from }); const implementationSlot = await getSlot(this.proxy, ImplementationSlot); - const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); expect(implementationAddress).to.be.equal(this.implementationV1); }); - + it('emits an event', async function () { expectEvent( await this.proxy.upgradeTo(this.implementationV1, { from }), @@ -110,7 +110,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro it('upgrades to the requested implementation', async function () { const implementationSlot = await getSlot(this.proxy, ImplementationSlot); - const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); expect(implementationAddress).to.be.equal(this.behavior.address); }); From 69b966218eeab5805fcf18bba274f21150988cf1 Mon Sep 17 00:00:00 2001 From: Francisco Date: Fri, 25 Nov 2022 13:09:43 -0300 Subject: [PATCH 10/13] remove extra space --- contracts/proxy/ERC1967/ERC1967Proxy.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/proxy/ERC1967/ERC1967Proxy.sol b/contracts/proxy/ERC1967/ERC1967Proxy.sol index 9bb0370f2d7..642d811ccae 100644 --- a/contracts/proxy/ERC1967/ERC1967Proxy.sol +++ b/contracts/proxy/ERC1967/ERC1967Proxy.sol @@ -29,7 +29,6 @@ contract ERC1967Proxy is Proxy, ERC1967Upgrade { * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` - * */ function _implementation() internal view virtual override returns (address impl) { return ERC1967Upgrade._getImplementation(); From a1a636bca852bec754227b9dbbfd6ad95f9cff03 Mon Sep 17 00:00:00 2001 From: Francisco Date: Fri, 25 Nov 2022 13:09:57 -0300 Subject: [PATCH 11/13] remove extra space --- contracts/proxy/ERC1967/ERC1967Upgrade.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/proxy/ERC1967/ERC1967Upgrade.sol b/contracts/proxy/ERC1967/ERC1967Upgrade.sol index b780dd95eb2..07dc048625a 100644 --- a/contracts/proxy/ERC1967/ERC1967Upgrade.sol +++ b/contracts/proxy/ERC1967/ERC1967Upgrade.sol @@ -116,7 +116,6 @@ abstract contract ERC1967Upgrade { * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` - * */ function _getAdmin() internal view returns (address) { return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; From 76291fe7b9d4d35feb63f905c6fac8bc121f7978 Mon Sep 17 00:00:00 2001 From: JulissaDantes Date: Fri, 25 Nov 2022 12:40:40 -0400 Subject: [PATCH 12/13] Add helper --- test/helpers/erc1967.js | 6 +++++ test/proxy/transparent/ProxyAdmin.test.js | 13 ++++------- .../TransparentUpgradeableProxy.behaviour.js | 23 +++++++------------ 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/test/helpers/erc1967.js b/test/helpers/erc1967.js index aab0e2288a3..6eb5fecdde6 100644 --- a/test/helpers/erc1967.js +++ b/test/helpers/erc1967.js @@ -13,6 +13,11 @@ function getSlot (address, slot) { ); } +async function getAddressInSlot (address, slot) { + const implementationSlot = await getSlot(address, slot); + return web3.utils.toChecksumAddress(implementationSlot.substr(-40)); +} + module.exports = { ImplementationLabel, AdminLabel, @@ -21,4 +26,5 @@ module.exports = { AdminSlot: labelToSlot(AdminLabel), BeaconSlot: labelToSlot(BeaconLabel), getSlot, + getAddressInSlot, }; diff --git a/test/proxy/transparent/ProxyAdmin.test.js b/test/proxy/transparent/ProxyAdmin.test.js index bcce479462c..b1f5500296d 100644 --- a/test/proxy/transparent/ProxyAdmin.test.js +++ b/test/proxy/transparent/ProxyAdmin.test.js @@ -1,5 +1,5 @@ const { expectRevert } = require('@openzeppelin/test-helpers'); -const { getSlot, ImplementationSlot, AdminSlot } = require('../../helpers/erc1967'); +const { getAddressInSlot, ImplementationSlot, AdminSlot } = require('../../helpers/erc1967'); const { expect } = require('chai'); const ImplV1 = artifacts.require('DummyImplementation'); const ImplV2 = artifacts.require('DummyImplementationV2'); @@ -39,10 +39,9 @@ contract('ProxyAdmin', function (accounts) { it('changes proxy admin', async function () { await this.proxyAdmin.changeProxyAdmin(this.proxy.address, newAdmin, { from: proxyAdminOwner }); - const proxyAdminSlot = await getSlot(this.proxy, AdminSlot); - const proxyAdminAddress = web3.utils.toChecksumAddress(proxyAdminSlot.substr(-40)); - expect(proxyAdminAddress).to.be.eq(newAdmin); + const newProxyAdmin = await getAddressInSlot(this.proxy, AdminSlot); + expect(newProxyAdmin).to.be.eq(newAdmin); }); }); @@ -60,8 +59,7 @@ contract('ProxyAdmin', function (accounts) { it('upgrades implementation', async function () { await this.proxyAdmin.upgrade(this.proxy.address, this.implementationV2.address, { from: proxyAdminOwner }); - const implementationSlot = await getSlot(this.proxy, ImplementationSlot); - const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + const implementationAddress = await getAddressInSlot(this.proxy, ImplementationSlot); expect(implementationAddress).to.be.eq(this.implementationV2.address); }); }); @@ -98,8 +96,7 @@ contract('ProxyAdmin', function (accounts) { await this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData, { from: proxyAdminOwner }, ); - const implementationSlot = await getSlot(this.proxy, ImplementationSlot); - const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + const implementationAddress = await getAddressInSlot(this.proxy, ImplementationSlot); expect(implementationAddress).to.be.eq(this.implementationV2.address); }); }); diff --git a/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js b/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js index 21e8de97ce7..478bc1c299b 100644 --- a/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js +++ b/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js @@ -1,6 +1,6 @@ const { BN, expectRevert, expectEvent, constants } = require('@openzeppelin/test-helpers'); const { ZERO_ADDRESS } = constants; -const { getSlot, ImplementationSlot, AdminSlot } = require('../../helpers/erc1967'); +const { getAddressInSlot, ImplementationSlot, AdminSlot } = require('../../helpers/erc1967'); const { expect } = require('chai'); @@ -34,8 +34,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro describe('implementation', function () { it('returns the current implementation address', async function () { - const implementationSlot = await getSlot(this.proxy, ImplementationSlot); - const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + const implementationAddress = await getAddressInSlot(this.proxy, ImplementationSlot); expect(implementationAddress).to.be.equal(this.implementationV0); }); @@ -55,8 +54,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro it('upgrades to the requested implementation', async function () { await this.proxy.upgradeTo(this.implementationV1, { from }); - const implementationSlot = await getSlot(this.proxy, ImplementationSlot); - const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + const implementationAddress = await getAddressInSlot(this.proxy, ImplementationSlot); expect(implementationAddress).to.be.equal(this.implementationV1); }); @@ -109,8 +107,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro }); it('upgrades to the requested implementation', async function () { - const implementationSlot = await getSlot(this.proxy, ImplementationSlot); - const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + const implementationAddress = await getAddressInSlot(this.proxy, ImplementationSlot); expect(implementationAddress).to.be.equal(this.behavior.address); }); @@ -175,8 +172,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro }); it('upgrades to the requested version and emits an event', async function () { - const implementationSlot = await getSlot(this.proxy, ImplementationSlot); - const implementation = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + const implementation = await getAddressInSlot(this.proxy, ImplementationSlot); expect(implementation).to.be.equal(this.behaviorV1.address); expectEvent(this.receipt, 'Upgraded', { implementation: this.behaviorV1.address }); }); @@ -202,8 +198,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro }); it('upgrades to the requested version and emits an event', async function () { - const implementationSlot = await getSlot(this.proxy, ImplementationSlot); - const implementation = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + const implementation = await getAddressInSlot(this.proxy, ImplementationSlot); expect(implementation).to.be.equal(this.behaviorV2.address); expectEvent(this.receipt, 'Upgraded', { implementation: this.behaviorV2.address }); }); @@ -232,8 +227,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro }); it('upgrades to the requested version and emits an event', async function () { - const implementationSlot = await getSlot(this.proxy, ImplementationSlot); - const implementation = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + const implementation = await getAddressInSlot(this.proxy, ImplementationSlot); expect(implementation).to.be.equal(this.behaviorV3.address); expectEvent(this.receipt, 'Upgraded', { implementation: this.behaviorV3.address }); }); @@ -279,8 +273,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro }); it('assigns new proxy admin', async function () { - const proxyAdminSlot = await getSlot(this.proxy, AdminSlot); - const newProxyAdmin = web3.utils.toChecksumAddress(proxyAdminSlot.substr(-40)); + const newProxyAdmin = await getAddressInSlot(this.proxy, AdminSlot); expect(newProxyAdmin).to.be.equal(anotherAccount); }); From 46f866a907433ad8bca8280386df546d402d1cd8 Mon Sep 17 00:00:00 2001 From: Francisco Date: Fri, 25 Nov 2022 15:12:50 -0300 Subject: [PATCH 13/13] rename variable --- test/helpers/erc1967.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/helpers/erc1967.js b/test/helpers/erc1967.js index 6eb5fecdde6..184f1cd0f8e 100644 --- a/test/helpers/erc1967.js +++ b/test/helpers/erc1967.js @@ -14,8 +14,8 @@ function getSlot (address, slot) { } async function getAddressInSlot (address, slot) { - const implementationSlot = await getSlot(address, slot); - return web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + const slotValue = await getSlot(address, slot); + return web3.utils.toChecksumAddress(slotValue.substr(-40)); } module.exports = {