mirror of
https://github.com/pezkuwichain/pezkuwi-api.git
synced 2026-10-28 21:49:49 +00:00
Rebrand: polkadot → pezkuwi, substrate → bizinikiwi, kusama → dicle
This commit is contained in:
commit
d5f038faea
1383 files changed
+1088018
No files matched your search
@@ -0,0 +1,13 @@
|
||||
// Copyright 2017-2025 @pezkuwi/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { BN } from '@pezkuwi/util';
|
||||
|
||||
export const FALLBACK_MAX_HASH_COUNT = 250;
|
||||
|
||||
// default here to 5 min eras, adjusted based on the actual blocktime
|
||||
export const FALLBACK_PERIOD = new BN(6 * 1000);
|
||||
|
||||
export const MAX_FINALITY_LAG = new BN(5);
|
||||
|
||||
export const MORTAL_PERIOD = new BN(5 * 60 * 1000);
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2017-2025 @pezkuwi/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { EventRecord, Hash, SignedBlock } from '@pezkuwi/types/interfaces';
|
||||
import type { DeriveApi } from '../types.js';
|
||||
|
||||
import { combineLatest, map, switchMap } from 'rxjs';
|
||||
|
||||
import { memo } from '../util/index.js';
|
||||
|
||||
interface Result {
|
||||
block: SignedBlock;
|
||||
events: EventRecord[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @name events
|
||||
* @param {Hash} at Block hash to query at.
|
||||
* @description Retrieves the block information alongside its events at a given block hash
|
||||
* @example
|
||||
* ```javascript
|
||||
* const blockHash = api.registry.createType(
|
||||
* "Hash",
|
||||
* "0xf1dc2efe8265be67deea5e91b05a98a7f9f81f66854e92825cf36f541beb7af6"
|
||||
* );
|
||||
* const { events, block } = await api.derive.tx.events(blockHash);
|
||||
* ```
|
||||
*/
|
||||
export function events (instanceId: string, api: DeriveApi): (at: Hash) => Observable<Result> {
|
||||
return memo(instanceId, (blockHash: Hash) =>
|
||||
combineLatest([
|
||||
api.rpc.chain.getBlock(blockHash),
|
||||
api.queryAt(blockHash).pipe(
|
||||
switchMap((queryAt) =>
|
||||
queryAt.system.events()
|
||||
)
|
||||
)
|
||||
]).pipe(
|
||||
map(([block, events]) => ({ block, events }))
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
// Copyright 2017-2025 @pezkuwi/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { GenericExtrinsic } from '@pezkuwi/types';
|
||||
import type { EventRecord, Hash } from '@pezkuwi/types/interfaces';
|
||||
import type { DeriveApi } from '../types.js';
|
||||
|
||||
import { map } from 'rxjs';
|
||||
|
||||
import { memo } from '../util/index.js';
|
||||
|
||||
interface ExtrinsicInfo {
|
||||
blockHash: Hash | string,
|
||||
blockNumber: number,
|
||||
extrinsic: GenericExtrinsic,
|
||||
events: EventRecord[]
|
||||
success: boolean
|
||||
}
|
||||
|
||||
interface ExtrinsicsInfo {
|
||||
blockHash: Hash | string,
|
||||
blockNumber: number,
|
||||
extrinsics: {
|
||||
events: EventRecord[]
|
||||
extrinsic: GenericExtrinsic,
|
||||
success: boolean
|
||||
}[],
|
||||
}
|
||||
|
||||
/**
|
||||
* @name extrinsicInfo
|
||||
* @param { Hash } at The block hash to query at.
|
||||
* @param { Uint8Array | string } transactionHash A transaction hash as U8 array or string.
|
||||
* @description Retrieves the extrinsic information and its events.
|
||||
* @example
|
||||
* ```javascript
|
||||
* const blockHash = api.registry.createType(
|
||||
* 'Hash',
|
||||
* '0xb772e4949d2f3eb5ba356aa43f885cc4f9097ee9812c5436543f3846a0491729'
|
||||
* );
|
||||
* const extrinsicInfo = await api.derive.tx.extrinsicInfo(
|
||||
* blockHash,
|
||||
* '0xcd96520b05e0c4648ea365f3f063f27c5cdd8be10d41a1c44566428c91f37dcb'
|
||||
* );
|
||||
*
|
||||
* console.log(extrinsicInfo.extrinsic.toHuman());
|
||||
* ```
|
||||
*/
|
||||
export function extrinsicInfo (instanceId: string, api: DeriveApi): (at: Hash, transactionHash: Uint8Array | string) => Observable<ExtrinsicInfo | null> {
|
||||
return memo(instanceId, (at: Hash, transactionHash: Uint8Array | string): Observable<ExtrinsicInfo | null> => {
|
||||
return api.derive.tx.events(at).pipe(map(({ block, events }) => {
|
||||
const index = block.block.extrinsics.findIndex((ext) => ext.hash.toString() === transactionHash);
|
||||
|
||||
if (index === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const extEvents = events.filter(
|
||||
({ phase }) => phase.isApplyExtrinsic && phase.asApplyExtrinsic.eq(index)
|
||||
);
|
||||
|
||||
return {
|
||||
blockHash: block.hash.toHex(),
|
||||
blockNumber: block.block.header.number.toNumber(),
|
||||
events: extEvents,
|
||||
extrinsic: block.block.extrinsics[index],
|
||||
success: (extEvents.findIndex((ev) => ev.event.method === 'ExtrinsicSuccess') !== -1)
|
||||
};
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name accountExtrinsics
|
||||
* @description Retrieves information about every extrinsic submitted by an account at a given block.
|
||||
* @param { Hash } at The block hash to query at.
|
||||
* @param { Uint8Array | strings } accountId The account identifier to query.
|
||||
* @example
|
||||
* ```javascript
|
||||
const blockHash = api.registry.createType(
|
||||
'Hash',
|
||||
'0xb772e4949d2f3eb5ba356aa43f885cc4f9097ee9812c5436543f3846a0491729'
|
||||
);
|
||||
const extrinsicsInfo = await api.derive.tx.accountExtrinsics(
|
||||
blockHash,
|
||||
'0x21895DdfD4640b4e0aDCa2865b907f2CE6e6B777'
|
||||
);
|
||||
|
||||
console.log(extrinsicsInfo.extrinsics[0]).extrinsic.toHuman();
|
||||
* ```
|
||||
*/
|
||||
export function accountExtrinsics (instanceId: string, api: DeriveApi): (at: Hash, accountId: Uint8Array | string) => Observable<ExtrinsicsInfo> {
|
||||
return memo(instanceId, (at: Hash, accountId: Uint8Array | string): Observable<ExtrinsicsInfo> => {
|
||||
return api.derive.tx.events(at).pipe(map(({ block, events }) => {
|
||||
const indexes: number[] = [];
|
||||
|
||||
return {
|
||||
blockHash: block.hash.toHex(),
|
||||
blockNumber: block.block.header.number.toNumber(),
|
||||
extrinsics: block.block.extrinsics.filter((ext, index) => {
|
||||
if (ext.signer.toString() === accountId) {
|
||||
indexes.push(index);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}).map((ext, i) => {
|
||||
const extEvents = events.filter(
|
||||
({ phase }) => phase.isApplyExtrinsic && phase.asApplyExtrinsic.eq(indexes[i])
|
||||
);
|
||||
|
||||
return {
|
||||
events: extEvents,
|
||||
extrinsic: ext,
|
||||
success: (extEvents.findIndex((ev) => ev.event.method === 'ExtrinsicSuccess') !== -1)
|
||||
};
|
||||
})
|
||||
};
|
||||
}));
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2017-2025 @pezkuwi/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export * from './events.js';
|
||||
export * from './extrinsicInfo.js';
|
||||
export * from './signingInfo.js';
|
||||
@@ -0,0 +1,129 @@
|
||||
// Copyright 2017-2025 @pezkuwi/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { AugmentedConsts } from '@pezkuwi/api-base/types';
|
||||
import type { u64 } from '@pezkuwi/types';
|
||||
import type { Header, Index } from '@pezkuwi/types/interfaces';
|
||||
import type { AnyNumber, Codec, IExtrinsicEra } from '@pezkuwi/types/types';
|
||||
import type { BN } from '@pezkuwi/util';
|
||||
import type { DeriveApi } from '../types.js';
|
||||
|
||||
import { catchError, combineLatest, map, of, switchMap } from 'rxjs';
|
||||
|
||||
import { isNumber, isUndefined } from '@pezkuwi/util';
|
||||
|
||||
import { unwrapBlockNumber } from '../util/index.js';
|
||||
import { FALLBACK_MAX_HASH_COUNT, FALLBACK_PERIOD, MAX_FINALITY_LAG, MORTAL_PERIOD } from './constants.js';
|
||||
|
||||
interface Result {
|
||||
header: Header | null;
|
||||
mortalLength: number;
|
||||
nonce: Index;
|
||||
}
|
||||
|
||||
function latestNonce (api: DeriveApi, address: string): Observable<Index> {
|
||||
return api.derive.balances.account(address).pipe(
|
||||
map(({ accountNonce }) => accountNonce)
|
||||
);
|
||||
}
|
||||
|
||||
function nextNonce (api: DeriveApi, address: string): Observable<Index> {
|
||||
if (api.call.accountNonceApi) {
|
||||
return api.call.accountNonceApi.accountNonce(address);
|
||||
} else {
|
||||
return api.rpc.system?.accountNextIndex
|
||||
? api.rpc.system.accountNextIndex(address)
|
||||
: latestNonce(api, address);
|
||||
}
|
||||
}
|
||||
|
||||
function signingHeader (api: DeriveApi): Observable<Header> {
|
||||
return combineLatest([
|
||||
api.rpc.chain.getHeader().pipe(
|
||||
switchMap((header) =>
|
||||
// check for chains at genesis (until block 1 is produced, e.g. 6s), since
|
||||
// we do need to allow transactions at chain start (also dev/seal chains)
|
||||
header.parentHash.isEmpty
|
||||
? of(header)
|
||||
// in the case of the current block, we use the parent to minimize the
|
||||
// impact of forks on the system, but not completely remove it
|
||||
: api.rpc.chain.getHeader(header.parentHash).pipe(
|
||||
catchError(() => of(header))
|
||||
)
|
||||
)
|
||||
),
|
||||
api.rpc.chain.getFinalizedHead().pipe(
|
||||
switchMap((hash) =>
|
||||
api.rpc.chain.getHeader(hash).pipe(
|
||||
catchError(() => of(null))
|
||||
)
|
||||
)
|
||||
)
|
||||
]).pipe(
|
||||
map(([current, finalized]) =>
|
||||
// determine the hash to use, current when lag > max, else finalized
|
||||
!finalized || unwrapBlockNumber(current).sub(unwrapBlockNumber(finalized)).gt(MAX_FINALITY_LAG)
|
||||
? current
|
||||
: finalized
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
interface Aura {
|
||||
slotDuration: u64 & AugmentedConsts<'rxjs'>;
|
||||
}
|
||||
|
||||
function babeOrAuraPeriod (api: DeriveApi): BN | undefined {
|
||||
const period = api.consts.babe?.expectedBlockTime ||
|
||||
// this will be present ones https://github.com/pezkuwichain/pezkuwi-sdk/pull/3732 is merged
|
||||
|
||||
// eslint-disable-next-line
|
||||
(api.consts['aura'] as unknown as Aura)?.['slotDuration'] ||
|
||||
api.consts.timestamp?.minimumPeriod.muln(2);
|
||||
|
||||
return period && period.isZero && !period.isZero() ? period : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name signingInfo
|
||||
* @description Retrieves signing-related information for an account, including the nonce, block header, and mortal length.
|
||||
* @param {string} address The account address for which signing information is needed.
|
||||
* @param { BN | bigint | Uint8Array | number | string } nonce? (Optional) The nonce to use. If `undefined`, the latest nonce is retrieved.
|
||||
* @param { IExtrinsicEra | number } era? (Optional) The transaction era.
|
||||
* @example
|
||||
* ```javascript
|
||||
* const info = await api.derive.tx.signingInfo(
|
||||
* "14mM9FRDDtwSYicjNxSvMfQkap8o4m9zHq7hNW4JpbSL4PPU"
|
||||
* );
|
||||
* console.log(info);
|
||||
* ```
|
||||
*/
|
||||
export function signingInfo (_instanceId: string, api: DeriveApi): (address: string, nonce?: AnyNumber | Codec, era?: IExtrinsicEra | number) => Observable<Result> {
|
||||
// no memo, we want to do this fresh on each run
|
||||
return (address: string, nonce?: AnyNumber | Codec, era?: IExtrinsicEra | number): Observable<Result> =>
|
||||
combineLatest([
|
||||
// retrieve nonce if none was specified
|
||||
isUndefined(nonce)
|
||||
? latestNonce(api, address)
|
||||
: nonce === -1
|
||||
? nextNonce(api, address)
|
||||
: of(api.registry.createType('Index', nonce)),
|
||||
// if no era (create) or era > 0 (mortal), do block retrieval
|
||||
(isUndefined(era) || (isNumber(era) && era > 0))
|
||||
? signingHeader(api)
|
||||
: of(null)
|
||||
]).pipe(
|
||||
map(([nonce, header]) => ({
|
||||
header,
|
||||
mortalLength: Math.min(
|
||||
api.consts.system?.blockHashCount?.toNumber() || FALLBACK_MAX_HASH_COUNT,
|
||||
MORTAL_PERIOD
|
||||
.div(babeOrAuraPeriod(api) || FALLBACK_PERIOD)
|
||||
.iadd(MAX_FINALITY_LAG)
|
||||
.toNumber()
|
||||
),
|
||||
nonce
|
||||
}))
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user