Sending Transactions
A critical feature of any exchange is the ability to withdraw the funds held in custody to an arbitrary address of the user's choosing. Because Zilliqa nodes do not provide an API for signing transactions on your behalf, you will have to do so locally using an SDK of your choosing. We provide examples using zilliqa-js, the official JavaScript SDK.
Constructing the Transaction Object
There are several ways to construct a Transaction
instance. We recommend
using the transaction factory that is on the umbrella Zilliqa object, like
so:
const { Zilliqa } = require("@zilliqa-js/zilliqa");
const { getPubKeyFromPrivateKey } = require("@zilliqa-js/crypto");
const { BN, Long, bytes, units } = require("@zilliqa-js/util");
const api = "https://dev-api.zilliqa.com";
const chainId = 333; // Testnet
const msgVersion = 1;
const zilliqa = new Zilliqa(api);
const toAddress = "BENCH32_ADDRESS";
const fromPrivateKey = "SENDER_PRIVATE_KEY";
const fromPublicKey = getPubKeyFromPrivateKey(fromPrivateKey);
const fromAddress = getAddressFromPrivateKey(fromPrivateKey);
const amountToSendInZil = 0.17;
const gasPriceInZil = 0.002;
const nextNonce =
(await zilliqa.blockchain.getBalance(fromAddress)).result.nonce + 1;
const rawTx = zilliqa.transactions.new({
version: bytes.pack(chainId, msgVersion),
amount: new BN(units.toQa(amountToSendInZil * 1000000, units.Units.Li)),
nonce: nextNonce,
gasLimit: Long.fromNumber(50), // normal (non-contract) transactions cost 50 gas after network upgrade in mid april 2021
gasPrice: new BN(units.toQa(gasPriceInZil * 1000000, units.Units.Li)), // the minimum gas price is 1,000 li
toAddr: toAddress,
pubKey: fromPublicKey, // this determines which account is used to send the tx
});
Signing the Transaction
Again, there are a few ways you can sign your transaction. Under the hood,
signing is done with the elliptic curve secp256k1
. The easiest way to do
this is by using a wallet. Extending our example above:
zilliqa.wallet.addByPrivateKey(fromPrivateKey);
// signWith uses the specified address to perform the signing of the transaction.
// note that we provided the nonce to use when constructing the transaction.
// if the nonce is not provided, zilliqa-js will automatically try to determine the correct nonce to use.
// however, if there is no network connection, zilliqa-js will not be able to
// do that, and signing will fail.
const signedTx = await zilliqa.wallet.signWith(rawTx, fromAddress);
Note that we provided the nonce to use when constructing the transaction. If the nonce is not provided, zilliqa-js will automatically try to determine the correct nonce to use. However, if there is no network connection, zilliqa-js will not be able to do that, and signing will fail.
If the Transaction
is successfully signed, you will be able to access the
signature
property on txParams
.
console.log(signedTx.txParams.signature); // 128-bit signature
At this stage, you'll be able to broadcast your newly-signed transaction to the network through a seed node.
Sending the Transaction
Broadcasting a signed transaction is trivial, but involves some subtleties that can trip you up if you do not have a deep understanding of Zilliqa's architecture.
We demonstrate a lower-level way to broadcast a transaction using the built-in
HTTPProvider
, as follows:
const res = await zilliqa.provider.send("CreateTransaction", signedTx.txParams);
This returns a Promise
that, if successful, will contain your transaction
hash:
console.log(res.result && res.result.TranID); // 32-byte transaction hash
However, note that result
will not exist on the response if there is an
error in processing the transaction. Instead, the response will contain an
error
key, which is an object that complies with JSON-RPC 2.0.
If you receive a TranID
, that means your transaction was accepted by the
seed node, and is now pending. zilliqa-js
provides a way to automatically
poll the lookup for confirmation:
// returns a Promise<Transaction>
// in this case, we try polling the node 33 times, increasing the interval
// between attempts by 1000ms each time. this works out roughly to the block
// time on the Zilliqa main net.
const tx = await signedTx.confirm(res.result.TranID, 33, 1000);
The confirm
method returns a Promise the status of which signifies the
confirmation status of the transaction. If the transaction was confirmed:
assert(signedTx.isConfirmed() === true);