Making Transactions by Gateway API

While simple queries using the Gateway API are straightforward, as shown in our Making API Calls examples, creating and submitting a transaction is a more complex multi-step process. This document provides an example of the steps of this process for a transfer of tokens.

Other transactions can be created and submitted in a similar manner, such as our following example of Creating a Token. To see all of the various types of transactions that can be made, see the API Specification.

The Gateway offers endpoints under /transaction/ which enables you to make a transaction for any Radix account that you (or your client software) is able to cryptographically sign.

Usage of the transaction endpoint requires the following steps:

  1. Build the transaction that you want by calling the /transaction/build api path with the desired input parameters. This returns a unsigned_transaction blob.

  2. Sign the transaction locally using the private key of the account.

  3. Finalize and submit the transaction by calling the /transaction/finalize and optionally transaction/submit if required, with the signature, public key, and blob bytes.

1. Build the Transaction

The /transaction endpoint allows you to ask an gateway to build a transaction for you by providing some simple parameters. To do a token transfer transaction, you will use a transaction action type of TransferTokens for which you will need the following pieces of information:

  • The address the transfer is from_account (ours)

  • The address the transfer is to_account (our recipient)

  • The amount of tokens to send

  • The rri for the type of token we wish to send

Keep in mind that amount is expressed in the minimum subunits of the token. Each “whole” XRD token is divided into 1x10^18 subunits (called "attos"), so to send 3.5 XRD tokens, the amount would be calculated by 3.5 x 1E18 = 3500000000000000000.

The rri (Radix Resource Identifier) is a unique string for each token created on Radix, including XRD (as we saw in Making API Calls).

With this information, you can construct a call to the RPC method construction.build_transaction with your inputs sent as an actions array inside the params object.

A well-formed build request from the command line might look like this:

curl -d '{
    "network_identifier": {
        "network": "mainnet"
    },
    "actions": [
        {
            "type": "TransferTokens",
            "from_account": {
                "address": "rdx1qspl7mgjqwgwqyjvy2tj8swe8a4lr6mxqdhwmn60cujl6a85mqh69eg37p9ph"
            },
            "to_account": {
                "address": "rdx1qthu8yn06k75dnwpkysyl8smtwn0v4xy29auzjlcrw7vgduxvnwnst6derj"
            },
            "amount": {
                "token_identifier": {
                    "rri": "xrd_rr1qy5wfsfh"
                },
                "value": "1000000000000000000"
            }
        }
    ],
    "fee_payer": {
        "address": "rdx1qspl7mgjqwgwqyjvy2tj8swe8a4lr6mxqdhwmn60cujl6a85mqh69eg37p9ph"
    },
    "disable_token_mint_and_burn": true
}' -H "Content-Type: application/json" -X POST "https://mainnet.radixdlt.com/transaction/build" | python -m json.tool

From this call, you should get back a response like this:

{
    "transaction_build": {
        "fee": {
            "value": "72600000000000000",
            "token_identifier": {
                "rri": "xrd_rr1qy5wfsfh"
            }
        },
        "unsigned_transaction": "0d0001073b07270cb1c58d2ef5cad10a24d14625a7727510bbce2acc710e885cf99eb00000002010021000000000000000000000000000000000000000000000000000101ed50bab1800002004506000403dc62fa04804f75d009a2fac32c8ceb9dc5eaccd54934fe20ef5b86be40c7a2ab010000000000000000000000000000000000000000075df8c7a1dedbb7599c80000008000002004506000403dc62fa04804f75d009a2fac32c8ceb9dc5eaccd54934fe20ef5b86be40c7a2ab010000000000000000000000000000000000000000075df8c793fe2503b2388000020045060004038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee010000000000000000000000000000000000000000000000000de0b6b3a764000000",
        "payload_to_sign": "5abf81346f75464c3ad8c0242fcedb8e7c8c5d1479fa6392591e492bdfd73b6e"
    }
}

You will need the returned values of unsigned_transaction in the following steps.

2. Sign the Transaction Locally

Next, you need to sign the payload_to_sign using the private key of your account (ie. the "from" account). The details of doing so are outside the scope of this doc, but Radix follows a similar signature method as that of Bitcoin, using ECDSA signatures using EC curve “secp256k1”.

The result should be a DER encoded signature in hex that you will use in the following step.

3. Finalize and Submit the Transaction

