UNPKG

@ledgerhq/coin-near

Version:
298 lines 11.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getCommission = exports.getValidators = exports.getStakingPositions = exports.broadcastTransaction = exports.getAccessKey = exports.getGasPrice = exports.getProtocolConfig = exports.getAccount = exports.fetchAccountDetails = void 0; const logs_1 = require("@ledgerhq/logs"); const network_1 = __importDefault(require("@ledgerhq/live-network/network")); const bignumber_js_1 = require("bignumber.js"); const nearAPI = __importStar(require("near-api-js")); const logic_1 = require("../logic"); const preload_1 = require("../preload"); const cache_1 = require("@ledgerhq/live-network/cache"); const constants_1 = require("../constants"); const config_1 = require("../config"); const fetchAccountDetails = async (address) => { const currencyConfig = (0, config_1.getCoinConfig)(); const { data } = await (0, network_1.default)({ method: "POST", url: currencyConfig.infra.API_NEAR_PRIVATE_NODE, data: { jsonrpc: "2.0", id: "id", method: "query", params: { request_type: "view_account", finality: "final", account_id: address, }, }, }); return data.result; }; exports.fetchAccountDetails = fetchAccountDetails; const getAccount = async (address) => { let accountDetails; accountDetails = await (0, exports.fetchAccountDetails)(address); if (!accountDetails) { accountDetails = { amount: "0", block_height: 0, storage_usage: 0, }; } const { stakingPositions, totalStaked, totalAvailable, totalPending } = await (0, exports.getStakingPositions)(address); const { storageCost } = (0, preload_1.getCurrentNearPreloadData)(); const balance = new bignumber_js_1.BigNumber(accountDetails.amount); const storageUsage = storageCost.multipliedBy(accountDetails.storage_usage); const minBalanceBuffer = new bignumber_js_1.BigNumber(constants_1.MIN_ACCOUNT_BALANCE_BUFFER); let spendableBalance = balance.minus(storageUsage).minus(minBalanceBuffer); if (spendableBalance.lt(0)) { spendableBalance = new bignumber_js_1.BigNumber(0); } return { blockHeight: accountDetails.block_height, balance: balance.plus(totalStaked).plus(totalAvailable).plus(totalPending), spendableBalance, nearResources: { stakedBalance: totalStaked, availableBalance: totalAvailable, pendingBalance: totalPending, storageUsageBalance: storageUsage.plus(minBalanceBuffer), stakingPositions, }, }; }; exports.getAccount = getAccount; const getProtocolConfig = async () => { const currencyConfig = (0, config_1.getCoinConfig)(); const { data } = await (0, network_1.default)({ method: "POST", url: currencyConfig.infra.API_NEAR_PRIVATE_NODE, data: { jsonrpc: "2.0", id: "id", method: "EXPERIMENTAL_protocol_config", params: { finality: "final", }, }, }); return data.result; }; exports.getProtocolConfig = getProtocolConfig; const getGasPrice = async () => { const currencyConfig = (0, config_1.getCoinConfig)(); const { data } = await (0, network_1.default)({ method: "POST", url: currencyConfig.infra.API_NEAR_PRIVATE_NODE, data: { jsonrpc: "2.0", id: "id", method: "gas_price", params: [null], }, }); return data?.result?.gas_price; }; exports.getGasPrice = getGasPrice; const getAccessKey = async ({ address, publicKey, }) => { const currencyConfig = (0, config_1.getCoinConfig)(); const { data } = await (0, network_1.default)({ method: "POST", url: currencyConfig.infra.API_NEAR_PRIVATE_NODE, data: { jsonrpc: "2.0", id: "id", method: "query", params: { request_type: "view_access_key", finality: "final", account_id: address, public_key: publicKey, }, }, }); return data.result || {}; }; exports.getAccessKey = getAccessKey; /** * Implements a retry mechanism for broadcasting a transaction * based on the near documentation: https://docs.near.org/api/rpc/transactions#what-could-go-wrong-send-tx * * `TIMEOUT_ERROR` can be thrown when the transaction is not yet executed in less than 10 seconds. * Documentation advises to "re-submit the request with the identical transaction" in this case. */ const broadcastTransaction = async (transaction, retries = 6) => { const currencyConfig = (0, config_1.getCoinConfig)(); const { data } = await (0, network_1.default)({ method: "POST", url: currencyConfig.infra.API_NEAR_PRIVATE_NODE, data: { jsonrpc: "2.0", id: "id", method: "send_tx", params: { signed_tx_base64: transaction, wait_until: "EXECUTED_OPTIMISTIC", }, }, }); if (data.error) { if (data.error?.cause?.name === "TIMEOUT_ERROR" && retries > 0) { (0, logs_1.log)("Near", "broadcastTransaction retrying after error", { data, payload: { jsonrpc: "2.0", id: "id", method: "send_tx", params: { signed_tx_base64: transaction, wait_until: "EXECUTED_OPTIMISTIC", }, }, retries, }); return (0, exports.broadcastTransaction)(transaction, retries - 1); } (0, logs_1.log)("Near", "broadcastTransaction error", data.error); throw new Error((data.error?.cause?.name || "UNKOWWN CAUSE") + ": " + data.error.message); } return data.result.transaction.hash; }; exports.broadcastTransaction = broadcastTransaction; const getStakingPositions = async (address) => { const currencyConfig = (0, config_1.getCoinConfig)(); const { connect, keyStores } = nearAPI; const config = { networkId: "mainnet", keyStore: new keyStores.InMemoryKeyStore(), nodeUrl: currencyConfig.infra.API_NEAR_PRIVATE_NODE, headers: {}, }; const near = await connect(config); const account = await near.account(address); let totalStaked = new bignumber_js_1.BigNumber(0); let totalAvailable = new bignumber_js_1.BigNumber(0); let totalPending = new bignumber_js_1.BigNumber(0); const activeDelegatedStakeBalance = await account.getActiveDelegatedStakeBalance(); const stakingPositions = await Promise.all(activeDelegatedStakeBalance.stakedValidators.map(async ({ validatorId }) => { const contract = new nearAPI.Contract(account, validatorId, { viewMethods: [ "get_account_staked_balance", "get_account_unstaked_balance", "is_account_unstaked_balance_available", ], changeMethods: [], useLocalViewExecution: false, }); const [rawStaked, rawUnstaked, isAvailable] = await Promise.all([ contract.get_account_staked_balance({ account_id: address, }), contract.get_account_unstaked_balance({ account_id: address, }), contract.is_account_unstaked_balance_available({ account_id: address, }), ]); const unstaked = new bignumber_js_1.BigNumber(rawUnstaked); let available = new bignumber_js_1.BigNumber(0); let pending = unstaked; if (isAvailable) { available = unstaked; pending = new bignumber_js_1.BigNumber(0); } const staked = new bignumber_js_1.BigNumber(rawStaked); available = new bignumber_js_1.BigNumber(available); pending = new bignumber_js_1.BigNumber(pending); const stakingThreshold = (0, logic_1.getYoctoThreshold)(); if (staked.gte(stakingThreshold)) { totalStaked = totalStaked.plus(staked); } if (available.gte(stakingThreshold)) { totalAvailable = totalAvailable.plus(available); } if (pending.gte(stakingThreshold)) { totalPending = totalPending.plus(pending); } return { staked, available, pending, validatorId, }; })); return { stakingPositions: stakingPositions.filter(sp => (0, logic_1.canUnstake)(sp) || (0, logic_1.canWithdraw)(sp) || sp.pending.gt(0)), totalStaked, totalAvailable, totalPending, }; }; exports.getStakingPositions = getStakingPositions; exports.getValidators = (0, cache_1.makeLRUCache)(async () => { const currencyConfig = (0, config_1.getCoinConfig)(); const { data } = await (0, network_1.default)({ method: "POST", url: currencyConfig.infra.API_NEAR_PRIVATE_NODE, data: { jsonrpc: "2.0", id: "id", method: "validators", params: [null], }, }); return data?.result?.current_validators || []; }, () => "", { ttl: 30 * 60 * 1000 }); exports.getCommission = (0, cache_1.makeLRUCache)(async (validatorAddress) => { const currencyConfig = (0, config_1.getCoinConfig)(); const { data } = await (0, network_1.default)({ method: "POST", url: currencyConfig.infra.API_NEAR_PRIVATE_NODE, data: { jsonrpc: "2.0", id: "id", method: "query", params: { request_type: "call_function", account_id: validatorAddress, method_name: "get_reward_fee_fraction", args_base64: "e30=", finality: "optimistic", }, }, }); const result = data?.result?.result; if (Array.isArray(result) && result.length) { const parsedResult = JSON.parse(Buffer.from(result).toString()); return Math.round((parsedResult.numerator / parsedResult.denominator) * 100); } return null; }, validatorAddress => validatorAddress, { ttl: 30 * 60 * 1000 }); //# sourceMappingURL=node.js.map