diff --git a/README.md b/README.md index b26ac32..ab7dd6f 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,9 @@ The UEAFactoryV1 is the central contract responsible for deploying and managing - `registerNewChain(bytes32 _chainHash, bytes32 _vmHash)`: Register a new chain with its VM type - `registerUEA(bytes32 _chainHash, bytes32 _vmHash, address _UEA)`: Register a UEA implementation for a VM type -- `deployUEA(UniversalAccount memory _id)`: Deploy a new UEA for an external chain user -- `computeUEA(UniversalAccount memory _id)`: Compute the address of a UEA before deployment -- `getUEAForOrigin(UniversalAccount memory _id)`: Get the UEA address for a given external chain user +- `deployUEA(UniversalAccountId memory _id)`: Deploy a new UEA for an external chain user +- `computeUEA(UniversalAccountId memory _id)`: Compute the address of a UEA before deployment +- `getUEAForOrigin(UniversalAccountId memory _id)`: Get the UEA address for a given external chain user ## UEA Implementations diff --git a/src/Interfaces/IUEA.sol b/src/Interfaces/IUEA.sol index 3dc49b6..6f9cdb5 100644 --- a/src/Interfaces/IUEA.sol +++ b/src/Interfaces/IUEA.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.26; -import {UniversalAccount, UniversalPayload} from "../libraries/Types.sol"; +import {UniversalAccountId, UniversalPayload} from "../libraries/Types.sol"; /** * @title IUEA (Interface for Universal Executor Account) @@ -26,7 +26,7 @@ interface IUEA { /** * @dev Initializes the UEA with the Universal Account information. - * @param universalAccount The UniversalAccount struct containing: + * @param universalAccount The UniversalAccountId struct containing: * - chain: The name of the external chain (e.g., "eip155:1", "eip155:900") * - owner: The owner's address/public key from the external chain * @@ -35,13 +35,13 @@ interface IUEA { * - For EVM-based UEAs: An Ethereum address (20 bytes) * - For SVM-based UEAs: A Solana public key (32 bytes) */ - function initialize(UniversalAccount memory universalAccount) external; + function initialize(UniversalAccountId memory universalAccount) external; /** * @dev Returns the Universal Account information for this UEA. - * @return The UniversalAccount struct containing the chain name and owner key. + * @return The UniversalAccountId struct containing the chain name and owner key. */ - function universalAccount() external view returns (UniversalAccount memory); + function universalAccount() external view returns (UniversalAccountId memory); /** * @dev Verifies if a signature is valid for a given message hash. @@ -51,11 +51,11 @@ interface IUEA { * * @notice Implementation behavior varies by UEA type: * - For EVM-based UEAs: Uses ECDSA recovery to verify that the signature was created by the - * address stored in the UniversalAccount.owner field. The owner is expected to be an + * address stored in the UniversalAccountId.owner field. The owner is expected to be an * Ethereum address represented as bytes. * * - For SVM-based UEAs: Uses a precompiled contract to verify Ed25519 signatures, where the - * UniversalAccount.owner field contains a Solana public key. The verification is done through + * UniversalAccountId.owner field contains a Solana public key. The verification is done through * a call to the VERIFIER_PRECOMPILE address. */ function verifyPayloadSignature(bytes32 messageHash, bytes memory signature) external view returns (bool); diff --git a/src/Interfaces/IUEAFactory.sol b/src/Interfaces/IUEAFactory.sol index 152d97b..48474b1 100644 --- a/src/Interfaces/IUEAFactory.sol +++ b/src/Interfaces/IUEAFactory.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.26; -import {UniversalAccount} from "../libraries/Types.sol"; +import {UniversalAccountId} from "../libraries/Types.sol"; /** * @title IUEAFactory @@ -13,7 +13,7 @@ interface IUEAFactory { event ChainRegistered(bytes32 indexed chainHash, bytes32 vmHash); /// @notice Emitted when a new UEA is deployed for an external chain owner - event UEADeployed(address indexed UEA, bytes owner, bytes32 chainHash); + event UEADeployed(address indexed UEA, bytes owner, uint256 sourceChainId, bytes32 chainHash); /// @notice Emitted when a UEA implementation is registered for a specific VM type event UEARegistered(bytes32 indexed chainHash, address UEA_Logic, bytes32 vmHash); @@ -48,7 +48,7 @@ interface IUEAFactory { * @param _id The Universal Account information containing chain and owner key * @return The address of the deployed UEA */ - function deployUEA(UniversalAccount memory _id) external returns (address); + function deployUEA(UniversalAccountId memory _id) external returns (address); /** * @dev Returns the UEA implementation address for a given chain @@ -71,7 +71,7 @@ interface IUEAFactory { * @return account The Universal Account information associated with this UEA * @return isUEA True if the address addr is a UEA contract. Else it is a native EOA of PUSH chain (i.e., isUEA = false) */ - function getOriginForUEA(address addr) external view returns (UniversalAccount memory account, bool isUEA); + function getOriginForUEA(address addr) external view returns (UniversalAccountId memory account, bool isUEA); /** * @dev Returns the computed UEA address for a given Universal Account ID and deployment status @@ -79,5 +79,5 @@ interface IUEAFactory { * @return uea The address of the UEA (computed deterministically) * @return isDeployed True if the UEA has already been deployed */ - function getUEAForOrigin(UniversalAccount memory _id) external view returns (address uea, bool isDeployed); + function getUEAForOrigin(UniversalAccountId memory _id) external view returns (address uea, bool isDeployed); } diff --git a/src/UEA/UEA_EVM.sol b/src/UEA/UEA_EVM.sol index a8ab5c9..98ebd26 100644 --- a/src/UEA/UEA_EVM.sol +++ b/src/UEA/UEA_EVM.sol @@ -7,7 +7,7 @@ import {IUEA} from "../Interfaces/IUEA.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import { - UniversalAccount, + UniversalAccountId, UniversalPayload, DOMAIN_SEPARATOR_TYPEHASH, UNIVERSAL_PAYLOAD_TYPEHASH @@ -24,7 +24,7 @@ contract UEA_EVM is ReentrancyGuard, IUEA { using ECDSA for bytes32; // @notice The Universal Account information - UniversalAccount internal id; + UniversalAccountId internal id; // @notice Flag to track initialization status bool private initialized; // @notice The nonce for the UEA @@ -35,7 +35,7 @@ contract UEA_EVM is ReentrancyGuard, IUEA { /** * @inheritdoc IUEA */ - function initialize(UniversalAccount memory _id) external { + function initialize(UniversalAccountId memory _id) external { if (initialized) { revert Errors.AlreadyInitialized(); } @@ -49,13 +49,7 @@ contract UEA_EVM is ReentrancyGuard, IUEA { * @return bytes32 The domain separator. */ function domainSeparator() public view returns (bytes32) { - uint256 chainId; - /* solhint-disable no-inline-assembly */ - /// @solidity memory-safe-assembly - assembly { - chainId := chainid() - } - /* solhint-enable no-inline-assembly */ + uint256 chainId = id.chainId; return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, keccak256(bytes(VERSION)), chainId, address(this))); } @@ -63,7 +57,7 @@ contract UEA_EVM is ReentrancyGuard, IUEA { /** * @inheritdoc IUEA */ - function universalAccount() public view returns (UniversalAccount memory) { + function universalAccount() public view returns (UniversalAccountId memory) { return id; } diff --git a/src/UEA/UEA_SVM.sol b/src/UEA/UEA_SVM.sol index 67b124a..7dd7a54 100644 --- a/src/UEA/UEA_SVM.sol +++ b/src/UEA/UEA_SVM.sol @@ -5,7 +5,7 @@ import {Errors} from "../libraries/Errors.sol"; import {IUEA} from "../Interfaces/IUEA.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import { - UniversalAccount, + UniversalAccountId, UniversalPayload, DOMAIN_SEPARATOR_TYPEHASH, UNIVERSAL_PAYLOAD_TYPEHASH @@ -20,7 +20,7 @@ import { contract UEA_SVM is ReentrancyGuard, IUEA { // @notice The Universal Account information - UniversalAccount internal id; + UniversalAccountId internal id; // @notice Flag to track initialization status bool private initialized; // @notice The nonce for the UEA @@ -35,13 +35,7 @@ contract UEA_SVM is ReentrancyGuard, IUEA { * @return bytes32 The domain separator. */ function domainSeparator() public view returns (bytes32) { - uint256 chainId; - /* solhint-disable no-inline-assembly */ - /// @solidity memory-safe-assembly - assembly { - chainId := chainid() - } - /* solhint-enable no-inline-assembly */ + uint256 chainId = id.chainId; return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, keccak256(bytes(VERSION)), chainId, address(this))); } @@ -49,7 +43,7 @@ contract UEA_SVM is ReentrancyGuard, IUEA { /** * @inheritdoc IUEA */ - function initialize(UniversalAccount memory _id) external { + function initialize(UniversalAccountId memory _id) external { if (initialized) { revert Errors.AlreadyInitialized(); } @@ -61,7 +55,7 @@ contract UEA_SVM is ReentrancyGuard, IUEA { /** * @inheritdoc IUEA */ - function universalAccount() public view returns (UniversalAccount memory) { + function universalAccount() public view returns (UniversalAccountId memory) { return id; } diff --git a/src/UEAFactoryV1.sol b/src/UEAFactoryV1.sol index 8e481e3..912f2c6 100644 --- a/src/UEAFactoryV1.sol +++ b/src/UEAFactoryV1.sol @@ -8,25 +8,23 @@ import {Errors} from "./libraries/Errors.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {IUEAFactory} from "./Interfaces/IUEAFactory.sol"; -import {UniversalAccount} from "./libraries/Types.sol"; +import {UniversalAccountId} from "./libraries/Types.sol"; /** * @title UEAFactoryV1 * @dev A factory contract for deploying and managing Universal Executor Accounts (UEA) instances. * - * Key Terms: - * - UEA (Universal Executor Account): Smart contract deployed for external chain users - * to interact with PUSH chain. Each UEA acts as a proxy for its owner. - * - UOA (Universal Owner Address): The address of the external chain owner who - * owns a particular UEA. This key is used for signature verification in UEAs. - * - VM Types: Different virtual machine environments (EVM, SVM, etc.) that require - * specific implementation logic. Each chain is registered with its VM type hash, and - * each VM type hash is mapped to a corresponding UEA implementation contract address. - * This allows the factory to deploy the correct UEA implementation for different - * blockchain environments. - * - Chain identifiers: These follow the CAIP-2 standard (e.g., "eip155:1" for Ethereum mainnet). - * These standardized chain IDs are used to identify which blockchain an account belongs to. - * The full identifier is hashed to produce a chainHash value for internal usage. + * - UEA (Universal Executor Account) : Smart contract deployed for external chain users to interact with PUSH chain. + * Each UEA acts as a proxy for its owner. + * - UOA (Universal Owner Address) : The address of the external chain owner who owns a particular UEA. + * This key is used for signature verification in UEAs. + * - VM Types : Different virtual machine environments (EVM, SVM, etc.) require specific implementation logic. + * Each chain is registered with a VM_TYPE_HASH, and each VM_TYPE_HASH is mapped to a corresponding UEA. + * This allows the factory to deploy the correct UEA implementation for different blockchain environments. + * - Chain identifiers : These follow the CAIP-2 standard (e.g., "eip155:1" for Ethereum mainnet). + * The UniversalAccountId struct uses the chainNamespace and chainId for chain identification. + * The full identifier is hashed to produce a chainHash value for internal usage. + * Note: chainHash = keccak256(abi.encode(_id.chainNamespace, _id.chainId)) * * The contract uses OZ's Clones library to create deterministic addresses (CREATE2) for UEA instances. * It keeps track of deployed UEAs and their corresponding user keys from external chains. @@ -39,11 +37,11 @@ contract UEAFactoryV1 is Initializable, OwnableUpgradeable, IUEAFactory { /// @notice Maps VM type hashes to their corresponding UEA implementation addresses mapping(bytes32 => address) public UEA_VM; - /// @notice Maps UniversalAccount(hash) to their deployed UEA contract addresses + /// @notice Maps UniversalAccountId(hash) to their deployed UEA contract addresses mapping(bytes32 => address) public UOA_to_UEA; /// @notice Maps UEA addresses to their Universal Account information - mapping(address => UniversalAccount) private UEA_to_UOA; + mapping(address => UniversalAccountId) private UEA_to_UOA; /// @notice Maps chain identifiers to their registered VM type hashes mapping(bytes32 => bytes32) public CHAIN_to_VM; @@ -152,14 +150,14 @@ contract UEAFactoryV1 is Initializable, OwnableUpgradeable, IUEAFactory { * @notice Will revert if the account already exists, the chain is not registered, * or if no UEA implementation is available for the chain's VM type */ - function deployUEA(UniversalAccount memory _id) external returns (address) { + function deployUEA(UniversalAccountId memory _id) external returns (address) { bytes32 salt = generateSalt(_id); if (UOA_to_UEA[salt] != address(0)) { revert Errors.AccountAlreadyExists(); } // Get the appropriate UEA Implementation based on VM type - bytes32 chainHash = keccak256(abi.encode(_id.chain)); + bytes32 chainHash = keccak256(abi.encode(_id.chainNamespace, _id.chainId)); (bytes32 vmHash, bool isRegistered) = getVMType(chainHash); if (!isRegistered) { revert Errors.InvalidInputArgs(); @@ -175,7 +173,7 @@ contract UEAFactoryV1 is Initializable, OwnableUpgradeable, IUEAFactory { UEA_to_UOA[_UEA] = _id; // Store the inverse mapping IUEA(_UEA).initialize(_id); - emit UEADeployed(_UEA, _id.owner, chainHash); + emit UEADeployed(_UEA, _id.owner, _id.chainId, chainHash); return _UEA; } @@ -186,8 +184,8 @@ contract UEAFactoryV1 is Initializable, OwnableUpgradeable, IUEAFactory { * @notice Will revert if the chain is not registered or if no UEA implementation * is available for the chain's VM type */ - function computeUEA(UniversalAccount memory _id) public view returns (address) { - bytes32 chainHash = keccak256(abi.encode(_id.chain)); + function computeUEA(UniversalAccountId memory _id) public view returns (address) { + bytes32 chainHash = keccak256(abi.encode(_id.chainNamespace, _id.chainId)); (bytes32 vmHash, bool isRegistered) = getVMType(chainHash); if (!isRegistered) { revert Errors.InvalidInputArgs(); @@ -216,7 +214,7 @@ contract UEAFactoryV1 is Initializable, OwnableUpgradeable, IUEAFactory { } /// @inheritdoc IUEAFactory - function getOriginForUEA(address addr) external view returns (UniversalAccount memory account, bool isUEA) { + function getOriginForUEA(address addr) external view returns (UniversalAccountId memory account, bool isUEA) { account = UEA_to_UOA[addr]; // If the address has no associated Universal Account (owner.length == 0), @@ -230,8 +228,8 @@ contract UEAFactoryV1 is Initializable, OwnableUpgradeable, IUEAFactory { } /// @inheritdoc IUEAFactory - function getUEAForOrigin(UniversalAccount memory _id) external view returns (address uea, bool isDeployed) { - // Generate salt from the UniversalAccount struct + function getUEAForOrigin(UniversalAccountId memory _id) external view returns (address uea, bool isDeployed) { + // Generate salt from the UniversalAccountId struct bytes32 salt = generateSalt(_id); // Check if we already have a mapping @@ -255,7 +253,7 @@ contract UEAFactoryV1 is Initializable, OwnableUpgradeable, IUEAFactory { * @param _id The Universal Account information * @return A unique salt derived from the account information */ - function generateSalt(UniversalAccount memory _id) public pure returns (bytes32) { + function generateSalt(UniversalAccountId memory _id) public pure returns (bytes32) { return keccak256(abi.encode(_id)); } } diff --git a/src/libraries/Types.sol b/src/libraries/Types.sol index d7e5900..37c40ae 100644 --- a/src/libraries/Types.sol +++ b/src/libraries/Types.sol @@ -2,8 +2,9 @@ pragma solidity 0.8.26; // User Struct -struct UniversalAccount { - string chain; // Chain identifier of the owner account (e.g., "eip155:1") +struct UniversalAccountId { + string chainNamespace; // Chain namespace identifier of the owner account (e.g., "eip155" or "solana") + uint256 chainId; // Chain ID of the source chain of the owner of this UEA. bytes owner; // Owner's public key or address in bytes format } diff --git a/test/UEAFactory.t.sol b/test/UEAFactory.t.sol index 540c47c..9939596 100644 --- a/test/UEAFactory.t.sol +++ b/test/UEAFactory.t.sol @@ -74,9 +74,9 @@ contract UEAFactoryTest is Test { solanaChainId = "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"; solanaAddress = "HGyAQb8SeAE6X6RfhgMpGWZQuVYU8kgA5tKitaTrUHfh"; - // Store chain hashes for reuse - ethereumChainHash = keccak256(abi.encode("ETHEREUM")); - solanaChainHash = keccak256(abi.encode("SOLANA")); + // Store chain hashes for reuse - now includes both chainNamespace and chainId + ethereumChainHash = keccak256(abi.encode("eip155", 1)); + solanaChainHash = keccak256(abi.encode("solana", 101)); // Register chains factory.registerNewChain(ethereumChainHash, EVM_HASH); @@ -88,7 +88,7 @@ contract UEAFactoryTest is Test { } function testRegisterNewChain() public { - bytes32 chainHash = keccak256(abi.encode("KOVAN")); + bytes32 chainHash = keccak256(abi.encode("KOVAN", 42)); factory.registerNewChain(chainHash, EVM_HASH); // Verify the chain was registered @@ -98,7 +98,7 @@ contract UEAFactoryTest is Test { } function testRegisterUEA() public { - bytes32 chainHash = keccak256(abi.encode("KOVAN")); + bytes32 chainHash = keccak256(abi.encode("KOVAN", 42)); factory.registerNewChain(chainHash, EVM_HASH); factory.registerUEA(chainHash, EVM_HASH, address(ueaEVMImpl)); @@ -112,8 +112,8 @@ contract UEAFactoryTest is Test { address[] memory implementations = new address[](2); // Use different chains than those in setUp - chainHashes[0] = keccak256(abi.encode("KOVAN")); - chainHashes[1] = keccak256(abi.encode("METIS")); + chainHashes[0] = keccak256(abi.encode("KOVAN", 42)); + chainHashes[1] = keccak256(abi.encode("METIS", 1088)); vmHashes[0] = EVM_HASH; vmHashes[1] = EVM_HASH; @@ -134,7 +134,7 @@ contract UEAFactoryTest is Test { } function testRevertWhenRegisteringSameChainTwice() public { - bytes32 chainHash = keccak256(abi.encode("KOVAN")); + bytes32 chainHash = keccak256(abi.encode("KOVAN", 42)); factory.registerNewChain(chainHash, EVM_HASH); // Try to register the same chain again @@ -143,7 +143,7 @@ contract UEAFactoryTest is Test { } function testRevertWhenRegisteringZeroAddressUEA() public { - bytes32 chainHash = keccak256(abi.encode("KOVAN")); + bytes32 chainHash = keccak256(abi.encode("KOVAN", 42)); factory.registerNewChain(chainHash, EVM_HASH); // Try to register zero address as UEA @@ -152,7 +152,7 @@ contract UEAFactoryTest is Test { } function testRevertWhenRegisteringUEAWithWrongVMHash() public { - bytes32 chainHash = keccak256(abi.encode("KOVAN")); + bytes32 chainHash = keccak256(abi.encode("KOVAN", 42)); factory.registerNewChain(chainHash, EVM_HASH); // Try to register UEA with wrong VM hash @@ -161,7 +161,7 @@ contract UEAFactoryTest is Test { } function testRevertWhenRegisteringUEAWithUnregisteredChain() public { - bytes32 chainHash = keccak256(abi.encode("UNREGISTERED")); + bytes32 chainHash = keccak256(abi.encode("UNREGISTERED", 999)); // Try to register UEA for unregistered chain vm.expectRevert(Errors.InvalidInputArgs.selector); @@ -174,8 +174,8 @@ contract UEAFactoryTest is Test { address[] memory implementations = new address[](1); // Mismatched length // Register chains first (use different chains than in setUp) - chainHashes[0] = keccak256(abi.encode("KOVAN")); - chainHashes[1] = keccak256(abi.encode("METIS")); + chainHashes[0] = keccak256(abi.encode("KOVAN", 42)); + chainHashes[1] = keccak256(abi.encode("METIS", 1088)); factory.registerNewChain(chainHashes[0], EVM_HASH); factory.registerNewChain(chainHashes[1], EVM_HASH); @@ -186,23 +186,24 @@ contract UEAFactoryTest is Test { } function testDeployUEA() public { - // Use ETHEREUM chain which is already registered in setUp - bytes memory ownerBytes = abi.encodePacked(makeAddr("newowner")); - UniversalAccount memory _id = UniversalAccount({chain: "ETHEREUM", owner: ownerBytes}); + // Use eip155 chain which is already registered in setUp + bytes memory newOwnerBytes = abi.encodePacked(makeAddr("newowner")); + UniversalAccountId memory _id = UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: newOwnerBytes}); address ueaAddress = factory.deployUEA(_id); assertTrue(factory.hasCode(ueaAddress)); // Check the owner - (UniversalAccount memory retrievedAccount, bool isUEA) = factory.getOriginForUEA(ueaAddress); - assertEq(keccak256(abi.encode(retrievedAccount.chain)), keccak256(abi.encode(_id.chain))); + (UniversalAccountId memory retrievedAccount, bool isUEA) = factory.getOriginForUEA(ueaAddress); + assertEq(keccak256(abi.encode(retrievedAccount.chainNamespace)), keccak256(abi.encode(_id.chainNamespace))); assertEq(keccak256(retrievedAccount.owner), keccak256(_id.owner)); assertTrue(isUEA); } function testRevertWhenDeployingUEAForUnregisteredChain() public { - bytes memory ownerBytes = abi.encodePacked(makeAddr("owner")); - UniversalAccount memory _id = UniversalAccount({chain: "UNREGISTERED", owner: ownerBytes}); + bytes memory testOwnerBytes = abi.encodePacked(makeAddr("owner")); + UniversalAccountId memory _id = + UniversalAccountId({chainNamespace: "UNREGISTERED", chainId: 999, owner: testOwnerBytes}); // Try to deploy UEA for unregistered chain vm.expectRevert(Errors.InvalidInputArgs.selector); @@ -211,11 +212,12 @@ contract UEAFactoryTest is Test { function testRevertWhenDeployingUEAWithNoImplementation() public { // Register chain but no implementation - bytes32 chainHash = keccak256(abi.encode("NEW_CHAIN")); + bytes32 chainHash = keccak256(abi.encode("NEW_CHAIN", 888)); factory.registerNewChain(chainHash, MOVE_VM_HASH); - bytes memory ownerBytes = abi.encodePacked(makeAddr("owner")); - UniversalAccount memory _id = UniversalAccount({chain: "NEW_CHAIN", owner: ownerBytes}); + bytes memory testOwnerBytes = abi.encodePacked(makeAddr("owner")); + UniversalAccountId memory _id = + UniversalAccountId({chainNamespace: "NEW_CHAIN", chainId: 888, owner: testOwnerBytes}); // Try to deploy UEA with no implementation vm.expectRevert(Errors.InvalidInputArgs.selector); @@ -223,9 +225,10 @@ contract UEAFactoryTest is Test { } function testRevertWhenDeployingSameUEATwice() public { - // Use a new owner with ETHEREUM chain - bytes memory ownerBytes = abi.encodePacked(makeAddr("uniqueowner")); - UniversalAccount memory _id = UniversalAccount({chain: "ETHEREUM", owner: ownerBytes}); + // Use a new owner with eip155 chain + bytes memory uniqueOwnerBytes = abi.encodePacked(makeAddr("uniqueowner")); + UniversalAccountId memory _id = + UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: uniqueOwnerBytes}); factory.deployUEA(_id); @@ -235,9 +238,10 @@ contract UEAFactoryTest is Test { } function testComputeUEA() public { - // Use ETHEREUM chain which is already registered in setUp - bytes memory ownerBytes = abi.encodePacked(makeAddr("uniquecomputeowner")); - UniversalAccount memory _id = UniversalAccount({chain: "ETHEREUM", owner: ownerBytes}); + // Use eip155 chain which is already registered in setUp + bytes memory uniqueComputeOwnerBytes = abi.encodePacked(makeAddr("uniquecomputeowner")); + UniversalAccountId memory _id = + UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: uniqueComputeOwnerBytes}); address computedAddress = factory.computeUEA(_id); assertTrue(computedAddress != address(0)); @@ -248,9 +252,10 @@ contract UEAFactoryTest is Test { } function testGetUEAForOrigin() public { - // Use ETHEREUM chain which is already registered in setUp - bytes memory ownerBytes = abi.encodePacked(makeAddr("uniquegetowner")); - UniversalAccount memory _id = UniversalAccount({chain: "ETHEREUM", owner: ownerBytes}); + // Use eip155 chain which is already registered in setUp + bytes memory uniqueGetOwnerBytes = abi.encodePacked(makeAddr("uniquegetowner")); + UniversalAccountId memory _id = + UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: uniqueGetOwnerBytes}); // Test for non-deployed UEA (address uea, bool isDeployed) = factory.getUEAForOrigin(_id); @@ -267,7 +272,7 @@ contract UEAFactoryTest is Test { } function testSwapImplementation() public { - // Use ETHEREUM chain which is already registered in setUp + // Use eip155 chain which is already registered in setUp address initialImpl = factory.getUEA(ethereumChainHash); // Deploy a new implementation @@ -280,15 +285,17 @@ contract UEAFactoryTest is Test { } function testMultipleOwners() public { - // Create two different owners for ETHEREUM chain + // Create two different owners for eip155 chain (address owner1, uint256 owner1PK) = makeAddrAndKey("owner1"); (address owner2, uint256 owner2PK) = makeAddrAndKey("owner2"); bytes memory ownerBytes1 = abi.encodePacked(owner1); bytes memory ownerBytes2 = abi.encodePacked(owner2); - UniversalAccount memory account1 = UniversalAccount({chain: "ETHEREUM", owner: ownerBytes1}); - UniversalAccount memory account2 = UniversalAccount({chain: "ETHEREUM", owner: ownerBytes2}); + UniversalAccountId memory account1 = + UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: ownerBytes1}); + UniversalAccountId memory account2 = + UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: ownerBytes2}); // Compute UEA addresses address computedUEA1 = factory.computeUEA(account1); @@ -299,45 +306,50 @@ contract UEAFactoryTest is Test { } function testMultipleDeployments() public { - // Deploy 10 UEAs for ETHEREUM chain + // Deploy 10 UEAs for eip155 chain for (uint256 i = 0; i < 10; i++) { (address testOwner, uint256 testOwnerPK) = makeAddrAndKey(string(abi.encodePacked("testOwner", i))); - bytes memory ownerBytes = abi.encodePacked(testOwner); - UniversalAccount memory _id = UniversalAccount({chain: "ETHEREUM", owner: ownerBytes}); + bytes memory testOwnerBytes = abi.encodePacked(testOwner); + UniversalAccountId memory _id = + UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: testOwnerBytes}); address ueaAddress = factory.deployUEA(_id); assertTrue(factory.hasCode(ueaAddress)); - (UniversalAccount memory retrievedAccount, bool isUEA) = factory.getOriginForUEA(ueaAddress); - assertEq(keccak256(retrievedAccount.owner), keccak256(ownerBytes)); + (UniversalAccountId memory retrievedAccount, bool isUEA) = factory.getOriginForUEA(ueaAddress); + assertEq(keccak256(retrievedAccount.owner), keccak256(testOwnerBytes)); assertTrue(isUEA); } } function testUEAOwnerChange() public { - // Deploy UEA for original owner on ETHEREUM chain - bytes memory ownerBytes = abi.encodePacked(makeAddr("originalOwner")); - UniversalAccount memory _id = UniversalAccount({chain: "ETHEREUM", owner: ownerBytes}); + // Deploy UEA for original owner on eip155 chain + bytes memory originalOwnerBytes = abi.encodePacked(makeAddr("originalOwner")); + UniversalAccountId memory _id = + UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: originalOwnerBytes}); address ueaAddress = factory.deployUEA(_id); // Create a new owner (address newOwner, uint256 newOwnerPK) = makeAddrAndKey("newOwner"); bytes memory newOwnerBytes = abi.encodePacked(newOwner); - UniversalAccount memory newAccount = UniversalAccount({chain: "ETHEREUM", owner: newOwnerBytes}); + UniversalAccountId memory newAccount = + UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: newOwnerBytes}); // The owner mapping can't be changed - a new UEA would need to be deployed - (UniversalAccount memory retrievedAccount, bool isUEA) = factory.getOriginForUEA(ueaAddress); - assertEq(keccak256(retrievedAccount.owner), keccak256(ownerBytes)); - assertEq(keccak256(abi.encode(retrievedAccount.chain)), keccak256(abi.encode(_id.chain))); + (UniversalAccountId memory retrievedAccount, bool isUEA) = factory.getOriginForUEA(ueaAddress); + assertEq(keccak256(retrievedAccount.owner), keccak256(originalOwnerBytes)); + assertEq(keccak256(abi.encode(retrievedAccount.chainNamespace)), keccak256(abi.encode(_id.chainNamespace))); assertTrue(isUEA); // Deploy UEA for new owner - this should be a different address address newUEAAddress = factory.deployUEA(newAccount); assertNotEq(ueaAddress, newUEAAddress); - (UniversalAccount memory newRetrievedAccount, bool isNewUEA) = factory.getOriginForUEA(newUEAAddress); + (UniversalAccountId memory newRetrievedAccount, bool isNewUEA) = factory.getOriginForUEA(newUEAAddress); assertEq(keccak256(newRetrievedAccount.owner), keccak256(newOwnerBytes)); - assertEq(keccak256(abi.encode(newRetrievedAccount.chain)), keccak256(abi.encode(newAccount.chain))); + assertEq( + keccak256(abi.encode(newRetrievedAccount.chainNamespace)), keccak256(abi.encode(newAccount.chainNamespace)) + ); assertTrue(isNewUEA); } @@ -346,7 +358,7 @@ contract UEAFactoryTest is Test { vm.prank(nonOwner); vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, nonOwner)); - bytes32 chainHash = keccak256(abi.encode("APTOS")); + bytes32 chainHash = keccak256(abi.encode("APTOS", 1)); factory.registerNewChain(chainHash, MOVE_VM_HASH); // Test that owner can register implementations @@ -360,7 +372,7 @@ contract UEAFactoryTest is Test { } function testDeploymentCreate2() public { - UniversalAccount memory _id = UniversalAccount({chain: "ETHEREUM", owner: ownerBytes}); + UniversalAccountId memory _id = UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: ownerBytes}); address ueaAddress = factory.deployUEA(_id); bytes32 salt = factory.generateSalt(_id); @@ -369,7 +381,7 @@ contract UEAFactoryTest is Test { } function testComputeUEAAddressNonEVM() public { - UniversalAccount memory _id = UniversalAccount({chain: "SOLANA", owner: ownerNonEVM}); + UniversalAccountId memory _id = UniversalAccountId({chainNamespace: "solana", chainId: 101, owner: ownerNonEVM}); address ueaAddress = factory.deployUEA(_id); address computedAddress = factory.computeUEA(_id); @@ -381,11 +393,11 @@ contract UEAFactoryTest is Test { } function testMissingImplementation() public { - bytes32 moveChainHash = keccak256(abi.encode("APTOS")); + bytes32 moveChainHash = keccak256(abi.encode("APTOS", 1)); factory.registerNewChain(moveChainHash, MOVE_VM_HASH); - // Create an UniversalAccount with VM type that has no implementation - UniversalAccount memory _id = UniversalAccount({chain: "APTOS", owner: ownerBytes}); + // Create an UniversalAccountId with VM type that has no implementation + UniversalAccountId memory _id = UniversalAccountId({chainNamespace: "APTOS", chainId: 1, owner: ownerBytes}); // Try to deploy with missing implementation vm.expectRevert(Errors.InvalidInputArgs.selector); @@ -394,14 +406,14 @@ contract UEAFactoryTest is Test { function testMultipleChainsSameVM() public { // Register two different chains with the same VM type (EVM) - bytes32 ethereumChainHash = keccak256(abi.encode("ETHEREUM")); - bytes32 polygonChainHash = keccak256(abi.encode("POLYGON")); + bytes32 ethereumChainHashLocal = keccak256(abi.encode("eip155", 1)); + bytes32 polygonChainHash = keccak256(abi.encode("POLYGON", 137)); // Polygon is not registered yet factory.registerNewChain(polygonChainHash, EVM_HASH); // We already registered EVM implementation for Ethereum, should work for Polygon too - (bytes32 vmHashEth, bool isRegisteredEth) = factory.getVMType(ethereumChainHash); + (bytes32 vmHashEth, bool isRegisteredEth) = factory.getVMType(ethereumChainHashLocal); (bytes32 vmHashPoly, bool isRegisteredPoly) = factory.getVMType(polygonChainHash); // Verify both chains use the same VM type @@ -410,12 +422,14 @@ contract UEAFactoryTest is Test { assertTrue(isRegisteredPoly); // Both chains should use the same implementation - assertEq(factory.getUEA(ethereumChainHash), factory.getUEA(polygonChainHash)); + assertEq(factory.getUEA(ethereumChainHashLocal), factory.getUEA(polygonChainHash)); // Deploy UEAs for both chains and verify they have different addresses despite same VM - UniversalAccount memory ethAccount = UniversalAccount({chain: "ETHEREUM", owner: ownerBytes}); + UniversalAccountId memory ethAccount = + UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: ownerBytes}); - UniversalAccount memory polyAccount = UniversalAccount({chain: "POLYGON", owner: ownerBytes}); + UniversalAccountId memory polyAccount = + UniversalAccountId({chainNamespace: "POLYGON", chainId: 137, owner: ownerBytes}); address ethUEA = factory.deployUEA(ethAccount); @@ -440,7 +454,7 @@ contract UEAFactoryTest is Test { assertEq(factory.owner(), newOwner); // Try to register a chain with old owner, should fail - bytes32 chainHash = keccak256(abi.encode("TestChain")); + bytes32 chainHash = keccak256(abi.encode("TestChain", 123)); vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, address(this))); factory.registerNewChain(chainHash, MOVE_VM_HASH); @@ -466,13 +480,13 @@ contract UEAFactoryTest is Test { // Register multiple chains string[] memory chains = new string[](3); - chains[0] = "ETHEREUM"; // Ethereum + chains[0] = "eip155"; // Ethereum chains[1] = "POLYGON"; // Polygon chains[2] = "BSC"; // BSC // Register chains that aren't already registered for (uint256 i = 1; i < 3; i++) { - bytes32 chainHash = keccak256(abi.encode(chains[i])); + bytes32 chainHash = keccak256(abi.encode(chains[i], i == 1 ? 137 : 56)); // Polygon (137) and BSC (56) // Check if chain is already registered (, bool isRegistered) = factory.getVMType(chainHash); if (!isRegistered) { @@ -484,23 +498,28 @@ contract UEAFactoryTest is Test { address[] memory deployedUEAs = new address[](5); for (uint256 i = 0; i < 5; i++) { string memory chain = chains[i % 3]; // Cycle through chains + uint256 chainId = i % 3 == 0 ? 1 : (i % 3 == 1 ? 137 : 56); // Ethereum, Polygon, BSC chain IDs - UniversalAccount memory account = UniversalAccount({chain: chain, owner: ownerValues[i]}); + UniversalAccountId memory account = + UniversalAccountId({chainNamespace: chain, chainId: chainId, owner: ownerValues[i]}); bytes32 salt = factory.generateSalt(account); deployedUEAs[i] = factory.deployUEA(account); // Verify mappings are consistent assertEq(factory.UOA_to_UEA(salt), deployedUEAs[i]); - (UniversalAccount memory retrievedAccount, bool isUEA) = factory.getOriginForUEA(deployedUEAs[i]); + (UniversalAccountId memory retrievedAccount, bool isUEA) = factory.getOriginForUEA(deployedUEAs[i]); assertEq(keccak256(retrievedAccount.owner), keccak256(ownerValues[i])); - assertEq(keccak256(abi.encode(retrievedAccount.chain)), keccak256(abi.encode(chain))); + assertEq(keccak256(abi.encode(retrievedAccount.chainNamespace)), keccak256(abi.encode(chain))); assertTrue(isUEA); } // Verify all deployed accounts are retrievable for (uint256 i = 0; i < 5; i++) { - UniversalAccount memory account = UniversalAccount({chain: chains[i % 3], owner: ownerValues[i]}); + string memory chain = chains[i % 3]; + uint256 chainId = i % 3 == 0 ? 1 : (i % 3 == 1 ? 137 : 56); // Ethereum, Polygon, BSC chain IDs + UniversalAccountId memory account = + UniversalAccountId({chainNamespace: chain, chainId: chainId, owner: ownerValues[i]}); bytes32 salt = factory.generateSalt(account); assertEq(factory.UOA_to_UEA(salt), deployedUEAs[i]); } @@ -508,11 +527,12 @@ contract UEAFactoryTest is Test { function testChainConfigChanges() public { // Create a new chain and register it - bytes32 chainHash = keccak256(abi.encode("KOVAN")); + bytes32 chainHash = keccak256(abi.encode("KOVAN", 42)); factory.registerNewChain(chainHash, EVM_HASH); // Deploy an account with the initial implementation - UniversalAccount memory account = UniversalAccount({chain: "KOVAN", owner: ownerBytes}); + UniversalAccountId memory account = + UniversalAccountId({chainNamespace: "KOVAN", chainId: 42, owner: ownerBytes}); // Get initial implementation address address initialImpl = factory.getUEA(chainHash); @@ -524,7 +544,7 @@ contract UEAFactoryTest is Test { UEA_EVM newImpl = new UEA_EVM(); // Change implementation for EVM type - bytes32 evmChainHash = keccak256(abi.encode("ETHEREUM")); + bytes32 evmChainHash = keccak256(abi.encode("eip155", 1)); factory.registerUEA(evmChainHash, EVM_HASH, address(newImpl)); // Verify implementation has changed @@ -535,7 +555,8 @@ contract UEAFactoryTest is Test { (address owner2, uint256 owner2PK) = makeAddrAndKey("owner2"); bytes memory owner2Key = abi.encodePacked(owner2); - UniversalAccount memory account2 = UniversalAccount({chain: "KOVAN", owner: owner2Key}); + UniversalAccountId memory account2 = + UniversalAccountId({chainNamespace: "KOVAN", chainId: 42, owner: owner2Key}); // Compute address with new implementation address newUEAAddress = factory.computeUEA(account2); @@ -546,14 +567,15 @@ contract UEAFactoryTest is Test { function testMappingConsistency() public { // Deploy a UEA - UniversalAccount memory account = UniversalAccount({chain: "ETHEREUM", owner: ownerBytes}); + UniversalAccountId memory account = + UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: ownerBytes}); address ueaAddress = factory.deployUEA(account); // Test getOriginForUEA - (UniversalAccount memory retrievedAccount, bool isUEA) = factory.getOriginForUEA(ueaAddress); + (UniversalAccountId memory retrievedAccount, bool isUEA) = factory.getOriginForUEA(ueaAddress); assertEq(keccak256(retrievedAccount.owner), keccak256(ownerBytes)); - assertEq(keccak256(abi.encode(retrievedAccount.chain)), keccak256(abi.encode(account.chain))); + assertEq(keccak256(abi.encode(retrievedAccount.chainNamespace)), keccak256(abi.encode(account.chainNamespace))); assertTrue(isUEA); // Test getUEAForOrigin @@ -563,16 +585,17 @@ contract UEAFactoryTest is Test { // Test with non-existent UEA address randomAddr = makeAddr("random"); - (UniversalAccount memory randomAccount, bool isRandomUEA) = factory.getOriginForUEA(randomAddr); + (UniversalAccountId memory randomAccount, bool isRandomUEA) = factory.getOriginForUEA(randomAddr); assertFalse(isRandomUEA); assertEq(randomAccount.owner.length, 0); - assertEq(bytes(randomAccount.chain).length, 0); + assertEq(bytes(randomAccount.chainNamespace).length, 0); // Test with non-existent owner key but predictable address (address newOwner, uint256 newOwnerPK) = makeAddrAndKey("newOwner"); bytes memory newOwnerKey = abi.encodePacked(newOwner); - UniversalAccount memory newAccount = UniversalAccount({chain: "ETHEREUM", owner: newOwnerKey}); + UniversalAccountId memory newAccount = + UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: newOwnerKey}); // Compute without deploying address computedAddress = factory.computeUEA(newAccount); @@ -591,7 +614,8 @@ contract UEAFactoryTest is Test { assertFalse(factory.hasCode(address(0x123))); // Newly deployed UEA should have code - UniversalAccount memory account = UniversalAccount({chain: "ETHEREUM", owner: ownerBytes}); + UniversalAccountId memory account = + UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: ownerBytes}); address ueaAddress = factory.deployUEA(account); assertTrue(factory.hasCode(ueaAddress)); @@ -603,8 +627,10 @@ contract UEAFactoryTest is Test { bytes memory owner2Key = abi.encodePacked(makeAddr("owner2")); // Create accounts for the same chain but different owners - UniversalAccount memory account1 = UniversalAccount({chain: "ETHEREUM", owner: owner1Key}); - UniversalAccount memory account2 = UniversalAccount({chain: "ETHEREUM", owner: owner2Key}); + UniversalAccountId memory account1 = + UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: owner1Key}); + UniversalAccountId memory account2 = + UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: owner2Key}); // Deploy both UEAs address uea1 = factory.deployUEA(account1); @@ -614,8 +640,8 @@ contract UEAFactoryTest is Test { assertTrue(uea1 != uea2); // Verify the owner keys are mapped correctly - (UniversalAccount memory retrievedAccount1, bool isUEA1) = factory.getOriginForUEA(uea1); - (UniversalAccount memory retrievedAccount2, bool isUEA2) = factory.getOriginForUEA(uea2); + (UniversalAccountId memory retrievedAccount1, bool isUEA1) = factory.getOriginForUEA(uea1); + (UniversalAccountId memory retrievedAccount2, bool isUEA2) = factory.getOriginForUEA(uea2); assertEq(keccak256(retrievedAccount1.owner), keccak256(owner1Key)); assertEq(keccak256(retrievedAccount2.owner), keccak256(owner2Key)); @@ -629,37 +655,38 @@ contract UEAFactoryTest is Test { address randomAddr = makeAddr("randomNative"); // Check if it's correctly identified as a native account - (UniversalAccount memory account, bool isUEA) = factory.getOriginForUEA(randomAddr); + (UniversalAccountId memory account, bool isUEA) = factory.getOriginForUEA(randomAddr); assertFalse(isUEA); // For native accounts, both owner and chain should be empty assertEq(account.owner.length, 0); - assertEq(bytes(account.chain).length, 0); + assertEq(bytes(account.chainNamespace).length, 0); } // Test for comparing native and UEA accounts function testCompareNativeAndUEAAccounts() public { // Create and deploy a UEA - bytes memory ownerBytes = abi.encodePacked(makeAddr("uea_owner")); - UniversalAccount memory uea_id = UniversalAccount({chain: "ETHEREUM", owner: ownerBytes}); + bytes memory ueaOwnerBytes = abi.encodePacked(makeAddr("uea_owner")); + UniversalAccountId memory uea_id = + UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: ueaOwnerBytes}); address ueaAddress = factory.deployUEA(uea_id); // Create a random native address address nativeAddr = makeAddr("native_user"); // Get account info for both - (UniversalAccount memory ueaAccount, bool isUEA) = factory.getOriginForUEA(ueaAddress); - (UniversalAccount memory nativeAccount, bool isNativeUEA) = factory.getOriginForUEA(nativeAddr); + (UniversalAccountId memory ueaAccount, bool isUEA) = factory.getOriginForUEA(ueaAddress); + (UniversalAccountId memory nativeAccount, bool isNativeUEA) = factory.getOriginForUEA(nativeAddr); // UEA should have proper data and be a UEA assertTrue(isUEA); - assertEq(keccak256(ueaAccount.owner), keccak256(ownerBytes)); - assertEq(keccak256(abi.encode(ueaAccount.chain)), keccak256(abi.encode("ETHEREUM"))); + assertEq(keccak256(ueaAccount.owner), keccak256(ueaOwnerBytes)); + assertEq(keccak256(abi.encode(ueaAccount.chainNamespace)), keccak256(abi.encode("eip155"))); // Native account should be marked as not a UEA and have empty data assertFalse(isNativeUEA); assertEq(nativeAccount.owner.length, 0); - assertEq(bytes(nativeAccount.chain).length, 0); + assertEq(bytes(nativeAccount.chainNamespace).length, 0); } // Test for multiple native accounts all returning empty data @@ -672,11 +699,11 @@ contract UEAFactoryTest is Test { // Check all addresses are correctly identified as native with empty data for (uint256 i = 0; i < nativeAddrs.length; i++) { - (UniversalAccount memory account, bool isUEA) = factory.getOriginForUEA(nativeAddrs[i]); + (UniversalAccountId memory account, bool isUEA) = factory.getOriginForUEA(nativeAddrs[i]); assertFalse(isUEA); assertEq(account.owner.length, 0); - assertEq(bytes(account.chain).length, 0); + assertEq(bytes(account.chainNamespace).length, 0); } } } diff --git a/test/UEA_EVM.t.sol b/test/UEA_EVM.t.sol index 4835609..ed2bdee 100644 --- a/test/UEA_EVM.t.sol +++ b/test/UEA_EVM.t.sol @@ -43,13 +43,13 @@ contract UEA_EVMTest is Test { ownerBytes = abi.encodePacked(owner); // Register EVM chain and implementation - bytes32 evmChainHash = keccak256(abi.encode("ETHEREUM")); + bytes32 evmChainHash = keccak256(abi.encode("eip155", 1)); factory.registerNewChain(evmChainHash, EVM_HASH); factory.registerUEA(evmChainHash, EVM_HASH, address(ueaEVMImpl)); } modifier deployEvmSmartAccount() { - UniversalAccount memory _owner = UniversalAccount({chain: "ETHEREUM", owner: ownerBytes}); + UniversalAccountId memory _owner = UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: ownerBytes}); address smartAccountAddress = factory.deployUEA(_owner); evmSmartAccountInstance = UEA_EVM(payable(smartAccountAddress)); @@ -57,18 +57,18 @@ contract UEA_EVMTest is Test { } function testUEAImplementation() public view { - bytes32 evmChainHash = keccak256(abi.encode("ETHEREUM")); + bytes32 evmChainHash = keccak256(abi.encode("eip155", 1)); assertEq(address(factory.getUEA(evmChainHash)), address(ueaEVMImpl)); } function testUniversalAccount() public deployEvmSmartAccount { - UniversalAccount memory account = evmSmartAccountInstance.universalAccount(); - assertEq(account.chain, "ETHEREUM"); + UniversalAccountId memory account = evmSmartAccountInstance.universalAccount(); + assertEq(account.chainNamespace, "eip155"); assertEq(account.owner, ownerBytes); } function testDeploymentCreate2() public deployEvmSmartAccount { - UniversalAccount memory _owner = UniversalAccount({chain: "ETHEREUM", owner: ownerBytes}); + UniversalAccountId memory _owner = UniversalAccountId({chainNamespace: "eip155", chainId: 1, owner: ownerBytes}); bytes32 salt = factory.generateSalt(_owner); assertEq(address(evmSmartAccountInstance), address(factory.UOA_to_UEA(salt))); assertEq(address(evmSmartAccountInstance), address(factory.computeUEA(_owner))); diff --git a/test/UEA_SVM.t.sol b/test/UEA_SVM.t.sol index 0a563a7..f856162 100644 --- a/test/UEA_SVM.t.sol +++ b/test/UEA_SVM.t.sol @@ -40,13 +40,14 @@ contract UEA_SVMTest is Test { svmSmartAccountImpl = new UEA_SVM(); // Register SVM chain and implementation - bytes32 svmChainHash = keccak256(abi.encode("SOLANA")); + bytes32 svmChainHash = keccak256(abi.encode("solana", 101)); factory.registerNewChain(svmChainHash, SVM_HASH); factory.registerUEA(svmChainHash, SVM_HASH, address(svmSmartAccountImpl)); } modifier deploySvmSmartAccount() { - UniversalAccount memory _owner = UniversalAccount({chain: "SOLANA", owner: ownerBytes}); + UniversalAccountId memory _owner = + UniversalAccountId({chainNamespace: "solana", chainId: 101, owner: ownerBytes}); address smartAccountAddress = factory.deployUEA(_owner); svmSmartAccountInstance = UEA_SVM(payable(smartAccountAddress)); @@ -54,7 +55,7 @@ contract UEA_SVMTest is Test { } function testRegisterChain() public view { - bytes32 svmChainHash = keccak256(abi.encode("SOLANA")); + bytes32 svmChainHash = keccak256(abi.encode("solana", 101)); (bytes32 vmHash, bool isRegistered) = factory.getVMType(svmChainHash); assertEq(vmHash, SVM_HASH); assertTrue(isRegistered); @@ -65,8 +66,8 @@ contract UEA_SVMTest is Test { } function testUniversalAccount() public deploySvmSmartAccount { - UniversalAccount memory account = svmSmartAccountInstance.universalAccount(); - assertEq(account.chain, "SOLANA"); + UniversalAccountId memory account = svmSmartAccountInstance.universalAccount(); + assertEq(account.chainNamespace, "solana"); assertEq(account.owner, ownerBytes); } diff --git a/test/coverage/src/UEA/UEA_EVM.sol.gcov.html b/test/coverage/src/UEA/UEA_EVM.sol.gcov.html index d471052..c179c1e 100644 --- a/test/coverage/src/UEA/UEA_EVM.sol.gcov.html +++ b/test/coverage/src/UEA/UEA_EVM.sol.gcov.html @@ -78,7 +78,7 @@ 7 : : import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; 8 : : import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; 9 : : import { - 10 : : UniversalAccount, + 10 : : UniversalAccountId, 11 : : UniversalPayload, 12 : : DOMAIN_SEPARATOR_TYPEHASH, 13 : : UNIVERSAL_PAYLOAD_TYPEHASH @@ -95,7 +95,7 @@ 24 : : using ECDSA for bytes32; 25 : : 26 : : // @notice The Universal Account information - 27 : : UniversalAccount internal id; + 27 : : UniversalAccountId internal id; 28 : : // @notice Flag to track initialization status 29 : : bool private initialized; 30 : : // @notice The nonce for the UEA @@ -106,7 +106,7 @@ 35 : : /** 36 : : * @inheritdoc IUEA 37 : : */ - 38 : 38 : function initialize(UniversalAccount memory _id) external { + 38 : 38 : function initialize(UniversalAccountId memory _id) external { 39 [ # ]: 0 : if (initialized) { 40 : 0 : revert Errors.AlreadyInitialized(); 41 : : } @@ -134,7 +134,7 @@ 63 : : /** 64 : : * @inheritdoc IUEA 65 : : */ - 66 : 1 : function universalAccount() public view returns (UniversalAccount memory) { + 66 : 1 : function universalAccount() public view returns (UniversalAccountId memory) { 67 : 1 : return id; 68 : : } 69 : : diff --git a/test/coverage/src/UEA/UEA_SVM.sol.gcov.html b/test/coverage/src/UEA/UEA_SVM.sol.gcov.html index df27ec7..d2770c4 100644 --- a/test/coverage/src/UEA/UEA_SVM.sol.gcov.html +++ b/test/coverage/src/UEA/UEA_SVM.sol.gcov.html @@ -76,7 +76,7 @@ 5 : : import {IUEA} from "../Interfaces/IUEA.sol"; 6 : : import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; 7 : : import { - 8 : : UniversalAccount, + 8 : : UniversalAccountId, 9 : : UniversalPayload, 10 : : DOMAIN_SEPARATOR_TYPEHASH, 11 : : UNIVERSAL_PAYLOAD_TYPEHASH @@ -91,7 +91,7 @@ 20 : : 21 : : contract UEA_SVM is ReentrancyGuard, IUEA { 22 : : // @notice The Universal Account information - 23 : : UniversalAccount internal id; + 23 : : UniversalAccountId internal id; 24 : : // @notice Flag to track initialization status 25 : : bool private initialized; 26 : : // @notice The nonce for the UEA @@ -120,7 +120,7 @@ 49 : : /** 50 : : * @inheritdoc IUEA 51 : : */ - 52 : 7 : function initialize(UniversalAccount memory _id) external { + 52 : 7 : function initialize(UniversalAccountId memory _id) external { 53 [ # ]: 0 : if (initialized) { 54 : 0 : revert Errors.AlreadyInitialized(); 55 : : } @@ -132,7 +132,7 @@ 61 : : /** 62 : : * @inheritdoc IUEA 63 : : */ - 64 : 1 : function universalAccount() public view returns (UniversalAccount memory) { + 64 : 1 : function universalAccount() public view returns (UniversalAccountId memory) { 65 : 1 : return id; 66 : : } 67 : : diff --git a/test/coverage/src/UEAFactoryV1.sol.gcov.html b/test/coverage/src/UEAFactoryV1.sol.gcov.html index 327f2f4..862331e 100644 --- a/test/coverage/src/UEAFactoryV1.sol.gcov.html +++ b/test/coverage/src/UEAFactoryV1.sol.gcov.html @@ -79,7 +79,7 @@ 8 : : import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 9 : : import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 10 : : import {IUEAFactory} from "./Interfaces/IUEAFactory.sol"; - 11 : : import {UniversalAccount} from "./libraries/Types.sol"; + 11 : : import {UniversalAccountId} from "./libraries/Types.sol"; 12 : : 13 : : /** 14 : : * @title UEAFactoryV1 @@ -110,11 +110,11 @@ 39 : : /// @notice Maps VM type hashes to their corresponding UEA implementation addresses 40 : : mapping(bytes32 => address) public UEA_VM; 41 : : - 42 : : /// @notice Maps UniversalAccount(hash) to their deployed UEA contract addresses + 42 : : /// @notice Maps UniversalAccountId(hash) to their deployed UEA contract addresses 43 : : mapping(bytes32 => address) public UOA_to_UEA; 44 : : 45 : : /// @notice Maps UEA addresses to their Universal Account information - 46 : : mapping(address => UniversalAccount) private UEA_to_UOA; + 46 : : mapping(address => UniversalAccountId) private UEA_to_UOA; 47 : : 48 : : /// @notice Maps chain identifiers to their registered VM type hashes 49 : : mapping(bytes32 => bytes32) public CHAIN_to_VM; @@ -223,7 +223,7 @@ 152 : : * @notice Will revert if the account already exists, the chain is not registered, 153 : : * or if no UEA implementation is available for the chain's VM type 154 : : */ - 155 : 49 : function deployUEA(UniversalAccount memory _id) external returns (address) { + 155 : 49 : function deployUEA(UniversalAccountId memory _id) external returns (address) { 156 : 49 : bytes32 salt = generateSalt(_id); 157 [ + ]: 49 : if (UOA_to_UEA[salt] != address(0)) { 158 : 1 : revert Errors.AccountAlreadyExists(); @@ -257,7 +257,7 @@ 186 : : * @notice Will revert if the chain is not registered or if no UEA implementation 187 : : * is available for the chain's VM type 188 : : */ - 189 : 8 : function computeUEA(UniversalAccount memory _id) public view returns (address) { + 189 : 8 : function computeUEA(UniversalAccountId memory _id) public view returns (address) { 190 : 10 : bytes32 chainHash = keccak256(abi.encode(_id.chain)); 191 : 10 : (bytes32 vmHash, bool isRegistered) = getVMType(chainHash); 192 [ # ]: 10 : if (!isRegistered) { @@ -287,7 +287,7 @@ 216 : : } 217 : : 218 : : /// @inheritdoc IUEAFactory - 219 : 28 : function getOriginForUEA(address addr) external view returns (UniversalAccount memory account, bool isUEA) { + 219 : 28 : function getOriginForUEA(address addr) external view returns (UniversalAccountId memory account, bool isUEA) { 220 : 28 : account = UEA_to_UOA[addr]; 221 : : 222 : : // If the address has no associated Universal Account (owner.length == 0), @@ -301,8 +301,8 @@ 230 : : } 231 : : 232 : : /// @inheritdoc IUEAFactory - 233 : 4 : function getUEAForOrigin(UniversalAccount memory _id) external view returns (address uea, bool isDeployed) { - 234 : : // Generate salt from the UniversalAccount struct + 233 : 4 : function getUEAForOrigin(UniversalAccountId memory _id) external view returns (address uea, bool isDeployed) { + 234 : : // Generate salt from the UniversalAccountId struct 235 : 4 : bytes32 salt = generateSalt(_id); 236 : : 237 : : // Check if we already have a mapping @@ -326,7 +326,7 @@ 255 : : * @param _id The Universal Account information 256 : : * @return A unique salt derived from the account information 257 : : */ - 258 : 12 : function generateSalt(UniversalAccount memory _id) public pure returns (bytes32) { + 258 : 12 : function generateSalt(UniversalAccountId memory _id) public pure returns (bytes32) { 259 : 75 : return keccak256(abi.encode(_id)); 260 : : } 261 : : }