Development of Unified Accounts System
Have you faced the situation where you need to keep a dozen wallets for working in different blockchains and manually transfer liquidity? Our team of engineers solves this problem with a unified accounts system — a single account that works across all networks. This is not just a convenient abstraction, but an architectural solution combining deterministic contract deployment, cross-chain synchronization, and intent-oriented execution. Let's examine how this works at the smart contract and infrastructure level. Unified accounts evolve from a multi-chain approach (many wallets) to chain-abstraction (one wallet, transparent multi-chain). The task is non-trivial: implementing it correctly means solving several independent technical problems simultaneously. Our team's experience in cross-chain development — over 10 successful projects.
How does a unified account solve the liquidity fragmentation problem?
When working with several L1/L2 chains (Ethereum, Polygon, Arbitrum, Optimism, Base), the user is forced to keep funds on each network separately. A unified account uses CREATE2 for deterministic addressing and synchronizes state through a secure bridge. This allows having a single logical balance that is automatically available on all chains without manual transfers. Compare: in a traditional scheme, to stake 100 USDC in a protocol on Arbitrum, you first need to transfer USDC from Ethereum — that's at least two transactions and 15 minutes of waiting. In a unified account, the user indicates the intent, and the system itself routes liquidity through the optimal bridge in a couple of clicks. We guarantee that gas and time savings reach 40–60% on typical operations.
What is a unified account at the technical level?
Naive implementation: a smart contract on each supported chain with the same address (via CREATE2), state synchronization via a bridge. This works but creates complexity: state is fragmented, synchronization has latency and cost.
Advanced implementation separates four layers: Identity, Intent, Execution, Settlement. Each layer solves its own task, and together they provide a seamless cross-chain experience.
| Layer | Task |
|---|---|
| Identity | Defines the user on all chains (one address) |
| Intent | Accepts the user's intent (what to do) |
| Execution | Selects the optimal chain and route for execution |
| Settlement | Performs final settlement and synchronization |
Deterministic Addresses via CREATE2
The first step is the same address on all EVM chains:
// Factory contract with the same address on all chains contract AccountFactory { function deployAccount( bytes32 salt, bytes calldata initCode ) external returns (address account) { // CREATE2: address depends only on factory address + salt + initCode // If factory is deployed via keyless deployment (same address on all EVM), // then Account will also have the same address assembly { account := create2(0, add(initCode, 32), mload(initCode), salt) } if (account == address(0)) revert DeploymentFailed(); } function predictAddress( bytes32 salt, bytes32 initCodeHash ) external view returns (address) { return address(uint160(uint256(keccak256(abi.encodePacked( bytes1(0xff), address(this), salt, initCodeHash ))))); } } Keyless deployment (via ERC-2470 Singleton Factory or Nick's method) ensures the same factory address on all EVM chains. Consequently — the same salt + initCode = the same account address on all chains. ERC-2470: Singleton Factory is the standard method for deploying contracts with the same address.
Cross-chain state synchronization
The second step — synchronizing account data between chains. For example: the user updates the list of owners on Ethereum, this should be reflected on Polygon.
Pattern: Primary chain + synchronization via bridge:
contract UnifiedAccountPrimary { // Primary state stored on the "home" chain mapping(address => bool) public owners; uint256 public nonce; // Synchronize changes to other chains function addOwnerAndSync( address newOwner, uint64[] calldata targetChains, address[] calldata targetAccounts ) external onlyOwner { owners[newOwner] = true; // Send update via bridge (CCIP, Axelar, LayerZero) for (uint i = 0; i < targetChains.length; i++) { bytes memory payload = abi.encode( "ADD_OWNER", newOwner, ++nonce ); bridge.sendMessage(targetChains[i], targetAccounts[i], payload); } emit OwnerAdded(newOwner); } } contract UnifiedAccountReplica { // Replica receives updates from Primary uint256 public lastSyncedNonce; function receiveSync( bytes calldata payload, bytes32 originMessageId ) external onlyBridge { (string memory action, address target, uint256 nonce) = abi.decode(payload, (string, address, uint256)); // Replay protection: nonce must be the next require(nonce == lastSyncedNonce + 1, "Invalid nonce"); lastSyncedNonce = nonce; if (keccak256(bytes(action)) == keccak256(bytes("ADD_OWNER"))) { _addOwner(target); } } } Intent-based execution
Unified account accepts user intents, not specific transactions. The user says "I want to stake 100 USDC in protocol X" — the system itself decides where to get USDC and on which chain to execute:
interface UserIntent { action: "stake" | "swap" | "transfer" | "borrow"; targetProtocol: string; targetChain?: number; // optional — if not specified, system chooses inputToken: string; inputAmount: string; outputToken?: string; minOutputAmount?: string; deadline?: number; } class IntentRouter { async resolveIntent(intent: UserIntent, userProfile: UserProfile): Promise<ExecutionPlan> { // 1. Find the best chain for execution const targetChain = intent.targetChain || await this.findOptimalChain(intent, userProfile); // 2. Determine where to get funds const fundingSource = await this.findBestFundingSource( intent.inputToken, intent.inputAmount, userProfile.balances, targetChain ); // 3. Build execution plan const steps: ExecutionStep[] = []; if (fundingSource.chainId !== targetChain) { // Need a bridge steps.push({ type: "bridge", fromChain: fundingSource.chainId, toChain: targetChain, token: intent.inputToken, amount: intent.inputAmount, bridgeProtocol: await this.selectBridge(fundingSource.chainId, targetChain), }); } // Main action steps.push({ type: intent.action, chainId: targetChain, protocol: intent.targetProtocol, ... }); return { steps, estimatedGas: await this.estimateTotalGas(steps), estimatedTime: this.estimateTime(steps), }; } } Gasless cross-chain operations
For a complete UX of a unified account — the user should not think about gas. The system abstracts this:
// User pays in USDC, system converts to native tokens async function executeGasless( intent: UserIntent, feeToken: "USDC" | "USDT" | string ): Promise<string> { const plan = await intentRouter.resolveIntent(intent, userProfile); // Calculate total cost in feeToken const totalCostInFeeToken = await priceOracle.convertGasCost( plan.estimatedGas, plan.steps.map(s => s.chainId), feeToken ); // Get UserOperation with sponsored gas const userOp = await buildSponsordUserOp(plan, feeToken, totalCostInFeeToken); // Sign once on the source chain const signedOp = await userAccount.signUserOperation(userOp); // Executor relay executes all steps return relayer.submitIntent(signedOp); } Account abstraction as a foundation
Unified accounts are natively built on ERC-4337: Smart account instead of EOA on each chain, UserOperations as a unit of intent, Bundler as executor, Paymaster as gas abstraction layer. ZeroDev Kernel, Safe with modules, or a custom implementation — the choice depends on the required flexibility.
The atomicity problem
Critical question: what happens if step 1 (bridge) succeeds, but step 2 (stake on the target chain) fails with an error? Options: Optimistic execution (continue, record pending state, retry on failure), Atomic through escrow (funds in escrow contract until final step confirmation), Two-phase commit (prepare → commit/rollback). In practice, most systems use optimistic with retry mechanism and manual fallback (funds remain on the target chain if the action is not executed).
Comparison of cross-chain communication approaches
| Protocol | Type | Latency | Security |
|---|---|---|---|
| LayerZero | light oracle + relayer | ~1 minute | depends on oracle |
| Axelar | validator set | ~5 minutes | 2/3 validators |
| CCIP (Chainlink) | decentralized oracles | ~10 minutes | audited |
What is included in the unified account development
- Analysis of target networks and synchronization requirements.
- Designing identity and intent layer architecture.
- Deploying factory contracts via keyless deployment.
- Integrating a bridge protocol and setting up synchronization.
- Implementing intent router with gas abstraction support.
- Integrating frontend SDK.
- Deploying to testnet and conducting an audit.
- Deploying to mainnet and launch.
Timelines
- Basic unified identity (CREATE2 same addresses, basic sync): 4-6 weeks
- Intent routing + cross-chain execution: 6-8 weeks
- Gas abstraction + feeToken: 3-4 weeks
- Production hardening + security audit: 6-8 weeks
- Total: 4-6 months
The budget for unified account development is calculated individually. Contact us, provide the specification of used chains and synchronization requirements — we will prepare an individual proposal with accurate timelines. Request a consultation to evaluate your project and get professional turnkey development.







