Making Transactions by Node API

While simple queries using the JSON-RPC 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 Node API Specification.

Archive nodes offer a /construction API endpoint with the methods you will need to make a transaction for any Radix account that you (or your client software) is able to cryptographically sign.

Usage of the /construction endpoint requires the following steps:

  1. Build the transaction that you want by calling the construction.build_transaction method with the desired input parameters. This returns a transaction blob and a hash.

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

  3. Finalize and submit the transaction by calling the construction.finalize_transaction method with the signature, public key, and blob.

1. Build the Transaction

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

  • The address the transfer is from (ours)

  • The address the transfer is to (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 '{
    "jsonrpc": "2.0",
    "method": "construction.build_transaction",
    "params": {
        "actions": [
            {
                "type": "TokenTransfer",
                "from": "rdx1qspl7mgjqwgwqyjvy2tj8swe8a4lr6mxqdhwmn60cujl6a85mqh69eg37p9ph",
                "to": "rdx1qthu8yn06k75dnwpkysyl8smtwn0v4xy29auzjlcrw7vgduxvnwnst6derj",
                "amount": "3500000000000000000",
                "rri": "xrd_rr1qy5wfsfh"
            }
        ],
        "feePayer": "rdx1qspl7mgjqwgwqyjvy2tj8swe8a4lr6mxqdhwmn60cujl6a85mqh69eg37p9ph"
    },
    "id": 0
}' -H "Content-Type: application/json" -X POST "https://mainnet.radixdlt.com/construction" | python -m json.tool

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

{
    "result": {
        "fee": "125000000000000000",
        "transaction": {
            "blob": "073d6436979bb0e6519e20d2710e6faa00c42b977c6bd4029e0f614b1c938991e2000000000100210000000000000000000000000000000000000000000000000001bc16d674ec800002004506000402a79a147fb3edf4d9924b936cc25c47dc84caa089e1c71affd1c5751e45fa2457010000000000000000000000000000000000000000000000363261d7c5eabe800000073d6436979bb0e6519e20d2710e6faa00c42b977c6bd4029e0f614b1c938991e20000000202005f06000402a79a147fb3edf4d9924b936cc25c47dc84caa089e1c71affd1c5751e45fa2457034aeb072698ba9391456dabccbadcd3808d55c65acdf7d44e9e750000000000000000000000000000000000000000033b2e3c91efc989409c000002005f06000403ff6d120390e0124c229723c1d93f6bf1eb66036eedcf4fc725fd74f4d82fa2e5034aeb072698ba9391456dabccbadcd3808d55c65acdf7d44e9e750000000000000000000000000000000000000000000000000de0b6b3a7640000000c00b03031666630336635613066336236663764333463316339373337646663626632633861303063623866663938653233316331366635643932626530653034363330316239643937643661343461633539353761363631336338373664656466333837656461636632646166333863316461636333356166663863633630633665633634373330666439633763303433343133626233366337616562326664626466376534613737306532656632343564",
            "hashOfBlobToSign": "42244f5ac531a551a8ffd6f6c9e63e481fe1cf30b7bc42676840c3149695445b"
        }
    },
    "id": 0,
    "jsonrpc": "2.0"
}

You will need the returned values of blob and hashOfBlobToSign in the following steps.

2. Sign the Transaction Locally

Next, you need to sign the hashOfBlobToSign 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 the RPC method construction.finalize_transaction in order to submit your signed output and submit the transaction to the network, using the following parameters:

  • blob is the contents you received previously as a response from construction.finalize_transaction.

  • 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."

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

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

curl -d '{
    "method": "construction.finalize_transaction",
    "params": {
        "blob": "071549f82b3223ccab9ab94ba96dad32e79b8978682f68ae8ae0f1bb46adec00f2000000000100210000000000000000000000000000000000000000000000000001baab0a3303800002004506000402a79a147fb3edf4d9924b936cc25c47dc84caa089e1c71affd1c5751e45fa245701000000000000000000000000000000000000000000000030c69a799887ae800000071549f82b3223ccab9ab94ba96dad32e79b8978682f68ae8ae0f1bb46adec00f20000000202005f06000402a79a147fb3edf4d9924b936cc25c47dc84caa089e1c71affd1c5751e45fa2457034aeb072698ba9391456dabccbadcd3808d55c65acdf7d44e9e750000000000000000000000000000000000000000033b2e3c91efc989409c000002005f06000403ff6d120390e0124c229723c1d93f6bf1eb66036eedcf4fc725fd74f4d82fa2e5034aeb072698ba9391456dabccbadcd3808d55c65acdf7d44e9e750000000000000000000000000000000000000000000000000de0b6b3a7640000000c00ae303166663033383262303463656263303432323662336662666430366432353766303239363963656334613338383138313166393831663436623366643631393438633439303839353063633064663835346234303165623266356262613063643563343864313533613438306131633830653631623032343335643032653665633334363161313537646164653763663337643161313037306137643362326261653039386635633037353064",
        "signatureDER": "30450221009b8618a0953174ad060d3ea00d2af120d2d39b0106d5a50ede14ea7e52fa6ed002202a4def499b53c10d9fd388dce078065596a6321c42a3da9547a42ca25cef3008",
        "publicKeyOfSigner": "02a79a147fb3edf4d9924b936cc25c47dc84caa089e1c71affd1c5751e45fa2457",
        "immediateSubmit":true
    },
    "id": 0
}' -H "Content-Type: application/json" -X POST "https://mainnet.radixdlt.com/construction" | python -m json.tool

