token-unit
Version:
Convert between token units and ether units
323 lines (272 loc) • 11 kB
JavaScript
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* TokenUnit v0.2.0
* Convert between token units and ether units
* (c) 2017 Airswap
* airswap.io
*
* Written by Adam Link
*/
var fs = require('fs');
var unitDirectory = './units';
var _ = require('lodash');
var big = require('big.js');
var tokens = [];
if (typeof fs.readdirSync === "function") {
// We are running on NodeJS
tokens = _.values(require('require-all')({
dirname: __dirname + '/' + unitDirectory
}));
} else {
// We may be on React
tokens = [require(unitDirectory + '/ether.json'), require(unitDirectory + '/ast.json')];
}
/**
* TokenUnit(amount, unit)
* @param {number} amount The number amount of tokens in the unit specified
* @param {string} unit The unit name for the amount specified
* @constructor
*/
var TokenUnit = function () {
function TokenUnit(amount, unit) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
_classCallCheck(this, TokenUnit);
big.DP = 32; // We can handle 32 decimal places by default
if (options.big && options.big.DP) {
big.DP = options.big.DP;
}
if (!big(amount) || !unit) {
throw new Error("Please provide an amount and unit during construction");
}
// The nominal unit's data
this.scaleAndNominalUnitData = this.getScaleAndNominalUnit(unit);
// The scaled unit data
this.scaledUnit = this.scaleAndNominalUnitData.name;
this.scaledAmount = big(amount);
// The common anchor unit for this nominal measurement metric
this.anchorUnit = this.scaleAndNominalUnitData.anchor;
this.anchorAmount = big(this.convertToAnchor(this.scaledAmount, this.scaleAndNominalUnitData));
// Fill in the ether equivalent for this anchor amount
this.etherEquivalent = big(this.findEtherEquivalent(this.anchorAmount, this.scaleAndNominalUnitData));
// Include quote times here if applicable
if (options.quoteFromTime && options.quoteToTime) {
this.quoteFromTime = options.quoteFromTime;
this.quoteToTime = options.quoteToTime;
}
}
/**
* getScaleAndNominalUnit(unit)
* Searches the unit JSON files for the unit string specified
* and returns information about that nominal unit and the
* scaled unit
*
* @param {string} unit The unit name (nominal or scale)
* @return {object} The unit information found in the JSON files
*/
_createClass(TokenUnit, [{
key: 'getScaleAndNominalUnit',
value: function getScaleAndNominalUnit(unit) {
if (!unit) {
throw new Error("Please provide a unit");
}
//
var nominalUnitFile = _.find(tokens, { values: [{ name: unit }] });
var scaleData = _.find(nominalUnitFile.values, { name: unit });
if (!nominalUnitFile || !scaleData) {
throw new Error("Unsupported unit provided");
}
return {
name: scaleData.name,
to_anchor: big(scaleData.to_anchor),
anchor: nominalUnitFile.anchor,
starting_anchor_to_ether: big(nominalUnitFile.starting_anchor_to_ether),
unit: nominalUnitFile.unit
};
}
/**
* convertToAnchor(scaleAmount, nominalUnitData)
* Given a scaled amount and scale unit data, scale the amount to the
* anchor unit for the nominal unit provided
*
* @param {number} scaleAmount The quantity of unit in the scaled unit
* @param {object} scaleUnitData An object with the to_anchor scaling factor
* to convert the scaled unit to the nominal unit
* @return {number} The amount of nominal units
*/
}, {
key: 'convertToAnchor',
value: function convertToAnchor(scaleAmount, scaleUnitData) {
return big(scaleAmount).times(big(scaleUnitData.to_anchor));
}
/**
* findEtherEquivalent(nominalAmount, nominalUnitData)
* Given the nominal amount and nominal unit data, find what this is worth
* in ether.
*
* @param {number} nominalAmount The quantity of nominal units
* @param {object} nominalUnitData An object that can be passed to the
* anchorToEther() function
* @return {number} The amount of ether
*/
}, {
key: 'findEtherEquivalent',
value: function findEtherEquivalent(nominalAmount, nominalUnitData) {
// find the Ether Equivalent of the currency right now for quick display
return big(nominalAmount).times(this.anchorToEther(nominalUnitData).price); // until we add Oracle
}
/**
* anchorToEther(nominalUnit)
* Convert the nominal units to ether, based on either (currently only)
* the starting, JSON-pegged amount of nominal units to ether or
* (eventually) the Oracle-provided price provided by the passed in Oracle class
*
* @param {object} nominalUnit The object nominal unit information, likely from
* getScaleAndNominalUnit()
* @return {object} An object with the conversion price and timestamp of the
* quote provided
*/
}, {
key: 'anchorToEther',
value: function anchorToEther(nominalUnit) {
// TODO
// This is the function that should call the current market
// price via Oracle for the TokenUnit / ETH pair.
// It should return the value of 1 Nominal Unit in terms of ETH's ether
// To discuss: do we want to peg this to ether or wei?
return {
price: big(nominalUnit.starting_anchor_to_ether).toString(),
time: Date.now()
};
}
/**
* to(unit)
* The workhorse of the library that converts from one scaled unit to
* another scaled unit, which could be across nominal units as well. We
* refer to scaled to scaled, within the same nominal unit, conversions
* as a "scaling conversion" while nominal unit conversions are called
* "currency conversions" since they change the token base.
*
* @param {string} unit The new scaled unit we are converting into
* @return {TokenUnit} The new TokenUnit provided by the operation
*/
}, {
key: 'to',
value: function to(unit) {
var toUnitData = this.getScaleAndNominalUnit(unit);
if (this.scaleAndNominalUnitData.unit == toUnitData.unit) {
// Just a scaling conversion
return new TokenUnit(this.scaleFromNominal(this.anchorAmount, toUnitData.to_anchor), toUnitData.name);
} else {
// Currency conversion with a new nominal unit
var marketQuoteFrom = this.anchorToEther(this.scaleAndNominalUnitData);
var fromAmountToEther = big(this.anchorAmount).times(marketQuoteFrom.price);
var marketQuoteTo = this.anchorToEther(toUnitData);
var fromEtherToTokenAnchor = big(fromAmountToEther).div(marketQuoteTo.price);
return new TokenUnit(this.scaleFromNominal(fromEtherToTokenAnchor, toUnitData.to_anchor), toUnitData.name, {
quoteFromTime: marketQuoteFrom.time,
quoteToTime: marketQuoteTo.time
});
}
}
/**
* scaleFromNominal(nominalAmount, to_anchor)
* Given a nominal quantity and a scaling factor, scale the nominal units
* to the new scaled amount according to the to_anchor factor
*
* @param {number} nominalAmount The quantity of nominal units
* @param {number} to_anchor The conversion factor of nominal to the new
* scaled unit amount
* @return {number} The new quantity in scaled units
*/
}, {
key: 'scaleFromNominal',
value: function scaleFromNominal(nominalAmount, to_anchor) {
return big(nominalAmount).div(big(to_anchor)).toString();
}
/**
* add(tokenUnit)
* Add a TokenUnit to the base TokenUnit and return a new instance of TokenUnit
*
* @param {TokenUnit} tokenUnit The TokenUnit to add to the base
* @return {TokenUnit} A new instance of TokenUnit with the result of the add
*/
}, {
key: 'add',
value: function add(tokenUnit) {
if (!tokenUnit instanceof TokenUnit) {
throw new Error("Argument passed in must be a TokenUnit type");
}
if (this.anchorUnit != tokenUnit.anchorUnit) {
tokenUnit = tokenUnit.to(this.anchorUnit);
}
return new TokenUnit(this.scaleFromNominal(this.anchorAmount.add(tokenUnit.anchorAmount), this.scaleAndNominalUnitData.to_anchor), this.scaledUnit);
}
/**
* sub(tokenUnit)
* A facade method for subtract()
*
* @param {TokenUnit} tokenUnit The TokenUnit to subtract from the base
* @return {TokenUnit} A new instance of TokenUnit with the result of the
* subtract
*/
}, {
key: 'sub',
value: function sub(tokenUnit) {
return this.subtract(tokenUnit);
}
/**
* subtract(tokenUnit)
* Subtract a TokenUnit from the base TokenUnit and return a new instance of
* TokenUnit
*
* @param {TokenUnit} tokenUnit The TokenUnit to subtract from the base
* @return {TokenUnit} A new instance of TokenUnit with the result of the
* subtract
*/
}, {
key: 'subtract',
value: function subtract(tokenUnit) {
if (!tokenUnit instanceof TokenUnit) {
throw new Error("Argument passed in must be a TokenUnit type");
}
if (this.anchorUnit != tokenUnit.anchorUnit) {
tokenUnit = tokenUnit.to(this.anchorUnit);
}
return new TokenUnit(this.scaleFromNominal(this.anchorAmount.sub(tokenUnit.anchorAmount), this.scaleAndNominalUnitData.to_anchor), this.scaledUnit);
}
/**
* toString()
* Useful for console logging just the important parts
*
* @return {object} It actually returns an object though
*/
}, {
key: 'toString',
value: function toString() {
return {
scaledUnit: this.scaleAndNominalUnitData.name,
scaledAmount: this.scaledAmount.toString(),
anchorUnit: this.scaleAndNominalUnitData.anchor,
anchorAmount: this.anchorAmount.toString(),
etherEquivalent: this.etherEquivalent.toString()
};
}
/**
* get(key)
* A simple getter function to make accessing parts of the TokenUnit easier
*
* @param {string} key The key to get from the TokenUnit
* @return {string|number} The value of the key
*/
}, {
key: 'get',
value: function get(key) {
return this.toString()[key];
}
}]);
return TokenUnit;
}();
module.exports = TokenUnit;
//# sourceMappingURL=index.js.map