newpay-wallet-js
Version:
196 lines (166 loc) • 7.27 kB
JavaScript
import { FetchChain, PrivateKey, Aes, TransactionHelper, ChainTypes, ops } from "bitsharesjs/es";
import { Price, Asset } from "MarketClasses.js";
const { operations } = ChainTypes;
function estimateFeeAsync(type, options = null, data = {}) {
return new Promise((res, rej) => {
FetchChain("getObject", "2.0.0").then(obj => {
res(estimateFee(type, options, obj, data));
}).catch(rej);
});
}
function checkFeePoolAsync({assetID, type = "transfer", options = null, data} = {}) {
return new Promise(res => {
if (assetID === "1.3.0") {
res(true);
} else {
Promise.all([
estimateFeeAsync(type, options, data),
FetchChain("getAsset", assetID)
])
.then(result => {
const [fee, feeAsset] = result;
res(parseInt(feeAsset.getIn(["dynamic", "fee_pool"]), 10) >= fee);
});
}
});
}
function checkFeeStatusAsync({accountID, feeID = "1.3.0", type = "transfer", options = null, data} = {}) {
return new Promise((res, rej) => {
Promise.all([
estimateFeeAsync(type, options, data),
checkFeePoolAsync({assetID: feeID, type, options, data}),
FetchChain("getAccount", accountID),
FetchChain("getAsset", "1.3.0"),
feeID !== "1.3.0" ? FetchChain("getAsset", feeID) : null
])
.then(result => {
let [coreFee, hasPoolBalance, account, coreAsset, feeAsset] = result;
let hasBalance = false;
if (feeID === "1.3.0") feeAsset = coreAsset;
let coreBalanceID = account.getIn(["balances", "1.3.0"]),
feeBalanceID = account.getIn(["balances", feeID]);
if (feeID === "1.3.0" && !coreBalanceID) return res({fee: new Asset({amount: coreFee}), hasBalance, hasPoolBalance});
Promise.all([
coreBalanceID ? FetchChain("getObject", coreBalanceID) : null,
feeBalanceID ? FetchChain("getObject", feeBalanceID) : null
])
.then(balances => {
let [coreBalance, feeBalance] = balances;
let fee = new Asset({amount: coreFee});
let hasValidCER = true;
/*
** If the fee is to be paid in a non-core asset, check the fee
** pool and convert the amount using the CER
*/
if (feeID !== "1.3.0") {
// Convert the amount using the CER
let cer = feeAsset.getIn(["options", "core_exchange_rate"]);
let b = cer.get("base").toJS();
b.precision = b.asset_id === feeID ? feeAsset.get("precision") : coreAsset.get("precision");
let base = new Asset(b);
let q = cer.get("quote").toJS();
q.precision = q.asset_id === feeID ? feeAsset.get("precision") : coreAsset.get("precision");
let quote = new Asset(q);
/*
** If the CER is incorrectly configured, the multiplication
** will fail, so catch the error and default to core
*/
try {
let price = new Price({base, quote});
fee = fee.times(price, true);
} catch(err) {
feeBalance = coreBalance;
hasValidCER = false;
hasPoolBalance = false;
}
}
if (feeBalance && feeBalance.get("balance") >= fee.getAmount()) hasBalance = true;
res({fee, hasBalance, hasPoolBalance, hasValidCER});
});
}).catch(rej);
});
}
const privKey = "5KikQ23YhcM7jdfHbFBQg1G7Do5y6SgD9sdBZq7BqQWXmNH7gqo";
const nonce = TransactionHelper.unique_nonce_uint64();
let _privKey;
let _cachedMessage, _prevContent;
function estimateFee(op_type, options, globalObject, data = {}) {
if (!globalObject) return 0;
let op_code = operations[op_type];
let currentFees = globalObject.getIn(["parameters", "current_fees", "parameters", op_code, 1]).toJS();
let fee = 0;
if (currentFees.fee) {
fee += currentFees.fee;
}
if (options) {
for (let option of options) {
const optionFee = currentFees[option];
if (option === "price_per_kbyte") {
if (data.type === "memo" && !!data.content) {
/* Dummy priv key */
let pKey = _privKey || PrivateKey.fromWif(privKey);
if (_privKey) _privKey = pKey;
let memoFromKey = "BTS6B1taKXkDojuC1qECjvC7g186d8AdeGtz8wnqWAsoRGC6RY8Rp";
// Memos are optional, but if you have one you need to encrypt it
let memoToKey = "BTS8eLeqSZZtB1YHdw7KjQxRSRmaKAseCxhUSqaLxUdqvdGpp6nck";
/* Encryption is very expensive so we cache the result for reuse */
let message;
if (data.content === _prevContent && _cachedMessage) {
message = _cachedMessage;
} else {
message = _cachedMessage = Aes.encrypt_with_checksum(
pKey,
memoToKey,
nonce,
data.content
);
}
let memo_object = {
from: memoFromKey,
to: memoToKey,
nonce,
message
};
let serialized = ops.memo_data.fromObject(memo_object);
const stringified = JSON.stringify(ops.memo_data.toHex(serialized));
const byteLength = Buffer.byteLength(stringified, "hex");
fee += (optionFee * byteLength / 1024);
_prevContent = data.content;
}
} else if (optionFee) {
fee += optionFee;
}
}
}
return fee * globalObject.getIn(["parameters", "current_fees", "scale"]) / 10000;
}
function checkBalance(amount, sendAsset, feeAmount, balance) {
if (!amount) return null;
if (typeof amount === "string") amount = parseFloat(String.prototype.replace.call(amount, /,/g, ""));
if (!balance || balance.get("balance") === 0) return false;
let sendAmount = new Asset({
asset_id: sendAsset.get("id"),
precision: sendAsset.get("precision"),
real: amount
});
let balanceAmount = sendAmount.clone(balance.get("balance"));
/* Insufficient balance */
if (balanceAmount.lt(sendAmount)) {
return false;
}
/* Check if enough remains to pay the fee */
if (sendAmount.asset_id === feeAmount.asset_id) {
sendAmount.plus(feeAmount);
if (balanceAmount.lt(sendAmount)) {
return false;
}
}
return true;
}
export {
estimateFee,
estimateFeeAsync,
checkFeePoolAsync,
checkFeeStatusAsync,
checkBalance
};