Finally, you call transaction endpoint /transaction/finalize in order to submit your signed output and submit the transaction to the network, using the following parameters:

  • unsigned_transaction is the contents you received previously as a response from /construction/build.

  • publicKey is the 66 hexadecimal characters of your public key that corresponds to the privateKey you used to sign in Section 2, “Sign the Transaction Locally”, also known as a "compressed public key."

  • bytes is the signed output you generated in the previous step.

A call from the command line for this might look like:

curl -d '{
  "network_identifier": {
    "network": "mainnet"
  },
  "unsigned_transaction": "0d0001073b07270cb1c58d2ef5cad10a24d14625a7727510bbce2acc710e885cf99eb00000002010021000000000000000000000000000000000000000000000000000101ed50bab1800002004506000403dc62fa04804f75d009a2fac32c8ceb9dc5eaccd54934fe20ef5b86be40c7a2ab010000000000000000000000000000000000000000075df8c7a1dedbb7599c80000008000002004506000403dc62fa04804f75d009a2fac32c8ceb9dc5eaccd54934fe20ef5b86be40c7a2ab010000000000000000000000000000000000000000075df8c793fe2503b2388000020045060004038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee010000000000000000000000000000000000000000000000000de0b6b3a764000000",
  "signature": {
    "public_key": {
      "hex": "03f89249c269bde09e72f351b0debb348ced0d3542d7dc26ff1bf39b7ed4bb25ca"
    },
    "bytes": "0d00010779c779406687996e102114ad1383167041502508bf88fe95918f2322f8ce0f0a00000002010021000000000000000000000000000000000000000000000000056d726770c110800002004506000403f89249c269bde09e72f351b0debb348ced0d3542d7dc26ff1bf39b7ed4bb25ca010000000000000000000000000000000000000000000000056a1c54ea050f800000010007023568346d337509003f54347c2a10a8f09efd8398ed4b259909a3f848cc9fa05ca20e225f8bfb1d2d1a0000000003d0f1475cabcb81651b60d980e7be247997878aba3a419d4a119b020060040003d0f1475cabcb81651b60d980e7be247997878aba3a419d4a119b0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000002005f06000403f89249c269bde09e72f351b0debb348ced0d3542d7dc26ff1bf39b7ed4bb25ca03d0f1475cabcb81651b60d980e7be247997878aba3a419d4a119b00000000000000000000000000000000000000000000152d02c7e14af680000002006d050003d0f1475cabcb81651b60d980e7be247997878aba3a419d4a119b00063568346d3375000a5465737420746f6b656e000a5465737420746f6b656e001668747470733a2f2f746573742d746f6b656e2e636f6d001668747470733a2f2f746573742d746f6b656e2e636f6d000b013819d07e36a62c4f97c5dda50a251c5964377267402c4b6490d61ef1e153ce134fd3db27e151727289a19b1c6dad6d678547225e36153ec67f699944f0c5578f"
  },
  "submit": true
}'  -H "Content-Type: application/json" -X POST "https://mainnet.radixdlt.com/transaction/finalize" | python -m json.tool

Note that we used the submit parameter to submit the transaction in the same step as finalizing it.

You should get a response with a txID (transaction id), being 32 bytes (64 hexadecimal characters).

{
"signed_transaction": "0d0001073b07270cb1c58d2ef5cad10a24d14625a7727510bbce2acc710e885cf99eb00000002010021000000000000000000000000000000000000000000000000000101ed50bab1800002004506000403dc62fa04804f75d009a2fac32c8ceb9dc5eaccd54934fe20ef5b86be40c7a2ab010000000000000000000000000000000000000000075df8c7a1dedbb7599c80000008000002004506000403dc62fa04804f75d009a2fac32c8ceb9dc5eaccd54934fe20ef5b86be40c7a2ab010000000000000000000000000000000000000000075df8c793fe2503b2388000020045060004038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee010000000000000000000000000000000000000000000000000de0b6b3a764000000",
"transaction_identifier": {
  "hash": "d275sdsf35b3cb37903982ccae91b20e2e05c592808a70c6b41eae0d81e39666c3"
}
}

At this point you’re done, and your transaction has been submitted to the network.

In the event that you chose not to use the submit parameter and set it to false, you will receive the same return parameters, but you will have to take a final step of passing this updated blob (which now includes your signature) to the submit_transaction method.

This approach requires an extra roundtrip, but can be used if you want to know the transaction ID before submitting it for processing.

4. (Optional) Submit a Finalized Transaction to the Network

If you elected not to use submit when finalizing, you will now have to submit the fully prepared transaction to the network.

