All pages
Powered by GitBook
1 of 16

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Install the Endless CLI

Install the Endless CLI

Choose which operating system to install the Endless CLI on:

  1. Linux (Ubuntu 22.04, Ubuntu 24.04)

  2. Windows (x86_64)

  3. Alibaba Cloud 3(x86_64)

  4. Mac OS

To zoom out and see the ways you can use the Endless CLI, go to the top-level documentation.

Install On Mac

Install the Endless CLI on Mac

  1. Go to the Endless Release.

  2. Download the appropriate zip file for your Ubuntu version.

  3. Unzip the downloaded file.

  4. Move the extracted Endless binary file, ie: endless into your preferred folder.

  5. Open a terminal and navigate to your preferred folder.

  6. Make endless an executable by running chmod +x endless.

  7. Verify that this installed version works by running endless --help.

    1. You should see instructions for how to use all CLI commands. These can be helpful in the future when you are trying to understand how to use specific commands.

  8. (Optional) It can be helpful to add the Endless CLI to a folder in your PATH, or to add it to your PATH directly. The steps to add a folder to your PATH are shell dependent.

    1. You can run echo $SHELL to print the default shell for your machine, then google specific steps to add a folder to your PATH for that shell.

Endless CLI

The Endless command line interface (CLI) is a tool to help you compile and test Move contracts. It can also help you quickly play with Endless features on-chain.

For more advanced users, the CLI can also be used to run a private Endless network (to help test code locally) and can be helpful managing a network node.

  • Install the Endless CLI

  • Setup CLI configuration

Explore a use case:

  1. Working With Move Contracts

  2. Trying Things On-Chain

  3. Running a Local Network

  4. Managing a Network Node

Cover

Windows

Cover

Linux

install-on-windows.md
install-on-linux.md

Install On Windows

Install the Endless CLI on Windows

  1. Go to the Endless Release.

  2. Download the appropriate zip file, ie: endless-cli-Windows-x86_64.zip.

  3. Unzip the downloaded file.

  4. Move the extracted Endless binary file, ie: endless.exe into your preferred folder.

  5. Right click, then copy the path to the executable.

    1. Ex. C:\Users\<username>\Downloads\endless.exe.

  6. Open PowerShell via the Start Menu.

  7. Verify the installation by running the help command.

    1. Ex. C:\Users\<username>\Downloads\endless.exe --help.

Use Endless CLI

Use the Endless CLI

The Endless command line interface (CLI) is a tool to help you compile and test Move contracts and quickly play with Endless features on-chain.

You can explore each of the use-cases here:

  1. Working with Move Contracts

  2. Trying things on-chain

  3. Running a local Endless network

  4. Managing a network node

Trying Things On-Chain

Trying Things On-Chain With Endless CLI

The CLI can be a convenient tool for quickly looking up on-chain data and sending transactions from your accounts.

The most common way to specify what accounts you want to interact with is through profiles. You can create a new profile on the cli by running the following command:

endless init --profile <your-profile-name>

If any command takes an account, you can pass in the name of a profile instead. If a command implicitly uses the default profile, it will usually have an optional parameter to use a specified profile instead which you can find by running endless <your-command> --help.

With that, the three main things you can use the CLI to do on-chain include:

  1. Looking Up On-Chain Account Info

  2. Creating test accounts and sending transactions

  3. Securely interacting on-chain via a Hardware Ledger

Create Test Accounts

Create Test Accounts and Send Transactions From Endless CLI

You can install the Endless CLI by following these steps if you have not done so already.

In general, to make a new account on-chain, you will need to generate keys and then fund the account. On test networks, you can fund a new account by asking a "faucet" account with test Endless tokens to send them to your account.

Using the CLI, you can generate and fund a test account using:

Once you have a funded account you can send coins between accounts with the transfer command like this:

You should see a result like:

This can be useful for manual testing of Move contracts or just to try seeing how the chain works in practice.

If you want more control over what your generated credentials look like, instead of endless init, you can use:

  1. endless key generate --vanity-prefix 0x<your-prefix>

  2. endless account fund-with-move --account <your-newly-generated-account-address> --private-key-file <your-newly-generated-account-private-key>

Install On Alibaba Cloud

