Sending XEM with a Transfer Transaction⚓︎
Sending XEM from one account to another is the most basic action on the NEM blockchain, and every other type of transaction follows the same general pattern.
This tutorial shows how to create, sign, and announce a transfer transaction that sends 1 XEM between two accounts, and then poll the transaction's status until it is confirmed.
Prerequisites⚓︎
Before you start, make sure to:
- Set Up your Development Environment.
- Create an account to send the transfer transaction, either from code or by using a wallet.
- Obtain XEM to pay for the transaction fee and transfer amount. See Getting Testnet Funds from the Faucet.
Full Code⚓︎
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | |
The whole code is wrapped in a single try block to provide simple error handling,
but applications will probably want to use more fine-grained control.
Code Explanation⚓︎
Setting Up the Accounts⚓︎
Every transfer transaction involves two accounts: a sender and a recipient.
The sender is the account that signs the transaction and pays the fee.
Its private key is loaded from the SIGNER_PRIVATE_KEY environment variable.
If not provided, a test key is used as default.
The recipient is the account that receives the XEM.
Its address is loaded from the RECIPIENT_ADDRESS environment variable.
If not provided, a test address is used as default.
Defining the Transfer Amount⚓︎
The snippet defines the transfer amount in the xem variable, loaded as a number from the XEM_AMOUNT environment
variable.
If not provided, a default of 1 XEM is used.
The transaction's amount field requires atomic units, not whole XEM.
XEM has a divisibility of 6, so one XEM equals one million atomic units.
The snippet derives amount by multiplying xem by 1'000'000.
Fetching Network Time⚓︎
Every NEM transaction contains two time fields, both expressed in network time, the number of seconds since the NEM nemesis block:
timestamp: The moment the transaction is created, set here to the current network time.deadline: How long the network keeps trying to confirm the transaction before discarding it. It must be after the timestamp and no more than 24 hours later. Otherwise, the node rejects the transaction. This example sets it two hours after the timestamp, well within the limit.
Building a transfer therefore needs an accurate network time.
The /time-sync/network-time GET endpoint reports the node's current network time.
The node returns this value in milliseconds, so the code divides it by 1000 to obtain the seconds that transactions
expect.
However, applications do not need to query the network time before every transaction. It can be fetched once and then adjusted using the local system clock when needed. This provides a good balance between accuracy and performance.
The code wraps the seconds value in the SDK's class to obtain the timestamp, and derives the
deadline from it with the helper.
Building the Transaction⚓︎
The snippet calls with a descriptor that supplies the transfer transaction's properties:
-
: This tutorial uses
TransferTransactionV2, the current transfer version, which can carry both XEM and other mosaics. No mosaics are attached here, so the transaction sends XEM only. -
: The signer is the account that will pay the fee. In a transfer transaction, it is also the source of the transferred XEM.
-
and : The values computed in the network time step.
-
: The address that will receive the XEM.
-
: The atomic-unit value computed earlier. For 1 XEM, this is
1_000_000.
Sending a mosaic or a message
A TransferTransactionV2 can also carry other mosaics instead of XEM, or include a message, with the fee
calculated differently in each case.
See the Transfer Mosaics and Transfer with a Message tutorials.
Calculating the Transaction Fee⚓︎
Every transaction pays a fee to the harvester account that includes it in a block.
Rather than implement NEM's fixed fee schedule by hand, the snippet calls the SDK's helper, which reads the XEM amount directly from the transaction built in the previous step.
The returned fee is assigned to transaction.fee before signing.
The fee starts at 0.05 XEM for small amounts and grows with the XEM sent, up to a cap of 1.25 XEM.
Signing and Serializing⚓︎
Once the transaction is created, it must be signed with the signing account's private key. Signing ensures the transaction is authentic and authorized by the sender.
returns a signature. adds the signature to the transaction and serializes it into a JSON payload ready to be submitted directly to a node for announcement.
Announcing the Transaction⚓︎
The signed payload is submitted to the /transaction/announce POST endpoint of any NEM node.
The node validates the transaction as soon as it is announced and reports the outcome in the response.
A result of SUCCESS means the transaction passed this first check and was added to the unconfirmed pool.
Any other result means the node did not accept it, and the response message explains why, for example that the
account does not hold enough XEM to cover the amount and the fee.
Do not rely on unconfirmed transactions
A SUCCESS result only means the transaction reached the unconfirmed pool.
It is not yet guaranteed to be included in a block.
Wait until it is confirmed, and ideally past the rewrite limit, before relying
on it.
Waiting for Confirmation⚓︎
The snippet above repeatedly queries the /transaction/get GET endpoint using the hash of the announced transaction.
Polling vs WebSockets
This step uses polling to check whether the transaction has been confirmed. Polling is used here for illustration purposes, but it is not the recommended approach for real applications.
WebSockets provide a more responsive solution without the overhead of repeated API calls.
While the transaction is still unconfirmed, the endpoint responds with an error, and the code waits one second before retrying, for up to 120 attempts (about two minutes).
Once the transaction is included in a block, the endpoint returns it together with the block height, and the loop ends.
NEM produces a block roughly once per minute, so confirmation usually takes from a few seconds to a couple of minutes.
Output⚓︎
The output shown below corresponds to a typical run of the program.
Some highlights from the output:
-
Signer public key (line 11): The account that signs the transaction and sends the XEM.
-
Transaction fee (line 13):
50000atomic units (0.05XEM), the fee for sending the default amount of 1 XEM. -
Recipient address (line 15): The account that receives the XEM. This is the same
RECIPIENT_ADDRESS, but it looks different because NEM's transaction format encodes each character of its Base32 text as an ASCII code in hexadecimal, so5442...decodes back toTBUL...(54isT,42isB, and so on). -
Transfer amount (line 16):
1000000atomic units, equal to 1 XEM. -
No mosaics (line 17): An empty mosaics array means the transaction sends XEM only.
-
Announcement result (line 20): A result of
SUCCESSmeans the node accepted the transaction into the unconfirmed pool. -
Transaction hash (line 21): The hash that uniquely identifies the transaction on the network.
-
Confirmation (line 34): The transaction is included in block
626588.
The number of pending checks depends on how soon the next block is harvested, so it varies between runs.
To see the transaction from the network's perspective, you can search for the transaction hash on the
NEM testnet explorer.
The hash is printed in the line that says Waiting for confirmation from /transaction/get?hash=....
Conclusion⚓︎
This tutorial showed how to:
Most other NEM transaction types are created, signed, and announced in the same way.