217 lines
6.2 KiB
Markdown
217 lines
6.2 KiB
Markdown
|
|
# Defender Access Control Integration
|
||
|
|
|
||
|
|
**Date**: 2025-12-11
|
||
|
|
**Reference**: [OpenZeppelin Access Control Documentation](https://docs.openzeppelin.com/contracts/5.x/access-control#access-management)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📚 Current Implementation
|
||
|
|
|
||
|
|
### MainnetTether & TransactionMirror
|
||
|
|
|
||
|
|
Both contracts use a **simple admin pattern** (similar to OpenZeppelin's `Ownable`):
|
||
|
|
|
||
|
|
```solidity
|
||
|
|
address public admin;
|
||
|
|
bool public paused;
|
||
|
|
|
||
|
|
modifier onlyAdmin() {
|
||
|
|
require(msg.sender == admin, "only admin");
|
||
|
|
_;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
This is equivalent to OpenZeppelin's `Ownable` pattern, where:
|
||
|
|
- A single `admin` address controls all administrative functions
|
||
|
|
- Functions are protected with `onlyAdmin` modifier
|
||
|
|
- Admin can be changed via `setAdmin()` function
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔐 Defender Integration
|
||
|
|
|
||
|
|
### Using Defender as Admin
|
||
|
|
|
||
|
|
According to OpenZeppelin's documentation, **"a contract can also be the owner of another one"**. This means:
|
||
|
|
|
||
|
|
✅ **Defender Relayer Address** can be set as the `admin` of our contracts
|
||
|
|
|
||
|
|
This provides:
|
||
|
|
- Automated transaction execution
|
||
|
|
- Gas price optimization
|
||
|
|
- Transaction monitoring and alerts
|
||
|
|
- Multi-signature support (via Defender)
|
||
|
|
- Rate limiting and security policies
|
||
|
|
- Non-custodial key management
|
||
|
|
|
||
|
|
### Current Setup
|
||
|
|
|
||
|
|
Our deployment scripts are configured to:
|
||
|
|
1. Check for `DEFENDER_ADMIN` environment variable first
|
||
|
|
2. Fall back to `TETHER_ADMIN`/`MIRROR_ADMIN` if Defender not set
|
||
|
|
3. Deploy contracts with Defender address as `admin`
|
||
|
|
|
||
|
|
```solidity
|
||
|
|
// Deployment script pattern
|
||
|
|
address admin = vm.envOr("DEFENDER_ADMIN", vm.envOr("TETHER_ADMIN", address(0)));
|
||
|
|
MainnetTether tether = new MainnetTether(admin);
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🚀 Advanced Access Control Options
|
||
|
|
|
||
|
|
### Option 1: Current Simple Admin (Recommended for Now)
|
||
|
|
|
||
|
|
**Status**: ✅ **Currently Implemented**
|
||
|
|
|
||
|
|
- Simple and effective
|
||
|
|
- Single Defender address as admin
|
||
|
|
- All admin functions controlled by Defender
|
||
|
|
- Easy to understand and audit
|
||
|
|
|
||
|
|
**Use Case**: Perfect for contracts with a single administrative role
|
||
|
|
|
||
|
|
### Option 2: AccessControl (Role-Based)
|
||
|
|
|
||
|
|
If you need more granular permissions in the future, you could upgrade to OpenZeppelin's `AccessControl`:
|
||
|
|
|
||
|
|
```solidity
|
||
|
|
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
|
||
|
|
|
||
|
|
contract MainnetTether is AccessControl {
|
||
|
|
bytes32 public constant ANCHOR_ROLE = keccak256("ANCHOR_ROLE");
|
||
|
|
bytes32 public constant PAUSE_ROLE = keccak256("PAUSE_ROLE");
|
||
|
|
|
||
|
|
constructor(address defenderAdmin) {
|
||
|
|
_grantRole(DEFAULT_ADMIN_ROLE, defenderAdmin);
|
||
|
|
}
|
||
|
|
|
||
|
|
function anchorStateProof(...) external onlyRole(ANCHOR_ROLE) {
|
||
|
|
// Only accounts with ANCHOR_ROLE can call this
|
||
|
|
}
|
||
|
|
|
||
|
|
function pause() external onlyRole(PAUSE_ROLE) {
|
||
|
|
// Only accounts with PAUSE_ROLE can call this
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Benefits**:
|
||
|
|
- Multiple roles (e.g., `ANCHOR_ROLE`, `PAUSE_ROLE`)
|
||
|
|
- Different permissions for different functions
|
||
|
|
- Defender can be `DEFAULT_ADMIN_ROLE` to manage all roles
|
||
|
|
|
||
|
|
**When to Use**: If you need different accounts for different functions
|
||
|
|
|
||
|
|
### Option 3: AccessManager (Centralized Permission Management)
|
||
|
|
|
||
|
|
For complex protocols with multiple contracts, OpenZeppelin's `AccessManager` provides centralized permission management:
|
||
|
|
|
||
|
|
```solidity
|
||
|
|
import {AccessManaged} from "@openzeppelin/contracts/access/manager/AccessManaged.sol";
|
||
|
|
|
||
|
|
contract MainnetTether is AccessManaged {
|
||
|
|
constructor(address accessManager) AccessManaged(accessManager) {}
|
||
|
|
|
||
|
|
function anchorStateProof(...) public restricted {
|
||
|
|
// Access controlled by AccessManager
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Benefits**:
|
||
|
|
- Centralized permission management across all contracts
|
||
|
|
- Execution delays for security
|
||
|
|
- Role-based access with delays
|
||
|
|
- Defender can be the initial admin
|
||
|
|
|
||
|
|
**When to Use**: For complex multi-contract systems
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📋 Defender Configuration
|
||
|
|
|
||
|
|
### Setting Up Defender
|
||
|
|
|
||
|
|
1. **Create Defender Relayer**
|
||
|
|
- Go to [OpenZeppelin Defender](https://defender.openzeppelin.com/)
|
||
|
|
- Create a new relayer
|
||
|
|
- Copy the relayer address
|
||
|
|
|
||
|
|
2. **Configure Environment**
|
||
|
|
```bash
|
||
|
|
DEFENDER_ADMIN=<defender_relayer_address>
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Deploy Contracts**
|
||
|
|
- Contracts will use Defender address as admin
|
||
|
|
- All admin functions can be executed via Defender
|
||
|
|
|
||
|
|
4. **Set Up Defender Actions**
|
||
|
|
- Create Defender actions for `anchorStateProof()`
|
||
|
|
- Create Defender actions for `mirrorTransaction()`
|
||
|
|
- Configure Defender policies and rate limits
|
||
|
|
|
||
|
|
### Defender Benefits
|
||
|
|
|
||
|
|
According to OpenZeppelin's best practices:
|
||
|
|
- ✅ **Automated Execution**: Defender can execute transactions automatically
|
||
|
|
- ✅ **Gas Optimization**: Defender optimizes gas prices
|
||
|
|
- ✅ **Monitoring**: Real-time alerts for contract events
|
||
|
|
- ✅ **Security**: Multi-signature support and rate limiting
|
||
|
|
- ✅ **Non-Custodial**: Keys managed securely by Defender
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔄 Migration Path
|
||
|
|
|
||
|
|
### Current → AccessControl (If Needed)
|
||
|
|
|
||
|
|
If you need role-based access control later:
|
||
|
|
|
||
|
|
1. Deploy new version with `AccessControl`
|
||
|
|
2. Grant `DEFAULT_ADMIN_ROLE` to Defender
|
||
|
|
3. Set up specific roles (e.g., `ANCHOR_ROLE`, `PAUSE_ROLE`)
|
||
|
|
4. Migrate admin functions to use `onlyRole` modifiers
|
||
|
|
|
||
|
|
### Current → AccessManager (If Needed)
|
||
|
|
|
||
|
|
For centralized permission management:
|
||
|
|
|
||
|
|
1. Deploy `AccessManager` contract
|
||
|
|
2. Set Defender as initial admin
|
||
|
|
3. Update contracts to inherit `AccessManaged`
|
||
|
|
4. Configure roles and permissions in AccessManager
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## ✅ Recommendation
|
||
|
|
|
||
|
|
**For MainnetTether and TransactionMirror**:
|
||
|
|
|
||
|
|
✅ **Keep the current simple admin pattern** with Defender as admin
|
||
|
|
|
||
|
|
**Reasons**:
|
||
|
|
- Simple and effective for single-admin contracts
|
||
|
|
- Defender provides all necessary security features
|
||
|
|
- Easy to understand and audit
|
||
|
|
- No need for complex role-based access control
|
||
|
|
- Can upgrade to `AccessControl` or `AccessManager` later if needed
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📚 References
|
||
|
|
|
||
|
|
- [OpenZeppelin Access Control Documentation](https://docs.openzeppelin.com/contracts/5.x/access-control#access-management)
|
||
|
|
- [OpenZeppelin Defender Documentation](https://docs.openzeppelin.com/defender)
|
||
|
|
- [Ownable Pattern](https://docs.openzeppelin.com/contracts/5.x/access-control#ownership-and-ownable)
|
||
|
|
- [AccessControl Pattern](https://docs.openzeppelin.com/contracts/5.x/access-control#role-based-access-control)
|
||
|
|
- [AccessManager Pattern](https://docs.openzeppelin.com/contracts/5.x/access-control#access-management)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Last Updated**: 2025-12-11
|
||
|
|
**Status**: Current Implementation Aligned with OpenZeppelin Best Practices
|
||
|
|
|