UNPKG

@aeternity/aepp-sdk

Version:
686 lines (564 loc) 23.8 kB
import _Object$getOwnPropertySymbols from "@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols"; import _Object$getOwnPropertyDescriptor from "@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptor"; import _forEachInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/for-each"; import _Object$getOwnPropertyDescriptors from "@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptors"; import _Object$defineProperties from "@babel/runtime-corejs3/core-js-stable/object/define-properties"; import _Object$defineProperty from "@babel/runtime-corejs3/core-js-stable/object/define-property"; import _typeof from "@babel/runtime-corejs3/helpers/typeof"; import _toConsumableArray from "@babel/runtime-corejs3/helpers/toConsumableArray"; import _defineProperty from "@babel/runtime-corejs3/helpers/defineProperty"; import _slicedToArray from "@babel/runtime-corejs3/helpers/slicedToArray"; function ownKeys(object, enumerableOnly) { var keys = _Object$keys(object); if (_Object$getOwnPropertySymbols) { var symbols = _Object$getOwnPropertySymbols(object); if (enumerableOnly) symbols = _filterInstanceProperty(symbols).call(symbols, function (sym) { return _Object$getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { var _context10; _forEachInstanceProperty(_context10 = ownKeys(Object(source), true)).call(_context10, function (key) { _defineProperty(target, key, source[key]); }); } else if (_Object$getOwnPropertyDescriptors) { _Object$defineProperties(target, _Object$getOwnPropertyDescriptors(source)); } else { var _context11; _forEachInstanceProperty(_context11 = ownKeys(Object(source))).call(_context11, function (key) { _Object$defineProperty(target, key, _Object$getOwnPropertyDescriptor(source, key)); }); } } return target; } import _mapInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/map"; import _reduceInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/reduce"; import _concatInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/concat"; import _Array$isArray from "@babel/runtime-corejs3/core-js-stable/array/is-array"; import _findInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/find"; import _filterInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/filter"; import _Object$entries from "@babel/runtime-corejs3/core-js-stable/object/entries"; import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes"; import _Object$assign from "@babel/runtime-corejs3/core-js-stable/object/assign"; import _Object$keys from "@babel/runtime-corejs3/core-js-stable/object/keys"; import _JSON$stringify from "@babel/runtime-corejs3/core-js-stable/json/stringify"; import _indexOfInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/index-of"; import BigNumber from 'bignumber.js'; import { decode as rlpDecode, encode as rlpEncode } from 'rlp'; import { AE_AMOUNT_FORMATS, formatAmount } from '../../utils/amount-formatter'; import { assertedType } from '../../utils/crypto'; import { DEFAULT_FEE, FIELD_TYPES, OBJECT_ID_TX_TYPE, PREFIX_ID_TAG, TX_DESERIALIZATION_SCHEMA, TX_FEE_BASE_GAS, TX_FEE_OTHER_GAS, TX_SERIALIZATION_SCHEMA, TX_TYPE, VALIDATION_MESSAGE, VSN } from './schema'; import { readInt, readId, readPointers, writeId, writeInt, buildPointers, encode, decode, buildHash } from './helpers'; import { toBytes } from '../../utils/bytes'; import MPTree from '../../utils/mptree'; /** * JavaScript-based Transaction builder * @module @aeternity/aepp-sdk/es/tx/builder * @export TxBuilder * @example import { TxBuilder } from '@aeternity/aepp-sdk' */ var ORACLE_TTL_TYPES = { delta: 'delta', block: 'block' }; // SERIALIZE AND DESERIALIZE PART function deserializeField(value, type, prefix) { if (!value) return ''; switch (type) { case FIELD_TYPES.ctVersion: { var _value = _slicedToArray(value, 3), vm = _value[0], abi = _value[2]; return { vmVersion: readInt(Buffer.from([vm])), abiVersion: readInt(Buffer.from([abi])) }; } case FIELD_TYPES.amount: return readInt(value); case FIELD_TYPES["int"]: return readInt(value); case FIELD_TYPES.id: return readId(value); case FIELD_TYPES.ids: return _mapInstanceProperty(value).call(value, readId); case FIELD_TYPES.bool: return value[0] === 1; case FIELD_TYPES.binary: return encode(value, prefix); case FIELD_TYPES.stateTree: return encode(value, 'ss'); case FIELD_TYPES.string: return value.toString(); case FIELD_TYPES.payload: return encode(value, 'ba'); case FIELD_TYPES.pointers: return readPointers(value); case FIELD_TYPES.rlpBinary: return unpackTx(value, true); case FIELD_TYPES.rlpBinaries: return _mapInstanceProperty(value).call(value, function (v) { return unpackTx(v, true); }); case FIELD_TYPES.rawBinary: return value; case FIELD_TYPES.hex: return value.toString('hex'); case FIELD_TYPES.offChainUpdates: return _mapInstanceProperty(value).call(value, function (v) { return unpackTx(v, true); }); case FIELD_TYPES.callStack: // TODO: fix this return [readInt(value)]; case FIELD_TYPES.mptrees: return _mapInstanceProperty(value).call(value, function (t) { return new MPTree(t); }); case FIELD_TYPES.callReturnType: switch (readInt(value)) { case '0': return 'ok'; case '1': return 'error'; case '2': return 'revert'; default: return value; } case FIELD_TYPES.sophiaCodeTypeInfo: return _reduceInstanceProperty(value).call(value, function (acc, _ref) { var _ref2 = _slicedToArray(_ref, 4), funHash = _ref2[0], fnName = _ref2[1], argType = _ref2[2], outType = _ref2[3]; return _objectSpread(_objectSpread({}, acc), {}, _defineProperty({}, fnName.toString(), { funHash: funHash, argType: argType, outType: outType })); }, {}); default: return value; } } function serializeField(value, type, prefix) { var _context; switch (type) { case FIELD_TYPES.amount: case FIELD_TYPES["int"]: return writeInt(value); case FIELD_TYPES.id: return writeId(value); case FIELD_TYPES.ids: return _mapInstanceProperty(value).call(value, writeId); case FIELD_TYPES.bool: return Buffer.from([value ? 1 : 0]); case FIELD_TYPES.binary: return decode(value, prefix); case FIELD_TYPES.stateTree: return decode(value, 'ss'); case FIELD_TYPES.hex: return Buffer.from(value, 'hex'); case FIELD_TYPES.signatures: return _mapInstanceProperty(value).call(value, Buffer.from); case FIELD_TYPES.payload: return typeof value === 'string' && value.split('_')[0] === 'ba' ? decode(value, 'ba') : toBytes(value); case FIELD_TYPES.string: return toBytes(value); case FIELD_TYPES.pointers: return buildPointers(value); case FIELD_TYPES.mptrees: return _mapInstanceProperty(value).call(value, function (t) { return t.serialize(); }); case FIELD_TYPES.ctVersion: return Buffer.from(_concatInstanceProperty(_context = []).call(_context, _toConsumableArray(toBytes(value.vmVersion)), [0], _toConsumableArray(toBytes(value.abiVersion)))); case FIELD_TYPES.callReturnType: switch (value) { case 'ok': return writeInt(0); case 'error': return writeInt(1); case 'revert': return writeInt(2); default: return value; } default: return value; } } function validateField(value, key, type, prefix) { var assert = function assert(valid, params) { return valid ? {} : _defineProperty({}, key, VALIDATION_MESSAGE[type](params)); }; // All fields are required if (value === undefined || value === null) return _defineProperty({}, key, 'Field is required'); // Validate type of value switch (type) { case FIELD_TYPES.amount: case FIELD_TYPES["int"]: { var isMinusValue = (!isNaN(value) || BigNumber.isBigNumber(value)) && BigNumber(value).lt(0); return assert((!isNaN(value) || BigNumber.isBigNumber(value)) && BigNumber(value).gte(0), { value: value, isMinusValue: isMinusValue }); } case FIELD_TYPES.id: if (_Array$isArray(prefix)) { var p = _findInstanceProperty(prefix).call(prefix, function (p) { return p === value.split('_')[0]; }); return assert(p && PREFIX_ID_TAG[value.split('_')[0]], { value: value, prefix: prefix }); } return assert(assertedType(value, prefix) && PREFIX_ID_TAG[value.split('_')[0]] && value.split('_')[0] === prefix, { value: value, prefix: prefix }); case FIELD_TYPES.binary: return assert(value.split('_')[0] === prefix, { prefix: prefix, value: value }); case FIELD_TYPES.string: return assert(true); case FIELD_TYPES.ctVersion: return assert(_typeof(value) === 'object' && Object.prototype.hasOwnProperty.call(value, 'abiVersion') && Object.prototype.hasOwnProperty.call(value, 'vmVersion')); case FIELD_TYPES.pointers: return assert(_Array$isArray(value) && !_findInstanceProperty(value).call(value, function (e) { return e !== Object(e); }), { value: value }); default: return {}; } } function transformParams(params, schema) { var _context2, _context3; var _ref5 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, denomination = _ref5.denomination; params = _reduceInstanceProperty(_context2 = _filterInstanceProperty(schema).call(schema, function (_ref6) { var _ref7 = _slicedToArray(_ref6, 2), _ = _ref7[0], t = _ref7[1]; return t === FIELD_TYPES.amount; })).call(_context2, function (acc, _ref8) { var _ref9 = _slicedToArray(_ref8, 1), key = _ref9[0]; return _objectSpread(_objectSpread({}, params), {}, _defineProperty({}, key, formatAmount(params[key], { denomination: denomination }))); }, params); var schemaKeys = _mapInstanceProperty(schema).call(schema, function (_ref10) { var _ref11 = _slicedToArray(_ref10, 1), k = _ref11[0]; return k; }); return _reduceInstanceProperty(_context3 = _Object$entries(params)).call(_context3, function (acc, _ref12) { var _context4; var _ref13 = _slicedToArray(_ref12, 2), key = _ref13[0], value = _ref13[1]; if (_includesInstanceProperty(schemaKeys).call(schemaKeys, key)) acc[key] = value; if (_includesInstanceProperty(_context4 = ['oracleTtl', 'queryTtl', 'responseTtl']).call(_context4, key)) { acc["".concat(key, "Type")] = value.type === ORACLE_TTL_TYPES.delta ? 0 : 1; acc["".concat(key, "Value")] = value.value; } return acc; }, {}); } // INTERFACE function getOracleRelativeTtl(params, txType) { var _TX_TYPE$oracleRegist; var ttlKey = (_TX_TYPE$oracleRegist = {}, _defineProperty(_TX_TYPE$oracleRegist, TX_TYPE.oracleRegister, 'oracleTtl'), _defineProperty(_TX_TYPE$oracleRegist, TX_TYPE.oracleExtend, 'oracleTtl'), _defineProperty(_TX_TYPE$oracleRegist, TX_TYPE.oracleQuery, 'queryTtl'), _defineProperty(_TX_TYPE$oracleRegist, TX_TYPE.oracleResponse, 'responseTtl'), _TX_TYPE$oracleRegist)[txType]; if (params[ttlKey] || params["".concat(ttlKey, "Value")]) { return params["".concat(ttlKey, "Value")] || params[ttlKey].value; } return 1; } /** * Calculate min fee * @function * @alias module:@aeternity/aepp-sdk/es/tx/builder/index * @rtype (txType, { gas = 0, params }) => String * @param {String} txType - Transaction type * @param {Options} options - Options object * @param {String|Number} options.gas - Gas amount * @param {Object} options.params - Tx params * @return {String|Number} * @example calculateMinFee('spendTx', { gas, params }) */ export function calculateMinFee(txType, _ref14) { var _ref14$gas = _ref14.gas, gas = _ref14$gas === void 0 ? 0 : _ref14$gas, params = _ref14.params, vsn = _ref14.vsn; var multiplier = BigNumber(1e9); // 10^9 GAS_PRICE if (!params) return BigNumber(DEFAULT_FEE).times(multiplier).toString(10); var actualFee = buildFee(txType, { params: _objectSpread(_objectSpread({}, params), {}, { fee: 0 }), multiplier: multiplier, gas: gas, vsn: vsn }); var expected = BigNumber(0); while (!actualFee.eq(expected)) { actualFee = buildFee(txType, { params: _objectSpread(_objectSpread({}, params), {}, { fee: actualFee }), multiplier: multiplier, gas: gas, vsn: vsn }); expected = actualFee; } return expected.toString(10); } /** * Calculate fee based on tx type and params * @param txType * @param params * @param gas * @param multiplier * @param vsn * @return {BigNumber} */ function buildFee(txType, _ref15) { var params = _ref15.params, _ref15$gas = _ref15.gas, gas = _ref15$gas === void 0 ? 0 : _ref15$gas, multiplier = _ref15.multiplier, vsn = _ref15.vsn; var _buildTx = buildTx(_objectSpread({}, params), txType, { vsn: vsn }), txWithOutFee = _buildTx.rlpEncoded; var txSize = txWithOutFee.length; return TX_FEE_BASE_GAS(txType).plus(TX_FEE_OTHER_GAS(txType)({ txSize: txSize, relativeTtl: getOracleRelativeTtl(params, txType) })).times(multiplier); } /** * Calculate fee * @function * @alias module:@aeternity/aepp-sdk/es/tx/builder * @rtype (fee, txType, gas = 0) => String * @param {String|Number} fee - fee * @param {String} txType - Transaction type * @param {Options} options - Options object * @param {String|Number} options.gas - Gas amount * @param {Object} options.params - Tx params * @return {String|Number} * @example calculateFee(null, 'spendTx', { gas, params }) */ export function calculateFee() { var fee = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; var txType = arguments.length > 1 ? arguments[1] : undefined; var _ref16 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, _ref16$gas = _ref16.gas, gas = _ref16$gas === void 0 ? 0 : _ref16$gas, params = _ref16.params, _ref16$showWarning = _ref16.showWarning, showWarning = _ref16$showWarning === void 0 ? true : _ref16$showWarning, vsn = _ref16.vsn; if (!params && showWarning) console.warn("Can't build transaction fee, we will use DEFAULT_FEE(".concat(DEFAULT_FEE, ")")); return fee || calculateMinFee(txType, { params: params, gas: gas, vsn: vsn }); } /** * Validate transaction params * @function * @alias module:@aeternity/aepp-sdk/es/tx/builder * @param {Object} params Object with tx params * @param {Array} schema Transaction schema * @param {Array} excludeKeys Array of keys to exclude for validation * @return {Object} Object with validation errors */ export function validateParams(params, schema, _ref17) { var _context5; var _ref17$excludeKeys = _ref17.excludeKeys, excludeKeys = _ref17$excludeKeys === void 0 ? [] : _ref17$excludeKeys; return _reduceInstanceProperty(_context5 = _filterInstanceProperty(schema).call(schema, function (_ref18) { var _ref19 = _slicedToArray(_ref18, 1), key = _ref19[0]; return !_includesInstanceProperty(excludeKeys).call(excludeKeys, key) && key !== 'payload'; })).call(_context5, function (acc, _ref20) { var _ref21 = _slicedToArray(_ref20, 3), key = _ref21[0], type = _ref21[1], prefix = _ref21[2]; return _Object$assign(acc, validateField(params[key], key, type, prefix)); }, {}); } /** * Build binary transaction * @function * @alias module:@aeternity/aepp-sdk/es/tx/builder * @param {Object} params Object with tx params * @param {Array} schema Transaction schema * @param {Object} [options={}] options * @param {Array} [options.excludeKeys=[]] excludeKeys Array of keys to exclude for validation and build * @param {String} [options.denomination='aettos'] denomination Denomination of amounts (default: aettos) * @throws {Error} Validation error * @return {Array} Array with binary fields of transaction */ export function buildRawTx(params, schema) { var _ref22 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, _ref22$excludeKeys = _ref22.excludeKeys, excludeKeys = _ref22$excludeKeys === void 0 ? [] : _ref22$excludeKeys, _ref22$denomination = _ref22.denomination, denomination = _ref22$denomination === void 0 ? AE_AMOUNT_FORMATS.AETTOS : _ref22$denomination; var filteredSchema = _filterInstanceProperty(schema).call(schema, function (_ref23) { var _ref24 = _slicedToArray(_ref23, 1), key = _ref24[0]; return !_includesInstanceProperty(excludeKeys).call(excludeKeys, key); }); // Transform `amount` type fields to `aettos` params = transformParams(params, filteredSchema, { denomination: denomination }); // Validation var valid = validateParams(params, schema, { excludeKeys: excludeKeys }); if (_Object$keys(valid).length) { throw new Error('Transaction build error. ' + _JSON$stringify(valid)); } return _mapInstanceProperty(filteredSchema).call(filteredSchema, function (_ref25) { var _ref26 = _slicedToArray(_ref25, 3), key = _ref26[0], fieldType = _ref26[1], prefix = _ref26[2]; return serializeField(params[key], fieldType, prefix); }); } /** * Unpack binary transaction * @function * @alias module:@aeternity/aepp-sdk/es/tx/builder * @param {Array} binary Array with binary transaction field's * @param {Array} schema Transaction schema * @return {Object} Object with transaction field's */ export function unpackRawTx(binary, schema) { return _reduceInstanceProperty(schema).call(schema, function (acc, _ref27, index) { var _ref28 = _slicedToArray(_ref27, 3), key = _ref28[0], fieldType = _ref28[1], prefix = _ref28[2]; return _Object$assign(acc, _defineProperty({}, key, deserializeField(binary[index], fieldType, prefix))); }, {}); } /** * Get transaction serialization/deserialization schema * @alias module:@aeternity/aepp-sdk/es/tx/builder * @param {{ vsn: String, objId: Number, type: String }} * @throws {Error} Schema not found error * @return {Object} Schema */ var getSchema = function getSchema(_ref29) { var vsn = _ref29.vsn, objId = _ref29.objId, type = _ref29.type; var isDeserialize = !!objId; var firstKey = isDeserialize ? objId : type; var schema = isDeserialize ? TX_DESERIALIZATION_SCHEMA : TX_SERIALIZATION_SCHEMA; if (!schema[firstKey]) { var _context6; throw new Error(_concatInstanceProperty(_context6 = "Transaction ".concat(isDeserialize ? 'deserialization' : 'serialization', " not implemented for ")).call(_context6, isDeserialize ? 'tag ' + objId : type)); } if (!schema[firstKey][vsn]) { var _context7, _context8; throw new Error(_concatInstanceProperty(_context7 = _concatInstanceProperty(_context8 = "Transaction ".concat(isDeserialize ? 'deserialization' : 'serialization', " not implemented for ")).call(_context8, isDeserialize ? 'tag ' + objId : type, " version ")).call(_context7, vsn)); } return schema[firstKey][vsn]; }; /** * Build transaction hash * @function * @alias module:@aeternity/aepp-sdk/es/tx/builder * @param {Object} params Object with tx params * @param {String} type Transaction type * @param {Object} [options={}] options * @param {Object} [options.excludeKeys] excludeKeys Array of keys to exclude for validation and build * @param {String} [options.prefix] Prefix of transaction * @throws {Error} Validation error * @return {Object} { tx, rlpEncoded, binary } Object with tx -> Base64Check transaction hash with 'tx_' prefix, rlp encoded transaction and binary transaction */ export function buildTx(params, type) { var _context9; var _ref30 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, _ref30$excludeKeys = _ref30.excludeKeys, excludeKeys = _ref30$excludeKeys === void 0 ? [] : _ref30$excludeKeys, _ref30$prefix = _ref30.prefix, prefix = _ref30$prefix === void 0 ? 'tx' : _ref30$prefix, _ref30$vsn = _ref30.vsn, vsn = _ref30$vsn === void 0 ? VSN : _ref30$vsn, _ref30$denomination = _ref30.denomination, denomination = _ref30$denomination === void 0 ? AE_AMOUNT_FORMATS.AETTOS : _ref30$denomination; var _getSchema = getSchema({ type: type, vsn: vsn }), _getSchema2 = _slicedToArray(_getSchema, 2), schema = _getSchema2[0], tag = _getSchema2[1]; var binary = _filterInstanceProperty(_context9 = buildRawTx(_objectSpread(_objectSpread({}, params), {}, { VSN: vsn, tag: tag }), schema, { excludeKeys: excludeKeys, denomination: params.denomination || denomination })).call(_context9, function (e) { return e !== undefined; }); var rlpEncoded = rlpEncode(binary); var tx = encode(rlpEncoded, prefix); return { tx: tx, rlpEncoded: rlpEncoded, binary: binary, txObject: unpackRawTx(binary, schema) }; } /** * Unpack transaction hash * @function * @alias module:@aeternity/aepp-sdk/es/tx/builder * @param {String|Buffer} encodedTx String or RLP encoded transaction array (if fromRlpBinary flag is true) * @param {Boolean} fromRlpBinary Unpack from RLP encoded transaction (default: false) * @param {String} prefix - Prefix of data * @return {Object} { tx, rlpEncoded, binary } Object with tx -> Object with transaction param's, rlp encoded transaction and binary transaction */ export function unpackTx(encodedTx) { var fromRlpBinary = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var prefix = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'tx'; var rlpEncoded = fromRlpBinary ? encodedTx : decode(encodedTx, prefix); var binary = rlpDecode(rlpEncoded); var objId = readInt(binary[0]); var vsn = readInt(binary[1]); var _getSchema3 = getSchema({ objId: objId, vsn: vsn }), _getSchema4 = _slicedToArray(_getSchema3, 1), schema = _getSchema4[0]; return { txType: OBJECT_ID_TX_TYPE[objId], tx: unpackRawTx(binary, schema), rlpEncoded: rlpEncoded, binary: binary }; } /** * Build a transaction hash * @function * @alias module:@aeternity/aepp-sdk/es/tx/builder * @param {String | Buffer} rawTx base64 or rlp encoded transaction * @param {Object} options * @param {Boolean} options.raw * @return {String} Transaction hash */ export function buildTxHash(rawTx, options) { if (typeof rawTx === 'string' && _indexOfInstanceProperty(rawTx).call(rawTx, 'tx_') !== -1) return buildHash('th', unpackTx(rawTx).rlpEncoded, options); return buildHash('th', rawTx, options); } export default { calculateMinFee: calculateMinFee, calculateFee: calculateFee, unpackTx: unpackTx, unpackRawTx: unpackRawTx, buildTx: buildTx, buildRawTx: buildRawTx, validateParams: validateParams, buildTxHash: buildTxHash }; //# sourceMappingURL=index.js.map