Note that we used the immediateSubmit 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).

{
    "result": {
        "blob": "073d6436979bb0e6519e20d2710e6faa00c42b977c6bd4029e0f614b1c938991e2000000000100210000000000000000000000000000000000000000000000000001bc16d674ec800002004506000402a79a147fb3edf4d9924b936cc25c47dc84caa089e1c71affd1c5751e45fa2457010000000000000000000000000000000000000000000000363261d7c5eabe800000073d6436979bb0e6519e20d2710e6faa00c42b977c6bd4029e0f614b1c938991e20000000202005f06000402a79a147fb3edf4d9924b936cc25c47dc84caa089e1c71affd1c5751e45fa2457034aeb072698ba9391456dabccbadcd3808d55c65acdf7d44e9e750000000000000000000000000000000000000000033b2e3c91efc989409c000002005f06000403ff6d120390e0124c229723c1d93f6bf1eb66036eedcf4fc725fd74f4d82fa2e5034aeb072698ba9391456dabccbadcd3808d55c65acdf7d44e9e750000000000000000000000000000000000000000000000000de0b6b3a7640000000c00b030316666303366356130663362366637643334633163393733376466636266326338613030636238666639386532333163313666356439326265306530343633303162396439376436613434616335393537613636313363383736646564663338376564616366326461663338633164616363333561666638636336306336656336343733306664396337633034333431336262333663376165623266646264663765346137373065326566323435640b0097d4c574aa8f55ecadc55bf3c5d527dbc0978b212a9d6ff14db951b45566827d211211745892a2041d004d312e279eb03706038444efcae2714bedb28343cc6e",
        "txID": "9c4070c4f6bc0b1ff7313b96f2b466943c629da68985d86435c7a7d57997401a"
    },
    "id": 0,
    "jsonrpc": "2.0"
}

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 immediateSubmit parameter, 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 immediateSubmit when finalizing, you will now have to submit the fully prepared transaction to the network.

Call RPC method construction.submit_transaction with your updated blob, and (optionally) the transaction ID.

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

curl -d '{
    "method": "construction.submit_transaction",
    "params": {
        "blob": "071549f82b3223ccab9ab94ba96dad32e79b8978682f68ae8ae0f1bb46adec00f2000000000100210000000000000000000000000000000000000000000000000001baab0a3303800002004506000402a79a147fb3edf4d9924b936cc25c47dc84caa089e1c71affd1c5751e45fa245701000000000000000000000000000000000000000000000030c69a799887ae800000071549f82b3223ccab9ab94ba96dad32e79b8978682f68ae8ae0f1bb46adec00f20000000202005f06000402a79a147fb3edf4d9924b936cc25c47dc84caa089e1c71affd1c5751e45fa2457034aeb072698ba9391456dabccbadcd3808d55c65acdf7d44e9e750000000000000000000000000000000000000000033b2e3c91efc989409c000002005f06000403ff6d120390e0124c229723c1d93f6bf1eb66036eedcf4fc725fd74f4d82fa2e5034aeb072698ba9391456dabccbadcd3808d55c65acdf7d44e9e750000000000000000000000000000000000000000000000000de0b6b3a7640000000c00ae303166663033383262303463656263303432323662336662666430366432353766303239363963656334613338383138313166393831663436623366643631393438633439303839353063633064663835346234303165623266356262613063643563343864313533613438306131633830653631623032343335643032653665633334363161313537646164653763663337643161313037306137643362326261653039386635633037353064",
        "txID": "9c4070c4f6bc0b1ff7313b96f2b466943c629da68985d86435c7a7d57997401a"
    },
    "id": 0
}' -H "Content-Type: application/json" -X POST "https://mainnet.radixdlt.com/construction" | 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 '{
    "jsonrpc": "2.0",
    "method": "transactions.get_transaction_status",
    "params": {
        "txID": "9c4070c4f6bc0b1ff7313b96f2b466943c629da68985d86435c7a7d57997401a"
    },
    "id": 1
}' -H "Content-Type: application/json" -u superadmin:nginx-password -X POST "https://mainnet.radixdlt.com/archive" | python -m json.tool

The response:

{
    "result": {
        "txID": "252bdaa65b8ed0650491370cfa6e8ff82ae0f2aee0b8bbfde502a52523e378cd",
        "status": "CONFIRMED"
    },
    "id": 1,
    "jsonrpc": "2.0"
}

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.