diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2ccda76043..f5a11021ed 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -420,6 +420,35 @@ jobs: path: js/api-augment/dist if-no-files-found: error + build-js-schemas: + name: Build JS Schemas + runs-on: ubuntu-22.04 + container: ghcr.io/frequency-chain/frequency/ci-base-image:1.3.1 + steps: + - name: Check Out Repo + uses: actions/checkout@v4 + with: + ref: ${{env.RELEASE_BRANCH_NAME}} + - name: Set up NodeJs + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "npm" + registry-url: "https://registry.npmjs.org" + cache-dependency-path: js/schemas/package-lock.json + - name: Install + run: npm install + working-directory: js/schemas + - name: Build + run: npm run build + working-directory: js/schemas + - name: Upload Dist Dir + uses: actions/upload-artifact@v4 + with: + name: js-schemas-${{github.run_id}} + path: js/schemas/dist + if-no-files-found: error + test-version-matches-release: needs: build-binaries name: Test Version Matches Release @@ -619,6 +648,7 @@ jobs: test-version-matches-release, build-rust-developer-docs, build-js-api-augment, + build-js-schemas, ] name: Wait for All Builds to Finish runs-on: ubuntu-22.04 @@ -1051,3 +1081,44 @@ jobs: working-directory: ./js/api-augment/dist env: NODE_AUTH_TOKEN: ${{secrets.NODE_AUTH_TOKEN}} + + release-js-schemas: + needs: wait-for-all-builds + name: Release JS Schemas + runs-on: ubuntu-22.04 + steps: + - name: Check Out Repo + uses: actions/checkout@v4 + with: + ref: ${{env.RELEASE_BRANCH_NAME}} + - name: Set up NodeJs + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "npm" + registry-url: "https://registry.npmjs.org" + cache-dependency-path: js/schemas/package-lock.json + - name: Download Dist Dir + id: download + uses: actions/download-artifact@v4 + with: + name: js-schemas-${{github.run_id}} + path: js/schemas/dist + - name: Version Package + if: env.TEST_RUN != 'true' + run: npm version --new-version "${{env.NEW_RELEASE_TAG}}" --no-git-tag-version + working-directory: js/schemas/dist + - name: Release on NPM @latest + if: env.TEST_RUN != 'true' && + steps.is-full-release.outputs.is-full-release == 'true' + run: npm publish --tag latest --access public + working-directory: ./js/schemas/dist + env: + NODE_AUTH_TOKEN: ${{secrets.NODE_AUTH_TOKEN}} + - name: Release Candidate on NPM + if: env.TEST_RUN != 'true' && + steps.is-full-release.outputs.is-full-release != 'true' + run: npm publish --tag next --access public + working-directory: ./js/schemas/dist + env: + NODE_AUTH_TOKEN: ${{secrets.NODE_AUTH_TOKEN}} diff --git a/.github/workflows/verify-pr-commit.yml b/.github/workflows/verify-pr-commit.yml index 7c9932dde0..3f02924244 100644 --- a/.github/workflows/verify-pr-commit.yml +++ b/.github/workflows/verify-pr-commit.yml @@ -181,9 +181,10 @@ jobs: steps: - name: Check Out Repo uses: actions/checkout@v4 + # using older version of cargo deny since the new one requires rustc version >= 1.81 - name: Set Up Cargo Deny run: | - cargo install --force --locked cargo-deny + cargo install --force --locked cargo-deny@0.16.1 cargo generate-lockfile - name: Run Cargo Deny run: cargo deny check --hide-inclusion-graph -c deny.toml @@ -453,6 +454,44 @@ jobs: path: js/api-augment/dist/frequency-chain-api-augment-0.0.0.tgz if-no-files-found: error + verify-js-schemas: + name: Verify JS Schemas + runs-on: ubuntu-22.04 + container: ghcr.io/frequency-chain/frequency/ci-base-image:1.3.1 + steps: + - name: Check Out Repo + uses: actions/checkout@v4 + - name: Set up NodeJs + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "npm" + cache-dependency-path: js/schemas/package-lock.json + - name: Install Latest Versions + run: npm install + working-directory: js/schemas + - name: Lint + run: npm run lint + working-directory: js/schemas + - name: Test + run: npm test + working-directory: js/schemas + - name: Build + run: npm run build + working-directory: js/schemas + - name: Publish Dry Run + run: npm publish --dry-run + working-directory: js/schemas/dist + - name: Generate npm tarball + run: npm pack + working-directory: js/schemas/dist + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: artifacts-schemas-${{github.run_id}} + path: js/schemas/dist/frequency-chain-schemas-0.0.0.tgz + if-no-files-found: error + verify-node-docker-images: needs: build-binaries name: Verify Node Docker Images diff --git a/Cargo.lock b/Cargo.lock index b79199c0f1..5d0de21e55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7831,6 +7831,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "hex", "log", "parity-scale-codec", "pretty_assertions", diff --git a/e2e/capacity/capacityFail.test.ts b/e2e/capacity/capacityFail.test.ts index fefed7c903..d6b1a5c41c 100644 --- a/e2e/capacity/capacityFail.test.ts +++ b/e2e/capacity/capacityFail.test.ts @@ -19,6 +19,7 @@ import { assertAddNewKey, } from '../scaffolding/helpers'; import { getFundingSource } from '../scaffolding/funding'; +import { getUnifiedPublicKey } from '../scaffolding/ethereum'; const FUNDS_AMOUNT: bigint = 50n * DOLLARS; const fundingSource = getFundingSource('capacity-transactions-fail'); @@ -111,7 +112,7 @@ describe('Capacity Transaction Failures', function () { // As current owner, add a new set of control keys that do not have a balance. const newControlKeypair = createKeys('NewKeyNoBalance'); - const newPublicKey = newControlKeypair.publicKey; + const newPublicKey = getUnifiedPublicKey(newControlKeypair); const addKeyPayload: AddKeyData = await generateAddKeyPayload({ msaId: capacityProvider, newPublicKey: newPublicKey, @@ -193,7 +194,7 @@ describe('Capacity Transaction Failures', function () { // Add new key const newKeyPayload: AddKeyData = await generateAddKeyPayload({ msaId: new u64(ExtrinsicHelper.api.registry, capacityProvider), - newPublicKey: noTokensKeys.publicKey, + newPublicKey: getUnifiedPublicKey(noTokensKeys), }); const addKeyData = ExtrinsicHelper.api.registry.createType('PalletMsaAddKeyData', newKeyPayload); diff --git a/e2e/capacity/capacity_rpc.test.ts b/e2e/capacity/capacity_rpc.test.ts index acb5653dff..f83882a10c 100644 --- a/e2e/capacity/capacity_rpc.test.ts +++ b/e2e/capacity/capacity_rpc.test.ts @@ -13,6 +13,7 @@ import { } from '../scaffolding/helpers'; import { FeeDetails } from '@polkadot/types/interfaces'; import { getFundingSource } from '../scaffolding/funding'; +import { getUnifiedPublicKey } from '../scaffolding/ethereum'; const FUNDS_AMOUNT: bigint = 50n * DOLLARS; const fundingSource = getFundingSource('capacity-rpcs'); @@ -46,7 +47,7 @@ describe('Capacity RPC', function () { const addProviderData = ExtrinsicHelper.api.registry.createType('PalletMsaAddProvider', addProviderPayload); const delegatorKeys = createKeys('delegatorKeys'); const call = ExtrinsicHelper.api.tx.msa.createSponsoredAccountWithDelegation( - delegatorKeys.publicKey, + getUnifiedPublicKey(delegatorKeys), signPayloadSr25519(delegatorKeys, addProviderData), addProviderPayload ); @@ -89,7 +90,7 @@ describe('Capacity RPC', function () { const addProviderData = ExtrinsicHelper.api.registry.createType('PalletMsaAddProvider', addProviderPayload); const delegatorKeys = createKeys('delegatorKeys'); const insideTx = ExtrinsicHelper.api.tx.msa.createSponsoredAccountWithDelegation( - delegatorKeys.publicKey, + getUnifiedPublicKey(delegatorKeys), signPayloadSr25519(delegatorKeys, addProviderData), addProviderPayload ); diff --git a/e2e/capacity/transactions.test.ts b/e2e/capacity/transactions.test.ts index 1498e8e89b..b64cb6d507 100644 --- a/e2e/capacity/transactions.test.ts +++ b/e2e/capacity/transactions.test.ts @@ -38,6 +38,7 @@ import { } from '../scaffolding/helpers'; import { ipfsCid } from '../messages/ipfs'; import { getFundingSource } from '../scaffolding/funding'; +import { getUnifiedPublicKey } from '../scaffolding/ethereum'; const FUNDS_AMOUNT: bigint = 50n * DOLLARS; const fundingSource = getFundingSource('capacity-transactions'); @@ -92,7 +93,7 @@ describe('Capacity Transactions', function () { authorizedKeys.push(await createAndFundKeypair(fundingSource, 50_000_000n)); defaultPayload.msaId = capacityProvider; - defaultPayload.newPublicKey = authorizedKeys[0].publicKey; + defaultPayload.newPublicKey = getUnifiedPublicKey(authorizedKeys[0]); const payload = await generateAddKeyPayload(defaultPayload); const addKeyData = ExtrinsicHelper.api.registry.createType('PalletMsaAddKeyData', payload); diff --git a/e2e/capacity/transactionsBatch.test.ts b/e2e/capacity/transactionsBatch.test.ts index b8845411ee..6af3079a73 100644 --- a/e2e/capacity/transactionsBatch.test.ts +++ b/e2e/capacity/transactionsBatch.test.ts @@ -16,6 +16,7 @@ import { getTestHandle, } from '../scaffolding/helpers'; import { getFundingSource } from '../scaffolding/funding'; +import { getUnifiedPublicKey } from '../scaffolding/ethereum'; const FUNDS_AMOUNT: bigint = 50n * DOLLARS; const fundingSource = getFundingSource('capacity-transactions-batch'); @@ -49,7 +50,7 @@ describe('Capacity Transactions Batch', function () { const addProviderData = ExtrinsicHelper.api.registry.createType('PalletMsaAddProvider', addProviderPayload); const delegatorKeys = createKeys('delegatorKeys'); const createSponsoredAccountWithDelegation = ExtrinsicHelper.api.tx.msa.createSponsoredAccountWithDelegation( - delegatorKeys.publicKey, + getUnifiedPublicKey(delegatorKeys), signPayloadSr25519(delegatorKeys, addProviderData), addProviderPayload ); @@ -70,7 +71,7 @@ describe('Capacity Transactions Batch', function () { }; const claimHandle = ExtrinsicHelper.api.tx.handles.claimHandle( - delegatorKeys.publicKey, + getUnifiedPublicKey(delegatorKeys), claimHandleProof, claimHandlePayload ); @@ -95,7 +96,7 @@ describe('Capacity Transactions Batch', function () { const addProviderData = ExtrinsicHelper.api.registry.createType('PalletMsaAddProvider', addProviderPayload); const delegatorKeys = createKeys('delegatorKeys'); const createSponsoredAccountWithDelegation = ExtrinsicHelper.api.tx.msa.createSponsoredAccountWithDelegation( - delegatorKeys.publicKey, + getUnifiedPublicKey(delegatorKeys), signPayloadSr25519(delegatorKeys, addProviderData), addProviderPayload ); @@ -116,7 +117,7 @@ describe('Capacity Transactions Batch', function () { }; const claimHandle = ExtrinsicHelper.api.tx.handles.claimHandle( - delegatorKeys.publicKey, + getUnifiedPublicKey(delegatorKeys), calimHandleProof, claimHandlePayload ); diff --git a/e2e/eslint.config.mjs b/e2e/eslint.config.mjs index e8f62eccce..f1de04d4c0 100644 --- a/e2e/eslint.config.mjs +++ b/e2e/eslint.config.mjs @@ -51,6 +51,19 @@ export default tseslint.config( }, ], 'allow-namespace': 'off', + 'no-restricted-syntax': [ + 'error', + { + message: + 'Direct usage of keyPair.address is not allowed in this file. please use getUnifiedAddress function.', + selector: 'MemberExpression[property.name="address"]', + }, + { + message: + 'Direct usage of keyPair.publicKey is not allowed in this file. please use getUnifiedPublicKey function', + selector: 'MemberExpression[property.name="publicKey"]', + }, + ], }, } ); diff --git a/e2e/load-tests/signatureRegistry.test.ts b/e2e/load-tests/signatureRegistry.test.ts index ee2547edd2..789de5599d 100644 --- a/e2e/load-tests/signatureRegistry.test.ts +++ b/e2e/load-tests/signatureRegistry.test.ts @@ -13,7 +13,7 @@ import { KeyringPair } from '@polkadot/keyring/types'; import { AddKeyData, ExtrinsicHelper } from '../scaffolding/extrinsicHelpers'; import { u64, Option } from '@polkadot/types'; import { getFundingSource } from '../scaffolding/funding'; -import { getUnifiedAddress } from '../scaffolding/ethereum'; +import { getUnifiedAddress, getUnifiedPublicKey } from '../scaffolding/ethereum'; interface GeneratedMsa { id: u64; @@ -154,7 +154,7 @@ async function addSigs(msaId: u64, keys: KeyringPair, blockNumber: number, nonce const defaultPayload: AddKeyData = {}; defaultPayload.msaId = msaId; - defaultPayload.newPublicKey = newKeys.publicKey; + defaultPayload.newPublicKey = getUnifiedPublicKey(newKeys); const payload = await generateAddKeyPayload(defaultPayload, 100, blockNumber); const addKeyData = ExtrinsicHelper.api.registry.createType('PalletMsaAddKeyData', payload); diff --git a/e2e/miscellaneous/balance.ethereum.test.ts b/e2e/miscellaneous/balance.ethereum.test.ts new file mode 100644 index 0000000000..0cd67b9ff1 --- /dev/null +++ b/e2e/miscellaneous/balance.ethereum.test.ts @@ -0,0 +1,67 @@ +import '@frequency-chain/api-augment'; +import assert from 'assert'; +import { DOLLARS, createAndFundKeypair, createKeys } from '../scaffolding/helpers'; +import { KeyringPair } from '@polkadot/keyring/types'; +import { Extrinsic, ExtrinsicHelper } from '../scaffolding/extrinsicHelpers'; +import { getFundingSource } from '../scaffolding/funding'; +import { getUnifiedAddress } from '../scaffolding/ethereum'; + +const fundingSource: KeyringPair = getFundingSource('frequency-balance-ethereum'); + +describe('Balance transfer ethereum', function () { + describe('setup', function () { + let senderSr25519Keys: KeyringPair; + let senderEthereumKeys: KeyringPair; + let ethereumKeys: KeyringPair; + let ethereumKeys2: KeyringPair; + let sr25519Keys: KeyringPair; + + before(async function () { + senderSr25519Keys = await createAndFundKeypair(fundingSource, 30n * DOLLARS); + senderEthereumKeys = await createAndFundKeypair(fundingSource, 30n * DOLLARS, undefined, undefined, 'ethereum'); + ethereumKeys = await createKeys('another-key-1', 'ethereum'); + ethereumKeys2 = await createKeys('another-key-2', 'ethereum'); + sr25519Keys = await createKeys('another-sr25519', 'sr25519'); + }); + + it('should transfer from sr25519 to ethereum style key', async function () { + const transferAmount = 10n * DOLLARS; + const extrinsic = new Extrinsic( + () => ExtrinsicHelper.api.tx.balances.transferKeepAlive(getUnifiedAddress(ethereumKeys), transferAmount), + senderSr25519Keys, + ExtrinsicHelper.api.events.balances.Transfer + ); + const { target } = await extrinsic.signAndSend(); + assert.notEqual(target, undefined, 'should have returned Transfer event'); + const accountInfo = await ExtrinsicHelper.getAccountInfo(ethereumKeys); + assert(accountInfo.data.free.toBigInt() >= transferAmount); + }); + + it('should transfer from sr25519 to ethereum 20 byte address', async function () { + const transferAmount = 10n * DOLLARS; + const extrinsic = new Extrinsic( + // this is using MultiAddress::Address20 type in Rust since addressRaw is 20 bytes ethereum address + () => ExtrinsicHelper.api.tx.balances.transferKeepAlive(ethereumKeys2.addressRaw, transferAmount), + senderSr25519Keys, + ExtrinsicHelper.api.events.balances.Transfer + ); + const { target } = await extrinsic.signAndSend(); + assert.notEqual(target, undefined, 'should have returned Transfer event'); + const accountInfo = await ExtrinsicHelper.getAccountInfo(ethereumKeys2); + assert(accountInfo.data.free.toBigInt() >= transferAmount); + }); + + it('should transfer from an ethereum key to sr25519 key', async function () { + const transferAmount = 10n * DOLLARS; + const extrinsic = new Extrinsic( + () => ExtrinsicHelper.api.tx.balances.transferKeepAlive(getUnifiedAddress(sr25519Keys), transferAmount), + senderEthereumKeys, + ExtrinsicHelper.api.events.balances.Transfer + ); + const { target } = await extrinsic.signAndSend(); + assert.notEqual(target, undefined, 'should have returned Transfer event'); + const accountInfo = await ExtrinsicHelper.getAccountInfo(sr25519Keys); + assert(accountInfo.data.free.toBigInt() >= transferAmount); + }); + }); +}); diff --git a/e2e/miscellaneous/frequency.test.ts b/e2e/miscellaneous/frequency.test.ts index d0848032dc..ce3c3b4330 100644 --- a/e2e/miscellaneous/frequency.test.ts +++ b/e2e/miscellaneous/frequency.test.ts @@ -6,7 +6,7 @@ import { Extrinsic, ExtrinsicHelper } from '../scaffolding/extrinsicHelpers'; import { getFundingSource } from '../scaffolding/funding'; import { u8, Option } from '@polkadot/types'; import { u8aToHex } from '@polkadot/util/u8a/toHex'; -import { getUnifiedAddress } from '../scaffolding/ethereum'; +import { getUnifiedAddress, getUnifiedPublicKey } from '../scaffolding/ethereum'; const fundingSource: KeyringPair = getFundingSource('frequency-misc'); @@ -23,7 +23,7 @@ describe('Frequency', function () { it('Get events successfully', async function () { const balance_pallet = new u8(ExtrinsicHelper.api.registry, 10); const transfer_event = new u8(ExtrinsicHelper.api.registry, 2); - const dest_account = u8aToHex(keypairB.publicKey).slice(2); + const dest_account = u8aToHex(getUnifiedPublicKey(keypairB)).slice(2); const beforeBlockNumber = await getBlockNumber(); const extrinsic = new Extrinsic( @@ -66,7 +66,7 @@ describe('Frequency', function () { } // wait a little for all of the above transactions to get queued await new Promise((resolve) => setTimeout(resolve, 1000)); - const missingNonce = await ExtrinsicHelper.getMissingNonceValues(keypairB.publicKey); + const missingNonce = await ExtrinsicHelper.getMissingNonceValues(getUnifiedPublicKey(keypairB)); assert.equal(missingNonce.length, 4, 'Could not get missing nonce values'); // applying the missing nonce values to next transactions to unblock the stuck ones diff --git a/e2e/msa/keyManagement.ethereum.test.ts b/e2e/msa/keyManagement.ethereum.test.ts new file mode 100644 index 0000000000..c66dd2f996 --- /dev/null +++ b/e2e/msa/keyManagement.ethereum.test.ts @@ -0,0 +1,122 @@ +import '@frequency-chain/api-augment'; +import assert from 'assert'; +import { + createKeys, + createAndFundKeypair, + generateAddKeyPayload, + CENTS, + signPayload, + MultiSignatureType, +} from '../scaffolding/helpers'; +import { KeyringPair } from '@polkadot/keyring/types'; +import { AddKeyData, ExtrinsicHelper } from '../scaffolding/extrinsicHelpers'; +import { u64 } from '@polkadot/types'; +import { Codec } from '@polkadot/types/types'; +import { getFundingSource } from '../scaffolding/funding'; +import { getUnifiedPublicKey } from '../scaffolding/ethereum'; + +const maxU64 = 18_446_744_073_709_551_615n; +const fundingSource = getFundingSource('msa-key-management-ethereum'); + +describe('MSA Key management Ethereum', function () { + describe('addPublicKeyToMsa Ethereum', function () { + let keys: KeyringPair; + let msaId: u64; + let secondaryKey: KeyringPair; + const defaultPayload: AddKeyData = {}; + let payload: AddKeyData; + let ownerSig: MultiSignatureType; + let newSig: MultiSignatureType; + let badSig: MultiSignatureType; + let addKeyData: Codec; + + before(async function () { + // Setup an MSA with one key and a secondary funded key + keys = await createAndFundKeypair(fundingSource, 5n * CENTS, undefined, undefined, 'ethereum'); + const { target } = await ExtrinsicHelper.createMsa(keys).signAndSend(); + assert.notEqual(target?.data.msaId, undefined, 'MSA Id not in expected event'); + msaId = target!.data.msaId; + + secondaryKey = await createAndFundKeypair(fundingSource, 5n * CENTS, undefined, undefined, 'ethereum'); + + // Default payload making it easier to test `addPublicKeyToMsa` + defaultPayload.msaId = msaId; + defaultPayload.newPublicKey = getUnifiedPublicKey(secondaryKey); + }); + + beforeEach(async function () { + payload = await generateAddKeyPayload(defaultPayload); + }); + + it('should fail to add public key if origin is not one of the signers of the payload (MsaOwnershipInvalidSignature) for a Ethereum key', async function () { + const badKeys: KeyringPair = createKeys(); + addKeyData = ExtrinsicHelper.api.registry.createType('PalletMsaAddKeyData', payload); + newSig = signPayload(secondaryKey, addKeyData); + badSig = signPayload(badKeys, addKeyData); + const op = ExtrinsicHelper.addPublicKeyToMsa(keys, badSig, newSig, payload); + await assert.rejects(op.fundAndSend(fundingSource), { + name: 'MsaOwnershipInvalidSignature', + }); + }); + + it('should fail to add public key if origin does not own MSA (NotMsaOwner) for a Ethereum key', async function () { + const newPayload = await generateAddKeyPayload({ + ...defaultPayload, + msaId: new u64(ExtrinsicHelper.api.registry, maxU64), + }); + addKeyData = ExtrinsicHelper.api.registry.createType('PalletMsaAddKeyData', newPayload); + ownerSig = signPayload(keys, addKeyData); + newSig = signPayload(secondaryKey, addKeyData); + const op = ExtrinsicHelper.addPublicKeyToMsa(keys, ownerSig, newSig, newPayload); + await assert.rejects(op.fundAndSend(fundingSource), { + name: 'NotMsaOwner', + }); + }); + + it('should successfully add a new public key to an existing MSA & disallow duplicate signed payload submission (SignatureAlreadySubmitted) for a Ethereum key', async function () { + addKeyData = ExtrinsicHelper.api.registry.createType('PalletMsaAddKeyData', payload); + + ownerSig = signPayload(keys, addKeyData); + newSig = signPayload(secondaryKey, addKeyData); + const addPublicKeyOp = ExtrinsicHelper.addPublicKeyToMsa(keys, ownerSig, newSig, payload); + + const { target: publicKeyEvents } = await addPublicKeyOp.fundAndSend(fundingSource); + + assert.notEqual(publicKeyEvents, undefined, 'should have added public key'); + + await assert.rejects( + addPublicKeyOp.fundAndSend(fundingSource), + 'should reject sending the same signed payload twice' + ); + }); + + it('should fail if attempting to add the same key more than once (KeyAlreadyRegistered) for a Ethereum key', async function () { + const addKeyData = ExtrinsicHelper.api.registry.createType('PalletMsaAddKeyData', payload); + + const ownerSig = signPayload(keys, addKeyData); + const newSig = signPayload(secondaryKey, addKeyData); + const addPublicKeyOp = ExtrinsicHelper.addPublicKeyToMsa(keys, ownerSig, newSig, payload); + + await assert.rejects(addPublicKeyOp.fundAndSend(fundingSource), { + name: 'KeyAlreadyRegistered', + }); + }); + + it('should allow new keypair to act for/on MSA for a Ethereum key', async function () { + const thirdKey = createKeys(); + const newPayload = await generateAddKeyPayload({ + ...defaultPayload, + newPublicKey: getUnifiedPublicKey(thirdKey), + }); + addKeyData = ExtrinsicHelper.api.registry.createType('PalletMsaAddKeyData', newPayload); + ownerSig = signPayload(secondaryKey, addKeyData); + newSig = signPayload(thirdKey, addKeyData); + const op = ExtrinsicHelper.addPublicKeyToMsa(secondaryKey, ownerSig, newSig, newPayload); + const { target: event } = await op.fundAndSend(fundingSource); + assert.notEqual(event, undefined, 'should have added public key'); + + // Cleanup + await assert.doesNotReject(ExtrinsicHelper.deletePublicKey(keys, getUnifiedPublicKey(thirdKey)).signAndSend()); + }); + }); +}); diff --git a/e2e/msa/msaKeyManagement.test.ts b/e2e/msa/msaKeyManagement.test.ts index 672257346f..36a3f836c8 100644 --- a/e2e/msa/msaKeyManagement.test.ts +++ b/e2e/msa/msaKeyManagement.test.ts @@ -14,6 +14,7 @@ import { AddKeyData, ExtrinsicHelper } from '../scaffolding/extrinsicHelpers'; import { u64 } from '@polkadot/types'; import { Codec } from '@polkadot/types/types'; import { getFundingSource } from '../scaffolding/funding'; +import { getUnifiedPublicKey } from '../scaffolding/ethereum'; const maxU64 = 18_446_744_073_709_551_615n; const fundingSource = getFundingSource('msa-key-management'); @@ -41,7 +42,7 @@ describe('MSA Key management', function () { // Default payload making it easier to test `addPublicKeyToMsa` defaultPayload.msaId = msaId; - defaultPayload.newPublicKey = secondaryKey.publicKey; + defaultPayload.newPublicKey = getUnifiedPublicKey(secondaryKey); }); beforeEach(async function () { @@ -157,7 +158,7 @@ describe('MSA Key management', function () { const thirdKey = createKeys(); const newPayload = await generateAddKeyPayload({ ...defaultPayload, - newPublicKey: thirdKey.publicKey, + newPublicKey: getUnifiedPublicKey(thirdKey), }); addKeyData = ExtrinsicHelper.api.registry.createType('PalletMsaAddKeyData', newPayload); ownerSig = signPayloadSr25519(secondaryKey, addKeyData); @@ -167,7 +168,7 @@ describe('MSA Key management', function () { assert.notEqual(event, undefined, 'should have added public key'); // Cleanup - await assert.doesNotReject(ExtrinsicHelper.deletePublicKey(keys, thirdKey.publicKey).signAndSend()); + await assert.doesNotReject(ExtrinsicHelper.deletePublicKey(keys, getUnifiedPublicKey(thirdKey)).signAndSend()); }); }); @@ -200,7 +201,7 @@ describe('MSA Key management', function () { const payload = await generateAddKeyPayload({ msaId, - newPublicKey: secondaryKey.publicKey, + newPublicKey: getUnifiedPublicKey(secondaryKey), }); const payloadData = ExtrinsicHelper.api.registry.createType('PalletMsaAddKeyData', payload); const ownerSig = signPayloadSr25519(keys, payloadData); @@ -219,7 +220,7 @@ describe('MSA Key management', function () { }); it('should fail to delete public key for self', async function () { - const op = ExtrinsicHelper.deletePublicKey(keys, keys.publicKey); + const op = ExtrinsicHelper.deletePublicKey(keys, getUnifiedPublicKey(keys)); await assert.rejects(op.signAndSend('current'), { name: 'RpcError', message: /Custom error: 4/, @@ -229,7 +230,7 @@ describe('MSA Key management', function () { it("should fail to delete key if not authorized for key's MSA", async function () { const [providerKeys] = await createProviderKeysAndId(fundingSource); - const op = ExtrinsicHelper.deletePublicKey(providerKeys, keys.publicKey); + const op = ExtrinsicHelper.deletePublicKey(providerKeys, getUnifiedPublicKey(keys)); await assert.rejects(op.signAndSend('current'), { name: 'RpcError', message: /Custom error: 5/, @@ -238,7 +239,7 @@ describe('MSA Key management', function () { it("should test for 'NoKeyExists' error", async function () { const key = createKeys('nothing key'); - const op = ExtrinsicHelper.deletePublicKey(keys, key.publicKey); + const op = ExtrinsicHelper.deletePublicKey(keys, getUnifiedPublicKey(key)); await assert.rejects(op.signAndSend('current'), { name: 'RpcError', message: /Custom error: 1/, @@ -246,7 +247,7 @@ describe('MSA Key management', function () { }); it('should delete secondary key', async function () { - const op = ExtrinsicHelper.deletePublicKey(keys, secondaryKey.publicKey); + const op = ExtrinsicHelper.deletePublicKey(keys, getUnifiedPublicKey(secondaryKey)); const { target: event } = await op.signAndSend(); assert.notEqual(event, undefined, 'should have returned PublicKeyDeleted event'); }); diff --git a/e2e/passkey/passkeyProxy.ethereum.test.ts b/e2e/passkey/passkeyProxy.ethereum.test.ts index 2aa71b72e6..03daef3f2b 100644 --- a/e2e/passkey/passkeyProxy.ethereum.test.ts +++ b/e2e/passkey/passkeyProxy.ethereum.test.ts @@ -4,14 +4,13 @@ import { createAndFundKeypair, EcdsaSignature, getBlockNumber, - getNextEpochBlock, getNonce, Sr25519Signature, } from '../scaffolding/helpers'; import { KeyringPair } from '@polkadot/keyring/types'; import { ExtrinsicHelper } from '../scaffolding/extrinsicHelpers'; import { getFundingSource } from '../scaffolding/funding'; -import { getConvertedEthereumPublicKey, getUnifiedAddress } from '../scaffolding/ethereum'; +import { getUnifiedPublicKey, getUnifiedAddress } from '../scaffolding/ethereum'; import { createPassKeyAndSignAccount, createPassKeyCall, createPasskeyPayload } from '../scaffolding/P256'; import { u8aToHex, u8aWrapBytes } from '@polkadot/util'; const fundingSource = getFundingSource('passkey-proxy-ethereum'); @@ -25,12 +24,12 @@ describe('Passkey Pallet Ethereum Tests', function () { before(async function () { fundedSr25519Keys = await createAndFundKeypair(fundingSource, 300_000_000n); fundedEthereumKeys = await createAndFundKeypair(fundingSource, 300_000_000n, undefined, undefined, 'ethereum'); - receiverKeys = await createAndFundKeypair(fundingSource, undefined, undefined, undefined, 'ethereum'); + receiverKeys = await createAndFundKeypair(fundingSource); }); it('should transfer via passkeys with root sr25519 key into an ethereum style account', async function () { const initialReceiverBalance = await ExtrinsicHelper.getAccountInfo(receiverKeys); - const accountPKey = fundedSr25519Keys.publicKey; + const accountPKey = getUnifiedPublicKey(fundedSr25519Keys); const nonce = await getNonce(fundedSr25519Keys); const transferCalls = ExtrinsicHelper.api.tx.balances.transferKeepAlive( getUnifiedAddress(receiverKeys), @@ -51,7 +50,8 @@ describe('Passkey Pallet Ethereum Tests', function () { }); it('should transfer via passkeys with root ethereum style key into another one', async function () { - const accountPKey = getConvertedEthereumPublicKey(fundedEthereumKeys); + const initialReceiverBalance = await ExtrinsicHelper.getAccountInfo(receiverKeys); + const accountPKey = getUnifiedPublicKey(fundedEthereumKeys); console.log(`accountPKey ${u8aToHex(accountPKey)}`); const nonce = await getNonce(fundedEthereumKeys); const transferCalls = ExtrinsicHelper.api.tx.balances.transferKeepAlive( diff --git a/e2e/passkey/passkeyProxy.test.ts b/e2e/passkey/passkeyProxy.test.ts index 4b59e4e807..b467018aef 100644 --- a/e2e/passkey/passkeyProxy.test.ts +++ b/e2e/passkey/passkeyProxy.test.ts @@ -12,6 +12,7 @@ import { ExtrinsicHelper } from '../scaffolding/extrinsicHelpers'; import { getFundingSource } from '../scaffolding/funding'; import { u8aToHex, u8aWrapBytes } from '@polkadot/util'; import { createPassKeyAndSignAccount, createPassKeyCall, createPasskeyPayload } from '../scaffolding/P256'; +import { getUnifiedPublicKey } from '../scaffolding/ethereum'; const fundingSource = getFundingSource('passkey-proxy'); describe('Passkey Pallet Tests', function () { @@ -25,7 +26,7 @@ describe('Passkey Pallet Tests', function () { }); it('should fail due to unsupported call', async function () { - const accountPKey = fundedKeys.publicKey; + const accountPKey = getUnifiedPublicKey(fundedKeys); const nonce = await getNonce(fundedKeys); const remarksCalls = ExtrinsicHelper.api.tx.system.remark('passkey-test'); @@ -40,9 +41,9 @@ describe('Passkey Pallet Tests', function () { }); it('should fail to transfer balance due to bad account ownership proof', async function () { - const accountPKey = fundedKeys.publicKey; + const accountPKey = getUnifiedPublicKey(fundedKeys); const nonce = await getNonce(fundedKeys); - const transferCalls = ExtrinsicHelper.api.tx.balances.transferKeepAlive(receiverKeys.publicKey, 0n); + const transferCalls = ExtrinsicHelper.api.tx.balances.transferKeepAlive(getUnifiedPublicKey(receiverKeys), 0n); const { passKeyPrivateKey, passKeyPublicKey, passkeySignature } = createPassKeyAndSignAccount(accountPKey); const accountSignature = fundedKeys.sign('badPasskeyPublicKey'); const multiSignature: Sr25519Signature = { Sr25519: u8aToHex(accountSignature) }; @@ -54,9 +55,9 @@ describe('Passkey Pallet Tests', function () { }); it('should fail to transfer balance due to bad passkey signature', async function () { - const accountPKey = fundedKeys.publicKey; + const accountPKey = getUnifiedPublicKey(fundedKeys); const nonce = await getNonce(fundedKeys); - const transferCalls = ExtrinsicHelper.api.tx.balances.transferKeepAlive(receiverKeys.publicKey, 0n); + const transferCalls = ExtrinsicHelper.api.tx.balances.transferKeepAlive(getUnifiedPublicKey(receiverKeys), 0n); const { passKeyPrivateKey, passKeyPublicKey, passkeySignature } = createPassKeyAndSignAccount(accountPKey); const accountSignature = fundedKeys.sign(u8aWrapBytes(passKeyPublicKey)); const multiSignature: Sr25519Signature = { Sr25519: u8aToHex(accountSignature) }; @@ -68,9 +69,12 @@ describe('Passkey Pallet Tests', function () { }); it('should transfer small balance from fundedKeys to receiverKeys', async function () { - const accountPKey = fundedKeys.publicKey; + const accountPKey = getUnifiedPublicKey(fundedKeys); const nonce = await getNonce(fundedKeys); - const transferCalls = ExtrinsicHelper.api.tx.balances.transferKeepAlive(receiverKeys.publicKey, 100_000_000n); + const transferCalls = ExtrinsicHelper.api.tx.balances.transferKeepAlive( + getUnifiedPublicKey(receiverKeys), + 100_000_000n + ); const { passKeyPrivateKey, passKeyPublicKey } = createPassKeyAndSignAccount(accountPKey); const accountSignature = fundedKeys.sign(u8aWrapBytes(passKeyPublicKey)); const multiSignature: Sr25519Signature = { Sr25519: u8aToHex(accountSignature) }; diff --git a/e2e/scaffolding/P256.ts b/e2e/scaffolding/P256.ts index 25aa16ce69..3d3fc3cbd6 100644 --- a/e2e/scaffolding/P256.ts +++ b/e2e/scaffolding/P256.ts @@ -1,5 +1,5 @@ import { SubmittableExtrinsic } from '@polkadot/api/types'; -import { base64UrlToUint8Array, Sr25519Signature, Ed25519Signature, EcdsaSignature } from './helpers'; +import { base64UrlToUint8Array, MultiSignatureType } from './helpers'; import { secp256r1 } from '@noble/curves/p256'; import { ISubmittableResult } from '@polkadot/types/types'; import { u8aWrapBytes } from '@polkadot/util'; @@ -16,7 +16,7 @@ export function createPassKeyAndSignAccount(accountPKey: Uint8Array) { export async function createPassKeyCall( accountPKey: Uint8Array, nonce: number, - accountSignature: Sr25519Signature | Ed25519Signature | EcdsaSignature, + accountSignature: MultiSignatureType, call: SubmittableExtrinsic<'rxjs', ISubmittableResult> ) { const ext_call_type = ExtrinsicHelper.api.registry.createType('Call', call); diff --git a/e2e/scaffolding/ethereum.ts b/e2e/scaffolding/ethereum.ts index 69eecf7638..4137452ac2 100644 --- a/e2e/scaffolding/ethereum.ts +++ b/e2e/scaffolding/ethereum.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-restricted-syntax */ import { KeyringPair } from '@polkadot/keyring/types'; import { encodeAddress, ethereumEncode } from '@polkadot/util-crypto'; import { hexToU8a, u8aToHex } from '@polkadot/util'; @@ -8,17 +9,44 @@ import { Keyring } from '@polkadot/api'; import { Keypair } from '@polkadot/util-crypto/types'; import { Address20MultiAddress } from './helpers'; +/** + * Returns unified 32 bytes SS58 accountId + * @param pair + */ export function getUnifiedAddress(pair: KeyringPair): string { if ('ethereum' === pair.type) { const etheAddressHex = ethereumEncode(pair.publicKey); - return getConvertedEthereumAccount(etheAddressHex); + return getSS58AccountFromEthereumAccount(etheAddressHex); } if (pair.type === 'ecdsa') { - throw new Error(`ecdsa type is not supported!`); + throw new Error('Ecdsa key type is not supported and it should be replaced with ethereum ones!'); } return pair.address; } +/** + * Returns ethereum style public key with prefixed zeros example: 0x00000000000000000000000019a701d23f0ee1748b5d5f883cb833943096c6c4 + * @param pair + */ +export function getUnifiedPublicKey(pair: KeyringPair): Uint8Array { + if ('ethereum' === pair.type) { + const publicKeyBytes = hexToU8a(ethereumEncode(pair.publicKey)); + const result = new Uint8Array(32); + result.fill(0, 0, 12); + result.set(publicKeyBytes, 12); + return result; + } + if (pair.type === 'ecdsa') { + throw new Error('Ecdsa key type is not supported and it should be replaced with ethereum ones!'); + } + return pair.publicKey; +} + +/** + * This custom signer can get used to mimic EIP-191 message signing. By replacing the `ethereumPair.sign` with + * any wallet call we can sign any extrinsic with any wallet + * @param ethereumPair + */ export function getEthereumStyleSigner(ethereumPair: KeyringPair): Signer { return { signRaw: async (payload): Promise => { @@ -35,48 +63,37 @@ export function getEthereumStyleSigner(ethereumPair: KeyringPair): Signer { } /** - * This is a helper method to allow being able to create a signature that might be created by metamask - * @param hexPayload - */ -function wrapCustomFrequencyTag(hexPayload: string): Uint8Array { - // wrapping in frequency tags to show this is a Frequency related payload - const frequencyWrapped = `${hexPayload.toLowerCase()}`; - return prefixEthereumTags(frequencyWrapped); -} - -/** - * prefixing with the EIP-191 for personal_sign messages (this gets wrapped automatically in metamask) - * @param hexPayload + * Convert a keyPair into a 20 byte ethereum address + * @param pair */ -function prefixEthereumTags(hexPayload: string): Uint8Array { - const wrapped = `\x19Ethereum Signed Message:\n${hexPayload.length}${hexPayload}`; - const buffer = Buffer.from(wrapped, 'utf-8'); - return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.length); -} - export function getAccountId20MultiAddress(pair: KeyringPair): Address20MultiAddress { + if (pair.type !== 'ethereum') { + throw new Error(`Only ethereum keys are supported!`); + } const etheAddress = ethereumEncode(pair.publicKey); const ethAddress20 = Array.from(hexToU8a(etheAddress)); return { Address20: ethAddress20 }; } /** - * Returns ethereum style public key with prefixed zeros example: 0x00000000000000000000000019a701d23f0ee1748b5d5f883cb833943096c6c4 - * @param pair + * + * @param secretKey of secp256k1 keypair exported from any wallet (should be 32 bytes) */ -export function getConvertedEthereumPublicKey(pair: KeyringPair): Uint8Array { - const publicKeyBytes = hexToU8a(ethereumEncode(pair.publicKey)); - const result = new Uint8Array(32); - result.fill(0, 0, 12); - result.set(publicKeyBytes, 12); - return result; +export function getKeyringPairFromSecp256k1PrivateKey(secretKey: Uint8Array): KeyringPair { + const publicKey = secp256k1.getPublicKey(secretKey, true); + const keypair: Keypair = { + secretKey, + publicKey, + }; + const keyring = new Keyring({ type: 'ethereum' }); + return keyring.addFromPair(keypair, undefined, 'ethereum'); } /** * converts an ethereum account to SS58 format * @param accountId20Hex */ -function getConvertedEthereumAccount(accountId20Hex: string): string { +function getSS58AccountFromEthereumAccount(accountId20Hex: string): string { const addressBytes = hexToU8a(accountId20Hex); const result = new Uint8Array(32); result.fill(0, 0, 12); @@ -85,15 +102,21 @@ function getConvertedEthereumAccount(accountId20Hex: string): string { } /** - * - * @param secretKey of secp256k1 keypair exported from any wallet (should be 32 bytes) + * This is a helper method to allow being able to create a signature that might be created by Metamask + * @param hexPayload */ -export function getKeyringPairFromSecp256k1PrivateKey(secretKey: Uint8Array): KeyringPair { - const publicKey = secp256k1.getPublicKey(secretKey, true); - const keypair: Keypair = { - secretKey, - publicKey, - }; - const keyring = new Keyring({ type: 'ethereum' }); - return keyring.addFromPair(keypair, undefined, 'ethereum'); +function wrapCustomFrequencyTag(hexPayload: string): Uint8Array { + // wrapping in frequency tags to show this is a Frequency related payload + const frequencyWrapped = `${hexPayload.toLowerCase()}`; + return prefixEthereumTags(frequencyWrapped); +} + +/** + * prefixing with the EIP-191 for personal_sign messages (this gets wrapped automatically in metamask) + * @param hexPayload + */ +function prefixEthereumTags(hexPayload: string): Uint8Array { + const wrapped = `\x19Ethereum Signed Message:\n${hexPayload.length}${hexPayload}`; + const buffer = Buffer.from(wrapped, 'utf-8'); + return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.length); } diff --git a/e2e/scaffolding/extrinsicHelpers.ts b/e2e/scaffolding/extrinsicHelpers.ts index aff5840827..3c0231dabe 100644 --- a/e2e/scaffolding/extrinsicHelpers.ts +++ b/e2e/scaffolding/extrinsicHelpers.ts @@ -6,7 +6,7 @@ import { Compact, u128, u16, u32, u64, Vec, Option, Bool } from '@polkadot/types import { FrameSystemAccountInfo, PalletPasskeyPasskeyPayload, SpRuntimeDispatchError } from '@polkadot/types/lookup'; import { AnyJson, AnyNumber, AnyTuple, Codec, IEvent, ISubmittableResult } from '@polkadot/types/types'; import { firstValueFrom, filter, map, pipe, tap } from 'rxjs'; -import { getBlockNumber, getExistentialDeposit, getFinalizedBlockNumber, log, Sr25519Signature } from './helpers'; +import { getBlockNumber, getExistentialDeposit, getFinalizedBlockNumber, log, MultiSignatureType } from './helpers'; import autoNonce, { AutoNonce } from './autoNonce'; import { connect, connectPromise } from './apiConnection'; import { DispatchError, Event, Index, SignedBlock } from '@polkadot/types/interfaces'; @@ -24,7 +24,7 @@ import { u8aToHex } from '@polkadot/util/u8a/toHex'; import { u8aWrapBytes } from '@polkadot/util'; import type { AccountId32, Call, H256 } from '@polkadot/types/interfaces/runtime'; import { hasRelayChain } from './env'; -import { getUnifiedAddress } from './ethereum'; +import { getUnifiedAddress, getUnifiedPublicKey } from './ethereum'; export interface ReleaseSchedule { start: number; @@ -234,7 +234,7 @@ export class Extrinsic { return firstValueFrom( this.extrinsic() - .paymentInfo(this.keys) + .paymentInfo(getUnifiedAddress(this.keys)) .pipe(map((info) => info.partialFee.toBigInt())) ); } @@ -479,7 +479,7 @@ export class ExtrinsicHelper { return new Extrinsic( () => ExtrinsicHelper.api.tx.schemas.createSchemaViaGovernance( - keys.publicKey, + getUnifiedPublicKey(keys), JSON.stringify(model), modelType, payloadLocation, @@ -502,7 +502,7 @@ export class ExtrinsicHelper { return new Extrinsic( () => ExtrinsicHelper.api.tx.schemas.createSchemaViaGovernanceV2( - keys.publicKey, + getUnifiedPublicKey(keys), JSON.stringify(model), modelType, payloadLocation, @@ -526,12 +526,13 @@ export class ExtrinsicHelper { public static addPublicKeyToMsa( keys: KeyringPair, - ownerSignature: Sr25519Signature, - newSignature: Sr25519Signature, + ownerSignature: MultiSignatureType, + newSignature: MultiSignatureType, payload: AddKeyData ) { return new Extrinsic( - () => ExtrinsicHelper.api.tx.msa.addPublicKeyToMsa(keys.publicKey, ownerSignature, newSignature, payload), + () => + ExtrinsicHelper.api.tx.msa.addPublicKeyToMsa(getUnifiedPublicKey(keys), ownerSignature, newSignature, payload), keys, ExtrinsicHelper.api.events.msa.PublicKeyAdded ); @@ -560,12 +561,16 @@ export class ExtrinsicHelper { public static createSponsoredAccountWithDelegation( delegatorKeys: KeyringPair, providerKeys: KeyringPair, - signature: Sr25519Signature, + signature: MultiSignatureType, payload: AddProviderPayload ) { return new Extrinsic( () => - ExtrinsicHelper.api.tx.msa.createSponsoredAccountWithDelegation(delegatorKeys.publicKey, signature, payload), + ExtrinsicHelper.api.tx.msa.createSponsoredAccountWithDelegation( + getUnifiedPublicKey(delegatorKeys), + signature, + payload + ), providerKeys, ExtrinsicHelper.api.events.msa.MsaCreated ); @@ -574,11 +579,11 @@ export class ExtrinsicHelper { public static grantDelegation( delegatorKeys: KeyringPair, providerKeys: KeyringPair, - signature: Sr25519Signature, + signature: MultiSignatureType, payload: AddProviderPayload ) { return new Extrinsic( - () => ExtrinsicHelper.api.tx.msa.grantDelegation(delegatorKeys.publicKey, signature, payload), + () => ExtrinsicHelper.api.tx.msa.grantDelegation(getUnifiedPublicKey(delegatorKeys), signature, payload), providerKeys, ExtrinsicHelper.api.events.msa.DelegationGranted ); @@ -666,13 +671,13 @@ export class ExtrinsicHelper { public static applyItemActionsWithSignature( delegatorKeys: KeyringPair, providerKeys: KeyringPair, - signature: Sr25519Signature, + signature: MultiSignatureType, payload: ItemizedSignaturePayload ) { return new Extrinsic( () => ExtrinsicHelper.api.tx.statefulStorage.applyItemActionsWithSignature( - delegatorKeys.publicKey, + getUnifiedPublicKey(delegatorKeys), signature, payload ), @@ -684,13 +689,13 @@ export class ExtrinsicHelper { public static applyItemActionsWithSignatureV2( delegatorKeys: KeyringPair, providerKeys: KeyringPair, - signature: Sr25519Signature, + signature: MultiSignatureType, payload: ItemizedSignaturePayloadV2 ) { return new Extrinsic( () => ExtrinsicHelper.api.tx.statefulStorage.applyItemActionsWithSignatureV2( - delegatorKeys.publicKey, + getUnifiedPublicKey(delegatorKeys), signature, payload ), @@ -702,11 +707,16 @@ export class ExtrinsicHelper { public static deletePageWithSignature( delegatorKeys: KeyringPair, providerKeys: KeyringPair, - signature: Sr25519Signature, + signature: MultiSignatureType, payload: PaginatedDeleteSignaturePayload ) { return new Extrinsic( - () => ExtrinsicHelper.api.tx.statefulStorage.deletePageWithSignature(delegatorKeys.publicKey, signature, payload), + () => + ExtrinsicHelper.api.tx.statefulStorage.deletePageWithSignature( + getUnifiedPublicKey(delegatorKeys), + signature, + payload + ), providerKeys, ExtrinsicHelper.api.events.statefulStorage.PaginatedPageDeleted ); @@ -715,12 +725,16 @@ export class ExtrinsicHelper { public static deletePageWithSignatureV2( delegatorKeys: KeyringPair, providerKeys: KeyringPair, - signature: Sr25519Signature, + signature: MultiSignatureType, payload: PaginatedDeleteSignaturePayloadV2 ) { return new Extrinsic( () => - ExtrinsicHelper.api.tx.statefulStorage.deletePageWithSignatureV2(delegatorKeys.publicKey, signature, payload), + ExtrinsicHelper.api.tx.statefulStorage.deletePageWithSignatureV2( + getUnifiedPublicKey(delegatorKeys), + signature, + payload + ), providerKeys, ExtrinsicHelper.api.events.statefulStorage.PaginatedPageDeleted ); @@ -729,11 +743,16 @@ export class ExtrinsicHelper { public static upsertPageWithSignature( delegatorKeys: KeyringPair, providerKeys: KeyringPair, - signature: Sr25519Signature, + signature: MultiSignatureType, payload: PaginatedUpsertSignaturePayload ) { return new Extrinsic( - () => ExtrinsicHelper.api.tx.statefulStorage.upsertPageWithSignature(delegatorKeys.publicKey, signature, payload), + () => + ExtrinsicHelper.api.tx.statefulStorage.upsertPageWithSignature( + getUnifiedPublicKey(delegatorKeys), + signature, + payload + ), providerKeys, ExtrinsicHelper.api.events.statefulStorage.PaginatedPageUpdated ); @@ -742,12 +761,16 @@ export class ExtrinsicHelper { public static upsertPageWithSignatureV2( delegatorKeys: KeyringPair, providerKeys: KeyringPair, - signature: Sr25519Signature, + signature: MultiSignatureType, payload: PaginatedUpsertSignaturePayloadV2 ) { return new Extrinsic( () => - ExtrinsicHelper.api.tx.statefulStorage.upsertPageWithSignatureV2(delegatorKeys.publicKey, signature, payload), + ExtrinsicHelper.api.tx.statefulStorage.upsertPageWithSignatureV2( + getUnifiedPublicKey(delegatorKeys), + signature, + payload + ), providerKeys, ExtrinsicHelper.api.events.statefulStorage.PaginatedPageUpdated ); @@ -772,7 +795,7 @@ export class ExtrinsicHelper { public static claimHandle(delegatorKeys: KeyringPair, payload: any) { const proof = { Sr25519: u8aToHex(delegatorKeys.sign(u8aWrapBytes(payload.toU8a()))) }; return new Extrinsic( - () => ExtrinsicHelper.api.tx.handles.claimHandle(delegatorKeys.publicKey, proof, payload), + () => ExtrinsicHelper.api.tx.handles.claimHandle(getUnifiedPublicKey(delegatorKeys), proof, payload), delegatorKeys, ExtrinsicHelper.api.events.handles.HandleClaimed ); diff --git a/e2e/scaffolding/funding.ts b/e2e/scaffolding/funding.ts index 927acb51e1..0f18fab281 100644 --- a/e2e/scaffolding/funding.ts +++ b/e2e/scaffolding/funding.ts @@ -21,16 +21,20 @@ export const fundingSources = [ 'capacity-unstaking', 'check-metadata-hash', 'frequency-misc', + 'frequency-balance-ethereum', 'handles', 'load-signature-registry', 'messages-add-ipfs', 'misc-util-batch', 'msa-create-msa', 'msa-key-management', + 'msa-key-management-ethereum', 'passkey-proxy', 'passkey-proxy-ethereum', + 'stateful-storage-ethereum', 'proxy-pallet', 'scenarios-grant-delegation', + 'grant-delegation-ethereum', 'schemas-create', 'stateful-storage-handle-itemized', 'stateful-storage-handle-paginated', diff --git a/e2e/scaffolding/helpers.ts b/e2e/scaffolding/helpers.ts index 733066478d..f5b081a3f2 100644 --- a/e2e/scaffolding/helpers.ts +++ b/e2e/scaffolding/helpers.ts @@ -57,6 +57,8 @@ export interface EcdsaSignature { Ecdsa: `0x${string}`; } +export type MultiSignatureType = Sr25519Signature | Ed25519Signature | EcdsaSignature; + export interface Address20MultiAddress { Address20: number[]; } @@ -79,6 +81,19 @@ export function signPayloadSr25519(keys: KeyringPair, data: Codec): Sr25519Signa return { Sr25519: u8aToHex(keys.sign(u8aWrapBytes(data.toU8a()))) }; } +export function signPayload(keys: KeyringPair, data: Codec): MultiSignatureType { + switch (keys.type) { + case 'ecdsa': + throw new Error('Ecdsa key type is not supported and it should be replaced with ethereum ones!'); + case 'sr25519': + return { Sr25519: u8aToHex(keys.sign(u8aWrapBytes(data.toU8a()))) }; + case 'ed25519': + return { Ed25519: u8aToHex(keys.sign(u8aWrapBytes(data.toU8a()))) }; + case 'ethereum': + return { Ecdsa: u8aToHex(keys.sign(data.toU8a())) }; + } +} + export async function generateDelegationPayload( payloadInputs: AddProviderPayload, expirationOffset: number = 100, @@ -148,7 +163,7 @@ export async function generateItemizedActionsPayloadAndSignature( ) { const payloadData = await generateItemizedSignaturePayload(payloadInput); const payload = ExtrinsicHelper.api.registry.createType(payloadType, payloadData); - const signature = signPayloadSr25519(signingKeys, payload); + const signature = signPayload(signingKeys, payload); return { payload: payloadData, signature }; } @@ -341,8 +356,12 @@ export async function createProviderKeysAndId(source: KeyringPair, amount?: bigi return [providerKeys, providerId]; } -export async function createDelegator(source: KeyringPair, amount?: bigint): Promise<[KeyringPair, u64]> { - const keys = await createAndFundKeypair(source, amount); +export async function createDelegator( + source: KeyringPair, + amount?: bigint, + keyType: KeypairType = 'sr25519' +): Promise<[KeyringPair, u64]> { + const keys = await createAndFundKeypair(source, amount, undefined, undefined, keyType); const createMsa = ExtrinsicHelper.createMsa(keys); const { target: msaCreatedEvent } = await createMsa.fundAndSend(source); const delegatorMsaId = msaCreatedEvent?.data.msaId || new u64(ExtrinsicHelper.api.registry, 0); @@ -354,11 +373,11 @@ export async function createDelegatorAndDelegation( source: KeyringPair, schemaId: u16 | u16[], providerId: u64, - providerKeys: KeyringPair + providerKeys: KeyringPair, + keyType: KeypairType = 'sr25519' ): Promise<[KeyringPair, u64]> { // Create a delegator msa - const [keys, delegatorMsaId] = await createDelegator(source); - + const [keys, delegatorMsaId] = await createDelegator(source, undefined, keyType); // Grant delegation to the provider const payload = await generateDelegationPayload({ authorizedMsaId: providerId, @@ -369,7 +388,7 @@ export async function createDelegatorAndDelegation( const grantDelegationOp = ExtrinsicHelper.grantDelegation( keys, providerKeys, - signPayloadSr25519(keys, addProviderData), + signPayload(keys, addProviderData), payload ); await grantDelegationOp.fundAndSend(source); @@ -666,8 +685,8 @@ export async function assertAddNewKey( newControlKeypair: KeyringPair ) { const addKeyPayloadCodec: Codec = ExtrinsicHelper.api.registry.createType('PalletMsaAddKeyData', addKeyPayload); - const ownerSig: Sr25519Signature = signPayloadSr25519(capacityKeys, addKeyPayloadCodec); - const newSig: Sr25519Signature = signPayloadSr25519(newControlKeypair, addKeyPayloadCodec); + const ownerSig: MultiSignatureType = signPayload(capacityKeys, addKeyPayloadCodec); + const newSig: MultiSignatureType = signPayload(newControlKeypair, addKeyPayloadCodec); const addPublicKeyOp = ExtrinsicHelper.addPublicKeyToMsa(capacityKeys, ownerSig, newSig, addKeyPayload); const { eventMap } = await addPublicKeyOp.signAndSend(); assertEvent(eventMap, 'system.ExtrinsicSuccess'); diff --git a/e2e/scenarios/grantDelegation.ethereum.test.ts b/e2e/scenarios/grantDelegation.ethereum.test.ts new file mode 100644 index 0000000000..357a569143 --- /dev/null +++ b/e2e/scenarios/grantDelegation.ethereum.test.ts @@ -0,0 +1,183 @@ +import '@frequency-chain/api-augment'; +import { KeyringPair } from '@polkadot/keyring/types'; +import { u16, u64 } from '@polkadot/types'; +import assert from 'assert'; +import { AddProviderPayload, Extrinsic, ExtrinsicHelper } from '../scaffolding/extrinsicHelpers'; +import { + DOLLARS, + createAndFundKeypair, + createAndFundKeypairs, + generateDelegationPayload, + signPayload, +} from '../scaffolding/helpers'; +import { SchemaId } from '@frequency-chain/api-augment/interfaces'; +import { getFundingSource } from '../scaffolding/funding'; + +const fundingSource = getFundingSource('grant-delegation-ethereum'); + +describe('Delegation Scenario Tests Ethereum', function () { + let keys: KeyringPair; + let otherMsaKeys: KeyringPair; + let thirdMsaKeys: KeyringPair; + let noMsaKeys: KeyringPair; + let providerKeys: KeyringPair; + let otherProviderKeys: KeyringPair; + let schemaId: u16; + let schemaId2: SchemaId; + let providerId: u64; + let otherProviderId: u64; + let msaId: u64; + let otherMsaId: u64; + let thirdMsaId: u64; + + before(async function () { + // Fund all the different keys + [noMsaKeys, keys, otherMsaKeys, thirdMsaKeys, providerKeys, otherProviderKeys] = await createAndFundKeypairs( + fundingSource, + ['noMsaKeys', 'keys', 'otherMsaKeys', 'thirdMsaKeys', 'providerKeys', 'otherProviderKeys'], + 1n * DOLLARS, + 'ethereum' + ); + + const { target: msaCreatedEvent1 } = await ExtrinsicHelper.createMsa(keys).signAndSend(); + msaId = msaCreatedEvent1!.data.msaId; + + const { target: msaCreatedEvent2 } = await ExtrinsicHelper.createMsa(otherMsaKeys).signAndSend(); + otherMsaId = msaCreatedEvent2!.data.msaId; + + const { target: msaCreatedEvent3 } = await ExtrinsicHelper.createMsa(thirdMsaKeys).signAndSend(); + thirdMsaId = msaCreatedEvent3!.data.msaId; + + let createProviderMsaOp = ExtrinsicHelper.createMsa(providerKeys); + await createProviderMsaOp.signAndSend(); + let createProviderOp = ExtrinsicHelper.createProvider(providerKeys, 'MyPoster'); + let { target: providerEvent } = await createProviderOp.signAndSend(); + assert.notEqual(providerEvent, undefined, 'setup should return a ProviderCreated event'); + providerId = providerEvent!.data.providerId; + + createProviderMsaOp = ExtrinsicHelper.createMsa(otherProviderKeys); + await createProviderMsaOp.signAndSend(); + createProviderOp = ExtrinsicHelper.createProvider(otherProviderKeys, 'MyPoster'); + ({ target: providerEvent } = await createProviderOp.signAndSend()); + assert.notEqual(providerEvent, undefined, 'setup should return a ProviderCreated event'); + otherProviderId = providerEvent!.data.providerId; + + const schema = { + type: 'record', + name: 'Post', + fields: [ + { name: 'title', type: { name: 'Title', type: 'string' } }, + { name: 'content', type: { name: 'Content', type: 'string' } }, + { name: 'fromId', type: { name: 'DSNPId', type: 'fixed', size: 8 } }, + { name: 'objectId', type: 'DSNPId' }, + ], + }; + + schemaId = await ExtrinsicHelper.getOrCreateSchemaV3( + keys, + schema, + 'AvroBinary', + 'OnChain', + [], + 'test.grantDelegation' + ); + + schemaId2 = await ExtrinsicHelper.getOrCreateSchemaV3( + keys, + schema, + 'AvroBinary', + 'OnChain', + [], + 'test.grantDelegationSecond' + ); + }); + + describe('delegation grants for a Ethereum key', function () { + it('should fail to grant delegation if payload not signed by delegator (AddProviderSignatureVerificationFailed)', async function () { + const payload = await generateDelegationPayload({ + authorizedMsaId: providerId, + schemaIds: [schemaId], + }); + const addProviderData = ExtrinsicHelper.api.registry.createType('PalletMsaAddProvider', payload); + + const grantDelegationOp = ExtrinsicHelper.grantDelegation( + keys, + providerKeys, + signPayload(providerKeys, addProviderData), + payload + ); + await assert.rejects(grantDelegationOp.fundAndSend(fundingSource), { + name: 'AddProviderSignatureVerificationFailed', + }); + }); + + it('should fail to grant delegation if ID in payload does not match origin (UnauthorizedDelegator)', async function () { + const payload = await generateDelegationPayload({ + authorizedMsaId: otherMsaId, + schemaIds: [schemaId], + }); + const addProviderData = ExtrinsicHelper.api.registry.createType('PalletMsaAddProvider', payload); + + const grantDelegationOp = ExtrinsicHelper.grantDelegation( + keys, + providerKeys, + signPayload(keys, addProviderData), + payload + ); + await assert.rejects(grantDelegationOp.fundAndSend(fundingSource), { name: 'UnauthorizedDelegator' }); + }); + + it('should grant a delegation to a provider', async function () { + const payload = await generateDelegationPayload({ + authorizedMsaId: providerId, + schemaIds: [schemaId], + }); + const addProviderData = ExtrinsicHelper.api.registry.createType('PalletMsaAddProvider', payload); + + const grantDelegationOp = ExtrinsicHelper.grantDelegation( + keys, + providerKeys, + signPayload(keys, addProviderData), + payload + ); + const { target: grantDelegationEvent } = await grantDelegationOp.fundAndSend(fundingSource); + assert.notEqual(grantDelegationEvent, undefined, 'should have returned DelegationGranted event'); + assert.deepEqual(grantDelegationEvent?.data.providerId, providerId, 'provider IDs should match'); + assert.deepEqual(grantDelegationEvent?.data.delegatorId, msaId, 'delegator IDs should match'); + }); + }); + + describe('createSponsoredAccountWithDelegation', function () { + let sponsorKeys: KeyringPair; + let op: Extrinsic; + let defaultPayload: AddProviderPayload; + + before(async function () { + sponsorKeys = await createAndFundKeypair(fundingSource, 50_000_000n, undefined, undefined, 'ethereum'); + defaultPayload = { + authorizedMsaId: providerId, + schemaIds: [schemaId], + }; + }); + + it('should successfully create a delegated account', async function () { + const payload = await generateDelegationPayload(defaultPayload); + const addProviderData = ExtrinsicHelper.api.registry.createType('PalletMsaAddProvider', payload); + + op = ExtrinsicHelper.createSponsoredAccountWithDelegation( + sponsorKeys, + providerKeys, + signPayload(sponsorKeys, addProviderData), + payload + ); + const { target: event, eventMap } = await op.fundAndSend(fundingSource); + assert.notEqual(event, undefined, 'should have returned MsaCreated event'); + assert.notEqual(eventMap['msa.DelegationGranted'], undefined, 'should have returned DelegationGranted event'); + await assert.rejects( + op.fundAndSend(fundingSource), + { name: 'SignatureAlreadySubmitted' }, + 'should reject double submission' + ); + }); + }); +}); diff --git a/e2e/stateful-pallet-storage/stateful.ethereum.test.ts b/e2e/stateful-pallet-storage/stateful.ethereum.test.ts new file mode 100644 index 0000000000..9cbe260efc --- /dev/null +++ b/e2e/stateful-pallet-storage/stateful.ethereum.test.ts @@ -0,0 +1,193 @@ +// E2E tests for pallets/stateful-pallet-storage/handleItemizedWithSignature.ts +import '@frequency-chain/api-augment'; +import assert from 'assert'; +import { + DOLLARS, + createDelegatorAndDelegation, + createProviderKeysAndId, + generateItemizedActions, + generateItemizedActionsSignedPayloadV2, + generatePaginatedDeleteSignaturePayloadV2, + generatePaginatedUpsertSignaturePayloadV2, + getCurrentPaginatedHash, + signPayload, +} from '../scaffolding/helpers'; +import { KeyringPair } from '@polkadot/keyring/types'; +import { ExtrinsicHelper } from '../scaffolding/extrinsicHelpers'; +import { AVRO_CHAT_MESSAGE } from '../stateful-pallet-storage/fixtures/itemizedSchemaType'; +import { MessageSourceId, SchemaId } from '@frequency-chain/api-augment/interfaces'; +import { Bytes, u16 } from '@polkadot/types'; +import { getFundingSource } from '../scaffolding/funding'; + +const fundingSource = getFundingSource('stateful-storage-ethereum'); + +describe('📗 Stateful Pallet Storage Ethereum', function () { + let itemizedSchemaId: SchemaId; + let paginatedSchemaId: SchemaId; + let msa_id: MessageSourceId; + let undelegatedProviderId: MessageSourceId; + let undelegatedProviderKeys: KeyringPair; + let delegatedProviderId: MessageSourceId; + let delegatedProviderKeys: KeyringPair; + let ethereumDelegatorKeys: KeyringPair; + + before(async function () { + // Create a provider. This provider will NOT be granted delegations; + // methods requiring a payload signature do not require a delegation + [undelegatedProviderKeys, undelegatedProviderId] = await createProviderKeysAndId(fundingSource, 2n * DOLLARS); + assert.notEqual(undelegatedProviderId, undefined, 'setup should populate undelegatedProviderId'); + assert.notEqual(undelegatedProviderKeys, undefined, 'setup should populate undelegatedProviderKeys'); + + // Create a provider for the MSA, the provider will be used to grant delegation + [delegatedProviderKeys, delegatedProviderId] = await createProviderKeysAndId(fundingSource, 2n * DOLLARS); + assert.notEqual(delegatedProviderId, undefined, 'setup should populate delegatedProviderId'); + assert.notEqual(delegatedProviderKeys, undefined, 'setup should populate delegatedProviderKeys'); + + // Create a schema for Itemized PayloadLocation + itemizedSchemaId = await ExtrinsicHelper.getOrCreateSchemaV3( + undelegatedProviderKeys, + AVRO_CHAT_MESSAGE, + 'AvroBinary', + 'Itemized', + ['AppendOnly', 'SignatureRequired'], + 'test.ItemizedSignatureRequired' + ); + + // Create a schema for Paginated PayloadLocation + paginatedSchemaId = await ExtrinsicHelper.getOrCreateSchemaV3( + undelegatedProviderKeys, + AVRO_CHAT_MESSAGE, + 'AvroBinary', + 'Paginated', + ['SignatureRequired'], + 'test.PaginatedSignatureRequired' + ); + + // Create a MSA for the delegator + [ethereumDelegatorKeys, msa_id] = await createDelegatorAndDelegation( + fundingSource, + [itemizedSchemaId, paginatedSchemaId], + delegatedProviderId, + delegatedProviderKeys, + 'ethereum' + ); + console.log('after createDelegatorAndDelegation'); + assert.notEqual(ethereumDelegatorKeys, undefined, 'setup should populate delegator_key'); + assert.notEqual(msa_id, undefined, 'setup should populate msa_id'); + }); + + describe('Itemized With Signature Storage Tests', function () { + it('provider should be able to call applyItemizedActionWithSignatureV2 and apply actions with Ethereum keys', async function () { + const { payload, signature } = await generateItemizedActionsSignedPayloadV2( + generateItemizedActions([ + { action: 'Add', value: 'Hello, world from Frequency' }, + { action: 'Add', value: 'Hello, world again from Frequency' }, + ]), + itemizedSchemaId, + ethereumDelegatorKeys, + msa_id + ); + + const itemized_add_result_1 = ExtrinsicHelper.applyItemActionsWithSignatureV2( + ethereumDelegatorKeys, + undelegatedProviderKeys, + signature, + payload + ); + const { target: pageUpdateEvent1, eventMap: chainEvents } = + await itemized_add_result_1.fundAndSend(fundingSource); + assert.notEqual( + chainEvents['system.ExtrinsicSuccess'], + undefined, + 'should have returned an ExtrinsicSuccess event' + ); + assert.notEqual( + chainEvents['transactionPayment.TransactionFeePaid'], + undefined, + 'should have returned a TransactionFeePaid event' + ); + assert.notEqual( + pageUpdateEvent1, + undefined, + 'should have returned a PalletStatefulStorageItemizedActionApplied event' + ); + }); + }); + + describe('Paginated With Signature Storage Tests with Ethereum keys', function () { + it('provider should be able to call upsertPageWithSignatureV2 a page and deletePageWithSignatureV2 it successfully with Ethereum keys', async function () { + const page_id = new u16(ExtrinsicHelper.api.registry, 1); + + // Add and update actions + let target_hash = await getCurrentPaginatedHash(msa_id, paginatedSchemaId, page_id.toNumber()); + const upsertPayload = await generatePaginatedUpsertSignaturePayloadV2({ + targetHash: target_hash, + schemaId: paginatedSchemaId, + pageId: page_id, + payload: new Bytes(ExtrinsicHelper.api.registry, 'Hello World From Frequency'), + }); + const upsertPayloadData = ExtrinsicHelper.api.registry.createType( + 'PalletStatefulStoragePaginatedUpsertSignaturePayloadV2', + upsertPayload + ); + const upsert_result = ExtrinsicHelper.upsertPageWithSignatureV2( + ethereumDelegatorKeys, + undelegatedProviderKeys, + signPayload(ethereumDelegatorKeys, upsertPayloadData), + upsertPayload + ); + const { target: pageUpdateEvent, eventMap: chainEvents1 } = await upsert_result.fundAndSend(fundingSource); + assert.notEqual( + chainEvents1['system.ExtrinsicSuccess'], + undefined, + 'should have returned an ExtrinsicSuccess event' + ); + assert.notEqual( + chainEvents1['transactionPayment.TransactionFeePaid'], + undefined, + 'should have returned a TransactionFeePaid event' + ); + assert.notEqual( + pageUpdateEvent, + undefined, + 'should have returned a PalletStatefulStoragePaginatedPageUpdate event' + ); + + // Remove the page + target_hash = await getCurrentPaginatedHash(msa_id, paginatedSchemaId, page_id.toNumber()); + const deletePayload = await generatePaginatedDeleteSignaturePayloadV2({ + targetHash: target_hash, + schemaId: paginatedSchemaId, + pageId: page_id, + }); + const deletePayloadData = ExtrinsicHelper.api.registry.createType( + 'PalletStatefulStoragePaginatedDeleteSignaturePayloadV2', + deletePayload + ); + const remove_result = ExtrinsicHelper.deletePageWithSignatureV2( + ethereumDelegatorKeys, + undelegatedProviderKeys, + signPayload(ethereumDelegatorKeys, deletePayloadData), + deletePayload + ); + const { target: pageRemove, eventMap: chainEvents2 } = await remove_result.fundAndSend(fundingSource); + assert.notEqual( + chainEvents2['system.ExtrinsicSuccess'], + undefined, + 'should have returned an ExtrinsicSuccess event' + ); + assert.notEqual( + chainEvents2['transactionPayment.TransactionFeePaid'], + undefined, + 'should have returned a TransactionFeePaid event' + ); + assert.notEqual(pageRemove, undefined, 'should have returned a event'); + + // no pages should exist + const result = await ExtrinsicHelper.getPaginatedStorage(msa_id, paginatedSchemaId); + assert.notEqual(result, undefined, 'should have returned a valid response'); + const thePage = result.toArray().find((page) => page.page_id === page_id); + assert.equal(thePage, undefined, 'inserted page should not exist'); + }); + }); +}); diff --git a/js/schemas/.gitignore b/js/schemas/.gitignore new file mode 100644 index 0000000000..d1db1b8a78 --- /dev/null +++ b/js/schemas/.gitignore @@ -0,0 +1,312 @@ +# Generated Files +metadata.* +interfaces/* + + +# Log Files +*.log + +# All of IDEA +.idea/* + +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive +ltximg/** + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + + +### Git ### +# Created by git for backups. To disable backups in Git: +# $ git config --global mergetool.keepBackup false +*.orig + +# Created by git when using merge tools for conflicts +*.BACKUP.* +*.BASE.* +*.LOCAL.* +*.REMOTE.* +*_BACKUP_*.txt +*_BASE_*.txt +*_LOCAL_*.txt +*_REMOTE_*.txt + +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij+all Patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test +.env*.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +### vscode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# End of https://www.toptal.com/developers/gitignore/api/intellij+all,vim,emacs,vscode,git,node + +junit.xml diff --git a/js/schemas/.mocharc.json b/js/schemas/.mocharc.json new file mode 100644 index 0000000000..53ebf896a5 --- /dev/null +++ b/js/schemas/.mocharc.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://json.schemastore.org/mocharc", + "extension": ["ts"], + "spec": ["test"] +} diff --git a/js/schemas/.node-version b/js/schemas/.node-version new file mode 100644 index 0000000000..87834047a6 --- /dev/null +++ b/js/schemas/.node-version @@ -0,0 +1 @@ +20.12.2 diff --git a/js/schemas/.prettierignore b/js/schemas/.prettierignore new file mode 100644 index 0000000000..2cf62f2962 --- /dev/null +++ b/js/schemas/.prettierignore @@ -0,0 +1,10 @@ +# package.json is formatted by package managers, so we ignore it here +package.json +build/* +coverage/* +multimodules/* +/*.d.ts +/*.map +/*.js +*.md +eslint.config.mjs diff --git a/js/schemas/.prettierrc.json b/js/schemas/.prettierrc.json new file mode 100644 index 0000000000..cd87a91cb1 --- /dev/null +++ b/js/schemas/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "trailingComma": "es5", + "tabWidth": 2, + "semi": true, + "singleQuote": true, + "useTabs": false, + "printWidth": 120 +} diff --git a/js/schemas/.tool-versions b/js/schemas/.tool-versions new file mode 100644 index 0000000000..958fb36989 --- /dev/null +++ b/js/schemas/.tool-versions @@ -0,0 +1 @@ +nodejs 20.12.2 diff --git a/js/schemas/CONTRIBUTING.md b/js/schemas/CONTRIBUTING.md new file mode 100644 index 0000000000..de3c703d41 --- /dev/null +++ b/js/schemas/CONTRIBUTING.md @@ -0,0 +1,12 @@ +# CONTRIBUTING + +For contributing guidelines see the [Project Liberty Contributing Guidelines](https://github.com/LibertyDSNP/meta/blob/main/CONTRIBUTING.md). + +## Running Tests +- `js/schemas` folder +- `npm run tests` + +### To generate dsnp schemas from mainnet +- inside `js/schemas/scripts` +- run `npm run generate` +- check the newly generated `data.ts` file inside `js/schemas` diff --git a/js/schemas/README.md b/js/schemas/README.md new file mode 100644 index 0000000000..aad0734df8 --- /dev/null +++ b/js/schemas/README.md @@ -0,0 +1,89 @@ +# Static Schemas package + + + + +[![Contributors][contributors-shield]][contributors-url] +[![Forks][forks-shield]][forks-url] +[![Stargazers][stars-shield]][stars-url] +[![Issues][issues-shield]][issues-url] +[![MIT License][license-shield]][license-url] +[![NPM @latest][npm-shield]][npm-url] +[![NPM @next][npm-next-shield]][npm-next-url] + +# Static schemas + +A convenient way to get schema details locally with TypeScript. + + + +## Getting Started + +- `npm install @frequency-chain/schemas` (static schemas library) + +## Usage +After importing, any of the following **Maps** can be used to fetch desired schema information. + +- `ID_TO_SCHEMA_FULL_NAME` is a **Map** that returns full names from schema ids (example `dsnp.tombstone@v1`) +- `FULL_NAME_TO_ID` is a **Map** that returns schema id from full name +- `ID_TO_SCHEMA_INFO` is a **Map** that return `SchemaInfo` from schema id + +Here is an example of a schema info object + +```javascript + { + id: 7, + namespace: 'dsnp', + name: 'public-key-key-agreement', + version: 1, + deprecated: false, + modelType: 'AvroBinary', + payloadLocation: 'Itemized', + appendOnly: true, + signatureRequired: true, + }, +``` + +## Upgrades and Matching Versions + +Assuming you are using no deprecated methods, any release version of `@frequency-chain/schemas` should work against a release version of a Frequency node. +If you are working against a development node it is suggested that you match the package version against the commit hash using `v0.0.0-[First 6 of the commit hash]`. + +Changelog is maintained in the [releases for Frequency](https://github.com/frequency-chain/frequency/releases). + + + + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) for more information. + + + +## License + +Distributed under the Apache 2.0 License. See `LICENSE` for more information. + + + + +[contributors-shield]: https://img.shields.io/github/contributors/frequency-chain/frequency.svg?style=for-the-badge +[contributors-url]: https://github.com/frequency-chain/frequency/graphs/contributors +[forks-shield]: https://img.shields.io/github/forks/frequency-chain/frequency.svg?style=for-the-badge +[forks-url]: https://github.com/frequency-chain/frequency/network/members +[stars-shield]: https://img.shields.io/github/stars/frequency-chain/frequency.svg?style=for-the-badge +[stars-url]: https://github.com/frequency-chain/frequency/stargazers +[issues-shield]: https://img.shields.io/github/issues/frequency-chain/frequency.svg?style=for-the-badge +[issues-url]: https://github.com/frequency-chain/frequency/issues +[license-shield]: https://img.shields.io/github/license/frequency-chain/frequency.svg?style=for-the-badge +[license-url]: https://github.com/frequency-chain/frequency/blob/master/LICENSE +[npm-shield]: https://img.shields.io/npm/v/@frequency-chain/schemas?label=npm%20%40latest&style=for-the-badge +[npm-url]: https://www.npmjs.com/package/@frequency-chain/schemas +[npm-next-shield]: https://img.shields.io/npm/v/@frequency-chain/schemas/next?label=npm%20%40next&style=for-the-badge +[npm-next-url]: https://www.npmjs.com/package/@frequency-chain/schemas diff --git a/js/schemas/data.ts b/js/schemas/data.ts new file mode 100644 index 0000000000..5f472db662 --- /dev/null +++ b/js/schemas/data.ts @@ -0,0 +1,224 @@ +import { SchemaInfo } from './schemas'; + +export const SCHEMA_INFOS: SchemaInfo[] = [ + { + id: 11, + namespace: 'dsnp', + name: 'user-attribute-set', + version: 1, + deprecated: true, + modelType: 'Parquet', + payloadLocation: 'IPFS', + appendOnly: false, + signatureRequired: false, + }, + { + id: 20, + namespace: 'dsnp', + name: 'user-attribute-set', + version: 2, + deprecated: false, + modelType: 'Parquet', + payloadLocation: 'IPFS', + appendOnly: false, + signatureRequired: false, + }, + { + id: 15, + namespace: 'dsnp', + name: 'profile-resources', + version: 1, + deprecated: false, + modelType: 'AvroBinary', + payloadLocation: 'Itemized', + appendOnly: false, + signatureRequired: false, + }, + { + id: 7, + namespace: 'dsnp', + name: 'public-key-key-agreement', + version: 1, + deprecated: false, + modelType: 'AvroBinary', + payloadLocation: 'Itemized', + appendOnly: true, + signatureRequired: true, + }, + { + id: 4, + namespace: 'dsnp', + name: 'reaction', + version: 1, + deprecated: false, + modelType: 'Parquet', + payloadLocation: 'IPFS', + appendOnly: false, + signatureRequired: false, + }, + { + id: 5, + namespace: 'dsnp', + name: 'update', + version: 1, + deprecated: true, + modelType: 'Parquet', + payloadLocation: 'IPFS', + appendOnly: false, + signatureRequired: false, + }, + { + id: 19, + namespace: 'dsnp', + name: 'update', + version: 2, + deprecated: false, + modelType: 'Parquet', + payloadLocation: 'IPFS', + appendOnly: false, + signatureRequired: false, + }, + { + id: 8, + namespace: 'dsnp', + name: 'public-follows', + version: 1, + deprecated: false, + modelType: 'AvroBinary', + payloadLocation: 'Paginated', + appendOnly: false, + signatureRequired: false, + }, + { + id: 10, + namespace: 'dsnp', + name: 'private-connections', + version: 1, + deprecated: false, + modelType: 'AvroBinary', + payloadLocation: 'Paginated', + appendOnly: false, + signatureRequired: false, + }, + { + id: 13, + namespace: 'dsnp', + name: 'ext-content-attribute-set', + version: 1, + deprecated: false, + modelType: 'Parquet', + payloadLocation: 'IPFS', + appendOnly: false, + signatureRequired: false, + }, + { + id: 6, + namespace: 'dsnp', + name: 'profile', + version: 1, + deprecated: false, + modelType: 'Parquet', + payloadLocation: 'IPFS', + appendOnly: false, + signatureRequired: false, + }, + { + id: 3, + namespace: 'dsnp', + name: 'reply', + version: 1, + deprecated: true, + modelType: 'Parquet', + payloadLocation: 'IPFS', + appendOnly: false, + signatureRequired: false, + }, + { + id: 18, + namespace: 'dsnp', + name: 'reply', + version: 2, + deprecated: false, + modelType: 'Parquet', + payloadLocation: 'IPFS', + appendOnly: false, + signatureRequired: false, + }, + { + id: 1, + namespace: 'dsnp', + name: 'tombstone', + version: 1, + deprecated: true, + modelType: 'Parquet', + payloadLocation: 'IPFS', + appendOnly: false, + signatureRequired: false, + }, + { + id: 16, + namespace: 'dsnp', + name: 'tombstone', + version: 2, + deprecated: false, + modelType: 'Parquet', + payloadLocation: 'IPFS', + appendOnly: false, + signatureRequired: false, + }, + { + id: 9, + namespace: 'dsnp', + name: 'private-follows', + version: 1, + deprecated: false, + modelType: 'AvroBinary', + payloadLocation: 'Paginated', + appendOnly: false, + signatureRequired: false, + }, + { + id: 14, + namespace: 'dsnp', + name: 'public-key-assertion-method', + version: 1, + deprecated: false, + modelType: 'AvroBinary', + payloadLocation: 'Itemized', + appendOnly: false, + signatureRequired: true, + }, + { + id: 2, + namespace: 'dsnp', + name: 'broadcast', + version: 1, + deprecated: true, + modelType: 'Parquet', + payloadLocation: 'IPFS', + appendOnly: false, + signatureRequired: false, + }, + { + id: 17, + namespace: 'dsnp', + name: 'broadcast', + version: 2, + deprecated: false, + modelType: 'Parquet', + payloadLocation: 'IPFS', + appendOnly: false, + signatureRequired: false, + }, + { + id: 12, + namespace: 'dsnp', + name: 'dsnp-content-attribute-set', + version: 1, + deprecated: false, + modelType: 'Parquet', + payloadLocation: 'IPFS', + appendOnly: false, + signatureRequired: false, + }, +]; diff --git a/js/schemas/eslint.config.mjs b/js/schemas/eslint.config.mjs new file mode 100644 index 0000000000..a8fb881ff9 --- /dev/null +++ b/js/schemas/eslint.config.mjs @@ -0,0 +1,68 @@ +// @ts-check + +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import mocha from 'eslint-plugin-mocha'; +import globals from 'globals'; + +// Needed for eslint 9 +const mochaConfig = [ + { + name: 'mocha/recommended', + languageOptions: { + globals: globals.mocha, + }, + plugins: { + mocha, + }, + rules: mocha.configs.flat.recommended.rules, + }, +]; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.strict, + ...tseslint.configs.stylistic, + ...mochaConfig, + { + ignores: ['dist/', 'scripts/'], + }, + { + linterOptions: { + // Needed as the generated code uses this + reportUnusedDisableDirectives: false, + }, + }, + { + languageOptions: { + parserOptions: { + projectService: { + allowDefaultProject: ['eslint.config.mjs', 'test/*.ts'], + defaultProject: './tsconfig.eslint.json', + }, + }, + }, + }, + { + rules: { + '@typescript-eslint/no-empty-interface': 'off', + '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-expressions': 'off', + '@typescript-eslint/no-empty-function': 'off', + semi: ['error', 'always'], + 'mocha/no-setup-in-describe': 'off', + 'no-use-before-define': 'off', + 'no-unused-vars': 'off', + 'no-var': 'error', + 'id-length': [ + 'error', + { + exceptionPatterns: ['[i-k]', 'e', 'c', 'x', 'y', 'r', 's', 'v'], + properties: 'never', + }, + ], + 'allow-namespace': 'off', + }, + } +); diff --git a/js/schemas/index.ts b/js/schemas/index.ts new file mode 100644 index 0000000000..42369b9b57 --- /dev/null +++ b/js/schemas/index.ts @@ -0,0 +1,6 @@ +import * as data from './data'; +import * as schemas from './schemas'; + +export * from './data'; +export * from './schemas'; +export default { ...data, ...schemas }; diff --git a/js/schemas/package-lock.json b/js/schemas/package-lock.json new file mode 100644 index 0000000000..0d22ff3623 --- /dev/null +++ b/js/schemas/package-lock.json @@ -0,0 +1,3490 @@ +{ + "name": "@frequency-chain/schemas", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@frequency-chain/schemas", + "version": "0.0.0", + "license": "Apache-2.0", + "devDependencies": { + "@eslint/js": "^9.14.0", + "@polkadot/api": "^14.3.1", + "@polkadot/rpc-provider": "^14.3.1", + "@types/mocha": "^10.0.9", + "eslint": "^9.14.0", + "eslint-plugin-mocha": "^10.5.0", + "mocha": "10.8.2", + "prettier": "^3.3.3", + "tsx": "^4.19.2", + "typescript": "^5.6.3", + "typescript-eslint": "^8.14.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.0.tgz", + "integrity": "sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.0.tgz", + "integrity": "sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.15.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.15.0.tgz", + "integrity": "sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", + "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", + "dev": true, + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@noble/curves": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.6.0.tgz", + "integrity": "sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==", + "dev": true, + "dependencies": { + "@noble/hashes": "1.5.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz", + "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==", + "dev": true, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.1.tgz", + "integrity": "sha512-/SMC/l7foRjpykLTUTacIH05H3mr9ip8b5xxfwXlVezXrNVLp3Cv0GX6uItkKd+ZjzVPf3PFrDF2B2/HLSNESA==", + "dev": true, + "optional": true + }, + "node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.1.0.tgz", + "integrity": "sha512-8GSFE5+EF73MCuLQm8tjrbCqlgclcHBSRaswvXziJ0ZW7iw3UEMsKkkKvELayWyBuOPa2T5i1nj6gFOeIsqvrg==", + "dev": true, + "optional": true + }, + "node_modules/@polkadot-api/metadata-builders": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.3.2.tgz", + "integrity": "sha512-TKpfoT6vTb+513KDzMBTfCb/ORdgRnsS3TDFpOhAhZ08ikvK+hjHMt5plPiAX/OWkm1Wc9I3+K6W0hX5Ab7MVg==", + "dev": true, + "optional": true, + "dependencies": { + "@polkadot-api/substrate-bindings": "0.6.0", + "@polkadot-api/utils": "0.1.0" + } + }, + "node_modules/@polkadot-api/observable-client": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.3.2.tgz", + "integrity": "sha512-HGgqWgEutVyOBXoGOPp4+IAq6CNdK/3MfQJmhCJb8YaJiaK4W6aRGrdQuQSTPHfERHCARt9BrOmEvTXAT257Ug==", + "dev": true, + "optional": true, + "dependencies": { + "@polkadot-api/metadata-builders": "0.3.2", + "@polkadot-api/substrate-bindings": "0.6.0", + "@polkadot-api/utils": "0.1.0" + }, + "peerDependencies": { + "@polkadot-api/substrate-client": "0.1.4", + "rxjs": ">=7.8.0" + } + }, + "node_modules/@polkadot-api/substrate-bindings": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.6.0.tgz", + "integrity": "sha512-lGuhE74NA1/PqdN7fKFdE5C1gNYX357j1tWzdlPXI0kQ7h3kN0zfxNOpPUN7dIrPcOFZ6C0tRRVrBylXkI6xPw==", + "dev": true, + "optional": true, + "dependencies": { + "@noble/hashes": "^1.3.1", + "@polkadot-api/utils": "0.1.0", + "@scure/base": "^1.1.1", + "scale-ts": "^1.6.0" + } + }, + "node_modules/@polkadot-api/substrate-client": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.1.4.tgz", + "integrity": "sha512-MljrPobN0ZWTpn++da9vOvt+Ex+NlqTlr/XT7zi9sqPtDJiQcYl+d29hFAgpaeTqbeQKZwz3WDE9xcEfLE8c5A==", + "dev": true, + "optional": true, + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.0.1", + "@polkadot-api/utils": "0.1.0" + } + }, + "node_modules/@polkadot-api/utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.1.0.tgz", + "integrity": "sha512-MXzWZeuGxKizPx2Xf/47wx9sr/uxKw39bVJUptTJdsaQn/TGq+z310mHzf1RCGvC1diHM8f593KrnDgc9oNbJA==", + "dev": true, + "optional": true + }, + "node_modules/@polkadot/api": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-14.3.1.tgz", + "integrity": "sha512-ZBKSXEVJa1S1bnmpnA7KT/fX3sJDIJOdVD9Hp3X+G73yvXzuK5k1Mn5z9bD/AcMs/HAGcbuYU+b9+b9IByH9YQ==", + "dev": true, + "dependencies": { + "@polkadot/api-augment": "14.3.1", + "@polkadot/api-base": "14.3.1", + "@polkadot/api-derive": "14.3.1", + "@polkadot/keyring": "^13.2.3", + "@polkadot/rpc-augment": "14.3.1", + "@polkadot/rpc-core": "14.3.1", + "@polkadot/rpc-provider": "14.3.1", + "@polkadot/types": "14.3.1", + "@polkadot/types-augment": "14.3.1", + "@polkadot/types-codec": "14.3.1", + "@polkadot/types-create": "14.3.1", + "@polkadot/types-known": "14.3.1", + "@polkadot/util": "^13.2.3", + "@polkadot/util-crypto": "^13.2.3", + "eventemitter3": "^5.0.1", + "rxjs": "^7.8.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-augment": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-14.3.1.tgz", + "integrity": "sha512-PE6DW+8kRhbnGKn7qCF7yM6eEt/kqrY8bh1i0RZcPY9QgwXW4bZZrtMK4WssX6Z70NTEoOW6xHYIjc7gFZuz8g==", + "dev": true, + "dependencies": { + "@polkadot/api-base": "14.3.1", + "@polkadot/rpc-augment": "14.3.1", + "@polkadot/types": "14.3.1", + "@polkadot/types-augment": "14.3.1", + "@polkadot/types-codec": "14.3.1", + "@polkadot/util": "^13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-base": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-14.3.1.tgz", + "integrity": "sha512-GZT6rTpT3HYZ/C3rLPjoX3rX3DOxNG/zgts+jKjNrCumAeZkVq5JErKIX8/3f2TVaE2Kbqniy3d1TH/AL4HBPA==", + "dev": true, + "dependencies": { + "@polkadot/rpc-core": "14.3.1", + "@polkadot/types": "14.3.1", + "@polkadot/util": "^13.2.3", + "rxjs": "^7.8.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-derive": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-14.3.1.tgz", + "integrity": "sha512-PhqUEJCY54vXtIaoYqGUtJY06wHd/K0cBmBz9yCLxp8UZkLoGWhfJRTruI25Jnucf9awS5cZKYqbsoDrL09Oqg==", + "dev": true, + "dependencies": { + "@polkadot/api": "14.3.1", + "@polkadot/api-augment": "14.3.1", + "@polkadot/api-base": "14.3.1", + "@polkadot/rpc-core": "14.3.1", + "@polkadot/types": "14.3.1", + "@polkadot/types-codec": "14.3.1", + "@polkadot/util": "^13.2.3", + "@polkadot/util-crypto": "^13.2.3", + "rxjs": "^7.8.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/keyring": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-13.2.3.tgz", + "integrity": "sha512-pgTo6DXNXub0wGD+MnVHYhKxf80Jl+QMOCb818ioGdXz++Uw4mTueFAwtB+N7TGo0HafhChUiNJDxFdlDkcAng==", + "dev": true, + "dependencies": { + "@polkadot/util": "13.2.3", + "@polkadot/util-crypto": "13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.2.3", + "@polkadot/util-crypto": "13.2.3" + } + }, + "node_modules/@polkadot/networks": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-13.2.3.tgz", + "integrity": "sha512-mG+zkXg/33AyPrkv2xBbAo3LBUwOwBn6qznBU/4jxiZPnVvCwMaxE7xHM22B5riItbNJ169FXv3wy0v6ZmkFbw==", + "dev": true, + "dependencies": { + "@polkadot/util": "13.2.3", + "@substrate/ss58-registry": "^1.51.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-augment": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-14.3.1.tgz", + "integrity": "sha512-Z8Hp8fFHwFCiTX0bBCDqCZ4U26wLIJl1NRSjJTsAr+SS68pYZBDGCwhKztpKGqndk1W1akRUaxrkGqYdIFmspQ==", + "dev": true, + "dependencies": { + "@polkadot/rpc-core": "14.3.1", + "@polkadot/types": "14.3.1", + "@polkadot/types-codec": "14.3.1", + "@polkadot/util": "^13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-core": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-14.3.1.tgz", + "integrity": "sha512-FV2NPhFwFxmX8LqibDcGc6IKTBqmvwr7xwF2OA60Br4cX+AQzMSVpFlfQcETll+0M+LnRhqGKGkP0EQWXaSowA==", + "dev": true, + "dependencies": { + "@polkadot/rpc-augment": "14.3.1", + "@polkadot/rpc-provider": "14.3.1", + "@polkadot/types": "14.3.1", + "@polkadot/util": "^13.2.3", + "rxjs": "^7.8.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-provider": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-14.3.1.tgz", + "integrity": "sha512-NF/Z/7lzT+jp5LZzC49g+YIjRzXVI0hFag3+B+4zh6E/kKADdF59EHj2Im4LDhRGOnEO9AE4H6/UjNEbZ94JtA==", + "dev": true, + "dependencies": { + "@polkadot/keyring": "^13.2.3", + "@polkadot/types": "14.3.1", + "@polkadot/types-support": "14.3.1", + "@polkadot/util": "^13.2.3", + "@polkadot/util-crypto": "^13.2.3", + "@polkadot/x-fetch": "^13.2.3", + "@polkadot/x-global": "^13.2.3", + "@polkadot/x-ws": "^13.2.3", + "eventemitter3": "^5.0.1", + "mock-socket": "^9.3.1", + "nock": "^13.5.5", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@substrate/connect": "0.8.11" + } + }, + "node_modules/@polkadot/types": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-14.3.1.tgz", + "integrity": "sha512-O748XgCLDQYxS5nQ6TJSqW88oC4QNIoNVlWZC2Qq4SmEXuSzaNHQwSVtdyPRJCCc4Oi1DCQvGui4O+EukUl7HA==", + "dev": true, + "dependencies": { + "@polkadot/keyring": "^13.2.3", + "@polkadot/types-augment": "14.3.1", + "@polkadot/types-codec": "14.3.1", + "@polkadot/types-create": "14.3.1", + "@polkadot/util": "^13.2.3", + "@polkadot/util-crypto": "^13.2.3", + "rxjs": "^7.8.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-augment": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-14.3.1.tgz", + "integrity": "sha512-SC4M6TBlgCglNz+gRbvfoVRDz0Vyeev6v0HeAdw0H6ayEW4BXUdo5bFr0092bdS5uTrEPgiSyUry5TJs2KoXig==", + "dev": true, + "dependencies": { + "@polkadot/types": "14.3.1", + "@polkadot/types-codec": "14.3.1", + "@polkadot/util": "^13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-codec": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-14.3.1.tgz", + "integrity": "sha512-3y3RBGd+8ebscGbNUOjqUjnRE7hgicgid5LtofHK3O1EDcJQJnYBDkJ7fOAi96CDgHsg+f2FWWkBWEPgpOQoMQ==", + "dev": true, + "dependencies": { + "@polkadot/util": "^13.2.3", + "@polkadot/x-bigint": "^13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-create": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-14.3.1.tgz", + "integrity": "sha512-F4EBvF3Zvym0xrkAA5Yz01IAVMepMV3w2Dwd0C9IygEAQ5sYLLPHmf72/aXn+Ag+bSyT2wlJHpDc+nEBXNQ3Gw==", + "dev": true, + "dependencies": { + "@polkadot/types-codec": "14.3.1", + "@polkadot/util": "^13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-known": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-14.3.1.tgz", + "integrity": "sha512-58b3Yc7+sxwNjs8axmrA9OCgnxmEKIq7XCH2VxSgLqTeqbohVtxwUSCW/l8NPrq1nxzj4J2sopu0PPg8/++q4g==", + "dev": true, + "dependencies": { + "@polkadot/networks": "^13.2.3", + "@polkadot/types": "14.3.1", + "@polkadot/types-codec": "14.3.1", + "@polkadot/types-create": "14.3.1", + "@polkadot/util": "^13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-support": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-14.3.1.tgz", + "integrity": "sha512-MfVe4iIOJIfBr+gj8Lu8gwIvhnO6gDbG5LeaKAjY6vS6Oh0y5Ztr8NdMIl8ccSpoyt3LqIXjfApeGzHiLzr6bw==", + "dev": true, + "dependencies": { + "@polkadot/util": "^13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-13.2.3.tgz", + "integrity": "sha512-pioNnsig3qHXrfOKMe4Yqos8B8N3/EZUpS+WfTpWnn1VjYban/0GrTXeavPlAwggnY27b8fS6rBzQBhnVYDw8g==", + "dev": true, + "dependencies": { + "@polkadot/x-bigint": "13.2.3", + "@polkadot/x-global": "13.2.3", + "@polkadot/x-textdecoder": "13.2.3", + "@polkadot/x-textencoder": "13.2.3", + "@types/bn.js": "^5.1.6", + "bn.js": "^5.2.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util-crypto": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.2.3.tgz", + "integrity": "sha512-5sbggmLbn5eiuVMyPROPlT5roHRqdKHOfSpioNbGvGIZ1qIWVoC1RfsK0NWJOVGDzy6DpQe0KYT/kgcU5Xsrzw==", + "dev": true, + "dependencies": { + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@polkadot/networks": "13.2.3", + "@polkadot/util": "13.2.3", + "@polkadot/wasm-crypto": "^7.4.1", + "@polkadot/wasm-util": "^7.4.1", + "@polkadot/x-bigint": "13.2.3", + "@polkadot/x-randomvalues": "13.2.3", + "@scure/base": "^1.1.7", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.2.3" + } + }, + "node_modules/@polkadot/wasm-bridge": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.4.1.tgz", + "integrity": "sha512-tdkJaV453tezBxhF39r4oeG0A39sPKGDJmN81LYLf+Fihb7astzwju+u75BRmDrHZjZIv00un3razJEWCxze6g==", + "dev": true, + "dependencies": { + "@polkadot/wasm-util": "7.4.1", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.4.1.tgz", + "integrity": "sha512-kHN/kF7hYxm1y0WeFLWeWir6oTzvcFmR4N8fJJokR+ajYbdmrafPN+6iLgQVbhZnDdxyv9jWDuRRsDnBx8tPMQ==", + "dev": true, + "dependencies": { + "@polkadot/wasm-bridge": "7.4.1", + "@polkadot/wasm-crypto-asmjs": "7.4.1", + "@polkadot/wasm-crypto-init": "7.4.1", + "@polkadot/wasm-crypto-wasm": "7.4.1", + "@polkadot/wasm-util": "7.4.1", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-asmjs": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.4.1.tgz", + "integrity": "sha512-pwU8QXhUW7IberyHJIQr37IhbB6DPkCG5FhozCiNTq4vFBsFPjm9q8aZh7oX1QHQaiAZa2m2/VjIVE+FHGbvHQ==", + "dev": true, + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-init": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.4.1.tgz", + "integrity": "sha512-AVka33+f7MvXEEIGq5U0dhaA2SaXMXnxVCQyhJTaCnJ5bRDj0Xlm3ijwDEQUiaDql7EikbkkRtmlvs95eSUWYQ==", + "dev": true, + "dependencies": { + "@polkadot/wasm-bridge": "7.4.1", + "@polkadot/wasm-crypto-asmjs": "7.4.1", + "@polkadot/wasm-crypto-wasm": "7.4.1", + "@polkadot/wasm-util": "7.4.1", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-wasm": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.4.1.tgz", + "integrity": "sha512-PE1OAoupFR0ZOV2O8tr7D1FEUAwaggzxtfs3Aa5gr+yxlSOaWUKeqsOYe1KdrcjmZVV3iINEAXxgrbzCmiuONg==", + "dev": true, + "dependencies": { + "@polkadot/wasm-util": "7.4.1", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-util": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.4.1.tgz", + "integrity": "sha512-RAcxNFf3zzpkr+LX/ItAsvj+QyM56TomJ0xjUMo4wKkHjwsxkz4dWJtx5knIgQz/OthqSDMR59VNEycQeNuXzA==", + "dev": true, + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/x-bigint": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-13.2.3.tgz", + "integrity": "sha512-VKgEAh0LsxTd/Hg517Tt5ZU4CySjBwMpaojbkjgv3fOdg1cN7t4eFEUxpyj7mlO0cp22SzDh7nmy4TO98qhLQA==", + "dev": true, + "dependencies": { + "@polkadot/x-global": "13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-fetch": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-13.2.3.tgz", + "integrity": "sha512-7Nmk+8ieEGzz43nc1rX6nH3rQo6rhGmAaIXJWnXY9gOHY0k1me1bJYbP+xDdh8vcLh8eY3D1sESUwG6QYZW2lg==", + "dev": true, + "dependencies": { + "@polkadot/x-global": "13.2.3", + "node-fetch": "^3.3.2", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-global": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-13.2.3.tgz", + "integrity": "sha512-7MYQIAEwBkRcNrgqac5PbB0kNPlI6ISJEy6/Nb+crj8BFjQ8rf11PF49fq0QsvDeuYM1aNLigrvYZNptQs4lbw==", + "dev": true, + "dependencies": { + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-randomvalues": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-13.2.3.tgz", + "integrity": "sha512-Zf0GTfLmVk+VzPUmcQSpXjjmFzMTjPhXoLuIoE7xIu73T+vQ+TX9j7DvorN6bIRsnZ9l1SyTZsSf/NTjNZKIZg==", + "dev": true, + "dependencies": { + "@polkadot/x-global": "13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.2.3", + "@polkadot/wasm-util": "*" + } + }, + "node_modules/@polkadot/x-textdecoder": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-13.2.3.tgz", + "integrity": "sha512-i8hRXPtGknmdm3FYv6/94I52VXHJZa5sgYNw1+Hqb4Jqmq4awUjea35CKXd/+aw70Qn8Ngg31l2GoiH494fa+Q==", + "dev": true, + "dependencies": { + "@polkadot/x-global": "13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-textencoder": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-13.2.3.tgz", + "integrity": "sha512-wJI3Bb/dC4zyBXJFm5+ZhyBXWoI5wvP8k8qX0/ZC0PQsgSAqs7LVhiofk4Wd94n0P41W5re58LrGXLyziSAshw==", + "dev": true, + "dependencies": { + "@polkadot/x-global": "13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-ws": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-13.2.3.tgz", + "integrity": "sha512-Y6MTAWgcnrnx/LkBx65X3ZyoJH5EFj3tXtflRoKg1+PLHSLuNBV7Wi5mLcE70z4e5c+4hgBbLq+8SqCqzFtSPw==", + "dev": true, + "dependencies": { + "@polkadot/x-global": "13.2.3", + "tslib": "^2.8.0", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "dev": true, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@substrate/connect": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.8.11.tgz", + "integrity": "sha512-ofLs1PAO9AtDdPbdyTYj217Pe+lBfTLltdHDs3ds8no0BseoLeAGxpz1mHfi7zB4IxI3YyAiLjH6U8cw4pj4Nw==", + "deprecated": "versions below 1.x are no longer maintained", + "dev": true, + "optional": true, + "dependencies": { + "@substrate/connect-extension-protocol": "^2.0.0", + "@substrate/connect-known-chains": "^1.1.5", + "@substrate/light-client-extension-helpers": "^1.0.0", + "smoldot": "2.0.26" + } + }, + "node_modules/@substrate/connect-extension-protocol": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-2.2.1.tgz", + "integrity": "sha512-GoafTgm/Jey9E4Xlj4Z5ZBt/H4drH2CNq8VrAro80rtoznrXnFDNVivLQzZN0Xaj2g8YXSn9pC9Oc9IovYZJXw==", + "dev": true, + "optional": true + }, + "node_modules/@substrate/connect-known-chains": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@substrate/connect-known-chains/-/connect-known-chains-1.7.0.tgz", + "integrity": "sha512-Qf+alxEPmNycUyrPkXWrlFA97punnBCGxSWqiLG1CNu+jQoFYqi8x7gZYfqmdUHDY4nG1F84KHPPk7Zy4ngSfg==", + "dev": true, + "optional": true + }, + "node_modules/@substrate/light-client-extension-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@substrate/light-client-extension-helpers/-/light-client-extension-helpers-1.0.0.tgz", + "integrity": "sha512-TdKlni1mBBZptOaeVrKnusMg/UBpWUORNDv5fdCaJklP4RJiFOzBCrzC+CyVI5kQzsXBisZ+2pXm+rIjS38kHg==", + "dev": true, + "optional": true, + "dependencies": { + "@polkadot-api/json-rpc-provider": "^0.0.1", + "@polkadot-api/json-rpc-provider-proxy": "^0.1.0", + "@polkadot-api/observable-client": "^0.3.0", + "@polkadot-api/substrate-client": "^0.1.2", + "@substrate/connect-extension-protocol": "^2.0.0", + "@substrate/connect-known-chains": "^1.1.5", + "rxjs": "^7.8.1" + }, + "peerDependencies": { + "smoldot": "2.x" + } + }, + "node_modules/@substrate/ss58-registry": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.51.0.tgz", + "integrity": "sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ==", + "dev": true + }, + "node_modules/@types/bn.js": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.6.tgz", + "integrity": "sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "10.0.9", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.9.tgz", + "integrity": "sha512-sicdRoWtYevwxjOHNMPTl3vSfJM6oyW8o1wXeI7uww6b6xHg8eBznQDNSGBCDJmsE8UMxP05JgZRtsKbTqt//Q==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.14.0.tgz", + "integrity": "sha512-tqp8H7UWFaZj0yNO6bycd5YjMwxa6wIHOLZvWPkidwbgLCsBMetQoGj7DPuAlWa2yGO3H48xmPwjhsSPPCGU5w==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.14.0", + "@typescript-eslint/type-utils": "8.14.0", + "@typescript-eslint/utils": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.14.0.tgz", + "integrity": "sha512-2p82Yn9juUJq0XynBXtFCyrBDb6/dJombnz6vbo6mgQEtWHfvHbQuEa9kAOVIt1c9YFwi7H6WxtPj1kg+80+RA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.14.0", + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/typescript-estree": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.14.0.tgz", + "integrity": "sha512-aBbBrnW9ARIDn92Zbo7rguLnqQ/pOrUguVpbUwzOhkFg2npFDwTgPGqFqE0H5feXcOoJOfX3SxlJaKEVtq54dw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.14.0.tgz", + "integrity": "sha512-Xcz9qOtZuGusVOH5Uk07NGs39wrKkf3AxlkK79RBK6aJC1l03CobXjJbwBPSidetAOV+5rEVuiT1VSBUOAsanQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "8.14.0", + "@typescript-eslint/utils": "8.14.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.14.0.tgz", + "integrity": "sha512-yjeB9fnO/opvLJFAsPNYlKPnEM8+z4og09Pk504dkqonT02AyL5Z9SSqlE0XqezS93v6CXn49VHvB2G7XSsl0g==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.14.0.tgz", + "integrity": "sha512-OPXPLYKGZi9XS/49rdaCbR5j/S14HazviBlUQFvSKz3npr3NikF+mrgK7CFVur6XEt95DZp/cmke9d5i3vtVnQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.14.0.tgz", + "integrity": "sha512-OGqj6uB8THhrHj0Fk27DcHPojW7zKwKkPmHXHvQ58pLYp4hy8CSUdTKykKeh+5vFqTTVmjz0zCOOPKRovdsgHA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.14.0", + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/typescript-estree": "8.14.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.14.0.tgz", + "integrity": "sha512-vG0XZo8AdTH9OE6VFRwAZldNc7qtJ/6NLGWak+BtENuEUXGZgFpihILPiBvKXvJ2nFu27XNGC6rKiwuaoMbYzQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.14.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", + "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.15.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.15.0.tgz", + "integrity": "sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.9.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.15.0", + "@eslint/plugin-kit": "^0.2.3", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.5", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-mocha": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.5.0.tgz", + "integrity": "sha512-F2ALmQVPT1GoP27O1JTZGrV9Pqg8k79OeIuvw63UxMtQKREZtmkK1NFgkZQ2TW7L2JSSFKHFPTtHu5z8R9QNRw==", + "dev": true, + "dependencies": { + "eslint-utils": "^3.0.0", + "globals": "^13.24.0", + "rambda": "^7.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-mocha/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-tsconfig": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mock-socket": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz", + "integrity": "sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/nock": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz", + "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/rambda": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/rambda/-/rambda-7.5.0.tgz", + "integrity": "sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==", + "dev": true + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/scale-ts": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/scale-ts/-/scale-ts-1.6.1.tgz", + "integrity": "sha512-PBMc2AWc6wSEqJYBDPcyCLUj9/tMKnLX70jLOSndMtcUoLQucP/DM0vnQo1wJAYjTrQiq8iG9rD0q6wFzgjH7g==", + "dev": true, + "optional": true + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/smoldot": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-2.0.26.tgz", + "integrity": "sha512-F+qYmH4z2s2FK+CxGj8moYcd1ekSIKH8ywkdqlOz88Dat35iB1DIYL11aILN46YSGMzQW/lbJNS307zBSDN5Ig==", + "dev": true, + "optional": true, + "dependencies": { + "ws": "^8.8.1" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", + "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.14.0.tgz", + "integrity": "sha512-K8fBJHxVL3kxMmwByvz8hNdBJ8a0YqKzKDX6jRlrjMuNXyd5T2V02HIq37+OiWXvUUOXgOOGiSSOh26Mh8pC3w==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.14.0", + "@typescript-eslint/parser": "8.14.0", + "@typescript-eslint/utils": "8.14.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/js/schemas/package.json b/js/schemas/package.json new file mode 100644 index 0000000000..bb9e07348d --- /dev/null +++ b/js/schemas/package.json @@ -0,0 +1,39 @@ +{ + "name": "@frequency-chain/schemas", + "version": "0.0.0", + "bugs": { + "url": "https://github.com/frequency-chain/frequency/issues" + }, + "description": "A static package which allows getting schemas locally", + "main": "index.js", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/frequency-chain/frequency.git", + "directory": "js/schemas" + }, + "scripts": { + "package": "node scripts/package.cjs", + "prepackage": "cp CONTRIBUTING.md README.md ../../LICENSE ./dist", + "build": "tsc -p ./tsconfig.types.json && tsc -p ./tsconfig.cjs.json && tsc -p ./tsconfig.esm.json", + "postbuild": "npm run package", + "test": "mocha --import=tsx ./test/**/*.test.ts", + "format": "tsc --noEmit --pretty && prettier --write . && eslint --fix", + "lint": "tsc --noEmit --pretty && prettier --check . && eslint" + }, + "author": "frequency-chain", + "license": "Apache-2.0", + "devDependencies": { + "@polkadot/api": "^14.3.1", + "@polkadot/rpc-provider": "^14.3.1", + "@eslint/js": "^9.14.0", + "@types/mocha": "^10.0.9", + "eslint": "^9.14.0", + "eslint-plugin-mocha": "^10.5.0", + "mocha": "10.8.2", + "prettier": "^3.3.3", + "tsx": "^4.19.2", + "typescript": "^5.6.3", + "typescript-eslint": "^8.14.0" + } +} diff --git a/js/schemas/schemas.ts b/js/schemas/schemas.ts new file mode 100644 index 0000000000..6a83807cdc --- /dev/null +++ b/js/schemas/schemas.ts @@ -0,0 +1,47 @@ +import { SCHEMA_INFOS } from './data'; + +export interface SchemaInfo { + id: number; + name: string; + namespace: string; + version: number; + modelType: string; + payloadLocation: string; + appendOnly: boolean; + signatureRequired: boolean; + deprecated: boolean; +} + +const schemaFullName = (info: SchemaInfo): string => `${info.namespace}.${info.name}@v${info.version}`; +const schemaFullNameWithoutVersion = (info: SchemaInfo): string => `${info.namespace}.${info.name}`; + +/** + * Mapping that will allow us to get schema full names from their ids + */ +export const ID_TO_SCHEMA_FULL_NAME = new Map(SCHEMA_INFOS.map((x) => [x.id, schemaFullName(x)])); + +/** + * Mapping that will allow us to get schema ids from their full names + */ +export const FULL_NAME_TO_ID = new Map(SCHEMA_INFOS.map((x) => [schemaFullName(x), x.id])); + +/** + * Mapping that will allow us to get active schema ids from their names + * example input dsnp.public-key-key-agreement + */ +export const NAME_TO_ID_ACTIVE = new Map( + SCHEMA_INFOS.filter((info) => !info.deprecated).map((x) => [schemaFullNameWithoutVersion(x), x.id]) +); + +/** + * Mapping that will allow us to get schema infos from their IDs + */ +export const ID_TO_SCHEMA_INFO = new Map(SCHEMA_INFOS.map((x) => [x.id, x])); + +/** + * A method that can retrieve all versions of a given schema name without any version + * example input dsnp.public-key-key-agreement + */ +export const getAllVersionsFromSchemaName = (schemaName: string): SchemaInfo[] => { + return SCHEMA_INFOS.filter((info) => schemaFullNameWithoutVersion(info) === schemaName); +}; diff --git a/js/schemas/scripts/fetchSchemaData.mjs b/js/schemas/scripts/fetchSchemaData.mjs new file mode 100644 index 0000000000..9fcebd9ab6 --- /dev/null +++ b/js/schemas/scripts/fetchSchemaData.mjs @@ -0,0 +1,135 @@ +// Only way to silence PolkadotJS API warnings we don't want +console.warn = () => {}; +import { ApiPromise, WsProvider } from '@polkadot/api'; +import { promises as fs } from 'fs'; + +const options = { + rpc: { + schemas: { + getBySchemaId: { + description: 'Get a Schema by Id', + params: [ + { + name: 'schema_id', + type: 'SchemaId', + }, + ], + type: 'Option', + }, + getVersions: { + description: 'Get different versions and schema ids for a complete schema name or only a namespace', + params: [ + { + name: 'schema_name', + type: 'String', + }, + ], + type: 'Option>', + }, + }, + }, + types: { + SchemaId: 'u16', + SchemaModel: 'Vec', + SchemaVersion: 'u8', + SchemaResponse: { + schema_id: 'SchemaId', + model: 'SchemaModel', + model_type: 'ModelType', + payload_location: 'PayloadLocation', + settings: 'Vec', + }, + ModelType: { + _enum: ['AvroBinary', 'Parquet'], + }, + PayloadLocation: { + _enum: ['OnChain', 'IPFS', 'Itemized', 'Paginated'], + }, + SchemaSetting: { + _enum: ['AppendOnly', 'SignatureRequired'], + }, + SchemaVersionResponse: { + schema_name: 'String', + schema_version: 'SchemaVersion', + schema_id: 'SchemaId', + }, + }, +}; +const SOURCE_URL = 'wss://1.rpc.frequency.xyz'; +const GENERATED_FILE_NAME = '../data.ts'; + +async function main() { + try { + await getSchemas(SOURCE_URL); + process.exit(0); + } catch (error) { + console.error('Error:', error); + process.exit(1); + } +} + +main(); + +async function getSchemas(sourceUrl) { + const provider = new WsProvider(sourceUrl); + const api = await ApiPromise.create({ provider, ...options }); + const nameToIds = await api.rpc.schemas.getVersions('dsnp'); + + const idToSchemaInfo = new Map(); + const fullNameToVersions = new Map(); + + for (const v of JSON.parse(JSON.stringify(nameToIds))) { + const fullName = v.schema_name; + const names = fullName.split('.'); + const version = v.schema_version; + const id = v.schema_id; + + if (fullNameToVersions.has(fullName)) { + const arr = fullNameToVersions.get(fullName); + arr.push({ + version: version, + id: id, + }); + fullNameToVersions.set(fullName, arr); + } else { + fullNameToVersions.set(fullName, [ + { + version: version, + id: id, + }, + ]); + } + + const chainSchemaInfo = await api.query.schemas.schemaInfos(id); + const objSchemaInfo = JSON.parse(JSON.stringify(chainSchemaInfo)); + idToSchemaInfo.set(id, { + id: id, + namespace: names[0], + name: names[1], + version: version, + deprecated: false, + modelType: objSchemaInfo.modelType, + payloadLocation: objSchemaInfo.payloadLocation, + appendOnly: (objSchemaInfo.settings & 1) > 0, + signatureRequired: (objSchemaInfo.settings & 2) > 0, + }); + } + + for (let arr of fullNameToVersions.values()) { + arr.sort((a, b) => a.version - b.version); + for (let i = 0; i < arr.length; i++) { + const inner = arr[i]; + const info = idToSchemaInfo.get(inner.id); + info.deprecated = i < arr.length - 1; + } + } + + let generated = ''; + for (let v of idToSchemaInfo.values()) { + generated += `\n${JSON.stringify(v, null, 2).replace(/"([^"]+)":/g, '$1:')},`; + } + let output = `import { SchemaInfo } from './schemas';\n\nexport const SCHEMA_INFOS: SchemaInfo[] = [${generated}\n];\n`; + + console.log(output); + await fs.writeFile(GENERATED_FILE_NAME, output, 'utf8'); +} diff --git a/js/schemas/scripts/package.cjs b/js/schemas/scripts/package.cjs new file mode 100644 index 0000000000..fba215a527 --- /dev/null +++ b/js/schemas/scripts/package.cjs @@ -0,0 +1,59 @@ +/** + * Build the package.json for the actual publishing + */ +// eslint-disable-next-line +const fs = require('fs'); +// eslint-disable-next-line +const path = require('path'); + +// eslint-disable-next-line +const rootPackage = require('../package.json'); + +// Remove test related work +delete rootPackage['jest-junit']; +delete rootPackage['jest']; + +// Don't keep scripts +delete rootPackage['scripts']; + +// Don't keep dev dependencies +delete rootPackage['devDependencies']; + +// Setup the main and types correctly +rootPackage['main'] = './cjs/index.js'; +rootPackage['module'] = './esm/index.js'; +rootPackage['types'] = 'index.d.ts'; +(rootPackage['exports'] = { + '.': { + types: './index.d.ts', + require: './cjs/index.js', + import: './esm/index.js', + default: './esm/index.js', + }, +}), + // Write it out + fs.writeFileSync( + `${path.join(__dirname, '../dist', 'package.json')}`, + JSON.stringify(rootPackage, null, 2), + (err) => { + if (err) throw new Error(err); + } + ); + +// Write out a simple type override for the esm side of things +fs.writeFileSync( + `${path.join(__dirname, '../dist/esm', 'package.json')}`, + JSON.stringify({ type: 'module' }, null, 2), + (err) => { + if (err) throw new Error(err); + } +); + +// Write out a simple type override for the cjs side of things +fs.writeFileSync( + `${path.join(__dirname, '../dist/cjs', 'package.json')}`, + JSON.stringify({ type: 'commonjs' }, null, 2), + (err) => { + if (err) throw new Error(err); + } +); diff --git a/js/schemas/scripts/package.json b/js/schemas/scripts/package.json new file mode 100644 index 0000000000..6cdce10515 --- /dev/null +++ b/js/schemas/scripts/package.json @@ -0,0 +1,13 @@ +{ + "name": "schema-data", + "version": "1.0.0", + "description": "Gets data from mainnet and outputs them in the format used by schema package", + "scripts": { + "generate": "node fetchSchemaData.mjs" + }, + "author": "", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api": "^13.2.1" + } +} diff --git a/js/schemas/test/schemas.test.ts b/js/schemas/test/schemas.test.ts new file mode 100644 index 0000000000..727042e2b5 --- /dev/null +++ b/js/schemas/test/schemas.test.ts @@ -0,0 +1,55 @@ +import assert from 'assert'; +import { + FULL_NAME_TO_ID, + getAllVersionsFromSchemaName, + ID_TO_SCHEMA_FULL_NAME, + ID_TO_SCHEMA_INFO, + NAME_TO_ID_ACTIVE, + SchemaInfo, +} from '../schemas'; + +describe('schemas', function () { + it('should be able to successfully get schemas from Id', function () { + const fullName = ID_TO_SCHEMA_FULL_NAME.get(7); + assert.equal(fullName, 'dsnp.public-key-key-agreement@v1'); + }); + + it('should be able to successfully get schema id from name', function () { + const id = FULL_NAME_TO_ID.get('dsnp.public-key-key-agreement@v1'); + assert.equal(id, 7); + }); + + it('should be able to successfully get schema info from ID', function () { + const info = ID_TO_SCHEMA_INFO.get(7); + const expected: SchemaInfo = { + id: 7, + namespace: 'dsnp', + name: 'public-key-key-agreement', + version: 1, + deprecated: false, + modelType: 'AvroBinary', + payloadLocation: 'Itemized', + appendOnly: true, + signatureRequired: true, + }; + assert.equal(info.id, expected.id); + assert.equal(info.namespace, expected.namespace); + assert.equal(info.name, expected.name); + assert.equal(info.version, expected.version); + assert.equal(info.deprecated, expected.deprecated); + assert.equal(info.modelType, expected.modelType); + assert.equal(info.payloadLocation, expected.payloadLocation); + assert.equal(info.appendOnly, expected.appendOnly); + assert.equal(info.signatureRequired, expected.signatureRequired); + }); + + it('should be able to successfully get all schema version from the schema name', function () { + const versions = getAllVersionsFromSchemaName('dsnp.tombstone'); + assert.deepEqual(versions, [ID_TO_SCHEMA_INFO.get(1), ID_TO_SCHEMA_INFO.get(16)]); + }); + + it('should be able to successfully get active schema version from the schema name', function () { + const id = NAME_TO_ID_ACTIVE.get('dsnp.tombstone'); + assert.equal(id, 16); + }); +}); diff --git a/js/schemas/tsconfig.cjs.json b/js/schemas/tsconfig.cjs.json new file mode 100644 index 0000000000..ebe4fe2b67 --- /dev/null +++ b/js/schemas/tsconfig.cjs.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "lib": ["ES2020"], + "module": "CommonJS", + "outDir": "dist/cjs", + "declaration": false + } +} diff --git a/js/schemas/tsconfig.eslint.json b/js/schemas/tsconfig.eslint.json new file mode 100644 index 0000000000..2d2a3b3f8b --- /dev/null +++ b/js/schemas/tsconfig.eslint.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules/**", "./scripts/**", "./dist/**"] +} diff --git a/js/schemas/tsconfig.esm.json b/js/schemas/tsconfig.esm.json new file mode 100644 index 0000000000..fc39b784b8 --- /dev/null +++ b/js/schemas/tsconfig.esm.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "lib": ["ES2020"], + "module": "ES2022", + "outDir": "dist/esm", + "declaration": false + } +} diff --git a/js/schemas/tsconfig.json b/js/schemas/tsconfig.json new file mode 100644 index 0000000000..ad486c3507 --- /dev/null +++ b/js/schemas/tsconfig.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Base", + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "moduleResolution": "node", + "module": "esnext", + "noImplicitAny": true, + "outDir": "dist", + "resolveJsonModule": true, + "sourceMap": false, + "strict": true, + "skipLibCheck": true, + "target": "es2022", + "typeRoots": ["node_modules/@types"] + }, + "exclude": ["node_modules/**", "./test/**", "./scripts/**", "**/*.test.ts", "./dist/**"] +} diff --git a/js/schemas/tsconfig.types.json b/js/schemas/tsconfig.types.json new file mode 100644 index 0000000000..420c04d5fd --- /dev/null +++ b/js/schemas/tsconfig.types.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "lib": ["ES2020"], + "target": "es2022", + "outDir": "dist", + "emitDeclarationOnly": true, + "declaration": true + } +} diff --git a/pallets/capacity/src/migration/provider_boost_init.rs b/pallets/capacity/src/migration/provider_boost_init.rs index 3a4747beda..a1e30d2d02 100644 --- a/pallets/capacity/src/migration/provider_boost_init.rs +++ b/pallets/capacity/src/migration/provider_boost_init.rs @@ -3,6 +3,8 @@ use frame_support::{ pallet_prelude::Weight, traits::{Get, OnRuntimeUpgrade}, }; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; #[cfg(feature = "try-runtime")] use sp_std::vec::Vec; diff --git a/pallets/stateful-storage/Cargo.toml b/pallets/stateful-storage/Cargo.toml index ec862e836d..ac2f9d8dbc 100644 --- a/pallets/stateful-storage/Cargo.toml +++ b/pallets/stateful-storage/Cargo.toml @@ -33,6 +33,7 @@ common-runtime = { path = "../../runtime/common", default-features = false } env_logger = { workspace = true } pretty_assertions = { workspace = true } sp-keystore = { workspace = true } +hex = { workspace = true, default-features = false, features = ["alloc"] } [features] default = ['std'] @@ -57,4 +58,4 @@ std = [ "common-runtime/std", ] try-runtime = ['frame-support/try-runtime'] -test = [] \ No newline at end of file +test = [] diff --git a/pallets/stateful-storage/src/lib.rs b/pallets/stateful-storage/src/lib.rs index 9bb27e8867..dab6c14568 100644 --- a/pallets/stateful-storage/src/lib.rs +++ b/pallets/stateful-storage/src/lib.rs @@ -36,7 +36,8 @@ mod tests; #[cfg(feature = "runtime-benchmarks")] use common_primitives::benchmarks::{MsaBenchmarkHelper, SchemaBenchmarkHelper}; use sp_std::prelude::*; - +/// storage migrations +pub mod migration; mod stateful_child_tree; pub mod types; pub mod weights; @@ -60,6 +61,8 @@ use sp_core::{bounded::BoundedVec, crypto::AccountId32}; use sp_runtime::{traits::Convert, DispatchError, MultiSignature}; pub use weights::*; +const LOG_TARGET: &str = "runtime::stateful-storage"; + #[frame_support::pallet] pub mod pallet { use super::*; @@ -124,8 +127,13 @@ pub mod pallet { // Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and // method. #[pallet::pallet] + #[pallet::storage_version(STATEFUL_STORAGE_VERSION)] pub struct Pallet(_); + /// A temporary storage for migration + #[pallet::storage] + pub(super) type MigrationPageIndex = StorageValue<_, u32, ValueQuery>; + #[pallet::error] pub enum Error { /// Page would exceed the highest allowable PageId @@ -220,6 +228,27 @@ pub mod pallet { }, } + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(_current: BlockNumberFor) -> Weight { + // this should get removed after rolling out to testnet + #[cfg(any(feature = "frequency-testnet", test))] + { + let page_index = >::get(); + let (weight, continue_migration) = migration::v1::paginated_migration_testnet::( + MIGRATION_PAGE_SIZE, + page_index, + ); + if continue_migration { + >::set(page_index.saturating_add(1)); + } + T::DbWeight::get().reads_writes(1, 1).saturating_add(weight) + } + #[cfg(not(any(feature = "frequency-testnet", test)))] + Weight::zero() + } + } + #[pallet::call] impl Pallet { /// Applies the Add or Delete Actions on the requested Itemized page. diff --git a/pallets/stateful-storage/src/migration/mod.rs b/pallets/stateful-storage/src/migration/mod.rs new file mode 100644 index 0000000000..5d89e038e1 --- /dev/null +++ b/pallets/stateful-storage/src/migration/mod.rs @@ -0,0 +1,2 @@ +/// migrations to v1 +pub mod v1; diff --git a/pallets/stateful-storage/src/migration/v1.rs b/pallets/stateful-storage/src/migration/v1.rs new file mode 100644 index 0000000000..66ad4a6111 --- /dev/null +++ b/pallets/stateful-storage/src/migration/v1.rs @@ -0,0 +1,258 @@ +#[cfg(feature = "try-runtime")] +use crate::types::STATEFUL_STORAGE_VERSION; +use crate::{ + stateful_child_tree::StatefulChildTree, + types::{ + ItemAction, ItemizedKey, ItemizedOperations, ItemizedPage, Page, ITEMIZED_STORAGE_PREFIX, + PALLET_STORAGE_PREFIX, + }, + Config, Pallet, LOG_TARGET, +}; +use common_primitives::{ + msa::MessageSourceId, + utils::{get_chain_type_by_genesis_hash, DetectedChainType}, +}; +use frame_support::{pallet_prelude::*, traits::OnRuntimeUpgrade, weights::Weight}; +use frame_system::pallet_prelude::BlockNumberFor; +use log; +#[cfg(feature = "try-runtime")] +use sp_core::hexdisplay::HexDisplay; +use sp_runtime::Saturating; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; +use sp_std::{vec, vec::Vec}; + +/// testnet specific msa ids for migration +#[cfg(any(feature = "frequency-testnet", test))] +pub fn get_testnet_msa_ids() -> Vec { + vec![ + 8004, 8009, 8816, 8817, 8818, 8819, 8820, 8822, 8823, 8824, 8825, 8826, 9384, 9753, 9919, + 9992, 9994, 9996, 9997, 10009, 10010, 10012, 10013, 10014, 10015, 10019, 10020, 10021, + 10022, 10023, 10024, 10025, 10026, 10027, 10028, 10029, 10030, 10031, 10032, 10033, 10034, + 10035, 10036, 10037, 10038, 10039, 10040, 10041, 10042, 10043, 10044, 10045, 10046, 10047, + 10048, 10049, 10050, 10051, 10052, 10053, 10054, 10055, 10056, 10057, 10058, 10059, 10061, + 10062, 10064, 10067, 10068, 10069, 10070, 10071, 10072, 10075, 10076, 10077, 10078, 10079, + 10138, 10139, 10140, 10206, 10207, 10209, 10212, 10218, 10219, 10220, 10221, 10222, 10223, + 10224, 10231, 10232, 10233, 10234, 10235, 10236, 10237, 10238, 10239, 10240, 10241, 10242, + 10243, 10247, 10248, 10251, 10253, 10254, 10255, 10256, 10257, 10258, 10259, 10260, 10261, + 10262, 10263, 10264, 10265, 10266, 10267, 10268, 10269, 10270, 10271, 10272, 10273, 10274, + 10275, 10287, 10288, 10289, 10290, 10291, 10292, 10293, 10294, 10295, 10296, 10297, 10298, + 10299, 10300, 10301, 10302, 10303, 10304, 10305, 10306, 10307, 10308, 10309, 10311, 10312, + 10313, 10314, 10315, 10316, 10317, 10318, 10319, 10320, 10321, 10322, 10323, 10324, 10325, + 10326, 10327, 10328, 10329, + ] +} + +/// returns the chain type from genesis hash +pub fn get_chain_type() -> DetectedChainType { + let genesis_block: BlockNumberFor = 0u32.into(); + let genesis = >::block_hash(genesis_block); + get_chain_type_by_genesis_hash(&genesis.encode()[..]) +} + +/// get the msa ids with key migrations +pub fn get_msa_ids() -> Vec { + let chain_type = get_chain_type::(); + if let DetectedChainType::FrequencyMainNet = chain_type { + vec![ + 227, 542, 1249820, 1287729, 1288925, 1309067, 1309241, 1309258, 1309367, 1309397, + 1329112, 1329535, 1330067, + ] + } else { + if cfg!(test) { + // this allows to test the mainnet path + vec![1] + } else { + // we are going to use hooks for this multi-block migration so this is empty to only flag that + // it as done for consistency + vec![] + } + } +} + +/// migration to v1 implementation +pub struct MigrateToV1(PhantomData); + +impl OnRuntimeUpgrade for MigrateToV1 { + fn on_runtime_upgrade() -> Weight { + migrate_to_v1::() + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, TryRuntimeError> { + log::info!(target: LOG_TARGET, "Running pre_upgrade..."); + let on_chain_version = Pallet::::on_chain_storage_version(); + let genesis_block: BlockNumberFor = 0u32.into(); + let genesis = >::block_hash(genesis_block); + if on_chain_version >= 1 { + return Ok(Vec::new()) + } + log::info!(target: LOG_TARGET, "Found genesis... {:?}", genesis); + let detected_chain = get_chain_type_by_genesis_hash(&genesis.encode()[..]); + log::info!(target: LOG_TARGET,"Detected Chain is {:?}", detected_chain); + Ok(Vec::new()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_: Vec) -> Result<(), TryRuntimeError> { + log::info!(target: LOG_TARGET, "Running post_upgrade..."); + let on_chain_version = Pallet::::on_chain_storage_version(); + if on_chain_version > 1 { + return Ok(()) + } + let onchain_version = Pallet::::on_chain_storage_version(); + assert_eq!(onchain_version, STATEFUL_STORAGE_VERSION); + // check to ensure updates took place + let schema_id = get_schema_id(); + let msa_ids = get_msa_ids::(); + for msa_id in msa_ids.into_iter() { + let itemized: ItemizedPage = Pallet::::get_itemized_page_for(msa_id, schema_id) + .map_err(|_| TryRuntimeError::Other("can not get storage"))? + .ok_or(TryRuntimeError::Other("no storage"))?; + let (_, val) = as ItemizedOperations>::try_parse( + &itemized, false, + ) + .map_err(|_| TryRuntimeError::Other("can not parse storage"))? + .items + .clone() + .into_iter() + .next() + .ok_or(TryRuntimeError::Other("no item"))?; + + assert_eq!(val.len(), 33); + log::info!(target: LOG_TARGET, "{:?}", HexDisplay::from(&val)); + } + log::info!(target: LOG_TARGET, "Finished post_upgrade"); + Ok(()) + } +} + +/// migrating to v1 +pub fn migrate_to_v1() -> Weight { + log::info!(target: LOG_TARGET, "Running storage migration..."); + let onchain_version = Pallet::::on_chain_storage_version(); + let current_version = Pallet::::in_code_storage_version(); + log::info!(target: LOG_TARGET, "onchain_version= {:?}, current_version={:?}", onchain_version, current_version); + if onchain_version < 1 { + let msa_ids = get_msa_ids::(); + let weights = migrate_msa_ids::(&msa_ids[..]); + // Set storage version to `1`. + StorageVersion::new(1).put::>(); + let total_weight = T::DbWeight::get().writes(1).saturating_add(weights); + log::info!(target: LOG_TARGET, "Migration Calculated weights={:?}",total_weight); + total_weight + } else { + log::info!( + target: LOG_TARGET, + "Migration did not execute. This probably should be removed onchain:{:?}, current:{:?}", + onchain_version, + current_version + ); + T::DbWeight::get().reads(1) + } +} + +/// migrating all msa_ids +pub fn migrate_msa_ids(msa_ids: &[MessageSourceId]) -> Weight { + let schema_id = get_schema_id(); + let key: ItemizedKey = (schema_id,); + let each_layer_access: u64 = 33 * 16; + let mut reads = 1u64; + let mut writes = 0u64; + let mut bytes = 0u64; + + for msa_id in msa_ids.iter() { + reads.saturating_inc(); + // get the itemized storages + let itemized_result: Result>, _> = + Pallet::::get_itemized_page_for(*msa_id, schema_id); + match itemized_result { + Ok(Some(existing_page)) => { + bytes = bytes.saturating_add(existing_page.encode().len() as u64); + bytes = bytes.saturating_add(each_layer_access * 3); // three layers in merkle tree + + match as ItemizedOperations>::try_parse( + &existing_page, + false, + ) { + Ok(parsed_page) => match parsed_page.items.clone().into_iter().next() { + Some((_, existing_value)) => match existing_value.len() { + 32usize => { + // 64 is decimal value for 0x40 + let mut prefixed = vec![64u8]; + prefixed.extend_from_slice(existing_value); + let bounded: BoundedVec = + prefixed.try_into().unwrap_or_default(); + + let empty_page = ItemizedPage::::default(); + match ::MaxItemizedPageSizeBytes> as ItemizedOperations>::apply_item_actions(&empty_page,&vec![ItemAction::Add { + data: bounded, + }]) { + Ok(mut updated_page) => { + updated_page.nonce = existing_page.nonce; + StatefulChildTree::::write( + msa_id, + PALLET_STORAGE_PREFIX, + ITEMIZED_STORAGE_PREFIX, + &key, + &updated_page, + ); + bytes = bytes.saturating_add(updated_page.encode().len() as u64); + writes.saturating_inc(); + }, + Err(e) => + log::error!(target: LOG_TARGET, "Error appending prefixed value {:?} and schema_id {:?} with {:?}", msa_id, schema_id, e), + } + }, + 33usize => + log::warn!(target: LOG_TARGET, "Itemized page item for msa_id {:?} and schema_id {:?} has correct size", msa_id, schema_id), + _ => + log::warn!(target: LOG_TARGET, "Itemized page item for msa_id {:?} and schema_id {:?} has invalid size {:?}", msa_id, schema_id, existing_value.len()), + }, + None => + log::warn!(target: LOG_TARGET, "Itemized page was empty for msa_id {:?} and schema_id {:?}", msa_id, schema_id), + }, + Err(e) => + log::error!(target: LOG_TARGET, "Error parsing page for msa_id {:?} and schema_id {:?} with {:?}", msa_id, schema_id, e), + } + }, + Ok(None) => + log::warn!(target: LOG_TARGET, "No page found for msa_id {:?} and schema_id {:?}", msa_id, schema_id), + Err(e) => + log::error!(target: LOG_TARGET, "Error getting the page for msa_id {:?} and schema_id {:?} with {:?}", msa_id, schema_id, e), + } + } + log::info!(target: LOG_TARGET, "Storage migrated to version 1 read={:?}, write={:?}, bytes={:?}", reads, writes, bytes); + let weights = T::DbWeight::get().reads_writes(reads, writes).add_proof_size(bytes); + log::info!(target: LOG_TARGET, "migrate_msa_ids weights={:?}",weights); + weights +} + +/// paginated migration for testnet +#[cfg(any(feature = "frequency-testnet", test))] +pub fn paginated_migration_testnet(page_size: u32, page_index: u32) -> (Weight, bool) { + let msa_ids: Vec = get_testnet_msa_ids(); + let mut chunks = msa_ids.chunks(page_size as usize); + let chunk_len = chunks.len() as u32; + let mut current = 0u32; + while current < page_index && current < chunk_len { + let _ = chunks.next(); + current += 1; + } + match chunks.next() { + Some(page) => { + let weight = migrate_msa_ids::(page); + (weight, true) + }, + None => (Weight::zero(), false), + } +} + +fn get_schema_id() -> u16 { + if cfg!(test) { + // Supported ITEMIZED_APPEND_ONLY_SCHEMA for tests + 103 + } else { + 7 + } +} diff --git a/pallets/stateful-storage/src/tests/migrations_tests.rs b/pallets/stateful-storage/src/tests/migrations_tests.rs new file mode 100644 index 0000000000..313f0ae76f --- /dev/null +++ b/pallets/stateful-storage/src/tests/migrations_tests.rs @@ -0,0 +1,104 @@ +use crate::{ + migration::{v1, v1::get_testnet_msa_ids}, + pallet::MigrationPageIndex, + test_common::constants::ITEMIZED_APPEND_ONLY_SCHEMA, + tests::mock::{ + new_test_ext, run_to_block, test_public, RuntimeOrigin, StatefulStoragePallet, Test, + }, + types::{ItemAction, MIGRATION_PAGE_SIZE}, +}; +use common_primitives::stateful_storage::PageHash; +use frame_support::{ + assert_ok, pallet_prelude::StorageVersion, traits::GetStorageVersion, BoundedVec, +}; + +#[test] +fn migration_to_v1_should_work_as_expected() { + new_test_ext().execute_with(|| { + // arrange + let msa_id = 1; + let caller_1 = test_public(msa_id); + let schema_id = ITEMIZED_APPEND_ONLY_SCHEMA; + let payload = + hex::decode("0e0abc9afdfb34cf30c59d16bbbbd76cae1668fc331eb9e505cb0d6af6365f17") + .expect("should decode hex"); + let expected = + hex::decode("400e0abc9afdfb34cf30c59d16bbbbd76cae1668fc331eb9e505cb0d6af6365f17") + .expect("should decode hex"); + let prev_content_hash: PageHash = 0; + let actions = vec![ItemAction::Add { data: payload.try_into().unwrap() }]; + + // act + assert_ok!(StatefulStoragePallet::apply_item_actions( + RuntimeOrigin::signed(caller_1), + msa_id, + schema_id, + prev_content_hash, + BoundedVec::try_from(actions).unwrap(), + )); + + // Act + let _ = v1::migrate_to_v1::(); + + // Assert + let current_version = StatefulStoragePallet::on_chain_storage_version(); + assert_eq!(current_version, StorageVersion::new(1)); + + let known_msa_ids = v1::get_msa_ids::(); + assert_eq!(known_msa_ids.len(), 1); + + let after_update = + StatefulStoragePallet::get_itemized_storage(msa_id, schema_id).expect("should get"); + let item = after_update.items.into_iter().next().expect("should item exists"); + assert_eq!(item.payload, expected); + }); +} + +#[test] +fn migration_to_v1_should_work_on_test_net_and_multi_block_path() { + new_test_ext().execute_with(|| { + // arrange + let msa_ids = get_testnet_msa_ids(); + let schema_id = ITEMIZED_APPEND_ONLY_SCHEMA; + let payload = + hex::decode("0e0abc9afdfb34cf30c59d16bbbbd76cae1668fc331eb9e505cb0d6af6365f17") + .expect("should decode hex"); + let expected = + hex::decode("400e0abc9afdfb34cf30c59d16bbbbd76cae1668fc331eb9e505cb0d6af6365f17") + .expect("should decode hex"); + let prev_content_hash: PageHash = 0; + let actions = vec![ItemAction::Add { data: payload.try_into().unwrap() }]; + for msa_id in msa_ids.iter() { + let caller_1 = test_public(*msa_id); + assert_ok!(StatefulStoragePallet::apply_item_actions( + RuntimeOrigin::signed(caller_1), + *msa_id, + schema_id, + prev_content_hash, + BoundedVec::try_from(actions.clone()).unwrap(), + )); + } + let msa_id_len = msa_ids.len() as u32; + + // Act + let _ = v1::migrate_to_v1::(); + + let blocks = msa_id_len / MIGRATION_PAGE_SIZE + 5; + for b in 2..=blocks { + run_to_block(b); + } + + // Assert + let current_version = StatefulStoragePallet::on_chain_storage_version(); + assert_eq!(current_version, StorageVersion::new(1)); + + for msa_id in msa_ids { + let after_update = + StatefulStoragePallet::get_itemized_storage(msa_id, schema_id).expect("should get"); + let item = after_update.items.into_iter().next().expect("should item exists"); + assert_eq!(item.payload, expected); + } + + assert_eq!(MigrationPageIndex::::get(), msa_id_len / MIGRATION_PAGE_SIZE + 1); + }); +} diff --git a/pallets/stateful-storage/src/tests/mock.rs b/pallets/stateful-storage/src/tests/mock.rs index 5810f40460..f7059447b4 100644 --- a/pallets/stateful-storage/src/tests/mock.rs +++ b/pallets/stateful-storage/src/tests/mock.rs @@ -16,10 +16,11 @@ use common_primitives::{ SchemaSetting, }, }; +use common_runtime::weights::rocksdb_weights::constants::RocksDbWeight; use frame_support::{ dispatch::DispatchResult, parameter_types, - traits::{ConstU16, ConstU32}, + traits::{ConstU16, ConstU32, OnFinalize, OnInitialize}, Twox128, }; use frame_system as system; @@ -32,7 +33,7 @@ use sp_runtime::{ type Block = frame_system::mocking::MockBlockU32; pub const INVALID_SCHEMA_ID: SchemaId = SchemaId::MAX; -pub const INVALID_MSA_ID: MessageSourceId = 100; +pub const INVALID_MSA_ID: MessageSourceId = 100_000_000; pub const TEST_ACCOUNT_SEED: [u8; 32] = [0; 32]; // Configure a mock runtime to test the pallet. @@ -48,7 +49,7 @@ impl system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); type BlockLength = (); - type DbWeight = (); + type DbWeight = RocksDbWeight; type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type Nonce = u64; @@ -406,7 +407,9 @@ fn get_signature_benchmarks_public_account() -> sr25519::Public { } pub fn test_public(n: MessageSourceId) -> AccountId32 { - AccountId32::new([n as u8; 32]) + let mut array = [0u8; 32]; + array[0..8].copy_from_slice(&n.to_le_bytes()); + AccountId32::new(array) } #[cfg(feature = "runtime-benchmarks")] @@ -420,3 +423,15 @@ pub fn new_test_ext_keystore() -> sp_io::TestExternalities { ext } + +/// advances the block +pub fn run_to_block(n: u32) { + while System::block_number() < n { + if System::block_number() > 1 { + System::on_finalize(System::block_number()); + } + System::set_block_number(System::block_number() + 1); + System::on_initialize(System::block_number()); + StatefulStoragePallet::on_initialize(System::block_number()); + } +} diff --git a/pallets/stateful-storage/src/tests/mod.rs b/pallets/stateful-storage/src/tests/mod.rs index 333aa74e56..ab44b035a1 100644 --- a/pallets/stateful-storage/src/tests/mod.rs +++ b/pallets/stateful-storage/src/tests/mod.rs @@ -4,5 +4,6 @@ mod apply_item_actions_tests; mod child_tree_tests; mod delete_page_tests; mod itemized_operations_tests; +mod migrations_tests; mod other_tests; mod upsert_page_tests; diff --git a/pallets/stateful-storage/src/types.rs b/pallets/stateful-storage/src/types.rs index 1570202cfa..41cb420adb 100644 --- a/pallets/stateful-storage/src/types.rs +++ b/pallets/stateful-storage/src/types.rs @@ -19,6 +19,10 @@ use sp_std::{ }; use twox_hash::XxHash64; +/// Migration page size +pub const MIGRATION_PAGE_SIZE: u32 = 20; +/// Current storage version of the pallet. +pub const STATEFUL_STORAGE_VERSION: StorageVersion = StorageVersion::new(1); /// pallet storage prefix pub const PALLET_STORAGE_PREFIX: &[u8] = b"stateful-storage"; /// itemized storage prefix diff --git a/runtime/frequency/src/lib.rs b/runtime/frequency/src/lib.rs index ac76dda45d..ea114a4d8a 100644 --- a/runtime/frequency/src/lib.rs +++ b/runtime/frequency/src/lib.rs @@ -114,7 +114,8 @@ pub use common_runtime::{ weights::{block_weights::BlockExecutionWeight, extrinsic_weights::ExtrinsicBaseWeight}, }; use frame_support::traits::Contains; - +#[cfg(feature = "try-runtime")] +use frame_support::traits::{TryStateSelect, UpgradeCheckSelect}; mod ethereum; mod genesis; @@ -345,6 +346,7 @@ pub type Executive = frame_executive::Executive< MigratePalletsCurrentStorage, pallet_capacity::migration::v4::MigrationToV4, pallet_capacity::migration::provider_boost_init::ProviderBoostInit, + pallet_stateful_storage::migration::v1::MigrateToV1, ), >;