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.

Step-by-Step Guide

Need the full migration tutorial instead of a quick reference table?

*   Start with the local [Ethereum to Aptos guide](/build/guides/ethereum-to-aptos)
*   Follow the [Billboard walkthrough](/build/guides/ethereum-to-aptos/billboard/demo) for a simple Solidity-to-Move port
*   Continue to the [Dutch Auction walkthrough](/build/guides/ethereum-to-aptos/dutch-auction/demo) for a richer token and object example

### High Level Overview

[](#high-level-overview)

<table><thead><tr><th>Feature</th><th>Ethereum</th><th>Aptos</th></tr></thead><tbody><tr><td><strong>Account Addresses</strong></td><td>160-bit</td><td>256-bit</td></tr><tr><td><strong>Storage Mindset</strong></td><td>Contract-based storage</td><td>Account centric mindset for code and data</td></tr><tr><td><strong>Caller ID</strong></td><td><code dir="auto">msg.sender</code></td><td><code dir="auto">&amp;signer</code> reference</td></tr><tr><td><strong>Smart Contracts</strong></td><td>Solidity, EVM</td><td>Move, MoveVM</td></tr><tr><td><strong>Benefits</strong></td><td>Mature, wide adoption</td><td>Scalability, low latency, predictable fees</td></tr><tr><td><strong>Transaction Fees</strong></td><td>Variable, can be high</td><td>Lower and more predictable</td></tr><tr><td><strong>Sponsored Transactions</strong></td><td>Requires third-party services or EIP-7702 wallet support (2025+)</td><td><a href="/build/guides/sponsored-transactions">Natively supported</a> via fee payer field. <a href="https://geomi.dev/docs/gas-stations">Geomi Gas Stations</a> provides production infrastructure</td></tr><tr><td><strong>Account Structure</strong></td><td>Balance in a single field, uses nonce</td><td>Modules and resources, uses sequence number</td></tr><tr><td><strong>Data Storage</strong></td><td>Patricia Merkle Trees</td><td>Global storage with resources and modules</td></tr><tr><td><strong>Upgradeability</strong></td><td>Proxy patterns</td><td>Direct module upgrades</td></tr><tr><td><strong>Safety &amp; Security</strong></td><td>Vulnerable to attacks like reentrancy</td><td>Mitigates common vulnerabilities</td></tr><tr><td><strong>Dispatch Type</strong></td><td>Dynamic dispatch</td><td>Static dispatch</td></tr><tr><td><strong>Frontend SDK</strong></td><td><a href="https://docs.ethers.org/v6/">Ethers.js library</a></td><td><a href="/build/sdks/ts-sdk">Aptos Typescript SDK</a></td></tr><tr><td><strong>NFT Standards</strong></td><td><a href="https://docs.openzeppelin.com/contracts/4.x/erc721">ERC-721</a>, <a href="https://docs.openzeppelin.com/contracts/4.x/erc1155">ERC-1155</a></td><td><a href="/build/smart-contracts/digital-asset">Digital Asset</a></td></tr><tr><td><strong>FT Standard</strong></td><td><a href="https://docs.openzeppelin.com/contracts/4.x/erc20">ERC-20</a>, factory pattern</td><td>See <a href="/build/smart-contracts/fungible-asset">Fungible Asset</a>, copy paste in your module: <code dir="auto">use aptos_framework::fungible_asset...</code></td></tr><tr><td><strong>Example Code</strong></td><td><a href="https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20">ERC-20</a> (new contract per deploy)</td><td><a href="https://github.com/aptos-labs/aptos-core/blob/main/aptos-move/framework/aptos-framework/sources/fungible_asset.move">Fungible Asset</a> (single reusable module)</td></tr></tbody></table>

  

Legacy [`Coin`](/build/smart-contracts/aptos-coin) documentation still covers the original standard; most new deployments should prefer the Fungible Asset module referenced above.

  

### Comparing Token Standards in Detail

[](#comparing-token-standards-in-detail)

<table><thead><tr><th></th><th>Solidity</th><th>Move (Aptos)</th></tr></thead><tbody><tr><td><strong>Token Structure</strong></td><td>Each token is its own contract.</td><td>Every token is a typed <code dir="auto">FungibleAsset</code> instantiation that reuses the same published module.</td></tr><tr><td><strong>Token Standard</strong></td><td>Must conform to standards like ERC-20; implementations can vary per deploy.</td><td>Uniform interface and implementation enforced by the shared module; new tokens simply register a new type rather than redeploying code.</td></tr><tr><td><strong>Balance Storage</strong></td><td>Balances stored in contract using a mapping structure.</td><td><strong>Resource-Oriented Balance</strong>: balances live in an extensible object owned by the user’s account.</td></tr><tr><td><strong>Transfer Mechanism</strong></td><td>Tokens can be transferred without receiver’s explicit permission.</td><td>Transfers can skip receiver permission, but only when the FA explicitly enables primary-store auto creation (visible in the token’s creation code).</td></tr></tbody></table>

  

### Comparing EVM and Move VM in Detail

[](#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.

  

<table><thead><tr><th></th><th>EVM (Ethereum Virtual Machine)</th><th>Move VM (Move Virtual Machine)</th></tr></thead><tbody><tr><td><strong>Data Storage</strong></td><td>Data is stored in the smart contract’s storage space.</td><td>Data is stored across smart contracts, user accounts, and objects.</td></tr><tr><td><strong>Parallelization</strong></td><td>Parallel execution is limited due to shared storage space.</td><td>More parallel execution enabled due to flexible split storage design.</td></tr><tr><td><strong>VM and Language Integration</strong></td><td>Separate layers for EVM and smart contract languages (e.g., Solidity).</td><td>Seamless integration between VM layer and Move language, with native functions written in Rust executable in Move.</td></tr><tr><td><strong>Critical Network Operations</strong></td><td>Implementation of network operations can be complex and less direct.</td><td>Critical operations like validator set management natively implemented in Move, allowing for direct execution.</td></tr><tr><td><strong>Function Calling</strong></td><td>Dynamic dispatch allows for arbitrary smart contract calls.</td><td>Static dispatch aligns with a focus on security and predictable behavior.</td></tr><tr><td><strong>Type Safety</strong></td><td>Contract types provide a level of type safety.</td><td>Module structs and generics in Move offer robust type safety.</td></tr><tr><td><strong>Transaction Safety</strong></td><td>Uses nonces for transaction ordering and safety.</td><td>Uses sequence numbers for transaction ordering and safety.</td></tr><tr><td><strong>Authenticated Storage</strong></td><td>Yes, with smart contract storage.</td><td>Yes, leveraging Move’s resource model.</td></tr><tr><td><strong>Object Accessibility</strong></td><td>Objects are not globally accessible; bound to smart contract scope.</td><td>Guaranteed global accessibility of objects.</td></tr></tbody></table>

## Migration Tips for Ethereum Developers

[](#migration-tips-for-ethereum-developers)

*   [Storage](#tab-panel-0-0)
*   [Authentication](#tab-panel-0-1)
*   [Resources & Abilities](#tab-panel-0-2)
*   [Upgradability](#tab-panel-0-3)
*   [Deploying Modules](#tab-panel-0-4)

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](/build/smart-contracts/objects), which creates a unique address per deployment and enables transferable code ownership. See [Using Objects](/build/smart-contracts/object/using-objects) for implementation details.

Learn more: [`global storage operators`](https://aptos-labs.github.io/move-book/global-storage.html), [`structs and resources`](https://aptos-labs.github.io/move-book/structs-and-enums.html)

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](/build/sdks/ts-sdk) and [wallet adapter](/build/sdks/wallet-adapter/wallets) 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](/build/cli):

```
aptos init
```

Learn more: [`signer`](https://aptos-labs.github.io/move-book/primitive-types.html) type, [`functions`](https://aptos-labs.github.io/move-book/functions.html)

Move 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`](https://aptos-labs.github.io/move-book/generics-and-abilities.html), [`structs and resources`](https://aptos-labs.github.io/move-book/structs-and-enums.html)

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](/build/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 immutable
```

Or configure in your `Move.toml`:

```
[package]name = "MyPackage"version = "1.0.0"upgrade_policy = "immutable"
```

See [Package Upgrades](https://aptos-labs.github.io/move-book/cli-deploy.html) 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](/build/cli).

Publish your package:

```
aptos move publish
```

Call 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](/build/guides/first-move-module) for a complete walkthrough and [Objects](/build/smart-contracts/objects) for object-centric patterns.