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:
Synchronous execution by preloading resources
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 toUser
'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