Beginner
Signing Transactions
In this tutorial, we will be exploring the process of signing transactions in MultiversX, a decentralized platform for smart contract development. Signing transactions is an important step in executing smart contract functions and transferring funds on the blockchain.
MultiversX provides a secure and convenient way to sign transactions, allowing developers to interact with the blockchain easily. Whether you are new to blockchain technology or an experienced developer, this tutorial will provide you with a comprehensive understanding of how to sign transactions in MultiversX.
We will cover the basics of transactions, how to sign a transaction in MultiversX, and how to verify the signature of a transaction. By the end of this tutorial, you will be able to confidently sign transactions in MultiversX and take the next step in your smart contract development journey.
How to serialize and sign the Transaction payload
Transactions must be signed with the Sender’s Private Key before submitting them to the MultiversX Network. Signing is performed with the Ed25519 algorithm.
General structure
An unsigned transaction has the following fields:
Field |
Type |
Required |
Description |
---|---|---|---|
|
number |
Yes |
The account sequence number |
|
string |
Yes (can be |
The value to transfer, represented in atomic units: |
|
string |
Yes |
The address of the receiver (bech32 format) |
|
string |
Yes |
The address of the sender (bech32 format) |
|
number |
Yes |
The gas price to be used in the scope of the transaction |
|
number |
Yes |
The maximum number of gas units allocated for the transaction |
|
string |
No |
Arbitrary information about the transaction, base64-encoded. |
|
string |
Yes |
The chain identifier. |
|
number |
Yes |
The version of the transaction (e.g. |
A signed transaction has the additional signature
field:
Field |
Type |
Description |
---|---|---|
signature |
string |
The digital signature consisting of 128 hex-characters (thus 64 bytes in a raw representation) |
Serialization for signing
Before signing a transaction, one has to serialize it, that is, to obtain its raw binary representation – as a sequence of bytes. This is achieved through the following steps:
- order the fields of the transaction with respect to their appearance order in the table above (
nonce
is first,version
is last). - discard the
data
field if it’s empty. - convert the
data
payload to its base64 representation. - obtain a JSON representation (UTF-8 string) of the transaction, maintaining the order of the fields. This JSON representation must contain no indentation and no separating spaces.
- encode the resulting JSON (UTF-8) string as a sequence of bytes.
For example, given the transaction:
nonce = 7 value = "10000000000000000000" # 10 EGLD receiver = "erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r" sender = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz" gasPrice = 1000000000 gasLimit = 70000 data = "for the book" chainID = "1" version = 1
By applying steps 1-3 (step 4 is omitted in this example), one obtains:
{"nonce":7,"value":"10000000000000000000","receiver":"erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r","sender":"erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz","gasPrice":1000000000,"gasLimit":70000,"data":"Zm9yIHRoZSBib29r","chainID":"1","version":1}
If the transaction has an empty no data field:
nonce = 8 value = "10000000000000000000" # 10 ERD receiver = "erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r" sender = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz" gasPrice = 1000000000 gasLimit = 50000 data = "" chainID = "1" version = 1
Then it’s serialized form (step 5 is omitted in this example) is as follows:
{"nonce":8,"value":"10000000000000000000","receiver":"erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r","sender":"erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz","gasPrice":1000000000,"gasLimit":50000,"chainID":"1","version":1}
Ed25519 signature
MultiversX uses the Ed25519 algorithm to sign transactions. In order to obtain the signature, one can use generic software libraries such as PyNaCl, tweetnacl-js or components of MultiversX SDK such as mx-sdk-js-wallet, mx-sdk-py-wallet, erdgo, erdjava, mx-sdk-js-wallet-cli etc.
The raw signature consisting of 64 bytes has to be hex-encoded afterwards and placed in the transaction object.
Ready to broadcast
Once the signature
field is set as well, the transaction is ready to be broadcasted. Following the examples above, their ready-to-broadcast form is as follows:
# With data field nonce = 7 value = "10000000000000000000" # 10 EGLD receiver = "erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r" sender = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz" gasPrice = 1000000000 gasLimit = 70000 data = "Zm9yIHRoZSBib29r" chainID = "1" version = 1 signature = "1702bb7696f992525fb77597956dd74059b5b01e88c813066ad1f6053c6afca97d6eaf7039b2a21cccc7d73b3e5959be4f4c16f862438c7d61a30c91e3d16c01"
# Without data field nonce = 8 value = "10000000000000000000" # 10 EGLD receiver = "erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r" sender = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz" gasPrice = 1000000000 gasLimit = 50000 data = "" chainID = "1" version = 1 signature = "4a6d8186eae110894e7417af82c9bf9592696c0600faf110972e0e5310d8485efc656b867a2336acec2b4c1e5f76c9cc70ba1803c6a46455ed7f1e2989a90105"
Tools for signing
In order to sign a transaction without actually dispatching it, one can use mxpy or sdk-js-wallet-cli.
Sign using mxpy (Command Line Interface)
Using a pem file:
$ mxpy tx new --nonce=41 --data="Hello, World" --gas-limit=70000 \ --receiver=erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz \ --pem=aliceKey.pem --pem-index=0 --outfile=myTransaction.json
Using a JSON wallet key (and its password):
mxpy tx new --nonce=41 --data="Hello, World" --gas-limit=70000 \ --receiver=erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz \ --keyfile=walletKeyOfAlice.json --passfile=passwordOfAlice.txt \ --outfile=myTransaction.json
In either case, the output file looks like this:
{ "tx": { "nonce": 41, "value": "0", "receiver": "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz", "sender": "erd1aedmqfsflx4rhwvs7v9z52e7eylkevz4w342jzuaa9ezy5unsc5qqy963v", "gasPrice": 1000000000, "gasLimit": 70000, "data": "SGVsbG8sIFdvcmxk", "chainID": "1596807148", "version": 123, "signature": "f432442ebfee6edf4518c10d006ab571d8ecbd6f2601995554c75d3402b424364908235d45449ba5dd28575e4a8129271020e4718cf8a4c6f44e22c0885ac40a" }, "hash": "", "data": "Hello, World" }
Sign using mxjs-wallet
Given an unsigned transaction in a JSON file:
{ "nonce": 42, "receiver": "erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r", "value": "100000000000000000", "gasPrice": 1000000000, "gasLimit": 70000, "data": "food for cats", "chainID": "1", "version": 1 }
You can sign it as follows:
$ mxjs-wallet sign -i ./aliceToBob.json -o ./aliceToBobSigned.json \ -k walletKeyOfAlice.json -p passwordOfAlice.txt
The signed transaction looks like this:
{ "nonce": 42, "value": "100000000000000000", "receiver": "erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r", "sender": "erd1ylzm22ngxl2tspgvwm0yth2myr6dx9avtx83zpxpu7rhxw4qltzs9tmjm9", "gasPrice": 1000000000, "gasLimit": 70000, "data": "Zm9vZCBmb3IgY2F0cw==", "chainID": "1", "version": 1, "signature": "5845301de8ca3a8576166fb3b7dd25124868ce54b07eec7022ae3ffd8d4629540dbb7d0ceed9455a259695e2665db614828728d0f9b0fb1cc46c07dd669d2f0e" }