GraphQL Programmable Transactions

Like the pysui legacy Programmable Transaction Builder (PTB), we have introduced a new PTB that leverages GraphQL RPC instead of JSON RPC. There are also other enhencements added to this new capabiity.

As with the previous page, the GraphQL PTB should be considered not ready for production.

The sections that follow will demonstrate using both the legacy and new PTB.

PTB Initialization

A few minor differences, noted in code comments, to initialize the PTB can be seen in the examples below.

Legacy

1from pysui import SuiConfig, SyncClient
2from pysui.sui.sui_txn import SyncTransaction
3
4client = SyncClient(SuiConfig.default_config())
5txn = SyncTransaction(client=client)

New

1from pysui import PysuiConfiguration, SyncGqlClient
2from pysui.sui.sui_pgql.pgql_sync_txn import SuiTransaction
3
4cfg = PysuiConfiguration(group_name=PysuiConfiguration.SUI_GQL_RPC_GROUP )
5client = SyncGqlClient(pysui_config=cfg)
6txn = SuiTransaction(client=client)

Notable Changes

Besides using our GraphQL QueryNodes behind the scene in the new PTB, we have also eliminated the need to wrap transaction command arguments. Now you can use native Python types for command parameters and move_call arguments.

Legacy

 1txn.move_call(
 2    target="0x0cce956e2b82b3844178b502e3a705dead7d2f766bfbe35626a0bbed06a42e9e::marketplace::buy_and_take",
 3    arguments=[
 4        ObjectID("0xb468f361f620ac05de721e487e0bdc9291c073a7d4aa7595862aeeba1d99d79e"),
 5        ObjectID("0xfd542ebc0f6743962077861cfa5ca9f1f19de8de63c3b09a6d9d0053d0104908"),
 6        ObjectID("0x97db1bba294cb30ce116cb94117714c64107eabf9a4843b155e90e0ae862ade5"),
 7        SuiAddress(coin_object_id),
 8        ObjectID(coin_object_id),
 9        SuiU64(1350000000),
10    ],
11    type_arguments=[
12        "0x3dcfc5338d8358450b145629c985a9d6cb20f9c0ab6667e328e152cdfd8022cd::suifrens::SuiFren<0x3dcfc5338d8358450b145629c985a9d6cb20f9c0ab6667e328e152cdfd8022cd::capy::Capy>",
13        "0x2::sui::SUI",
14    ],
15)

New

 1txn.move_call(
 2    target="0x0cce956e2b82b3844178b502e3a705dead7d2f766bfbe35626a0bbed06a42e9e::marketplace::buy_and_take",
 3    arguments=[
 4        "0xb468f361f620ac05de721e487e0bdc9291c073a7d4aa7595862aeeba1d99d79e",
 5        "0xfd542ebc0f6743962077861cfa5ca9f1f19de8de63c3b09a6d9d0053d0104908",
 6        "0x97db1bba294cb30ce116cb94117714c64107eabf9a4843b155e90e0ae862ade5",
 7        coin_object_id,
 8        coin_object_id,
 9        1350000000,
10    ],
11    type_arguments=[
12        "0x3dcfc5338d8358450b145629c985a9d6cb20f9c0ab6667e328e152cdfd8022cd::suifrens::SuiFren<0x3dcfc5338d8358450b145629c985a9d6cb20f9c0ab6667e328e152cdfd8022cd::capy::Capy>",
13        "0x2::sui::SUI",
14    ],
15)

DryRun Transaction

Before executing a transaction for debugging and general insight into how the transaction may perform, you can execute a DryRun:

DryRun Lite

A lite weight dryrun just uses the TransactionKind (transaction before all gas is reconcilled). The query node to use in this case is DryRunTransactionKind. If no options are provided, Sui will provide defautls. See the DryRunTransactionKind documentation.

 1def dry_run_kind(txn:SuiTransaction):
 2    """Uses defaults for DryRunTransactionKind."""
 3    raw_kind = txer.raw_kind()
 4    raw_kind_ser = base64.b64encode(raw_kind.serialize().decode())
 5
 6    # Print the TransactionType BCS (pre-serialized) structure
 7    # print(raw_kind.to_json(indent=2))
 8    # print(raw_kind_ser)
 9
10    # Execute the dry run for kind
11    result = txer.client.execute_query_node(
12        with_node=qn.DryRunTransactionKind(tx_bytestr=raw_kind_ser)
13    )
14
15    if result.is_ok():
16        print(result.result_data.to_json(indent=2))
17    else:
18        print(result.result_string)

DryRun Full

The full dryrun is basiscally a fully built transaction without signatures, but is dryrun for inspection

 1def dry_run(txn:SuiTransaction):
 2    """Uses fully built TransactionData for DryRunTransaction"""
 3    raw_kind = txer.raw_kind()
 4    # Print the TransactionData BCS (pre-serialized) structure
 5    # print(raw_kind.to_json(indent=2))
 6
 7    # Execute the dry run
 8    result =
 9        txn.client.execute_query_node(
10            with_node=qn.DryRunTransaction(tx_bytestr=txn.build())
11        )
12
13    if result.is_ok():
14        print(result.result_data.to_json(indent=2))
15    else:
16        print(result.result_string)

Execute Transaction

When it comes time to execute a transaction, both the bytecode of the transaction and signatures are required:

 1def transaction_execute(txn: SuiTransaction):
 2    """Uses fully built and serialized TransactionData for ExecuteTransaction."""
 3    raw_kind = txn.raw_kind()
 4    # Print the TransactionData BCS (pre-serialized) structure
 5    # print(raw_kind.to_json(indent=2))
 6
 7    # Build and sign to get the base64 transaction bytes and list of signatures
 8    tx_dict = txer.build_and_sign()
 9    # Execute the transaction
10    result = txer.client.execute_query_node(
11        with_node=qn.ExecuteTransaction(**tx_dict)
12
13    if result.is_ok():
14        # Unlike JSON RPC, the GraphRPC transaction execution just returns
15        # Status and the transaction digest. You can then take the transaction digest
16        # and then execute the GetTx for details.
17        print(result.result_data.to_json(indent=2))
18    else:
19        print(result.result_string)