APTOS Developer’s Guide | Your first MOVE module | Typescript

This guide details how to write, compile, test, publish and interact with Move modules on the Aptos blockchain. The steps are as follows:

  1. Write, compile, and test a Move module.
  2. Publish the Move module to the Aptos blockchain
  3. Initialize and interact with Move module resources

This tutorial is based on your first transaction as a library for this example. The following tutorial contains the example code, which you can download in full below:

In this tutorial, we will focus on hello_blockchain.ts and reuse the first_transaction.ts library from the previous tutorial.

You can find the typescript project here

Step 1: Write and test the Move module

Step 1.1 Load the Aptos-core

For the simplicity of this exercise, Aptos-core has a move-examples directory which makes it easy to create and test Move modules without loading additional resources. Over time, we’ll expand this section to describe how to use Move tools for development.

In the meantime, download and prepare Aptos-core:

git clone https://github.com/aptos-labs/aptos-core.git
cd aptos-core
./scripts/dev_setup.sh
source ~/.cargo/env
git checkout origin/devnet
Enter fullscreen mode Exit fullscreen mode.

Install the Aptos command line utility. Learn more about the Aptos command line utility

cargo install --git https://github.com/aptos-labs/aptos-core.git aptos
Enter fullscreen mode Exit fullscreen mode

Step 1.2 module overview

In this terminal change the contents of the directories to aptos-move/move-examples/hello_blockchain. Save this terminal window for the rest of this tutorial — we’ll call it the «Move window» later on. In this section, we will look at the sources/hello_blockchain.move file.

This module allows users to create a String resource under their account and install it. Users can only install their own resource and cannot install others’ resources.

module HelloBlockchain::Message {
    use std::string;
    use std::error;
    use std::signer;

    struct MessageHolder has key {
        message: string::String,
    }

