@btq-js/tx
This package is based on EthereumJS's Tx package. Most of the implementation and interfaces remains the same, except that:
- For signature scheme, we replaced
ECDSA
withFalcon
for quantum safety. - Transation structure remains the same. However, definitions for
r
,s
andv
are changed due to new signature scheme. Please see the definition in the table describingtxData
. - In
TxOptions
we add a new optionpublicKey
due to the design that Falcon public key can not be recovered from its signature. For more detail please take a look atTxOptions
section.
We strongly suggest to read through the documentations from EthereumJS's Tx package beforehead.
Please note that:
This package depends on @btq-js/falcon-js
by default. If we want to change the underlying falcon implementation from javascript
to web assembly
, please follw these simple steps
Installation
To obtain the latest version, simply require the project using npm
:
npm install @btq-js/tx
You can also use yarn
or pnpm
based on your project's setup:
yarn add @btq-js/tx
pnpm add @btq-js/tx
Transaction Types
Currently the following transaction types are supported
Name | Description |
---|---|
FeeMarketEIP1559Transaction |
EIP-1559, gas fee market |
AccessListEIP2930Transaction |
EIP-2930, optional access lists |
Legacy Transaction |
Legacy or type-0 transactions |
Gas Fee Market Transactions (EIP-1559)
Name | Value |
---|---|
Class | FeeMarketEIP1559Transaction |
Type | 2 |
txData type | FeeMarketEIP1559TxData |
import { FeeMarketEIP1559Transaction } from '@btq-js/tx';
const txData = {
type: 2,
nonce: 0,
maxPriorityFeePerGas: 100,
maxFeePerGas: 1000,
gasLimit: 1000000000,
value: 0,
to: '0x8dDDbD32233CfAA36aa659feebFCD4368DF8c18f',
};
const tx = FeeMarketEIP1559Transaction.fromTxData(txData);
Access List Transactions (EIP-2930)
Name | Value |
---|---|
Class | AccessListEIP2930Transaction |
Type | 1 |
txData type | AccessListEIP2930TxData |
import { AccessListEIP2930Transaction } from '@btq-js/tx';
const txData = {
type: 1,
nonce: 1,
gasPrice: 100,
gasLimit: 1000000000,
value: 0,
to: '0x8dDDbD32233CfAA36aa659feebFCD4368DF8c18f',
chainId: '0x01',
accessList: [
{
address: '0x0000000000000000000000000000000000000101',
storageKeys: ['0x0000000000000000000000000000000000000000000000000000000000000000', '0x00000000000000000000000000000000000000000000000000000000000060a7'],
},
],
};
const tx = AccessListEIP2930Transaction.fromTxData(txData);
Legacy Transactions
Name | Value |
---|---|
Class | Transaction |
Type |
0 (internal) |
txData type | txData |
import { Transaction } from '@btq-js/tx';
const txParams = {
nonce: 1,
gasPrice: 100,
gasLimit: 1000000000,
value: 0,
to: '0x8dDDbD32233CfAA36aa659feebFCD4368DF8c18f',
data: '0x1a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
};
const tx = Transaction.fromTxData(txParams);
Transaction Factory
If you only know on runtime which tx type will be used within your code or if you want to keep your code transparent to tx types, this library comes with a TransactionFactory for your convenience which can be used as follows:
import { TransactionFactory } from '@btq-js/tx';
const txData = {}; // Use data from the different tx type examples
const tx = TransactionFactory.fromTxData(txData);
if (tx.supports(Capability.EIP2930AccessLists)) {
// Do something which only makes sense for txs with support for access lists
}
The correct tx type class for instantiation will then be chosen on runtime based on the data provided as an input.
TransactionFactory
supports the following static constructor methods except for fromEthersProvider()
provided by @ethereumjs/tx
:
public static fromTxData(txData: TxData | AccessListEIP2930TxData, txOptions: TxOptions = {}): TypedTransaction
public static fromSerializedData(data: Buffer, txOptions: TxOptions = {}): TypedTransaction
public static fromBlockBodyData(data: Buffer | Buffer[], txOptions: TxOptions = {})
Tx signing
import falcon from '@btq-js/falcon-js';
const seed = Buffer.from('<random_seed>', 'hex');
const key = await falcon.keyPair(seed);
const signedTx = await tx.sign(Buffer.from(key.publicKey), Buffer.from(key.privateKey));
Tx serialization
const serializedTx = signedTx.serialize().toString('hex');
const requestData = {
jsonrpc: '2.0',
method: 'eth_sendRawTransaction',
params: [serializedTx],
id: 1,
};
const postTx = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestData),
};
await fetch(network, postData);
TxOptions
We add a new option publicKey
which does not exist in EthereumJS's Tx package
Name | Type |
---|---|
common ? |
Common |
freeze ? |
boolean |
publicKey ? |
Buffer |
Unlike ECDSA, Falcon public can not be recovered from its signature, if you want to verify a signed tx which has nonce
larger than 0 (its r
value is address
instead of publicKey
), you should provide publicKey
.
Please see the following example:
import falcon from '@btq-js/falcon-js';
import { Transaction } from '@btq-js/tx';
const seed = Buffer.from('<random_seed>', 'hex');
const key = await falcon.keyPair(seed);
const publicKey = Buffer.from(key.publicKey);
const txData = {
nonce: 1,
gasPrice: 100,
gasLimit: 1000000000,
value: 0,
to: '0x8dDDbD32233CfAA36aa659feebFCD4368DF8c18f',
data: '0x1a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
r: '<address>',
s: '<signature_generated_by_falcon>',
v: '<chainId_and_metadata>',
};
const signedTx = Transaction.fromTxData(txData, { publicKey });
// Verification will fail if publicKey is not provided.
const isVerified = await signedTx.verifySignature();