feat: Add support for the Ledger Generic App (#1408)
* initial commit * issue with payload * test commit * cleanup * Update signer and get result back to client * Cleanup * removed getMetadataRaw and useMetadataRaw * updated metadata update message * added assert checks * updated prefix search for Ledger * fixed ledger account import --------- Co-authored-by: tarikgul <tariksnow37@gmail.com>
This commit is contained in:
parent
d751a923c5
commit
d2f164084c
@ -409,14 +409,14 @@ export default class Extension {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private signingApproveSignature ({ id, signature }: RequestSigningApproveSignature): boolean {
|
private signingApproveSignature ({ id, signature, signedTransaction }: RequestSigningApproveSignature): boolean {
|
||||||
const queued = this.#state.getSignRequest(id);
|
const queued = this.#state.getSignRequest(id);
|
||||||
|
|
||||||
assert(queued, 'Unable to find request');
|
assert(queued, 'Unable to find request');
|
||||||
|
|
||||||
const { resolve } = queued;
|
const { resolve } = queued;
|
||||||
|
|
||||||
resolve({ id, signature });
|
resolve({ id, signature, signedTransaction });
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -299,6 +299,7 @@ export interface RequestSigningApprovePassword {
|
|||||||
export interface RequestSigningApproveSignature {
|
export interface RequestSigningApproveSignature {
|
||||||
id: string;
|
id: string;
|
||||||
signature: HexString;
|
signature: HexString;
|
||||||
|
signedTransaction?: HexString;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RequestSigningCancel {
|
export interface RequestSigningCancel {
|
||||||
@ -358,6 +359,7 @@ export type TransportResponseMessage<TMessageType extends MessageTypes> =
|
|||||||
export interface ResponseSigning {
|
export interface ResponseSigning {
|
||||||
id: string;
|
id: string;
|
||||||
signature: HexString;
|
signature: HexString;
|
||||||
|
signedTransaction?: HexString;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResponseDeriveValidate {
|
export interface ResponseDeriveValidate {
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export function metadataExpand (definition: MetadataDef, isPartial = false): Cha
|
|||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { chain, genesisHash, icon, metaCalls, specVersion, ss58Format, tokenDecimals, tokenSymbol, types, userExtensions } = definition;
|
const { chain, genesisHash, icon, metaCalls, rawMetadata, specVersion, ss58Format, tokenDecimals, tokenSymbol, types, userExtensions } = definition;
|
||||||
const registry = new TypeRegistry();
|
const registry = new TypeRegistry();
|
||||||
|
|
||||||
if (!isPartial) {
|
if (!isPartial) {
|
||||||
@ -41,8 +41,8 @@ export function metadataExpand (definition: MetadataDef, isPartial = false): Cha
|
|||||||
|
|
||||||
const hasMetadata = !!metaCalls && !isPartial;
|
const hasMetadata = !!metaCalls && !isPartial;
|
||||||
|
|
||||||
if (hasMetadata) {
|
if (hasMetadata || !!rawMetadata) {
|
||||||
registry.setMetadata(new Metadata(registry, base64Decode(metaCalls)), undefined, userExtensions);
|
registry.setMetadata(new Metadata(registry, hasMetadata ? base64Decode(metaCalls) : rawMetadata), undefined, userExtensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isUnknown = genesisHash === '0x';
|
const isUnknown = genesisHash === '0x';
|
||||||
|
|||||||
@ -66,6 +66,7 @@ export interface MetadataDef extends MetadataDefBase {
|
|||||||
tokenSymbol: string;
|
tokenSymbol: string;
|
||||||
types: Record<string, Record<string, string> | string>;
|
types: Record<string, Record<string, string> | string>;
|
||||||
metaCalls?: string;
|
metaCalls?: string;
|
||||||
|
rawMetadata?: HexString;
|
||||||
userExtensions?: ExtDef;
|
userExtensions?: ExtDef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,7 @@
|
|||||||
"@fortawesome/free-solid-svg-icons": "^6.5.1",
|
"@fortawesome/free-solid-svg-icons": "^6.5.1",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
"@paraspell/xcm-analyser": "^1.3.0",
|
"@paraspell/xcm-analyser": "^1.3.0",
|
||||||
|
"@polkadot-api/merkleize-metadata": "^1.1.0",
|
||||||
"@polkadot/api": "^12.2.1",
|
"@polkadot/api": "^12.2.1",
|
||||||
"@polkadot/extension-base": "0.49.4-0-x",
|
"@polkadot/extension-base": "0.49.4-0-x",
|
||||||
"@polkadot/extension-chains": "0.49.4-0-x",
|
"@polkadot/extension-chains": "0.49.4-0-x",
|
||||||
|
|||||||
@ -70,7 +70,7 @@ function Request ({ className, metaId, request, url }: Props): React.ReactElemen
|
|||||||
</Table>
|
</Table>
|
||||||
<div className='requestInfo'>
|
<div className='requestInfo'>
|
||||||
<Warning className='requestWarning'>
|
<Warning className='requestWarning'>
|
||||||
{t('This approval will add the metadata to your extension instance, allowing future requests to be decoded using this metadata.')}
|
{t('This approval will add the metadata to your extension instance, allowing future requests to be decoded using this metadata. It will also allow the use of Ledger\'s Generic Polkadot App.')}
|
||||||
</Warning>
|
</Warning>
|
||||||
<Button
|
<Button
|
||||||
className='btnAccept'
|
className='btnAccept'
|
||||||
|
|||||||
@ -1,15 +1,19 @@
|
|||||||
// Copyright 2019-2024 @polkadot/extension-ui authors & contributors
|
// Copyright 2019-2024 @polkadot/extension-ui authors & contributors
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
import type { ExtrinsicPayload } from '@polkadot/types/interfaces';
|
import type { Chain } from '@polkadot/extension-chains/types';
|
||||||
|
import type { SignerPayloadJSON } from '@polkadot/types/types';
|
||||||
import type { HexString } from '@polkadot/util/types';
|
import type { HexString } from '@polkadot/util/types';
|
||||||
|
|
||||||
import { faSync } from '@fortawesome/free-solid-svg-icons';
|
import { faSync } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { assert, objectSpread, u8aToHex } from '@polkadot/util';
|
||||||
|
import { merkleizeMetadata } from '@polkadot-api/merkleize-metadata';
|
||||||
|
|
||||||
import { Button, Warning } from '../../components/index.js';
|
import { Button, Warning } from '../../components/index.js';
|
||||||
import { useLedger, useTranslation } from '../../hooks/index.js';
|
import { useLedger, useMetadata, useTranslation } from '../../hooks/index.js';
|
||||||
import { styled } from '../../styled.js';
|
import { styled } from '../../styled.js';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -18,21 +22,44 @@ interface Props {
|
|||||||
className?: string;
|
className?: string;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
genesisHash?: string;
|
genesisHash?: string;
|
||||||
onSignature?: ({ signature }: { signature: HexString }) => void;
|
onSignature?: ({ signature }: { signature: HexString }, signedTransaction: HexString) => void;
|
||||||
payload?: ExtrinsicPayload;
|
payload?: SignerPayloadJSON;
|
||||||
setError: (value: string | null) => void;
|
setError: (value: string | null) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMetadataProof (chain: Chain, payload: SignerPayloadJSON) {
|
||||||
|
const m = chain.definition.rawMetadata;
|
||||||
|
|
||||||
|
assert(m, 'To sign with Ledger\'s Polkadot Generic App, the metadata must be present in the extension.');
|
||||||
|
|
||||||
|
const merkleizedMetadata = merkleizeMetadata(m, {
|
||||||
|
base58Prefix: chain.ss58Format,
|
||||||
|
decimals: chain.tokenDecimals,
|
||||||
|
specName: chain.name.toLowerCase(),
|
||||||
|
specVersion: chain.specVersion,
|
||||||
|
tokenSymbol: chain.tokenSymbol
|
||||||
|
});
|
||||||
|
const metadataHash = u8aToHex(merkleizedMetadata.digest());
|
||||||
|
const newPayload = objectSpread<SignerPayloadJSON>({}, payload, { metadataHash, mode: 1 });
|
||||||
|
const raw = chain.registry.createType('ExtrinsicPayload', newPayload);
|
||||||
|
|
||||||
|
return {
|
||||||
|
raw,
|
||||||
|
txMetadata: merkleizedMetadata.getProofForExtrinsicPayload(u8aToHex(raw.toU8a(true)))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function LedgerSign ({ accountIndex, addressOffset, className, error, genesisHash, onSignature, payload, setError }: Props): React.ReactElement<Props> {
|
function LedgerSign ({ accountIndex, addressOffset, className, error, genesisHash, onSignature, payload, setError }: Props): React.ReactElement<Props> {
|
||||||
const [isBusy, setIsBusy] = useState(false);
|
const [isBusy, setIsBusy] = useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const chain = useMetadata(genesisHash);
|
||||||
const { error: ledgerError, isLoading: ledgerLoading, isLocked: ledgerLocked, ledger, refresh, warning: ledgerWarning } = useLedger(genesisHash, accountIndex, addressOffset);
|
const { error: ledgerError, isLoading: ledgerLoading, isLocked: ledgerLocked, ledger, refresh, warning: ledgerWarning } = useLedger(genesisHash, accountIndex, addressOffset);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ledgerError) {
|
if (ledgerError) {
|
||||||
setError(ledgerError);
|
setError(ledgerError);
|
||||||
}
|
}
|
||||||
}, [ledgerError, setError]);
|
}, [chain, ledgerError, setError]);
|
||||||
|
|
||||||
const _onRefresh = useCallback(() => {
|
const _onRefresh = useCallback(() => {
|
||||||
refresh();
|
refresh();
|
||||||
@ -41,21 +68,43 @@ function LedgerSign ({ accountIndex, addressOffset, className, error, genesisHas
|
|||||||
|
|
||||||
const _onSignLedger = useCallback(
|
const _onSignLedger = useCallback(
|
||||||
(): void => {
|
(): void => {
|
||||||
if (!ledger || !payload || !onSignature) {
|
if (!ledger || !payload || !onSignature || !chain) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!chain?.definition.rawMetadata) {
|
||||||
|
setError('No metadata found for this chain. You must upload the metadata to the extension in order to use Ledger.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { raw, txMetadata } = getMetadataProof(chain, payload);
|
||||||
|
|
||||||
|
const metaBuff = Buffer.from(txMetadata);
|
||||||
|
|
||||||
setError(null);
|
setError(null);
|
||||||
setIsBusy(true);
|
setIsBusy(true);
|
||||||
ledger.sign(payload.toU8a(true), accountIndex, addressOffset)
|
ledger.signWithMetadata(raw.toU8a(true), accountIndex, addressOffset, { metadata: metaBuff })
|
||||||
.then((signature) => {
|
.then((signature) => {
|
||||||
onSignature(signature);
|
const extrinsic = chain.registry.createType(
|
||||||
|
'Extrinsic',
|
||||||
|
{ method: raw.method },
|
||||||
|
{ version: 4 }
|
||||||
|
);
|
||||||
|
|
||||||
|
ledger.getAddress(chain.ss58Format, false, accountIndex, addressOffset)
|
||||||
|
.then(({ address }) => {
|
||||||
|
extrinsic.addSignature(address, signature.signature, raw.toHex());
|
||||||
|
onSignature(signature, extrinsic.toHex());
|
||||||
|
})
|
||||||
|
.catch((e: Error) => {
|
||||||
|
setError(e.message);
|
||||||
|
setIsBusy(false);
|
||||||
|
});
|
||||||
}).catch((e: Error) => {
|
}).catch((e: Error) => {
|
||||||
setError(e.message);
|
setError(e.message);
|
||||||
setIsBusy(false);
|
setIsBusy(false);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[accountIndex, addressOffset, ledger, onSignature, payload, setError]
|
[accountIndex, addressOffset, chain, ledger, onSignature, payload, setError]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -93,7 +142,7 @@ function LedgerSign ({ accountIndex, addressOffset, className, error, genesisHas
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default styled(LedgerSign)<Props>`
|
export default styled(LedgerSign) <Props>`
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 6px 24px;
|
padding: 6px 24px;
|
||||||
|
|
||||||
|
|||||||
@ -68,8 +68,8 @@ export default function Request ({ account: { accountIndex, addressOffset, genes
|
|||||||
}, [request]);
|
}, [request]);
|
||||||
|
|
||||||
const _onSignature = useCallback(
|
const _onSignature = useCallback(
|
||||||
({ signature }: { signature: HexString }): void => {
|
({ signature }: { signature: HexString }, signedTransaction?: HexString): void => {
|
||||||
approveSignSignature(signId, signature)
|
approveSignSignature(signId, signature, signedTransaction)
|
||||||
.then(() => onAction())
|
.then(() => onAction())
|
||||||
.catch((error: Error): void => {
|
.catch((error: Error): void => {
|
||||||
setError(error.message);
|
setError(error.message);
|
||||||
@ -117,7 +117,7 @@ export default function Request ({ account: { accountIndex, addressOffset, genes
|
|||||||
error={error}
|
error={error}
|
||||||
genesisHash={json.genesisHash}
|
genesisHash={json.genesisHash}
|
||||||
onSignature={_onSignature}
|
onSignature={_onSignature}
|
||||||
payload={payload}
|
payload={json}
|
||||||
setError={setError}
|
setError={setError}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -6,13 +6,16 @@
|
|||||||
/* eslint-disable deprecation/deprecation */
|
/* eslint-disable deprecation/deprecation */
|
||||||
|
|
||||||
import type { Network } from '@polkadot/networks/types';
|
import type { Network } from '@polkadot/networks/types';
|
||||||
|
import type { HexString } from '@polkadot/util/types';
|
||||||
|
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { Ledger } from '@polkadot/hw-ledger';
|
import { LedgerGeneric } from '@polkadot/hw-ledger';
|
||||||
|
import { knownLedger } from '@polkadot/networks/defaults';
|
||||||
import { settings } from '@polkadot/ui-settings';
|
import { settings } from '@polkadot/ui-settings';
|
||||||
import { assert } from '@polkadot/util';
|
import { assert } from '@polkadot/util';
|
||||||
|
|
||||||
|
import chains from '../../../extension-ui/src/util/chains';
|
||||||
import ledgerChains from '../util/legerChains.js';
|
import ledgerChains from '../util/legerChains.js';
|
||||||
import useTranslation from './useTranslation.js';
|
import useTranslation from './useTranslation.js';
|
||||||
|
|
||||||
@ -26,7 +29,7 @@ interface State extends StateBase {
|
|||||||
error: string | null;
|
error: string | null;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
isLocked: boolean;
|
isLocked: boolean;
|
||||||
ledger: Ledger | null;
|
ledger: LedgerGeneric | null;
|
||||||
refresh: () => void;
|
refresh: () => void;
|
||||||
warning: string | null;
|
warning: string | null;
|
||||||
}
|
}
|
||||||
@ -44,8 +47,8 @@ function getState (): StateBase {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function retrieveLedger (genesis: string): Ledger {
|
function retrieveLedger (genesis: string): LedgerGeneric {
|
||||||
let ledger: Ledger | null = null;
|
let ledger: LedgerGeneric | null = null;
|
||||||
|
|
||||||
const { isLedgerCapable } = getState();
|
const { isLedgerCapable } = getState();
|
||||||
|
|
||||||
@ -55,7 +58,11 @@ function retrieveLedger (genesis: string): Ledger {
|
|||||||
|
|
||||||
assert(def, 'There is no known Ledger app available for this chain');
|
assert(def, 'There is no known Ledger app available for this chain');
|
||||||
|
|
||||||
ledger = new Ledger('webusb', def.network);
|
assert(def.slip44, 'Slip44 is not available for this network, please report an issue to update this chains slip44');
|
||||||
|
|
||||||
|
// All chains use the `slip44` from polkadot in their derivation path in ledger.
|
||||||
|
// This interface is specific to the underlying PolkadotGenericApp.
|
||||||
|
ledger = new LedgerGeneric('webusb', def.network, knownLedger['polkadot']);
|
||||||
|
|
||||||
return ledger;
|
return ledger;
|
||||||
}
|
}
|
||||||
@ -68,6 +75,7 @@ export default function useLedger (genesis?: string | null, accountIndex = 0, ad
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [address, setAddress] = useState<string | null>(null);
|
const [address, setAddress] = useState<string | null>(null);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const ledger = useMemo(() => {
|
const ledger = useMemo(() => {
|
||||||
setError(null);
|
setError(null);
|
||||||
setIsLocked(false);
|
setIsLocked(false);
|
||||||
@ -102,7 +110,15 @@ export default function useLedger (genesis?: string | null, accountIndex = 0, ad
|
|||||||
setError(null);
|
setError(null);
|
||||||
setWarning(null);
|
setWarning(null);
|
||||||
|
|
||||||
ledger.getAddress(false, accountIndex, addressOffset)
|
// This is used with a genesisHash only when importing the Ledger account
|
||||||
|
// and when signing with Ledger. In both cases, the genesisHash is known and
|
||||||
|
// will be in this array.
|
||||||
|
const chosenNetwork = chains.find(({ genesisHash }) => genesisHash === genesis as HexString);
|
||||||
|
|
||||||
|
// Just in case, but this shouldn't be triggered
|
||||||
|
assert(chosenNetwork, t('This network is not available, please report an issue to update the known chains'));
|
||||||
|
|
||||||
|
ledger.getAddress(chosenNetwork.ss58Format, false, accountIndex, addressOffset)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
setAddress(res.address);
|
setAddress(res.address);
|
||||||
|
|||||||
@ -118,8 +118,8 @@ export async function approveSignPassword (id: string, savePass: boolean, passwo
|
|||||||
return sendMessage('pri(signing.approve.password)', { id, password, savePass });
|
return sendMessage('pri(signing.approve.password)', { id, password, savePass });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function approveSignSignature (id: string, signature: HexString): Promise<boolean> {
|
export async function approveSignSignature (id: string, signature: HexString, signedTransaction?: HexString): Promise<boolean> {
|
||||||
return sendMessage('pri(signing.approve.signature)', { id, signature });
|
return sendMessage('pri(signing.approve.signature)', { id, signature, signedTransaction });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createAccountExternal (name: string, address: string, genesisHash: HexString | null): Promise<boolean> {
|
export async function createAccountExternal (name: string, address: string, genesisHash: HexString | null): Promise<boolean> {
|
||||||
|
|||||||
@ -68,7 +68,6 @@
|
|||||||
"decimals": "",
|
"decimals": "",
|
||||||
"symbol": "",
|
"symbol": "",
|
||||||
"upgrade": "",
|
"upgrade": "",
|
||||||
"This approval will add the metadata to your extension instance, allowing future requests to be decoded using this metadata.": "",
|
|
||||||
"Yes, do this metadata update": "",
|
"Yes, do this metadata update": "",
|
||||||
"Reject": "",
|
"Reject": "",
|
||||||
"Restore from JSON": "",
|
"Restore from JSON": "",
|
||||||
@ -176,5 +175,8 @@
|
|||||||
"Is your ledger locked?": "",
|
"Is your ledger locked?": "",
|
||||||
"App \"{{network}}\" does not seem to be open": "",
|
"App \"{{network}}\" does not seem to be open": "",
|
||||||
"Ledger error: {{errorMessage}}": "",
|
"Ledger error: {{errorMessage}}": "",
|
||||||
"assetId": ""
|
"assetId": "",
|
||||||
|
"This approval will add the metadata to your extension instance, allowing future requests to be decoded using this metadata. It will also allow the use of Ledger's Generic Polkadot App.": "",
|
||||||
|
"No metadata found for this chain. You must upload the metadata to the extension in order to use Ledger.": "",
|
||||||
|
"This network is not available, please report an issue to update the known chains": ""
|
||||||
}
|
}
|
||||||
|
|||||||
30
yarn.lock
30
yarn.lock
@ -668,6 +668,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@polkadot-api/merkleize-metadata@npm:^1.1.0":
|
||||||
|
version: 1.1.1
|
||||||
|
resolution: "@polkadot-api/merkleize-metadata@npm:1.1.1"
|
||||||
|
dependencies:
|
||||||
|
"@polkadot-api/substrate-bindings": "npm:0.6.1"
|
||||||
|
"@polkadot-api/utils": "npm:0.1.1"
|
||||||
|
checksum: 10/dc0d1aa9e15049fdf263451ea1a8d7a4c5c5907e8b9b7ac8861f2e3281a82e99b5b07619a2c5cab105d8c14253007a006a7bc9d44fcaef1eb1d0356fc63d0485
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@polkadot-api/metadata-builders@npm:0.0.1":
|
"@polkadot-api/metadata-builders@npm:0.0.1":
|
||||||
version: 0.0.1
|
version: 0.0.1
|
||||||
resolution: "@polkadot-api/metadata-builders@npm:0.0.1"
|
resolution: "@polkadot-api/metadata-builders@npm:0.0.1"
|
||||||
@ -704,6 +714,18 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@polkadot-api/substrate-bindings@npm:0.6.1":
|
||||||
|
version: 0.6.1
|
||||||
|
resolution: "@polkadot-api/substrate-bindings@npm:0.6.1"
|
||||||
|
dependencies:
|
||||||
|
"@noble/hashes": "npm:^1.3.1"
|
||||||
|
"@polkadot-api/utils": "npm:0.1.1"
|
||||||
|
"@scure/base": "npm:^1.1.1"
|
||||||
|
scale-ts: "npm:^1.6.0"
|
||||||
|
checksum: 10/bce8bd7758174302b9e72da1addce04103ab0e9d0626ff2eacffdfb28c7a058071edaf4b47a7569bd7c8c73dd1595e1d78f90d419abafb86a6eadf078c83d1e4
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@polkadot-api/substrate-client@npm:0.0.1":
|
"@polkadot-api/substrate-client@npm:0.0.1":
|
||||||
version: 0.0.1
|
version: 0.0.1
|
||||||
resolution: "@polkadot-api/substrate-client@npm:0.0.1"
|
resolution: "@polkadot-api/substrate-client@npm:0.0.1"
|
||||||
@ -718,6 +740,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@polkadot-api/utils@npm:0.1.1":
|
||||||
|
version: 0.1.1
|
||||||
|
resolution: "@polkadot-api/utils@npm:0.1.1"
|
||||||
|
checksum: 10/ffd30039cd91396abb5528132bea9a72465eae9cdd088ecbd463ce962aa51c01b9e8c7d3e64c16c8438c0e55966c4d36fe4d18744a01b2b61f7e778f61dbf2b0
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@polkadot/api-augment@npm:12.2.1":
|
"@polkadot/api-augment@npm:12.2.1":
|
||||||
version: 12.2.1
|
version: 12.2.1
|
||||||
resolution: "@polkadot/api-augment@npm:12.2.1"
|
resolution: "@polkadot/api-augment@npm:12.2.1"
|
||||||
@ -996,6 +1025,7 @@ __metadata:
|
|||||||
"@fortawesome/free-solid-svg-icons": "npm:^6.5.1"
|
"@fortawesome/free-solid-svg-icons": "npm:^6.5.1"
|
||||||
"@fortawesome/react-fontawesome": "npm:^0.2.0"
|
"@fortawesome/react-fontawesome": "npm:^0.2.0"
|
||||||
"@paraspell/xcm-analyser": "npm:^1.3.0"
|
"@paraspell/xcm-analyser": "npm:^1.3.0"
|
||||||
|
"@polkadot-api/merkleize-metadata": "npm:^1.1.0"
|
||||||
"@polkadot/api": "npm:^12.2.1"
|
"@polkadot/api": "npm:^12.2.1"
|
||||||
"@polkadot/dev-test": "npm:^0.79.3"
|
"@polkadot/dev-test": "npm:^0.79.3"
|
||||||
"@polkadot/extension-base": "npm:0.49.4-0-x"
|
"@polkadot/extension-base": "npm:0.49.4-0-x"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user