Action
The Action
type represents a single action call against a smart contract method. One or more of these actions are required for the creation of a Transaction.
Anatomy of an Action
Every action on an Antelope blockchain consists of the following information:
{
account: 'eosio.token',
name: 'transfer',
authorization: [
{
actor: 'foo',
permission: 'active'
}
],
data: {
from: 'teamgreymass',
to: 'funds.gm',
quantity: '0.0001 EOS',
memo: 'Thanks for all the fish!'
}
}
The first two fields, account
and name
, correspond to the smart contract and method the action will call. The account
specifies the account name the smart contract is deployed on, and the name
indicates the name of the method to call.
The authorization
array defines the account(s) that will authorize the accounts that authorize the transaction in the PermissionLevel format. Each account specified as an authorization
will need to be accompanied by a Signature.
Finally, the data
object in the action defines the parameters passed to the smart contract call. This field on the action is serialized before it’s submitted to the blockchain, which is what the Action
Antelope data type helps achieve. This data type provides the methods needed in order to encode and decode the serialized data, depending on the developer’s needs.
Once serialized, the actual anatomy of an Action
at the blockchain level looks like this:
{
account: 'eosio.token',
name: 'transfer',
authorization: [
{
actor: 'foo',
permission: 'active'
}
],
data: '80b1915e5d268dca00000092019ca65e010000000000000004454f5300000000185468616e6b7320666f7220616c6c20746865206669736821'
}
Usage
A smart contract Action
can be created in multiple ways, depending on whether the data is serialized or not.
import { Action } from "@wharfkit/antelope"
// Passing in data which contains serialized data (either raw or using a Struct)
const action = Action.from(data)
// Passing in unserialized data alongside an ABI or Struct as the 2nd parameter
const action = Action.from(data, abi)
The resulting typed Action
will be represented in the serialized format and be ready for inclusion in a Transaction.
Creating an Action
Using Unserialized Data With an ABI
An ABI can also be passed as a 2nd parameter to the .from
method to automatically convert unserialized data.
const data = {
from: "teamgreymass",
to: "funds.gm",
quantity: "0.0001 EOS",
memo: "Thanks for all the fish!",
}
const untypedAction = {
account: "eosio.token",
name: "transfer",
authorization: [{ actor: "foo", permission: "active" }],
data,
}
const { abi } = await client.v1.chain.get_abi("eosio.token")
const typedAction = Action.from(untypedAction, abi)
/*
{
"account":"eosio.token",
"name":"transfer",
"authorization":[{"actor":"foo","permission":"active"}],
"data":"80b1915e5d268dca00000092019ca65e010000000000000004454f5300000000185468616e6b7320666f7220616c6c20746865206669736821"
}
*/
Using Unserialized Data With a Struct
A Struct can be used to wrap unserialized Action
data for automatic serialization.
@Struct.type("transfer")
export class Transfer extends Struct {
@Struct.field(Name) from!: Name
@Struct.field(Name) to!: Name
@Struct.field(Asset) quantity!: Asset
@Struct.field("string") memo!: string
}
const data = Transfer.from({
from: "teamgreymass",
to: "funds.gm",
quantity: "0.0001 EOS",
memo: "Thanks for all the fish!",
})
const untypedAction = {
account: "eosio.token",
name: "transfer",
authorization: [{ actor: "foo", permission: "active" }],
data,
}
const typedAction = Action.from(untypedAction)
/*
{
"account":"eosio.token",
"name":"transfer",
"authorization":[{"actor":"foo","permission":"active"}],
"data":"80b1915e5d268dca00000092019ca65e010000000000000004454f5300000000185468616e6b7320666f7220616c6c20746865206669736821"
}
*/
Using Unserialized Data With a Predefined ABI
The ABI passed in as a 2nd parameter can also be manually defined or read from a cached version.
const data = {
from: "teamgreymass",
to: "funds.gm",
quantity: "0.0001 EOS",
memo: "Thanks for all the fish!",
}
const untypedAction = {
account: "eosio.token",
name: "transfer",
authorization: [{ actor: "foo", permission: "active" }],
data,
}
const abi = ABI.from({
version: "eosio::abi/1.0",
types: [],
variants: [],
structs: [
{
name: "transfer",
base: "",
fields: [
{
name: "from",
type: "name",
},
{
name: "to",
type: "name",
},
{
name: "quantity",
type: "asset",
},
{
name: "memo",
type: "string",
},
],
},
],
actions: [],
tables: [],
ricardian_clauses: [],
})
const typedAction = Action.from(untypedAction, abi)
/*
{
"account":"eosio.token",
"name":"transfer",
"authorization":[{"actor":"foo","permission":"active"}],
"data":"80b1915e5d268dca00000092019ca65e010000000000000004454f5300000000185468616e6b7320666f7220616c6c20746865206669736821"
}
*/
Using Serialized Data
Passing in raw serialized data does not require an ABI to assemble an Action
.
const data = {
account: "eosio.token",
name: "transfer",
authorization: [{ actor: "foo", permission: "active" }],
data: "80b1915e5d268dca00000092019ca65e010000000000000004454f5300000000185468616e6b7320666f7220616c6c20746865206669736821",
}
const typedAction = Action.from(data)
/*
{
"account":"eosio.token",
"name":"transfer",
"authorization":[{"actor":"foo","permission":"active"}],
"data":"80b1915e5d268dca00000092019ca65e010000000000000004454f5300000000185468616e6b7320666f7220616c6c20746865206669736821"
}
*/
Decoding Action data
Instances of the Action
type can also be used to decode the action data and represent it in native Antelope core types. Each Action
instance has a built-in decodeData
method which utilizes the Serializer to convert the data.
Using decodeAction With an ABI
Any ABI either manually defined in-code or retrieved from an APIClient can be passed to decodeData
to decode the serialized data into an object.
const data = {
account: "eosio.token",
name: "transfer",
authorization: [{ actor: "foo", permission: "active" }],
data: "80b1915e5d268dca00000092019ca65e010000000000000004454f5300000000185468616e6b7320666f7220616c6c20746865206669736821",
}
const typedAction = Action.from(data)
const { abi } = await client.v1.chain.get_abi("eosio.token")
const decoded = typedAction.decodeData(abi)
/**
{
from: Name { value: UInt64 { value: [BN] } },
to: Name { value: UInt64 { value: [BN] } },
quantity: Asset {
units: Int64 { value: [BN] },
symbol: Symbol { value: [UInt64] }
},
memo: 'Thanks for all the fish!'
}
*/
Using decodeAction With a Struct
Any Struct types defined in-code can be passed to decodeData
to decode the serialized data into an object.
@Struct.type("transfer")
class Transfer extends Struct {
@Struct.field(Name) from!: Name
@Struct.field(Name) to!: Name
@Struct.field(Asset) quantity!: Asset
@Struct.field("string") memo!: string
}
const typedAction = Action.from({
account: "eosio.token",
name: "transfer",
authorization: [{ actor: "foo", permission: "active" }],
data: "80b1915e5d268dca00000092019ca65e010000000000000004454f5300000000185468616e6b7320666f7220616c6c20746865206669736821",
})
const decoded = typedAction.decodeData(Transfer)
/**
{
from: Name { value: UInt64 { value: [BN] } },
to: Name { value: UInt64 { value: [BN] } },
quantity: Asset {
units: Int64 { value: [BN] },
symbol: Symbol { value: [UInt64] }
},
memo: 'Thanks for all the fish!'
}
*/
Making Data Human Readable
The decodeData
function returns all values in Antelope typed formats (Name, Asset, etc) for developers to work with in their applications. In order to display this data in a human readable format, the Serializer provides a helper function called Objectify which will iterate over a data structure and convert all the Antelope types to human readable values.
@Struct.type("transfer")
class Transfer extends Struct {
@Struct.field(Name) from!: Name
@Struct.field(Name) to!: Name
@Struct.field(Asset) quantity!: Asset
@Struct.field("string") memo!: string
}
const typedAction = Action.from({
account: "eosio.token",
name: "transfer",
authorization: [{ actor: "foo", permission: "active" }],
data: "80b1915e5d268dca00000092019ca65e010000000000000004454f5300000000185468616e6b7320666f7220616c6c20746865206669736821",
})
const decoded = typedAction.decodeData(Transfer)
/**
{
from: Name { value: UInt64 { value: [BN] } },
to: Name { value: UInt64 { value: [BN] } },
quantity: Asset {
units: Int64 { value: [BN] },
symbol: Symbol { value: [UInt64] }
},
memo: 'Thanks for all the fish!'
}
*/
const readable = Serializer.objectify(decoded)
/**
{
from: 'teamgreymass',
to: 'funds.gm',
quantity: '0.0001 EOS',
memo: 'Thanks for all the fish!'
}
*/