Source: symbol/TransactionFactory.js

const { Address } = require('./Network');
const { generateNamespaceId, generateMosaicId } = require('./idGenerator');
const sc = require('./models');
const { Hash256, PublicKey } = require('../CryptoTypes');
const { RuleBasedTransactionFactory } = require('../RuleBasedTransactionFactory');
const { uint8ToHex } = require('../utils/converter');

/**
 * Factory for creating Symbol transactions.
 */
class TransactionFactory {
	/**
	 * Creates a factory for the specified network.
	 * @param {Network} network Symbol network.
	 * @param {Map} typeRuleOverrides Type rule overrides.
	 */
	constructor(network, typeRuleOverrides) {
		this.factory = TransactionFactory.buildRules(typeRuleOverrides);
		this.network = network;
	}

	_createAndExtend(transactionDescriptor, FactoryClass) {
		const transaction = this.factory.createFromFactory(FactoryClass.createByName, {
			...transactionDescriptor,
			network: this.network.identifier
		});

		// autogenerate artifact ids
		if (sc.TransactionType.NAMESPACE_REGISTRATION === transaction.type) {
			const rawNamespaceId = generateNamespaceId(new TextDecoder().decode(transaction.name), transaction.parentId.value);
			transaction.id = new sc.NamespaceId(rawNamespaceId);
		} else if (sc.TransactionType.MOSAIC_DEFINITION === transaction.type) {
			const address = this.network.publicKeyToAddress(new PublicKey(transaction.signerPublicKey.bytes));
			transaction.id = new sc.MosaicId(generateMosaicId(address, transaction.nonce.value));
		}

		return transaction;
	}

	/**
	 * Creates a transaction from a transaction descriptor.
	 * @param {object} transactionDescriptor Transaction descriptor.
	 * @returns {object} Newly created transaction.
	 */
	create(transactionDescriptor) {
		return this._createAndExtend(transactionDescriptor, sc.TransactionFactory);
	}

	/**
	 * Creates an embedded transaction from a transaction descriptor.
	 * @param {object} transactionDescriptor Transaction descriptor.
	 * @returns {object} Newly created transaction.
	 */
	createEmbedded(transactionDescriptor) {
		return this._createAndExtend(transactionDescriptor, sc.EmbeddedTransactionFactory);
	}

	/**
	 * Attaches a signature to a transaction.
	 * @param {object} transaction Transaction object.
	 * @param {Signature} signature Signature to attach.
	 * @returns {string} JSON transaction payload.
	 */
	static attachSignature(transaction, signature) {
		transaction.signature = new sc.Signature(signature.bytes);

		const transactionBuffer = transaction.serialize();
		const hexPayload = uint8ToHex(transactionBuffer);
		const jsonPayload = `{"payload": "${hexPayload}"}`;
		return jsonPayload;
	}

	static _symbolTypeConverter(value) {
		if (value instanceof Address)
			return new sc.UnresolvedAddress(value.bytes);

		return undefined;
	}

	static buildRules(typeRuleOverrides) {
		const factory = new RuleBasedTransactionFactory(sc, this._symbolTypeConverter, typeRuleOverrides);
		factory.autodetect();

		['MosaicFlags', 'AccountRestrictionFlags'].forEach(name => { factory.addFlagsParser(name); });

		[
			'AliasAction', 'LinkAction', 'LockHashAlgorithm',
			'MosaicRestrictionType', 'MosaicSupplyChangeAction',
			'NamespaceRegistrationType', 'NetworkType', 'TransactionType'
		].forEach(name => { factory.addEnumParser(name); });

		factory.addStructParser('UnresolvedMosaic');

		const sdkTypeMapping = {
			UnresolvedAddress: Address,
			Address,
			Hash256,
			PublicKey,
			VotingPublicKey: PublicKey
		};
		Object.keys(sdkTypeMapping).forEach(name => { factory.addPodParser(name, sdkTypeMapping[name]); });

		['UnresolvedMosaicId', 'TransactionType', 'UnresolvedAddress', 'struct:UnresolvedMosaic'].forEach(name => {
			factory.addArrayParser(name);
		});

		return factory;
	}
}

module.exports = { TransactionFactory };