import {
Address,
/* eslint-disable no-unused-vars */
Network
/* eslint-enable no-unused-vars */
} from './Network.js';
import * as nc from './models.js';
import {
Hash256,
PublicKey,
/* eslint-disable no-unused-vars */
Signature
/* eslint-enable no-unused-vars */
} from '../CryptoTypes.js';
import RuleBasedTransactionFactory from '../RuleBasedTransactionFactory.js';
import { uint8ToHex } from '../utils/converter.js';
/**
* Factory for creating NEM transactions.
*/
export default class TransactionFactory {
/**
* Creates a factory for the specified network.
* @param {Network} network NEM network.
* @param {Map<string, function>|undefined} typeRuleOverrides Type rule overrides.
*/
constructor(network, typeRuleOverrides = undefined) {
/**
* @private
*/
this._factory = TransactionFactory._buildRules(typeRuleOverrides); // eslint-disable-line no-underscore-dangle
/**
* @private
*/
this._network = network;
}
/**
* Gets rule names with registered hints.
*/
get ruleNames() {
return Array.from(this._factory.rules.keys());
}
/**
* Looks up the friendly name for the specified transaction.
* @param {nc.TransactionType} transactionType Transaction type.
* @param {number} transactionVersion Transaction version.
* @returns {string} Transaction friendly name.
*/
static lookupTransactionName(transactionType, transactionVersion) {
return `${nc.TransactionType.valueToKey(transactionType.value).toLowerCase()}_transaction_v${transactionVersion}`;
}
/**
* Creates a transaction from a transaction descriptor.
* @param {object} transactionDescriptor Transaction descriptor.
* @param {boolean} autosort When set (default), descriptor arrays requiring ordering will be automatically sorted.
* When unset, descriptor arrays will be presumed to be already sorted.
* @returns {nc.Transaction} Newly created transaction.
*/
create(transactionDescriptor, autosort = true) {
const transaction = this._factory.createFromFactory(nc.TransactionFactory.createByName, {
...transactionDescriptor,
network: this._network.identifier
});
if (autosort)
transaction.sort();
// hack: explicitly translate transfer message
if (nc.TransactionType.TRANSFER === transaction.type && transaction.message && 'string' === typeof (transaction.message.message))
transaction.message.message = new TextEncoder().encode(transaction.message.message);
return transaction;
}
/**
* Converts a transaction to a non-verifiable transaction.
* @param {nc.Transaction|nc.NonVerifiableTransaction} transaction Transaction object.
* @returns {nc.NonVerifiableTransaction} Non-verifiable transaction object.
*/
static toNonVerifiableTransaction(transaction) {
// search module to find class name because `constructor.name` is dropped during minification
let nonVerifiableClassName = Object.getOwnPropertyNames(nc).find(key => transaction.constructor === nc[key]);
if (!nonVerifiableClassName)
throw Error('invalid transaction instance');
if (0 !== nonVerifiableClassName.indexOf('NonVerifiable'))
nonVerifiableClassName = `NonVerifiable${nonVerifiableClassName}`;
const NonVerifiableClass = nc[nonVerifiableClassName];
const nonVerifiableTransaction = new NonVerifiableClass();
Object.getOwnPropertyNames(transaction).forEach(key => {
if (key in nonVerifiableTransaction)
nonVerifiableTransaction[key] = transaction[key];
});
return nonVerifiableTransaction;
}
/**
* Attaches a signature to a transaction.
* @param {nc.Transaction} transaction Transaction object.
* @param {Signature} signature Signature to attach.
* @returns {string} JSON transaction payload.
*/
static attachSignature(transaction, signature) {
transaction.signature = new nc.Signature(signature.bytes);
const transactionHex = uint8ToHex(this.toNonVerifiableTransaction(transaction).serialize());
const signatureHex = signature.toString();
const jsonPayload = `{"data":"${transactionHex}", "signature":"${signatureHex}"}`;
return jsonPayload;
}
/**
* Tries to coerce an sdk type to a model type.
* @param {object} value Value to convert.
* @returns {nc.Address|undefined} Converted value or undefined.
* @private
*/
static _nemTypeConverter(value) {
if (value instanceof Address) {
// yes, unfortunately, nem's Address is 40 bytes string, but we need to pass it as actual bytes not to confuse ByteArray
return new nc.Address(new TextEncoder().encode(value.toString()));
}
return undefined;
}
/**
* Builds a rule based transaction factory.
* @param {Map<string, function>|undefined} typeRuleOverrides Type rule overrides.
* @returns {RuleBasedTransactionFactory} Rule based transaction factory.
* @private
*/
static _buildRules(typeRuleOverrides) {
const factory = new RuleBasedTransactionFactory(nc, this._nemTypeConverter, typeRuleOverrides);
factory.autodetect();
[
'LinkAction', 'MessageType', 'MosaicSupplyChangeAction', 'MosaicTransferFeeType',
'MultisigAccountModificationType', 'NetworkType', 'TransactionType'
].forEach(name => { factory.addEnumParser(name); });
[
'Message', 'NamespaceId', 'MosaicId', 'Mosaic', 'SizePrefixedMosaic', 'MosaicLevy',
'MosaicProperty', 'SizePrefixedMosaicProperty', 'MosaicDefinition',
'MultisigAccountModification', 'SizePrefixedMultisigAccountModification'
].forEach(name => { factory.addStructParser(name); });
const sdkTypeMapping = {
Address,
Hash256,
PublicKey
};
Object.keys(sdkTypeMapping).forEach(name => { factory.addPodParser(name, sdkTypeMapping[name]); });
[
'struct:SizePrefixedMosaic', 'struct:SizePrefixedMosaicProperty', 'struct:SizePrefixedMultisigAccountModification'
].forEach(name => { factory.addArrayParser(name); });
return factory;
}
}