Skip to content
This repository has been archived by the owner on May 26, 2023. It is now read-only.

feat/support-for-batch-txn #280

Merged
merged 26 commits into from
Apr 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions examples/node/createBatchTransaction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (C) 2018 Zilliqa
//
// This file is part of Zilliqa-Javascript-Library.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

const { BN, Long, bytes, units } = require('@zilliqa-js/util');
const { Zilliqa } = require('@zilliqa-js/zilliqa');
const {
toBech32Address,
getAddressFromPrivateKey,
} = require('@zilliqa-js/crypto');

const zilliqa = new Zilliqa('https://dev-api.zilliqa.com');

const chainId = 333; // chainId of the developer testnet
const msgVersion = 1; // current msgVersion
const VERSION = bytes.pack(chainId, msgVersion);

// Populate the wallet with an account
const privateKey =
'deb5c896228f8515146aa16f94a558ba14e52d8496b4b267b2d59cd9036f39a6';

zilliqa.wallet.addByPrivateKey(privateKey);

const address = getAddressFromPrivateKey(privateKey);
console.log(`My account address is: ${address}`);
console.log(`My account bech32 address is: ${toBech32Address(address)}`);

async function testBatchTransaction() {
try {
let txList = [];

for (let i = 0; i < 2; i++) {
// create a new transaction object
const tx = zilliqa.transactions.new(
{
version: VERSION,
toAddr: '0xA54E49719267E8312510D7b78598ceF16ff127CE',
amount: new BN(units.toQa('1', units.Units.Zil)),
gasPrice: units.toQa('2000', units.Units.Li),
gasLimit: Long.fromNumber(1),
},
false,
);

txList.push(tx);
}

console.log('Batch transactions created:');
console.log(txList);
console.log('Sending batch transactions...');

// sign the batch transaction
const signedTxList = await zilliqa.wallet.signBatch(txList);

// output signature for comparison
for (const signedTx of signedTxList) {
console.log(
'The expected transaction signature (before sending) is: %o',
signedTx.txParams.signature,
);
}

// send batch transaction
const batchResult = await zilliqa.blockchain.createBatchTransaction(
signedTxList,
);

console.log('Transactions created:...\n');
for (const confirmedTx of batchResult) {
console.log('The transaction id is: %o', confirmedTx.id);
console.log(`The transaction status is: %o`, confirmedTx.receipt);
console.log(
'Then actual transaction signature is: %o',
confirmedTx.signature,
);
}
} catch (err) {
console.error(err);
}
}

testBatchTransaction();
96 changes: 96 additions & 0 deletions examples/node/createBatchTransactionWithoutConfirm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright (C) 2018 Zilliqa
//
// This file is part of Zilliqa-Javascript-Library.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

const { BN, Long, bytes, units } = require('@zilliqa-js/util');
const { Zilliqa } = require('@zilliqa-js/zilliqa');
const {
toBech32Address,
getAddressFromPrivateKey,
} = require('@zilliqa-js/crypto');

const zilliqa = new Zilliqa('https://dev-api.zilliqa.com');

const chainId = 333; // chainId of the developer testnet
const msgVersion = 1; // current msgVersion
const VERSION = bytes.pack(chainId, msgVersion);

// Populate the wallet with an account
const privateKey =
'deb5c896228f8515146aa16f94a558ba14e52d8496b4b267b2d59cd9036f39a6';

zilliqa.wallet.addByPrivateKey(privateKey);

const address = getAddressFromPrivateKey(privateKey);
console.log(`My account address is: ${address}`);
console.log(`My account bech32 address is: ${toBech32Address(address)}`);

async function testBatchTransactionWithoutConfirm() {
try {
let txList = [];

for (let i = 0; i < 2; i++) {
// create a new transaction object
const tx = zilliqa.transactions.new(
{
version: VERSION,
toAddr: '0xA54E49719267E8312510D7b78598ceF16ff127CE',
amount: new BN(units.toQa('1', units.Units.Zil)),
gasPrice: units.toQa('2000', units.Units.Li),
gasLimit: Long.fromNumber(1),
},
false,
);

txList.push(tx);
}

console.log('Signing and sending transactions...\n');

// for batch transactions without confirm
// batch transactions must be signed first
// this would set the correct nonce for each transactions
const signedTxList = await zilliqa.wallet.signBatch(txList);

// output signature for comparison
for (const signedTx of signedTxList) {
console.log(
'The expected transaction signature (before sending) is: %o',
signedTx.txParams.signature,
);
}

const batchResult = await zilliqa.blockchain.createBatchTransactionWithoutConfirm(
signedTxList,
);

for (const tx of batchResult) {
// nonce must be different
// signatures must be identical when comparing to after they are processed on the blockchain
console.log('The transaction id is: %o', tx.id);
console.log('The transaction nonce is: %o', tx.nonce);
console.log('The actual transaction signature is: %o', tx.signature);
console.log(
'Is the current transaction confirmed?: %o\n',
tx.isConfirmed(),
);
}
} catch (err) {
console.error(err);
}
}

