Skip to content

Commit

Permalink
[ENHANCEMENT] Added Docs and Gas Optimisation for Multisig [DONE]
Browse files Browse the repository at this point in the history
  • Loading branch information
Ronnieraj37 committed Nov 11, 2024
1 parent 29fad97 commit cb824af
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 69 deletions.
4 changes: 2 additions & 2 deletions solidity/src/AccessRegistry/AccessRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ abstract contract AccessRegistry is Context, SuperAdmin2Step, FallbackAdmin2Step
}
}

function removeSigner(address _signer) external virtual onlySuperAdmin notZeroAddress(_signer) {
function removeSigner(address _signer) external virtual onlySuperAdmin {
if (!isSigner(_signer)) revert NonExistingSigner();
if (_signer == superAdmin()) revert SuperAdminCannotRemoved();
if (_signer == _msgSender()) revert SuperAdminCannotRemoved();
if (totalSigners() == 1) revert WalletCannotBeSignerLess();
// require(totalSigners() > 1, "ACL::wallet cannot be ownerless");
signers[_signer] = false;
Expand Down
13 changes: 7 additions & 6 deletions solidity/src/MultiSigWallet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ contract MultiSigWallet is Initializable, AccessRegistry, UUPSUpgradeable {

// ========== STRUCTS ==========
struct Transaction {
address proposer;
bytes4 selector; // The function call data
bytes params;
uint256 proposedAt; // When the transaction was proposed
uint256 firstSignAt; // When the first signer approved
uint256 approvals; // Number of approvals received
address proposer;
bytes4 selector; // The function call data
TransactionState state; //state of the transaction(pending,)
bool isFallbackAdmin; // Whether this was proposed by fallback admin
bytes params;
}

// ========== STATE ==========
Expand Down Expand Up @@ -193,6 +193,7 @@ contract MultiSigWallet is Initializable, AccessRegistry, UUPSUpgradeable {
* @param amount The amount of tokens to mint
* @return The transaction ID
*/
/// TODO: add modifiers for amount != 0
function createMintTransaction(address to, uint256 amount) external virtual notZeroAddress(to) returns (uint256) {
return _createStandardTransaction(MINT_SELECTOR, abi.encode(to, amount));
}
Expand Down Expand Up @@ -304,9 +305,9 @@ contract MultiSigWallet is Initializable, AccessRegistry, UUPSUpgradeable {
transactions[txId].selector = _selector;
transactions[txId].params = _params;
transactions[txId].proposedAt = block.timestamp;
transactions[txId].firstSignAt = 0;
transactions[txId].approvals = 0;
transactions[txId].state = TransactionState.Pending;
// transactions[txId].firstSignAt = 0; //already 0 when creating
// transactions[txId].approvals = 0;
// transactions[txId].state = TransactionState.Pending;
transactions[txId].isFallbackAdmin = isFallbackAdmin;

emit TransactionProposed(txId, _msgSender(), block.timestamp);
Expand Down
35 changes: 14 additions & 21 deletions solidity/src/OptimisticStack/MultiSigL2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ contract MultiSigWallet is Initializable, AccessRegistry, UUPSUpgradeable {
uint256 private constant FALLBACK_ADMIN_WINDOW = 72 hours;
uint256 private constant APPROVAL_THRESHOLD = 60; // 60% of signers must approve

///@dev bytes4(keccak256("mint(address,uint256)"))
bytes4 private constant MINT_SELECTOR = 0x40c10f19;
///@dev bytes4(keccak256("authorizeBridge(address)"))
bytes4 private constant AUTHORIZE_BRIDGE_SELECTOR = 0xe53595ee;

///@dev bytes4(keccak256("burn(address,uint256)"))
bytes4 private constant BURN_SELECTOR = 0x9dc29fac;
///@dev bytes4(keccak256("revokeBridgeAuthorization(address)"))
bytes4 private constant REVOKE_BRDIGE_AUTHORIZE_SELECTOR = 0xaf84ce93;

///@dev bytes4(keccak256("updateOperationalState(uint8)"))
bytes4 private constant PAUSE_STATE_SELECTOR = 0x50f20190;
Expand Down Expand Up @@ -106,8 +106,8 @@ contract MultiSigWallet is Initializable, AccessRegistry, UUPSUpgradeable {
_initializeAccessRegistry(_superAdmin, _fallbackAdmin);
// Set up function permissions
// Fallback admin can only mint and burn
fallbackAdminFunctions[MINT_SELECTOR] = true;
fallbackAdminFunctions[BURN_SELECTOR] = true;
fallbackAdminFunctions[AUTHORIZE_BRIDGE_SELECTOR] = true;
fallbackAdminFunctions[REVOKE_BRDIGE_AUTHORIZE_SELECTOR] = true;

// Signers can pause/unpause and manage blacklist
signerFunctions[PAUSE_STATE_SELECTOR] = true;
Expand Down Expand Up @@ -188,28 +188,21 @@ contract MultiSigWallet is Initializable, AccessRegistry, UUPSUpgradeable {
}

/**
* @notice Creates a mint transaction
* @param to The address to mint tokens to
* @param amount The amount of tokens to mint
* @notice Creates a transaction to Authorize Bridge
* @param bridge The address to authorize bridges
* @return The transaction ID
*/
function createMintTransaction(address to, uint256 amount) external virtual notZeroAddress(to) returns (uint256) {
return _createStandardTransaction(MINT_SELECTOR, abi.encode(to, amount));
function createAuthorizeBridgeTransaction(address bridge) external virtual returns (uint256) {
return _createStandardTransaction(AUTHORIZE_BRIDGE_SELECTOR, abi.encode(bridge));
}

/**
* @notice Creates a burn transaction
* @param from The address from which to burn tokens
* @param amount The amount of tokens to burn
* @notice Creates a transaction to Revoke Authorization of Bridge
* @param bridge The address from which to revoke authorization
* @return The transaction ID
*/
function createBurnTransaction(address from, uint256 amount)
external
virtual
notZeroAddress(from)
returns (uint256)
{
return _createStandardTransaction(BURN_SELECTOR, abi.encode(from, amount));
function createRevokeBridgeAuthorizationTransaction(address bridge) external virtual returns (uint256) {
return _createStandardTransaction(REVOKE_BRDIGE_AUTHORIZE_SELECTOR, abi.encode(bridge));
}

/**
Expand Down
80 changes: 40 additions & 40 deletions solidity/src/OptimisticStack/OptimismMintableERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,12 @@ contract OptimismMintableERC20 is IOptimismMintableERC20, ILegacyMintableERC20,
event Burn(address indexed account, uint256 amount);

/**
* @notice A modifier that restricts function access to authorized bridges only
*/
modifier onlyAuthorizedBridge() {
require(isAuthorizedBridge(_msgSender()), "OptimismMintableERC20: caller is not an authorized bridge");
_;
}
* @notice A modifier that restricts function access to authorized bridges only
*/
modifier onlyAuthorizedBridge() {
require(isAuthorizedBridge(_msgSender()), "OptimismMintableERC20: caller is not an authorized bridge");
_;
}

/**
* @custom:semver 1.0.0
Expand All @@ -96,41 +96,40 @@ modifier onlyAuthorizedBridge() {
authorizedBridges[_bridge] = true;
}


/**
* @notice Authorizes a new bridge to mint and burn tokens
* @dev Can only be called by contract admin/owner
* @param _bridge Address of the bridge to authorize
*/
function authorizeBridge(address _bridge) external onlyMultiSig {
require(_bridge != address(0), "OptimismMintableERC20: bridge cannot be zero address");
require(!authorizedBridges[_bridge], "OptimismMintableERC20: bridge already authorized");

authorizedBridges[_bridge] = true;
emit BridgeAuthorized(_bridge, _msgSender());
}
/**
* @notice Authorizes a new bridge to mint and burn tokens
* @dev Can only be called by contract admin/owner
* @param _bridge Address of the bridge to authorize
*/
function authorizeBridge(address _bridge) external onlyMultiSig {
require(_bridge != address(0), "OptimismMintableERC20: bridge cannot be zero address");
require(!authorizedBridges[_bridge], "OptimismMintableERC20: bridge already authorized");

/**
* @notice Revokes a bridge's authorization to mint and burn tokens
* @dev Can only be called by contract admin/owner
* @param _bridge Address of the bridge to unauthorized
*/
function revokeBridgeAuthorization(address _bridge) external onlyMultiSig {
require(_bridge != address(0), "OptimismMintableERC20: bridge cannot be zero address");
require(authorizedBridges[_bridge], "OptimismMintableERC20: bridge not authorized");

authorizedBridges[_bridge] = false;
emit BridgeUnauthorized(_bridge, _msgSender());
}
authorizedBridges[_bridge] = true;
emit BridgeAuthorized(_bridge, _msgSender());
}

/**
* @notice Checks if a given address is an authorized bridge
* @param _bridge Address to check for bridge authorization
* @return bool True if the address is an authorized bridge, false otherwise
*/
function isAuthorizedBridge(address _bridge) public view returns(bool) {
return authorizedBridges[_bridge];
}
/**
* @notice Revokes a bridge's authorization to mint and burn tokens
* @dev Can only be called by contract admin/owner
* @param _bridge Address of the bridge to unauthorized
*/
function revokeBridgeAuthorization(address _bridge) external onlyMultiSig {
// require(_bridge != address(0), "OptimismMintableERC20: bridge cannot be zero address");
require(authorizedBridges[_bridge], "OptimismMintableERC20: bridge not authorized");

authorizedBridges[_bridge] = false;
emit BridgeUnauthorized(_bridge, _msgSender());
}

/**
* @notice Checks if a given address is an authorized bridge
* @param _bridge Address to check for bridge authorization
* @return bool True if the address is an authorized bridge, false otherwise
*/
function isAuthorizedBridge(address _bridge) public view returns (bool) {
return authorizedBridges[_bridge];
}

/**
* @notice Allows the StandardBridge on this network to mint tokens.
Expand Down Expand Up @@ -262,11 +261,12 @@ function isAuthorizedBridge(address _bridge) public view returns(bool) {
function l1Token() public view returns (address) {
return REMOTE_TOKEN;
}

/// TODO: Repeated fnc Here
/**
* @custom:legacy
* @notice Legacy getter for REMOTE_TOKEN.
*/

function remoteToken() public view returns (address) {
return REMOTE_TOKEN;
}
Expand Down
79 changes: 79 additions & 0 deletions solidity/src/OptimisticStack/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# OptimismMintableERC20

## Overview

The `OptimismMintableERC20` smart contract is a specialized ERC20 token implementation designed for use in cross-chain scenarios, particularly as a bridgeable token on the Base Layer 2 network. This contract allows for the seamless minting and burning of tokens across the Layer 1 (L1) and Layer 2 (L2) networks, enabling cross-chain token transfers by mirroring the token on both chains.

The contract is fully compliant with ERC20 standards and includes extensions for bridge authorization, pausing, blacklisting, and token recovery, making it versatile and secure for multi-chain applications.

## Key Features

- **Cross-Chain Minting and Burning**: Supports token minting and burning via authorized bridge contracts, allowing token representation on both L1 and L2 networks.
- **Bridge Authorization Management**: Only authorized bridge addresses are permitted to mint and burn tokens, enhancing security.
- **Pausable Operations**: Contract functions can be partially or fully paused by a multisig-admin, adding control over operations during specific situations.
- **Blacklist Functionality**: Ability to blacklist certain addresses, preventing them from performing token transfers.
- **Token Recovery**: Admins can recover any ERC20 tokens accidentally sent to the contract.
- **Versioning**: Uses `Semver` for tracking contract version.

## System Architecture

### Role Hierarchy

1. **Multisig Admin**
- Grants and revokes bridge authorization
- Manages contract state (pause, unpause)
- Recovers accidentally sent tokens
- Controls blacklist management

2. **Authorized Bridges**
- Can mint and burn tokens on L2 on behalf of L1 operations

### Key Components

#### 1. Cross-Chain Functionality

The contract utilizes `REMOTE_TOKEN` to store the address of the equivalent L1 or L2 token. Only authorized bridge contracts can initiate cross-chain transactions by calling the `mint` and `burn` functions. This ensures secure and controlled token movement across chains.

#### 2. Pausable and Blacklist Features

The contract includes both **pause** and **blacklist** functionality:
- **Pause Mechanism**: Admin can set contract status to active, partially paused, or fully paused, restricting operations under specific conditions.
- **Blacklist Mechanism**: Specific addresses can be blacklisted, preventing them from transferring or approving tokens, adding another layer of security.

#### 3. Token Rescue

A token recovery function is provided, allowing the multisig admin to recover any mistakenly sent ERC20 tokens from this contract.

## Contract Functions

### Core Functions

- **authorizeBridge(address _bridge)**: Adds a bridge to the list of authorized bridges allowed to mint and burn tokens. Callable only by the multisig admin.
- **revokeBridgeAuthorization(address _bridge)**: Removes a bridge from the list of authorized bridges, disabling its ability to mint and burn tokens.
- **mint(address _to, uint256 _amount)**: Mints tokens to a specified address. Only callable by an authorized bridge during active or partially paused states, and only if the recipient is not blacklisted.
- **burn(address _from, uint256 _amount)**: Burns tokens from a specified address. Only callable by an authorized bridge during active or partially paused states.
- **transfer** and **transferFrom**: Modified to include blacklist checks, ensuring blacklisted addresses cannot send or receive tokens.

### Helper Functions

- **isAuthorizedBridge(address _bridge)**: Verifies if a specific bridge address is authorized to mint or burn tokens.
- **recoverToken(address asset, address to)**: Allows the admin to recover tokens accidentally sent to this contract, transferring them to a specified recipient address.
- **supportsInterface(bytes4 _interfaceId)**: Implements ERC165 to specify the interfaces supported by this contract.
- **updateOperationalState(uint8 newState)**: Updates the contract's operational state (active, partial pause, or full pause), managed by the multisig admin.

### Events

- **BridgeAuthorized**: Emitted when a bridge is authorized for minting and burning.
- **BridgeUnauthorized**: Emitted when a bridge’s authorization is revoked.
- **Mint**: Emitted whenever tokens are minted to an address.
- **Burn**: Emitted whenever tokens are burned from an address.
- **Token_Rescued**: Emitted when tokens are recovered from the contract by the admin.

## Usage Example

1. **Bridge Authorization**: The admin authorizes a new bridge to handle minting and burning.
2. **Cross-Chain Minting**: An authorized bridge mints tokens on L2 based on L1 operations.
3. **Pausing**: If necessary, the admin can partially or fully pause contract operations.
4. **Blacklist Management**: Specific addresses can be blacklisted, preventing them from transferring tokens.
5. **Token Recovery**: Admin can recover mistakenly sent ERC20 tokens to the contract.

0 comments on commit cb824af

Please sign in to comment.