Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ out/
.claude/
docs-internal/

# Deployments
deployments

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
Expand All @@ -19,3 +22,4 @@ docs-internal/
# Safe to ignore
.wake/
.vscode/
foundry.lock
55 changes: 37 additions & 18 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ forge fmt --check
- Implementation: `CEA` (src/CEA/CEA.sol)
- Deployment: Deterministic via `CEAFactory` using CREATE2 (src/CEA/CEAFactory.sol:32)
- Identity: One UEA → one CEA per external chain
- Gateway integration: Routes CEA→UEA transfers through `IUniversalGateway.sendUniversalTxViaCEA()` for FUNDS and FUNDS_AND_PAYLOAD tx types

### Proxy Pattern Architecture

Expand Down Expand Up @@ -122,10 +123,11 @@ Both UEA and CEA use minimal proxy (EIP-1167 clone) architecture:
**CEA Migration:**
- Mirrors UEA migration pattern for safe proxy upgrades (v1 → v2)
- `CEAMigration` (src/CEA/CEAMigration.sol) - Slot-writer migration singleton
- CEA detects `MIGRATION_SELECTOR` and routes to `_handleMigration()`
- CEA detects `MIGRATION_SELECTOR` at top-level payload (three-way branch in `_handleExecution`, aligned with UEA_EVM pattern)
- Migration payload format: `MIGRATION_SELECTOR` directly (no Multicall wrapper)
- Factory tracks migration contract via `CEA_MIGRATION_CONTRACT` state variable
- Migration executed via delegatecall (preserves all state and funds)
- Safety constraints: self-targeted, zero-value, standalone execution only
- Explicit prevention: `MIGRATION_SELECTOR` inside a Multicall array reverts with `InvalidCall`
- CEA stores factory reference to fetch migration contract at runtime

