Solidity to Move

msg.sender and signer

  • Solidity: uses msg.sender to identify the caller of a function.

    function transfer(address recipient, uint256 amount) public {
    require(msg.sender != address(0), "Invalid sender");
    // Implementation
    }

  • Move: introduces a &signer reference, which provides a more secure way to represent the transaction authorizer.

    public entry fun transfer(account: &signer, recipient: address, amount: u64) {
    // Implementation
    }


msg.value Equivalent in Move

  • Solidity: msg.value represents the amount of native tokens sent within a function call.
  • Move: No direct equivalent. Transactions involving tokens in Move require explicit handling through module functions. The example below uses the legacy Coin module.

    public entry fun transfer(sender: &signer, recipient: address, amount: u64) {
    let sender_coins = coin::withdraw<Coin<MyTokenType>>(sender, amount);
    coin::deposit(recipient, sender_coins);
    }


Transferring Fungible Assets - Solidity: Ether and tokens are directly sent through address

operations and ERC-20 transfer calls. - Move: Used the coin module for native tokens and assets are handled through resources which are more tightly controlled. However, Aptos Move is now migrating to the Fungible Asset standard.


Vectors and Other Data Structures

  • Solidity: Offers arrays and mappings as primary data structures.
  • Move: Supports vector<T> for ordered collections and other modules for maps and sets.

State Variables, Resources and Struct Abilities

State Variables in Solidity are used to store data that is part of the contract’s state on the blockchain. They are declared at contract level, outside of functions, and can represent any data type. However, Move doesn’t use state variables, and instead has a unique characteristic in which developers can give their structs abilities. Structs with the key ability are known as “Resources”. These are unique, first-class citizens used to manage assets and other critical data safely.

1. copy

Allows the struct to be copied. If omitted, the struct cannot be duplicated, and attempts to copy will result in a compile time error.


struct NoCopyStruct has drop {
data: u64,
}
public fun try_to_copy() {
let instance = NoCopyStruct { data: 42 };
let _copy_instance = instance; // This line will cause a compile-time error
// Error: 'instance' cannot be copied because its type 'NoCopyExample::NoCopyStruct'
// does not have the 'copy' ability
}

2. drop

Allows the struct to be explicitly destroyed or go out of scope without being stored. If omitted, you must ensure that the struct is stored somewhere or passed on to avoid compile time error.


struct NoDropStruct has key, store { // Hypothetically, if 'drop' were not implicit
data: u64,
}
public entry fun init(account: &signer) {
let unused_instance = NoDropStruct { data: 42 };
// Error: 'NoDropStruct' without 'drop' ability must not be discarded.
}

3. key

Indicates the struct can serve as a key for global storage. Structs with this ability can be uniquely identified in global storage, making them suitable for resources or data intended to be accessed across transactions.

4. store

Permits the struct to be stored in global storage. This is crucial for persistent data that needs to live beyond the execution of a single transaction. A struct with the store ability can be a part of other data structures or resources that are identified by a key in global storage. It’s suitable for components of larger entities, where direct access to those components isn’t required independently, but they still need to persist beyond a single transaction.

Here’s a code snippet that demonstrates all of the struct abilities.


module 0x1::my_module {
use std::signer;
// A simple struct with `copy` and `drop` abilities, but it cannot be stored in global storage
struct CopyableDropable has copy, drop {
data: u64,
}
// A resource struct that can be stored and uniquely identified in global storage
struct MyResource has key, store {
value: u64,
}
// Initialize and store a `MyResource` in the signer's account
public entry fun init_resource(account: &signer, val: u64) {
let resource = MyResource { value: val };
move_to(account, resource); // Requires `store` and `key` abilities to work
}
// Function demonstrating `copy` and `drop`
public fun demonstrate_copy_drop(val: u64): u64 {
let data = CopyableDropable { data: val };
let data_copy = data; // Allowed because of `copy`
let sum = data.data + data_copy.data; // Use both structs
// Both `data` and `data_copy` will be dropped here, allowed by `drop` sum
}
}