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.