### Token Primitives
Expand Down Expand Up @@ -186,8 +188,16 @@ struct UniversalPayload {
```

**Special Selectors:**
- `MULTICALL_SELECTOR = bytes4(keccak256("UEA_MULTICALL"))` - Batch multiple calls
- `MIGRATION_SELECTOR = bytes4(keccak256("UEA_MIGRATION"))` - Trigger migration
- `MULTICALL_SELECTOR = bytes4(keccak256("UEA_MULTICALL"))` - Batch multiple calls (0xc25b8d90)
- `MIGRATION_SELECTOR = bytes4(keccak256("UEA_MIGRATION"))` - Trigger migration (0xb0c47dc5)

**UEA_EVM Execution Routing (`UEA_EVM._handleExecution`):**
- Three-way dispatch based on `payload.data`:
1. `isMulticall(payload.data)` → `_handleMulticall(payload)` — batch execution
2. `isMigration(payload.data)` → `_handleMigration(payload)` — delegatecall to factory's migration contract
3. else → `_handleSingleCall(payload)` — single target call
- `UE_MODULE` (0x14191Ea54B4c176fCf86f51b0FAc7CB1E71Df7d7) can execute without signature verification
- Factory reference stored for fetching migration contract at runtime

### CEA Execution Flow

Expand All @@ -196,6 +206,7 @@ struct UniversalPayload {
**Payload Format:**
- New format: `MULTICALL_SELECTOR + abi.encode(Multicall[])`
- Old format: Direct `abi.encode(Multicall[])` (backwards compatible)
- Migration format: `MIGRATION_SELECTOR` at top level (no Multicall wrapper)

**Multicall Structure (src/libraries/Types.sol:31):**
```solidity
Expand All @@ -207,23 +218,26 @@ struct Multicall {
```

**Execution Routing (`CEA._handleExecution`):**
1. Check if payload starts with `MULTICALL_SELECTOR`
2. If yes, decode as `Multicall[]` and route based on content:
- Single-element with `MIGRATION_SELECTOR` → `_handleMigration()`
- Otherwise → `_handleMulticall()`
3. If no, route to `_handleSingleCall()` (backwards compatibility)
1. Check if payload starts with `MULTICALL_SELECTOR` → decode as `Multicall[]`, route to `_handleMulticall()`
2. Check if payload starts with `MIGRATION_SELECTOR` → route to `_handleMigration()` (no params, top-level)
3. Otherwise → route to `_handleSingleCall()` (backwards compatibility)

**Self-Call Pattern:**
- `sendUniversalTxToUEA(token, amount)` - Transfer funds from CEA to UEA
- `sendUniversalTxToUEA(token, amount, payload)` - Transfer funds/payload from CEA to UEA via gateway
- Only callable via self-call (`msg.sender == address(this)`)
- Must be included in multicall array for execution
- Always sends empty payload/signature (funds-only transfer)
- Supports two tx types:
- **FUNDS**: amount > 0, payload empty
- **FUNDS_AND_PAYLOAD**: amount > 0, payload non-empty
- Both tx types route through `IUniversalGateway.sendUniversalTxViaCEA()`
- SDK must include ERC20 approval steps before this call

**Safety Constraints:**
- Self-calls must have `value == 0`
- Migration must be standalone (cannot be batched)
- Self-calls must have `value == 0` (enforced in `_handleMulticall`)
- Migration must be standalone (cannot appear inside multicall arrays — `isMigrationCall` check)
- Migration rejects `msg.value > 0` (logic upgrade, not value transfer)
- All calls executed sequentially via `.call()`
- No strict `msg.value == totalValue` enforcement (CEA can spend pre-existing balance)

## Key Contracts Reference

Expand Down Expand Up @@ -252,6 +266,7 @@ struct Multicall {
- `ICEA`: src/Interfaces/ICEA.sol
- `ICEAFactory`: src/Interfaces/ICEAFactory.sol
- `IUniversalCore`: src/Interfaces/IUniversalCore.sol
- `IUniversalGateway`: src/Interfaces/IUniversalGateway.sol - UniversalTxRequest struct, sendUniversalTx/sendUniversalTxViaCEA
- `IPRC20`: src/Interfaces/IPRC20.sol

## Test Structure
Expand All @@ -260,10 +275,11 @@ Tests are organized by component:
- `test/tests_uea_and_factory/` - UEA and factory tests
- `test/tests_cea/` - CEA and factory tests
- `CEA.t.sol` - Core CEA tests (82 tests, canonical helpers)
- `CEA_multicalls.t.sol` - Multicall execution tests (130 tests)
- `CEA_selfCalls.t.sol` - Self-call pattern tests (93 tests)
- Total: 418 CEA tests
- `test/tests_ceaMigration/` - CEA migration tests (42 tests)
- `CEA_multicalls.t.sol` - Multicall execution tests (132 tests)
- `CEA_selfCalls.t.sol` - Self-call pattern tests (120 tests)
- `CEAFactory.t.sol` - Factory tests (116 tests)
- Total: 450 CEA tests
- `test/tests_ceaMigration/` - CEA migration tests (43 tests)
- `CEAMigration.t.sol` - Unit tests for migration contract
- `CEAFactory_Migration.t.sol` - Factory migration management
- `CEA_Migration.t.sol` - CEA migration logic tests
Expand All @@ -279,8 +295,11 @@ Test files follow the pattern `ContractName.t.sol` and use Foundry's testing fra
**Key Test Helpers (CEA.t.sol):**
- `makeCall(to, value, data)` - Create Multicall struct
- `encodeCalls(Multicall[])` - Encode with MULTICALL_SELECTOR
- `buildWithdrawPayload(token, amount)` - Build sendUniversalTxToUEA payload
- `buildSendToUEAPayload(token, amount)` - Build sendUniversalTxToUEA payload (funds-only, empty payload)
- `buildSendToUEAPayloadWithData(token, amount, payload)` - Build sendUniversalTxToUEA with UEA payload (FUNDS_AND_PAYLOAD)
- `buildExternalSingleCall(to, value, data)` - Single external call
- `buildSelfSendToUEACall(token, amount)` - Self-call Multicall with value=0
- `buildSendToUEAMulticallPayload(token, amount, approveGateway)` - Full multicall with optional ERC20 approval

## Important Constants

Expand Down
5 changes: 4 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ solc = "0.8.26"
optimizer = true
optimizer_runs = 99999
auto_detect_solc = false
evm_version = "shanghai"
evm_version = "shanghai"
via_ir = true

fs_permissions = [{ access = "read-write", path = "deployments/" }]
83 changes: 53 additions & 30 deletions scripts/cea/deployCEAFactory.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.s
*/
contract DeployCEAFactoryScript is Script {
// ============================================================================
// DEPLOYMENT PARAMETERS - Pre-Deployement Checklist
// DEPLOYMENT PARAMETERS - Pre-Deployement Checklist
// ============================================================================

// Owner of the CEAFactory (can update implementations, pause, etc.)
Expand All @@ -36,13 +36,13 @@ contract DeployCEAFactoryScript is Script {
address public VAULT_ADDRESS = 0x0000000000000000000000000000000000000001;

// UniversalGateway contract address on this chain (handles cross-chain messages)
address public UNIVERSAL_GATEWAY_ADDRESS = 0x0000000000000000000000000000000000000002;

address public UNIVERSAL_GATEWAY_ADDRESS = 0x4DCab975cDe839632db6695e2e936A29ce3e325E;

function run() external {
// Get chain ID and deployer info
uint256 chainId = block.chainid;
address deployer = vm.addr(vm.envUint("KEY"));
uint256 deployerKey = vm.envUint("KEY");
address deployer = vm.addr(deployerKey);

console.log("=== CEAFactory Deployment Configuration ===");
console.log("Chain ID:", chainId);
Expand All @@ -63,7 +63,7 @@ contract DeployCEAFactoryScript is Script {
console.log("Universal Gateway:", universalGateway);
console.log("");

vm.startBroadcast();
vm.startBroadcast(deployerKey);

// 1. Deploy CEA implementation (logic contract)
CEA ceaImplementation = new CEA();
Expand All @@ -84,19 +84,16 @@ contract DeployCEAFactoryScript is Script {
// 5. Prepare initialization data for CEAFactory
bytes memory initData = abi.encodeWithSelector(
CEAFactory.initialize.selector,
owner, // initialOwner
vault, // initialVault
address(ceaProxyImplementation), // ceaProxyImplementation
address(ceaImplementation), // ceaImplementation
universalGateway // universalGateway
owner, // initialOwner
vault, // initialVault
address(ceaProxyImplementation), // ceaProxyImplementation
address(ceaImplementation), // ceaImplementation
universalGateway // universalGateway
);

// 6. Deploy TransparentUpgradeableProxy wrapping CEAFactory
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
address(ceaFactoryImplementation),
address(proxyAdmin),
initData
);
TransparentUpgradeableProxy proxy =
new TransparentUpgradeableProxy(address(ceaFactoryImplementation), address(proxyAdmin), initData);
console.log("[5/7] CEAFactory Proxy (CANONICAL):", address(proxy));

// 7. Wrap proxy in CEAFactory interface for verification
Expand Down Expand Up @@ -124,7 +121,11 @@ contract DeployCEAFactoryScript is Script {

console.log("Factory Owner:", verifiedOwner, verifiedOwner == owner ? "[OK]" : "[MISMATCH]");
console.log("Vault:", verifiedVault, verifiedVault == vault ? "[OK]" : "[MISMATCH]");
console.log("CEA Proxy Impl:", verifiedCEAProxy, verifiedCEAProxy == address(ceaProxyImplementation) ? "[OK]" : "[MISMATCH]");
console.log(
"CEA Proxy Impl:",
verifiedCEAProxy,
verifiedCEAProxy == address(ceaProxyImplementation) ? "[OK]" : "[MISMATCH]"
);
console.log("CEA Impl:", verifiedCEA, verifiedCEA == address(ceaImplementation) ? "[OK]" : "[MISMATCH]");
console.log("Gateway:", verifiedGateway, verifiedGateway == universalGateway ? "[OK]" : "[MISMATCH]");
console.log("ProxyAdmin:", verifiedProxyAdmin);
Expand All @@ -137,20 +138,42 @@ contract DeployCEAFactoryScript is Script {

// 10. Generate JSON output for deployment tracking
console.log("\n=== Deployment Addresses (JSON) ===");
string memory json = string(abi.encodePacked(
'{\n',
' "chainId": ', vm.toString(chainId), ',\n',
' "deployer": "', vm.toString(deployer), '",\n',
' "ceaImplementation": "', vm.toString(address(ceaImplementation)), '",\n',
' "ceaProxyImplementation": "', vm.toString(address(ceaProxyImplementation)), '",\n',
' "ceaFactoryImplementation": "', vm.toString(address(ceaFactoryImplementation)), '",\n',
' "proxyAdmin": "', vm.toString(address(proxyAdmin)), '",\n',
' "ceaFactoryProxy": "', vm.toString(address(proxy)), '",\n',
' "owner": "', vm.toString(owner), '",\n',
' "vault": "', vm.toString(vault), '",\n',
' "universalGateway": "', vm.toString(universalGateway), '"\n',
'}'
));
string memory json = string(
abi.encodePacked(
"{\n",
' "chainId": ',
vm.toString(chainId),
",\n",
' "deployer": "',
vm.toString(deployer),
'",\n',
' "ceaImplementation": "',
vm.toString(address(ceaImplementation)),
'",\n',
' "ceaProxyImplementation": "',
vm.toString(address(ceaProxyImplementation)),
'",\n',
' "ceaFactoryImplementation": "',
vm.toString(address(ceaFactoryImplementation)),
'",\n',
' "proxyAdmin": "',
vm.toString(address(proxyAdmin)),
'",\n',
' "ceaFactoryProxy": "',
vm.toString(address(proxy)),
'",\n',
' "owner": "',
vm.toString(owner),
'",\n',
' "vault": "',
vm.toString(vault),
'",\n',
' "universalGateway": "',
vm.toString(universalGateway),
'"\n',
"}"
)
);
console.log(json);

// Write to file
Expand Down
Loading