feat: initial Pezkuwi Apps rebrand from polkadot-apps

Rebranded terminology:
- Polkadot → Pezkuwi
- Kusama → Dicle
- Westend → Zagros
- Rococo → PezkuwiChain
- Substrate → Bizinikiwi
- parachain → teyrchain

Custom logos with Kurdistan brand colors (#e6007a → #86e62a):
- bizinikiwi-hexagon.svg
- sora-bizinikiwi.svg
- hezscanner.svg
- heztreasury.svg
- pezkuwiscan.svg
- pezkuwistats.svg
- pezkuwiassembly.svg
- pezkuwiholic.svg
This commit is contained in:
2026-01-07 13:05:27 +03:00
commit d21bfb1320
5867 changed files with 329019 additions and 0 deletions
@@ -0,0 +1,83 @@
// Copyright 2017-2025 @pezkuwi/app-teyrchains authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ParaId } from '@pezkuwi/types/interfaces';
import type { BN } from '@pezkuwi/util';
import type { OwnedId } from '../types.js';
import React from 'react';
import { Button } from '@pezkuwi/react-components';
import { useApi, useCall, useToggle } from '@pezkuwi/react-hooks';
import { useTranslation } from '../translate.js';
import { LOWEST_PUBLIC_ID } from './constants.js';
import DeregisterId from './DeregisterId.js';
import RegisterId from './RegisterId.js';
import RegisterThread from './RegisterThread.js';
interface Props {
className?: string;
ownedIds: OwnedId[];
}
const OPT_NEXT = {
transform: (nextId: ParaId) =>
nextId.isZero()
? LOWEST_PUBLIC_ID
: nextId
};
function Actions ({ className, ownedIds }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const [isRegisterOpen, toggleRegisterOpen] = useToggle();
const [isReserveOpen, toggleReserveOpen] = useToggle();
const [isDeregisterOpen, toggleDeregisterOpen] = useToggle();
const nextParaId = useCall<ParaId | BN>(api.query.registrar.nextFreeParaId, [], OPT_NEXT);
return (
<Button.Group className={className}>
<Button
icon='minus'
isDisabled={api.tx.registrar.deregister ? !ownedIds.length : false}
label={t('Deregister')}
onClick={toggleDeregisterOpen}
/>
{isDeregisterOpen && (
<DeregisterId
nextParaId={nextParaId}
onClose={toggleDeregisterOpen}
ownedIds={ownedIds}
/>
)}
<Button
icon='plus'
isDisabled={!api.tx.registrar.reserve}
label={t('ParaId')}
onClick={toggleReserveOpen}
/>
{isReserveOpen && (
<RegisterId
nextParaId={nextParaId}
onClose={toggleReserveOpen}
/>
)}
<Button
icon='plus'
isDisabled={api.tx.registrar.reserve ? !ownedIds.length : false}
label={t('ParaThread')}
onClick={toggleRegisterOpen}
/>
{isRegisterOpen && (
<RegisterThread
nextParaId={nextParaId}
onClose={toggleRegisterOpen}
ownedIds={ownedIds}
/>
)}
</Button.Group>
);
}
export default React.memo(Actions);
@@ -0,0 +1,124 @@
// Copyright 2017-2025 @pezkuwi/app-teyrchains authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { OwnedId, OwnerInfo } from '../types.js';
import BN from 'bn.js';
import React, { useCallback, useMemo, useState } from 'react';
import { InputAddress, InputNumber, MarkWarning, Modal, styled, TxButton } from '@pezkuwi/react-components';
import { useApi } from '@pezkuwi/react-hooks';
import { CallExpander } from '@pezkuwi/react-params';
import InputOwner from '../InputOwner.js';
import { useTranslation } from '../translate.js';
import { LOWEST_INVALID_ID } from './constants.js';
interface Props {
className?: string;
onClose: () => void;
nextParaId?: BN;
ownedIds: OwnedId[];
}
function DeregisterId ({ className, nextParaId, onClose, ownedIds }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const [accountId, setAccountId] = useState<string | null>(null);
const [paraId, setParaId] = useState<BN | undefined>();
const _setOwner = useCallback(
({ accountId, paraId }: OwnerInfo) => {
setAccountId(accountId);
setParaId(new BN(paraId));
},
[]
);
const isIdError = !paraId || !paraId.gt(LOWEST_INVALID_ID);
const extrinsic = useMemo(() => api.tx.registrar.deregister(paraId), [api.tx.registrar, paraId]);
return (
<Modal
className={className}
header={t('Deregister ParaId')}
onClose={onClose}
size='large'
>
<Modal.Content>
{api.tx.registrar.reserve
? (
<InputOwner
noCodeCheck
onChange={_setOwner}
ownedIds={ownedIds}
/>
)
: (
<>
<Modal.Columns hint={t('This account will be associated with the teyrchain and pay the deposit.')}>
<InputAddress
label={t('register from')}
onChange={setAccountId}
type='account'
value={accountId}
/>
</Modal.Columns>
<Modal.Columns hint={t('The id of this teyrchain as known on the network')}>
<InputNumber
autoFocus
defaultValue={nextParaId}
isError={isIdError}
isZeroable={false}
label={t('teyrchain id')}
onChange={setParaId}
/>
</Modal.Columns>
</>
)
}
<Modal.Columns>
<CallExpander
isExpanded
isHeader
value={extrinsic}
/>
</Modal.Columns>
<Modal.Columns>
<MarkWarning withIcon={false}>
<strong>{t('Deregistering a paraID will:')}</strong>
<WarningList>
<WarningItem>{t('Remove it from the active teyrchain/parathread set.')}</WarningItem>
<WarningItem>{t('Exclude it from future auctions and onboarding.')}</WarningItem>
<WarningItem>{t('Potentially release any reserved deposits linked to it.')}</WarningItem>
<WarningItem>{t('Require re-registration if you wish to use it again.')}</WarningItem>
</WarningList>
<p><strong>{t('This action is permanent.')}</strong> {t('Please ensure this is your intended action before continuing.')}</p>
</MarkWarning>
</Modal.Columns>
</Modal.Content>
<Modal.Actions>
<TxButton
accountId={accountId}
extrinsic={extrinsic}
icon='minus'
isDisabled={isIdError}
label={t('Deregister')}
onStart={onClose}
/>
</Modal.Actions>
</Modal>
);
}
const WarningList = styled.ul`
padding-left: 20px;
margin: 8px 0;
`;
const WarningItem = styled.li`
margin-bottom: 2px;
`;
export default React.memo(DeregisterId);
@@ -0,0 +1,83 @@
// Copyright 2017-2025 @pezkuwi/app-teyrchains authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ParaId } from '@pezkuwi/types/interfaces';
import type { LeaseInfo, LeasePeriod, QueuedAction } from '../types.js';
import React, { useMemo } from 'react';
import { AddressSmall, ParaLink, Table, TxButton } from '@pezkuwi/react-components';
import { useAccounts, useApi } from '@pezkuwi/react-hooks';
import Lifecycle from '../Overview/Lifecycle.js';
// import TeyrchainInfo from '../Overview/TeyrchainInfo.js';
import Periods from '../Overview/Periods.js';
import { useTranslation } from '../translate.js';
import useThreadInfo from './useThreadInfo.js';
interface Props {
id: ParaId;
leasePeriod: LeasePeriod;
leases: LeaseInfo[];
nextAction?: QueuedAction;
}
function Parathread ({ id, leasePeriod, leases, nextAction }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const { isAccount } = useAccounts();
const { headHex, lifecycle, manager } = useThreadInfo(id);
const periods = useMemo(
() => leasePeriod?.currentPeriod && leases?.map(({ period }) => period),
[leasePeriod?.currentPeriod, leases]
);
const isManager = isAccount(manager?.toString());
return (
<tr>
<Table.Column.Id value={id} />
<td className='badge'><ParaLink id={id} /></td>
<td className='address media--2000'>{manager && <AddressSmall value={manager} />}</td>
<td className='start together hash media--1500'>
<div className='shortHash'>{headHex}</div>
</td>
<td className='start'>
<Lifecycle
lifecycle={lifecycle}
nextAction={nextAction}
/>
</td>
<td className='all' />
<td className='number no-pad-left'>
{/* <!-- TeyrchainInfo id={id} /--> */}
</td>
<td className='number together'>
{leasePeriod && leases && periods && (
leases.length
? (
<Periods
fromFirst
leasePeriod={leasePeriod}
periods={periods}
/>
)
: t('None')
)}
</td>
<td className='button media--900'>
<TxButton
accountId={manager}
icon='times'
isDisabled={!isManager}
label={t('Deregister')}
params={[id]}
tx={api.tx.registrar.deregister}
/>
</td>
</tr>
);
}
export default React.memo(Parathread);
@@ -0,0 +1,70 @@
// Copyright 2017-2025 @pezkuwi/app-teyrchains authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { u128 } from '@pezkuwi/types';
import type { BN } from '@pezkuwi/util';
import React, { useState } from 'react';
import { InputAddress, InputBalance, InputNumber, Modal, TxButton } from '@pezkuwi/react-components';
import { useApi } from '@pezkuwi/react-hooks';
import { useTranslation } from '../translate.js';
interface Props {
className?: string;
nextParaId?: BN;
onClose: () => void;
}
function RegisterId ({ className, nextParaId, onClose }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const [accountId, setAccountId] = useState<string | null>(null);
return (
<Modal
className={className}
header={t('Reserve ParaId')}
onClose={onClose}
size='large'
>
<Modal.Content>
<Modal.Columns hint={t('This account will be used to the Id reservation and for the future parathread.')}>
<InputAddress
label={t('reserve from')}
onChange={setAccountId}
type='account'
value={accountId}
/>
</Modal.Columns>
<Modal.Columns hint={t('The Id of this teyrchain as known on the network (selected from nextFreeId)')}>
<InputNumber
defaultValue={nextParaId}
isDisabled
label={t('teyrchain id')}
/>
</Modal.Columns>
<Modal.Columns hint={t('The reservation fee for this Id')}>
<InputBalance
defaultValue={api.consts.registrar.paraDeposit as u128}
isDisabled
label={t('reserved deposit')}
/>
</Modal.Columns>
</Modal.Content>
<Modal.Actions>
<TxButton
accountId={accountId}
icon='plus'
isDisabled={!nextParaId}
onStart={onClose}
params={[]}
tx={api.tx.registrar.reserve}
/>
</Modal.Actions>
</Modal>
);
}
export default React.memo(RegisterId);
@@ -0,0 +1,142 @@
// Copyright 2017-2025 @pezkuwi/app-teyrchains authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { BalanceOf } from '@pezkuwi/types/interfaces';
import type { PezkuwiRuntimeTeyrchainsConfigurationHostConfiguration } from '@pezkuwi/types/lookup';
import type { OwnedId, OwnerInfo } from '../types.js';
import React, { useCallback, useMemo, useState } from 'react';
import { InputAddress, InputBalance, InputFile, InputNumber, Modal, TxButton } from '@pezkuwi/react-components';
import { useApi, useCall } from '@pezkuwi/react-hooks';
import { BN, compactAddLength } from '@pezkuwi/util';
import InputOwner from '../InputOwner.js';
import { useTranslation } from '../translate.js';
import { LOWEST_INVALID_ID } from './constants.js';
interface Props {
className?: string;
nextParaId?: BN;
onClose: () => void;
ownedIds: OwnedId[];
}
function RegisterThread ({ className, nextParaId, onClose, ownedIds }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const [accountId, setAccountId] = useState<string | null>(null);
const [paraId, setParaId] = useState<BN | undefined>();
const [wasm, setWasm] = useState<Uint8Array | null>(null);
const [genesisState, setGenesisState] = useState<Uint8Array | null>(null);
const paraConfig = useCall<PezkuwiRuntimeTeyrchainsConfigurationHostConfiguration>(api.query.configuration?.activeConfig);
const _setGenesisState = useCallback(
(data: Uint8Array) => setGenesisState(compactAddLength(data)),
[]
);
const _setWasm = useCallback(
(data: Uint8Array) => setWasm(compactAddLength(data)),
[]
);
const _setOwner = useCallback(
({ accountId, paraId }: OwnerInfo) => {
setAccountId(accountId);
setParaId(new BN(paraId));
},
[]
);
const reservedDeposit = useMemo(
() => (api.consts.registrar.paraDeposit as BalanceOf)
.add((api.consts.registrar.dataDepositPerByte as BalanceOf).muln(
paraConfig?.maxCodeSize
? paraConfig.maxCodeSize.toNumber()
: wasm
? wasm.length
: 0
))
.iadd((api.consts.registrar.dataDepositPerByte as BalanceOf).muln(genesisState ? genesisState.length : 0)),
[api, wasm, genesisState, paraConfig]
);
const isIdError = !paraId || !paraId.gt(LOWEST_INVALID_ID);
return (
<Modal
className={className}
header={t('Register parathread')}
onClose={onClose}
size='large'
>
<Modal.Content>
{api.tx.registrar.reserve
? (
<InputOwner
noCodeCheck
onChange={_setOwner}
ownedIds={ownedIds}
/>
)
: (
<>
<Modal.Columns hint={t('This account will be associated with the teyrchain and pay the deposit.')}>
<InputAddress
label={t('register from')}
onChange={setAccountId}
type='account'
value={accountId}
/>
</Modal.Columns>
<Modal.Columns hint={t('The id of this teyrchain as known on the network')}>
<InputNumber
autoFocus
defaultValue={nextParaId}
isError={isIdError}
isZeroable={false}
label={t('teyrchain id')}
onChange={setParaId}
/>
</Modal.Columns>
</>
)
}
<Modal.Columns hint={t('The WASM validation function for this teyrchain.')}>
<InputFile
isError={!wasm}
label={t('code')}
onChange={_setWasm}
/>
</Modal.Columns>
<Modal.Columns hint={t('The genesis state for this teyrchain.')}>
<InputFile
isError={!genesisState}
label={t('initial state')}
onChange={_setGenesisState}
/>
</Modal.Columns>
<Modal.Columns hint={t('The reservation fee for this teyrchain, including base fee and per-byte fees')}>
<InputBalance
defaultValue={reservedDeposit}
isDisabled
label={t('reserved deposit')}
/>
</Modal.Columns>
</Modal.Content>
<Modal.Actions>
<TxButton
accountId={accountId}
icon='plus'
isDisabled={!wasm || !genesisState || isIdError}
onStart={onClose}
params={[paraId, genesisState, wasm]}
tx={api.tx.registrar.register}
/>
</Modal.Actions>
</Modal>
);
}
export default React.memo(RegisterThread);
@@ -0,0 +1,10 @@
// Copyright 2017-2025 @pezkuwi/app-teyrchains authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { BN, BN_ONE, BN_THOUSAND } from '@pezkuwi/util';
export const LOWEST_PUBLIC_ID = new BN(2_000);
export const LOWEST_INVALID_ID = LOWEST_PUBLIC_ID.sub(BN_ONE);
export const LOWEST_USER_ID = BN_THOUSAND;
@@ -0,0 +1,62 @@
// Copyright 2017-2025 @pezkuwi/app-teyrchains authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ParaId } from '@pezkuwi/types/interfaces';
import type { LeasePeriod, OwnedId, QueuedAction } from '../types.js';
import React, { useRef } from 'react';
import { Table } from '@pezkuwi/react-components';
import { useTranslation } from '../translate.js';
import Actions from './Actions.js';
import Parathread from './Parathread.js';
import useParaMap from './useParaMap.js';
interface Props {
actionsQueue: QueuedAction[];
className?: string;
ids?: ParaId[];
leasePeriod?: LeasePeriod;
ownedIds: OwnedId[];
}
function Parathreads ({ actionsQueue, className, ids, leasePeriod, ownedIds }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const leaseMap = useParaMap(ids);
const headerRef = useRef<([React.ReactNode?, string?, number?] | false)[]>([
[t('parathreads'), 'start', 2],
['', 'media--2000'],
[t('head'), 'start media--1500'],
[t('lifecycle'), 'start'],
[],
[], // [t('chain'), 'no-pad-left'],
[t('leases')],
['', 'media--900']
]);
return (
<div className={className}>
<Actions ownedIds={ownedIds} />
<Table
empty={leasePeriod && ids && (ids.length === 0 || leaseMap) && t('There are no available parathreads')}
header={headerRef.current}
>
{leasePeriod && leaseMap?.map(([id, leases]): React.ReactNode => (
<Parathread
id={id}
key={id.toString()}
leasePeriod={leasePeriod}
leases={leases}
nextAction={actionsQueue.find(({ paraIds }) =>
paraIds.some((p) => p.eq(id))
)}
/>
))}
</Table>
</div>
);
}
export default React.memo(Parathreads);
@@ -0,0 +1,72 @@
// Copyright 2017-2025 @pezkuwi/app-teyrchains authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Option } from '@pezkuwi/types';
import type { AccountId, BalanceOf, ParaId } from '@pezkuwi/types/interfaces';
import type { ITuple } from '@pezkuwi/types/types';
import type { LeaseInfo } from '../types.js';
import { useCallback } from 'react';
import { createNamedHook, useApi, useCall, useIsParasLinked } from '@pezkuwi/react-hooks';
type Result = [ParaId, LeaseInfo[]][];
function extractParaMap (hasLinksMap: Record<string, boolean>, paraIds: ParaId[], leases: Option<ITuple<[AccountId, BalanceOf]>>[][]): Result {
return paraIds
.reduce((all: Result, id, index): Result => {
all.push([
id,
leases[index]
.map((optLease, period): LeaseInfo | null => {
if (optLease.isNone) {
return null;
}
const [accountId, balance] = optLease.unwrap();
return {
accountId,
balance,
period
};
})
.filter((item): item is LeaseInfo => !!item)
]);
return all;
}, [])
.sort(([aId, aLeases], [bId, bLeases]): number => {
const aKnown = hasLinksMap[aId.toString()] || false;
const bKnown = hasLinksMap[bId.toString()] || false;
return aLeases.length && bLeases.length
? (aLeases[0].period - bLeases[0].period) || aId.cmp(bId)
: aLeases.length
? -1
: bLeases.length
? 1
: aKnown === bKnown
? aId.cmp(bId)
: aKnown
? -1
: 1;
});
}
function useParaMapImpl (ids?: ParaId[]): Result | undefined {
const { api } = useApi();
const hasLinksMap = useIsParasLinked(ids);
const transform = useCallback(
([[paraIds], leases]: [[ParaId[]], Option<ITuple<[AccountId, BalanceOf]>>[][]]): Result =>
extractParaMap(hasLinksMap, paraIds, leases),
[hasLinksMap]
);
return useCall<Result>(ids && api.query.slots.leases.multi, [ids], {
transform,
withParamsTransform: true
});
}
export default createNamedHook('useParaMap', useParaMapImpl);
@@ -0,0 +1,46 @@
// Copyright 2017-2025 @pezkuwi/app-teyrchains authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Option } from '@pezkuwi/types';
import type { AccountId, HeadData, ParaId } from '@pezkuwi/types/interfaces';
import type { PezkuwiRuntimeCommonParasRegistrarParaInfo, PezkuwiRuntimeTeyrchainsParasParaGenesisArgs, PezkuwiRuntimeTeyrchainsParasParaLifecycle } from '@pezkuwi/types/lookup';
import { createNamedHook, useApi, useCallMulti } from '@pezkuwi/react-hooks';
interface Result {
headHex: string | null;
lifecycle: PezkuwiRuntimeTeyrchainsParasParaLifecycle | null;
manager: AccountId | null;
}
const OPT_MULTI = {
defaultValue: {
headHex: null,
lifecycle: null,
manager: null
},
transform: ([optHead, optGenesis, optLifecycle, optInfo]: [Option<HeadData>, Option<PezkuwiRuntimeTeyrchainsParasParaGenesisArgs>, Option<PezkuwiRuntimeTeyrchainsParasParaLifecycle>, Option<PezkuwiRuntimeCommonParasRegistrarParaInfo>]): Result => ({
headHex: optHead.isSome
? optHead.unwrap().toHex()
: optGenesis.isSome
? optGenesis.unwrap().genesisHead.toHex()
: null,
lifecycle: optLifecycle.unwrapOr(null),
manager: optInfo.isSome
? optInfo.unwrap().manager
: null
})
};
function useThreadInfoImpl (id: ParaId): Result {
const { api } = useApi();
return useCallMulti<Result>([
[api.query.paras.heads, id],
[api.query.paras.upcomingParasGenesis, id],
[api.query.paras.paraLifecycles, id],
[api.query.registrar.paras, id]
], OPT_MULTI);
}
export default createNamedHook('useThreadInfo', useThreadInfoImpl);