Skip to content

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.

| 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.


| | 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). |


  • 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. |

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