Install the Endless CLI on Alibaba Cloud 3

  1. Go to the .

  2. Browse endless-cli section.

  • Download the endless-cli-AliCloud3-x86_64.zip file and unzip the downloaded file.

  • Move the extracted Endless binary file, ie: endless into your preferred folder.

  • Open a terminal and navigate to your preferred folder.

  • Make endless an executable by running chmod +x endless.

  • Verify that this installed version works by running endless --help.

    • You should see instructions for how to use all CLI commands. These can be helpful in the future when you are trying to understand how to use specific commands.

  • (Optional) It can be helpful to add the Endless CLI to a folder in your PATH, or to add it to your PATH directly. The steps to add a folder to your PATH are shell dependent.

    • You can run echo $SHELL to print the default shell for your machine, then google specific steps to add a folder to your PATH for that shell.

  • Endless Release

    Install On Linux

    Install the Endless CLI on Linux

    1. Go to the Endless Release.

    2. Download the appropriate zip file for your Ubuntu version.

    3. Unzip the downloaded file.

    4. Move the extracted Endless binary file, ie: endless into your preferred folder.

    5. Open a terminal and navigate to your preferred folder.

    6. Make endless an executable by running chmod +x endless.

    7. Verify that this installed version works by running endless --help.

      1. You should see instructions for how to use all CLI commands. These can be helpful in the future when you are trying to understand how to use specific commands.

    8. (Optional) It can be helpful to add the Endless CLI to a folder in your PATH, or to add it to your PATH directly. The steps to add a folder to your PATH are shell dependent.

      1. You can run echo $SHELL to print the default shell for your machine, then google specific steps to add a folder to your PATH for that shell.

    Note however that addresses are different than keys.

    endless init --profile <your-profile-name>
    endless account transfer --account superuser --amount 100
    gas_unit_price:100
    gas_used:10
    balance_changes:[]
    sender:GinLHfukKufLYJ757NfyCLpeDvjeNjLPQwc7waco3o7b
    success:true
    version:26629
    vm_status:Executed successfully
    transaction_hash:3n9pVx1mW6cPdgXVCwj7kuRPYLfR4pbjRKtUZFNK6hC5

    Managing a Network Node

    Managing a Network Node via Endless CLI

    If you are running a validator node or validator full node (VFN), you can use the CLI to interact with your node.

    Specifically, you can use the CLI to:

    1. Manage staking pools you own.

    2. Vote on proposals.

    Beyond that, you can run this help command to see more specialized commands the CLI can do relating to operating your node:

    endless node --help

    CLI Configuration

    Setup CLI Initial Configuration

    If you are using the CLI to try things out on-chain, you will need to configure the network and credentials you want the CLI to use.

    This makes using the CLI easier and more secure as you will not be forced to repeatedly copy addresses or private keys.

    :::caution If you still need to install the CLI, follow these steps. :::

    1. Run endless init and follow the instructions in the command line.

      1. To use default settings, you can provide no input and just press "Enter". For example:

    2. Later, if you want to update these settings, you can do so by running endless init again.

    3. The rest of these configuration steps are optional / quality of life. To continue to use the CLI for your specific use case, follow the usage guide here.

    (Optional) Creating Named Configurations (Profiles)

    For testing more complicated scenarios, you will often want multiple accounts on-chain. One way to do this is to create a named configuration which we call a profile.

    To create a profile, run endless init --profile <name_of_profile>. The configuration you generate will be usable when calling CLI commands as replacements for arguments.

    For example:

    (Optional) Setting Up Shell Completion

    One quality of life feature you can enable is shell auto-completions.

    1. Determine which shell you are using (you can run echo $SHELL if you are unsure).

    2. Look up where configuration files for shell completions go for that shell (it varies from shell to shell). The supported shells are [bash, zsh, fish, PowerShell, elvish].

    3. Run the following command with your specific shell and the output file for completions using your shell:

    Example command for :

    (Optional) Global Config

    By default, the CLI will look for a configuration in .endless/config.yaml in each workspace directory. If you would like to use a shared configuration for all workspaces, you can follow these steps:

    1. Create a folder in your home directory called .endless (so it has the path ~/.endless).

    2. Create a yaml file inside .endless called global_config.yaml.

    3. Run the command:

    You should see:

    Look Up On-Chain Account Info

    Look Up On-Chain Account Info Using Endless CLI

    You can install the Endless CLI by following these steps if you have not done so already.

    You can look up resources and data an account has on-chain by running the following command:

    This will show all resources that an account has. For example:

    oh my zsh

    If you’re interested in a specific type of account data, you can specify that with the --query parameter. The supported queries are:

    • balance - to see the current balance and a list of deposit and withdrawal events.

    • modules - see the Move contracts that are published on this account.

    • resources - this is what the default command does with no query specified.

    Here’s an example of what calling with the --query modules parameter looks like:

    endless account list --account <your-profile-name-or-account-address>
    [
        0x1::account::Account:
        authentication_key:[0xb9bd2cfa58ca29bce1d7add25fce5c62220604cd0236fe3f90d9de91ed9fb8cb]
        guid_creation_num:0
        num_signatures_required:1
        sequence_number:0
    ]
    endless init
    
    Configuring for profile default
    Choose network from [devnet, testnet, mainnet, local, custom | defaults to devnet]
    
    No network given, using devnet...
    Enter your private key as a hex literal (0x...) [Current: None | No input: Generate new key (or keep one if present)]
    
    No key given, generating key...
    Account 0xa2146cf381c6eb4bc82c6c4b78ee02ab63ab8f6825456e143ceb5e929a4c7c2d doesn't exist, creating it and funding it with 100000000 Veins
    Account 0xa2146cf381c6eb4bc82c6c4b78ee02ab63ab8f6825456e143ceb5e929a4c7c2d funded successfully
    
    ---
    Endless CLI is now set up for account 0xa2146cf381c6eb4bc82c6c4b78ee02ab63ab8f6825456e143ceb5e929a4c7c2d as profile default!  Run `endless --help` for more information about commands
    Success
    endless init --profile bob
    ...
    ...
    Endless CLI is now set up for account 0x51024fd5e73c45de085b4c4f1d676273cd40578505364657df1ce14efbeba138 as profile bob!
    endless config generate-shell-completions --shell <YOUR_SHELL_HERE> --output-file <OUTPUT_DESTINATION_FOR_YOUR_SHELL>
    endless config generate-shell-completions --shell zsh --output-file ~/.oh-my-zsh/completions/_endless
    endless config set-global-config --config-type global
    config_type:Global
    default_prompt_response:Prompt
    endless account list --query modules --json
    
    {
      "Result": [
        {
          "bytecode": "0xa11ceb0b050000000b01000a020a12031c2504410405452d0772da0108cc0240068c030a0a9603150cab03650d90040400000101010201030104000506000006080004070700020e0401060100080001000009020300010f0404000410060100031107000002120709010602130a030106050806080105010802020c0a02000103040508020802070801010a0201060c010800010b0301090002070b030109000900074d657373616765056572726f72056576656e74067369676e657206737472696e67124d6573736167654368616e67654576656e740d4d657373616765486f6c64657206537472696e670b6765745f6d6573736167650b7365745f6d6573736167650c66726f6d5f6d6573736167650a746f5f6d657373616765076d657373616765156d6573736167655f6368616e67655f6576656e74730b4576656e7448616e646c65096e6f745f666f756e6404757466380a616464726573735f6f66106e65775f6576656e745f68616e646c650a656d69745f6576656e74b9bd2cfa58ca29bce1d7add25fce5c62220604cd0236fe3f90d9de91ed9fb8cb0000000000000000000000000000000000000000000000000000000000000001030800000000000000000002020a08020b08020102020c08020d0b030108000001000101030b0a002901030607001102270b002b0110001402010104010105240b0111030c040e0011040c020a02290120030b05120e000b040e00380012012d0105230b022a010c050a051000140c030a050f010b030a04120038010b040b050f0015020100010100",
          "abi": {
            "address": "0xb9bd2cfa58ca29bce1d7add25fce5c62220604cd0236fe3f90d9de91ed9fb8cb",
            "name": "Message",
            "friends": [],
            "exposed_functions": [
              {
                "name": "get_message",
                "visibility": "public",
                "is_entry": false,
                "generic_type_params": [],
                "params": [
                  "address"
                ],
                "return": [
                  "0x1::string::String"
                ]
              },
              {
                "name": "set_message",
                "visibility": "public",
                "is_entry": true,
                "generic_type_params": [],
                "params": [
                  "signer",
                  "vector<u8>"
                ],
                "return": []
              }
            ],
            "structs": [
              {
                "name": "MessageChangeEvent",
                "is_native": false,
                "abilities": [
                  "drop",
                  "store"
                ],
                "generic_type_params": [],
                "fields": [
                  {
                    "name": "from_message",
                    "type": "0x1::string::String"
                  },
                  {
                    "name": "to_message",
                    "type": "0x1::string::String"
                  }
                ]
              },
              {
                "name": "MessageHolder",
                "is_native": false,
                "abilities": [
                  "key"
                ],
                "generic_type_params": [],
                "fields": [
                  {
                    "name": "message",
                    "type": "0x1::string::String"
                  },
                  {
                    "name": "message_change_events",
                    "type": "0x1::event::EventHandle<0xb9bd2cfa58ca29bce1d7add25fce5c62220604cd0236fe3f90d9de91ed9fb8cb::Message::MessageChangeEvent>"
                  }
                ]
              }
            ]
          }
        }
      ]
    }

    Running a Public Network

    Running a Public Network

    If you just want to run your own local network for testing, you can learn how to do that here.

    Genesis ceremonies

    The endless tool supports bootstrapping new blockchains through what is known as a genesis ceremony. The output of the genesis ceremony is the output of move instructions that prepares a blockchain for online operation. The input consists of:

    • A set of validators and their configuration

    • The initial set of Move modules, known as a framework

    • A unique ChainId (u8) that distinguishes this from other networks

    • For test chains, there also exists an account that manages the minting of EndlessCoin

    Generating genesis

    • The genesis organizer constructs a Layout and distributes it.

    • The genesis organizer prepares the Endless framework's bytecode and distributes it.

    • Each participant generates their ValidatorConfiguration and distributes it.

    Prepare endless-core

    The following sections rely on tools from the Endless source. See Building Endless From Source for setup.

    The layout file

    The layout file contains:

    • root_key: an Ed25519 public key for EndlessCoin management.

    • users: the set of participants

    • chain_id: the ChainId or a unique integer that distinguishes this deployment from other Endless networks

    An example:

    Building the Endless Framework

    From your Endless repository, build the framework and package it:

    The framework will be stored within the endless-framework-release directory.

    The ValidatorConfiguration file

    The ValidatorConfiguration file contains:

    • account_address: The account that manages this validator. This must be derived from the account_key provided within the ValidatorConfiguration file.

    • consensus_key: The public key for authenticating consensus messages from the validator

    • account_key

    An example:

    To generate this using the endless CLI:

    1. Generate your validator's keys:

    1. Generate your ValidatorConfiguration:

    1. The last command will produce a bob.yaml file that should be distributed to other participants for genesis.blob generation.

    Generating a genesis and waypoint

    genesis.blob and the waypoint can be generated after obtaining the layout file, each of the individual ValidatorConfiguration files, and the framework release. It is important to validate that the ValidatorConfiguration provided in the earlier stage is the same as in the distribution for generating the genesis.blob. If there is a mismatch, inform all participants.

    To generate the genesis.blob and waypoint:

    • Place the layout file in a directory, e.g., genesis.

    • Place all the ValidatorConfiguration files into the genesis directory.

    • Ensure that the ValidatorConfiguration files are listed under the set of

    Starting an endless-node

    Upon generating the genesis.blob and waypoint, place them into your validator and fullnode's configuration directory and begin your validator and fullnode.

    Running A Local Network

    Running a Local Network via Endless CLI

    Local networks can be helpful when testing your code. They are not connected to any production Endless networks like mainnet, but they are useful for three main reasons:

    1. No rate limits: You can interact with hosted services like the Node API, Indexer API, and faucet with no rate-limits to speed up testing.

    Each participant generates a genesis.blob from the resulting contributions.
  • The genesis organizer executes the genesis.blob to derive the initial waypoint and distributes it.

  • Each participant begins their endless-node. The endless-node verifies upon startup that the genesis.blob with the waypoint provided by the genesis organizer.

  • The blockchain will begin consensus after a quorum of stake is available.

  • : The public key for the account that manages this validator. This is used to derive the
    account_address
    .
  • network_key: The public key for both validator and fullnode network authentication and encryption.

  • validator_host: The network address where the validator resides. This contains a host and port field. The host should either be a DNS name or an IP address. Currently only IPv4 is supported.

  • full_node_host: An optional network address where the fullnode resides. This contains a host and port field. The host should either be a DNS name or an IP address. Currently only IPv4 is supported.

  • stake_amount: The number of coins being staked by this node. This is expected to be 1, if it is different the configuration will be considered invalid.

  • users
    within the
    layout
    file.
  • Make a framework directory within the genesiss directory and place the framework release .mv files into the framework directory.

  • Use the endless CLI to generate genesis and waypoint:

  • Reproducibility: You can set up specific on-chain scenarios and restart the network from scratch at any point to return to a clean slate.

  • High availability: The Endless devnet and testnet networks are periodically upgraded, during which time they can be unavailable. Local development networks are also always available even if you have no internet access.

  • Starting A Local Network

    1. Ensure you have the Endless CLI installed.

    2. Ensure you have Docker installed.

      1. This is exclusively needed for making a production-like environment by running the Indexer API. Many downstream tools such as the Endless SDK depend on the Indexer API.

      2. Docker recommends that you install via Docker Desktop to get automatic updates.

    3. Start Docker.

    4. Run the following command in a new terminal to start the private network:

    Note: Despite the name (local-testnet), this has nothing to do with the Endless testnet, it will run a network entirely local to your machine.

    You should expect to see an output similar to this:

    1. Wait for the final line Setup is complete, you can now use the localnet!

      :::caution If you ran into an error, jump to the Common Errors section below. :::

    As you can see from the above example output, once the local network is running, you have access to the following services:

    • Node API: This is a REST API that runs directly on the node. It enables core write functionality such as transaction submission and a limited set of read functionality, such as reading account resources or Move module information.

    • Transaction Stream Service: This is a gRPC stream of transactions used by the Indexer API. This is only relevant to you if you are developing a custom processor.

    If you do not want to run any of these sub-components of a network, there are flags to disable them.

    If you are writing a script and would like to wait for the local network to come up with all services, you can make a GET request to http://127.0.0.1:8070. At first this will return http code 503. When it returns 200 it means all the services are ready.

    For more information on different flags you can pass when starting your local network, or configuration settings such as changing which port certain services run on, run the help command:

    Common Errors On Network Startup

    If you successfully started the local network, skip to Using The Local Network.

    Address Already In Use

    This means one of the ports needed by the local network is already in use by another process.

    To fix this on Unix systems, you can:

    1. Identify the name and PID of the process by running lsof -i :8080.

    2. Run kill <pid> once you know the PID to free up that port.

    Too many open files error

    This means there were too many open files on your system. On many Unix systems you can increase the maximum number of open files by adding something like this to your .zshrc:

    Docker is not available

    To debug this, try the below fixes:

    1. Make sure you have docker installed by running docker --version.

    2. Ensure the Docker daemon is running by running docker info (if this errors saying Cannot connect to the Docker daemon Docker is NOT running)

    3. Make sure the socket for connecting to Docker is present on your machine in the default location. For example, on Unix systems /var/run/docker.sock should exist.

      1. If that file does not exist, open Docker Desktop and enable Settings -> Advanced -> Allow the default Docker socket to be used.

      2. Or, you can find where the Docker socket is by running docker context inspect | grep Host, then symlink that location to the default location by running sudo ln -s /Users/dport/.docker/run/docker.sock /var/run/docker.sock

    Using The Local Network

    Now that the network is running, you can use it like you would any other network.

    So, you can create a local profile like this:

    You can then use that profile for any commands you want to use going forward. For example, if you wanted to publish a Move module like the hello_blockchain package to your local network you could run:

    Configuring the TypeScript SDK

    If you want to use the local network with the TypeScript SDK, you can use local network URLs when initializing the client object (Endless):

    Resetting the local network

    Sometimes while developing it is helpful to reset the local network back to its initial state, for example:

    • You made backwards incompatible changes to a Move module, and you'd like to redeploy it without renaming it or using a new account.

    • You are building a custom indexer processor and would like to index using a fresh network.

    • You want to clear all on chain state, e.g. accounts, objects, etc.

    To start with a brand new local network, use the --force-restart flag:

    It will then prompt you if you really want to restart the chain, to ensure that you do not delete your work by accident.

    If you do not want to be prompted, include --assume-yes as well:

    root_key: "0xca3579457555c80fc7bb39964eb298c414fd60f81a2f8eedb0244ec07a26e575"
    users:
      - alice
      - bob
    chain_id: 8
    cargo run --package framework
    mkdir endless-framework-release
    cp endless-framework/releases/artifacts/current/build/**/bytecode_modules/* endless-framework-release
    account_address: ccd49f3ea764365ac21e99f029ca63a9b0fbfab1c8d8d5482900e4fa32c5448a
    consensus_key: "0xa05b8f41057ac72f9ca99f5e3b1b787930f03ba5e448661f2a1fac98371775ee"
    account_key: "0x3d15ab64c8b14c9aab95287fd0eb894aad0b4bd929a5581bcc8225b5688f053b"
    network_key: "0x43ce1a4ac031b98bb1ee4a5cd72a4cca0fd72933d64b22cef4f1a61895c2e544"
    validator_host:
      host: bobs_host
      port: 6180
    full_node_host:
      host: bobs_host
      port: 6182
    stake_amount: 1
    cargo run --package endless -- genesis generate-keys --output-dir bobs
    cargo run --package endless -- \\
        genesis set-validator-configuration \\
        --keys-dir bobs \\
        --username bob \\
        --validator-host bobs_host:6180 \\
        --full-node-host bobs_host:6180 \\
        --local-repository-dir .
    cargo run --package endless -- genesis generate-genesis --local-repository-dir genesis
    endless node run-local-testnet
    Identified node type (Validator) and chain ID (Some(testing)) from node config!
    
    
    Readiness endpoint: http://0.0.0.0:8070/
    
    Transaction stream is starting, please wait...
    Node API is starting, please wait...
    
    Completed generating configuration:
            Log file: "/Users/dport/.endless/testnet/validator.log"
            Test dir: "/Users/dport/.endless/testnet"
            Endless root key path: "/Users/dport/.endless/testnet/mint.key"
            Waypoint: 0:a7ee6363f24362a6ab7362558b7f0d2e63be78b19a73d3a8c1f5697f65aa1ac1
            ChainId: 223
            REST API endpoint: http://0.0.0.0:8080
            Metrics endpoint: http://0.0.0.0:9101/metrics
            Endlessnet fullnode network endpoint: /ip4/0.0.0.0/tcp/6181
            Indexer gRPC node stream endpoint: 0.0.0.0:50051
    
    Endless is running, press ctrl-c to exit
    
    Node API is ready. Endpoint: http://0.0.0.0:8080/
    Transaction stream is ready. Endpoint: http://0.0.0.0:50051/
    
    Applying post startup steps...
    
    Setup is complete, you can now use the local testnet!
    endless node run-local-testnet --help
    panicked at 'error binding to 0.0.0.0:8080: error creating server listener: Address already in use (os error 48)
    panicked at crates/endless/src/node/local_testnet/logging.rs:64:10:
    called `Result::unwrap()` on an `Err` value: Os { code: 24, kind: Uncategorized, message: \"Too many open files\" }"""
    ulimit -n 32768
    Unexpected error: Failed to apply pre-run steps for Postgres: Docker is not available, confirm it is installed and running. On Linux you may need to use sudo
    endless init --profile <your-profile-name> --network local
    endless move publish --profile <your-profile-name> --package-dir /opt/git/endless-core/endless-move/move-examples/hello_blockchain --named-addresses HelloBlockchain=local
    import { Endless, EndlessConfig, Network } from "@endless-labs/ts-sdk";
    
    const network = Network.LOCAL;
    const config = new EndlessConfig({ network });
    const client = new Endless(config);
    endless node run-local-testnet --force-restart
    Are you sure you want to delete the existing chain? [yes/no]
    > yes
    endless node run-local-testnet --force-restart --assume-yes

    Working With Move Contracts

    The Endless CLI is mostly used to compile, test, and formally verify Move contracts. If you have not installed the Endless CLI yet, you can do so by following the steps here Install the Endless CLI.

    You can jump to sections here:

    1. Compiling Move

    2. Unit Testing Move Contracts

    3. Generating Test Coverage Reports

    4. Publishing Move Contracts

    5. Running Published Contracts

    6. (Optional) Formally Verifying Move Scripts

    To see how to chain together Move contracts on-chain using the CLI, you can follow this "CLI Arguments" tutorial.

    Throughout this document there are parts of commands you will have to modify to fit your situation. Those variables will be wrapped in triangle brackets <like this>.

    1. Compiling Move

    You can compile a Move package by running:

    The package directory is the folder which contains the Move.toml file.

    Based on the settings in your Move.toml file, you may need to pass in additional information to that compile command.

    For example, if you look at the , in the Move.toml file it specifies a variable named address called hello_blockchain.

    So, to compile this, you will need to pass in the value for hello_blockchain with the --named-addresses parameter.

    You can learn more about optional parameters when compiling Move contracts by running endless move compile --help.

    2. Unit Testing Move Contracts

    The Endless CLI can also be used to compile and run unit tests locally by running:

    This command both compiles and runs tests, so it needs all the same optional parameters you use when compiling.

    You can learn more about the optional parameters for testing move contracts by running endless move test --help.

    Printing Debugging Information

    When writing tests, it can be helpful to print out debug information or stack traces. You can do that by using debug::print and debug::print_stack_trace to print information when you use endless move test. See an example of how they are used in .

    To see the output of testing ’s package:

    1. Clone .

    2. Navigate to the by running cd crates/endless/debug-move-example.

    3. Run endless move test.

    You should see:

    For more on how to write unit tests with Move, follow this .

    3. Generating Test Coverage Reports

    The Endless CLI can be used to analyze and improve the testing of your Move modules. To use this feature:

    To see the code coverage of your tests run the following command from your Move package’s directory:

    If you would like to focus your coverage down to specific packages, you can do so with the --filter option. To narrow even further to specific Move modules, use the --module parameter.

    For more detailed / advanced coverage information (such as your test coverage in the compiled bytecode) you can run endless move coverage . With that command, the CLI will prompt you for more details on what specifically you would like more coverage information about.

    You can learn more about optional parameters for test coverage by running endless move test --help and endless move coverage --help.

    4. Publishing Move Contracts

    To publish a Move contract, you will need to run:

    Note that when you are publishing on the main network, the credentials you pass into optional parameters like --named-addresses will need to reflect accounts on that network instead of test credentials.

    The package will be published to your default profile in the CLI. You can override that to specify which account to publish to using --profile in the command. To generate a new profile for a specific account, use endless init --profile <name_of_profile> and follow the prompts.

    Please also note that when publishing Move modules, if multiple modules are in one package, then all modules in that package must use the same account. If they use different accounts, then the publishing will fail at the transaction level.

    You can estimate the gas fees associated with publishing your Move contract by using the Gas Profiler.

    :::caution By default Move contracts publish their source code. To avoid publishing with source code, publish with the --included-artifacts none argument.

    Since the Endless blockchain is inherently open by design, note that even without source access it is possible to regenerate Move source from published Move bytecode. :::

    5. Running Published Contracts

    Now that you have published your Move package, you can run it directly from the CLI.

    You will first need to construct your function-id by combining:

    You can then pass in args by using the --args parameter.

    As an example, if you were to have published the to an account with an address b9bd2cfa58ca29bce1d7add25fce5c62220604cd0236fe3f90d9de91ed9fb8cb you could run its set_message function via the following command:

    Which should result in:

    6. (Optional) Formally Verifying Move Scripts

    For cases where you want to guarantee that your code works as expected beyond unit testing, you can use the Move Prover to formally verify your Move contract code.

    Once you have installed the Move Prover, you can use it from the Endless CLI by running:

    To learn how to formally verify your code, please follow the in-depth Move tutorial (step 7 and 8 cover how to use the Move Prover and write formal specifications in the example code).

    hello_blockchain example Move contract
    DebugDemo.move
    DebugDemo.move
    endless-core
    debug-move-example
    Move tutorial
    hello_blockchain example package
    here
    endless move compile --package-dir <your-package-directory>
    [addresses]
    hello_blockchain = "_"
    endless move compile \
      --package-dir endless-move/move-examples/hello_blockchain/ \
      --named-addresses hello_blockchain=superuser
    endless move test --package-dir <your-package-directory>
    Running Move unit tests
    [debug] 0000000000000000000000000000000000000000000000000000000000000001
    Call Stack:
        [0] 0000000000000000000000000000000000000000000000000000000000000001::Message::sender_can_set_message
    
            Code:
                [4] CallGeneric(0)
                [5] MoveLoc(0)
                [6] LdConst(0)
              > [7] Call(1)
                [8] Ret
    
            Locals:
                [0] -
                [1] 0000000000000000000000000000000000000000000000000000000000000001
    
    Operand Stack:
    endless move test --coverage
    endless move publish --package-dir <your-package-directory>
    <the-address-you-published-to>::<module_name>::<function_name>
    endless move run --function-id 0xb9bd2cfa58ca29bce1d7add25fce5c62220604cd0236fe3f90d9de91ed9fb8cb::message::set_message --args string:hello!
    {
      "Result": {
        "changes": [
          {
            "address": "b9bd2cfa58ca29bce1d7add25fce5c62220604cd0236fe3f90d9de91ed9fb8cb",
            "data": {
              "authentication_key": "0xb9bd2cfa58ca29bce1d7add25fce5c62220604cd0236fe3f90d9de91ed9fb8cb",
              "self_address": "0xb9bd2cfa58ca29bce1d7add25fce5c62220604cd0236fe3f90d9de91ed9fb8cb",
              "sequence_number": "3"
            },
            "event": "write_resource",
            "resource": "0x1::account::Account"
          },
          {
            "address": "b9bd2cfa58ca29bce1d7add25fce5c62220604cd0236fe3f90d9de91ed9fb8cb",
            "data": {
              "coin": {
                "value": "9777"
              },
              "deposit_events": {
                "counter": "1",
                "guid": {
                  "id": {
                    "addr": "0xb9bd2cfa58ca29bce1d7add25fce5c62220604cd0236fe3f90d9de91ed9fb8cb",
                    "creation_num": "1"
                  }
                }
              },
              "withdraw_events": {
                "counter": "1",
                "guid": {
                  "id": {
                    "addr": "0xb9bd2cfa58ca29bce1d7add25fce5c62220604cd0236fe3f90d9de91ed9fb8cb",
                    "creation_num": "2"
                  }
                }
              }
            },
            "event": "write_resource",
            "resource": "0x1::coin::CoinStore<0x1::endless_coin::EndlessCoin>"
          },
          {
            "address": "b9bd2cfa58ca29bce1d7add25fce5c62220604cd0236fe3f90d9de91ed9fb8cb",
            "data": {
              "counter": "4"
            },
            "event": "write_resource",
            "resource": "0x1::guid::Generator"
          },
          {
            "address": "b9bd2cfa58ca29bce1d7add25fce5c62220604cd0236fe3f90d9de91ed9fb8cb",
            "data": {
              "message": "hello!",
              "message_change_events": {
                "counter": "0",
                "guid": {
                  "id": {
                    "addr": "0xb9bd2cfa58ca29bce1d7add25fce5c62220604cd0236fe3f90d9de91ed9fb8cb",
                    "creation_num": "3"
                  }
                }
              }
            },
            "event": "write_resource",
            "resource": "0xb9bd2cfa58ca29bce1d7add25fce5c62220604cd0236fe3f90d9de91ed9fb8cb::Message::MessageHolder"
          }
        ],
        "gas_used": 41,
        "success": true,
        "version": 3488,
        "vm_status": "Executed successfully"
      }
    }
    endless move prove --package-dir <your-package-directory>

    Arguments in JSON Tutorial

    Package info

    This section references the CliArgs example package, which contains the following manifest:

    Here, the package is deployed under the named address test_account.

    Set your working directory to to follow along:

    Deploying the package

    Start by mining a vanity address for Ace, who will deploy the package:

    The exact account address should vary for each run, though the vanity prefix should not.

    Store Ace's address in a shell variable, so you can call it inline later on:

    Fund Ace's account with the faucet (either devnet or testnet):

    Now publish the package under Ace's account:

    Entry functions

    The only module in the package, cli_args.move, defines a simple Holder resource with fields of various data types:

    A public entry function with multi-nested vectors can be used to set the fields:

    After the package has been published, endless move run can be used to call set_vals():

    To pass vectors (including nested vectors) as arguments from the command line, use JSON syntax escaped with quotes!

    The function ID, type arguments, and arguments can alternatively be specified in a JSON file: move-examples/cli_args/entry_function_arguments.json

    Here, the call to endless move run looks like:

    If you are trying to run the example yourself don't forget to substitute Ace's actual address for <test_account> in entry_function_arguments.json!

    View functions

    Once the values in a Holder have been set, the reveal() view function can be used to check the first three fields, and to compare type arguments against the last two fields:

    This view function can be called with arguments specified either from the CLI or from a JSON file:

    If you are trying to run the example yourself don't forget to substitute Ace's actual address for <test_account> in view_function_arguments.json (twice)!

    Output:

    Script functions

    The package also contains a script, set_vals.move, which is a wrapper for the setter function:

    First compile the package (this will compile the script):

    Next, run endless move run-script Arguments via CLI

    Arguments via JSON file

    script_function_arguments.json

    Both such script function invocations result in the following reveal() view function output:

    Output:

    As of the time of this writing, the endless CLI only supports script function arguments for vectors of type u8, and only up to a vector depth of 1. Hence vector<address> and vector<vector<u8>> are invalid script function argument types.

    [package]
    name = "CliArgs"
    version = "0.1.0"
    upgrade_policy = "compatible"
    
    [addresses]
    test_account = "_"
    
    [dependencies.EndlessFramework]
    local = "../../framework/endless-framework"
    endless-move/move-examples/cli_args
    cd <endless-core-parent-directory>/endless-core/endless-move/move-examples/cli_args
    endless key generate \
        --vanity-prefix 0xace \
        --output-file ace.key
    # Your exact address should vary
    ace_addr=0xacef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46
    endless account fund-with-move --account $ace_addr --private-key-file ace.key
    endless move publish \
        --named-addresses test_account=$ace_addr \
        --private-key-file ace.key \
        --assume-yes
    struct Holder has key, drop {
        u8_solo: u8,
        bytes: vector<u8>,
        utf8_string: String,
        bool_vec: vector<bool>,
        address_vec_vec: vector<vector<address>>,
        type_info_1: TypeInfo,
        type_info_2: TypeInfo,
    }
    public entry fun set_vals<T1, T2>(
        account: signer,
        u8_solo: u8,
        bytes: vector<u8>,
        utf8_string: String,
        bool_vec: vector<bool>,
        address_vec_vec: vector<vector<address>>,
    ) acquires Holder {
        let account_addr = signer::address_of(&account);
        if (exists<Holder>(account_addr)) {
            move_from<Holder>(account_addr);
        };
        move_to(&account, Holder {
            u8_solo,
            bytes,
            utf8_string,
            bool_vec,
            address_vec_vec,
            type_info_1: type_info::type_of<T1>(),
            type_info_2: type_info::type_of<T2>(),
        });
    }
    endless move run \
        --function-id $ace_addr::cli_args::set_vals \
        --type-args \
            0x1::account::Account \
            0x1::chain_id::ChainId \
        --args \
            u8:123 \
            "hex:0x1234" \
            "string:hello, world\! ♥" \
            "bool:[false, true, false, false]" \
            'address:[["0xace", "0xbee"], ["0xcad"], []]' \
        --private-key-file ace.key \
        --assume-yes
    {
        "function_id": "<test_account>::cli_args::set_vals",
        "type_args": [
            "0x1::account::Account",
            "0x1::chain_id::ChainId"
        ],
        "args": [
            {
                "type": "u8",
                "value": 123
            },
            {
                "type": "hex",
                "value": "0x1234"
            },
            {
                "type": "string",
                "value": "hello, world! ♥"
            },
            {
                "type": "bool",
                "value": [
                    false,
                    true,
                    false,
                    false
                ]
            },
            {
                "type": "address",
                "value": [
                    [
                        "0xace",
                        "0xbee"
                    ],
                    [
                        "0xcad"
                    ],
                    []
                ]
            }
        ]
    }
    endless move run \
        --json-file entry_function_arguments.json \
        --private-key-file ace.key \
        --assume-yes
    struct RevealResult has drop {
            u8_solo: u8,
            bytes: vector<u8>,
            utf8_string: String,
            bool_vec: vector<bool>,
            address_vec_vec: vector<vector<address>>,
            type_info_1_match: bool,
            type_info_2_match: bool
        }
    
    #[view]
    /// Pack into a `RevealResult` the first three fields in host's
    /// `Holder`, as well as two `bool` flags denoting if `T1` & `T2`
    /// respectively match `Holder.type_info_1` & `Holder.type_info_2`,
    /// then return the `RevealResult`.
    public fun reveal<T1, T2>(host: address): RevealResult acquires Holder {
        let holder_ref = borrow_global<Holder>(host);
        RevealResult {
            u8_solo: holder_ref.u8_solo,
            bytes: holder_ref.bytes,
            utf8_string: holder_ref.utf8_string,
            bool_vec: holder_ref.bool_vec,
            address_vec_vec: holder_ref.address_vec_vec,
            type_info_1_match:
                type_info::type_of<T1>() == holder_ref.type_info_1,
            type_info_2_match:
                type_info::type_of<T2>() == holder_ref.type_info_2
        }
    }
    endless move view \
        --function-id $ace_addr::cli_args::reveal \
        --type-args \
            0x1::account::Account \
            0x1::account::Account \
        --args address:$ace_addr
    endless move view --json-file view_function_arguments.json
    {
        "function_id": "<test_account>::cli_args::reveal",
        "type_args": [
            "0x1::account::Account",
            "0x1::account::Account"
        ],
        "args": [
            {
                "type": "address",
                "value": "<test_account>"
            }
        ]
    }
    {
      "Result": [
        {
          "address_vec_vec": [
            [
              "0xace",
              "0xbee"
            ],
            [
              "0xcad"
            ],
            []
          ],
          "bool_vec": [
            false,
            true,
            false,
            false
          ],
          "bytes": "0x1234",
          "type_info_1_match": true,
          "type_info_2_match": false,
          "u8_solo": 123,
          "utf8_string": "hello, world! ♥"
        }
      ]
    }
    script {
        use test_account::cli_args;
        use std::vector;
        use std::string::String;
    
        /// Get a `bool` vector where each element indicates `true` if the
        /// corresponding element in `u8_vec` is greater than `u8_solo`.
        /// Then pack `address_solo` in a `vector<vector<<address>>` and
        /// pass resulting argument set to public entry function.
        fun set_vals<T1, T2>(
            account: signer,
            u8_solo: u8,
            bytes: vector<u8>,
            utf8_string: String,
            u8_vec: vector<u8>,
            address_solo: address,
        ) {
            let bool_vec = vector::map_ref(&u8_vec, |e_ref| *e_ref > u8_solo);
            let addr_vec_vec = vector[vector[address_solo]];
            cli_args::set_vals<T1, T2>(account, u8_solo, bytes, utf8_string, bool_vec, addr_vec_vec);
        }
    } 
    endless move compile --named-addresses test_account=$ace_addr
    endless move run-script \
        --compiled-script-path build/CliArgs/bytecode_scripts/set_vals.mv \
        --type-args \
            0x1::account::Account \
            0x1::chain_id::ChainId \
        --args \
            u8:123 \
            "hex:0x1234" \
            "string:hello, world\! ♥" \
            "u8:[122, 123, 124, 125]" \
            address:"0xace" \
        --private-key-file ace.key \
        --assume-yes
    endless move run-script \
        --compiled-script-path build/CliArgs/bytecode_scripts/set_vals.mv \
        --json-file script_function_arguments.json \
        --private-key-file ace.key \
        --assume-yes
    {
        "type_args": [
            "0x1::account::Account",
            "0x1::chain_id::ChainId"
        ],
        "args": [
            {
                "type": "u8",
                "value": 123
            },
            {
                "type": "hex",
                "value": "0x1234"
            },
            {
                "type": "string",
                "value": "hello, world! ♥"
            },
            {
                "type": "u8",
                "value": [
                    122,
                    123,
                    124,
                    125
                ]
            },
            {
                "type": "address",
                "value": "0xace"
            }
        ]
    }
    endless move view \
        --function-id $ace_addr::cli_args::reveal \
        --type-args \
            0x1::account::Account \
            0x1::chain_id::ChainId \
        --args address:$ace_addr
    {
      "Result": [
        {
          "address_vec_vec": [["0xace"]],
          "bool_vec": [false, false, true, true],
          "bytes": "0x1234",
          "type_info_1_match": true,
          "type_info_2_match": true,
          "u8_solo": 123,
          "utf8_string": "hello, world! ♥"
        }
      ]
    }