Add Oracle Aggregator and CCIP Integration
- Introduced Aggregator.sol for Chainlink-compatible oracle functionality, including round-based updates and access control. - Added OracleWithCCIP.sol to extend Aggregator with CCIP cross-chain messaging capabilities. - Created .gitmodules to include OpenZeppelin contracts as a submodule. - Developed a comprehensive deployment guide in NEXT_STEPS_COMPLETE_GUIDE.md for Phase 2 and smart contract deployment. - Implemented Vite configuration for the orchestration portal, supporting both Vue and React frameworks. - Added server-side logic for the Multi-Cloud Orchestration Portal, including API endpoints for environment management and monitoring. - Created scripts for resource import and usage validation across non-US regions. - Added tests for CCIP error handling and integration to ensure robust functionality. - Included various new files and directories for the orchestration portal and deployment scripts.
This commit is contained in:
366
docs/guides/MIGRATION_GUIDE.md
Normal file
366
docs/guides/MIGRATION_GUIDE.md
Normal file
@@ -0,0 +1,366 @@
|
||||
# Migration Guide: Avoiding OpenZeppelin Dependencies
|
||||
|
||||
## Overview
|
||||
|
||||
This guide provides patterns and best practices for creating new contracts without OpenZeppelin dependencies, following the patterns used in the new WETH contracts.
|
||||
|
||||
## Patterns to Follow
|
||||
|
||||
### 1. Minimal IERC20 Interface
|
||||
|
||||
Instead of using OpenZeppelin's IERC20, use a minimal interface:
|
||||
|
||||
```solidity
|
||||
// Minimal IERC20 interface for token operations
|
||||
interface IERC20 {
|
||||
function transferFrom(address from, address to, uint256 amount) external returns (bool);
|
||||
function transfer(address to, uint256 amount) external returns (bool);
|
||||
function approve(address spender, uint256 amount) external returns (bool);
|
||||
function balanceOf(address account) external view returns (uint256);
|
||||
}
|
||||
|
||||
// Usage
|
||||
require(IERC20(token).transferFrom(msg.sender, address(this), amount), "Transfer failed");
|
||||
require(IERC20(token).approve(spender, amount), "Approval failed");
|
||||
```
|
||||
|
||||
**Reference**: `contracts/ccip/CCIPWETH9Bridge.sol`
|
||||
|
||||
---
|
||||
|
||||
### 2. Custom Admin Pattern
|
||||
|
||||
Instead of using OpenZeppelin's Ownable, use a custom admin pattern:
|
||||
|
||||
```solidity
|
||||
contract MyContract {
|
||||
address public admin;
|
||||
|
||||
modifier onlyAdmin() {
|
||||
require(msg.sender == admin, "MyContract: only admin");
|
||||
_;
|
||||
}
|
||||
|
||||
constructor(address _admin) {
|
||||
require(_admin != address(0), "MyContract: zero address");
|
||||
admin = _admin;
|
||||
}
|
||||
|
||||
function changeAdmin(address newAdmin) external onlyAdmin {
|
||||
require(newAdmin != address(0), "MyContract: zero address");
|
||||
admin = newAdmin;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Reference**: `contracts/ccip/CCIPWETH9Bridge.sol`
|
||||
|
||||
---
|
||||
|
||||
### 3. Standard ERC20 Calls
|
||||
|
||||
Instead of using SafeERC20, use standard ERC20 calls with require statements:
|
||||
|
||||
```solidity
|
||||
// Instead of SafeERC20
|
||||
// IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
|
||||
|
||||
// Use standard ERC20 calls
|
||||
require(IERC20(token).transferFrom(msg.sender, address(this), amount), "Transfer failed");
|
||||
require(IERC20(token).approve(spender, amount), "Approval failed");
|
||||
```
|
||||
|
||||
**Note**: This works for standard ERC20 tokens. If you need to handle non-standard tokens, consider using SafeERC20 or a try-catch pattern.
|
||||
|
||||
**Reference**: `contracts/ccip/CCIPWETH9Bridge.sol`
|
||||
|
||||
---
|
||||
|
||||
### 4. Error Handling
|
||||
|
||||
Always use require statements for error handling:
|
||||
|
||||
```solidity
|
||||
// Good: Explicit error messages
|
||||
require(amount > 0, "MyContract: invalid amount");
|
||||
require(recipient != address(0), "MyContract: zero recipient");
|
||||
require(balance >= amount, "MyContract: insufficient balance");
|
||||
|
||||
// Bad: Silent failures
|
||||
if (amount == 0) return;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
### For New Contracts
|
||||
|
||||
- [ ] Use minimal IERC20 interface instead of OpenZeppelin's IERC20
|
||||
- [ ] Use custom admin pattern instead of Ownable
|
||||
- [ ] Use standard ERC20 calls instead of SafeERC20
|
||||
- [ ] Add explicit error messages with require statements
|
||||
- [ ] Test with standard ERC20 tokens
|
||||
- [ ] Document any non-standard token requirements
|
||||
|
||||
### For Existing Contracts
|
||||
|
||||
- [ ] Identify OpenZeppelin dependencies
|
||||
- [ ] Replace SafeERC20 with standard ERC20 calls
|
||||
- [ ] Replace Ownable with custom admin pattern
|
||||
- [ ] Replace IERC20 with minimal interface
|
||||
- [ ] Update tests
|
||||
- [ ] Verify security
|
||||
- [ ] Update documentation
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Token Transfer Contract
|
||||
|
||||
```solidity
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
// Minimal IERC20 interface
|
||||
interface IERC20 {
|
||||
function transferFrom(address from, address to, uint256 amount) external returns (bool);
|
||||
function transfer(address to, uint256 amount) external returns (bool);
|
||||
function approve(address spender, uint256 amount) external returns (bool);
|
||||
function balanceOf(address account) external view returns (uint256);
|
||||
}
|
||||
|
||||
contract TokenTransfer {
|
||||
address public admin;
|
||||
address public token;
|
||||
|
||||
modifier onlyAdmin() {
|
||||
require(msg.sender == admin, "TokenTransfer: only admin");
|
||||
_;
|
||||
}
|
||||
|
||||
constructor(address _admin, address _token) {
|
||||
require(_admin != address(0), "TokenTransfer: zero admin");
|
||||
require(_token != address(0), "TokenTransfer: zero token");
|
||||
admin = _admin;
|
||||
token = _token;
|
||||
}
|
||||
|
||||
function transferTokens(address to, uint256 amount) external onlyAdmin {
|
||||
require(to != address(0), "TokenTransfer: zero recipient");
|
||||
require(amount > 0, "TokenTransfer: invalid amount");
|
||||
require(IERC20(token).transfer(to, amount), "TokenTransfer: transfer failed");
|
||||
}
|
||||
|
||||
function changeAdmin(address newAdmin) external onlyAdmin {
|
||||
require(newAdmin != address(0), "TokenTransfer: zero address");
|
||||
admin = newAdmin;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Example 2: Cross-Chain Bridge
|
||||
|
||||
```solidity
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
interface IERC20 {
|
||||
function transferFrom(address from, address to, uint256 amount) external returns (bool);
|
||||
function transfer(address to, uint256 amount) external returns (bool);
|
||||
function balanceOf(address account) external view returns (uint256);
|
||||
}
|
||||
|
||||
contract CrossChainBridge {
|
||||
address public admin;
|
||||
address public token;
|
||||
address public router;
|
||||
|
||||
modifier onlyAdmin() {
|
||||
require(msg.sender == admin, "CrossChainBridge: only admin");
|
||||
_;
|
||||
}
|
||||
|
||||
constructor(address _admin, address _token, address _router) {
|
||||
require(_admin != address(0), "CrossChainBridge: zero admin");
|
||||
require(_token != address(0), "CrossChainBridge: zero token");
|
||||
require(_router != address(0), "CrossChainBridge: zero router");
|
||||
admin = _admin;
|
||||
token = _token;
|
||||
router = _router;
|
||||
}
|
||||
|
||||
function bridgeTokens(address recipient, uint256 amount) external {
|
||||
require(recipient != address(0), "CrossChainBridge: zero recipient");
|
||||
require(amount > 0, "CrossChainBridge: invalid amount");
|
||||
require(IERC20(token).transferFrom(msg.sender, address(this), amount), "CrossChainBridge: transfer failed");
|
||||
// Bridge logic here
|
||||
}
|
||||
|
||||
function changeAdmin(address newAdmin) external onlyAdmin {
|
||||
require(newAdmin != address(0), "CrossChainBridge: zero address");
|
||||
admin = newAdmin;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Always Validate Inputs
|
||||
|
||||
```solidity
|
||||
// Good
|
||||
require(amount > 0, "MyContract: invalid amount");
|
||||
require(recipient != address(0), "MyContract: zero recipient");
|
||||
|
||||
// Bad
|
||||
if (amount == 0) return;
|
||||
```
|
||||
|
||||
### 2. Use Explicit Error Messages
|
||||
|
||||
```solidity
|
||||
// Good
|
||||
require(balance >= amount, "MyContract: insufficient balance");
|
||||
|
||||
// Bad
|
||||
require(balance >= amount);
|
||||
```
|
||||
|
||||
### 3. Check Return Values
|
||||
|
||||
```solidity
|
||||
// Good
|
||||
require(IERC20(token).transfer(to, amount), "Transfer failed");
|
||||
|
||||
// Bad
|
||||
IERC20(token).transfer(to, amount);
|
||||
```
|
||||
|
||||
### 4. Validate Addresses
|
||||
|
||||
```solidity
|
||||
// Good
|
||||
require(admin != address(0), "MyContract: zero address");
|
||||
|
||||
// Bad
|
||||
admin = _admin;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### 1. Non-Standard ERC20 Tokens
|
||||
|
||||
Standard ERC20 calls may fail with non-standard tokens. If you need to handle non-standard tokens:
|
||||
|
||||
- Use SafeERC20 (requires OpenZeppelin)
|
||||
- Use try-catch pattern
|
||||
- Document token requirements
|
||||
|
||||
### 2. Access Control
|
||||
|
||||
Custom admin pattern provides same security as Ownable:
|
||||
|
||||
- Simple address-based access control
|
||||
- Same security level
|
||||
- No external dependencies
|
||||
|
||||
### 3. Error Handling
|
||||
|
||||
Always use require statements:
|
||||
|
||||
- Explicit error messages
|
||||
- Revert on failure
|
||||
- Gas-efficient
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
### Test with Standard ERC20 Tokens
|
||||
|
||||
```solidity
|
||||
function testTransfer() public {
|
||||
// Deploy standard ERC20 token
|
||||
ERC20 token = new ERC20();
|
||||
|
||||
// Test transfer
|
||||
require(token.transfer(recipient, amount), "Transfer failed");
|
||||
}
|
||||
```
|
||||
|
||||
### Test Error Cases
|
||||
|
||||
```solidity
|
||||
function testInvalidAmount() public {
|
||||
vm.expectRevert("MyContract: invalid amount");
|
||||
contract.transferTokens(recipient, 0);
|
||||
}
|
||||
|
||||
function testZeroRecipient() public {
|
||||
vm.expectRevert("MyContract: zero recipient");
|
||||
contract.transferTokens(address(0), amount);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
### Contract Examples
|
||||
- `contracts/ccip/CCIPWETH9Bridge.sol` - Minimal IERC20 interface, custom admin pattern
|
||||
- `contracts/ccip/CCIPWETH10Bridge.sol` - Minimal IERC20 interface, custom admin pattern
|
||||
- `contracts/tokens/WETH10.sol` - No external dependencies
|
||||
|
||||
### Documentation
|
||||
- [Contract Inventory](./CONTRACT_INVENTORY.md)
|
||||
- [OpenZeppelin Usage Analysis](./OPENZEPPELIN_USAGE_ANALYSIS.md)
|
||||
- [Dependencies Guide](./DEPENDENCIES.md)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
### Key Patterns
|
||||
1. ✅ Minimal IERC20 interface
|
||||
2. ✅ Custom admin pattern
|
||||
3. ✅ Standard ERC20 calls
|
||||
4. ✅ Explicit error handling
|
||||
|
||||
### Benefits
|
||||
- ✅ No external dependencies
|
||||
- ✅ Smaller code size
|
||||
- ✅ Lower gas costs
|
||||
- ✅ Better maintainability
|
||||
|
||||
### When to Use OpenZeppelin
|
||||
- ⚠️ Complex security features needed
|
||||
- ⚠️ Battle-tested implementation required
|
||||
- ⚠️ Non-standard token handling needed
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. ✅ Follow patterns from new WETH contracts
|
||||
2. ✅ Use minimal interfaces
|
||||
3. ✅ Use custom admin pattern
|
||||
4. ✅ Test thoroughly
|
||||
5. ✅ Document dependencies
|
||||
|
||||
---
|
||||
|
||||
## Questions?
|
||||
|
||||
For questions about migration patterns, refer to:
|
||||
- [Contract Inventory](./CONTRACT_INVENTORY.md)
|
||||
- [OpenZeppelin Usage Analysis](./OPENZEPPELIN_USAGE_ANALYSIS.md)
|
||||
- [Dependencies Guide](./DEPENDENCIES.md)
|
||||
|
||||
Reference in New Issue
Block a user