Skip to content

Commit

Permalink
fix(governance): replace multisig to operator (#185)
Browse files Browse the repository at this point in the history
  • Loading branch information
ahramy authored Sep 6, 2024
1 parent b53530c commit febcd9f
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 121 deletions.
10 changes: 5 additions & 5 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Multisig operations demand multi-signatory authorization for proposal execution.

## Axelar Service Governance

Building upon the Interchain Governance Contract, the Service Governance Contract is specifically designed to manage operations that require coordination. By incorporating `MultisigBase`, it introduces the functionality to approve, execute, and cancel multisig proposals, in addition to schedule and cancel TimeLock proposals. This is intended to be used as the owner for services such as the Interchain token service contract, allowing Axelar governance to manage it.
Building upon the Interchain Governance Contract, the Service Governance Contract is specifically designed to manage operations that require coordination. Axelar Service Governance introduces the functionality to approve, execute, and cancel operator proposals, in addition to schedule and cancel TimeLock proposals. This is intended to be used as the owner for services such as the Interchain token service contract, allowing Axelar governance to manage it.

### Service Governance Operations

Expand All @@ -66,10 +66,10 @@ The contract orchestrates four governance operations:

- **Cancel TimeLock Proposal**: Again, similar to Interchain Governance, it cancels an existing governance proposal.

- **Approve Multisig Proposal**: This function enables multisig proposal approval by setting the proposal's approval status to true. It resets any previous voting and signals successful approval via a MultisigApproved event.
- **Approve Operator Proposal**: This function enables operator proposal approval by setting the proposal's approval status to true. It resets any previous voting and signals successful approval via a OperatorProposalApproved event.

- **Cancel Multisig Approval**: Cancels an approved multisig proposal, setting the approval status of the proposal to false and indicating successful cancellation through a `MultisigCancelled` event.
- **Cancel Operator Approval**: Cancels an approved operator proposal, setting the approval status of the proposal to false and indicating successful cancellation through a `OperatorProposalCancelled` event.

### Secure Execution of Multisig Proposals
### Secure Execution of Operator Proposals

Each time a new multisig proposal receives approval from governance, the multisig voting count is reset to 0. This ensures that any previous votes on similar proposals will not affect the new proposal. When a multisig proposal gathers the required number of signatory approvals, it becomes ready for execution. Before execution, the contract verifies the proposal's approval status. If the status is set to false, the transaction is reverted. Once executed successfully, the approval status of the proposal is reset, and a MultisigExecuted event gets emitted.
Each time a new operator proposal receives approval from governance, the operator voting count is reset to 0. This ensures that any previous votes on similar proposals will not affect the new proposal. When a operator proposal gathers the required number of signatory approvals, it becomes ready for execution. Before execution, the contract verifies the proposal's approval status. If the status is set to false, the transaction is reverted. Once executed successfully, the approval status of the proposal is reset, and a OperatorProposalExecuted event gets emitted.
70 changes: 35 additions & 35 deletions contracts/governance/AxelarServiceGovernance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,27 @@ import { InterchainGovernance } from './InterchainGovernance.sol';
/**
* @title AxelarServiceGovernance Contract
* @dev This contract is part of the Axelar Governance system, it inherits the Interchain Governance contract
* with added functionality to approve and execute multisig proposals.
* with added functionality to approve and execute operator proposals.
*/
contract AxelarServiceGovernance is InterchainGovernance, IAxelarServiceGovernance {
enum ServiceGovernanceCommand {
ScheduleTimeLockProposal,
CancelTimeLockProposal,
ApproveMultisigProposal,
CancelMultisigApproval
ApproveOperatorProposal,
CancelOperatorApproval
}

address public multisig;
address public operator;

mapping(bytes32 => bool) public multisigApprovals;
mapping(bytes32 => bool) public operatorApprovals;

modifier onlyMultisig() {
if (msg.sender != multisig) revert NotAuthorized();
modifier onlyOperator() {
if (msg.sender != operator) revert NotAuthorized();
_;
}

modifier onlyMultisigOrSelf() {
if (msg.sender != multisig && msg.sender != address(this)) revert NotAuthorized();
modifier onlyOperatorOrSelf() {
if (msg.sender != operator && msg.sender != address(this)) revert NotAuthorized();
_;
}

Expand All @@ -38,67 +38,67 @@ contract AxelarServiceGovernance is InterchainGovernance, IAxelarServiceGovernan
* @param governanceChain_ The name of the governance chain
* @param governanceAddress_ The address of the governance contract
* @param minimumTimeDelay The minimum time delay for timelock operations
* @param multisig_ The multisig contract address
* @param operator_ The operator address
*/
constructor(
address gateway_,
string memory governanceChain_,
string memory governanceAddress_,
uint256 minimumTimeDelay,
address multisig_
address operator_
) InterchainGovernance(gateway_, governanceChain_, governanceAddress_, minimumTimeDelay) {
if (multisig_ == address(0)) revert InvalidMultisigAddress();
multisig = multisig_;
if (operator_ == address(0)) revert InvalidOperator();
operator = operator_;
}

/**
* @notice Returns whether a multisig proposal has been approved
* @notice Returns whether an operator proposal has been approved
* @param target The address of the contract targeted by the proposal
* @param callData The call data to be sent to the target contract
* @param nativeValue The amount of native tokens to be sent to the target contract
* @return bool True if the proposal has been approved, False otherwise
*/
function isMultisigProposalApproved(
function isOperatorProposalApproved(
address target,
bytes calldata callData,
uint256 nativeValue
) external view returns (bool) {
return multisigApprovals[_getProposalHash(target, callData, nativeValue)];
return operatorApprovals[_getProposalHash(target, callData, nativeValue)];
}

/**
* @notice Executes a multisig proposal.
* @notice Executes an operator proposal.
* @param target The target address the proposal will call
* @param callData The data that encodes the function and arguments to call on the target contract
* @param nativeValue The value of native token to be sent to the target contract
*/
function executeMultisigProposal(
function executeOperatorProposal(
address target,
bytes calldata callData,
uint256 nativeValue
) external payable onlyMultisig {
) external payable onlyOperator {
bytes32 proposalHash = _getProposalHash(target, callData, nativeValue);

if (!multisigApprovals[proposalHash]) revert NotApproved();
if (!operatorApprovals[proposalHash]) revert NotApproved();

multisigApprovals[proposalHash] = false;
operatorApprovals[proposalHash] = false;

emit MultisigExecuted(proposalHash, target, callData, nativeValue);
emit OperatorProposalExecuted(proposalHash, target, callData, nativeValue);

_call(target, callData, nativeValue);
}

/**
* @notice Transfers the multisig address to a new address
* @dev Only the current multisig or the governance can call this function
* @param newMultisig The new multisig address
* @notice Transfers the operator address to a new address
* @dev Only the current operator or the governance can call this function
* @param newOperator The new operator address
*/
function transferMultisig(address newMultisig) external onlyMultisigOrSelf {
if (newMultisig == address(0)) revert InvalidMultisigAddress();
function transferOperatorship(address newOperator) external onlyOperatorOrSelf {
if (newOperator == address(0)) revert InvalidOperator();

emit MultisigTransferred(multisig, newMultisig);
emit OperatorshipTransferred(operator, newOperator);

multisig = newMultisig;
operator = newOperator;
}

/**
Expand Down Expand Up @@ -128,15 +128,15 @@ contract AxelarServiceGovernance is InterchainGovernance, IAxelarServiceGovernan

emit ProposalCancelled(proposalHash, target, callData, nativeValue, eta);
return;
} else if (commandType == uint256(ServiceGovernanceCommand.ApproveMultisigProposal)) {
multisigApprovals[proposalHash] = true;
} else if (commandType == uint256(ServiceGovernanceCommand.ApproveOperatorProposal)) {
operatorApprovals[proposalHash] = true;

emit MultisigApproved(proposalHash, target, callData, nativeValue);
emit OperatorProposalApproved(proposalHash, target, callData, nativeValue);
return;
} else if (commandType == uint256(ServiceGovernanceCommand.CancelMultisigApproval)) {
multisigApprovals[proposalHash] = false;
} else if (commandType == uint256(ServiceGovernanceCommand.CancelOperatorApproval)) {
operatorApprovals[proposalHash] = false;

emit MultisigCancelled(proposalHash, target, callData, nativeValue);
emit OperatorProposalCancelled(proposalHash, target, callData, nativeValue);
return;
} else {
revert InvalidCommand();
Expand Down
32 changes: 16 additions & 16 deletions contracts/interfaces/IAxelarServiceGovernance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,71 +6,71 @@ import { IInterchainGovernance } from './IInterchainGovernance.sol';

/**
* @title IAxelarServiceGovernance Interface
* @dev This interface extends IInterchainGovernance and IMultisigBase for multisig proposal actions
* @dev This interface extends IInterchainGovernance for operator proposal actions
*/
interface IAxelarServiceGovernance is IInterchainGovernance {
error InvalidMultisigAddress();
error InvalidOperator();
error NotApproved();
error NotAuthorized();

event MultisigApproved(
event OperatorProposalApproved(
bytes32 indexed proposalHash,
address indexed targetContract,
bytes callData,
uint256 nativeValue
);

event MultisigCancelled(
event OperatorProposalCancelled(
bytes32 indexed proposalHash,
address indexed targetContract,
bytes callData,
uint256 nativeValue
);

event MultisigExecuted(
event OperatorProposalExecuted(
bytes32 indexed proposalHash,
address indexed targetContract,
bytes callData,
uint256 nativeValue
);

event MultisigTransferred(address indexed oldMultisig, address indexed newMultisig);
event OperatorshipTransferred(address indexed oldOperator, address indexed newOperator);

/**
* @notice Returns whether a multisig proposal has been approved
* @notice Returns whether an operator proposal has been approved
* @param proposalHash The hash of the proposal
* @return bool True if the proposal has been approved, False otherwise
*/
function multisigApprovals(bytes32 proposalHash) external view returns (bool);
function operatorApprovals(bytes32 proposalHash) external view returns (bool);

/**
* @notice Returns whether a multisig proposal has been approved
* @notice Returns whether an operator proposal has been approved
* @param target The address of the contract targeted by the proposal
* @param callData The call data to be sent to the target contract
* @param nativeValue The amount of native tokens to be sent to the target contract
* @return bool True if the proposal has been approved, False otherwise
*/
function isMultisigProposalApproved(
function isOperatorProposalApproved(
address target,
bytes calldata callData,
uint256 nativeValue
) external view returns (bool);

/**
* @notice Executes a multisig proposal
* @notice Executes an operator proposal
* @param targetContract The target address the proposal will call
* @param callData The data that encodes the function and arguments to call on the target contract
*/
function executeMultisigProposal(
function executeOperatorProposal(
address targetContract,
bytes calldata callData,
uint256 value
) external payable;

/**
* @notice Transfers the multisig address to a new address
* @dev Only the current multisig or the governance can call this function
* @param newMultisig The new multisig address
* @notice Transfers the operator address to a new address
* @dev Only the current operator or the governance can call this function
* @param newOperator The new operator address
*/
function transferMultisig(address newMultisig) external;
function transferOperatorship(address newOperator) external;
}
Loading

0 comments on commit febcd9f

Please sign in to comment.