@ledgerhq/coin-near
Version:
143 lines • 6.03 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getStakingFees = exports.getYoctoThreshold = exports.canWithdraw = exports.canUnstake = exports.canStake = exports.mapStakingPositions = exports.getTotalSpent = exports.getMaxAmount = exports.getStakingGas = exports.isImplicitAccount = exports.isValidAddress = void 0;
const bignumber_js_1 = require("bignumber.js");
const near_api_js_1 = require("near-api-js");
const index_1 = require("@ledgerhq/coin-framework/currencies/index");
const createTransaction_1 = require("./createTransaction");
const jsHelpers_1 = require("@ledgerhq/coin-framework/bridge/jsHelpers");
const preload_1 = require("./preload");
const constants_1 = require("./constants");
const isValidAddress = (address) => {
const readableAddressRegex = /^(([a-z\d]+[-_])*[a-z\d]+\.)*([a-z\d]+[-_])*[a-z\d]+$/;
const hexAddressRegex = /^[a-f0-9]{64}$/;
if ((0, exports.isImplicitAccount)(address)) {
return hexAddressRegex.test(address);
}
return readableAddressRegex.test(address);
};
exports.isValidAddress = isValidAddress;
const isImplicitAccount = (address) => {
return !address.includes(".");
};
exports.isImplicitAccount = isImplicitAccount;
const getStakingGas = (t, multiplier = 5) => {
const stakingGasBase = new bignumber_js_1.BigNumber(constants_1.STAKING_GAS_BASE);
if (t?.mode === "withdraw" && t?.useAllAmount) {
multiplier = 7;
}
return stakingGasBase.multipliedBy(multiplier);
};
exports.getStakingGas = getStakingGas;
/*
* Get the max amount that can be spent, taking into account tx type and pending operations.
*/
const getMaxAmount = (account, transaction, fees) => {
let maxAmount;
const selectedValidator = account.nearResources?.stakingPositions.find(({ validatorId }) => validatorId === transaction.recipient);
let pendingUnstakingAmount = new bignumber_js_1.BigNumber(0);
let pendingWithdrawingAmount = new bignumber_js_1.BigNumber(0);
account.pendingOperations.forEach(({ type, value, recipients }) => {
const recipient = recipients[0];
if (recipient === selectedValidator?.validatorId) {
if (type === "UNSTAKE") {
pendingUnstakingAmount = pendingUnstakingAmount.plus(value);
}
else if (type === "WITHDRAW_UNSTAKED") {
pendingWithdrawingAmount = pendingWithdrawingAmount.plus(value);
}
}
});
switch (transaction.mode) {
case "unstake":
maxAmount = selectedValidator?.staked.minus(pendingUnstakingAmount);
break;
case "withdraw":
maxAmount = selectedValidator?.available.minus(pendingWithdrawingAmount);
break;
default:
maxAmount = account.spendableBalance;
if (fees) {
maxAmount = maxAmount.minus(fees);
}
}
if (maxAmount === undefined || maxAmount.lt(0)) {
return new bignumber_js_1.BigNumber(0);
}
return maxAmount;
};
exports.getMaxAmount = getMaxAmount;
const getTotalSpent = (a, t, fees) => {
if (["unstake", "withdraw"].includes(t.mode)) {
return fees;
}
if (t.useAllAmount) {
return a.spendableBalance;
}
return new bignumber_js_1.BigNumber(t.amount).plus(fees);
};
exports.getTotalSpent = getTotalSpent;
const mapStakingPositions = (stakingPositions, validators, unit) => {
return stakingPositions.map(sp => {
const rank = validators.findIndex(v => v.validatorAddress === sp.validatorId);
const validator = validators[rank] ?? sp;
const formatConfig = {
disableRounding: false,
alwaysShowSign: false,
showCode: true,
};
return {
...sp,
formattedAmount: (0, index_1.formatCurrencyUnit)(unit, sp.staked, formatConfig),
formattedPending: (0, index_1.formatCurrencyUnit)(unit, sp.pending, formatConfig),
formattedAvailable: (0, index_1.formatCurrencyUnit)(unit, sp.available, formatConfig),
rank,
validator,
};
});
};
exports.mapStakingPositions = mapStakingPositions;
/*
* Make sure that an account has enough funds to stake, unstake, AND withdraw before staking.
*/
const canStake = (account) => {
let transaction = (0, createTransaction_1.createTransaction)(account);
transaction = (0, jsHelpers_1.updateTransaction)(transaction, {
mode: "stake",
});
const { gasPrice } = (0, preload_1.getCurrentNearPreloadData)();
const fees = (0, exports.getStakingFees)(transaction, gasPrice).multipliedBy(3);
return (0, exports.getMaxAmount)(account, transaction, fees).gt(0);
};
exports.canStake = canStake;
const canUnstake = (stakingPosition) => {
return stakingPosition.staked.gte((0, exports.getYoctoThreshold)());
};
exports.canUnstake = canUnstake;
const canWithdraw = (stakingPosition) => {
return stakingPosition.available.gte((0, exports.getYoctoThreshold)());
};
exports.canWithdraw = canWithdraw;
/*
* The threshold that the NEAR wallet uses for staking, unstaking, and withdrawing.
* A "1" is subtracted due to the value from the node being 1 yoctoNEAR less than what was staked.
*/
const getYoctoThreshold = () => {
return new bignumber_js_1.BigNumber(10)
.pow(new bignumber_js_1.BigNumber(near_api_js_1.utils.format.NEAR_NOMINATION_EXP - constants_1.FRACTIONAL_DIGITS))
.minus(constants_1.YOCTO_THRESHOLD_VARIATION);
};
exports.getYoctoThreshold = getYoctoThreshold;
/*
* An estimation for the fee by using the staking gas and scaling accordingly.
* Buffer added so that the transaction never fails - we'll always overestimate.
*/
const getStakingFees = (t, gasPrice) => {
const stakingGas = (0, exports.getStakingGas)(t);
return stakingGas
.plus(constants_1.STAKING_GAS_BASE) // Buffer
.multipliedBy(gasPrice)
.dividedBy(10);
};
exports.getStakingFees = getStakingFees;
//# sourceMappingURL=logic.js.map