-
Notifications
You must be signed in to change notification settings - Fork 286
Use upgradeToAndCall depending on upgrade interface version #883
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
ericglau
merged 15 commits into
OpenZeppelin:master
from
ericglau:upgradeToAndCallHardhat
Sep 27, 2023
Merged
Changes from 9 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
7e955b1
Use upgradeToAndCall depending on interface version
ericglau 882a5f8
Decode UPGRADE_INTERFACE_VERSION string directly
ericglau 30d54a4
Add changelogs, bump versions
ericglau a22eccd
Use more specific changelog message
ericglau 2869e2a
Simplify test contracts
ericglau 167e1fd
Add tests for importing 5.0 proxies
ericglau bb3b58b
Add note for changeProxyAdmin
ericglau 9670db3
Update note
ericglau b3e436f
Update changelog
ericglau 980ff69
Rename to signature
ericglau 5f112bb
Update packages/core/CHANGELOG.md
ericglau 69ce9a1
Improve changelog for hardhat
ericglau 8d376e4
Merge branch 'master' into upgradeToAndCallHardhat
ericglau 970399f
Update CHANGELOG.md
ericglau 5142294
Update packages/core/src/call-optional-signature.ts
ericglau File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { keccak256 } from 'ethereumjs-util'; | ||
| import { call, EthereumProvider } from './provider'; | ||
|
|
||
| export async function callOptionalSelector(provider: EthereumProvider, address: string, selector: string) { | ||
ericglau marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const data = '0x' + keccak256(Buffer.from(selector)).toString('hex').slice(0, 8); | ||
| try { | ||
| return await call(provider, address, data); | ||
| } catch (e: any) { | ||
| if ( | ||
| !( | ||
| e.message.includes('function selector was not recognized') || | ||
| e.message.includes('invalid opcode') || | ||
| e.message.includes('revert') || | ||
| e.message.includes('execution error') | ||
| ) | ||
| ) { | ||
| throw e; | ||
| } else { | ||
| return undefined; | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| import test from 'ava'; | ||
| import { EthereumProvider } from './provider'; | ||
| import { getUpgradeInterfaceVersion } from './upgrade-interface-version'; | ||
|
|
||
| const hash = '0x1234'; | ||
|
|
||
| function makeProviderReturning(result: unknown): EthereumProvider { | ||
| return { send: (_method: string, _params: unknown[]) => Promise.resolve(result) } as EthereumProvider; | ||
| } | ||
|
|
||
| function makeProviderError(msg: string): EthereumProvider { | ||
| return { | ||
| send: (_method: string, _params: unknown[]) => { | ||
| throw new Error(msg); | ||
| }, | ||
| } as EthereumProvider; | ||
| } | ||
|
|
||
| test('getUpgradeInterfaceVersion returns version', async t => { | ||
| // abi encoding of '5.0.0' | ||
| const provider = makeProviderReturning( | ||
| '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005352e302e30000000000000000000000000000000000000000000000000000000', | ||
| ); | ||
| t.is(await getUpgradeInterfaceVersion(provider, hash), '5.0.0'); | ||
| }); | ||
|
|
||
| test('getUpgradeInterfaceVersion throws unrelated error', async t => { | ||
| const provider = makeProviderError('unrelated error'); | ||
| await t.throwsAsync(() => getUpgradeInterfaceVersion(provider, hash), { message: 'unrelated error' }); | ||
| }); | ||
|
|
||
| test('getUpgradeInterfaceVersion returns undefined for invalid selector', async t => { | ||
| const provider = makeProviderError( | ||
| `Transaction reverted: function selector was not recognized and there's no fallback function`, | ||
| ); | ||
| t.is(await getUpgradeInterfaceVersion(provider, hash), undefined); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| import { callOptionalSelector } from './call-optional-selector'; | ||
| import { EthereumProvider } from './provider'; | ||
|
|
||
| export async function getUpgradeInterfaceVersion( | ||
| provider: EthereumProvider, | ||
| address: string, | ||
| ): Promise<string | undefined> { | ||
| const encodedVersion = await callOptionalSelector(provider, address, 'UPGRADE_INTERFACE_VERSION()'); | ||
| if (encodedVersion !== undefined) { | ||
| // Encoded string | ||
| const buf = Buffer.from(encodedVersion.replace(/^0x/, ''), 'hex'); | ||
|
|
||
| // The first 32 bytes represent the offset, which should be 32 for a string | ||
| const offset = parseInt(buf.slice(0, 32).toString('hex'), 16); | ||
| if (offset !== 32) { | ||
| throw new Error(`Unexpected type for UPGRADE_INTERFACE_VERSION at address ${address}. Expected a string`); | ||
| } | ||
|
|
||
| // The next 32 bytes represent the length of the string | ||
| const length = parseInt(buf.slice(32, 64).toString('hex'), 16); | ||
|
|
||
| // The rest is the string itself | ||
| return buf.slice(64, 64 + length).toString('utf8'); | ||
| } else { | ||
| return undefined; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| pragma solidity >= 0.4.22 <0.8.0; | ||
|
|
||
| import "./Greeter.sol"; | ||
| import "./utils/Proxiable50.sol"; | ||
|
|
||
| contract Greeter50Proxiable is Greeter, Proxiable50 {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| pragma solidity ^0.5.1; | ||
|
|
||
| import "./GreeterV2.sol"; | ||
| import "./utils/Proxiable50.sol"; | ||
|
|
||
| contract Greeter50V2Proxiable is GreeterV2, Proxiable50 {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| pragma solidity ^0.5.1; | ||
|
|
||
| import "./GreeterV3.sol"; | ||
| import "./utils/Proxiable50.sol"; | ||
|
|
||
| contract Greeter50V3Proxiable is GreeterV3, Proxiable50 {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.20; | ||
|
|
||
| import "@openzeppelin/contracts-5.0/proxy/beacon/BeaconProxy.sol"; | ||
| import "@openzeppelin/contracts-5.0/proxy/beacon/UpgradeableBeacon.sol"; | ||
| import "@openzeppelin/contracts-5.0/proxy/ERC1967/ERC1967Proxy.sol"; | ||
| import "@openzeppelin/contracts-5.0/proxy/transparent/TransparentUpgradeableProxy.sol"; | ||
| import "@openzeppelin/contracts-5.0/proxy/transparent/ProxyAdmin.sol"; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| pragma solidity >= 0.4.22 <0.8.0; | ||
|
|
||
| // This contract is for testing only, it is not safe for use in production. | ||
|
|
||
| contract Proxiable50 { | ||
| bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; | ||
|
|
||
| string public constant UPGRADE_INTERFACE_VERSION = "5.0.0"; | ||
|
|
||
| function upgradeToAndCall(address newImplementation, bytes calldata data) external { | ||
| _setImplementation(newImplementation); | ||
| if (data.length > 0) { | ||
| /** | ||
| * Using address(this).call is dangerous as the call can impersonate the proxy being upgraded. | ||
| * a better option is to use a delegate call with an oz-upgrades-unsafe-allow, but this is not | ||
| * supported by the early version of solidity used here. | ||
| * | ||
| * /// @custom:oz-upgrades-unsafe-allow delegatecall | ||
| * (bool success, ) = newImplementation.delegatecall(data); | ||
| * | ||
| * Note that using delegate call can make your implementation contract vulnerable if this function | ||
| * is not protected with the `onlyProxy` modifier. Again, This contract is for testing only, it is | ||
| * not safe for use in production. Instead, use the `UUPSUpgradeable` contract available in | ||
| * @openzeppelin/contracts-upgradeable | ||
| */ | ||
| (bool success, ) = address(this).call(data); | ||
| require(success, "upgrade call reverted"); | ||
| } else { | ||
| _checkNonPayable(); | ||
| } | ||
| } | ||
|
|
||
| function _checkNonPayable() private { | ||
| if (msg.value > 0) { | ||
| revert('non-payable upgrade call'); | ||
| } | ||
| } | ||
|
|
||
| function _setImplementation(address newImplementation) private { | ||
| bytes32 slot = _IMPLEMENTATION_SLOT; | ||
| // solhint-disable-next-line no-inline-assembly | ||
| assembly { | ||
| sstore(slot, newImplementation) | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.