Drop the foreign chains' parameters; fix the Zagros genesis

- api/constants: only PEZKUWI_GENESIS and ZAGROS_GENESIS remain. The
  Zagros value was a placeholder from before its launch; it is now the
  live chain's genesis (0x1b0b4727…2cd5, measured).
- Inflation, proposal thresholds and governance tracks keep the Pezkuwi
  entries; the Dicle, Kulupu, Cere, Vara, Joystream, zkVerify, Dock,
  Neatcoin and NFTMart ones go, as do the dicle tracks.
- getTeleportWeight was used nowhere: removed.
- page-society: the Dicle-only "design samples" button and its canary
  drawing go, with their table column.
- page-assets: the next-id rule keyed on Polkadot's and Kusama's asset
  hub genesis never matched a Pezkuwi chain; removed, no change there.
- ParaLink: the Subscan link went to scan.pezkuwichain.io, which does
  not resolve; removed (coretime keeps the homepage link).
- typesBundle generator: no unused sort-keys directive when empty.
This commit is contained in:
Satoshi Qazi Muhammed authored and SatoshiQaziMuhammed committed 2026-10-10 04:54:16 +04:00
1 parent 66e8d6c1be
commit 8c9ee48278
16 files changed
+20 -655

No files matched your search

+2 -48
View File
@@ -1,54 +1,8 @@
// Copyright 2017-2026 @pezkuwi/apps-config authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { HexString } from '@pezkuwi/util/types';
import { knownGenesis } from '@pezkuwi/networks/defaults';
import { assert, BN } from '@pezkuwi/util';
export function getGenesis (name: string): HexString {
const network = Object.entries(knownGenesis).find(([network]) => network === name);
assert(network?.[1][0], `Unable to find genesisHash for ${name}`);
return network[1][0];
}
export const KULUPU_GENESIS = getGenesis('kulupu');
// Dicle - Pezkuwi canary relay chain (placeholder until mainnet launch)
export const DICLE_GENESIS = '0xd9d3cd7c1e5d890d969b957f4c5b71a111bbeeabc968f1d0d4538c2663f080a7';
// Pezkuwi - Pezkuwi main relay chain
export const PEZKUWI_GENESIS = '0x1aa94987791a5544e9667ec249d2cef1b8fdd6083c85b93fc37892d54a1156ca';
export const PEZKUWI_DENOM_BLOCK = new BN(1248328);
export const PASEO_GENESIS = '0x77afd6190f1554ad45fd0d31aee62aacc33c6db0ea801129acb813f913e0764f';
// Zagros - Pezkuwi test relay chain (placeholder until testnet launch)
export const ZAGROS_GENESIS = '0x297f5a4d105b4b28312586ff1915572ffe4ee015ff772b76399ecbff25a22026';
// PezkuwiChain - Pezkuwi dev relay chain (placeholder until devnet launch)
export const PEZKUWICHAIN_GENESIS = '0x32154fd2c844f928c82964ff66168b41b15fc235f3a956d14393734c1ed4326b';
export const NEATCOIN_GENESIS = '0xfbb541421d30423c9a753ffa844b64fd44d823f513bf49e3b73b3a656309a595';
export const DOCK_GENESIS = '0x6bfe24dca2a3be10f22212678ac13a6446ec764103c0f3471c71609eac384aae';
export const DOCK_POS_TESTNET_GENESIS = '0x59d93e2ce42abb8aa52ca9a9e820233667104751f8f2980578a47a26a7235027';
export const NFTMART_GENESIS = '0xfcf9074303d8f319ad1bf0195b145871977e7c375883b834247cb01ff22f51f9';
export const CERE_NETWORK_GENESIS = '0x81443836a9a24caaa23f1241897d1235717535711d1d3fe24eae4fdc942c092c';
export const CERE_NETWORK_TESTNET_GENESIS = '0x42b9b44b4950b6c1edae543a7696caf8d0a160e9bc0424ab4ab217f7a8ba30dc';
export const VARA_NETWORK_GENESIS = '0xfe1b4c55fd4d668101126434206571a7838a8b6b93a6d1b95d607e78e6c53763';
export const VARA_NETWORK_TESTNET_GENESIS = '0x525639f713f397dcf839bd022cd821f367ebcf179de7b9253531f8adbe5436d6';
export const JOYSTREAM_GENESIS = '0x6b5e488e0fa8f9821110d5c13f4c468abcd43ce5e297e62b34c53c3346465956';
export const ZKVERIFY_GENESIS = '0x060e3dd3fa2904d031206bb913c954687a2bcc350e5a83d33d9e273ad21460f1';
export const ZKVERIFY_VOLTA_GENESIS = '0xff7fe5a610f15fe7a0c52f94f86313fb7db7d3786e7f8acf2b66c11d5be7c242';
// Zagros - Pezkuwi test relay chain
export const ZAGROS_GENESIS = '0x1b0b4727bbb5e44ed587d1056b4ad537ffb330034509a58866a088d77e792cd5';
@@ -3,5 +3,4 @@
export * from './inflation.js';
export * from './proposalThresholds.js';
export * from './teleport.js';
export * from './tracks/index.js';
@@ -3,7 +3,7 @@
import type { ApiPromise } from '@pezkuwi/api';
import { CERE_NETWORK_GENESIS, CERE_NETWORK_TESTNET_GENESIS, DICLE_GENESIS, DOCK_POS_TESTNET_GENESIS, JOYSTREAM_GENESIS, NEATCOIN_GENESIS, NFTMART_GENESIS, PEZKUWI_GENESIS, VARA_NETWORK_GENESIS, VARA_NETWORK_TESTNET_GENESIS, ZKVERIFY_GENESIS, ZKVERIFY_VOLTA_GENESIS } from '../constants.js';
import { PEZKUWI_GENESIS } from '../constants.js';
interface InflationParams {
auctionAdjust: number;
@@ -27,33 +27,8 @@ const DEFAULT_PARAMS: InflationParams = {
stakeTarget: 0.5
};
const CERE_NETWORK_INFLATION_PARAMS = { ...DEFAULT_PARAMS, maxInflation: 0.05, minInflation: 0.0001, stakeTarget: 0.2 };
const VARA_NETWORK_INFLATION_PARAMS = { ...DEFAULT_PARAMS, maxInflation: 0, minInflation: 0.0001, stakeTarget: 0.85 };
const JOYSTREAM_INFLATION_PARAMS = { ...DEFAULT_PARAMS, maxInflation: 0.03, minInflation: 0.0075 };
const ZKVERIFY_INFLATION_PARAMS = { ...DEFAULT_PARAMS, auctionAdjust: 0, maxInflation: 0.025, minInflation: 0.025, stakeTarget: 0 };
const ZKVERIFY_VOLTA_INFLATION_PARAMS = { ...DEFAULT_PARAMS, auctionAdjust: 0, maxInflation: 0.025, minInflation: 0.025, stakeTarget: 0 };
const KNOWN_PARAMS: Record<string, InflationParams> = {
[CERE_NETWORK_GENESIS]: CERE_NETWORK_INFLATION_PARAMS,
[CERE_NETWORK_TESTNET_GENESIS]: CERE_NETWORK_INFLATION_PARAMS,
// 30% for up to 60 slots, see
// https://github.com/pezkuwichain/pezkuwi/blob/816cb64ea16102c6c79f6be2a917d832d98df757/runtime/dicle/src/lib.rs#L526-L527
// 75% ideal target, see
// https://github.com/pezkuwichain/pezkuwi/blob/816cb64ea16102c6c79f6be2a917d832d98df757/runtime/dicle/src/lib.rs#L529-L531
[DICLE_GENESIS]: { ...DEFAULT_PARAMS, auctionAdjust: (0.3 / 60), auctionMax: 60, stakeTarget: 0.75 },
[DOCK_POS_TESTNET_GENESIS]: { ...DEFAULT_PARAMS, stakeTarget: 0.75 },
[JOYSTREAM_GENESIS]: JOYSTREAM_INFLATION_PARAMS,
[NEATCOIN_GENESIS]: { ...DEFAULT_PARAMS, stakeTarget: 0.75 },
[NFTMART_GENESIS]: { ...DEFAULT_PARAMS, falloff: 0.04, stakeTarget: 0.60 },
[PEZKUWI_GENESIS]: { ...DEFAULT_PARAMS, stakeTarget: 0.75 },
[VARA_NETWORK_GENESIS]: VARA_NETWORK_INFLATION_PARAMS,
[VARA_NETWORK_TESTNET_GENESIS]: VARA_NETWORK_INFLATION_PARAMS,
[ZKVERIFY_GENESIS]: ZKVERIFY_INFLATION_PARAMS,
[ZKVERIFY_VOLTA_GENESIS]: ZKVERIFY_VOLTA_INFLATION_PARAMS
[PEZKUWI_GENESIS]: { ...DEFAULT_PARAMS, stakeTarget: 0.75 }
};
export function getInflationParams (api: ApiPromise): InflationParams {
@@ -3,7 +3,7 @@
import type { ApiPromise } from '@pezkuwi/api';
import { DICLE_GENESIS, KULUPU_GENESIS, PEZKUWI_GENESIS } from '../constants.js';
import { PEZKUWI_GENESIS } from '../constants.js';
// normal fast-track proposals
const FAST_TRACK: Record<string, number> = {
@@ -16,21 +16,16 @@ const FAST_TRACK_NO_DELAY: Record<string, number> = {
};
const PROPOSE: Record<string, number> = {
[DICLE_GENESIS]: 1 / 2,
[KULUPU_GENESIS]: 1,
[PEZKUWI_GENESIS]: 3 / 5,
default: 1 / 2
};
const SLASH: Record<string, number> = {
[DICLE_GENESIS]: 1 / 2,
[PEZKUWI_GENESIS]: 3 / 4,
default: 1 / 2
};
const TREASURY: Record<string, number> = {
[DICLE_GENESIS]: 3 / 5,
[KULUPU_GENESIS]: 1 / 2,
[PEZKUWI_GENESIS]: 3 / 5,
default: 3 / 5
};
@@ -1,19 +0,0 @@
// Copyright 2017-2026 @pezkuwi/apps-config authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ApiPromise } from '@pezkuwi/api';
import { DICLE_GENESIS } from '../constants.js';
// 4 * BaseXcmWeight on Dicle
const DICLE_WEIGHT = 4 * 1_000_000_000;
const DEFAULT_WEIGHT = DICLE_WEIGHT;
const KNOWN_WEIGHTS: Record<string, number> = {
[DICLE_GENESIS]: DICLE_WEIGHT
};
export function getTeleportWeight (api: ApiPromise): number {
return KNOWN_WEIGHTS[api.genesisHash.toHex()] || DEFAULT_WEIGHT;
}
@@ -1,208 +0,0 @@
// Copyright 2017-2026 @pezkuwi/apps-config authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { TrackInfo } from './types.js';
import { BN } from '@pezkuwi/util';
import { compareFellowshipRank, formatSpendFactory } from './util.js';
// hardcoded here since this is static (hopefully no re-denomination anytime...)
const formatSpend = formatSpendFactory({
decimals: 12,
forceUnit: '-',
withSi: true,
withUnit: 'DCL'
});
// https://github.com/pezkuwichain/pezkuwi/blob/6e3f2c5b4b6e6927915de2f784e1d831717760fa/runtime/dicle/constants/src/lib.rs#L28-L32
const UNITS = new BN(1_000_000_000_000);
const QUID = UNITS.divn(30);
const GRAND = QUID.muln(1_000);
// https://github.com/pezkuwichain/pezkuwi/blob/6e3f2c5b4b6e6927915de2f784e1d831717760fa/runtime/dicle/src/governance/origins.rs#L170-L179
const SPEND_LIMITS = {
BigSpender: formatSpend(1_000, GRAND),
BigTipper: formatSpend(1, GRAND),
MediumSpender: formatSpend(100, GRAND),
SmallSpender: formatSpend(10, GRAND),
SmallTipper: formatSpend(250, QUID),
Treasurer: formatSpend(10_000, GRAND)
};
export const dicle: Record<string, TrackInfo[]> = {
fellowshipReferenda: [
{
compare: compareFellowshipRank(0),
id: 0,
name: 'candidates',
origin: { Origins: 'FellowshipInitiates' },
text: 'Origin commanded by any members of the Pezkuwi Fellowship (no Dan grade needed)'
},
{
compare: compareFellowshipRank(1),
id: 1,
name: 'members',
origin: { Origins: 'Fellowship1Dan' },
text: 'Origin commanded by rank 1 of the Pezkuwi Fellowship and with a success of 1'
},
{
compare: compareFellowshipRank(2),
id: 2,
name: 'proficients',
origin: { Origins: 'Fellowship2Dan' },
text: 'Origin commanded by rank 2 of the Pezkuwi Fellowship and with a success of 2'
},
{
compare: compareFellowshipRank(3),
id: 3,
name: 'fellows',
origin: [
{ Origins: 'Fellowship3Dan' },
{ Origins: 'Fellows' }
],
text: 'Origin commanded by Pezkuwi Fellows (3rd Dan fellows or greater)'
},
{
compare: compareFellowshipRank(4),
id: 4,
name: 'senior fellows',
origin: { Origins: 'Fellowship4Dan' },
text: 'Origin commanded by rank 4 of the Pezkuwi Fellowship and with a success of 4'
},
{
compare: compareFellowshipRank(5),
id: 5,
name: 'experts',
origin: [
{ Origins: 'Fellowship5Dan' },
{ Origins: 'FellowshipExperts' }
],
text: 'Origin commanded by Pezkuwi Experts (5th Dan fellows or greater)'
},
{
compare: compareFellowshipRank(6),
id: 6,
name: 'senior experts',
origin: { Origins: 'Fellowship6Dan' },
text: 'Origin commanded by rank 6 of the Pezkuwi Fellowship and with a success of 6'
},
{
compare: compareFellowshipRank(7),
id: 7,
name: 'masters',
origin: [
{ Origins: 'Fellowship7Dan' },
{ Origins: 'FellowshipMasters' }
],
text: 'Origin commanded by Pezkuwi Masters (7th Dan fellows of greater)'
},
{
compare: compareFellowshipRank(8),
id: 8,
name: 'senior masters',
origin: { Origins: 'Fellowship8Dan' },
text: 'Origin commanded by rank 8 of the Pezkuwi Fellowship and with a success of 8'
},
{
compare: compareFellowshipRank(9),
id: 9,
name: 'grand masters',
origin: { Origins: 'Fellowship9Dan' },
text: 'Origin commanded by rank 9 of the Pezkuwi Fellowship and with a success of 9'
}
],
referenda: [
{
id: 0,
name: 'root',
origin: { system: 'Root' },
text: 'Origin for the system root'
},
{
id: 1,
name: 'whitelisted_caller',
origin: { Origins: 'WhitelistedCaller' },
text: 'Origin able to dispatch a whitelisted call'
},
{
id: 10,
name: 'staking_admin',
origin: { Origins: 'StakingAdmin' },
text: 'Origin for cancelling slashes'
},
{
id: 11,
name: 'treasurer',
origin: { Origins: 'Treasurer' },
text: 'Origin for spending (any amount of) funds'
},
{
id: 12,
name: 'lease_admin',
origin: { Origins: 'LeaseAdmin' },
text: 'Origin able to force slot leases'
},
{
id: 13,
name: 'fellowship_admin',
origin: { Origins: 'FellowshipAdmin' },
text: 'Origin for managing the composition of the fellowship'
},
{
id: 14,
name: 'general_admin',
origin: { Origins: 'GeneralAdmin' },
text: 'Origin for managing the registrar'
},
{
id: 15,
name: 'auction_admin',
origin: { Origins: 'AuctionAdmin' },
text: 'Origin for starting auctions'
},
{
id: 20,
name: 'referendum_canceller',
origin: { Origins: 'ReferendumCanceller' },
text: 'Origin able to cancel referenda'
},
{
id: 21,
name: 'referendum_killer',
origin: { Origins: 'ReferendumKiller' },
text: 'Origin able to kill referenda'
},
{
id: 30,
name: 'small_tipper',
origin: { Origins: 'SmallTipper' },
text: `Origin able to spend up to ${SPEND_LIMITS.SmallTipper} from the treasury at once`
},
{
id: 31,
name: 'big_tipper',
origin: { Origins: 'BigTipper' },
text: `Origin able to spend up to ${SPEND_LIMITS.BigTipper} from the treasury at once`
},
{
id: 32,
name: 'small_spender',
origin: { Origins: 'SmallSpender' },
text: `Origin able to spend up to ${SPEND_LIMITS.SmallSpender} from the treasury at once`
},
{
id: 33,
name: 'medium_spender',
origin: { Origins: 'MediumSpender' },
text: `Origin able to spend up to ${SPEND_LIMITS.MediumSpender} from the treasury at once`
},
{
id: 34,
name: 'big_spender',
origin: { Origins: 'BigSpender' },
text: `Origin able to spend up to ${SPEND_LIMITS.BigSpender} from the treasury at once`
}
]
};
@@ -4,17 +4,14 @@
import type { ApiPromise } from '@pezkuwi/api';
import type { TrackInfo } from './types.js';
import { DICLE_GENESIS, PEZKUWI_GENESIS } from '../../constants.js';
import { dicle } from './dicle.js';
import { PEZKUWI_GENESIS } from '../../constants.js';
import { pezkuwi } from './pezkuwi.js';
const KNOWN_GENE_TRACKS: Record<string, Record<string, TrackInfo[]>> = {
[DICLE_GENESIS]: dicle,
[PEZKUWI_GENESIS]: pezkuwi
};
const KNOWN_SPEC_TRACKS: Record<string, Record<string, TrackInfo[]>> = {
dicle,
// for kitchensink, we just use the root
node: {
referenda: [
@@ -20,6 +20,12 @@ describe('typesBundle', (): void => {
const typesBundle: { chain: Record<string, unknown>, spec: Record<string, unknown> } = { chain: {}, spec: {} };
afterAll((): void => {
// sort-keys has nothing to report (and its directive is flagged as unused) without entries
const isEmpty = !specEntries.length && !chainEntries.length;
const sortKeys = isEmpty
? ''
: '\n/* eslint-disable sort-keys */';
fs.writeFileSync('packages/apps-config/src/api/typesBundle.ts', `// Copyright 2017-2026 @pezkuwi/apps-config authors & contributors
// SPDX-License-Identifier: Apache-2.0
@@ -28,8 +34,7 @@ describe('typesBundle', (): void => {
import type { OverrideBundleType } from '@pezkuwi/types/types';
/* eslint-disable @stylistic/quotes */
/* eslint-disable @stylistic/quote-props */
/* eslint-disable sort-keys */
/* eslint-disable @stylistic/quote-props */${sortKeys}
export const typesBundle = ${JSON.stringify(typesBundle, null, 2)} as unknown as OverrideBundleType;
`);
@@ -7,7 +7,6 @@ import type { OverrideBundleType } from '@pezkuwi/types/types';
/* eslint-disable @stylistic/quotes */
/* eslint-disable @stylistic/quote-props */
/* eslint-disable sort-keys */
export const typesBundle = {
"chain": {},
+5 -17
View File
@@ -5,14 +5,12 @@
import '@pezkuwi/api-augment/bizinikiwi';
import type { BN } from '@pezkuwi/util';
import type { HexString } from '@pezkuwi/util/types';
import React, { useMemo, useRef } from 'react';
import { Route, Routes } from 'react-router';
import { getGenesis } from '@pezkuwi/apps-config';
import { Tabs } from '@pezkuwi/react-components';
import { useAccounts, useApi, useAssetIds, useAssetInfos } from '@pezkuwi/react-hooks';
import { useAccounts, useAssetIds, useAssetInfos } from '@pezkuwi/react-hooks';
import { BN_ONE } from '@pezkuwi/util';
import Balances from './Balances/index.js';
@@ -27,18 +25,11 @@ interface Props {
className?: string;
}
// Chains in which next asset id should be incremented from 1
const GENESIS_HASHES = [getGenesis('statemint'), getGenesis('statemine')];
function findOpenId (genesisHash: HexString, ids?: BN[]): BN {
function findOpenId (ids?: BN[]): BN {
if (!ids?.length) {
return BN_ONE;
}
if (GENESIS_HASHES.includes(genesisHash)) {
return ids.sort((a, b) => a.cmp(b))[ids.length - 1].add(BN_ONE);
}
const lastTaken = ids.find((id, index) =>
index === 0
? !id.eq(BN_ONE)
@@ -52,7 +43,6 @@ function findOpenId (genesisHash: HexString, ids?: BN[]): BN {
function AssetApp ({ basePath, className }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const { hasAccounts } = useAccounts();
const ids = useAssetIds();
const infos = useAssetInfos(ids);
@@ -86,11 +76,9 @@ function AssetApp ({ basePath, className }: Props): React.ReactElement<Props> {
);
const openId = useMemo(
() => findOpenId(
api.genesisHash.toHex(),
// Check if id is valid digit
ids?.filter((id) => /^\d{1,3}(,\d{3})*$/.test(id.toString()))),
[api.genesisHash, ids]
// Check if id is valid digit
() => findOpenId(ids?.filter((id) => /^\d{1,3}(,\d{3})*$/.test(id.toString()))),
[ids]
);
return (
-5
View File
@@ -137,11 +137,6 @@ function Row ({ chainRecord, highlight = false, id, lastCommittedTimeslice, leas
$p={highlight}
className='media--800'
>{<div style={{ alignItems: 'center', columnGap: '12px', display: 'flex', flexDirection: 'row', justifyContent: 'left' }}>
<ParaLink
id={new BN(id)}
showLogo={false}
type={ParaLinkType.SUBSCAN}
/>
<div style={{ marginBottom: '2px' }}>
<ParaLink
id={new BN(id)}
@@ -1,74 +0,0 @@
// Copyright 2017-2026 @pezkuwi/app-society authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { AccountId } from '@pezkuwi/types/interfaces';
import React, { useEffect, useRef, useState } from 'react';
import { DICLE_GENESIS } from '@pezkuwi/apps-config';
import { Button, Modal } from '@pezkuwi/react-components';
import { useApi, useToggle } from '@pezkuwi/react-hooks';
import drawCanary, { PADD, SIZE } from '../draw/canary.js';
import { useTranslation } from '../translate.js';
interface Props {
accountId: AccountId;
}
const CANVAS_STYLE = {
display: 'block',
margin: '0 auto'
};
const HEIGHT = (SIZE * 2) + (PADD * 1);
const WIDTH = (SIZE * 3) + (PADD * 2);
function DesignDicle ({ accountId }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const { api } = useApi();
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const [onDicle] = useState(() => api.genesisHash.eq(DICLE_GENESIS));
const [isShowing, toggleDesign] = useToggle();
useEffect((): void => {
if (canvasRef.current) {
const ctx = canvasRef.current.getContext('2d');
if (ctx) {
drawCanary(ctx, accountId);
}
}
});
if (!onDicle) {
return null;
}
return (
<>
<Button
icon='pen-nib'
onClick={toggleDesign}
/>
{isShowing && (
<Modal
header={t('design samples')}
onClose={toggleDesign}
size='large'
>
<Modal.Content>
<canvas
height={HEIGHT}
ref={canvasRef}
style={CANVAS_STYLE}
width={WIDTH}
/>
</Modal.Content>
</Modal>
)}
</>
);
}
export default React.memo(DesignDicle);
@@ -13,7 +13,6 @@ import { BlockToTime, FormatBalance } from '@pezkuwi/react-query';
import { formatNumber } from '@pezkuwi/util';
import { useTranslation } from '../translate.js';
import DesignDicle from './DesignDicle.js';
interface Props {
bestNumber?: BN;
@@ -141,9 +140,6 @@ function Member ({ bestNumber, className = '', value: { accountId, isCandidateVo
</td>
<td className='together'>{votedOn}</td>
<td className='number'>{formatNumber(strikes)}</td>
<td className='button start'>
<DesignDicle accountId={accountId} />
</td>
</StyledTr>
);
}
@@ -23,8 +23,7 @@ function Members ({ className = '', mapMembers }: Props): React.ReactElement<Pro
const headerRef = useRef<[React.ReactNode?, string?, number?][]>([
[t('members'), 'start', 2],
[t('voted on'), 'start'],
[t('strikes')],
[]
[t('strikes')]
]);
return (
-216
View File
@@ -1,216 +0,0 @@
// Copyright 2017-2026 @pezkuwi/app-society authors & contributors
// SPDX-License-Identifier: Apache-2.0
// Adapted (with permission) from https://www.w3schools.com/code/tryit.asp?filename=GGIGKE2GG7N1
import type { AccountId } from '@pezkuwi/types/interfaces';
// const DEFAULT_FN = (ctx: CanvasRenderingContext2D, path: Path2D) => ctx.fill(path);
export const PADD = 25;
export const SIZE = 300; // 250
function canary (ctx: CanvasRenderingContext2D, w: number, h: number, s: number, f: (ctx: CanvasRenderingContext2D, path: Path2D) => void): void {
const path = new Path2D('M373.1,126.9c-5.2-4.1-11.4-9.7-22.7-11.1c-10.6-1.4-21.4,5.7-28.7,10.4c-7.3,4.7-21.1,18.5-26.8,22.7 c-5.7,4.2-20.3,8.1-43.8,22.2s-115.7,73.3-115.7,73.3l24,0.3L52.4,299.8h10.7l-15.4,11.7c0,0,13.6,3.6,25-3.6l0,3.3 c0,0,127.4-50.2,152-37.2l-15,4.4c1.3,0,25.5,1.6,25.5,1.6s0.8,15.1,15.4,24.8c14.6,9.6,14.9,14.9,14.9,14.9s-7.6,3.1-7.6,7 c0,0,11.2-3.4,21.6-3.1c10.4,0.3,19.5,3.1,19.5,3.1s-0.8-4.2-10.9-7c-10.2-2.9-20.1-13.8-25-19.8c-4.9-6-8.3-16.7-4.1-27.4 c3.5-9.1,15.7-14.1,40.9-27.1c29.7-15.4,36.5-26.8,40.7-35.7c4.2-8.9,10.4-26.6,13.9-34.9c4.4-10.7,9.8-16.4,14.3-19.8 c4.4-3.4,24.5-10.9,24.5-10.9S378,130.8,373.1,126.9z');
ctx.save();
ctx.translate(w / 2, h / 2);
ctx.scale(s / 440, s / 440);
ctx.translate(-220, -220);
f(ctx, path);
ctx.restore();
}
function addressToBits (publicKey: Uint8Array): boolean[] {
return publicKey.reduce((bits: boolean[], byte): boolean[] => {
for (let j = 0; j < 8; ++j) {
bits.push((byte & (1 << (7 - j))) !== 0);
}
return bits;
}, []);
}
function ring (ctx: CanvasRenderingContext2D, r: number, bits: boolean[], f: (ctx: CanvasRenderingContext2D, on: boolean) => void): void {
ctx.save();
ctx.translate(0.5, 0.5);
for (let i = 0; i < bits.length; i++) {
ctx.save();
ctx.rotate(Math.PI * 2 / bits.length * i);
ctx.translate(0, -r);
f(ctx, bits[i]);
ctx.restore();
}
ctx.restore();
}
function splitRows (bits: boolean[], rows: number[]) {
let i = 0;
// eslint-disable-next-line no-return-assign
return rows.map((r) => bits.slice(i, i += r));
}
function tattoo (ctx: CanvasRenderingContext2D, bits: boolean[]): void {
const rows = splitRows(bits, [71, 61, 51, 41, 32]);
for (let i = 0; i < rows.length; ++i) {
ring(ctx, 0.5 - (31 / 500) * (i + 0.5), rows[i], (ctx, on) => {
if (on) {
ctx.beginPath();
ctx.arc(0, 0, 8 / 500, 0, 2 * Math.PI);
ctx.fillStyle = 'black';
ctx.fill();
}
});
}
ctx.lineWidth = 10;
canary(ctx, 1, 1, 200 / 500, (ctx, path) => ctx.stroke(path));
}
function tattooSpiro (ctx: CanvasRenderingContext2D, bits: boolean[]): void {
const cycles = 8;
const limit = 0.75;
const dot = 8 / 500;
ctx.save();
ctx.translate(0.5 - dot, 0.5 + dot);
ctx.fillStyle = 'black';
let radius = 0.5 - dot;
for (let i = 0, count = bits.length; i < count; i++) {
radius -= 0.5 / count * limit / (radius * 4);
ctx.rotate(Math.PI * 2 / count * cycles / (radius * 4));
ctx.save();
ctx.translate(0, -radius);
ctx.beginPath();
ctx.arc(0, 0, (bits[i] ? dot : dot / 2), 0, 2 * Math.PI);
ctx.fillStyle = bits[i] ? 'black' : '#86e62a';
ctx.fill();
ctx.restore();
}
ctx.restore();
ctx.lineWidth = 10;
canary(ctx, 1 - dot, 1 + dot, 220 / 500, (ctx, path) => ctx.stroke(path));
}
function tattooPink (ctx: CanvasRenderingContext2D, bits: boolean[]): void {
const rows = splitRows(bits, [71, 61, 51, 41, 32]);
for (let i = 0; i < rows.length; ++i) {
ring(ctx, 0.5 - (31 / 500) * (i + 0.5), rows[i], (ctx, on) => {
ctx.beginPath();
ctx.arc(0, 0, (on ? 8 : 4) / 500, 0, 2 * Math.PI);
ctx.fillStyle = on ? 'black' : '#86e62a';
ctx.fill();
});
}
canary(ctx, 1, 1, 220 / 500, (ctx, path) => ctx.fill(path));
}
function tattoo2 (ctx: CanvasRenderingContext2D, bits: boolean[]): void {
const rows = splitRows(bits, [64, 64, 64, 64]);
for (let i = 0; i < rows.length; ++i) {
ring(ctx, 0.5 - (36 / 500) * (i + 0.5), rows[i], (ctx, on) => {
if (on) {
ctx.beginPath();
ctx.moveTo(0, -18 / 500);
ctx.lineTo(0, 18 / 500);
ctx.lineWidth = 0.01;
ctx.stroke();
}
});
}
canary(ctx, 1, 1, 220 / 500, (ctx, path) => ctx.fill(path));
}
function tattoo2b (ctx: CanvasRenderingContext2D, bits: boolean[]): void {
const rows = splitRows(bits, [128, 128]);
for (let i = 0; i < rows.length; ++i) {
ring(ctx, 0.5 - (36 / 500) * (i + 0.5), rows[i], (ctx, on) => {
if (on) {
ctx.beginPath();
ctx.moveTo(0, -18 / 500);
ctx.lineTo(0, 18 / 500);
ctx.lineWidth = 0.01;
ctx.stroke();
}
});
}
ctx.lineWidth = 6;
canary(ctx, 1, 1, 350 / 500, (ctx, path) => ctx.stroke(path));
}
function tattoo3 (ctx: CanvasRenderingContext2D, bits: boolean[]): void {
ctx.lineWidth = 0.01;
for (let i = 0; i < 8; ++i) {
for (let j = 0; j < 32; ++j) {
if (bits[i * 32 + j]) {
ctx.save();
ctx.translate((j + 0.5) / 32, i / 8);
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(0, 1 / 8);
ctx.stroke();
ctx.restore();
}
}
}
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
canary(ctx, 1, 1, 1, (ctx, path) => ctx.fill(path));
ctx.lineWidth = 6;
canary(ctx, 1, 1, 1, (ctx, path) => ctx.stroke(path));
}
export default function draw (ctx: CanvasRenderingContext2D, accountId: AccountId): void {
console.log(`Generating ink for ${accountId.toString()} as ${accountId.toHex()}`);
const bits = addressToBits(accountId.toU8a());
ctx.save();
ctx.translate(0, 0);
ctx.scale(SIZE, SIZE);
tattoo(ctx, bits);
ctx.restore();
ctx.save();
ctx.translate(SIZE + PADD, 0);
ctx.scale(SIZE, SIZE);
tattooPink(ctx, bits);
ctx.restore();
ctx.save();
ctx.translate(0, SIZE + PADD);
ctx.scale(SIZE, SIZE);
tattoo2(ctx, bits);
ctx.restore();
ctx.save();
ctx.translate(SIZE + PADD, SIZE + PADD);
ctx.scale(SIZE, SIZE);
tattoo3(ctx, bits);
ctx.restore();
ctx.save();
ctx.translate((SIZE + PADD) * 2, 0);
ctx.scale(SIZE, SIZE);
tattooSpiro(ctx, bits);
ctx.restore();
ctx.save();
ctx.translate((SIZE + PADD) * 2, SIZE + PADD);
ctx.scale(SIZE, SIZE);
tattoo2b(ctx, bits);
ctx.restore();
}
+1 -21
View File
@@ -5,7 +5,6 @@ import type { BN } from '@pezkuwi/util';
import React, { useMemo } from 'react';
import { Subscan } from '@pezkuwi/apps-config/links/subscan';
import { useParaEndpoints } from '@pezkuwi/react-hooks';
import ChainImg from './ChainImg.js';
@@ -14,8 +13,7 @@ import { styled } from './styled.js';
export enum ParaLinkType {
PJS = 'pjs',
HOME = 'home',
SUBSCAN = 'subscan'
HOME = 'home'
}
interface Props {
@@ -40,11 +38,6 @@ function ParaLink ({ className, id, showLogo = true, type = ParaLinkType.PJS }:
? links[links.length - 1]
: endpoints[0];
const subscanUrl = text &&
typeof text === 'string' &&
Subscan.chains[text] &&
Subscan.create(Subscan.chains[text], '', '').toString();
return (
<StyledDiv className={className}>
{showLogo && (
@@ -57,19 +50,6 @@ function ParaLink ({ className, id, showLogo = true, type = ParaLinkType.PJS }:
{links.length
? (
<>
{type === ParaLinkType.SUBSCAN && !!subscanUrl && (
<a
href={subscanUrl}
rel='noopener noreferrer'
target='_blank'
>
<img
alt='Subscan'
height='20'
src={Subscan.ui.logo}
/>
</a>
)}
{type === ParaLinkType.HOME && homepage && (
<a
href={homepage}