Ethereum to Aptos Migration Guide
Aptos is built to allow you to quickly prototype and scale secure production applications. It combines a fast, cost-efficient, and stable blockchain layer with Move’s compile-time safety that catches exploits before deployment, comprehensive tooling for rapid development, and a strong ecosystem of exchanges and bridges for seamless integration.
High Level Overview
Section titled “High Level Overview”| Feature | Ethereum | Aptos |
|---|---|---|
| Account Addresses | 160-bit | 256-bit |
| Storage Mindset | Contract-based storage | Account centric mindset for code and data |
| Caller ID | msg.sender | &signer reference |
| Smart Contracts | Solidity, EVM | Move, MoveVM |
| Benefits | Mature, wide adoption | Scalability, low latency, predictable fees |
| Transaction Fees | Variable, can be high | Lower and more predictable |
| Sponsored Transactions | Requires third-party services or EIP-7702 wallet support (2025+) | Natively supported via fee payer field. Geomi Gas Stations provides production infrastructure |
| Account Structure | Balance in a single field, uses nonce | Modules and resources, uses sequence number |
| Data Storage | Patricia Merkle Trees | Global storage with resources and modules |
| Upgradeability | Proxy patterns | Direct module upgrades |
| Safety & Security | Vulnerable to attacks like reentrancy | Mitigates common vulnerabilities |
| Dispatch Type | Dynamic dispatch | Static dispatch |
| Frontend SDK | Ethers.js library | Aptos Typescript SDK |
| NFT Standards | ERC-721, ERC-1155 | Digital Asset |
| FT Standard | ERC-20, factory pattern | See Fungible Asset, copy paste in your module: use aptos_framework::fungible_asset... |
| Example Code | ERC-20 (new contract per deploy) | Fungible Asset (single reusable module) |
Legacy Coin documentation still covers the original standard; most new deployments should prefer the Fungible Asset module referenced above.
Comparing Token Standards in Detail
Section titled “Comparing Token Standards in Detail”| Solidity | Move (Aptos) | |
|---|---|---|
| Token Structure | Each token is its own contract. | Every token is a typed FungibleAsset instantiation that reuses the same published module. |
| Token Standard | Must conform to standards like ERC-20; implementations can vary per deploy. | Uniform interface and implementation enforced by the shared module; new tokens simply register a new type rather than redeploying code. |
| Balance Storage | Balances stored in contract using a mapping structure. | Resource-Oriented Balance: balances live in an extensible object owned by the user’s account. |
| Transfer Mechanism | Tokens can be transferred without receiver’s explicit permission. | Transfers can skip receiver permission, but only when the FA explicitly enables primary-store auto creation (visible in the token’s creation code). |
Comparing EVM and Move VM in Detail
Section titled “Comparing EVM and Move VM in Detail”- EVM: Known for its flexibility and dynamic dispatch, which allows a wide range of smart contract behaviors. This flexibility, however, can lead to complexities in parallel execution and network operations.
- Move VM: Focuses on safety and efficiency with a more integrated approach between the VM and the programming language. Its data storage model allows for better parallelization, and its static dispatch method enhances security and predictability.
| EVM (Ethereum Virtual Machine) | Move VM (Move Virtual Machine) | |
|---|---|---|
| Data Storage | Data is stored in the smart contract’s storage space. | Data is stored across smart contracts, user accounts, and objects. |
| Parallelization | Parallel execution is limited due to shared storage space. | More parallel execution enabled due to flexible split storage design. |
| VM and Language Integration | Separate layers for EVM and smart contract languages (e.g., Solidity). | Seamless integration between VM layer and Move language, with native functions written in Rust executable in Move. |
| Critical Network Operations | Implementation of network operations can be complex and less direct. | Critical operations like validator set management natively implemented in Move, allowing for direct execution. |
| Function Calling | Dynamic dispatch allows for arbitrary smart contract calls. | Static dispatch aligns with a focus on security and predictable behavior. |
| Type Safety | Contract types provide a level of type safety. | Module structs and generics in Move offer robust type safety. |
| Transaction Safety | Uses nonces for transaction ordering and safety. | Uses sequence numbers for transaction ordering and safety. |
| Authenticated Storage | Yes, with smart contract storage. | Yes, leveraging Move’s resource model. |
| Object Accessibility | Objects are not globally accessible; bound to smart contract scope. | Guaranteed global accessibility of objects. |
Migration Tips for Ethereum Developers
Section titled “Migration Tips for Ethereum Developers”Ethereum stores all data in contract storage using mappings. Aptos uses an account-centric model where each account stores their own resources. Instead of a contract maintaining a mapping(address => T), each user stores their own T resource at their address.
module my_hackathon_account::prototype { use std::string; use std::signer;
// Unlike Solidity's mapping(address => string), each account stores their own resource struct MessageHolder has key, store, drop { message: string::String, }
entry fun set_message(account: &signer, message: string::String) acquires MessageHolder { let addr = signer::address_of(account); // Check if resource exists at this account (like checking mapping[addr]) if (exists<MessageHolder>(addr)) { move_from<MessageHolder>(addr); // Remove old resource }; // Store resource at the user's address (in their account, not in contract storage!) move_to(account, MessageHolder { message }); }
#[view] public fun get_message(addr: address): string::String acquires MessageHolder { assert!(exists<MessageHolder>(addr), 0); // Read message stored at the user's address borrow_global<MessageHolder>(addr).message }}By default, modules deploy to your account address. For production apps, consider deploying to Objects, which creates a unique address per deployment and enables transferable code ownership. See Using Objects for implementation details.
Learn more: global storage operators, structs and resources
On Ethereum, msg.sender is set by the EVM, but many bugs come from using tx.origin for auth or trusting user-supplied addresses. Aptos’ &signer goes further: it’s an unforgeable capability created only by the VM for actual transaction signers, so any function that requires &signer can’t be called with a spoofed identity. In dapps, wallets plus the Aptos TypeScript SDK and wallet adapter bridge this signer identity from frontend to on-chain.
module my_hackathon_account::prototype { use std::string; use std::signer;
struct MessageHolder has key, store, drop { message: string::String, }
// Only the account owner can provide their &signer (unforgeable authentication) entry fun set_message(account: &signer, message: string::String) acquires MessageHolder { // Extract address from authenticated signer (no spoofing possible!) let addr = signer::address_of(account); if (exists<MessageHolder>(addr)) { move_from<MessageHolder>(addr); }; // account is guaranteed to be authentic move_to(account, MessageHolder { message }); }
#[view] public fun get_message(addr: address): string::String acquires MessageHolder { assert!(exists<MessageHolder>(addr), 0); // Read message stored at the user's address borrow_global<MessageHolder>(addr).message }}Create and fund an account using the Aptos CLI:
aptos initMove has four abilities: copy, drop, store, and key, which control how values can be used. In this example, MessageHolder deliberately omits copy so messages stored as resources can’t be duplicated; you generally avoid copy on any type that represents on-chain state or assets. key + store allow it to live in global storage at an address, and drop lets you destroy the old resource safely when overwriting it.
module my_hackathon_account::prototype { use std::string; use std::signer;
// Resources: structs with 'key' ability that live in global storage // key = can be stored at account addresses (makes it a "resource") // store = can be stored inside other structs // drop = can be destroyed/discarded implicitly struct MessageHolder has key, store, drop { message: string::String, }
entry fun set_message(account: &signer, message: string::String) acquires MessageHolder { let addr = signer::address_of(account); // The 'drop' ability allows implicit destruction if (exists<MessageHolder>(addr)) { move_from<MessageHolder>(addr); // Old resource is destroyed (requires 'drop') }; move_to(account, MessageHolder { message }); }
#[view] public fun get_message(addr: address): string::String acquires MessageHolder { assert!(exists<MessageHolder>(addr), 0); // Read message stored at the user's address borrow_global<MessageHolder>(addr).message }}Learn more: abilities, structs and resources
Aptos packages support two upgrade policies: compatible (default; only backward-compatible changes allowed) and immutable (no upgrades allowed). By default, packages published with the Aptos CLI using aptos move publish use the compatible policy, which lets you push new versions as long as you don’t break struct layouts or public function signatures.
To prevent all future upgrades, set the immutable policy:
aptos move publish --upgrade-policy immutableOr configure in your Move.toml:
[package]name = "MyPackage"version = "1.0.0"upgrade_policy = "immutable"See Package Upgrades for the exact compatibility rules.
On Ethereum you “deploy a contract to a new address.” On Aptos you publish a package of Move modules to an account (or object) address using the Aptos CLI.
Publish your package:
aptos move publishCall an entry function after deployment:
aptos move run --function-id 'your_address::module_name::function_name'Both account-based publishing and object-based deployment (via aptos move deploy-object) respect the package’s upgrade policy. See Your First Move Module for a complete walkthrough and Objects for object-centric patterns.