Merge pull request #10218 from filecoin-project/mikers/itestsProxy
test: itest: fevm proxy contracts
This commit is contained in:
commit
03fd703db5
1
itests/contracts/TransparentUpgradeableProxy.hex
Normal file
1
itests/contracts/TransparentUpgradeableProxy.hex
Normal file
File diff suppressed because one or more lines are too long
590
itests/contracts/TransparentUpgradeableProxy.sol
Normal file
590
itests/contracts/TransparentUpgradeableProxy.sol
Normal file
@ -0,0 +1,590 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
abstract contract Initializable {
|
||||
uint8 private _initialized;
|
||||
|
||||
bool private _initializing;
|
||||
|
||||
event Initialized(uint8 version);
|
||||
|
||||
modifier initializer() {
|
||||
bool isTopLevelCall = !_initializing;
|
||||
require(
|
||||
(isTopLevelCall && _initialized < 1) ||
|
||||
(!Address.isContract(address(this)) && _initialized == 1),
|
||||
"Initializable: contract is already initialized"
|
||||
);
|
||||
_initialized = 1;
|
||||
if (isTopLevelCall) {
|
||||
_initializing = true;
|
||||
}
|
||||
_;
|
||||
if (isTopLevelCall) {
|
||||
_initializing = false;
|
||||
emit Initialized(1);
|
||||
}
|
||||
}
|
||||
|
||||
modifier reinitializer(uint8 version) {
|
||||
require(
|
||||
!_initializing && _initialized < version,
|
||||
"Initializable: contract is already initialized"
|
||||
);
|
||||
_initialized = version;
|
||||
_initializing = true;
|
||||
_;
|
||||
_initializing = false;
|
||||
emit Initialized(version);
|
||||
}
|
||||
|
||||
modifier onlyInitializing() {
|
||||
require(_initializing, "Initializable: contract is not initializing");
|
||||
_;
|
||||
}
|
||||
|
||||
function _disableInitializers() internal virtual {
|
||||
require(!_initializing, "Initializable: contract is initializing");
|
||||
if (_initialized != type(uint8).max) {
|
||||
_initialized = type(uint8).max;
|
||||
emit Initialized(type(uint8).max);
|
||||
}
|
||||
}
|
||||
|
||||
function _getInitializedVersion() internal view returns (uint8) {
|
||||
return _initialized;
|
||||
}
|
||||
|
||||
function _isInitializing() internal view returns (bool) {
|
||||
return _initializing;
|
||||
}
|
||||
}
|
||||
|
||||
contract Implementation4 is Initializable {
|
||||
uint256 internal _value;
|
||||
|
||||
function initialize() public initializer {}
|
||||
|
||||
function setValue(uint256 _number) public {
|
||||
_value = _number;
|
||||
}
|
||||
|
||||
function getValue() public view returns (uint256) {
|
||||
return _value;
|
||||
}
|
||||
|
||||
fallback() external {
|
||||
_value = 1;
|
||||
}
|
||||
}
|
||||
|
||||
contract Implementation2 is Initializable {
|
||||
uint256 internal _value;
|
||||
|
||||
function initialize() public initializer {}
|
||||
|
||||
function setValue(uint256 _number) public {
|
||||
_value = _number;
|
||||
}
|
||||
|
||||
function getValue() public view returns (uint256) {
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
|
||||
abstract contract Proxy {
|
||||
function _delegate(address implementation) internal virtual {
|
||||
assembly {
|
||||
calldatacopy(0, 0, calldatasize())
|
||||
|
||||
let result := delegatecall(
|
||||
gas(),
|
||||
implementation,
|
||||
0,
|
||||
calldatasize(),
|
||||
0,
|
||||
0
|
||||
)
|
||||
|
||||
returndatacopy(0, 0, returndatasize())
|
||||
|
||||
switch result
|
||||
case 0 {
|
||||
revert(0, returndatasize())
|
||||
}
|
||||
default {
|
||||
return(0, returndatasize())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _implementation() internal view virtual returns (address);
|
||||
|
||||
function _fallback() internal virtual {
|
||||
_beforeFallback();
|
||||
_delegate(_implementation());
|
||||
}
|
||||
|
||||
fallback() external payable virtual {
|
||||
_fallback();
|
||||
}
|
||||
|
||||
receive() external payable virtual {
|
||||
_fallback();
|
||||
}
|
||||
|
||||
function _beforeFallback() internal virtual {}
|
||||
}
|
||||
|
||||
interface IBeacon {
|
||||
function implementation() external view returns (address);
|
||||
}
|
||||
|
||||
interface IERC1822Proxiable {
|
||||
function proxiableUUID() external view returns (bytes32);
|
||||
}
|
||||
|
||||
library Address {
|
||||
function isContract(address account) internal view returns (bool) {
|
||||
return account.code.length > 0;
|
||||
}
|
||||
|
||||
function sendValue(address payable recipient, uint256 amount) internal {
|
||||
require(
|
||||
address(this).balance >= amount,
|
||||
"Address: insufficient balance"
|
||||
);
|
||||
|
||||
(bool success, ) = recipient.call{value: amount}("");
|
||||
require(
|
||||
success,
|
||||
"Address: unable to send value, recipient may have reverted"
|
||||
);
|
||||
}
|
||||
|
||||
function functionCall(
|
||||
address target,
|
||||
bytes memory data
|
||||
) internal returns (bytes memory) {
|
||||
return
|
||||
functionCallWithValue(
|
||||
target,
|
||||
data,
|
||||
0,
|
||||
"Address: low-level call failed"
|
||||
);
|
||||
}
|
||||
|
||||
function functionCall(
|
||||
address target,
|
||||
bytes memory data,
|
||||
string memory errorMessage
|
||||
) internal returns (bytes memory) {
|
||||
return functionCallWithValue(target, data, 0, errorMessage);
|
||||
}
|
||||
|
||||
function functionCallWithValue(
|
||||
address target,
|
||||
bytes memory data,
|
||||
uint256 value
|
||||
) internal returns (bytes memory) {
|
||||
return
|
||||
functionCallWithValue(
|
||||
target,
|
||||
data,
|
||||
value,
|
||||
"Address: low-level call with value failed"
|
||||
);
|
||||
}
|
||||
|
||||
function functionCallWithValue(
|
||||
address target,
|
||||
bytes memory data,
|
||||
uint256 value,
|
||||
string memory errorMessage
|
||||
) internal returns (bytes memory) {
|
||||
require(
|
||||
address(this).balance >= value,
|
||||
"Address: insufficient balance for call"
|
||||
);
|
||||
(bool success, bytes memory returndata) = target.call{value: value}(
|
||||
data
|
||||
);
|
||||
return
|
||||
verifyCallResultFromTarget(
|
||||
target,
|
||||
success,
|
||||
returndata,
|
||||
errorMessage
|
||||
);
|
||||
}
|
||||
|
||||
function functionStaticCall(
|
||||
address target,
|
||||
bytes memory data
|
||||
) internal view returns (bytes memory) {
|
||||
return
|
||||
functionStaticCall(
|
||||
target,
|
||||
data,
|
||||
"Address: low-level static call failed"
|
||||
);
|
||||
}
|
||||
|
||||
function functionStaticCall(
|
||||
address target,
|
||||
bytes memory data,
|
||||
string memory errorMessage
|
||||
) internal view returns (bytes memory) {
|
||||
(bool success, bytes memory returndata) = target.staticcall(data);
|
||||
return
|
||||
verifyCallResultFromTarget(
|
||||
target,
|
||||
success,
|
||||
returndata,
|
||||
errorMessage
|
||||
);
|
||||
}
|
||||
|
||||
function functionDelegateCall(
|
||||
address target,
|
||||
bytes memory data
|
||||
) internal returns (bytes memory) {
|
||||
return
|
||||
functionDelegateCall(
|
||||
target,
|
||||
data,
|
||||
"Address: low-level delegate call failed"
|
||||
);
|
||||
}
|
||||
|
||||
function functionDelegateCall(
|
||||
address target,
|
||||
bytes memory data,
|
||||
string memory errorMessage
|
||||
) internal returns (bytes memory) {
|
||||
(bool success, bytes memory returndata) = target.delegatecall(data);
|
||||
return
|
||||
verifyCallResultFromTarget(
|
||||
target,
|
||||
success,
|
||||
returndata,
|
||||
errorMessage
|
||||
);
|
||||
}
|
||||
|
||||
function verifyCallResultFromTarget(
|
||||
address target,
|
||||
bool success,
|
||||
bytes memory returndata,
|
||||
string memory errorMessage
|
||||
) internal view returns (bytes memory) {
|
||||
if (success) {
|
||||
if (returndata.length == 0) {
|
||||
require(isContract(target), "Address: call to non-contract");
|
||||
}
|
||||
return returndata;
|
||||
} else {
|
||||
_revert(returndata, errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
function verifyCallResult(
|
||||
bool success,
|
||||
bytes memory returndata,
|
||||
string memory errorMessage
|
||||
) internal pure returns (bytes memory) {
|
||||
if (success) {
|
||||
return returndata;
|
||||
} else {
|
||||
_revert(returndata, errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
function _revert(
|
||||
bytes memory returndata,
|
||||
string memory errorMessage
|
||||
) private pure {
|
||||
if (returndata.length > 0) {
|
||||
assembly {
|
||||
let returndata_size := mload(returndata)
|
||||
revert(add(32, returndata), returndata_size)
|
||||
}
|
||||
} else {
|
||||
revert(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
library StorageSlot {
|
||||
struct AddressSlot {
|
||||
address value;
|
||||
}
|
||||
|
||||
struct BooleanSlot {
|
||||
bool value;
|
||||
}
|
||||
|
||||
struct Bytes32Slot {
|
||||
bytes32 value;
|
||||
}
|
||||
|
||||
struct Uint256Slot {
|
||||
uint256 value;
|
||||
}
|
||||
|
||||
function getAddressSlot(
|
||||
bytes32 slot
|
||||
) internal pure returns (AddressSlot storage r) {
|
||||
assembly {
|
||||
r.slot := slot
|
||||
}
|
||||
}
|
||||
|
||||
function getBooleanSlot(
|
||||
bytes32 slot
|
||||
) internal pure returns (BooleanSlot storage r) {
|
||||
assembly {
|
||||
r.slot := slot
|
||||
}
|
||||
}
|
||||
|
||||
function getBytes32Slot(
|
||||
bytes32 slot
|
||||
) internal pure returns (Bytes32Slot storage r) {
|
||||
assembly {
|
||||
r.slot := slot
|
||||
}
|
||||
}
|
||||
|
||||
function getUint256Slot(
|
||||
bytes32 slot
|
||||
) internal pure returns (Uint256Slot storage r) {
|
||||
assembly {
|
||||
r.slot := slot
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract contract ERC1967Upgrade {
|
||||
bytes32 private constant _ROLLBACK_SLOT =
|
||||
0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
|
||||
|
||||
bytes32 internal constant _IMPLEMENTATION_SLOT =
|
||||
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
|
||||
|
||||
event Upgraded(address indexed implementation);
|
||||
|
||||
function _getImplementation() internal view returns (address) {
|
||||
return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
|
||||
}
|
||||
|
||||
function _setImplementation(address newImplementation) private {
|
||||
require(
|
||||
Address.isContract(newImplementation),
|
||||
"ERC1967: new implementation is not a contract"
|
||||
);
|
||||
StorageSlot
|
||||
.getAddressSlot(_IMPLEMENTATION_SLOT)
|
||||
.value = newImplementation;
|
||||
}
|
||||
|
||||
function _upgradeTo(address newImplementation) internal {
|
||||
_setImplementation(newImplementation);
|
||||
emit Upgraded(newImplementation);
|
||||
}
|
||||
|
||||
function _upgradeToAndCall(
|
||||
address newImplementation,
|
||||
bytes memory data,
|
||||
bool forceCall
|
||||
) internal {
|
||||
_upgradeTo(newImplementation);
|
||||
if (data.length > 0 || forceCall) {
|
||||
Address.functionDelegateCall(newImplementation, data);
|
||||
}
|
||||
}
|
||||
|
||||
function _upgradeToAndCallUUPS(
|
||||
address newImplementation,
|
||||
bytes memory data,
|
||||
bool forceCall
|
||||
) internal {
|
||||
if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
|
||||
_setImplementation(newImplementation);
|
||||
} else {
|
||||
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (
|
||||
bytes32 slot
|
||||
) {
|
||||
require(
|
||||
slot == _IMPLEMENTATION_SLOT,
|
||||
"ERC1967Upgrade: unsupported proxiableUUID"
|
||||
);
|
||||
} catch {
|
||||
revert("ERC1967Upgrade: new implementation is not UUPS");
|
||||
}
|
||||
_upgradeToAndCall(newImplementation, data, forceCall);
|
||||
}
|
||||
}
|
||||
|
||||
bytes32 internal constant _ADMIN_SLOT =
|
||||
0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
|
||||
|
||||
event AdminChanged(address previousAdmin, address newAdmin);
|
||||
|
||||
function _getAdmin() internal view returns (address) {
|
||||
return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
|
||||
}
|
||||
|
||||
function _setAdmin(address newAdmin) private {
|
||||
require(
|
||||
newAdmin != address(0),
|
||||
"ERC1967: new admin is the zero address"
|
||||
);
|
||||
StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
|
||||
}
|
||||
|
||||
function _changeAdmin(address newAdmin) internal {
|
||||
emit AdminChanged(_getAdmin(), newAdmin);
|
||||
_setAdmin(newAdmin);
|
||||
}
|
||||
|
||||
bytes32 internal constant _BEACON_SLOT =
|
||||
0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
|
||||
|
||||
event BeaconUpgraded(address indexed beacon);
|
||||
|
||||
function _getBeacon() internal view returns (address) {
|
||||
return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
|
||||
}
|
||||
|
||||
function _setBeacon(address newBeacon) private {
|
||||
require(
|
||||
Address.isContract(newBeacon),
|
||||
"ERC1967: new beacon is not a contract"
|
||||
);
|
||||
require(
|
||||
Address.isContract(IBeacon(newBeacon).implementation()),
|
||||
"ERC1967: beacon implementation is not a contract"
|
||||
);
|
||||
StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
|
||||
}
|
||||
|
||||
function _upgradeBeaconToAndCall(
|
||||
address newBeacon,
|
||||
bytes memory data,
|
||||
bool forceCall
|
||||
) internal {
|
||||
_setBeacon(newBeacon);
|
||||
emit BeaconUpgraded(newBeacon);
|
||||
if (data.length > 0 || forceCall) {
|
||||
Address.functionDelegateCall(
|
||||
IBeacon(newBeacon).implementation(),
|
||||
data
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contract ERC1967Proxy is Proxy, ERC1967Upgrade {
|
||||
constructor(address _logic, bytes memory _data) payable {
|
||||
_upgradeToAndCall(_logic, _data, false);
|
||||
}
|
||||
|
||||
function _implementation()
|
||||
internal
|
||||
view
|
||||
virtual
|
||||
override
|
||||
returns (address impl)
|
||||
{
|
||||
return ERC1967Upgrade._getImplementation();
|
||||
}
|
||||
}
|
||||
|
||||
contract TransparentUpgradeableProxy is ERC1967Proxy {
|
||||
constructor(address _logic) payable ERC1967Proxy(_logic, "") {
|
||||
_changeAdmin(msg.sender);
|
||||
}
|
||||
|
||||
modifier ifAdmin() {
|
||||
if (msg.sender == _getAdmin()) {
|
||||
_;
|
||||
} else {
|
||||
_fallback();
|
||||
}
|
||||
}
|
||||
|
||||
function admin() external payable ifAdmin returns (address admin_) {
|
||||
_requireZeroValue();
|
||||
admin_ = _getAdmin();
|
||||
}
|
||||
|
||||
function implementation()
|
||||
external
|
||||
payable
|
||||
ifAdmin
|
||||
returns (address implementation_)
|
||||
{
|
||||
_requireZeroValue();
|
||||
implementation_ = _implementation();
|
||||
}
|
||||
|
||||
function changeAdmin(address newAdmin) external payable virtual ifAdmin {
|
||||
_requireZeroValue();
|
||||
_changeAdmin(newAdmin);
|
||||
}
|
||||
|
||||
function upgradeTo(address newImplementation) external payable ifAdmin {
|
||||
_requireZeroValue();
|
||||
_upgradeToAndCall(newImplementation, bytes(""), false);
|
||||
}
|
||||
|
||||
function upgradeToAndCall(
|
||||
address newImplementation,
|
||||
bytes calldata data
|
||||
) external payable ifAdmin {
|
||||
_upgradeToAndCall(newImplementation, data, true);
|
||||
}
|
||||
|
||||
function _admin() internal view virtual returns (address) {
|
||||
return _getAdmin();
|
||||
}
|
||||
|
||||
function _beforeFallback() internal virtual override {
|
||||
require(
|
||||
msg.sender != _getAdmin(),
|
||||
"TransparentUpgradeableProxy: admin cannot fallback to proxy target"
|
||||
);
|
||||
super._beforeFallback();
|
||||
}
|
||||
|
||||
function _requireZeroValue() private {
|
||||
require(msg.value == 0);
|
||||
}
|
||||
}
|
||||
|
||||
contract TestHelper {
|
||||
function getValue(address proxyAddress) public returns (uint256) {
|
||||
Implementation2 proxyInstance2 = Implementation2(proxyAddress);
|
||||
return proxyInstance2.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
contract TransparentUpgradeableProxyTestRunner {
|
||||
function test() public {
|
||||
assert(0 == getValue());
|
||||
}
|
||||
|
||||
function getValue() public returns (uint256) {
|
||||
Implementation4 instance4 = new Implementation4();
|
||||
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
|
||||
address(instance4)
|
||||
);
|
||||
Implementation2 instance2 = new Implementation2();
|
||||
proxy.upgradeTo(address(instance2));
|
||||
//use helper because proxy admin can't call getValue()
|
||||
TestHelper h = new TestHelper();
|
||||
return h.getValue(address(proxy));
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ set -o pipefail
|
||||
# to compile all of the .sol files to their corresponding evm binary files stored as .hex
|
||||
# solc outputs to stdout a format that we just want to grab the last line of and then remove the trailing newline on that line
|
||||
|
||||
find . -name \*.sol -print0 |
|
||||
find . -maxdepth 1 -name \*.sol -print0 |
|
||||
xargs -0 -I{} bash -euc -o pipefail 'solc --bin {} |tail -n1 | tr -d "\n" > $(echo {} | sed -e s/.sol$/.hex/)'
|
||||
|
||||
|
||||
@ -17,3 +17,7 @@ for filename in Constructor TestApp ValueSender Create2Factory DeployValueTest;
|
||||
solc --bin $filename.sol | tail -n5|head -n1 | tr -d "\n" > $filename.hex
|
||||
done
|
||||
|
||||
for filename in TransparentUpgradeableProxy ; do
|
||||
echo $filename
|
||||
solc --bin $filename.sol | tail -n1| tr -d "\n" > $filename.hex
|
||||
done
|
||||
|
@ -843,3 +843,15 @@ func TestFEVMBareTransferTriggersSmartContractLogic(t *testing.T) {
|
||||
// The receive() function emits one log, that's how we know we hit it.
|
||||
require.Len(t, receipt.Logs, 1)
|
||||
}
|
||||
|
||||
func TestFEVMProxyUpgradeable(t *testing.T) {
|
||||
ctx, cancel, client := kit.SetupFEVMTest(t)
|
||||
defer cancel()
|
||||
|
||||
//install transparently upgradeable proxy
|
||||
proxyFilename := "contracts/TransparentUpgradeableProxy.hex"
|
||||
fromAddr, contractAddr := client.EVM().DeployContractFromFilename(ctx, proxyFilename)
|
||||
|
||||
_, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "test()", []byte{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user