Call transaction endpoint /transaction/submit with your updated signed_transaction blob, and (optionally) the transaction ID.

IMPORTANT: The signed_transaction returned from finalize_transaction are different than the unsigned_transaction returned from build_transaction! The updated signed_transaction includes your signature, so be sure to submit the new one!

curl -d '{
  "network_identifier": {
    "network": "mainnet"
  },
  "signed_transaction": "0d0001073b07270cb1c58d2ef5cad10a24d14625a7727510bbce2acc710e885cf99eb00000002010021000000000000000000000000000000000000000000000000000101ed50bab1800002004506000403dc62fa04804f75d009a2fac32c8ceb9dc5eaccd54934fe20ef5b86be40c7a2ab010000000000000000000000000000000000000000075df8c7a1dedbb7599c80000008000002004506000403dc62fa04804f75d009a2fac32c8ceb9dc5eaccd54934fe20ef5b86be40c7a2ab010000000000000000000000000000000000000000075df8c793fe2503b2388000020045060004038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee010000000000000000000000000000000000000000000000000de0b6b3a764000000"
}' -H "Content-Type: application/json" -X POST "https://mainnet.radixdlt.com/transaction/submit" | python -m json.tool

If the response to this call is your same transaction ID, this indicates that the transaction was submitted successfully. The transaction has now been submitted to the “mempool” of the network where, if all is well, it should be swiftly processed and confirmed.

To poll for the transaction’s status, you may call RPC method transactions.get_transaction_status with the transaction’s txID like this:

curl -d '{
    "network_identifier": {
        "network": "mainnet"
    },
    "transaction_identifier": {
        "hash": "d275ff35b3cb37903982ccae91b20e2e05c592808a70c6b41eae0d81e39666c3"
    }
}' -X POST "https://mainnet.radixdlt.com/transaction/status" | python -m json.tool

The response:

{
    "ledger_state": {
        "version": 54973189,
        "timestamp": "2022-02-10T19:55:59.454Z",
        "epoch": 5435,
        "round": 8357
    },
    "transaction": {
        "transaction_status": {
            "status": "CONFIRMED",
            "confirmed_time": "2022-02-10T15:25:45.634Z",
            "ledger_state_version": 54921133
        },
        "transaction_identifier": {
            "hash": "d275ff35b3cb37903982ccae91b20e2e05c592808a70c6b41eae0d81e39666c3"
        },
        "actions": [
            {
                "from_account": {
                    "address": "rdx1qspl7mgjqwgwqyjvy2tj8swe8a4lr6mxqdhwmn60cujl6a85mqh69eg37p9ph"
                },
                "to_account": {
                    "address": "rdx1qthu8yn06k75dnwpkysyl8smtwn0v4xy29auzjlcrw7vgduxvnwnst6derj"
                },
                "amount": {
                    "value": "1000000000000000000",
                    "token_identifier": {
                        "rri": "xrd_tr1qyf0x76s"
                    }
                },
                "type": "TransferTokens"
            }
        ],
        "fee_paid": {
            "value": "72600000000000000",
            "token_identifier": {
                "rri": "xrd_rr1qy5wfsfh"
            }
        },
        "metadata": {
            "hex": "0d00010771eeb1593283fb56c98e2af8231b28c39861e9586cf5ef42a8f669ca07b15ef100000000010021000000000000000000000000000000000000000000000000000101ed50bab1800002004506000403f89249c269bde09e72f351b0debb348ced0d3542d7dc26ff1bf39b7ed4bb25ca0100000000000000000000000000000000000000000000000377765afca29700000008000002004506000403f89249c269bde09e72f351b0debb348ced0d3542d7dc26ff1bf39b7ed4bb25ca010000000000000000000000000000000000000000000000036995a448fb3300000200450600040273cad5ab910b3a614b345d8aa7178a0e0da4765c4fc6976bc3bdba2109ac3c81010000000000000000000000000000000000000000000000000de0b6b3a7640000000b01d28ed9fa9498d9a2743d3d500a6a86c1983579606d3d53e96f9b9a62a8d055175ebdbb78101855328330f2c6345464056d0fba5dc9a743455eb6675857af84a4"
        }
    }
}

will return one of following values depending on the status of the transaction:

PENDING

The transaction is processing and should not be considered complete.

CONFIRMED

The transaction has been accepted as valid and has been committed to the ledger.

FAILED

There was a problem, or a conflict, with the transaction causing it to be rejected.