move-langaptossui

How do I call a function in a different Move module / smart contract?


I know there is a Move module (smart contract) on chain with a function that looks like this:

public entry fun do_nothing() {}

I know it is deployed at 6286dfd5e2778ec069d5906cd774efdba93ab2bec71550fa69363482fbd814e7::other::do_nothing. You can see the module in the explorer here.

I have a Move module of my own that looks like this.

Move.toml:

[package]
name = 'mine'
version = '1.0.0'

[dependencies.AptosFramework]
git = 'https://github.com/aptos-labs/aptos-core.git'
rev = 'main'
subdir = 'aptos-move/framework/aptos-framework'

[addresses]
my_addr = "81e2e2499407693c81fe65c86405ca70df529438339d9da7a6fc2520142b591e"
other_addr = "6286dfd5e2778ec069d5906cd774efdba93ab2bec71550fa69363482fbd814e7"

sources/mine.move:

module my_addr::mine {
    use other_addr::other::do_nothing;

    public entry fun do_stuff() {
        do_nothing();
    }
}

As you can see, I'm telling the compiler where the other module is by setting other_addr = "6286dfd5e2778ec069d5906cd774efdba93ab2bec71550fa69363482fbd814e7". However, when I try to compile my Move module, it fails, saying "unbound module", meaning it doesn't know what the "other" module is.

$ aptos move compile --named-addresses my_addr="`yq .profiles.default.account < .aptos/config.yaml`"
Compiling, may take a little while to download git dependencies...
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING mine
error[E03002]: unbound module
  ┌─ /Users/dport/github/move-examples/call_other_module/mine/sources/mine.move:2:9
  │
2 │     use other_addr::other::do_nothing;
  │         ^^^^^^^^^^^^^^^^^ Invalid 'use'. Unbound module: '(other_addr=0x6286DFD5E2778EC069D5906CD774EFDBA93AB2BEC71550FA69363482FBD814E7)::other'

error[E03005]: unbound unscoped name
  ┌─ /Users/dport/github/move-examples/call_other_module/mine/sources/mine.move:5:9
  │
5 │         do_nothing();
  │         ^^^^^^^^^^ Unbound function 'do_nothing' in current scope

{
  "Error": "Move compilation failed: Compilation error"
}

Why is compilation failing? Why can't the compiler figure it out for me based on the ABIs of the Move modules it finds at other_addr on chain?


Solution

  • The problem

    In order to publish a Move module that calls a function in another Move module, you need its source code. This is true of all Move modules, not just your own. You'll notice in Move.toml there is already a dependency on AptosFramework. This is what allows you to call all the framework functions, e.g. those related to coins, tokens, signer, timestamps, etc.

    So to make this work, you need to have access to the source.

    Source: Git Dependency

    If you have access to the source in another git repository, you can tell the compiler where to find the other module by adding this to your Move.toml:

    [dependencies.other]
    git = 'https://github.com/banool/move-examples.git'
    rev = 'main'
    subdir = 'call_other_module/other'
    

    This is telling the compiler, "the source code for other can be found in the call_other_module/other/ directory at that git repo".

    Source: Local

    If you have the source code locally, you can do this instead:

    [dependencies.other]
    local = "../other"
    

    Where the argument for local is the path to the source code.

    Source: I don't have it?

    If you don't have the source, you can try to download it. By default, when someone publishes a Move module, they include the source code alongside it.

    First try to download the code:

    cd /tmp
    aptos move download --account 6286dfd5e2778ec069d5906cd774efdba93ab2bec71550fa69363482fbd814e7 --package other
    

    If the source code was indeed deployed on chain, you should see this:

    Saved package with 1 module(s) to `/tmp/other`
    {
      "Result": "Download succeeded"
    }
    

    Inside /tmp/other you'll find the full source, including Move.toml and sources/.

    From here, you can just follow the steps for Source: Local above.

    Note: The value for --package should match the name field in Move.toml of the deployed code. More to come on how to determine this based on on-chain data.

    Source: The download failed?

    If you ran aptos move download and saw this:

    module without code: other
    Saved package with 1 module(s) to `/private/tmp/other_code/other`
    {
      "Result": "Download succeeded"
    }
    

    You'll find that sources/other.move is empty.

    This means the author published the code with this CLI argument set:

    --included-artifacts none
    

    Meaning they purposely chose not to include the source on chain.

    Unfortunately at this point you're out of luck right now. It is a hard requirement of compilation that if you want to call a function in another Move module, you must have the source for that module. There is work in the pipeline that should enable decompilation of Move bytecode, but that's not ready yet.


    I hope this helps, happy coding!!

    The code used in this answer can be found here: https://github.com/banool/move-examples/tree/main/call_other_module.