PHP WebShell
Текущая директория: /opt/BitGoJS/node_modules/@aptos-labs/ts-sdk/src/internal
Просмотр файла: transaction.ts
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0
/**
* This file contains the underlying implementations for exposed API surface in
* the {@link api/transaction}. By moving the methods out into a separate file,
* other namespaces and processes can access these methods without depending on the entire
* transaction namespace and without having a dependency cycle error.
*/
import { AptosConfig } from "../api/aptosConfig";
import { getAptosFullNode, paginateWithCursor } from "../client";
import { AptosApiError } from "../errors";
import {
TransactionResponseType,
type AnyNumber,
type GasEstimation,
type HexInput,
type PaginationArgs,
type TransactionResponse,
WaitForTransactionOptions,
CommittedTransactionResponse,
Block,
} from "../types";
import { DEFAULT_TXN_TIMEOUT_SEC, ProcessorType } from "../utils/const";
import { sleep } from "../utils/helpers";
import { memoizeAsync } from "../utils/memoize";
import { getIndexerLastSuccessVersion, getProcessorStatus } from "./general";
/**
* Retrieve a list of transactions based on the specified options.
*
* @param {Object} args - The parameters for retrieving transactions.
* @param {Object} args.aptosConfig - The configuration object for Aptos.
* @param {Object} args.options - The options for pagination.
* @param {number} args.options.offset - The number of transactions to skip before starting to collect the result set.
* @param {number} args.options.limit - The maximum number of transactions to return.
*/
export async function getTransactions(args: {
aptosConfig: AptosConfig;
options?: PaginationArgs;
}): Promise<TransactionResponse[]> {
const { aptosConfig, options } = args;
return paginateWithCursor<{}, TransactionResponse[]>({
aptosConfig,
originMethod: "getTransactions",
path: "transactions",
params: { start: options?.offset, limit: options?.limit },
});
}
/**
* Retrieves the estimated gas price for transactions on the Aptos network.
* This function helps users understand the current gas price, which is essential for transaction planning and cost estimation.
*
* @param args - The configuration parameters for the Aptos network.
* @param args.aptosConfig - The configuration object containing network details.
*/
export async function getGasPriceEstimation(args: { aptosConfig: AptosConfig }) {
const { aptosConfig } = args;
return memoizeAsync(
async () => {
const { data } = await getAptosFullNode<{}, GasEstimation>({
aptosConfig,
originMethod: "getGasPriceEstimation",
path: "estimate_gas_price",
});
return data;
},
`gas-price-${aptosConfig.network}`,
1000 * 60 * 5, // 5 minutes
)();
}
/**
* Retrieves the transaction details associated with a specific ledger version.
*
* @param args - The arguments for the transaction retrieval.
* @param args.aptosConfig - The configuration settings for the Aptos client.
* @param args.ledgerVersion - The ledger version for which to retrieve the transaction.
* @returns The transaction details for the specified ledger version.
*/
export async function getTransactionByVersion(args: {
aptosConfig: AptosConfig;
ledgerVersion: AnyNumber;
}): Promise<TransactionResponse> {
const { aptosConfig, ledgerVersion } = args;
const { data } = await getAptosFullNode<{}, TransactionResponse>({
aptosConfig,
originMethod: "getTransactionByVersion",
path: `transactions/by_version/${ledgerVersion}`,
});
return data;
}
/**
* Retrieves transaction details using the specified transaction hash.
*
* @param args - The arguments for retrieving the transaction.
* @param args.aptosConfig - The configuration settings for the Aptos client.
* @param args.transactionHash - The hash of the transaction to retrieve.
* @returns A promise that resolves to the transaction details.
*/
export async function getTransactionByHash(args: {
aptosConfig: AptosConfig;
transactionHash: HexInput;
}): Promise<TransactionResponse> {
const { aptosConfig, transactionHash } = args;
const { data } = await getAptosFullNode<{}, TransactionResponse>({
aptosConfig,
path: `transactions/by_hash/${transactionHash}`,
originMethod: "getTransactionByHash",
});
return data;
}
/**
* Checks if a transaction is currently pending based on its hash.
* This function helps determine the status of a transaction in the Aptos network.
*
* @param args - The arguments for checking the transaction status.
* @param args.aptosConfig - The configuration settings for connecting to the Aptos network.
* @param args.transactionHash - The hash of the transaction to check.
* @returns A boolean indicating whether the transaction is pending.
* @throws An error if the transaction cannot be retrieved due to reasons other than a 404 status.
*/
export async function isTransactionPending(args: {
aptosConfig: AptosConfig;
transactionHash: HexInput;
}): Promise<boolean> {
const { aptosConfig, transactionHash } = args;
try {
const transaction = await getTransactionByHash({ aptosConfig, transactionHash });
return transaction.type === TransactionResponseType.Pending;
} catch (e: any) {
if (e?.status === 404) {
return true;
}
throw e;
}
}
/**
* Waits for a transaction to be confirmed by its hash.
* This function allows you to monitor the status of a transaction until it is finalized.
*
* @param args - The arguments for the function.
* @param args.aptosConfig - The configuration settings for the Aptos client.
* @param args.transactionHash - The hash of the transaction to wait for.
*/
export async function longWaitForTransaction(args: {
aptosConfig: AptosConfig;
transactionHash: HexInput;
}): Promise<TransactionResponse> {
const { aptosConfig, transactionHash } = args;
const { data } = await getAptosFullNode<{}, TransactionResponse>({
aptosConfig,
path: `transactions/wait_by_hash/${transactionHash}`,
originMethod: "longWaitForTransaction",
});
return data;
}
/**
* Waits for a transaction to be confirmed on the blockchain and handles potential errors during the process.
* This function allows you to monitor the status of a transaction until it is either confirmed or fails.
*
* @param args - The arguments for waiting for a transaction.
* @param args.aptosConfig - The configuration settings for Aptos.
* @param args.transactionHash - The hash of the transaction to wait for.
* @param args.options - Optional settings for waiting, including timeout and success check.
* @param args.options.timeoutSecs - The maximum time to wait for the transaction in seconds. Defaults to a predefined value.
* @param args.options.checkSuccess - A flag indicating whether to check the success status of the transaction. Defaults to true.
* @returns A promise that resolves to the transaction response once the transaction is confirmed.
* @throws WaitForTransactionError if the transaction times out or remains pending.
* @throws FailedTransactionError if the transaction fails.
*/
export async function waitForTransaction(args: {
aptosConfig: AptosConfig;
transactionHash: HexInput;
options?: WaitForTransactionOptions;
}): Promise<CommittedTransactionResponse> {
const { aptosConfig, transactionHash, options } = args;
const timeoutSecs = options?.timeoutSecs ?? DEFAULT_TXN_TIMEOUT_SEC;
const checkSuccess = options?.checkSuccess ?? true;
let isPending = true;
let timeElapsed = 0;
let lastTxn: TransactionResponse | undefined;
let lastError: AptosApiError | undefined;
let backoffIntervalMs = 200;
const backoffMultiplier = 1.5;
/**
* Handles API errors by throwing the last error or a timeout error for a failed transaction.
*
* @param e - The error object that occurred during the API call.
* @throws {Error} Throws the last error if it exists; otherwise, throws a WaitForTransactionError indicating a timeout.
*/
function handleAPIError(e: any) {
// In short, this means we will retry if it was an AptosApiError and the code was 404 or 5xx.
const isAptosApiError = e instanceof AptosApiError;
if (!isAptosApiError) {
throw e; // This would be unexpected
}
lastError = e;
const isRequestError = e.status !== 404 && e.status >= 400 && e.status < 500;
if (isRequestError) {
throw e;
}
}
// check to see if the txn is already on the blockchain
try {
lastTxn = await getTransactionByHash({ aptosConfig, transactionHash });
isPending = lastTxn.type === TransactionResponseType.Pending;
} catch (e) {
handleAPIError(e);
}
// If the transaction is pending, we do a long wait once to avoid polling
if (isPending) {
const startTime = Date.now();
try {
lastTxn = await longWaitForTransaction({ aptosConfig, transactionHash });
isPending = lastTxn.type === TransactionResponseType.Pending;
} catch (e) {
handleAPIError(e);
}
timeElapsed = (Date.now() - startTime) / 1000;
}
// Now we do polling to see if the transaction is still pending
while (isPending) {
if (timeElapsed >= timeoutSecs) {
break;
}
try {
// eslint-disable-next-line no-await-in-loop
lastTxn = await getTransactionByHash({ aptosConfig, transactionHash });
isPending = lastTxn.type === TransactionResponseType.Pending;
if (!isPending) {
break;
}
} catch (e) {
handleAPIError(e);
}
// eslint-disable-next-line no-await-in-loop
await sleep(backoffIntervalMs);
timeElapsed += backoffIntervalMs / 1000; // Convert to seconds
backoffIntervalMs *= backoffMultiplier;
}
// There is a chance that lastTxn is still undefined. Let's throw the last error otherwise a WaitForTransactionError
if (lastTxn === undefined) {
if (lastError) {
throw lastError;
} else {
throw new WaitForTransactionError(
`Fetching transaction ${transactionHash} failed and timed out after ${timeoutSecs} seconds`,
lastTxn,
);
}
}
if (lastTxn.type === TransactionResponseType.Pending) {
throw new WaitForTransactionError(
`Transaction ${transactionHash} timed out in pending state after ${timeoutSecs} seconds`,
lastTxn,
);
}
if (!checkSuccess) {
return lastTxn;
}
if (!lastTxn.success) {
throw new FailedTransactionError(
`Transaction ${transactionHash} failed with an error: ${lastTxn.vm_status}`,
lastTxn,
);
}
return lastTxn;
}
/**
* Waits for the indexer to sync up to the specified ledger version. The timeout is 3 seconds.
*
* @param args - The arguments for the function.
* @param args.aptosConfig - The configuration object for Aptos.
* @param args.minimumLedgerVersion - The minimum ledger version that the indexer should sync to.
* @param args.processorType - (Optional) The type of processor to check the last success version from.
*/
export async function waitForIndexer(args: {
aptosConfig: AptosConfig;
minimumLedgerVersion: AnyNumber;
processorType?: ProcessorType;
}): Promise<void> {
const { aptosConfig, processorType } = args;
const minimumLedgerVersion = BigInt(args.minimumLedgerVersion);
const timeoutMilliseconds = 3000; // 3 seconds
const startTime = new Date().getTime();
let indexerVersion = BigInt(-1);
while (indexerVersion < minimumLedgerVersion) {
// check for timeout
if (new Date().getTime() - startTime > timeoutMilliseconds) {
throw new Error("waitForLastSuccessIndexerVersionSync timeout");
}
if (processorType === undefined) {
// Get the last success version from all processor
// eslint-disable-next-line no-await-in-loop
indexerVersion = await getIndexerLastSuccessVersion({ aptosConfig });
} else {
// Get the last success version from the specific processor
// eslint-disable-next-line no-await-in-loop
const processor = await getProcessorStatus({ aptosConfig, processorType });
indexerVersion = processor.last_success_version;
}
if (indexerVersion >= minimumLedgerVersion) {
// break out immediately if we are synced
break;
}
// eslint-disable-next-line no-await-in-loop
await sleep(200);
}
}
/**
* Represents an error that occurs when waiting for a transaction to complete.
* This error is thrown by the `waitForTransaction` function when a transaction
* times out or when the transaction response is undefined.
*
* @param message - A descriptive message for the error.
* @param lastSubmittedTransaction - The last submitted transaction response, if available.
*/
export class WaitForTransactionError extends Error {
public readonly lastSubmittedTransaction: TransactionResponse | undefined;
/**
* Constructs an instance of the class with a specified message and transaction response.
*
* @param message - The message associated with the transaction.
* @param lastSubmittedTransaction - The transaction response object containing details about the transaction.
*/
constructor(message: string, lastSubmittedTransaction: TransactionResponse | undefined) {
super(message);
this.lastSubmittedTransaction = lastSubmittedTransaction;
}
}
/**
* Represents an error that occurs when a transaction fails.
* This error is thrown by the `waitForTransaction` function when the `checkSuccess` parameter is set to true.
*
* @param message - A description of the error.
* @param transaction - The transaction response associated with the failure.
*/
export class FailedTransactionError extends Error {
public readonly transaction: TransactionResponse;
constructor(message: string, transaction: TransactionResponse) {
super(message);
this.transaction = transaction;
}
}
/**
* Retrieves a block from the Aptos blockchain by its ledger version.
* This function allows you to obtain detailed information about a specific block, including its transactions if requested.
*
* @param args - The arguments for retrieving the block.
* @param args.aptosConfig - The configuration object for connecting to the Aptos node.
* @param args.ledgerVersion - The ledger version of the block to retrieve.
* @param args.options - Optional parameters for the request.
* @param args.options.withTransactions - Indicates whether to include transactions in the block data.
*/
export async function getBlockByVersion(args: {
aptosConfig: AptosConfig;
ledgerVersion: AnyNumber;
options?: { withTransactions?: boolean };
}): Promise<Block> {
const { aptosConfig, ledgerVersion, options } = args;
const { data: block } = await getAptosFullNode<{}, Block>({
aptosConfig,
originMethod: "getBlockByVersion",
path: `blocks/by_version/${ledgerVersion}`,
params: { with_transactions: options?.withTransactions },
});
return fillBlockTransactions({ block, ...args });
}
/**
* Retrieves a block from the Aptos blockchain by its height.
*
* @param args - The parameters for retrieving the block.
* @param args.aptosConfig - The configuration object for connecting to the Aptos network.
* @param args.blockHeight - The height of the block to retrieve.
* @param args.options - Optional parameters for the request.
* @param args.options.withTransactions - Indicates whether to include transactions in the block data.
* @returns A promise that resolves to the block data, potentially including its transactions.
*/
export async function getBlockByHeight(args: {
aptosConfig: AptosConfig;
blockHeight: AnyNumber;
options?: { withTransactions?: boolean };
}): Promise<Block> {
const { aptosConfig, blockHeight, options } = args;
const { data: block } = await getAptosFullNode<{}, Block>({
aptosConfig,
originMethod: "getBlockByHeight",
path: `blocks/by_height/${blockHeight}`,
params: { with_transactions: options?.withTransactions },
});
return fillBlockTransactions({ block, ...args });
}
/**
* Fills in the block with transactions if not enough were returned. This function ensures that the block contains all relevant
* transactions by fetching any missing ones based on the specified options.
* @param args - The arguments for filling the block transactions.
* @param args.aptosConfig - The configuration settings for Aptos.
* @param args.block - The block object that will be filled with transactions.
* @param args.options - Optional settings for fetching transactions.
* @param args.options.withTransactions - Indicates whether to include transactions in the block.
*/
async function fillBlockTransactions(args: {
aptosConfig: AptosConfig;
block: Block;
options?: { withTransactions?: boolean };
}) {
const { aptosConfig, block, options } = args;
if (options?.withTransactions) {
// Transactions should be filled, but this ensures it
block.transactions = block.transactions ?? [];
const lastTxn = block.transactions[block.transactions.length - 1];
const firstVersion = BigInt(block.first_version);
const lastVersion = BigInt(block.last_version);
// Convert the transaction to the type
const curVersion: string | undefined = (lastTxn as any)?.version;
let latestVersion;
// This time, if we don't have any transactions, we will try once with the start of the block
if (curVersion === undefined) {
latestVersion = firstVersion - 1n;
} else {
latestVersion = BigInt(curVersion);
}
// If we have all the transactions in the block, we can skip out, otherwise we need to fill the transactions
if (latestVersion === lastVersion) {
return block;
}
// For now, we will grab all the transactions in groups of 100, but we can make this more efficient by trying larger
// amounts
const fetchFutures = [];
const pageSize = 100n;
for (let i = latestVersion + 1n; i < lastVersion; i += BigInt(100)) {
fetchFutures.push(
getTransactions({
aptosConfig,
options: {
offset: i,
limit: Math.min(Number(pageSize), Number(lastVersion - i + 1n)),
},
}),
);
}
// Combine all the futures
const responses = await Promise.all(fetchFutures);
for (const txns of responses) {
block.transactions.push(...txns);
}
}
return block;
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!