npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@thalalabs/surf

v1.3.1

Published

TypeScript Interfaces & React Hooks for interacting with Aptos Smart Contracts with type safety.

Downloads

1,277

Readme

Features

  • No code-generation: Interact with smart contracts using fully typed APIs based on static type inference. Inspired by Viem.
  • Get rid of encoding/decoding: Surf takes care of the complexities within the APIs, so you don't have to.
  • Linting and Auto-Completion: Enjoy a better development experience with type safety. No more guesswork for input and output.
  • Both TypeScript Interfaces & React Hooks: Easy to use, whether working with wallets or private keys.
  • Low runtime cost & small bundle size: minzipped size: < 2 kB.

Overview

const client = createSurfClient(new Aptos());

const result = await client.useABI(COIN_ABI).entry.transfer({
  functionArguments: ['0x1', 1],
  typeArguments: ['0x1::aptos_coin::AptosCoin'],
  account: Account.fromPrivateKey(...),
});

const [balance] = await client.useABI(COIN_ABI).view.balance({
  functionArguments: ['0x1'],
  typeArguments: ['0x1::aptos_coin::AptosCoin'],
});

When you input client.useABI(COIN_ABI).view. into your IDE, the auto-completion show as below. You could also see the input and output types for the function, all of which are statically inferred from the ABI.

demo image

Quick Start

Installation

npm i @thalalabs/surf @aptos-labs/ts-sdk

If you want to use the React Hooks, install the @aptos-labs/wallet-adapter-react@^2.0.0 additionally. Those React Hooks will be moved to a separate package in near future.

Start

Create the client:

import { createSurfClient } from '@thalalabs/surf';
import { Aptos, Network, AptosConfig } from '@aptos-labs/ts-sdk';

const client = createSurfClient(new Aptos(
  new AptosConfig({ network: Network.TESTNET })
));

Surf infers types from ABI to give you the end-to-end type-safety from your Move contract to your frontend. So firstly, you need to prepare the ABI json object of your contract in TypeScript (You can get ABI from Aptos Explorer, for example: 0x1::coin):

const abi = {…} as const;

If type inference isn't working, it's likely you forgot to add the const assertion for the object. And make sure that you set strict to true in your tsconfig.json compiler options.

Call View functions

There are two ways to call a view function with the client:

// Option 1. Use the `useABI` interface
const [balance] = await client.useABI(COIN_ABI).view.balance({
  functionArguments: ['0x1'],
  typeArguments: ['0x1::aptos_coin::AptosCoin'],
  ledgerVersion: '562606728', // ledger_version is optional
});

// Option 2. Create payload and use the `view` interface
import { createViewPayload } from '@thalalabs/surf';
const payload = createViewPayload(COIN_ABI, {
  function: 'balance',
  functionArguments: ['0x1'],
  typeArguments: ['0x1::aptos_coin::AptosCoin'],
});
const [balance] = await client.view({
  payload,
  options: { ledgerVersion: '562606728' }, // ledger_version is optional
});

Both of the interfaces can provide type safety.

Calling useABI returns an object without any ABI parsing, so that the runtime cost is low.

Submit Transaction

Similar to the view function, there are also two ways to submit transactions.

// prepare your AptosAccount
const account = /* your AptosAccount */;

// Option 1. Use the `useABI` interface
const { hash } = await client.useABI(COIN_ABI).entry.transfer({
    functionArguments: ['0x1', 1],
    typeArguments: ['0x1::aptos_coin::AptosCoin'],
    account,
});

// Option 2. Create payload and use the `submitTransaction` interface
import { createEntryPayload } from "@thalalabs/surf";
const payload = createEntryPayload(COIN_ABI, {
  function: 'transfer',
  functionArguments: ['0x1', 1],
  typeArguments: ['0x1::aptos_coin::AptosCoin'],
});
const result = await client.submitTransaction({
  payload,
  signer: account,
});

You can also simulate a transaction:

// prepare your AptosAccount
const account = /* your AptosAccount */;

// Option 1. Use the `useABI` interface
const { hash } = await client.useABI(COIN_ABI).entry.transfer({
    arguments: ['0x1', 1],
    type_arguments: ['0x1::aptos_coin::AptosCoin'],
    account,
    isSimulation: true,
});

// Option 2. Create payload and use the `simulateTransaction` interface
import { createEntryPayload } from "@thalalabs/surf";
const entryPayload = createEntryPayload(COIN_ABI, {
    function: 'transfer',
    arguments: ['0x1', 1],
    type_arguments: ['0x1::aptos_coin::AptosCoin'],
});

const { hash } = await client.simulateTransaction({
  payload,
  sender: account.accountAddress,
  publicKey: account.publicKey,
});

Get account resource

To get account resource with type safety:

const { data } = await client.useABI(COIN_ABI).resource.CoinStore({
  typeArguments: ['0x1::aptos_coin::AptosCoin'],
  account: '0x1',
});

// Get property in the struct with type safety
console.log(data.frozen);

// It also works for nested struct type.
// The `coin` property's type is 0x1::coin::Coin<T0>
console.log(data.coin.value);

Some fields of a stuct may reference external modules.To inference the type of a nested struct, it needs the ABI of the external module. Surf currently only built-in some of the ABIs from 0x1, so that it can inference types like 0x1::coin::Coin. The type of an unidentifiable field would be object. Developer can provide additional modules to Surf like this:

import { DefaultABITable } from "@thalalabs/surf";
import { createSurfClient } from '@thalalabs/surf';
import { Aptos } from '@aptos-labs/ts-sdk';

type ABITAble = DefaultABITable & {
    '0x4dcae85fc5559071906cd5c76b7420fcbb4b0a92f00ab40ffc394aadbbff5ee9::fixed_point64': typeof FIXED_POINT64_ABI,
};

const client = createSurfClient<ABITAble>(new Aptos());

With this customized ABITAble, Surf can inference the struct from 0x4dcae85fc5559071906cd5c76b7420fcbb4b0a92f00ab40ffc394aadbbff5ee9::fixed_point64.

Considering the ABITable is only been used as a type, it would be stripped out after compiling. So it won't increase the bundle size. You can put the ABITable in a separate file and import type { ABITable } from "./ABITable.ts" to ensure that.

React Hooks

Surf currently offers two React Hooks: useWalletClient and useSubmitTransaction. Both require the @aptos-labs/wallet-adapter-react. Check out the example NextJS package for more information.

Special types

Surf support some special types like 0x1::object::Object, 0x1::option::Option. Aptos has specific rule for these types. For example, Aptos accepts hex strings as input for 0x1::object::Object argument type.

Design details

Learning more backgrounds and design details from this blog post.

TODOs

Compared to Viem, Surf is still in its infancy. Any contribution is welcome and appreciated. Here are some TODOs:

  • [ ] Deploy a dedicated smart contract on the testnet for Surf to run tests that cover all data types. Currently, Surf has some tests running in CI, but they do not cover all types.
  • [ ] Support struct types for return values for view function.
  • [ ] Accept Uint8Array and string for vector<u8> input. Currently users can pass these values to createEntryPayload, and Surf will correctly encode it. But the type system will complain. So users need to use as any to pass Uint8Array or string for vector<u8>. The type system only accept number[] for vector<u8> now.
  • [ ] Add the functionality available in AptosClient to Surf, such as estimateGasPrice.

License

Released under MIT by @ThalaLabs.