UNPKG

@aeternity/aepp-sdk

Version:

SDK for the æternity blockchain

448 lines (431 loc) 13.1 kB
import { AE_AMOUNT_FORMATS, formatAmount } from './utils/amount-formatter.js'; import { isAccountNotFoundError, pause } from './utils/other.js'; import { unwrapProxy } from './utils/wrap-proxy.js'; import { isName, produceNameId } from './tx/builder/helpers.js'; import { DRY_RUN_ACCOUNT } from './tx/builder/constants.js'; import { AensPointerContextError, DryRunError, InvalidAensNameError, TxTimedOutError, TxNotInChainError, InternalError } from './utils/errors.js'; import { decode, encode, Encoding } from './utils/encoder.js'; async function getEventInterval(type, { _expectedMineRate, _microBlockCycle, onNode }) { if (_expectedMineRate != null && type === 'key-block') return _expectedMineRate; if (_microBlockCycle != null && type === 'micro-block') return _microBlockCycle; const networkId = await onNode.getNetworkId(); if (networkId === 'ae_dev') return 0; if (!['ae_mainnet', 'ae_uat'].includes(networkId) && (await onNode._isHyperchain())) return 3000; if (type === 'key-block') return 180000;else return 3000; } /** * @category chain * @param type - Type * @param options - Options */ export async function _getPollInterval(type, options) { return Math.floor((await getEventInterval(type, options)) / 3); } const heightCache = new WeakMap(); /** * Obtain current height of the chain * @category chain * @param options - Options * @param options.cached - Get height from the cache. The lag behind the actual height shouldn't * be more than 1 block. Use if needed to reduce requests count, and approximate value can be used. * For example, for timeout check in transaction status polling. * @returns Current chain height */ export async function getHeight({ cached = false, ...options }) { const onNode = unwrapProxy(options.onNode); if (cached) { const cache = heightCache.get(onNode); if (cache != null && cache.time > Date.now() - (await _getPollInterval('key-block', options))) { return cache.height; } } const { height } = await onNode.getCurrentKeyBlockHeight(); heightCache.set(onNode, { height, time: Date.now() }); return height; } /** * Return transaction details if it is mined, fail otherwise. * If the transaction has ttl specified then would wait till it leaves the mempool. * Otherwise would fail if a specified amount of blocks were mined. * @category chain * @param th - The hash of transaction to poll * @param options - Options * @param options.interval - Interval (in ms) at which to poll the chain * @param options.blocks - Number of blocks mined after which to fail if transaction ttl is not set * @param options.onNode - Node to use * @returns The transaction as it was mined */ export async function poll(th, { blocks = 5, interval, ...options }) { interval !== null && interval !== void 0 ? interval : interval = await _getPollInterval('micro-block', options); let max; do { const tx = await options.onNode.getTransactionByHash(th); if (tx.blockHeight !== -1) return tx; if (max == null) { max = tx.tx.ttl !== 0 ? -1 : (await getHeight({ ...options, cached: true })) + blocks; } await pause(interval); } while (max === -1 ? true : (await getHeight({ ...options, cached: true })) < max); throw new TxTimedOutError(blocks, th); } /** * Wait for the chain to reach a specific height * @category chain * @param height - Height to wait for * @param options - Options * @param options.interval - Interval (in ms) at which to poll the chain * @param options.onNode - Node to use * @returns Current chain height */ export async function awaitHeight(height, { interval, ...options }) { interval !== null && interval !== void 0 ? interval : interval = Math.min(await _getPollInterval('key-block', options), 5000); let currentHeight; do { if (currentHeight != null) await pause(interval); currentHeight = await getHeight(options); } while (currentHeight < height); return currentHeight; } /** * Wait for transaction confirmation * @category chain * @param txHash - Transaction hash * @param options - Options * @param options.confirm - Number of micro blocks to wait for transaction confirmation * @param options.onNode - Node to use * @returns Current Height */ export async function waitForTxConfirm(txHash, { confirm = 3, onNode, ...options }) { const { blockHeight } = await onNode.getTransactionByHash(txHash); const height = await awaitHeight(blockHeight + confirm, { onNode, ...options }); const { blockHeight: newBlockHeight } = await onNode.getTransactionByHash(txHash); switch (newBlockHeight) { case -1: throw new TxNotInChainError(txHash); case blockHeight: return height; default: return waitForTxConfirm(txHash, { onNode, confirm, ...options }); } } /** * Get account by account public key * @category chain * @param address - Account address (public key) * @param options - Options * @param options.height - Get account on specific block by block height * @param options.hash - Get account on specific block by micro block hash or key block hash * @param options.onNode - Node to use */ export async function getAccount(address, { height, hash, onNode }) { if (height != null) return onNode.getAccountByPubkeyAndHeight(address, height); if (hash != null) return onNode.getAccountByPubkeyAndHash(address, hash); return onNode.getAccountByPubkey(address); } /** * Request the balance of specified account * @category chain * @param address - The public account address to obtain the balance for * @param options - Options * @param options.format * @param options.height - The chain height at which to obtain the balance for * (default: top of chain) * @param options.hash - The block hash on which to obtain the balance for (default: top of chain) */ export async function getBalance(address, { /** * @deprecated no replacement implemented yet */ format = AE_AMOUNT_FORMATS.AETTOS, ...options }) { const addr = address.startsWith('ok_') ? encode(decode(address), Encoding.AccountAddress) : address; const { balance } = await getAccount(addr, options).catch(error => { if (!isAccountNotFoundError(error)) throw error; return { balance: 0n }; }); return formatAmount(balance, { targetDenomination: format }); } /** * Obtain current generation * @category chain * @param options - Options * @param options.onNode - Node to use * @returns Current Generation * @deprecated Use {@link Node.getCurrentGeneration} instead */ export async function getCurrentGeneration({ onNode }) { return onNode.getCurrentGeneration(); } /** * Get generation by hash or height * @category chain * @param hashOrHeight - Generation hash or height * @param options - Options * @param options.onNode - Node to use * @returns Generation * @deprecated Use {@link Node.getGenerationByHash} or {@link Node.getGenerationByHeight} instead */ export async function getGeneration(hashOrHeight, { onNode }) { if (typeof hashOrHeight === 'number') return onNode.getGenerationByHeight(hashOrHeight); return onNode.getGenerationByHash(hashOrHeight); } /** * Get micro block transactions * @category chain * @param hash - Micro block hash * @param options - Options * @param options.onNode - Node to use * @returns Transactions * @deprecated Use {@link Node.getMicroBlockTransactionsByHash} instead */ export async function getMicroBlockTransactions(hash, { onNode }) { return (await onNode.getMicroBlockTransactionsByHash(hash)).transactions; } /** * Get key block * @category chain * @param hashOrHeight - Key block hash or height * @param options - Options * @param options.onNode - Node to use * @returns Key Block * @deprecated Use {@link Node.getKeyBlockByHeight} or {@link Node.getKeyBlockByHash} instead */ export async function getKeyBlock(hashOrHeight, { onNode }) { if (typeof hashOrHeight === 'number') return onNode.getKeyBlockByHeight(hashOrHeight); return onNode.getKeyBlockByHash(hashOrHeight); } /** * Get micro block header * @category chain * @param hash - Micro block hash * @param options - Options * @param options.onNode - Node to use * @returns Micro block header * @deprecated Use {@link Node.getMicroBlockHeaderByHash} instead */ export async function getMicroBlockHeader(hash, { onNode }) { return onNode.getMicroBlockHeaderByHash(hash); } const txDryRunRequests = new Map(); async function txDryRunHandler(key, onNode) { const rs = txDryRunRequests.get(key); txDryRunRequests.delete(key); if (rs == null) throw new InternalError("Can't get dry-run request"); let dryRunRes; try { const top = typeof rs[0].top === 'number' ? (await onNode.getKeyBlockByHeight(rs[0].top)).hash : rs[0].top; dryRunRes = await onNode.protectedDryRunTxs({ top, txEvents: rs[0].txEvents, txs: rs.map(req => ({ tx: req.tx })), accounts: Array.from(new Set(rs.map(req => req.accountAddress))).map(pubKey => ({ pubKey, amount: DRY_RUN_ACCOUNT.amount })) }); } catch (error) { rs.forEach(({ reject }) => reject(error)); return; } const { results, txEvents } = dryRunRes; results.forEach(({ result, reason, ...resultPayload }, idx) => { const { resolve, reject, tx, accountAddress } = rs[idx]; if (result === 'ok') resolve({ ...resultPayload, txEvents });else reject(Object.assign(new DryRunError(reason), { tx, accountAddress })); }); } /** * Transaction dry-run * @category chain * @param tx - transaction to execute * @param accountAddress - address that will be used to execute transaction * @param options - Options * @param options.top - hash of block on which to make dry-run * @param options.txEvents - collect and return on-chain tx events that would result from the call * @param options.combine - Enables combining of similar requests to a single dry-run call * @param options.onNode - Node to use */ export async function txDryRun(tx, accountAddress, { top, txEvents, combine, onNode }) { var _txDryRunRequests$get; const key = combine === true ? [top, txEvents].join() : 'immediate'; const requests = (_txDryRunRequests$get = txDryRunRequests.get(key)) !== null && _txDryRunRequests$get !== void 0 ? _txDryRunRequests$get : []; txDryRunRequests.set(key, requests); return new Promise((resolve, reject) => { var _requests$timeout; requests.push({ tx, accountAddress, top, txEvents, resolve, reject }); if (combine !== true) { void txDryRunHandler(key, onNode); return; } (_requests$timeout = requests.timeout) !== null && _requests$timeout !== void 0 ? _requests$timeout : requests.timeout = setTimeout(() => { void txDryRunHandler(key, onNode); }); }); } /** * Get contract byte code * @category contract * @param contractId - Contract address * @param options - Options * @param options.onNode - Node to use * @deprecated Use {@link Node.getContractCode} instead */ export async function getContractByteCode(contractId, { onNode }) { return onNode.getContractCode(contractId); } /** * Get contract entry * @category contract * @param contractId - Contract address * @param options - Options * @param options.onNode - Node to use * @deprecated Use {@link Node.getContract} instead */ export async function getContract(contractId, { onNode }) { return onNode.getContract(contractId); } /** * Get name entry * @category AENS * @param name - AENS name * @param options - Options * @param options.onNode - Node to use * @deprecated Use {@link Node.getNameEntryByName} or {@link Name.getState} instead */ export async function getName(name, { onNode }) { return onNode.getNameEntryByName(name); } /** * Resolve AENS name and return name hash * @category AENS * @param nameOrId - AENS name or address * @param key - in AENS pointers record * @param options - Options * @param options.verify - To ensure that name exist and have a corresponding pointer * // TODO: avoid that to don't trust to current api gateway * @param options.resolveByNode - Enables pointer resolving using node * @param options.onNode - Node to use * @returns Address or AENS name hash */ export async function resolveName(nameOrId, key, { verify = true, resolveByNode = false, onNode }) { if (isName(nameOrId)) { if (verify || resolveByNode) { const name = await onNode.getNameEntryByName(nameOrId); const pointer = name.pointers.find(p => p.key === key); if (pointer == null) throw new AensPointerContextError(nameOrId, key); if (resolveByNode) return pointer.id; } return produceNameId(nameOrId); } try { decode(nameOrId); return nameOrId; } catch (error) { throw new InvalidAensNameError(`Invalid name or address: ${nameOrId}`); } } //# sourceMappingURL=chain.js.map