UNPKG

@bbachain/web3.js

Version:

BBAChain Javascript API

1,730 lines (1,601 loc) 195 kB
import HttpKeepAliveAgent, { HttpsAgent as HttpsKeepAliveAgent, } from 'agentkeepalive'; import bs58 from 'bs58'; import {Buffer} from 'buffer'; // @ts-ignore import fastStableStringify from 'fast-stable-stringify'; import type {Agent as NodeHttpAgent} from 'http'; import {Agent as NodeHttpsAgent} from 'https'; import { type as pick, number, string, array, boolean, literal, record, union, optional, nullable, coerce, instance, create, tuple, unknown, any, } from 'superstruct'; import type {Struct} from 'superstruct'; import RpcClient from 'jayson/lib/client/browser'; import {JSONRPCError} from 'jayson'; import {EpochSchedule} from './epoch-schedule'; import {SendTransactionError, BBAChainJSONRPCError} from './errors'; import fetchImpl, {Response} from './fetch-impl'; import {DurableNonce, NonceAccount} from './nonce-account'; import {PublicKey} from './publickey'; import {Signer} from './keypair'; import RpcWebSocketClient from './rpc-websocket'; import {MS_PER_SLOT} from './timing'; import { Transaction, TransactionStatus, TransactionVersion, VersionedTransaction, } from './transaction'; import {Message, MessageHeader, MessageV0, VersionedMessage} from './message'; import {AddressLookupTableAccount} from './programs/address-lookup-table/state'; import assert from './utils/assert'; import {sleep} from './utils/sleep'; import {toBuffer} from './utils/to-buffer'; import { TransactionExpiredBlockheightExceededError, TransactionExpiredNonceInvalidError, TransactionExpiredTimeoutError, } from './transaction/expiry-custom-errors'; import {makeWebsocketUrl} from './utils/makeWebsocketUrl'; import type {Blockhash} from './blockhash'; import type {FeeCalculator} from './fee-calculator'; import type {TransactionSignature} from './transaction'; import type {CompiledInstruction} from './message'; const PublicKeyFromString = coerce( instance(PublicKey), string(), value => new PublicKey(value), ); const RawAccountDataResult = tuple([string(), literal('base64')]); const BufferFromRawAccountData = coerce( instance(Buffer), RawAccountDataResult, value => Buffer.from(value[0], 'base64'), ); /** * Attempt to use a recent blockhash for up to 30 seconds * @internal */ export const BLOCKHASH_CACHE_TIMEOUT_MS = 30 * 1000; /** * HACK. * Copied from rpc-websockets/dist/lib/client. * Otherwise, `yarn build` fails with: * https://gist.github.com/steveluscher/c057eca81d479ef705cdb53162f9971d */ interface IWSRequestParams { [x: string]: any; [x: number]: any; } type ClientSubscriptionId = number; /** @internal */ type ServerSubscriptionId = number; /** @internal */ type SubscriptionConfigHash = string; /** @internal */ type SubscriptionDisposeFn = () => Promise<void>; /** @internal */ type SubscriptionStateChangeCallback = ( nextState: StatefulSubscription['state'], ) => void; /** @internal */ type SubscriptionStateChangeDisposeFn = () => void; /** * @internal * Every subscription contains the args used to open the subscription with * the server, and a list of callers interested in notifications. */ type BaseSubscription<TMethod = SubscriptionConfig['method']> = Readonly<{ args: IWSRequestParams; callbacks: Set<Extract<SubscriptionConfig, {method: TMethod}>['callback']>; }>; /** * @internal * A subscription may be in various states of connectedness. Only when it is * fully connected will it have a server subscription id associated with it. * This id can be returned to the server to unsubscribe the client entirely. */ type StatefulSubscription = Readonly< // New subscriptions that have not yet been // sent to the server start in this state. | { state: 'pending'; } // These subscriptions have been sent to the server // and are waiting for the server to acknowledge them. | { state: 'subscribing'; } // These subscriptions have been acknowledged by the // server and have been assigned server subscription ids. | { serverSubscriptionId: ServerSubscriptionId; state: 'subscribed'; } // These subscriptions are intended to be torn down and // are waiting on an acknowledgement from the server. | { serverSubscriptionId: ServerSubscriptionId; state: 'unsubscribing'; } // The request to tear down these subscriptions has been // acknowledged by the server. The `serverSubscriptionId` // is the id of the now-dead subscription. | { serverSubscriptionId: ServerSubscriptionId; state: 'unsubscribed'; } >; /** * A type that encapsulates a subscription's RPC method * names and notification (callback) signature. */ type SubscriptionConfig = Readonly< | { callback: AccountChangeCallback; method: 'accountSubscribe'; unsubscribeMethod: 'accountUnsubscribe'; } | { callback: LogsCallback; method: 'logsSubscribe'; unsubscribeMethod: 'logsUnsubscribe'; } | { callback: ProgramAccountChangeCallback; method: 'programSubscribe'; unsubscribeMethod: 'programUnsubscribe'; } | { callback: RootChangeCallback; method: 'rootSubscribe'; unsubscribeMethod: 'rootUnsubscribe'; } | { callback: SignatureSubscriptionCallback; method: 'signatureSubscribe'; unsubscribeMethod: 'signatureUnsubscribe'; } | { callback: SlotChangeCallback; method: 'slotSubscribe'; unsubscribeMethod: 'slotUnsubscribe'; } | { callback: SlotUpdateCallback; method: 'slotsUpdatesSubscribe'; unsubscribeMethod: 'slotsUpdatesUnsubscribe'; } >; /** * @internal * Utility type that keeps tagged unions intact while omitting properties. */ type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never; /** * @internal * This type represents a single subscribable 'topic.' It's made up of: * * - The args used to open the subscription with the server, * - The state of the subscription, in terms of its connectedness, and * - The set of callbacks to call when the server publishes notifications * * This record gets indexed by `SubscriptionConfigHash` and is used to * set up subscriptions, fan out notifications, and track subscription state. */ type Subscription = BaseSubscription & StatefulSubscription & DistributiveOmit<SubscriptionConfig, 'callback'>; type RpcRequest = (methodName: string, args: Array<any>) => Promise<any>; type RpcBatchRequest = (requests: RpcParams[]) => Promise<any[]>; /** * @internal */ export type RpcParams = { methodName: string; args: Array<any>; }; export type TokenAccountsFilter = | { mint: PublicKey; } | { programId: PublicKey; }; /** * Extra contextual information for RPC responses */ export type Context = { slot: number; }; /** * Options for sending transactions */ export type SendOptions = { /** disable transaction verification step */ skipPreflight?: boolean; /** preflight commitment level */ preflightCommitment?: Commitment; /** Maximum number of times for the RPC node to retry sending the transaction to the leader. */ maxRetries?: number; /** The minimum slot that the request can be evaluated at */ minContextSlot?: number; }; /** * Options for confirming transactions */ export type ConfirmOptions = { /** disable transaction verification step */ skipPreflight?: boolean; /** desired commitment level */ commitment?: Commitment; /** preflight commitment level */ preflightCommitment?: Commitment; /** Maximum number of times for the RPC node to retry sending the transaction to the leader. */ maxRetries?: number; /** The minimum slot that the request can be evaluated at */ minContextSlot?: number; }; /** * Options for getConfirmedSignaturesForAddress2 */ export type ConfirmedSignaturesForAddress2Options = { /** * Start searching backwards from this transaction signature. * @remark If not provided the search starts from the highest max confirmed block. */ before?: TransactionSignature; /** Search until this transaction signature is reached, if found before `limit`. */ until?: TransactionSignature; /** Maximum transaction signatures to return (between 1 and 1,000, default: 1,000). */ limit?: number; }; /** * Options for getSignaturesForAddress */ export type SignaturesForAddressOptions = { /** * Start searching backwards from this transaction signature. * @remark If not provided the search starts from the highest max confirmed block. */ before?: TransactionSignature; /** Search until this transaction signature is reached, if found before `limit`. */ until?: TransactionSignature; /** Maximum transaction signatures to return (between 1 and 1,000, default: 1,000). */ limit?: number; /** The minimum slot that the request can be evaluated at */ minContextSlot?: number; }; /** * RPC Response with extra contextual information */ export type RpcResponseAndContext<T> = { /** response context */ context: Context; /** response value */ value: T; }; export type BlockhashWithExpiryBlockHeight = Readonly<{ blockhash: Blockhash; lastValidBlockHeight: number; }>; /** * A strategy for confirming transactions that uses the last valid * block height for a given blockhash to check for transaction expiration. */ export type BlockheightBasedTransactionConfirmationStrategy = BaseTransactionConfirmationStrategy & BlockhashWithExpiryBlockHeight; /** * A strategy for confirming durable nonce transactions. */ export type DurableNonceTransactionConfirmationStrategy = BaseTransactionConfirmationStrategy & { /** * The lowest slot at which to fetch the nonce value from the * nonce account. This should be no lower than the slot at * which the last-known value of the nonce was fetched. */ minContextSlot: number; /** * The account where the current value of the nonce is stored. */ nonceAccountPubkey: PublicKey; /** * The nonce value that was used to sign the transaction * for which confirmation is being sought. */ nonceValue: DurableNonce; }; /** * Properties shared by all transaction confirmation strategies */ export type BaseTransactionConfirmationStrategy = Readonly<{ /** A signal that, when aborted, cancels any outstanding transaction confirmation operations */ abortSignal?: AbortSignal; signature: TransactionSignature; }>; /** * This type represents all transaction confirmation strategies */ export type TransactionConfirmationStrategy = | BlockheightBasedTransactionConfirmationStrategy | DurableNonceTransactionConfirmationStrategy; /* @internal */ function assertEndpointUrl(putativeUrl: string) { if (/^https?:/.test(putativeUrl) === false) { throw new TypeError('Endpoint URL must start with `http:` or `https:`.'); } return putativeUrl; } /** @internal */ function extractCommitmentFromConfig<TConfig>( commitmentOrConfig?: Commitment | ({commitment?: Commitment} & TConfig), ) { let commitment: Commitment | undefined; let config: Omit<TConfig, 'commitment'> | undefined; if (typeof commitmentOrConfig === 'string') { commitment = commitmentOrConfig; } else if (commitmentOrConfig) { const {commitment: specifiedCommitment, ...specifiedConfig} = commitmentOrConfig; commitment = specifiedCommitment; config = specifiedConfig; } return {commitment, config}; } /** * @internal */ function createRpcResult<T, U>(result: Struct<T, U>) { return union([ pick({ jsonrpc: literal('2.0'), id: string(), result, }), pick({ jsonrpc: literal('2.0'), id: string(), error: pick({ code: unknown(), message: string(), data: optional(any()), }), }), ]); } const UnknownRpcResult = createRpcResult(unknown()); /** * @internal */ function jsonRpcResult<T, U>(schema: Struct<T, U>) { return coerce(createRpcResult(schema), UnknownRpcResult, value => { if ('error' in value) { return value; } else { return { ...value, result: create(value.result, schema), }; } }); } /** * @internal */ function jsonRpcResultAndContext<T, U>(value: Struct<T, U>) { return jsonRpcResult( pick({ context: pick({ slot: number(), }), value, }), ); } /** * @internal */ function notificationResultAndContext<T, U>(value: Struct<T, U>) { return pick({ context: pick({ slot: number(), }), value, }); } /** * @internal */ function versionedMessageFromResponse( version: TransactionVersion | undefined, response: MessageResponse, ): VersionedMessage { if (version === 0) { return new MessageV0({ header: response.header, staticAccountKeys: response.accountKeys.map( accountKey => new PublicKey(accountKey), ), recentBlockhash: response.recentBlockhash, compiledInstructions: response.instructions.map(ix => ({ programIdIndex: ix.programIdIndex, accountKeyIndexes: ix.accounts, data: bs58.decode(ix.data), })), addressTableLookups: response.addressTableLookups!, }); } else { return new Message(response); } } /** * The level of commitment desired when querying state * <pre> * 'processed': Query the most recent block which has reached 1 confirmation by the connected node * 'confirmed': Query the most recent block which has reached 1 confirmation by the cluster * 'finalized': Query the most recent block which has been finalized by the cluster * </pre> */ export type Commitment = | 'processed' | 'confirmed' | 'finalized' | 'recent' // Deprecated as of v1.5.5 | 'single' // Deprecated as of v1.5.5 | 'singleGossip' // Deprecated as of v1.5.5 | 'root' // Deprecated as of v1.5.5 | 'max'; // Deprecated as of v1.5.5 /** * A subset of Commitment levels, which are at least optimistically confirmed * <pre> * 'confirmed': Query the most recent block which has reached 1 confirmation by the cluster * 'finalized': Query the most recent block which has been finalized by the cluster * </pre> */ export type Finality = 'confirmed' | 'finalized'; /** * Filter for largest accounts query * <pre> * 'circulating': Return the largest accounts that are part of the circulating supply * 'nonCirculating': Return the largest accounts that are not part of the circulating supply * </pre> */ export type LargestAccountsFilter = 'circulating' | 'nonCirculating'; /** * Configuration object for changing `getAccountInfo` query behavior */ export type GetAccountInfoConfig = { /** The level of commitment desired */ commitment?: Commitment; /** The minimum slot that the request can be evaluated at */ minContextSlot?: number; /** Optional data slice to limit the returned account data */ dataSlice?: DataSlice; }; /** * Configuration object for changing `getBalance` query behavior */ export type GetBalanceConfig = { /** The level of commitment desired */ commitment?: Commitment; /** The minimum slot that the request can be evaluated at */ minContextSlot?: number; }; /** * Configuration object for changing `getBlock` query behavior */ export type GetBlockConfig = { /** The level of finality desired */ commitment?: Finality; /** * Whether to populate the rewards array. If parameter not provided, the default includes rewards. */ rewards?: boolean; /** * Level of transaction detail to return, either "full", "accounts", "signatures", or "none". If * parameter not provided, the default detail level is "full". If "accounts" are requested, * transaction details only include signatures and an annotated list of accounts in each * transaction. Transaction metadata is limited to only: fee, err, pre_balances, post_balances, * pre_token_balances, and post_token_balances. */ transactionDetails?: 'accounts' | 'full' | 'none' | 'signatures'; }; /** * Configuration object for changing `getBlock` query behavior */ export type GetVersionedBlockConfig = { /** The level of finality desired */ commitment?: Finality; /** The max transaction version to return in responses. If the requested transaction is a higher version, an error will be returned */ maxSupportedTransactionVersion?: number; /** * Whether to populate the rewards array. If parameter not provided, the default includes rewards. */ rewards?: boolean; /** * Level of transaction detail to return, either "full", "accounts", "signatures", or "none". If * parameter not provided, the default detail level is "full". If "accounts" are requested, * transaction details only include signatures and an annotated list of accounts in each * transaction. Transaction metadata is limited to only: fee, err, pre_balances, post_balances, * pre_token_balances, and post_token_balances. */ transactionDetails?: 'accounts' | 'full' | 'none' | 'signatures'; }; /** * Configuration object for changing `getStakeMinimumDelegation` query behavior */ export type GetStakeMinimumDelegationConfig = { /** The level of commitment desired */ commitment?: Commitment; }; /** * Configuration object for changing `getBlockHeight` query behavior */ export type GetBlockHeightConfig = { /** The level of commitment desired */ commitment?: Commitment; /** The minimum slot that the request can be evaluated at */ minContextSlot?: number; }; /** * Configuration object for changing `getEpochInfo` query behavior */ export type GetEpochInfoConfig = { /** The level of commitment desired */ commitment?: Commitment; /** The minimum slot that the request can be evaluated at */ minContextSlot?: number; }; /** * Configuration object for changing `getInflationReward` query behavior */ export type GetInflationRewardConfig = { /** The level of commitment desired */ commitment?: Commitment; /** An epoch for which the reward occurs. If omitted, the previous epoch will be used */ epoch?: number; /** The minimum slot that the request can be evaluated at */ minContextSlot?: number; }; /** * Configuration object for changing `getLatestBlockhash` query behavior */ export type GetLatestBlockhashConfig = { /** The level of commitment desired */ commitment?: Commitment; /** The minimum slot that the request can be evaluated at */ minContextSlot?: number; }; /** * Configuration object for changing `getSlot` query behavior */ export type GetSlotConfig = { /** The level of commitment desired */ commitment?: Commitment; /** The minimum slot that the request can be evaluated at */ minContextSlot?: number; }; /** * Configuration object for changing `getSlotLeader` query behavior */ export type GetSlotLeaderConfig = { /** The level of commitment desired */ commitment?: Commitment; /** The minimum slot that the request can be evaluated at */ minContextSlot?: number; }; /** * Configuration object for changing `getTransaction` query behavior */ export type GetTransactionConfig = { /** The level of finality desired */ commitment?: Finality; }; /** * Configuration object for changing `getTransaction` query behavior */ export type GetVersionedTransactionConfig = { /** The level of finality desired */ commitment?: Finality; /** The max transaction version to return in responses. If the requested transaction is a higher version, an error will be returned */ maxSupportedTransactionVersion?: number; }; /** * Configuration object for changing `getLargestAccounts` query behavior */ export type GetLargestAccountsConfig = { /** The level of commitment desired */ commitment?: Commitment; /** Filter largest accounts by whether they are part of the circulating supply */ filter?: LargestAccountsFilter; }; /** * Configuration object for changing `getSupply` request behavior */ export type GetSupplyConfig = { /** The level of commitment desired */ commitment?: Commitment; /** Exclude non circulating accounts list from response */ excludeNonCirculatingAccountsList?: boolean; }; /** * Configuration object for changing query behavior */ export type SignatureStatusConfig = { /** enable searching status history, not needed for recent transactions */ searchTransactionHistory: boolean; }; /** * Information describing a cluster node */ export type ContactInfo = { /** Identity public key of the node */ pubkey: string; /** Gossip network address for the node */ gossip: string | null; /** TPU network address for the node (null if not available) */ tpu: string | null; /** JSON RPC network address for the node (null if not available) */ rpc: string | null; /** Software version of the node (null if not available) */ version: string | null; }; /** * Information describing a vote account */ export type VoteAccountInfo = { /** Public key of the vote account */ votePubkey: string; /** Identity public key of the node voting with this account */ nodePubkey: string; /** The stake, in daltons, delegated to this vote account and activated */ activatedStake: number; /** Whether the vote account is staked for this epoch */ epochVoteAccount: boolean; /** Recent epoch voting credit history for this voter */ epochCredits: Array<[number, number, number]>; /** A percentage (0-100) of rewards payout owed to the voter */ commission: number; /** Most recent slot voted on by this vote account */ lastVote: number; }; /** * A collection of cluster vote accounts */ export type VoteAccountStatus = { /** Active vote accounts */ current: Array<VoteAccountInfo>; /** Inactive vote accounts */ delinquent: Array<VoteAccountInfo>; }; /** * Network Inflation * (see https://docs.bbachain.com/implemented-proposals/ed_overview) */ export type InflationGovernor = { foundation: number; foundationTerm: number; initial: number; taper: number; terminal: number; }; const GetInflationGovernorResult = pick({ foundation: number(), foundationTerm: number(), initial: number(), taper: number(), terminal: number(), }); /** * The inflation reward for an epoch */ export type InflationReward = { /** epoch for which the reward occurs */ epoch: number; /** the slot in which the rewards are effective */ effectiveSlot: number; /** reward amount in daltons */ amount: number; /** post balance of the account in daltons */ postBalance: number; /** vote account commission when the reward was credited */ commission?: number | null; }; /** * Expected JSON RPC response for the "getInflationReward" message */ const GetInflationRewardResult = jsonRpcResult( array( nullable( pick({ epoch: number(), effectiveSlot: number(), amount: number(), postBalance: number(), commission: optional(nullable(number())), }), ), ), ); export type InflationRate = { /** total inflation */ total: number; /** inflation allocated to validators */ validator: number; /** inflation allocated to the foundation */ foundation: number; /** epoch for which these values are valid */ epoch: number; }; /** * Expected JSON RPC response for the "getInflationRate" message */ const GetInflationRateResult = pick({ total: number(), validator: number(), foundation: number(), epoch: number(), }); /** * Information about the current epoch */ export type EpochInfo = { epoch: number; slotIndex: number; slotsInEpoch: number; absoluteSlot: number; blockHeight?: number; transactionCount?: number; }; const GetEpochInfoResult = pick({ epoch: number(), slotIndex: number(), slotsInEpoch: number(), absoluteSlot: number(), blockHeight: optional(number()), transactionCount: optional(number()), }); const GetEpochScheduleResult = pick({ slotsPerEpoch: number(), leaderScheduleSlotOffset: number(), warmup: boolean(), firstNormalEpoch: number(), firstNormalSlot: number(), }); /** * Leader schedule * (see https://docs.bbachain.com/terminology#leader-schedule) */ export type LeaderSchedule = { [address: string]: number[]; }; const GetLeaderScheduleResult = record(string(), array(number())); /** * Transaction error or null */ const TransactionErrorResult = nullable(union([pick({}), string()])); /** * Signature status for a transaction */ const SignatureStatusResult = pick({ err: TransactionErrorResult, }); /** * Transaction signature received notification */ const SignatureReceivedResult = literal('receivedSignature'); /** * Version info for a node */ export type Version = { /** Version of solana-core */ 'solana-core': string; 'feature-set'?: number; }; const VersionResult = pick({ 'solana-core': string(), 'feature-set': optional(number()), }); export type SimulatedTransactionAccountInfo = { /** `true` if this account's data contains a loaded program */ executable: boolean; /** Identifier of the program that owns the account */ owner: string; /** Number of daltons assigned to the account */ daltons: number; /** Optional data assigned to the account */ data: string[]; /** Optional rent epoch info for account */ rentEpoch?: number; }; export type TransactionReturnDataEncoding = 'base64'; export type TransactionReturnData = { programId: string; data: [string, TransactionReturnDataEncoding]; }; export type SimulateTransactionConfig = { /** Optional parameter used to enable signature verification before simulation */ sigVerify?: boolean; /** Optional parameter used to replace the simulated transaction's recent blockhash with the latest blockhash */ replaceRecentBlockhash?: boolean; /** Optional parameter used to set the commitment level when selecting the latest block */ commitment?: Commitment; /** Optional parameter used to specify a list of account addresses to return post simulation state for */ accounts?: { encoding: 'base64'; addresses: string[]; }; /** Optional parameter used to specify the minimum block slot that can be used for simulation */ minContextSlot?: number; }; export type SimulatedTransactionResponse = { err: TransactionError | string | null; logs: Array<string> | null; accounts?: (SimulatedTransactionAccountInfo | null)[] | null; unitsConsumed?: number; returnData?: TransactionReturnData | null; }; const SimulatedTransactionResponseStruct = jsonRpcResultAndContext( pick({ err: nullable(union([pick({}), string()])), logs: nullable(array(string())), accounts: optional( nullable( array( nullable( pick({ executable: boolean(), owner: string(), daltons: number(), data: array(string()), rentEpoch: optional(number()), }), ), ), ), ), unitsConsumed: optional(number()), returnData: optional( nullable( pick({ programId: string(), data: tuple([string(), literal('base64')]), }), ), ), }), ); export type ParsedInnerInstruction = { index: number; instructions: (ParsedInstruction | PartiallyDecodedInstruction)[]; }; export type TokenBalance = { accountIndex: number; mint: string; owner?: string; uiTokenAmount: TokenAmount; }; /** * Metadata for a parsed confirmed transaction on the ledger * * @deprecated Deprecated since BBAChain v1.8.0. Please use {@link ParsedTransactionMeta} instead. */ export type ParsedConfirmedTransactionMeta = ParsedTransactionMeta; /** * Collection of addresses loaded by a transaction using address table lookups */ export type LoadedAddresses = { writable: Array<PublicKey>; readonly: Array<PublicKey>; }; /** * Metadata for a parsed transaction on the ledger */ export type ParsedTransactionMeta = { /** The fee charged for processing the transaction */ fee: number; /** An array of cross program invoked parsed instructions */ innerInstructions?: ParsedInnerInstruction[] | null; /** The balances of the transaction accounts before processing */ preBalances: Array<number>; /** The balances of the transaction accounts after processing */ postBalances: Array<number>; /** An array of program log messages emitted during a transaction */ logMessages?: Array<string> | null; /** The token balances of the transaction accounts before processing */ preTokenBalances?: Array<TokenBalance> | null; /** The token balances of the transaction accounts after processing */ postTokenBalances?: Array<TokenBalance> | null; /** The error result of transaction processing */ err: TransactionError | null; /** The collection of addresses loaded using address lookup tables */ loadedAddresses?: LoadedAddresses; /** The compute units consumed after processing the transaction */ computeUnitsConsumed?: number; }; export type CompiledInnerInstruction = { index: number; instructions: CompiledInstruction[]; }; /** * Metadata for a confirmed transaction on the ledger */ export type ConfirmedTransactionMeta = { /** The fee charged for processing the transaction */ fee: number; /** An array of cross program invoked instructions */ innerInstructions?: CompiledInnerInstruction[] | null; /** The balances of the transaction accounts before processing */ preBalances: Array<number>; /** The balances of the transaction accounts after processing */ postBalances: Array<number>; /** An array of program log messages emitted during a transaction */ logMessages?: Array<string> | null; /** The token balances of the transaction accounts before processing */ preTokenBalances?: Array<TokenBalance> | null; /** The token balances of the transaction accounts after processing */ postTokenBalances?: Array<TokenBalance> | null; /** The error result of transaction processing */ err: TransactionError | null; /** The collection of addresses loaded using address lookup tables */ loadedAddresses?: LoadedAddresses; /** The compute units consumed after processing the transaction */ computeUnitsConsumed?: number; }; /** * A processed transaction from the RPC API */ export type TransactionResponse = { /** The slot during which the transaction was processed */ slot: number; /** The transaction */ transaction: { /** The transaction message */ message: Message; /** The transaction signatures */ signatures: string[]; }; /** Metadata produced from the transaction */ meta: ConfirmedTransactionMeta | null; /** The unix timestamp of when the transaction was processed */ blockTime?: number | null; }; /** * A processed transaction from the RPC API */ export type VersionedTransactionResponse = { /** The slot during which the transaction was processed */ slot: number; /** The transaction */ transaction: { /** The transaction message */ message: VersionedMessage; /** The transaction signatures */ signatures: string[]; }; /** Metadata produced from the transaction */ meta: ConfirmedTransactionMeta | null; /** The unix timestamp of when the transaction was processed */ blockTime?: number | null; /** The transaction version */ version?: TransactionVersion; }; /** * A processed transaction message from the RPC API */ type MessageResponse = { accountKeys: string[]; header: MessageHeader; instructions: CompiledInstruction[]; recentBlockhash: string; addressTableLookups?: ParsedAddressTableLookup[]; }; /** * A confirmed transaction on the ledger * * @deprecated Deprecated since BBAChain v1.8.0. */ export type ConfirmedTransaction = { /** The slot during which the transaction was processed */ slot: number; /** The details of the transaction */ transaction: Transaction; /** Metadata produced from the transaction */ meta: ConfirmedTransactionMeta | null; /** The unix timestamp of when the transaction was processed */ blockTime?: number | null; }; /** * A partially decoded transaction instruction */ export type PartiallyDecodedInstruction = { /** Program id called by this instruction */ programId: PublicKey; /** Public keys of accounts passed to this instruction */ accounts: Array<PublicKey>; /** Raw base-58 instruction data */ data: string; }; /** * A parsed transaction message account */ export type ParsedMessageAccount = { /** Public key of the account */ pubkey: PublicKey; /** Indicates if the account signed the transaction */ signer: boolean; /** Indicates if the account is writable for this transaction */ writable: boolean; /** Indicates if the account key came from the transaction or a lookup table */ source?: 'transaction' | 'lookupTable'; }; /** * A parsed transaction instruction */ export type ParsedInstruction = { /** Name of the program for this instruction */ program: string; /** ID of the program for this instruction */ programId: PublicKey; /** Parsed instruction info */ parsed: any; }; /** * A parsed address table lookup */ export type ParsedAddressTableLookup = { /** Address lookup table account key */ accountKey: PublicKey; /** Parsed instruction info */ writableIndexes: number[]; /** Parsed instruction info */ readonlyIndexes: number[]; }; /** * A parsed transaction message */ export type ParsedMessage = { /** Accounts used in the instructions */ accountKeys: ParsedMessageAccount[]; /** The atomically executed instructions for the transaction */ instructions: (ParsedInstruction | PartiallyDecodedInstruction)[]; /** Recent blockhash */ recentBlockhash: string; /** Address table lookups used to load additional accounts */ addressTableLookups?: ParsedAddressTableLookup[] | null; }; /** * A parsed transaction */ export type ParsedTransaction = { /** Signatures for the transaction */ signatures: Array<string>; /** Message of the transaction */ message: ParsedMessage; }; /** * A parsed and confirmed transaction on the ledger * * @deprecated Deprecated since BBAChain v1.8.0. Please use {@link ParsedTransactionWithMeta} instead. */ export type ParsedConfirmedTransaction = ParsedTransactionWithMeta; /** * A parsed transaction on the ledger with meta */ export type ParsedTransactionWithMeta = { /** The slot during which the transaction was processed */ slot: number; /** The details of the transaction */ transaction: ParsedTransaction; /** Metadata produced from the transaction */ meta: ParsedTransactionMeta | null; /** The unix timestamp of when the transaction was processed */ blockTime?: number | null; /** The version of the transaction message */ version?: TransactionVersion; }; /** * A processed block fetched from the RPC API */ export type BlockResponse = { /** Blockhash of this block */ blockhash: Blockhash; /** Blockhash of this block's parent */ previousBlockhash: Blockhash; /** Slot index of this block's parent */ parentSlot: number; /** Vector of transactions with status meta and original message */ transactions: Array<{ /** The transaction */ transaction: { /** The transaction message */ message: Message; /** The transaction signatures */ signatures: string[]; }; /** Metadata produced from the transaction */ meta: ConfirmedTransactionMeta | null; /** The transaction version */ version?: TransactionVersion; }>; /** Vector of block rewards */ rewards?: Array<{ /** Public key of reward recipient */ pubkey: string; /** Reward value in daltons */ daltons: number; /** Account balance after reward is applied */ postBalance: number | null; /** Type of reward received */ rewardType: string | null; /** Vote account commission when the reward was credited, only present for voting and staking rewards */ commission?: number | null; }>; /** The unix timestamp of when the block was processed */ blockTime: number | null; }; /** * A processed block fetched from the RPC API where the `transactionDetails` mode is `accounts` */ export type AccountsModeBlockResponse = VersionedAccountsModeBlockResponse; /** * A processed block fetched from the RPC API where the `transactionDetails` mode is `none` */ export type NoneModeBlockResponse = VersionedNoneModeBlockResponse; /** * A block with parsed transactions */ export type ParsedBlockResponse = { /** Blockhash of this block */ blockhash: Blockhash; /** Blockhash of this block's parent */ previousBlockhash: Blockhash; /** Slot index of this block's parent */ parentSlot: number; /** Vector of transactions with status meta and original message */ transactions: Array<{ /** The details of the transaction */ transaction: ParsedTransaction; /** Metadata produced from the transaction */ meta: ParsedTransactionMeta | null; /** The transaction version */ version?: TransactionVersion; }>; /** Vector of block rewards */ rewards?: Array<{ /** Public key of reward recipient */ pubkey: string; /** Reward value in daltons */ daltons: number; /** Account balance after reward is applied */ postBalance: number | null; /** Type of reward received */ rewardType: string | null; /** Vote account commission when the reward was credited, only present for voting and staking rewards */ commission?: number | null; }>; /** The unix timestamp of when the block was processed */ blockTime: number | null; /** The number of blocks beneath this block */ blockHeight: number | null; }; /** * A block with parsed transactions where the `transactionDetails` mode is `accounts` */ export type ParsedAccountsModeBlockResponse = Omit< ParsedBlockResponse, 'transactions' > & { transactions: Array< Omit<ParsedBlockResponse['transactions'][number], 'transaction'> & { transaction: Pick< ParsedBlockResponse['transactions'][number]['transaction'], 'signatures' > & { accountKeys: ParsedMessageAccount[]; }; } >; }; /** * A block with parsed transactions where the `transactionDetails` mode is `none` */ export type ParsedNoneModeBlockResponse = Omit< ParsedBlockResponse, 'transactions' >; /** * A processed block fetched from the RPC API */ export type VersionedBlockResponse = { /** Blockhash of this block */ blockhash: Blockhash; /** Blockhash of this block's parent */ previousBlockhash: Blockhash; /** Slot index of this block's parent */ parentSlot: number; /** Vector of transactions with status meta and original message */ transactions: Array<{ /** The transaction */ transaction: { /** The transaction message */ message: VersionedMessage; /** The transaction signatures */ signatures: string[]; }; /** Metadata produced from the transaction */ meta: ConfirmedTransactionMeta | null; /** The transaction version */ version?: TransactionVersion; }>; /** Vector of block rewards */ rewards?: Array<{ /** Public key of reward recipient */ pubkey: string; /** Reward value in daltons */ daltons: number; /** Account balance after reward is applied */ postBalance: number | null; /** Type of reward received */ rewardType: string | null; /** Vote account commission when the reward was credited, only present for voting and staking rewards */ commission?: number | null; }>; /** The unix timestamp of when the block was processed */ blockTime: number | null; }; /** * A processed block fetched from the RPC API where the `transactionDetails` mode is `accounts` */ export type VersionedAccountsModeBlockResponse = Omit< VersionedBlockResponse, 'transactions' > & { transactions: Array< Omit<VersionedBlockResponse['transactions'][number], 'transaction'> & { transaction: Pick< VersionedBlockResponse['transactions'][number]['transaction'], 'signatures' > & { accountKeys: ParsedMessageAccount[]; }; } >; }; /** * A processed block fetched from the RPC API where the `transactionDetails` mode is `none` */ export type VersionedNoneModeBlockResponse = Omit< VersionedBlockResponse, 'transactions' >; /** * A confirmed block on the ledger * * @deprecated Deprecated since BBAChain v1.8.0. */ export type ConfirmedBlock = { /** Blockhash of this block */ blockhash: Blockhash; /** Blockhash of this block's parent */ previousBlockhash: Blockhash; /** Slot index of this block's parent */ parentSlot: number; /** Vector of transactions and status metas */ transactions: Array<{ transaction: Transaction; meta: ConfirmedTransactionMeta | null; }>; /** Vector of block rewards */ rewards?: Array<{ pubkey: string; daltons: number; postBalance: number | null; rewardType: string | null; commission?: number | null; }>; /** The unix timestamp of when the block was processed */ blockTime: number | null; }; /** * A Block on the ledger with signatures only */ export type BlockSignatures = { /** Blockhash of this block */ blockhash: Blockhash; /** Blockhash of this block's parent */ previousBlockhash: Blockhash; /** Slot index of this block's parent */ parentSlot: number; /** Vector of signatures */ signatures: Array<string>; /** The unix timestamp of when the block was processed */ blockTime: number | null; }; /** * recent block production information */ export type BlockProduction = Readonly<{ /** a dictionary of validator identities, as base-58 encoded strings. Value is a two element array containing the number of leader slots and the number of blocks produced */ byIdentity: Readonly<Record<string, ReadonlyArray<number>>>; /** Block production slot range */ range: Readonly<{ /** first slot of the block production information (inclusive) */ firstSlot: number; /** last slot of block production information (inclusive) */ lastSlot: number; }>; }>; export type GetBlockProductionConfig = { /** Optional commitment level */ commitment?: Commitment; /** Slot range to return block production for. If parameter not provided, defaults to current epoch. */ range?: { /** first slot to return block production information for (inclusive) */ firstSlot: number; /** last slot to return block production information for (inclusive). If parameter not provided, defaults to the highest slot */ lastSlot?: number; }; /** Only return results for this validator identity (base-58 encoded) */ identity?: string; }; /** * Expected JSON RPC response for the "getBlockProduction" message */ const BlockProductionResponseStruct = jsonRpcResultAndContext( pick({ byIdentity: record(string(), array(number())), range: pick({ firstSlot: number(), lastSlot: number(), }), }), ); /** * A performance sample */ export type PerfSample = { /** Slot number of sample */ slot: number; /** Number of transactions in a sample window */ numTransactions: number; /** Number of slots in a sample window */ numSlots: number; /** Sample window in seconds */ samplePeriodSecs: number; }; function createRpcClient( url: string, httpHeaders?: HttpHeaders, customFetch?: FetchFn, fetchMiddleware?: FetchMiddleware, disableRetryOnRateLimit?: boolean, httpAgent?: NodeHttpAgent | NodeHttpsAgent | false, ): RpcClient { const fetch = customFetch ? customFetch : fetchImpl; let agent: NodeHttpAgent | NodeHttpsAgent | undefined; if (process.env.BROWSER) { if (httpAgent != null) { console.warn( 'You have supplied an `httpAgent` when creating a `Connection` in a browser environment.' + 'It has been ignored; `httpAgent` is only used in Node environments.', ); } } else { if (httpAgent == null) { if (process.env.NODE_ENV !== 'test') { const agentOptions = { // One second fewer than the BBAChain RPC's keepalive timeout. // Read more: https://github.com/solana-labs/solana/issues/27859#issuecomment-1340097889 freeSocketTimeout: 19000, keepAlive: true, maxSockets: 25, }; if (url.startsWith('https:')) { agent = new HttpsKeepAliveAgent(agentOptions); } else { agent = new HttpKeepAliveAgent(agentOptions); } } } else { if (httpAgent !== false) { const isHttps = url.startsWith('https:'); if (isHttps && !(httpAgent instanceof NodeHttpsAgent)) { throw new Error( 'The endpoint `' + url + '` can only be paired with an `https.Agent`. You have, instead, supplied an ' + '`http.Agent` through `httpAgent`.', ); } else if (!isHttps && httpAgent instanceof NodeHttpsAgent) { throw new Error( 'The endpoint `' + url + '` can only be paired with an `http.Agent`. You have, instead, supplied an ' + '`https.Agent` through `httpAgent`.', ); } agent = httpAgent; } } } let fetchWithMiddleware: FetchFn | undefined; if (fetchMiddleware) { fetchWithMiddleware = async (info, init) => { const modifiedFetchArgs = await new Promise<Parameters<FetchFn>>( (resolve, reject) => { try { fetchMiddleware(info, init, (modifiedInfo, modifiedInit) => resolve([modifiedInfo, modifiedInit]), ); } catch (error) { reject(error); } }, ); return await fetch(...modifiedFetchArgs); }; } const clientBrowser = new RpcClient(async (request, callback) => { const options = { method: 'POST', body: request, agent, headers: Object.assign( { 'Content-Type': 'application/json', }, httpHeaders || {}, COMMON_HTTP_HEADERS, ), }; try { let too_many_requests_retries = 5; let res: Response; let waitTime = 500; for (;;) { if (fetchWithMiddleware) { res = await fetchWithMiddleware(url, options); } else { res = await fetch(url, options); } if (res.status !== 429 /* Too many requests */) { break; } if (disableRetryOnRateLimit === true) { break; } too_many_requests_retries -= 1; if (too_many_requests_retries === 0) { break; } console.log( `Server responded with ${res.status} ${res.statusText}. Retrying after ${waitTime}ms delay...`, ); await sleep(waitTime); waitTime *= 2; } const text = await res.text(); if (res.ok) { callback(null, text); } else { callback(new Error(`${res.status} ${res.statusText}: ${text}`)); } } catch (err) { if (err instanceof Error) callback(err); } }, {}); return clientBrowser; } function createRpcRequest(client: RpcClient): RpcRequest { return (method, args) => { return new Promise((resolve, reject) => { client.request(method, args, (err: any, response: any) => { if (err) { reject(err); return; } resolve(response); }); }); }; } function createRpcBatchRequest(client: RpcClient): RpcBatchRequest { return (requests: RpcParams[]) => { return new Promise((resolve, reject) => { // Do nothing if requests is empty if (requests.length === 0) resolve([]); const batch = requests.map((params: RpcParams) => { return client.request(params.methodName, params.args); }); client.request(batch, (err: any, response: any) => { if (err) { reject(err); return; } resolve(response); }); }); }; } /** * Expected JSON RPC response for the "getInflationGovernor" message */ const GetInflationGovernorRpcResult = jsonRpcResult(GetInflationGovernorResult); /** * Expected JSON RPC response for the "getInflationRate" message */ const GetInflationRateRpcResult = jsonRpcResult(GetInflationRateResult); /** * Expected JSON RPC response for the "getEpochInfo" message */ const GetEpochInfoRpcResult = jsonRpcResult(GetEpochInfoResult); /** * Expected JSON RPC response for the "getEpochSchedule" message */ const GetEpochScheduleRpcResult = jsonRpcResult(GetEpochScheduleResult); /** * Expected JSON RPC response for the "getLeaderSchedule" message */ const GetLeaderScheduleRpcResult = jsonRpcResult(GetLeaderScheduleResult); /** * Expected JSON RPC response for the "minimumLedgerSlot" and "getFirstAvailableBlock" messages */ const SlotRpcResult = jsonRpcResult(number()); /** * Supply */ export type Supply = { /** Total supply in daltons */ total: number; /** Circulating supply in daltons */ circulating: number; /** Non-circulating supply in daltons */ nonCirculating: number; /** List of non-circulating account addresses */ nonCirculatingAccounts: Array<PublicKey>; }; /** * Expected JSON RPC response for the "getSupply" message */ const GetSupplyRpcResult = jsonRpcResultAndContext( pick({ total: number(), circulating: number(), nonCirculating: number(), nonCirculatingAccounts: array(PublicKeyFromString), }), ); /** * Token amount object which returns a token amount in different formats * for various client use cases. */ export type TokenAmount = { /** Raw amount of tokens as string ignoring decimals */ amount: string; /** Number of decimals configured for token's mint */ decimals: number; /** Token amount as float, accounts for decimals */ uiAmount: number | null; /** Token amount as string, accounts for decimals */ uiAmountString?: string; }; /** * Expected JSON RPC structure for token amounts */ const TokenAmountResult = pick({ amount: