[entropy] audit: 3. two step transfer (#1178)
* two step owner transfer * admin two step transfer * remove redundant test
This commit is contained in:
parent
941ee777f0
commit
f0c67c5996
|
@ -9,31 +9,49 @@ import "./EntropyState.sol";
|
||||||
* @dev `Governance` defines a means to enacting changes to the Entropy contract.
|
* @dev `Governance` defines a means to enacting changes to the Entropy contract.
|
||||||
*/
|
*/
|
||||||
abstract contract EntropyGovernance is EntropyState {
|
abstract contract EntropyGovernance is EntropyState {
|
||||||
event AdminSet(address oldAdmin, address newAdmin);
|
|
||||||
event PythFeeSet(uint oldPythFee, uint newPythFee);
|
event PythFeeSet(uint oldPythFee, uint newPythFee);
|
||||||
event DefaultProviderSet(
|
event DefaultProviderSet(
|
||||||
address oldDefaultProvider,
|
address oldDefaultProvider,
|
||||||
address newDefaultProvider
|
address newDefaultProvider
|
||||||
);
|
);
|
||||||
|
|
||||||
function getAdmin() external view returns (address) {
|
event NewAdminProposed(address oldAdmin, address newAdmin);
|
||||||
return _state.admin;
|
event NewAdminAccepted(address oldAdmin, address newAdmin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the address of the proposed admin.
|
||||||
|
*/
|
||||||
|
function proposedAdmin() public view virtual returns (address) {
|
||||||
|
return _state.proposedAdmin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Set the admin of the contract.
|
* @dev Proposes a new admin of the contract. Replaces the proposed admin if there is one.
|
||||||
*
|
* Can only be called by either admin or owner.
|
||||||
* Calls {_authoriseAdminAction}.
|
|
||||||
*
|
|
||||||
* Emits an {AdminSet} event.
|
|
||||||
*/
|
*/
|
||||||
function setAdmin(address newAdmin) external {
|
function proposeAdmin(address newAdmin) public virtual {
|
||||||
_authoriseAdminAction();
|
_authoriseAdminAction();
|
||||||
|
|
||||||
address oldAdmin = _state.admin;
|
_state.proposedAdmin = newAdmin;
|
||||||
_state.admin = newAdmin;
|
emit NewAdminProposed(_state.admin, newAdmin);
|
||||||
|
}
|
||||||
|
|
||||||
emit AdminSet(oldAdmin, newAdmin);
|
/**
|
||||||
|
* @dev The proposed admin accepts the admin transfer.
|
||||||
|
*/
|
||||||
|
function acceptAdmin() external {
|
||||||
|
if (msg.sender != _state.proposedAdmin)
|
||||||
|
revert EntropyErrors.Unauthorized();
|
||||||
|
|
||||||
|
address oldAdmin = _state.admin;
|
||||||
|
_state.admin = msg.sender;
|
||||||
|
|
||||||
|
_state.proposedAdmin = address(0);
|
||||||
|
emit NewAdminAccepted(oldAdmin, msg.sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAdmin() external view returns (address) {
|
||||||
|
return _state.admin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -34,6 +34,9 @@ contract EntropyInternalStructs {
|
||||||
mapping(bytes32 => EntropyStructs.Request) requestsOverflow;
|
mapping(bytes32 => EntropyStructs.Request) requestsOverflow;
|
||||||
// Mapping from randomness providers to information about each them.
|
// Mapping from randomness providers to information about each them.
|
||||||
mapping(address => EntropyStructs.ProviderInfo) providers;
|
mapping(address => EntropyStructs.ProviderInfo) providers;
|
||||||
|
// proposedAdmin is the new admin's account address proposed by either the owner or the current admin.
|
||||||
|
// If there is no pending transfer request, this value will hold `address(0)`.
|
||||||
|
address proposedAdmin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ pragma solidity ^0.8.0;
|
||||||
|
|
||||||
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
||||||
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
||||||
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
|
||||||
import "@pythnetwork/entropy-sdk-solidity/EntropyErrors.sol";
|
import "@pythnetwork/entropy-sdk-solidity/EntropyErrors.sol";
|
||||||
|
|
||||||
import "./EntropyGovernance.sol";
|
import "./EntropyGovernance.sol";
|
||||||
|
@ -11,7 +11,7 @@ import "./Entropy.sol";
|
||||||
|
|
||||||
contract EntropyUpgradable is
|
contract EntropyUpgradable is
|
||||||
Initializable,
|
Initializable,
|
||||||
OwnableUpgradeable,
|
Ownable2StepUpgradeable,
|
||||||
UUPSUpgradeable,
|
UUPSUpgradeable,
|
||||||
Entropy,
|
Entropy,
|
||||||
EntropyGovernance
|
EntropyGovernance
|
||||||
|
@ -42,7 +42,7 @@ contract EntropyUpgradable is
|
||||||
);
|
);
|
||||||
|
|
||||||
// We need to transfer the ownership from deployer to the new owner
|
// We need to transfer the ownership from deployer to the new owner
|
||||||
transferOwnership(owner);
|
_transferOwnership(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensures the contract cannot be uninitialized and taken over.
|
/// Ensures the contract cannot be uninitialized and taken over.
|
||||||
|
|
|
@ -24,6 +24,8 @@ contract EntropyAuthorized is Test, EntropyTestUtils {
|
||||||
address public provider1 = address(4);
|
address public provider1 = address(4);
|
||||||
address public provider2 = address(5);
|
address public provider2 = address(5);
|
||||||
|
|
||||||
|
address public owner2 = address(6);
|
||||||
|
|
||||||
uint128 pythFeeInWei = 7;
|
uint128 pythFeeInWei = 7;
|
||||||
|
|
||||||
function setUp() public {
|
function setUp() public {
|
||||||
|
@ -39,24 +41,6 @@ contract EntropyAuthorized is Test, EntropyTestUtils {
|
||||||
random.initialize(owner, admin, pythFeeInWei, provider1, false);
|
random.initialize(owner, admin, pythFeeInWei, provider1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testSetAdminByAdmin() public {
|
|
||||||
vm.prank(admin);
|
|
||||||
random.setAdmin(admin2);
|
|
||||||
assertEq(random.getAdmin(), admin2);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testSetAdminByOwner() public {
|
|
||||||
vm.prank(owner);
|
|
||||||
random.setAdmin(admin2);
|
|
||||||
assertEq(random.getAdmin(), admin2);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testExpectRevertSetAdminByUnauthorized() public {
|
|
||||||
vm.expectRevert(EntropyErrors.Unauthorized.selector);
|
|
||||||
vm.prank(admin2);
|
|
||||||
random.setAdmin(admin);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testSetPythFeeByAdmin() public {
|
function testSetPythFeeByAdmin() public {
|
||||||
vm.prank(admin);
|
vm.prank(admin);
|
||||||
random.setPythFee(10);
|
random.setPythFee(10);
|
||||||
|
@ -120,4 +104,74 @@ contract EntropyAuthorized is Test, EntropyTestUtils {
|
||||||
vm.prank(owner);
|
vm.prank(owner);
|
||||||
random.upgradeTo(address(randomDifferentMagic));
|
random.upgradeTo(address(randomDifferentMagic));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testExpectRevertRequestOwnershipTransferByUnauthorized() public {
|
||||||
|
vm.expectRevert("Ownable: caller is not the owner");
|
||||||
|
vm.prank(provider1);
|
||||||
|
random.transferOwnership(owner2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testExpectRevertRequestOwnershipTransferByAdmin() public {
|
||||||
|
vm.expectRevert("Ownable: caller is not the owner");
|
||||||
|
vm.prank(admin);
|
||||||
|
random.transferOwnership(owner2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testRequestAndAcceptOwnershipTransfer() public {
|
||||||
|
vm.prank(owner);
|
||||||
|
random.transferOwnership(owner2);
|
||||||
|
assertEq(random.pendingOwner(), owner2);
|
||||||
|
|
||||||
|
vm.prank(owner2);
|
||||||
|
random.acceptOwnership();
|
||||||
|
assertEq(random.owner(), owner2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testRequestAndAcceptOwnershipTransferUnauthorizedAccept() public {
|
||||||
|
vm.prank(owner);
|
||||||
|
random.transferOwnership(owner2);
|
||||||
|
assertEq(random.pendingOwner(), owner2);
|
||||||
|
|
||||||
|
vm.prank(admin);
|
||||||
|
vm.expectRevert("Ownable2Step: caller is not the new owner");
|
||||||
|
random.acceptOwnership();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testProposeAdminByOwner() public {
|
||||||
|
vm.prank(owner);
|
||||||
|
random.proposeAdmin(admin2);
|
||||||
|
|
||||||
|
assertEq(random.proposedAdmin(), admin2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testProposeAdminByAdmin() public {
|
||||||
|
vm.prank(admin);
|
||||||
|
random.proposeAdmin(admin2);
|
||||||
|
|
||||||
|
assertEq(random.proposedAdmin(), admin2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testProposeAdminByUnauthorized() public {
|
||||||
|
vm.expectRevert(EntropyErrors.Unauthorized.selector);
|
||||||
|
random.proposeAdmin(admin2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testAcceptAdminByPropsed() public {
|
||||||
|
vm.prank(owner);
|
||||||
|
random.proposeAdmin(admin2);
|
||||||
|
|
||||||
|
vm.prank(admin2);
|
||||||
|
random.acceptAdmin();
|
||||||
|
|
||||||
|
assertEq(random.getAdmin(), admin2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testAcceptAdminByUnauthorized() public {
|
||||||
|
vm.prank(owner);
|
||||||
|
random.proposeAdmin(admin2);
|
||||||
|
|
||||||
|
vm.prank(provider1);
|
||||||
|
vm.expectRevert(EntropyErrors.Unauthorized.selector);
|
||||||
|
random.acceptAdmin();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue