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
View File
View File
+1
View File
@@ -0,0 +1 @@
# @pezkuwi/app-alliance
+23
View File
@@ -0,0 +1,23 @@
{
"bugs": "https://github.com/pezkuwichain/pezkuwi-apps/issues",
"engines": {
"node": ">=18"
},
"homepage": "https://github.com/pezkuwichain/pezkuwi-apps/tree/master/packages/page-alliance#readme",
"license": "Apache-2.0",
"name": "@pezkuwi/app-alliance",
"private": true,
"repository": {
"directory": "packages/page-alliance",
"type": "git",
"url": "https://github.com/pezkuwichain/pezkuwi-apps.git"
},
"sideEffects": false,
"type": "module",
"version": "0.168.2-4-x",
"peerDependencies": {
"react": "*",
"react-dom": "*",
"react-is": "*"
}
}
@@ -0,0 +1,42 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Cid } from '../types.js';
import React from 'react';
import { useIpfsLink } from '@pezkuwi/react-hooks';
interface Props {
className?: string;
value: Cid;
}
function Website ({ className, value: { cid: { codec, hash_: { code }, version }, ipfs } }: Props): React.ReactElement<Props> {
const ipfsLink = useIpfsLink(ipfs);
return (
<tr className={className}>
<td className='start all'>
{ipfsLink && (
<a
href={ipfsLink.ipfsUrl}
rel='noopener noreferrer'
target='_blank'
>{ipfsLink.ipfsHash}</a>
)}
</td>
<td className='number'>
{version.type}
</td>
<td className='number'>
0x{codec.toString(16)}
</td>
<td className='number'>
0x{code.toString(16)}
</td>
</tr>
);
}
export default React.memo(Website);
@@ -0,0 +1,45 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Cid } from '../types.js';
import React, { useRef } from 'react';
import { Table } from '@pezkuwi/react-components';
import { useTranslation } from '../translate.js';
import Accouncement from './Accouncement.js';
interface Props {
accouncements?: Cid[];
className?: string;
}
function Announcements ({ accouncements, className }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const annRef = useRef<([React.ReactNode?, string?, number?] | false)[]>([
[t('announcements'), 'start'],
[t('version'), 'number'],
[t('codec'), 'number'],
[t('code'), 'number']
]);
return (
<div className={className}>
<Table
empty={accouncements && t('No announcements')}
header={annRef.current}
>
{accouncements?.map((a) => (
<Accouncement
key={a.key}
value={a}
/>
))}
</Table>
</div>
);
}
export default React.memo(Announcements);
@@ -0,0 +1,74 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Member, Unscrupulous } from '../types.js';
import React, { useMemo, useState } from 'react';
import { InputAddress, InputBalance, Modal, TxButton } from '@pezkuwi/react-components';
import { useAccounts, useApi } from '@pezkuwi/react-hooks';
import { useTranslation } from '../translate.js';
interface Props {
className?: string;
members: Member[];
onClose: () => void;
unscrupulous: Unscrupulous;
}
function Join ({ className, members, onClose, unscrupulous: { accounts } }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const { allAccounts } = useAccounts();
const [accountId, setAccountId] = useState<string | null>(null);
const available = useMemo(
(): string[] =>
allAccounts.filter((a) =>
!members.some(({ accountId }) =>
accountId === a
) &&
!accounts.some((accountId) =>
accountId === a
)
),
[accounts, allAccounts, members]
);
return (
<Modal
className={className}
header={t('Join alliance')}
onClose={onClose}
size='large'
>
<Modal.Content>
<Modal.Columns hint={t('This account will be submitted to join the aliance. It will be allocated one of the alliance roles upon joining, starting with Ally.')}>
<InputAddress
filter={available}
label={t('alliance account')}
onChange={setAccountId}
type='account'
/>
</Modal.Columns>
<Modal.Columns hint={t('The bond will be reserved for the duration of your alliance membership.')}>
<InputBalance
defaultValue={api.consts.alliance.allyDeposit}
isDisabled
label={t('alliance deposit')}
/>
</Modal.Columns>
</Modal.Content>
<Modal.Actions>
<TxButton
accountId={accountId}
onStart={onClose}
tx={api.tx.alliance.joinAlliance}
/>
</Modal.Actions>
</Modal>
);
}
export default React.memo(Join);
@@ -0,0 +1,123 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { BN } from '@pezkuwi/util';
import type { Member as MemberType } from '../types.js';
import React, { useCallback, useMemo } from 'react';
import { AddressSmall, Menu, Popup, Tag } from '@pezkuwi/react-components';
import { useAccounts, useApi, useQueue } from '@pezkuwi/react-hooks';
import { useTranslation } from '../translate.js';
import useMemberInfo from '../useMemberInfo.js';
interface Props {
bestNumber?: BN;
className?: string;
info: MemberType;
isPrime: boolean;
isVoter: boolean;
}
function Member ({ bestNumber, className, info: { accountId, role }, isPrime, isVoter }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const { allAccounts } = useAccounts();
const info = useMemberInfo(accountId);
const { queueExtrinsic } = useQueue();
const hasNotice = !!api.tx.alliance.giveRetirementNotice;
const hasActions = useMemo(
() => allAccounts.includes(accountId),
[allAccounts, accountId]
);
const doRetire = useCallback(
() => queueExtrinsic({
accountId,
extrinsic: api.tx.alliance.retire()
}),
[accountId, api, queueExtrinsic]
);
const doNotice = useCallback(
() => queueExtrinsic({
accountId,
extrinsic: api.tx.alliance.giveRetirementNotice()
}),
[accountId, api, queueExtrinsic]
);
return (
<tr className={className}>
<td className='address all relative'>
<AddressSmall value={accountId} />
<div className='absolute'>
{(info?.isRetiringAt && (
<Tag
color='yellow'
hover={t('Is retiring')}
label={t('retirting')}
/>
)) || (info?.isUpForKicking && (
<Tag
color='red'
hover={t('Up for kicking')}
label={t('kicking')}
/>
)) || (isPrime && (
<Tag
color='green'
hover={t('Current prime member, default voting')}
label={t('prime voter')}
/>
)) || (isVoter && (
<Tag
color='green'
hover={t('Allowed to vote on motions')}
label={t('voter')}
/>
))}
</div>
</td>
<td className='number'>
{role}
</td>
<td className='button'>
{hasActions && (
<Popup
key='settings'
value={
<Menu>
{hasNotice && (
<Menu.Item
isDisabled={!!(info?.isRetiringAt)}
label={t('Announce retirement')}
onClick={doNotice}
/>
)}
<Menu.Item
isDisabled={
!!info && (
info.isUpForKicking ||
(
hasNotice
? !bestNumber || !info.isRetiringAt || info.isRetiringAt.lt(bestNumber)
: false
)
)
}
label={t('Retire')}
onClick={doRetire}
/>
</Menu>
}
/>
)}
</td>
</tr>
);
}
export default React.memo(Member);
@@ -0,0 +1,52 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Member as MemberType, Rule } from '../types.js';
import React from 'react';
import { CardSummary, SummaryBox } from '@pezkuwi/react-components';
import { useIpfsLink } from '@pezkuwi/react-hooks';
import { formatNumber } from '@pezkuwi/util';
import { useTranslation } from '../translate.js';
interface Props {
className?: string;
members?: MemberType[];
rule?: Rule;
}
function Summary ({ className, members, rule }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const ipfsLink = useIpfsLink(rule?.cid?.ipfs);
return (
<SummaryBox className={className}>
<CardSummary label={t('rule')}>
{rule
? rule.hasRule
? ipfsLink
? (
<a
href={ipfsLink.ipfsUrl}
rel='noopener noreferrer'
target='_blank'
>{ipfsLink.ipfsShort}</a>
)
: t('yes')
: t('no')
: <span className='--tmp'>{t('no')}</span>
}
</CardSummary>
<CardSummary label={t('members')}>
{members
? formatNumber(members.length)
: <span className='--tmp'>99</span>
}
</CardSummary>
</SummaryBox>
);
}
export default React.memo(Summary);
@@ -0,0 +1,76 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Member as MemberType, Rule, Unscrupulous } from '../types.js';
import React, { useRef } from 'react';
import { Button, Table } from '@pezkuwi/react-components';
import { useBestNumber, useToggle } from '@pezkuwi/react-hooks';
import { useTranslation } from '../translate.js';
import Join from './Join.js';
import Member from './Member.js';
import Summary from './Summary.js';
interface Props {
className?: string;
isVoter?: boolean;
members?: MemberType[];
prime?: string | null;
rule?: Rule;
unscrupulous?: Unscrupulous;
voters?: string[];
}
function Overview ({ className, members, prime, rule, unscrupulous, voters }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [isJoinOpen, toggleJoin] = useToggle();
const bestNumber = useBestNumber();
const hdrRef = useRef<([React.ReactNode?, string?, number?] | false)[]>([
[t('members'), 'start', 3]
]);
return (
<div className={className}>
<Summary
members={members}
rule={rule}
/>
<Button.Group>
<Button
icon='add'
isDisabled={!members || !unscrupulous}
label={t('Join')}
onClick={toggleJoin}
/>
{members && unscrupulous && isJoinOpen && (
<Join
members={members}
onClose={toggleJoin}
unscrupulous={unscrupulous}
/>
)}
</Button.Group>
<Table
empty={members && t('No members')}
header={hdrRef.current}
isSplit
maxColumns={2}
>
{members?.map((m) => (
<Member
bestNumber={bestNumber}
info={m}
isPrime={prime === m.accountId}
isVoter={!!voters && voters.includes(m.accountId)}
key={m.accountId}
/>
))}
</Table>
</div>
);
}
export default React.memo(Overview);
@@ -0,0 +1,23 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
import { AddressSmall } from '@pezkuwi/react-components';
interface Props {
className?: string;
value: string;
}
function Account ({ className, value }: Props): React.ReactElement<Props> {
return (
<tr className={className}>
<td className='address all'>
<AddressSmall value={value} />
</td>
</tr>
);
}
export default React.memo(Account);
@@ -0,0 +1,21 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
interface Props {
className?: string;
value: string;
}
function Website ({ className, value }: Props): React.ReactElement<Props> {
return (
<tr className={className}>
<td className='start all'>
{value}
</td>
</tr>
);
}
export default React.memo(Website);
@@ -0,0 +1,58 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Unscrupulous as UnscrupulousType } from '../types.js';
import React, { useRef } from 'react';
import { Table } from '@pezkuwi/react-components';
import { useTranslation } from '../translate.js';
import Account from './Account.js';
import Website from './Website.js';
interface Props {
className?: string;
unscrupulous?: UnscrupulousType;
}
function Unscrupulous ({ className, unscrupulous }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const accRef = useRef<([React.ReactNode?, string?, number?] | false)[]>([
[t('accounts'), 'start']
]);
const webRef = useRef<([React.ReactNode?, string?, number?] | false)[]>([
[t('websites'), 'start']
]);
return (
<div className={className}>
<Table
empty={unscrupulous?.accounts && t('No accounts')}
header={accRef.current}
>
{unscrupulous?.accounts.map((m) => (
<Account
key={m}
value={m}
/>
))}
</Table>
<Table
empty={unscrupulous?.websites && t('No websites')}
header={webRef.current}
>
{unscrupulous?.websites.map((m) => (
<Website
key={m}
value={m}
/>
))}
</Table>
</div>
);
}
export default React.memo(Unscrupulous);
+121
View File
@@ -0,0 +1,121 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Hash } from '@pezkuwi/types/interfaces';
import React, { useCallback, useMemo } from 'react';
import { Route, Routes } from 'react-router';
import Motions from '@pezkuwi/app-tech-comm/Proposals';
import { Tabs } from '@pezkuwi/react-components';
import { useApi, useCall, useCollectiveMembers } from '@pezkuwi/react-hooks';
import Announcements from './Announcements/index.js';
import Members from './Members/index.js';
import Unscrupulous from './Unscrupulous/index.js';
import { useTranslation } from './translate.js';
import useAnnoucements from './useAnnoucements.js';
import useMembers from './useMembers.js';
import useRule from './useRule.js';
import useUnscrupulous from './useUnscrupulous.js';
export { default as useCounter } from './useCounter.js';
interface Props {
basePath: string;
className?: string;
}
// TODO Make configurable
const DEFAULT_THRESHOLD = 2 / 3;
function AllianceApp ({ basePath, className }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const proposalHashes = useCall<Hash[]>(api.derive.alliance.proposalHashes);
const { isMember: isVoter, members: voters, prime } = useCollectiveMembers('alliance');
const accouncements = useAnnoucements();
const members = useMembers();
const rule = useRule();
const unscrupulous = useUnscrupulous();
const motionFilter = useCallback(
(section: string) => section === 'alliance',
[]
);
const items = useMemo(() => [
{
isRoot: true,
name: 'overview',
text: t('Overview')
},
{
name: 'motions',
text: t('Motions ({{count}})', { replace: { count: proposalHashes?.length || 0 } })
},
{
name: 'announcements',
text: t('Announcements ({{count}})', { replace: { count: accouncements?.length || 0 } })
},
{
name: 'unscrupulous',
text: t('Unscrupulous')
}
], [accouncements, proposalHashes, t]);
return (
<main className={className}>
<Tabs
basePath={basePath}
items={items}
/>
<Routes>
<Route path={basePath}>
<Route
element={
<Announcements accouncements={accouncements} />
}
path='announcements'
/>
<Route
element={
<Motions
defaultProposal={api.tx.alliance.addUnscrupulousItems}
defaultThreshold={DEFAULT_THRESHOLD}
filter={motionFilter}
isMember={isVoter}
members={voters}
prime={prime}
proposalHashes={proposalHashes}
type='alliance'
/>
}
path='motions'
/>
<Route
element={
<Unscrupulous unscrupulous={unscrupulous} />
}
path='unscrupulous'
/>
<Route
element={
<Members
isVoter={isVoter}
members={members}
prime={prime}
rule={rule}
unscrupulous={unscrupulous}
voters={voters}
/>
}
index
/>
</Route>
</Routes>
</main>
);
}
export default React.memo(AllianceApp);
+8
View File
@@ -0,0 +1,8 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { useTranslation as useTranslationBase } from 'react-i18next';
export function useTranslation (): { t: (key: string, options?: { replace: Record<string, unknown> }) => string } {
return useTranslationBase('app-alliance');
}
+34
View File
@@ -0,0 +1,34 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { PalletAllianceCid, PalletAllianceMemberRole } from '@pezkuwi/types/lookup';
import type { BN } from '@pezkuwi/util';
export interface Cid {
cid: PalletAllianceCid;
ipfs: string | null;
key: string;
}
export interface Member {
accountId: string;
// Founder here is deprecated
role: PalletAllianceMemberRole['type'] | 'Founder';
}
export interface MemberInfo {
accountId: string;
deposit?: BN | null;
isRetiringAt?: BN | null;
isUpForKicking?: boolean;
}
export interface Rule {
cid: Cid | null;
hasRule: boolean;
}
export interface Unscrupulous {
accounts: string[];
websites: string[];
}
@@ -0,0 +1,22 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { PalletAllianceCid } from '@pezkuwi/types/lookup';
import type { Cid } from './types.js';
import { createNamedHook, useApi, useCall } from '@pezkuwi/react-hooks';
import { createCid } from './util.js';
const OPT_ANN = {
transform: (cids: PalletAllianceCid[]): Cid[] =>
cids.map(createCid)
};
function useAnnouncementsImpl (): Cid[] | undefined {
const { api } = useApi();
return useCall<Cid[]>(api.query.alliance.announcements, [], OPT_ANN);
}
export default createNamedHook('useAnnouncements', useAnnouncementsImpl);
+18
View File
@@ -0,0 +1,18 @@
// Copyright 2017-2025 @pezkuwi/app-democracy authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { useMemo } from 'react';
import { createNamedHook, useApi, useCall } from '@pezkuwi/react-hooks';
function useCounterImpl (): number {
const { api, isApiReady } = useApi();
const proposalHashes = useCall<unknown[]>(isApiReady && api.derive.alliance.proposalHashes);
return useMemo(
() => proposalHashes?.length || 0,
[proposalHashes]
);
}
export default createNamedHook('useCounter', useCounterImpl);
@@ -0,0 +1,28 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { bool, Option, UInt } from '@pezkuwi/types';
import type { MemberInfo } from './types.js';
import { useMemo } from 'react';
import { createNamedHook, useApi, useCall } from '@pezkuwi/react-hooks';
function useMemberInfoImpl (accountId: string): MemberInfo | undefined {
const { api } = useApi();
const upForKicking = useCall<bool>(api.query.alliance.upForKicking, [accountId]);
const retiringAt = useCall<Option<UInt>>(api.query.alliance.retiringMembers, [accountId]);
const depositOf = useCall<Option<UInt>>(api.query.alliance.depositOf, [accountId]);
return useMemo(
() => depositOf && {
accountId,
deposit: depositOf.unwrapOr(null),
isUpForKicking: upForKicking && upForKicking.isTrue,
retiringAt: retiringAt?.unwrapOr(null)
},
[accountId, depositOf, retiringAt, upForKicking]
);
}
export default createNamedHook('useMemberInfo', useMemberInfoImpl);
+54
View File
@@ -0,0 +1,54 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { AccountId32 } from '@pezkuwi/types/interfaces';
import type { Member } from './types.js';
import { useEffect, useState } from 'react';
import { createNamedHook, useApi, useCall } from '@pezkuwi/react-hooks';
const ROLES = ['Retiring', 'Ally', 'Fellow'] as const;
function addMembers (prev: Member[], ...query: AccountId32[][]): Member[] {
const all: Member[] = [];
for (let i = 0; i < ROLES.length; i++) {
const role = ROLES[i];
const accountIds = query[i];
for (let j = 0, count = accountIds.length; j < count; j++) {
const accountId = accountIds[j].toString();
const existing = prev.find((p) =>
p.accountId === accountId &&
p.role === role
);
all.push(existing || {
accountId,
role
});
}
}
return all.reverse();
}
function useMembersImpl (): Member[] | undefined {
const { api } = useApi();
const [state, setState] = useState<Member[] | undefined>();
const role0 = useCall<AccountId32[]>(api.query.alliance.members, [ROLES[0]]);
const role1 = useCall<AccountId32[]>(api.query.alliance.members, [ROLES[1]]);
const role2 = useCall<AccountId32[]>(api.query.alliance.members, [ROLES[2]]);
useEffect((): void => {
role0 && role1 && role2 &&
setState((prev = []) =>
addMembers(prev, role0, role1, role2)
);
}, [role0, role1, role2]);
return state;
}
export default createNamedHook('useMembers', useMembersImpl);
+25
View File
@@ -0,0 +1,25 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Option } from '@pezkuwi/types';
import type { PalletAllianceCid } from '@pezkuwi/types/lookup';
import type { Rule } from './types.js';
import { createNamedHook, useApi, useCall } from '@pezkuwi/react-hooks';
import { createCid } from './util.js';
const OPT_RULE = {
transform: (opt: Option<PalletAllianceCid>): Rule =>
opt.isSome
? { cid: createCid(opt.unwrap()), hasRule: true }
: { cid: null, hasRule: false }
};
function useRuleImpl (): Rule | undefined {
const { api } = useApi();
return useCall<Rule>(api.query.alliance.rule, [], OPT_RULE);
}
export default createNamedHook('useRule', useRuleImpl);
@@ -0,0 +1,37 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Bytes } from '@pezkuwi/types';
import type { AccountId32 } from '@pezkuwi/types/interfaces';
import type { Unscrupulous } from './types.js';
import { useMemo } from 'react';
import { createNamedHook, useApi, useCall } from '@pezkuwi/react-hooks';
function mapString (all: { toString: () => string }[]): string[] {
return all.map((a) => a.toString());
}
const OPT_ACC = {
transform: (accounts: AccountId32[]): string[] =>
mapString(accounts)
};
const OPT_WEB = {
transform: (websites: Bytes[]): string[] =>
mapString(websites.filter((b) => b.isAscii))
};
function useUnscrupulousImpl (): Unscrupulous | undefined {
const { api } = useApi();
const accounts = useCall<string[]>(api.query.alliance.unscrupulousAccounts, [], OPT_ACC);
const websites = useCall<string[]>(api.query.alliance.unscrupulousWebsites, [], OPT_WEB);
return useMemo(
() => accounts && websites && { accounts, websites },
[accounts, websites]
);
}
export default createNamedHook('useUnscrupulous', useUnscrupulousImpl);
+48
View File
@@ -0,0 +1,48 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
/// <reference types="@pezkuwi/dev-test/globals.d.ts" />
import { Metadata, TypeRegistry } from '@pezkuwi/types';
import bizinikiwi from '@pezkuwi/types-support/metadata/static-bizinikiwi';
import { createCid, createPalletCid } from './util.js';
const registry = new TypeRegistry();
const metadata = new Metadata(registry, bizinikiwi);
registry.setMetadata(metadata);
describe('util', (): void => {
describe('createPalletCid', (): void => {
it('encodes an ipfs hash to a pallet CID', (): void => {
expect(
createPalletCid(registry, 'QmTx8GZrQGwQ1ZLquZ4yPqSukDdkvKwpYCULsfLo6G47TX')?.toHuman()
).toEqual({
codec: 0x70.toString(),
hash_: {
code: 0x12.toString(),
digest: '0x5360ecbd380c10f43bc0a6aba27556580011a5e833592df8dcf330c94759e862'
},
version: 'V0'
});
});
});
describe('createIpfsHash', (): void => {
it('encodes a pallet CID into an ipfs hash', (): void => {
const cid = registry.createType('PalletAllianceCid', {
codec: 0x70,
hash_: {
code: 0x12,
digest: '0x5360ecbd380c10f43bc0a6aba27556580011a5e833592df8dcf330c94759e862'
},
version: 'V0'
});
expect(
createCid(cid).ipfs
).toEqual('QmTx8GZrQGwQ1ZLquZ4yPqSukDdkvKwpYCULsfLo6G47TX');
});
});
});
+22
View File
@@ -0,0 +1,22 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { PalletAllianceCid } from '@pezkuwi/types/lookup';
import type { Registry } from '@pezkuwi/types/types';
import type { Cid } from './types.js';
import { fromIpfsCid, toIpfsCid } from '@pezkuwi/react-params/util';
export function createPalletCid (registry: Registry, cid: string): PalletAllianceCid | null {
const expanded = fromIpfsCid(cid);
return expanded && registry.createType('PalletAllianceCid', expanded);
}
export function createCid (cid: PalletAllianceCid): Cid {
return {
cid,
ipfs: toIpfsCid(cid),
key: cid.toHex()
};
}
@@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"baseUrl": "..",
"outDir": "./build",
"rootDir": "./src"
},
"references": [
{ "path": "../page-tech-comm/tsconfig.build.json" },
{ "path": "../react-components/tsconfig.build.json" }
]
}