App interface

The snippets below are taken from a simple lending tutorial.

When you execute the move-to-ts command from inside your Move project, it automatically generates the typescript files under project-path/build/typescript. The top-level index.ts contains an App class which gives you access to all the features of the TypeScript SDK, including:

  • fetching state from chain

  • executing move functions (using App interface)

  • executing move functions (using TypeScript methods)

Creating App object

import { App } from 'path-to-generated-ts-folder';
import { AptosClient } from 'aptos';

async function act() {
  const client = new AptosClient(...);
  const app = new App(client);
}

Loading resources from fullnodes

  // continues from above snippet

  // select module lend2 under package hippo_tutorial
  const lend2 = app.hippo_tutorial.lend2;
  const userAddr = new HexString(...);
  const user = await lend2.loadUser(userAddr);
  const protocol = await lend2.loadLendingProtocol(lend2.moduleAddress, false);
  

User and LendingProtocol are resource structs defined in the lend2 module. For all resource structs, you will be able to fetch them by using:

const resource = await app.package.module.loadStructName(ownerAddress);

The generated loadX method would actually try to load all the key-value pairs of any IterableTable contained within the resource. To disable this additional loading behavior, you can pass in another boolean variable,

// do not use loadFullState()
const resource = await app.package.module.loadStructName(ownerAddress, false);

Do note that if the resource struct contains Table members, you need to explicitly pass in a false value to ask the loader to not loadFullState. This is because there isn't an easy way to enumerate all key-value pairs of a Table. And we require you to pass in the additional false to acknowledge that the struct's state is only partially loaded.

Execute Move functions (arbitrary)

There are 2 approaches to execute arbitrary move functions from TypeScript:

  1. Synchronous execution by preloading resources

  2. Asynchronous execution

Both require you to place the #[app] attribute on the function you want to execute, for example:

#[app]
public fun global_get_user_limits(user: address): (bool, u64, u64) acquires User, LendingProtocol {
    let user = borrow_global<User>(user);
    let protocol = borrow_global<LendingProtocol>(@hippo_tutorial);
    user_get_limits(user, protocol)
}

The synchronous approach requires you to manually load the needed resources. Once the needed resources are loaded, you may repeatedly call the synchronous function and obtain the return value immediately.

// preloading User & LendingProtocol, then invoke global function
const userAddr = new HexString("0xabcd");
await app.loadUser(userAddr);
await app.loadLendingProtocol(lend2.moduleAddress, false, true);
console.log(app.app_global_get_user_limits(userAddr));

The asynchronous approach requires you to run move-to-ts with the `-a` flag, and can handle state loading for you automatically by making asynchronous calls. This returns the most up-to-date result, but every call to the async function may involve some delay due to network access.

// using async loading. Global objects are loaded from inside the async function
// only works when transpiled with the "-a" flag (asynchronous)
const userAddr = new HexString("0xabcd");
print(await app.app_global_get_user_limits(userAddr));

Execute Move functions (methods)

move-to-ts can also generate TypeScript methods for if you use the #[method] attribute

  // call user_get_limits to compute some info about user's state
  const [isUserHealthy, totalBorrow, totalDeposit] = user.user_get_limits(protocol);
  console.log(isUserHealthy, totalBorrow, totalDeposit);

We note that user_get_limits is a Move function with the following signature:

public fun user_get_limits(user: &User, protocol: &LendingProtocol): (bool, u64, u64);

To add user_get_limits as a method into the generated TypeScript User class, you need to do the following:

  • Make sure the first parameter of user_get_limits is of type &User

  • add a #[method] attribute to User's Move struct declaration, like below:

#[method(user_get_limits)]
struct User has key, store {
    ...
}

If you need to attach multiple methods, just include more function names in the method attribute, and separate the function names by comma:

#[method(check_borrow_within_limit, user_get_limits)]
struct User has key, store { ... }

Last updated