mirror of
https://github.com/status-im/web3.js.git
synced 2025-02-24 03:58:13 +00:00
335 lines
9.7 KiB
JavaScript
335 lines
9.7 KiB
JavaScript
/*
|
|
This file is part of ethereum.js.
|
|
|
|
ethereum.js is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
ethereum.js 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 Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
/**
|
|
* @file coder.js
|
|
* @author Marek Kotewicz <marek@ethdev.com>
|
|
* @date 2015
|
|
*/
|
|
|
|
var BigNumber = require('bignumber.js');
|
|
var utils = require('../utils/utils');
|
|
var f = require('./formatters');
|
|
var SolidityParam = require('./param');
|
|
|
|
/**
|
|
* Should be used to check if a type is an array type
|
|
*
|
|
* @method isArrayType
|
|
* @param {String} type
|
|
* @return {Bool} true is the type is an array, otherwise false
|
|
*/
|
|
var isArrayType = function (type) {
|
|
return type.slice(-2) === '[]';
|
|
};
|
|
|
|
/**
|
|
* SolidityType prototype is used to encode/decode solidity params of certain type
|
|
*/
|
|
var SolidityType = function (config) {
|
|
this._name = config.name;
|
|
this._match = config.match;
|
|
this._mode = config.mode;
|
|
this._inputFormatter = config.inputFormatter;
|
|
this._outputFormatter = config.outputFormatter;
|
|
};
|
|
|
|
/**
|
|
* Should be used to determine if this SolidityType do match given type
|
|
*
|
|
* @method isType
|
|
* @param {String} name
|
|
* @return {Bool} true if type match this SolidityType, otherwise false
|
|
*/
|
|
SolidityType.prototype.isType = function (name) {
|
|
if (this._match === 'strict') {
|
|
return this._name === name || (name.indexOf(this._name) === 0 && name.slice(this._name.length) === '[]');
|
|
} else if (this._match === 'prefix') {
|
|
// TODO better type detection!
|
|
return name.indexOf(this._name) === 0;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Should be used to transform plain param to SolidityParam object
|
|
*
|
|
* @method formatInput
|
|
* @param {Object} param - plain object, or an array of objects
|
|
* @param {Bool} arrayType - true if a param should be encoded as an array
|
|
* @return {SolidityParam} encoded param wrapped in SolidityParam object
|
|
*/
|
|
SolidityType.prototype.formatInput = function (param, arrayType) {
|
|
if (utils.isArray(param) && arrayType) { // TODO: should fail if this two are not the same
|
|
var self = this;
|
|
return param.map(function (p) {
|
|
return self._inputFormatter(p);
|
|
}).reduce(function (acc, current) {
|
|
return acc.combine(current);
|
|
}, f.formatInputInt(param.length)).withOffset(32);
|
|
}
|
|
return this._inputFormatter(param);
|
|
};
|
|
|
|
/**
|
|
* Should be used to transoform SolidityParam to plain param
|
|
*
|
|
* @method formatOutput
|
|
* @param {SolidityParam} byteArray
|
|
* @param {Bool} arrayType - true if a param should be decoded as an array
|
|
* @return {Object} plain decoded param
|
|
*/
|
|
SolidityType.prototype.formatOutput = function (param, arrayType) {
|
|
if (arrayType) {
|
|
// let's assume, that we solidity will never return long arrays :P
|
|
var result = [];
|
|
var length = new BigNumber(param.dynamicPart().slice(0, 64), 16);
|
|
for (var i = 0; i < length * 64; i += 64) {
|
|
result.push(this._outputFormatter(new SolidityParam(param.dynamicPart().substr(i + 64, 64))));
|
|
}
|
|
return result;
|
|
}
|
|
return this._outputFormatter(param);
|
|
};
|
|
|
|
/**
|
|
* Should be used to slice single param from bytes
|
|
*
|
|
* @method sliceParam
|
|
* @param {String} bytes
|
|
* @param {Number} index of param to slice
|
|
* @param {String} type
|
|
* @returns {SolidityParam} param
|
|
*/
|
|
SolidityType.prototype.sliceParam = function (bytes, index, type) {
|
|
if (this._mode === 'bytes') {
|
|
return SolidityParam.decodeBytes(bytes, index);
|
|
} else if (isArrayType(type)) {
|
|
return SolidityParam.decodeArray(bytes, index);
|
|
}
|
|
return SolidityParam.decodeParam(bytes, index);
|
|
};
|
|
|
|
/**
|
|
* SolidityCoder prototype should be used to encode/decode solidity params of any type
|
|
*/
|
|
var SolidityCoder = function (types) {
|
|
this._types = types;
|
|
};
|
|
|
|
/**
|
|
* This method should be used to transform type to SolidityType
|
|
*
|
|
* @method _requireType
|
|
* @param {String} type
|
|
* @returns {SolidityType}
|
|
* @throws {Error} throws if no matching type is found
|
|
*/
|
|
SolidityCoder.prototype._requireType = function (type) {
|
|
var solidityType = this._types.filter(function (t) {
|
|
return t.isType(type);
|
|
})[0];
|
|
|
|
if (!solidityType) {
|
|
throw Error('invalid solidity type!: ' + type);
|
|
}
|
|
|
|
return solidityType;
|
|
};
|
|
|
|
/**
|
|
* Should be used to transform plain param of given type to SolidityParam
|
|
*
|
|
* @method _formatInput
|
|
* @param {String} type of param
|
|
* @param {Object} plain param
|
|
* @return {SolidityParam}
|
|
*/
|
|
SolidityCoder.prototype._formatInput = function (type, param) {
|
|
return this._requireType(type).formatInput(param, isArrayType(type));
|
|
};
|
|
|
|
/**
|
|
* Should be used to encode plain param
|
|
*
|
|
* @method encodeParam
|
|
* @param {String} type
|
|
* @param {Object} plain param
|
|
* @return {String} encoded plain param
|
|
*/
|
|
SolidityCoder.prototype.encodeParam = function (type, param) {
|
|
return this._formatInput(type, param).encode();
|
|
};
|
|
|
|
/**
|
|
* Should be used to encode list of params
|
|
*
|
|
* @method encodeParams
|
|
* @param {Array} types
|
|
* @param {Array} params
|
|
* @return {String} encoded list of params
|
|
*/
|
|
SolidityCoder.prototype.encodeParams = function (types, params) {
|
|
var self = this;
|
|
var solidityParams = types.map(function (type, index) {
|
|
return self._formatInput(type, params[index]);
|
|
});
|
|
|
|
return SolidityParam.encodeList(solidityParams);
|
|
};
|
|
|
|
/**
|
|
* Should be used to decode bytes to plain param
|
|
*
|
|
* @method decodeParam
|
|
* @param {String} type
|
|
* @param {String} bytes
|
|
* @return {Object} plain param
|
|
*/
|
|
SolidityCoder.prototype.decodeParam = function (type, bytes) {
|
|
return this.decodeParams([type], bytes)[0];
|
|
};
|
|
|
|
/**
|
|
* Should be used to decode list of params
|
|
*
|
|
* @method decodeParam
|
|
* @param {Array} types
|
|
* @param {String} bytes
|
|
* @return {Array} array of plain params
|
|
*/
|
|
SolidityCoder.prototype.decodeParams = function (types, bytes) {
|
|
var self = this;
|
|
return types.map(function (type, index) {
|
|
var solidityType = self._requireType(type);
|
|
var p = solidityType.sliceParam(bytes, index, type);
|
|
return solidityType.formatOutput(p, isArrayType(type));
|
|
});
|
|
};
|
|
|
|
var SolidityTypeAddress = function () {
|
|
this._name = 'address';
|
|
this._match = 'strict';
|
|
this._mode = 'value';
|
|
this._inputFormatter = f.formatInputInt;
|
|
this._outputFormatter = f.formatOutputAddress;
|
|
};
|
|
|
|
SolidityTypeAddress.prototype = new SolidityType({});
|
|
SolidityTypeAddress.prototype.constructor = SolidityTypeAddress;
|
|
|
|
var SolidityTypeBool = function () {
|
|
this._name = 'bool';
|
|
this._match = 'strict';
|
|
this._mode = 'value';
|
|
this._inputFormatter = f.formatInputBool;
|
|
this._outputFormatter = f.formatOutputBool;
|
|
};
|
|
|
|
SolidityTypeBool.prototype = new SolidityType({});
|
|
SolidityTypeBool.prototype.constructor = SolidityTypeBool;
|
|
|
|
var SolidityTypeInt = function () {
|
|
this._name = 'int';
|
|
this._match = 'prefix';
|
|
this._mode = 'value';
|
|
this._inputFormatter = f.formatInputInt;
|
|
this._outputFormatter = f.formatOutputInt;
|
|
};
|
|
|
|
SolidityTypeInt.prototype = new SolidityType({});
|
|
SolidityTypeInt.prototype.constructor = SolidityTypeInt;
|
|
|
|
var SolidityTypeUInt = function () {
|
|
this._name = 'uint';
|
|
this._match = 'prefix';
|
|
this._mode = 'value';
|
|
this._inputFormatter = f.formatInputInt;
|
|
this._outputFormatter = f.formatOutputUInt;
|
|
};
|
|
|
|
SolidityTypeUInt.prototype = new SolidityType({});
|
|
SolidityTypeUInt.prototype.constructor = SolidityTypeUInt;
|
|
|
|
var SolidityTypeDynamicBytes = function () {
|
|
this._name = 'bytes';
|
|
this._match = 'strict';
|
|
this._mode = 'bytes';
|
|
this._inputFormatter = f.formatInputDynamicBytes;
|
|
this._outputFormatter = f.formatOutputDynamicBytes;
|
|
};
|
|
|
|
SolidityTypeDynamicBytes.prototype = new SolidityType({});
|
|
SolidityTypeDynamicBytes.prototype.constructor = SolidityTypeDynamicBytes;
|
|
|
|
var SolidityTypeBytes = function () {
|
|
this._name = 'bytes';
|
|
this._match = 'prefix';
|
|
this._mode = 'value';
|
|
this._inputFormatter = f.formatInputBytes;
|
|
this._outputFormatter = f.formatOutputBytes;
|
|
};
|
|
|
|
SolidityTypeBytes.prototype = new SolidityType({});
|
|
SolidityTypeBytes.prototype.constructor = SolidityTypeBytes;
|
|
|
|
var SolidityTypeString = function () {
|
|
this._name = 'string';
|
|
this._match = 'strict';
|
|
this._mode = 'bytes';
|
|
this._inputFormatter = f.formatInputString;
|
|
this._outputFormatter = f.formatOutputString;
|
|
};
|
|
|
|
SolidityTypeString.prototype = new SolidityType({});
|
|
SolidityTypeString.prototype.constructor = SolidityTypeString;
|
|
|
|
var SolidityTypeReal = function () {
|
|
this._name = 'real';
|
|
this._match = 'prefix';
|
|
this._mode = 'value';
|
|
this._inputFormatter = f.formatInputReal;
|
|
this._outputFormatter = f.formatOutputReal;
|
|
};
|
|
|
|
SolidityTypeReal.prototype = new SolidityType({});
|
|
SolidityTypeReal.prototype.constructor = SolidityTypeReal;
|
|
|
|
var SolidityTypeUReal = function () {
|
|
this._name = 'ureal';
|
|
this._match = 'prefix';
|
|
this._mode = 'value';
|
|
this._inputFormatter = f.formatInputReal;
|
|
this._outputFormatter = f.formatOutputUReal;
|
|
};
|
|
|
|
SolidityTypeUReal.prototype = new SolidityType({});
|
|
SolidityTypeUReal.prototype.constructor = SolidityTypeUReal;
|
|
|
|
var coder = new SolidityCoder([
|
|
new SolidityTypeAddress(),
|
|
new SolidityTypeBool(),
|
|
new SolidityTypeInt(),
|
|
new SolidityTypeUInt(),
|
|
new SolidityTypeDynamicBytes(),
|
|
new SolidityTypeBytes(),
|
|
new SolidityTypeString(),
|
|
new SolidityTypeReal(),
|
|
new SolidityTypeUReal()
|
|
]);
|
|
|
|
module.exports = coder;
|
|
|