Skip to main content

Calling a smart contract

Functions that are part of a smart contract's public interface—meaning they are exported from the contract entry point file—can be invoked by end users or other smart contracts. These functions can accept parameters and return data.

Currently, function arguments and return values only support raw bytes, represented as StaticArray<u8>. This approach allows for flexible data handling, as any data type can be serialized to raw bytes for transmission and processing.

Example of a public callable function

Here is an example of a public callable function named hello, which takes and returns a StaticArray<u8> :

// main.ts 
export function hello(argsData: StaticArray<u8>): StaticArray<u8> {
// Function logic here
}

Since raw bytes are used as both input and output, you can work with various data types by serializing them to StaticArray<u8> for input and deserializing them back into the appropriate type within the function. Similarly, any return data should be serialized to StaticArray<u8> before being sent back.

Serialization and Deserialization

To use complex data types in your public callable functions, you must first serialize them into raw bytes before passing them as arguments. On the receiving end, the function will need to deserialize the bytes back into the original data type. This allows for versatile function inputs and outputs, even though the system natively supports only raw byte arrays.

Using Args for Serialization and Deserialization

Massa provides specialized tools for handling the serialization and deserialization of data in both AssemblyScript and TypeScript, making it easier to manage data exchanges between smart contracts and other parts of the Massa ecosystem. These tools simplify the process of converting data into raw bytes for smart contract calls and results, and they ensure consistency across different parts of your project.

Available Args Implementations

  1. AssemblyScript:

    • The Args implementation for AssemblyScript is available in the @massalabs/as-types package.
    • This package provides functions for encoding and decoding various data types (like integers, strings, and arrays) to and from raw bytes within smart contracts.
  2. TypeScript:

    • For TypeScript, the Args implementation can be found in the @massalabs/massa-web3 package.
    • This library is particularly useful for interfacing with Massa smart contracts from TypeScript-based applications, enabling seamless serialization and deserialization when calling smart contracts or handling call results.
  3. Go:

    • Certain aspects of serialization logic are also available in the Go programming language within the MassaStation codebase. This can be beneficial for developers looking to integrate with Massa from Go-based applications or understand how the serialization is implemented at a lower level.

Benefits of using Args

  • Consistent Serialization: By using Args, developers ensure that data serialization is consistent between the client and smart contract. This reduces the risk of data mismatches or errors during contract calls.
  • Simplified Data Handling: Args provides easy-to-use methods for encoding and decoding data, making it straightforward to work with complex data types like nested arrays or objects.
  • Cross-Language Compatibility: Since Args is implemented in multiple languages, it facilitates interoperability between smart contracts written in AssemblyScript and client applications in TypeScript or Go, broadening the range of possible integrations.

Native serializable types

Massa provides built-in support for serializing and deserializing AssemblyScript native types, big integers, and arrays of these types. The Args utility serves as a versatile tool for handling function parameters that involve multiple arguments, as well as for managing complex data structures in function returns.

Supported Serializable Types

The following types can be directly serialized and deserialized in Massa smart contracts:

  • Native AssemblyScript Types: These includes basic types like integers (i32, u64, etc.), bool, and string.
  • Big Integers: Using the as-bignum library, Massa smart contracts can handle large integers beyond standard 64-bit limits such u256.
  • Arrays: Arrays of native types, such as Array<i32> or Array<string>, can also be serialized, allowing for the handling of collections of data in contract calls.
  • Generic serializable and array of serializable object: Serializable is a generic class that wrap the serialization logic and allow to define a custom serialization/deserialization of complex data.

Examples of a string serialization / deserialization with args

In this example, we use the hello function to demonstrate how to handle string arguments with Args. The hello function takes a user's name as a serialized string, deserializes it, and returns a personalized greeting message. This example assumes that the smart contract has already been deployed on the Massa blockchain.

Contract code (assemblyscript)

// main.ts (assemblyscript)
import { Args, stringToBytes } from '@massalabs/as-types';

export function hello(argsData: StaticArray<u8>): StaticArray<u8> {
const name = new Args(argsData).nextString().expect('Name argument is missing or invalid');
return strToBytes(`Hello ${name}!`)
}

Explanation of the code

  1. Import Statements:

    • We import Args from @massalabs/as-types to handle the deserialization of function arguments.
    • stringToBytes is also imported, which converts the resulting greeting string into a byte array, as required for the function's return type.
  2. Function Argument Deserialization:

    • ArgsData is the raw byte input representing the serialized function arguments.
    • Args(ArgsData) initializes the Args object with the serialized data.
    • nextString() is called on the Args object to extract the next serialized string from the arguments. It's paired with expect(), which throws an error if the argument is missing or invalid.
  3. Return Value Serialization:

    • The greeting message is created by interpolating the user-provided name into the string.
    • stringToBytes() converts the string message into a StaticArray<u8> so that it can be returned in the expected format.

Calling the contract from TypeScript

To call this hello function from a TypeScript application, you can use the @massalabs/massa-web3 library to serialize the argument and handle the response. Here's a quick example:

// hello.ts (typescript)
import {
Account,
Args,
bytesToStr,
SmartContract,
Web3Provider
} from '@massalabs/massa-web3';
const account = await Account.fromEnv();
const provider = Web3Provider.newPublicBuildnetProvider(account);

const helloContract = new SmartContract(provider, "<deployed_contract_address>");

const name = "Satoshi"
const args = new Args().addString(name)
const result = await helloContract.call('hello',args);

// deserialize message
const message = bytesToStr(result.value);
console.log(message)
// should log "Hello Satoshi!"

In this example, we:

  1. Serialize the Argument:

    • We create a new Args object and add a string argument ("Satoshi") to it, then serialize it for use in the contract call.
  2. Deserialize the Response:

    • After receiving the response, we use Args again to deserialize the returned bytes into a string, which gives us the greeting message.

By following this approach, you can easily handle string inputs and outputs in Massa smart contracts using the Args utility for serialization and deserialization.

Example of multi argument deserialization with args

// main.ts (assemblyscript)
import { Args, stringToBytes } from '@massalabs/as-types';

export function hello(argsData: StaticArray<u8>): StaticArray<u8> {
const args = new Args(argsData);
const name = args.nextString().expect('Name argument is missing or invalid');
const age = args.nextU8().expect('Age argument is missing or invalid');
return strToBytes(`Hello ${name}! ich bin ${age.toString()} jahre`)
}

Calling code (typescript)

// hello.ts (typescript)
import {
Args,
bytesToStr,
SmartContract,
} from '@massalabs/massa-web3';
{...}
const helloContract = new SmartContract(provider, "<deployed_contract_address>");
const name = "Satoshi";
const age = 8n;
const args = new Args().addString(name).addU8(age)
const result = await helloContract.call('hello',args);

// deserialize message
const message = bytesToStr(result.value);
console.log(message)
// should log "Hello Satoshi! ich bin 8 jahre"

In the example below, args is used to serialize 2 arguments, a string name and a uint8 age. The nextString and nextU8 should be done in the same order than done at serialization step