jingtum-lib
Version:
jingtum lib
846 lines (791 loc) • 31.6 kB
JavaScript
/**
* Created by Administrator on 2016/11/20.
*/
var extend = require('extend');
var baselib = require('jingtum-base-lib').Wallet;
var KeyPair = require('jingtum-base-lib').KeyPair;
var Transaction = require('./transaction');
var _ = require('lodash');
var utf8 = require('utf8');
var config = require('./config');
const currency = config.currency;
var bignumber = require('bignumber.js');
// Flags for ledger entries
var LEDGER_FLAGS = {
// Account Root
account_root: {
PasswordSpent: 0x00010000, // True, if password set fee is spent.
RequireDestTag: 0x00020000, // True, to require a DestinationTag for payments.
RequireAuth: 0x00040000, // True, to require a authorization to hold IOUs.
DisallowSWT: 0x00080000, // True, to disallow sending SWT.
DisableMaster: 0x00100000 // True, force regular key.
},
// Offer
offer: {
Passive: 0x00010000,
Sell: 0x00020000 // True, offer was placed as a sell.
},
// Skywell State
state: {
LowReserve: 0x00010000, // True, if entry counts toward reserve.
HighReserve: 0x00020000,
LowAuth: 0x00040000,
HighAuth: 0x00080000,
LowNoSkywell: 0x00100000,
HighNoSkywell: 0x00200000
}
};
function hexToString(h) {
var a = [];
var i = 0;
if (h.length % 2) {
a.push(String.fromCharCode(parseInt(h.substring(0, 1), 16)));
i = 1;
}
for (; i<h.length; i+=2) {
a.push(String.fromCharCode(parseInt(h.substring(i, i+2), 16)));
}
return a.join('');
}
function stringToHex(s) {
var result = '';
for (var i=0; i<s.length; i++) {
var b = s.charCodeAt(i);
result += b < 16 ? '0' + b.toString(16) : b.toString(16);
}
return result;
}
function string2Hex(s) {
var zero = '0000000000000000000000000000000000000000000000000000000000000000';
var result = '';
for (var i=0; i<s.length; i++) {
var b = s.charCodeAt(i);
result += b < 16 ? '0' + b.toString(16) : b.toString(16);
}
if(result.length < 64)
result += zero.substr(result.length);
return result;
}
function number2Hex(n) {
n = n.toString(16);
var zero = '0000000000000000000000000000000000000000000000000000000000000000';
return zero.substr(0, 64 - n.length) + n;
}
function hex2Number(h) {
return parseInt(h, 16);
}
/**
* check {value: '', currency:'', issuer: ''}
* @param amount
* @returns {boolean}
*/
function isValidAmount(amount) {
if (amount === null || typeof amount !== 'object') {
return false;
}
// check amount value
if ((!amount.value && amount.value !== 0)
|| Number(amount.value) === NaN) {
return false;
}
// check amount currency
if (!amount.currency || !isValidCurrency(amount.currency)) {
return false;
}
// native currency issuer is empty
if (amount.currency === currency && amount.issuer !== '') {
return false;
}
// non native currency issuer is not allowed to be empty
if (amount.currency !== currency
&& !baselib.isValidAddress(amount.issuer)) {
return false;
}
return true;
}
/**
* check {currency: '', issuer: ''}
* @param amount
* @returns {boolean}
*/
function isValidAmount0(amount) {
if (amount === null || typeof amount !== 'object') {
return false;
}
// check amount currency
if (!amount.currency || !isValidCurrency(amount.currency)) {
return false;
}
// native currency issuer is empty
if (amount.currency === currency && amount.issuer !== '') {
return false;
}
// non native currency issuer is not allowed to be empty
if (amount.currency !== currency
&& !baselib.isValidAddress(amount.issuer)) {
return false;
}
return true;
}
/**
* parse amount and return uni format data
* {value: '', currency: '', issuer: ''}
* @param amount
* @returns {*}
*/
function parseAmount(amount) {
if (!isNaN(amount)) {
var value = String(new bignumber(amount).dividedBy(1000000.0));
return {value: value, currency: currency, issuer: ''};
} else if (typeof amount === 'object' && isValidAmount(amount)) {
return amount;
} else {
return null;
}
}
var CURRENCY_RE = /^([a-zA-Z0-9]{3,6}|[A-F0-9]{40})$/;
function isValidCurrency(currency) {
if (!currency || typeof currency !== 'string'
|| currency === '') {
return false;
}
return CURRENCY_RE.test(currency);
}
var LEDGER_STATES = ['current', 'closed', 'validated'];
var HASH__RE = /^[A-F0-9]{64}$/;
/**
* hash check for tx and ledger hash
* @param hash
* @returns {boolean}
*/
function isValidHash(hash) {
if (!hash || typeof hash !== 'string'
|| hash === '') {
return false;
}
return HASH__RE.test(hash);
}
/**
* get meta node type
* @param node
* @returns {*}
*/
function getTypeNode(node) {
var NODE_TYPES = ['CreatedNode', 'ModifiedNode', 'DeletedNode'];
for (var index in NODE_TYPES) {
var type = NODE_TYPES[index];
if (node.hasOwnProperty(type)) {
return node[type];
}
}
return null;
}
function processAffectNode(an) {
var result = {};
["CreatedNode", "ModifiedNode", "DeletedNode"].forEach(function(x) {
if (an[x]) result.diffType = x;
});
if (!result.diffType) return null;
an = an[result.diffType];
result.entryType = an.LedgerEntryType;
result.ledgerIndex = an.LedgerIndex;
result.fields = _.extend({}, an.PreviousFields, an.NewFields, an.FinalFields);
result.fieldsPrev = an.PreviousFields || {};
result.fieldsNew = an.NewFields || {};
result.fieldsFinal = an.FinalFields || {};
result.PreviousTxnID = an.PreviousTxnID;
return result;
}
/**
* get effect accounts
* @param data
* @returns {Array}
*/
function affectedAccounts(tx) {
var accounts = {};
accounts[tx.transaction.Account] = 1;
if (tx.transaction.Destination) {
accounts[tx.transaction.Destination] = 1;
}
if (tx.transaction.LimitAmount) {
accounts[tx.transaction.LimitAmount.issuer] = 1;
}
var meta = tx.meta;
if (meta && meta.TransactionResult === 'tesSUCCESS') {
meta.AffectedNodes.forEach(function(n) {
var node = processAffectNode(n);
if (node.entryType === 'AccountRoot' && node.fields.Account) {
accounts[node.fields.Account] = 1;
}
if (node.entryType === 'SkywellState') {
if (node.fields.HighLimit.issuer) {
accounts[node.fields.HighLimit.issuer] = 1;
}
if (node.fields.LowLimit.issuer) {
accounts[node.fields.LowLimit.issuer] = 1;
}
}
if (node.entryType === 'Offer' && node.fields.Account) {
accounts[node.fields.Account] = 1;
}
});
}
return Object.keys(accounts);
}
/**
* get affect order book
* @param tx
* @returns {Array}
*/
function affectedBooks(tx) {
var data = tx.meta;
if (typeof data !== 'object') return [];
if (!Array.isArray(data.AffectedNodes)) return [];
var books = {};
for (var i = 0; i < data.AffectedNodes.length; ++i) {
var node = getTypeNode(data.AffectedNodes[i]);
if (!node || node.LedgerEntryType !== 'Offer') {
continue;
}
var fields = extend({}, node.PreviousFields, node.NewFields, node.FinalFields);
var gets = parseAmount(fields.TakerGets);
var pays = parseAmount(fields.TakerPays);
var getsKey = gets.currency === currency ? gets.currency : gets.currency + '/' + gets.issuer;
var paysKey = pays.currency === currency ? pays.currency : pays.currency + '/' + pays.issuer;
var key = getsKey + ':' + paysKey;
if (tx.transaction.Flags & LEDGER_FLAGS.offer.Sell) {
// sell
key = paysKey + ':' + getsKey;
} else {
// buy
key = getsKey + ':' + paysKey;
}
books[key] = 1;
}
return Object.keys(books);
}
/**
* parse tx type to specific transaction type
* @param tx
* @param account
* @returns {string}
*/
function txnType(tx, account) {
if (tx.Account === account || tx.Target === account || (tx.Destination && tx.Destination === account)
|| (tx.LimitAmount && tx.LimitAmount.issuer === account) || tx.BlackListAccountID === account || tx.Issuer === account) {
switch(tx.TransactionType) {
case 'Payment': //支付类
return tx.Account === account ?
tx.Destination === account ? 'convert' : 'sent' : 'received';
case 'OfferCreate': //创建挂单类
return 'offernew';
case 'OfferCancel'://取消挂单类
return 'offercancel';
case 'TrustSet': //设置信任线
return tx.Account === account ? 'trusting' : 'trusted';
case 'RelationDel':
case 'AccountSet':
case 'SetRegularKey':
case 'RelationSet':
case 'SignSet':
case 'Operation':
case 'ConfigContract': //lua版本合约类
case 'AlethContract': //solidity版本合约类
case 'Brokerage': //设置手续费类
case 'SignerListSet': //签名列表类
case 'SetBlackList': //黑名单
case 'RemoveBlackList': //解除黑名单
case 'TokenIssue': //原生721设置发行权限
case 'TransferToken': //原生721token流转
case 'TokenDel': //原生721token删除
return tx.TransactionType.toLowerCase();
default :
// TODO CHECK
return 'unknown';
}
} else {
return 'offereffect';
}
}
/**
* get counterparty amount
* @param amount
* @param account
* @returns {{value: string, currency: *, issuer: *}}
*/
function reverseAmount(amount, account) {
return {
value: String(-Number(amount.value)),
currency: amount.currency,
issuer: account
};
}
function isAmountZero(amount) {
if (!amount) return false;
return Number(amount.value) < 1e-12;
}
function AmountNegate(amount) {
if (!amount) return amount;
return {
value: String(- new bignumber(amount.value)),
currency: amount.currency,
issuer: amount.issuer
};
}
function AmountAdd(amount1, amount2) {
if (!amount1) return amount2;
if (!amount2) return amount1;
if (amount1 && amount2) {
return {
value: String(new bignumber(amount1.value).plus(amount2.value)),
currency: amount1.currency,
issuer: amount1.issuer
};
}
return null;
}
function AmountSubtract(amount1, amount2) {
return AmountAdd(amount1, AmountNegate(amount2));
}
function AmountRatio(amount1, amount2) {
return String(new bignumber(amount1.value).dividedBy(amount2.value));
}
function getPrice(effect, funded) {
var g = effect.got ? effect.got : effect.pays;
var p = effect.paid ? effect.paid : effect.gets;
if(!funded){
return AmountRatio(g, p);
} else {
return AmountRatio(p, g);
}
}
function formatArgs(args) {
var newArgs = [];
if(args)
for(var i = 0; i < args.length; i++){
newArgs.push(hexToString(args[i].Arg.Parameter));
}
return newArgs;
}
/**
* process transaction in view of account
* get basic transaction information,
* and transaction effects
*
* @param txn
* @param account
*/
function processTx(txn, account) {
var tx = txn.tx || txn.transaction || txn, meta = txn.meta;
// basic information
var result = {};
result.date = (tx.date || tx.Timestamp) + 0x386D4380; // unix time
result.hash = tx.hash;
result.type = txnType(tx, account);
result.fee = String(Number(tx.Fee) / 1000000.0);
// if(tx.TransactionType !== 'RelationSet')
result.result = meta ? meta.TransactionResult : 'failed';
result.memos = [];
result.ledger_index = tx.ledger_index || txn.ledger_index;
switch(result.type) {
case 'sent':
result.counterparty = tx.Destination;
result.amount = parseAmount(tx.Amount);
break;
case 'received':
result.counterparty = tx.Account;
result.amount = parseAmount(tx.Amount);
break;
case 'trusted':
result.counterparty = tx.Account;
result.amount = reverseAmount(tx.LimitAmount, tx.Account);
break;
case 'trusting':
result.counterparty = tx.LimitAmount.issuer;
result.amount = tx.LimitAmount;
break;
case 'convert':
result.spent = parseAmount(tx.SendMax);
result.amount = parseAmount(tx.Amount);
break;
case 'offernew':
result.offertype = tx.Flags & Transaction.flags.OfferCreate.Sell ? 'sell' : 'buy';
result.gets = parseAmount(tx.TakerGets);
result.pays = parseAmount(tx.TakerPays);
result.seq = tx.Sequence;
result.price = result.offertype === 'sell' ? new bignumber(result.pays.value).div(result.gets.value).toNumber() : new bignumber(result.gets.value).div(result.pays.value).toNumber();
break;
case 'offercancel':
result.offerseq = tx.Sequence;
break;
case 'relationset':
result.counterparty = account === tx.Target ? tx.Account : tx.Target;
result.relationtype = tx.RelationType === 3 ? 'freeze':'authorize';
result.isactive = account === tx.Target ? false : true;
result.amount = parseAmount(tx.LimitAmount);
break;
case 'relationdel':
result.counterparty = account === tx.Target ? tx.Account : tx.Target;
result.relationtype = tx.RelationType === 3 ? 'unfreeze':'unknown';
result.isactive = account === tx.Target ? false : true;
result.amount = parseAmount(tx.LimitAmount);
break;
case 'configcontract':
result.params = formatArgs(tx.Args);
if(tx.Method === 0){
result.method = 'deploy';
result.payload = tx.Payload;
}else if(tx.Method === 1){
result.method = 'call';
result.destination = tx.Destination;
}
break;
case 'alethcontract':
if(tx.Method === 0){
result.method = 'deploy';
result.seq = tx.Sequence;
result.payload = tx.Payload;
}else if(tx.Method === 1){
result.method = 'call';
result.seq = tx.Sequence;
result.destination = tx.Destination;
result.amount = Number(tx.Amount);
var method = hexToString(tx.MethodSignature);
result.func = method.substring(0, method.indexOf('('));//函数名
result.func_parms = method.substring(method.indexOf('(') + 1, method.indexOf(')')).split(','); //函数参数
if(result.func_parms.length === 1 && result.func_parms[0] === '')//没有参数,返回空数组
result.func_parms = [];
}
break;
case 'brokerage':
result.feeAccount = tx.FeeAccountID;
result.mol = parseInt(tx.OfferFeeRateNum, 16);
result.den = parseInt(tx.OfferFeeRateDen,16);
result.amount = parseAmount(tx.Amount);
result.seq = tx.Sequence;
break;
case 'signerlistset':
var l = [];
if(tx.SignerEntries){
tx.SignerEntries.forEach(function (s) {
l.push({account: s.SignerEntry.Account, weight: s.SignerEntry.SignerWeight});
});
}
result.threshold = tx.SignerQuorum;
result.lists = l;
result.seq = tx.Sequence;
break;
case 'setblacklist':
case 'removeblacklist':
result.code = tx.FundCode;
result.black = tx.BlackListAccountID;
break;
case 'tokenissue':
result.account = tx.Account;
result.publisher = tx.Issuer;
result.token = utf8.decode(hexToString(tx.FundCode));
result.number = parseInt(tx.TokenSize, 16);
result.flag = tx.Flags;
break;
case 'transfertoken':
result.publisher = tx.Account;//[tokenissue: tx.Issuer]
result.receiver = tx.Destination;
if(tx.FundCode) result.token = utf8.decode(hexToString(tx.FundCode));
result.tokenId = tx.TokenID;
if(tx.TokenInfos){
var infos = [];
tx.TokenInfos.forEach(function (s) {
infos.push({type: utf8.decode(hexToString(s.TokenInfo.InfoType)), data: utf8.decode(hexToString(s.TokenInfo.InfoData))});
});
result.infos = infos;
}
break;
case 'tokendel':
result.publisher = tx.Account;
result.tokenId = tx.TokenID;
break;
default :
// TODO parse other type
break;
}
if(tx.Signers){//添加签名列表
result.signers = [];
tx.Signers.forEach(function (s) {
result.signers.push( s.Signer.Account);
});
}
// add memo
if (Array.isArray(tx.Memos) && tx.Memos.length > 0) {
for (var m = 0; m < tx.Memos.length; ++m) {
var memo = tx.Memos[m].Memo;
for (var property in memo) {
try {
memo[property] = utf8.decode(hexToString(memo[property]));
} catch (e) {
// TODO to unify to utf8
memo[property] = memo[property];
}
}
result.memos.push(memo);
}
}
result.effects = [];
// no effect, return now
if (!meta || meta.TransactionResult !== 'tesSUCCESS') {
return result;
}
var cos = [];//cos.length求出几类货币撮合
var getsValue = 0;//实际对方获得的
var paysValue = 0;//实际对方支付的
var totalRate = 0;//一共收取的挂单手续费
if(result.gets){
cos.push(result.gets.currency);
cos.push(result.pays.currency);
}
result.balances = {}; //存放交易后余额
result.balancesPrev = {}; //存放交易前余额
// process effects
meta.AffectedNodes.forEach(function(n) {
var node = processAffectNode(n);
var effect = {};
/**
* TODO now only get offer related effects, need to process other entry type
*/
if (node && node.entryType === 'Offer') {
// for new and cancelled offers
var fieldSet = node.fields;
var sell = node.fields.Flags & LEDGER_FLAGS.offer.Sell;
// current account offer
if (node.fields.Account === account) {
// 1. offer_partially_funded
if (node.diffType === 'ModifiedNode' || (node.diffType === 'DeletedNode' && node.fieldsPrev.TakerGets && !isAmountZero(parseAmount(node.fieldsFinal.TakerGets)))) {
effect.effect = 'offer_partially_funded';
effect.counterparty = {account: tx.Account, seq: tx.Sequence, hash: tx.hash};
if (node.diffType !== 'DeletedNode') {
// TODO no need partially funded must remains offers
effect.remaining = !isAmountZero(parseAmount(node.fields.TakerGets));
} else {
effect.cancelled = true;
}
effect.gets = parseAmount(fieldSet.TakerGets);
effect.pays = parseAmount(fieldSet.TakerPays);
effect.got = AmountSubtract(parseAmount(node.fieldsPrev.TakerPays), parseAmount(node.fields.TakerPays));
effect.paid = AmountSubtract(parseAmount(node.fieldsPrev.TakerGets), parseAmount(node.fields.TakerGets));
effect.type = sell ? 'sold' : 'bought';
if(node.fields.OfferFeeRateNum){
effect.platform = node.fields.Platform;
effect.rate = new bignumber(parseInt(node.fields.OfferFeeRateNum, 16)).div(parseInt(node.fields.OfferFeeRateDen, 16)).toNumber();
}
} else {
// offer_funded, offer_created or offer_cancelled offer effect
effect.effect = node.diffType === 'CreatedNode' ? 'offer_created' : node.fieldsPrev.TakerPays ? 'offer_funded' : 'offer_cancelled';
// 2. offer_funded
if (effect.effect === 'offer_funded') {
fieldSet = node.fieldsPrev;
effect.counterparty = {account: tx.Account, seq: tx.Sequence, hash: tx.hash};
effect.got = AmountSubtract(parseAmount(node.fieldsPrev.TakerPays), parseAmount(node.fields.TakerPays));
effect.paid = AmountSubtract(parseAmount(node.fieldsPrev.TakerGets), parseAmount(node.fields.TakerGets));
effect.type = sell ? 'sold' : 'bought';
if(node.fields.OfferFeeRateNum){
effect.platform = node.fields.Platform;
effect.rate = new bignumber(parseInt(node.fields.OfferFeeRateNum, 16)).div(parseInt(node.fields.OfferFeeRateDen, 16)).toNumber();
}
}
// 3. offer_created
if (effect.effect === 'offer_created') {
effect.gets = parseAmount(fieldSet.TakerGets);
effect.pays = parseAmount(fieldSet.TakerPays);
effect.type = sell ? 'sell' : 'buy';
if(fieldSet.OfferFeeRateNum){
effect.platform = fieldSet.Platform;
effect.rate = new bignumber(parseInt(fieldSet.OfferFeeRateNum, 16)).div(parseInt(fieldSet.OfferFeeRateDen, 16)).toNumber();
}
}
// 4. offer_cancelled
if (effect.effect === 'offer_cancelled') {
effect.hash = node.fields.PreviousTxnID;
// collect data for cancel transaction type
if (result.type === 'offercancel') {
result.gets = parseAmount(fieldSet.TakerGets);
result.pays = parseAmount(fieldSet.TakerPays);
}
effect.gets = parseAmount(fieldSet.TakerGets);
effect.pays = parseAmount(fieldSet.TakerPays);
effect.type = sell ? 'sell' : 'buy';
if(fieldSet.OfferFeeRateNum){
effect.platform = fieldSet.Platform;
effect.rate = new bignumber(parseInt(fieldSet.OfferFeeRateNum, 16)).div(parseInt(fieldSet.OfferFeeRateDen, 16)).toNumber();
}
}
}
effect.seq = node.fields.Sequence;
}
// 5. offer_bought
else if (tx.Account === account && !_.isEmpty(node.fieldsPrev)) {
effect.effect = 'offer_bought';
effect.counterparty = { account: node.fields.Account, seq: node.fields.Sequence, hash: node.PreviousTxnID || node.fields.PreviousTxnID };
effect.paid = AmountSubtract(parseAmount(node.fieldsPrev.TakerPays ? node.fieldsPrev.TakerPays : node.fields.TakerPays), parseAmount(node.fields.TakerPays));
effect.got = AmountSubtract(parseAmount(node.fieldsPrev.TakerGets ? node.fieldsPrev.TakerGets : node.fields.TakerGets), parseAmount(node.fields.TakerGets));
if(result.offertype === 'buy' && sell || result.offertype === 'sell' && !sell){
effect.type = sell ? 'bought' : 'sold';
}else {
effect.type = sell ? 'sold' : 'bought';
}
}
// add price
if ((effect.gets && effect.pays) || (effect.got && effect.paid)) {
var created = effect.effect === 'offer_created' && effect.type === 'buy';
var funded = effect.effect === 'offer_funded' && effect.type === 'bought';
var cancelled = effect.effect === 'offer_cancelled' && effect.type === 'buy';
var bought = effect.effect === 'offer_bought' && effect.type === 'bought';
var partially_funded = effect.effect === 'offer_partially_funded' && effect.type === 'bought';
effect.price = getPrice(effect, (created || funded || cancelled || bought || partially_funded ));
}
}
if(result.type === 'offereffect' && node && node.entryType === 'AccountRoot'){
if(node.fields.RegularKey === account){
effect.effect = 'set_regular_key';
effect.type = 'null';
effect.account = node.fields.Account;
effect.regularkey = account;
}
}
if(node && node.entryType === 'Brokerage'){
result.platform = node.fields.Platform;
result.rate = new bignumber(parseInt(node.fields.OfferFeeRateNum, 16)).div(parseInt(node.fields.OfferFeeRateDen, 16)).toNumber();
}
if(node && node.entryType === 'ETHState'){//合约调用日志
if(node.fields && node.fields.info)
result.eventLog = hexToString(node.fields.info);
}
if(node && node.entryType === 'SkywellState'){//其他币种余额
if(node.fields.HighLimit.issuer === account || node .fields.LowLimit.issuer === account){
result.balances[node.fields.Balance.currency] = Math.abs(node.fields.Balance.value);
if(node.fieldsPrev.Balance){
result.balancesPrev[node.fieldsPrev.Balance.currency] = Math.abs(node.fieldsPrev.Balance.value);
}else if(node.fieldsNew.Balance){//新增币种
result.balancesPrev[node.fields.Balance.currency] = 0;
}else {
delete result.balances[node.fields.Balance.currency];
}
}
}
if(node && node.entryType === 'AccountRoot'){//基础币种余额
if(node.fields.Account === account){
result.balances[config.currency] = node.fields.Balance / 1000000;
if(node.fieldsPrev.Balance){
result.balancesPrev[config.currency] = Math.abs(node.fieldsPrev.Balance / 1000000);
}else if(node.fieldsNew.Balance){
result.balancesPrev[config.currency] = 0;
}else {//交易前后余额没有变化
delete result.balances[config.currency];
}
}
}
// add effect
if (!_.isEmpty(effect)) {
if (node.diffType === 'DeletedNode' && effect.effect !== 'offer_bought') {
effect.deleted = true;
}
result.effects.push(effect);
}
if(result.type === 'offernew' && effect.got){
if(result.gets.currency === effect.paid.currency){
getsValue = new bignumber(effect.paid.value).plus(getsValue).toNumber();
}
if(result.pays.currency === effect.got.currency){
paysValue = new bignumber(effect.got.value).plus(paysValue).toNumber();
}
if(result.gets.currency !== effect.paid.currency || result.pays.currency !== effect.got.currency) {
if(cos.indexOf(effect.got.currency) === -1)
cos.push(effect.got.currency);
if(cos.indexOf(effect.paid.currency) === -1)
cos.push(effect.paid.currency);
}
}
});
/**
* TODO check cross gateway when parse more effect, specially trust related effects, now ignore it
*
*/
for(var i = 0; i < result.effects.length; i++){
var e = result.effects[i];
if(result.rate && e.effect === 'offer_bought'){
if(e.got.currency === result.pays.currency)//涉及多路径
totalRate = new bignumber(e.got.value).multipliedBy(result.rate).plus(totalRate).toNumber();
e.rate = result.rate;
e.platform = result.platform;
e.got.value = new bignumber(e.got.value).multipliedBy(1 - e.rate).toString();
e.fee = new bignumber(e.got.value).div(1 - e.rate).multipliedBy(e.rate).toString();
}
if(e.rate && (e.effect === 'offer_funded' || e.effect === 'offer_partially_funded')){//不涉及多路径
totalRate = new bignumber(e.got.value).multipliedBy(e.rate).plus(totalRate).toNumber() ;
e.got.value = new bignumber(e.got.value).multipliedBy(1 - e.rate).toString();
e.fee = new bignumber(e.got.value).div(1 - e.rate).multipliedBy(e.rate).toString();
}
}
delete result.rate;
delete result.platform;
if(getsValue){
result.dealGets = {
value: getsValue + '',
currency: result.gets.currency,
issuer: result.gets.issuer || ''
};
result.dealPays = {
value: paysValue + '',
currency: result.pays.currency,
issuer: result.pays.issuer || ''
};
result.totalRate = {
value: totalRate + '',
currency: result.pays.currency,
issuer: result.pays.issuer || ''
};
result.dealPrice = result.offertype === 'sell' ? new bignumber(paysValue).div(getsValue).toString() : new bignumber(getsValue).div(paysValue).toString();
result.dealNum = cos.length;
}
getsValue = null;
paysValue = null;
cos = null;
totalRate = null;
return result;
}
function arraySet(count, value) {
var a = new Array(count);
for (var i=0; i<count; i++) {
a[i] = value;
}
return a;
}
function eth2Jingtum(ethadr) {
var buf = new Buffer(20);
var b = ethadr.replace(/0x/i, '');
b = b.length > 40 ? b.slice(24) : b;
buf.write(b, 0, 'hex');
return KeyPair.__encode(buf);
}
var ACCOUNT_ZERO = config.ACCOUNT_ZERO;
var ACCOUNT_ONE = config.ACCOUNT_ONE;
module.exports = {
hexToString: hexToString,
stringToHex: stringToHex,
string2Hex: string2Hex,
number2Hex: number2Hex,
hex2Number: hex2Number,
isValidAmount: isValidAmount,
isValidAmount0: isValidAmount0,
parseAmount: parseAmount,
isValidCurrency: isValidCurrency,
isValidHash: isValidHash,
isValidAddress: baselib.isValidAddress,
isValidSecret: baselib.isValidSecret,
affectedAccounts: affectedAccounts,
affectedBooks: affectedBooks,
processTx: processTx,
LEDGER_STATES: LEDGER_STATES,
ACCOUNT_ZERO: ACCOUNT_ZERO,
ACCOUNT_ONE: ACCOUNT_ONE,
arraySet:arraySet,
eth2Jingtum: eth2Jingtum
};