    public entry fun set_message(account: signer, message_bytes: vector<u8>)
    acquires MessageHolder {
        let message = string::utf8(message_bytes);
        let account_addr = signer::address_of(&account);
        if (!exists<MessageHolder>(account_addr)) {
            move_to(&account, MessageHolder {
                message,
            })
        } else {
            let old_message_holder = borrow_global_mut<MessageHolder>(account_addr);
            old_message_holder.message = message;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

The two important sections in the above code are the MessageHolder structure and the set_message function. set_message is a script function which allows it to be called directly by transactions. When it is called, the function determines whether the current account has a MessageHolder resource and creates and saves a message if it does not exist. If the resource exists, message in MessageHolder is overwritten.

Step 1.3 Testing the module

Move allows built-in tests, so we add get_message for easy retrieval message and a test function sender_can_set_message to test the end-to-end process . This can be checked by running cargo test. In the sources/hello_blockchain_test.move folder, there is another test that demonstrates another method of writing tests.

You can verify it by entering the command cargo test test test_hello_blockchain -p move-examples -- --exact in the terminal.

Note: sender_can_set_message is a script function to call script function set_message.

    const ENO_MESSAGE: u64 = 0;

    public fun get_message(addr: address): string::String acquires MessageHolder {
        assert!(exists<MessageHolder>(addr), Errors::not_published(ENO_MESSAGE));
        *&borrow_global<MessageHolder>(addr).message
    }

    #[test(account = @0x1)]
    public(script) fun sender_can_set_message(account: signer) acquires MessageHolder {
        let addr = Signer::address_of(&account);
        set_message(account,  b"Hello, Blockchain");

        assert!(
          get_message(addr) == string::utf8(b"Hello, Blockchain"),
          0
        );
    }
Enter fullscreen mode Exit fullscreen mode

Step 2: Publishing and interacting with the Move module

Now we go back to our application to deploy the module on the Aptos blockchain and interact with it. As mentioned earlier, this tutorial builds on the previous tutorial and uses generic code. As a result, this guide only discusses new features of this library, including the ability to publish, send a set_message transaction, and read MessageHolder::message. The only difference between publishing a module and sending a transaction is the type of payload. See the following:

Step 2.1 Publish Module Move

const client = new AptosClient(NODE_URL);
const faucetClient = new FaucetClient(NODE_URL, FAUCET_URL);

/** Publish a new module to the blockchain within the specified account */
export async function publishModule(accountFrom: AptosAccount, moduleHex: string): Promise<string> {
  const moduleBundlePayload = new TxnBuilderTypes.TransactionPayloadModuleBundle(
    new TxnBuilderTypes.ModuleBundle([new TxnBuilderTypes.Module(new HexString(moduleHex).toUint8Array())]),
  );

  const [{ sequence_number: sequenceNumber }, chainId] = await Promise.all([
    client.getAccount(accountFrom.address()),
    client.getChainId(),
  ]);

  const rawTxn = new TxnBuilderTypes.RawTransaction(
    TxnBuilderTypes.AccountAddress.fromHex(accountFrom.address()),
    BigInt(sequenceNumber),
    moduleBundlePayload,
    1000n,
    1n,
    BigInt(Math.floor(Date.now() / 1000) + 10),
    new TxnBuilderTypes.ChainId(chainId),
  );

  const bcsTxn = AptosClient.generateBCSTransaction(accountFrom, rawTxn);
  const transactionRes = await client.submitSignedBCSTransaction(bcsTxn);

  return transactionRes.hash;
}
Enter fullscreen mode. Exit fullscreen mode

TIP .
You can write the init_module function to initialize the module. This private function is executed automatically when the module is published. This init_module function must be private and accept only signer or a link to signer as a parameter and not return any value. Here is an example:

 fun init_module(creator: &signer) {
        move_to(
            creator,
            ModuleData { global_counter: 0 }
        );
    }

Step 2.2 Read resource.

The module is published to the address. Below is the address contract_address. This is similar to the previous example, where Coin is at 0x1. The contract_address will be the same as the account that publishes it.

/** Retrieve the resource Message::MessageHolder::message */
async function getMessage(contractAddress: HexString, accountAddress: MaybeHexString): Promise<string> {
  try {
    const resource = await client.getAccountResource(
      accountAddress,
      `${contractAddress.toString()}::message::MessageHolder`,
    );
    return (resource as any).data["message"];
  } catch (_) {
    return "";
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 2.3 Changing the resource

Move modules should reveal script functions to initialize and manage resources. Then script can be called from the transaction.

Note: Although the REST interface can display strings, due to JSON and Move restrictions it cannot determine whether an argument is a string or a string in hexadecimal. Therefore, transaction arguments always assume the latter. Consequently, in this example, the message is encoded as a hex string.

/**  Potentially initialize and set the resource Message::MessageHolder::message */
async function setMessage(contractAddress: HexString, accountFrom: AptosAccount, message: string): Promise<string> {
  const entryFunctionPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction(
    TxnBuilderTypes.EntryFunction.natural(
      `${contractAddress.toString()}::message`,
      "set_message",
      [],
      [BCS.bcsSerializeStr(message)],
    ),
  );

  const [{ sequence_number: sequenceNumber }, chainId] = await Promise.all([
    client.getAccount(accountFrom.address()),
    client.getChainId(),
  ]);

  const rawTxn = new TxnBuilderTypes.RawTransaction(
    TxnBuilderTypes.AccountAddress.fromHex(accountFrom.address()),
    BigInt(sequenceNumber),
    entryFunctionPayload,
    1000n,
    1n,
    BigInt(Math.floor(Date.now() / 1000) + 10),
    new TxnBuilderTypes.ChainId(chainId),
  );

  const bcsTxn = AptosClient.generateBCSTransaction(accountFrom, rawTxn);
  const transactionRes = await client.submitSignedBCSTransaction(bcsTxn);

  return transactionRes.hash;
}
Enter fullscreen mode Exit fullscreen mode

Step 3. Initializing and interacting with the Move unit

For Typescript:

  • Download a sample project.
  • Open your favorite terminal and go to where you downloaded the above sample project
  • Install the required libraries: yarn install
  • Execute the example: yarn hello_blockchain Message.mv
  • After a few moments, a message will appear: "Update module with Alice address, build, copy to specified path and press enter."
  • In the terminal "Move window" and for the Move file we looked at earlier:

  • Copy Alice address.

  • Compile the modules with the Alice address with the command aptos move compile --package-dir . --named-addresses hello_blockchain=0x{alice_address_here}. Here we replace the generic named address hello_blockchain='_' in hello_blockchain/move.toml with the address Alice

  • Copy build/Examples/bytecode_modules/Message.mv into the same folder as the code of this project guide.

  • Go back to another terminal window and press "enter" on the prompt to continue with the rest of the code

The output should look like this:

=== Addresses ===
Alice: 11c32982d04fbcc79b694647edff88c5b5d5b1a99c9d2854039175facbeefb40
Bob: 7ec8f962139943bc41c17a72e782b7729b1625cf65ed7812152a5677364a4f88

=== Initial Balances ===
Alice: 10000000
Bob: 10000000

Update the module with Alice's address, build, copy to the provided path, and press enter.

=== Testing Alice ===
Publishing...
Initial value: None
Setting the message to "Hello, Blockchain"
New value: Hello, Blockchain

=== Testing Bob ===
Initial value: None
Setting the message to "Hello, Blockchain"
New value: Hello, Blockchain

Enter fullscreen mode Exit fullscreen mode

The result shows that Alice and Bob have gone from no resource to a resource with message"Hello, Blockchain".

You can check the data by visiting either the REST interface or the Explorer:

  • Alice's account via the Aptos REST interface.
  • Bob's account via Aptos Explorer.

Оцените статью
devanswers.ru
Добавить комментарий