Skip to content
On this page

Invoke Actions

Pando is a set of blockchain protocols executed by sending transactions to multisig address.

To retrieve information, call the APIs and read the response.

However, if you want to interact with the protocols and tell them what you want to do with cryptocurrency, you need to send transactions to Pando's multisig address.

The behavior of each transaction is illustrated by the Action Protocol, a byte array encoded in the transaction memo field. When you send a transaction, MTG Nodes decode the memo field and execute the action within.

To better understand how the action works, let's generate the action manually. This time, we'll use 4swap's swap action as an example: We want to swap CNB to DOGE.

First, we need to know the asset ID of CNB and DOGE:

  • CNB: 965e5c6e-434c-3fa9-b780-c50f43cd955c
  • DOGE: 6770a1e5-6086-44d5-b60f-545f9d9e8ffd

Those 2 assets has a pool at Pando, so we can swap them directly.

Form the action

The "Action" is formed as a byte-based structure. You can see the specification in the reference.

You can build the structure yourself, or using the packages provided by Pando Team.

Generate action using @foxone/memo-encode

For JavaScript developers, you can use the @foxone/memo-encode to generate the action quickly:

Here is an example to generate the action for the swap action:

javascript
import { swap as MemoEncoder } from "@foxone/memo-encode";

// swap encode
const params = {
  // an UUID to trace the order. We just generate a random UUID.
  follow_id: uuid(),
  // the asset ID of the asset you want to get. In the example, we want to get `DOGE`.
  fill_asset_id: '6770a1e5-6086-44d5-b60f-545f9d9e8ffd',
  // the minimum amount of asset you will get. In the example, we want to get at least `0.00000001 DOGE`.
  minimum: 0.00000001,
  // the routes of the swap. We'll leave it blank for now
  // Pando will use CNB/DOGE pool for the swap. 
  // In real projects, you need to calculate the routes by yourself
  route_hash: "",
  // the members group to receive the filled asset.
  // we leave it blank to use the sender(our bot) as the receiver.
  members: [],
};

// base64 memo for mixin transaction
const memo = MemoEncoder.encodeSwapMemo(params);

Generate actions by mtg packages

For Golang developers, you can use the mtg/mtgpack and mtg/protocol packages to generate the action quickly.

Here is an example to generate the action for the swap action:

go
import (
  "encoding/base64"
  "github.com/google/uuid"
  "github.com/pandodao/mtg/mtgpack"
  "github.com/pandodao/mtg/protocol"
  "github.com/shopspring/decimal"
)

func generateSwapMemo() string {
  // protocol header
  header := protocol.Header{
    Version:    1,
    ProtocolID: protocol.ProtocolFswap,
    FollowID:   uuid.New(),
    Action:     3,
  }

  // the receiver group to receive the filled asset.
  // we leave it blank to use the sender(our bot) as the receiver.
  receiver := protocol.MultisigReceiver{
    Version:   1,
    Members:   []uuid.UUID{},
    Threshold: 0,
  }

  // id of the asset you want to get
  assetID := "6770a1e5-6086-44d5-b60f-545f9d9e8ffd"
  
  // minimum amount of the asset you want to get
  min, _ := decimal.NewFromString("0.00000001")
  
  // the routes of the swap. We'll leave it blank for now
  // Pando will use CNB/DOGE pool for the swap. 
  // In real projects, you need to calculate the routes by yourself: https://github.com/fox-one/4swap-sdk-go/blob/e62757b2c4966d3ebac7eb40dcad7d1926c7f9e3/route.go 
  route := ""

  // encode memo
  enc := mtgpack.NewEncoder()
  if err := enc.EncodeValues(header, receiver, uuid.MustParse(assetID), route, min); err != nil {
    panic(err)
  }
  return base64.StdEncoding.EncodeToString(enc.Bytes())
}

TIP

To simplify the process, we don't specify the receiver and route fields. However, in real projects, you may need to calculate the routes by yourself, and specify the correct receiver or receivers group.

For the route hash algorithm, you can refer to the 4swap-sdk-go.

For the receiver group specification, you can refer to the Action Specification.

Send transactions

Send by mixin-cli

When you have the encoded action for the memo field, you can send transactions to Pando's MTG.

There are various ways to send transactions, but for testing, you can use mixin-cli.

bash
$ mixin-cli -f ./keystore.json transfer --asset 965e5c6e-434c-3fa9-b780-c50f43cd955c \
--amount 20 \
--receivers a753e0eb-3010-4c4a-a7b2-a7bda4063f62 \
--receivers 099627f8-4031-42e3-a846-006ee598c56e \
--receivers aefbfd62-727d-4424-89db-ae41f75d2e04 \
--receivers d68ca71f-0e2c-458a-bb9c-1d6c2eed2497 \
--receivers e4bc0740-f8fe-418c-ae1b-32d9926f5863 \
--threshold 3 \
--memo $YOUR_MEMO_HERE 

Note that the receivers fields are filled with the members of the MTG. The threshold field is the multisig threshold of the MTG. You can get the members and threshold from /info API as we mentioned in the previous section.

the memo field is the action you generated.

Run the command and type y to confirm the transfer, you will see the transaction is sent to the 4swap MTG. Wait for a while, you will receive some DOGE in your keystore:

bash
$ mixin-cli -f $LYRIC_KEYSTORE asset list
AssetId                               Symbol     Name                     Balance
6770a1e5-6086-44d5-b60f-545f9d9e8ffd  DOGE       Dogecoin                 0.00000212
965e5c6e-434c-3fa9-b780-c50f43cd955c  CNB        Chui Niu Bi              12.919

Send programmatically

For production, you may need to use the mixin-sdk-go or other SDKs to do that.

go
client := mixin.NewFromKeystore(keystore)

tx, err := client.Transaction(ctx, &mixin.TransferInput{
  AssetID: payAssetID,
  Amount:  decimal.RequireFromString(amount),
  TraceID: mixin.RandomTraceID(),
  Memo:    `$YOUR_MEMO_HERE`,
  OpponentMultisig: struct {
    Receivers []string `json:"receivers,omitempty"`
    Threshold uint8    `json:"threshold,omitempty"`
  }{
    Receivers: members,          
    Threshold: uint8(threshold),
  },
}, *pin)