testBatchTransactionWithoutConfirm();
82 changes: 82 additions & 0 deletions examples/node/signBatchTransaction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (C) 2018 Zilliqa
//
// This file is part of Zilliqa-Javascript-Library.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

const { BN, Long, bytes, units } = require('@zilliqa-js/util');
const { Zilliqa } = require('@zilliqa-js/zilliqa');
const {
toBech32Address,
getAddressFromPrivateKey,
} = require('@zilliqa-js/crypto');

const zilliqa = new Zilliqa('https://dev-api.zilliqa.com');

const chainId = 333; // chainId of the developer testnet
const msgVersion = 1; // current msgVersion
const VERSION = bytes.pack(chainId, msgVersion);

// Populate the wallet with an account
const privateKey =
'deb5c896228f8515146aa16f94a558ba14e52d8496b4b267b2d59cd9036f39a6';

zilliqa.wallet.addByPrivateKey(privateKey);

const address = getAddressFromPrivateKey(privateKey);
console.log(`My account address is: ${address}`);
console.log(`My account bech32 address is: ${toBech32Address(address)}`);

async function testSignBatchTransaction() {
try {
let txList = [];

for (let i = 0; i < 2; i++) {
// create a new transaction object
const tx = zilliqa.transactions.new(
{
version: VERSION,
toAddr: '0xA54E49719267E8312510D7b78598ceF16ff127CE',
amount: new BN(units.toQa('1', units.Units.Zil)),
gasPrice: units.toQa('2000', units.Units.Li),
gasLimit: Long.fromNumber(1),
},
false,
);

txList.push(tx);
}

console.log('Batch transactions created:');
console.log(txList);
console.log('Signing batch transactions...');

// sign the batch transaction sequentially
const batchResult = await zilliqa.wallet.signBatch(txList);

console.log('Transactions signed...\n');
for (const signedTx of batchResult) {
// nonce must be different
console.log('The signed transaction nonce is: %o', signedTx.nonce);
console.log(
'The signed transaction signature is: %o\n',
signedTx.signature,
);
}
} catch (err) {
console.error(err);
}
}

testSignBatchTransaction();
45 changes: 45 additions & 0 deletions packages/zilliqa-js-account/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,51 @@ There is an offline mode that can be activated manually by setting the optional

- `Promise<Transaction>` - a signed transaction.

### `signBatch(txList: Transaction[]): Promise<Transaction[]>`

Sign a list of `Transaction` with the default `Account`. This method is asynchronous
as it will attempt to obtain the `nonce` from the `Provider`.

**Parameters**

- `txList`: `Transaction[]` - a list of `Transaction` instances.

**Returns**

- `Promise<Transaction[]>` - a list of signed transactions.

**Example**

```json
// zilliqa, wallet obj declaration omitted for clarity

let txList = [];
for (let i = 0; i < 2; i++) {
// create a new transaction object
const tx = zilliqa.transactions.new(
{
version: VERSION,
toAddr: '0xA54E49719267E8312510D7b78598ceF16ff127CE',
amount: new BN(units.toQa('1', units.Units.Zil)),
gasPrice: units.toQa('2000', units.Units.Li),
gasLimit: Long.fromNumber(1),
},
false,
);

txList.push(tx);
}

// sign the batch transactions sequentially
const batchResult = await zilliqa.wallet.signBatch(txList);

for (const signedTx of batchResult) {
// nonce must be different
console.log('The signed transaction nonce is: %o', signedTx.nonce);
console.log('The signed transaction signature is: %o\n', signedTx.signature);
}
```

## `Transaction`

A class that represents a single `Transaction` on the Zilliqa network. It is a functor. Its purpose is to encode the possible states a Transaction can be in: Confirmed, Rejected, Pending, or Initialised (i.e., not broadcasted).
Expand Down
33 changes: 33 additions & 0 deletions packages/zilliqa-js-account/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,40 @@ export const isTxParams = (obj: unknown): obj is TxParams => {
};

export const formatOutgoingTx: ReqMiddlewareFn<[TxParams]> = (req) => {
// if batch create transaction, payload is array
if (
Array.isArray(req.payload) &&
req.payload[0].method === RPCMethod.CreateTransaction &&
isTxParams(req.payload[0].params[0])
) {
// loop thru batch payloads and format the params
let payloads = [];
for (const txPayload of req.payload) {
const txConfig = txPayload.params[0];
payloads.push({
...txPayload,
params: [
{
...txConfig,
amount: txConfig.amount.toString(),
gasLimit: txConfig.gasLimit.toString(),
gasPrice: txConfig.gasPrice.toString(),
},
],
});
}

const ret = {
...req,
payload: payloads,
};

return ret;
}

// non-batch create transactions
if (
!Array.isArray(req.payload) &&
req.payload.method === RPCMethod.CreateTransaction &&
isTxParams(req.payload.params[0])
) {
Expand Down
Loading