diff --git a/.gitignore b/.gitignore index 3214ad8..60c65b8 100644 --- a/.gitignore +++ b/.gitignore @@ -8,11 +8,14 @@ dist/ .playwright* !.env.example !web/.env.example +!web/.env.product-devnet !contracts/evm/.env.example web/node_modules/ web/dist/ web/dist-bulletin* +web/dist-product/ +web/*.car web/.playwright* web/playwright-report/ web/test-results/ diff --git a/CLAUDE.md b/CLAUDE.md index b1a96b0..7d211e6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -250,6 +250,14 @@ cd web npm run smoke:production-env ``` +When Product DevNet endpoints, `deployments.json`, or the DevNet build profile +change, also run the read-only endpoint check: + +```bash +cd web +npm run smoke:devnet +``` + For deployment-sensitive work, add the relevant read-only health, chain, contract, IPFS gateway, and wrong-network checks. Record the environment and evidence without exposing credentials. diff --git a/README.md b/README.md index eed1f79..0875db9 100644 --- a/README.md +++ b/README.md @@ -34,20 +34,28 @@ aura lights the whole field (`web/src/styles/aura.css`). creates one personal `SmartRuntime` per artist, and `ArtistDirectory` indexes artist addresses to their runtimes. -**Frontend**: Static React + Vite web app deployed to dot.li. +**Frontend**: Static React + Vite web app deployed to Netlify and, through the +Product profile, Bulletin/DotNS at `dotify-test01.dot`. **WebRTC**: real-time music streaming. **Socket.IO**: signaling for room discovery and SDP/ICE exchange. A future iteration can move signaling to statement-store style infrastructure. -**Product SDK direction**: Dotify remains a standalone web app first. Product -SDK / Playground / Humanity work is a progressive-enhancement track documented -in -[`docs/backlog/polkadot-product-readiness-and-killer-dapp-roadmap.md`](docs/backlog/polkadot-product-readiness-and-killer-dapp-roadmap.md). -The current SDK snapshot is prototype/reference/unaudited and must be proven -against Dotify's Host, key-delivery, room, and contract constraints before it -becomes a production dependency. +**Product SDK direction**: Dotify now has an adaptive Product DevNet build for +`dotify-test01.dot`. It keeps standalone link-first rooms and Free listening intact, +adds explicit app-scoped Product identity, and publishes through +Bulletin/DotNS. The runtime hooks now sit behind typed ports with the current +viem implementation and an experimental Product CDM/PAPI adapter boundary. +The backend key-delivery protocol now has an explicit Product sr25519 +signature scheme that binds the Product account public key to the derived H160 +requester before access checks. The Product frontend can now submit that +Product proof after explicit host-account connection; contract writes remain +passkey/EVM until CDM-installed runtime packages and host-signed transaction +evidence are proven. See +[`docs/explanation/product-devnet-architecture.md`](docs/explanation/product-devnet-architecture.md) +and the +[`Product roadmap`](docs/backlog/polkadot-product-readiness-and-killer-dapp-roadmap.md). ## Deployed @@ -69,7 +77,7 @@ becomes a production dependency. **Gateway URL** — -**DotNS name** — `dotify.dot.li` +**DotNS name** — `dotify-test01.dev-dot.li` ## How to run end-to-end (locally) @@ -118,7 +126,8 @@ npm run dev | Variable | Required | Purpose | | --------------------------- | ---------------- | -------------------------------------------------------- | -| `API_ORIGIN` | Production | Frontend origin allowed by API CORS | +| `API_ORIGIN` | Compatibility | Singular frontend CORS origin fallback | +| `API_ORIGINS` | Production | Comma-separated exact frontend CORS origins | | `PASEO_ASSET_HUB_RPC` | Key requests | Paseo Asset Hub EVM RPC used for access checks | | `DOTIFY_DIRECTORY_ADDRESS` | Key requests | ArtistDirectory address used to resolve artist runtimes | | `DOTIFY_CHAIN_ID` | Key requests | Chain ID expected in wallet-signed key requests | @@ -298,6 +307,10 @@ releasing it. Gated tracks use a signed session or signed key request; the backend verifies the requester, resolves the artist runtime, and calls `musicAccCanAccess` before releasing a per-track key. If access is denied, the UI shows the action needed to unlock the track and plays no protected audio. +Standalone clients sign with the default `eip191` scheme. Product-host clients +can use `product-sr25519-v1` by signing the same canonical Dotify message bytes +with the app-scoped Product account and sending `productPublicKey`; the backend +derives the H160 requester from that public key before any nonce is consumed. For registered artist tracks, users without a connected wallet can play Free tracks. For gated tracks, they see a sign-in/unlock gate. Dev-account fallback @@ -430,9 +443,9 @@ handle: and decide whether a backend read-through gateway is needed. 4. Keep demo-mode browser-exposed Pinata/content secrets out of public deployments. -5. Run Product SDK feasibility spikes: Host detection, Product account signing, - resource allocation, Playground/Bulletin/DotNS deployment, and PolkaVM/CDM - contract portability. +5. Validate the Product host/account and Bulletin/DotNS deployment baseline, + then wire frontend Product-signed key/session requests, resource allocation, + and PolkaVM/CDM contract portability. 6. Add a production artist dashboard on `/artists`: release drafts, edit metadata, royalty analytics, and profile verification state. 7. Deploy and monitor a public signaling server for DotNS / Bulletin builds. diff --git a/contracts/evm/contracts/ArtistRuntimeFactory.sol b/contracts/evm/contracts/ArtistRuntimeFactory.sol index ab648d4..1b43c75 100644 --- a/contracts/evm/contracts/ArtistRuntimeFactory.sol +++ b/contracts/evm/contracts/ArtistRuntimeFactory.sol @@ -253,8 +253,12 @@ contract ArtistRuntimeFactory { selectors[4] = MusicRoyaltiesPallet.musicRoyTotalBps.selector; } + /// @dev The two registrar selectors are retained so already-deployed runtimes keep a + /// stable ABI; the setter now reverts. `musicAccPersonhoodInfo` is new, so a + /// runtime created before this change needs a diamond Add cut to gain it — its + /// access decisions already follow the precompile without it. function _musicAccessSelectors() private pure returns (bytes4[] memory selectors) { - selectors = new bytes4[](7); + selectors = new bytes4[](8); selectors[0] = MusicAccessPallet.setPersonhoodRegistrar.selector; selectors[1] = MusicAccessPallet.musicAccSetPersonhoodLevel.selector; selectors[2] = MusicAccessPallet.musicAccCanAccess.selector; @@ -262,5 +266,6 @@ contract ArtistRuntimeFactory { selectors[4] = MusicAccessPallet.musicAccPersonhoodLevel.selector; selectors[5] = MusicAccessPallet.musicAccHasPersonhood.selector; selectors[6] = MusicAccessPallet.musicAccGetRegistrar.selector; + selectors[7] = MusicAccessPallet.musicAccPersonhoodInfo.selector; } } diff --git a/contracts/evm/contracts/interfaces/IPersonhood.sol b/contracts/evm/contracts/interfaces/IPersonhood.sol new file mode 100644 index 0000000..09519c8 --- /dev/null +++ b/contracts/evm/contracts/interfaces/IPersonhood.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +/// @title IPersonhood — Proof of Personhood precompile (Individuality) +/// @notice Minimal interface for the `pallet-revive` personhood precompile, live on +/// Asset Hub at `0x000000000000000000000000000000000A010000`. +/// +/// Mirrors the canonical declaration in +/// `paseo-network/runtimes/precompiles/personhood/sol/IPersonhood.sol`. +/// Only `personhoodStatus` is declared here: Dotify reads status, it does not +/// verify raw ring proofs, so `personhoodInfoByProof` is deliberately omitted +/// rather than carried as unused surface. +/// +/// The precompile reads the alias-accounts pallet, which stores per-context +/// alias mappings backed by ring membership proofs. Ring roots arrive from the +/// People chain by XCM. +interface IPersonhood { + /// @param status Personhood tier: 0 = None, 1 = Lite, 2 = Full. Tiers are + /// incremental, so a future tier leaves these values unchanged. + /// @param contextAlias Per-context 32-byte pseudonym derived from the ring membership + /// proof. Unique per person per context, which is what prevents + /// cross-application linkability. Zero when status is None. + struct PersonhoodInfo { + uint8 status; + bytes32 contextAlias; + } + + /// @notice Personhood info for `account` within a specific application `context`. + /// @param context A fixed 32-byte application identifier. The same person yields a + /// different `contextAlias` under a different context. + function personhoodStatus(address account, bytes32 context) external view returns (PersonhoodInfo memory info); +} diff --git a/contracts/evm/contracts/libraries/LibMusicAccess.sol b/contracts/evm/contracts/libraries/LibMusicAccess.sol index 59adc1c..583b01c 100644 --- a/contracts/evm/contracts/libraries/LibMusicAccess.sol +++ b/contracts/evm/contracts/libraries/LibMusicAccess.sol @@ -2,11 +2,21 @@ pragma solidity ^0.8.28; import { LibMusicRegistry } from './LibMusicRegistry.sol'; +import { LibPersonhood } from './LibPersonhood.sol'; /// @title LibMusicAccess -/// @notice Namespaced storage for listener access records and proof-of-personhood levels. -/// Personhood levels mirror the Individuality Chain DIM tiers; in the current -/// prototype they are set via an admin registrar account. +/// @notice Namespaced storage for listener access records, and personhood gating read +/// from the Individuality precompile. +/// +/// Personhood is no longer stored here. `personhoodLevelOf` and +/// `personhoodRegistrar` remain declared so existing runtimes keep their storage +/// layout intact — a diamond cannot safely reorder occupied slots — but neither +/// participates in an access decision any more. The registrar defaulted to the +/// artist, which meant an artist could grant personhood to their own listeners; +/// the precompile removes that path. +/// +/// PersonhoodLevel maps onto the precompile tiers by ordinal: +/// None(0) -> None(0), DIM1(1) -> Lite(1), DIM2(2) -> Full(2). /// /// Storage slot: keccak256("smart.runtime.pallet.music-access.storage") library LibMusicAccess { @@ -15,9 +25,10 @@ library LibMusicAccess { struct Storage { // contentHash → listener → paid mapping(bytes32 => mapping(address => bool)) paidAccess; - // account → verified personhood tier + // DEPRECATED — no longer read for access. Kept to preserve the storage layout of + // already-deployed runtimes. Personhood now comes from the precompile. mapping(address => LibMusicRegistry.PersonhoodLevel) personhoodLevelOf; - // address authorised to set personhood levels + // DEPRECATED — see above. Retained for layout compatibility only. address personhoodRegistrar; } @@ -32,9 +43,19 @@ library LibMusicAccess { // Internal helpers // ------------------------------------------------------------------------- - function hasRequiredPersonhood(Storage storage s, address account, LibMusicRegistry.PersonhoodLevel required) internal view returns (bool) { - if (required == LibMusicRegistry.PersonhoodLevel.None) return true; - return uint8(s.personhoodLevelOf[account]) >= uint8(required); + /// @notice True when `account` meets `required` personhood, per the Individuality + /// precompile. The storage argument is unused and kept only so existing + /// call sites and the pallet ABI stay unchanged. + /// @dev Fails closed when the precompile cannot answer. See LibPersonhood.hasStatus. + function hasRequiredPersonhood(Storage storage, address account, LibMusicRegistry.PersonhoodLevel required) internal view returns (bool) { + return LibPersonhood.hasStatus(account, uint8(required)); + } + + /// @notice Dotify-context personhood tier and pseudonym for `account`. + /// @dev Exposes the alias so a runtime can later count distinct people rather than + /// distinct addresses. Not used for access decisions today. + function personhoodOf(address account) internal view returns (uint8 status, bytes32 contextAlias, bool live) { + return LibPersonhood.readStatus(account); } function setPersonhoodRegistrar(Storage storage s, address registrar) internal returns (address previousRegistrar) { diff --git a/contracts/evm/contracts/libraries/LibPersonhood.sol b/contracts/evm/contracts/libraries/LibPersonhood.sol new file mode 100644 index 0000000..aaab23c --- /dev/null +++ b/contracts/evm/contracts/libraries/LibPersonhood.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +import { IPersonhood } from '../interfaces/IPersonhood.sol'; + +/// @title LibPersonhood +/// @notice Reads proof of personhood from the Individuality precompile. +/// +/// This replaces Dotify's admin-registrar personhood, which could only ever be +/// as trustworthy as the account operating it — and that account defaults to +/// the artist, who therefore had the technical ability to grant personhood to +/// their own listeners. Reading the precompile removes that forgery path +/// entirely: personhood becomes a fact about a person on the People chain, not +/// a row an operator can write. +/// +/// It also earns Dotify a property the registrar could not offer. The precompile +/// returns a per-context alias, so the same listener appears under a different +/// pseudonym in every application. Dotify learns "this is a distinct person" +/// without learning who they are anywhere else. +library LibPersonhood { + /// @dev Fixed precompile address. `pallet-revive` left-shifts the user-defined + /// `AddressMatcher::Fixed(0x0A01)` index by 16 bits to form this suffix. + /// Verified live on EVM chain 420420417 (Paseo Asset Hub, para 1000): a call + /// returns a 64-byte PersonhoodInfo, where absent addresses return empty. + address internal constant PERSONHOOD_PRECOMPILE = 0x000000000000000000000000000000000a010000; + + /// @dev Dotify's application context. Fixed forever: changing it re-pseudonymises + /// every listener, so any change is an identity migration, not a config edit. + bytes32 internal constant DOTIFY_CONTEXT = bytes32('dotify'); + + uint8 internal constant STATUS_NONE = 0; + uint8 internal constant STATUS_LITE = 1; + uint8 internal constant STATUS_FULL = 2; + + /// @notice Read personhood for `account` in Dotify's context. + /// @return status Personhood tier, or 0 when the precompile is unavailable. + /// @return alias_ Per-context pseudonym, zero when status is 0. + /// @return live True when the precompile answered with a decodable struct. + /// + /// @dev Deliberately a low-level staticcall rather than a typed call. The precompile + /// declares `HAS_CONTRACT_INFO = false`, so its `extcodesize` can be zero, and + /// Solidity's high-level call inserts an `extcodesize` check that would revert + /// against it. The staticcall also lets a chain without the precompile — a local + /// Hardhat node — resolve to "not live" instead of reverting every access query. + /// + /// `live` is returned rather than swallowed so callers can distinguish "this + /// person has no personhood" from "this chain cannot answer". Those are the same + /// decision (deny) but not the same diagnosis, and conflating them is how a + /// misconfigured deployment gets mistaken for an empty user base. + function readStatus(address account) internal view returns (uint8 status, bytes32 alias_, bool live) { + bytes memory callData = abi.encodeWithSelector(IPersonhood.personhoodStatus.selector, account, DOTIFY_CONTEXT); + + (bool ok, bytes memory returnData) = PERSONHOOD_PRECOMPILE.staticcall(callData); + + // A call to an address with no code succeeds with empty returndata, so success + // alone proves nothing. Only a full struct counts as an answer. + if (!ok || returnData.length < 64) { + return (STATUS_NONE, bytes32(0), false); + } + + IPersonhood.PersonhoodInfo memory info = abi.decode(returnData, (IPersonhood.PersonhoodInfo)); + return (info.status, info.contextAlias, true); + } + + /// @notice True when `account` holds at least `requiredStatus` in Dotify's context. + /// @dev Fails closed: an unavailable precompile denies every gated track rather than + /// admitting everyone. Product invariant — ambiguous access decisions fail closed. + function hasStatus(address account, uint8 requiredStatus) internal view returns (bool) { + if (requiredStatus == STATUS_NONE) return true; + (uint8 status, , bool live) = readStatus(account); + if (!live) return false; + return status >= requiredStatus; + } +} diff --git a/contracts/evm/contracts/pallets/MusicAccessPallet.sol b/contracts/evm/contracts/pallets/MusicAccessPallet.sol index 27e72f8..a2afafe 100644 --- a/contracts/evm/contracts/pallets/MusicAccessPallet.sol +++ b/contracts/evm/contracts/pallets/MusicAccessPallet.sol @@ -15,10 +15,17 @@ import { LibMusicNFT } from '../libraries/LibMusicNFT.sol'; /// 3. HumanFree track → granted if caller meets personhood level /// 4. Classic track → granted if caller has paid /// -/// Personhood levels (DIM1, DIM2) are set by the personhood registrar — -/// an admin account that in production will mirror the Individuality Chain. -/// The SmartRuntime owner remains the only account allowed to update -/// the registrar assignment. +/// Personhood (DIM1, DIM2) is read from the Individuality precompile at +/// 0x000000000000000000000000000000000A010000, in Dotify's own application +/// context. DIM1 maps to Lite, DIM2 to Full. +/// +/// The former admin registrar is retired. It defaulted to the artist, so an +/// artist could grant personhood to their own listeners — which made +/// `human-free` a claim the contract could not actually support. Reading the +/// precompile removes that path, and adds a per-context alias so Dotify can +/// recognise a distinct person without learning who they are in any other +/// application. Registrar entry points remain declared for ABI stability; +/// the setter reverts. /// /// Storage: LibMusicAccess (owns), LibMusicRegistry (reads), LibMusicNFT (reads) /// Prefix: musicAcc — avoids selector collisions with other pallets @@ -47,14 +54,14 @@ contract MusicAccessPallet { // Personhood level management (registrar-only) // ------------------------------------------------------------------------- - /// @notice Set the proof-of-personhood level for an account. - /// In production this would be called by an oracle reading the Individuality Chain. - function musicAccSetPersonhoodLevel(address account, LibMusicRegistry.PersonhoodLevel level) external { - LibMusicAccess.Storage storage as_ = LibMusicAccess.store(); - LibMusicAccess.requireRegistrar(as_); - require(account != address(0), 'MusicAccess: zero address'); - as_.personhoodLevelOf[account] = level; - emit MusicAccPersonhoodLevelSet(account, level); + /// @notice DEPRECATED — personhood is read from the Individuality precompile and can + /// no longer be assigned by an operator. + /// @dev Reverts rather than writing to storage no access decision reads. Accepting a + /// write that silently changes nothing would leave an operator believing a + /// listener was granted access they do not have. The parameters are retained so + /// the selector and ABI stay stable for already-deployed runtimes. + function musicAccSetPersonhoodLevel(address, LibMusicRegistry.PersonhoodLevel) external pure { + revert('MusicAccess: personhood is read from the Individuality precompile'); } // ------------------------------------------------------------------------- @@ -89,9 +96,24 @@ contract MusicAccessPallet { return LibMusicAccess.store().paidAccess[contentHash][listener]; } - /// @notice Returns the verified personhood level for `account`. + /// @notice Returns the verified personhood level for `account`, read from the + /// Individuality precompile in Dotify's application context. + /// @dev Returns None when the precompile is unavailable, matching the access + /// decision. Use `musicAccPersonhoodInfo` to tell those two cases apart. function musicAccPersonhoodLevel(address account) external view returns (LibMusicRegistry.PersonhoodLevel) { - return LibMusicAccess.store().personhoodLevelOf[account]; + (uint8 status, , bool live) = LibMusicAccess.personhoodOf(account); + if (!live) return LibMusicRegistry.PersonhoodLevel.None; + return LibMusicRegistry.PersonhoodLevel(status); + } + + /// @notice Full personhood reading for `account`: tier, Dotify-context pseudonym, and + /// whether the precompile answered at all. + /// @dev `live == false` means this chain cannot answer, which is a deployment + /// diagnosis, not a statement about the listener. `contextAlias` is the same + /// person under a different pseudonym in every other application, so it can + /// identify a returning listener without revealing who they are elsewhere. + function musicAccPersonhoodInfo(address account) external view returns (uint8 status, bytes32 contextAlias, bool live) { + return LibMusicAccess.personhoodOf(account); } /// @notice Returns true if `account` meets `required` personhood level. diff --git a/contracts/evm/contracts/test/MockPersonhoodPrecompile.sol b/contracts/evm/contracts/test/MockPersonhoodPrecompile.sol new file mode 100644 index 0000000..97be2e7 --- /dev/null +++ b/contracts/evm/contracts/test/MockPersonhoodPrecompile.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +/// @dev TEST-ONLY stand-in for the Individuality personhood precompile. +/// +/// The real precompile lives at a fixed address inside `pallet-revive` and has no +/// deployable bytecode, so a Hardhat node cannot host it. Tests install this +/// contract's runtime code at that same address with `hardhat_setCode`, then write +/// its storage through the ordinary setter below — the storage lands under the +/// precompile address, so the runtime's staticcall reads it exactly as it would +/// read the real thing. +/// +/// This is the only way to exercise the fail-closed path and the granted path on a +/// chain that has no Individuality pallet. +contract MockPersonhoodPrecompile { + struct PersonhoodInfo { + uint8 status; + bytes32 contextAlias; + } + + // account => context => info + mapping(address => mapping(bytes32 => PersonhoodInfo)) private _info; + + /// @notice Set the personhood reading returned for `account` in `context`. + function setPersonhood(address account, bytes32 context, uint8 status, bytes32 contextAlias) external { + _info[account][context] = PersonhoodInfo({ status: status, contextAlias: contextAlias }); + } + + /// @notice Matches the real precompile's signature and return shape. + function personhoodStatus(address account, bytes32 context) external view returns (PersonhoodInfo memory info) { + return _info[account][context]; + } +} diff --git a/contracts/evm/test/ArtistRuntime.test.ts b/contracts/evm/test/ArtistRuntime.test.ts index 05961fa..0b804c4 100644 --- a/contracts/evm/test/ArtistRuntime.test.ts +++ b/contracts/evm/test/ArtistRuntime.test.ts @@ -29,6 +29,20 @@ import { MUSIC_REGISTRY_REGISTER_SELECTOR, buildRegistryHotfixCalldata, registry const FacetCutAction = { Add: 0, Replace: 1, Remove: 2 } as const; const AccessMode = { HumanFree: 0, Classic: 1, Free: 2 } as const; const PersonhoodLevel = { None: 0, DIM1: 1, DIM2: 2 } as const; + +// The Individuality personhood precompile. Fixed inside pallet-revive, so a Hardhat +// node has nothing there until a test installs mock code at the same address. +const PERSONHOOD_PRECOMPILE = '0x000000000000000000000000000000000a010000' as const; +const DOTIFY_CONTEXT = `0x${Buffer.from('dotify').toString('hex').padEnd(64, '0')}` as `0x${string}`; + +/** Install the mock precompile at the real precompile address and return a handle. */ +async function installPersonhoodPrecompile() { + const mock = await hre.viem.deployContract('MockPersonhoodPrecompile'); + const publicClient = await hre.viem.getPublicClient(); + const runtimeCode = await publicClient.getCode({ address: mock.address }); + await hre.network.provider.request({ method: 'hardhat_setCode', params: [PERSONHOOD_PRECOMPILE, runtimeCode] }); + return hre.viem.getContractAt('MockPersonhoodPrecompile', PERSONHOOD_PRECOMPILE); +} const ZERO_ADDR = '0x0000000000000000000000000000000000000000' as const; function selectorsFromAbi(abi: Abi): `0x${string}`[] { @@ -251,7 +265,10 @@ describe('DotifyRuntimeInitializer — bootstrap', () => { expect(registrar.toLowerCase()).to.equal(artistA.account.address.toLowerCase()); }); - it('owner can reassign the personhood registrar and the new registrar can grant levels', async () => { + it('no registrar can grant personhood any more, not even the artist', async () => { + // The registrar defaulted to the artist, so this path let an artist manufacture + // personhood for their own listeners. It must now fail loudly rather than write + // to storage that no access decision reads. const { factory, directory, artistA, other, listener } = await loadFixture(deployDotifySystemFixture); await createArtistRuntime(factory, artistA); @@ -260,13 +277,14 @@ describe('DotifyRuntimeInitializer — bootstrap', () => { const artistAccess = await hre.viem.getContractAt('MusicAccessPallet', runtimeAddr, { client: { wallet: artistA } }); await artistAccess.write.setPersonhoodRegistrar([other.account.address]); - const access = await hre.viem.getContractAt('MusicAccessPallet', runtimeAddr); - expect((await access.read.musicAccGetRegistrar()).toLowerCase()).to.equal(other.account.address.toLowerCase()); - const delegatedRegistrar = await hre.viem.getContractAt('MusicAccessPallet', runtimeAddr, { client: { wallet: other } }); - await delegatedRegistrar.write.musicAccSetPersonhoodLevel([listener.account.address, PersonhoodLevel.DIM1]); - expect(await access.read.musicAccPersonhoodLevel([listener.account.address])).to.equal(PersonhoodLevel.DIM1); + try { + await delegatedRegistrar.write.musicAccSetPersonhoodLevel([listener.account.address, PersonhoodLevel.DIM1]); + expect.fail('Should have reverted'); + } catch (e: unknown) { + expect((e as Error).message).to.include('Individuality precompile'); + } }); it('delegated registrar cannot rotate itself; only the owner can update the registrar', async () => { @@ -365,8 +383,8 @@ describe('Artist SmartRuntime — music pallets', () => { expect((await publicClient.getBalance({ address: royaltyRecip.account.address })) > recipBefore).to.equal(true); }); - it('HumanFree track: access granted after artist sets DIM1 personhood', async () => { - const { registry, royalties, access, artistA, listener, royaltyRecip } = await withArtistRuntime(); + it('HumanFree track: access follows the Individuality precompile, not the artist', async () => { + const { registry, access, artistA, listener, royaltyRecip } = await withArtistRuntime(); const artistRegistry = await hre.viem.getContractAt('MusicRegistryPallet', registry.address, { client: { wallet: artistA } }); await artistRegistry.write.musicRegRegister([ @@ -380,15 +398,74 @@ describe('Artist SmartRuntime — music pallets', () => { [10_000] ]); + // No precompile installed yet: the chain cannot answer, so a gated track denies. + expect(await access.read.musicAccCanAccess([TRACK_HASH2, listener.account.address])).to.equal(false); + + const precompile = await installPersonhoodPrecompile(); + + // Present but with no personhood recorded — still denied. expect(await access.read.musicAccCanAccess([TRACK_HASH2, listener.account.address])).to.equal(false); - // Artist is the registrar (bootstrapped by initializer) — grant DIM1 - const artistAccess = await hre.viem.getContractAt('MusicAccessPallet', access.address, { client: { wallet: artistA } }); - await artistAccess.write.musicAccSetPersonhoodLevel([listener.account.address, PersonhoodLevel.DIM1]); + const alias_ = `0x${'ab'.repeat(32)}` as `0x${string}`; + await precompile.write.setPersonhood([listener.account.address, DOTIFY_CONTEXT, PersonhoodLevel.DIM1, alias_]); + + expect(await access.read.musicAccCanAccess([TRACK_HASH2, listener.account.address])).to.equal(true); + expect(await access.read.musicAccPersonhoodLevel([listener.account.address])).to.equal(PersonhoodLevel.DIM1); + + const [status, contextAlias, live] = await access.read.musicAccPersonhoodInfo([listener.account.address]); + expect(status).to.equal(PersonhoodLevel.DIM1); + expect(contextAlias).to.equal(alias_); + expect(live).to.equal(true); + void artistA; + }); + + it('HumanFree track: a lower tier than required is still denied', async () => { + const { registry, access, artistA, listener, royaltyRecip } = await withArtistRuntime(); + + const artistRegistry = await hre.viem.getContractAt('MusicRegistryPallet', registry.address, { client: { wallet: artistA } }); + await artistRegistry.write.musicRegRegister([ + sampleRegistration({ + contentHash: TRACK_HASH2, + accessMode: AccessMode.HumanFree, + pricePlanck: 0n, + requiredPersonhood: PersonhoodLevel.DIM2 + }), + [royaltyRecip.account.address], + [10_000] + ]); + + const precompile = await installPersonhoodPrecompile(); + await precompile.write.setPersonhood([listener.account.address, DOTIFY_CONTEXT, PersonhoodLevel.DIM1, `0x${'cd'.repeat(32)}`]); + expect(await access.read.musicAccCanAccess([TRACK_HASH2, listener.account.address])).to.equal(false); + await precompile.write.setPersonhood([listener.account.address, DOTIFY_CONTEXT, PersonhoodLevel.DIM2, `0x${'cd'.repeat(32)}`]); expect(await access.read.musicAccCanAccess([TRACK_HASH2, listener.account.address])).to.equal(true); }); + it('personhood granted in another application context does not unlock Dotify', async () => { + // This is the property the registrar could never provide: the same person carries a + // different alias per context, and a proof issued to another app is not Dotify's. + const { registry, access, artistA, listener, royaltyRecip } = await withArtistRuntime(); + + const artistRegistry = await hre.viem.getContractAt('MusicRegistryPallet', registry.address, { client: { wallet: artistA } }); + await artistRegistry.write.musicRegRegister([ + sampleRegistration({ + contentHash: TRACK_HASH2, + accessMode: AccessMode.HumanFree, + pricePlanck: 0n, + requiredPersonhood: PersonhoodLevel.DIM1 + }), + [royaltyRecip.account.address], + [10_000] + ]); + + const precompile = await installPersonhoodPrecompile(); + const otherContext = `0x${Buffer.from('dotns').toString('hex').padEnd(64, '0')}` as `0x${string}`; + await precompile.write.setPersonhood([listener.account.address, otherContext, PersonhoodLevel.DIM2, `0x${'ef'.repeat(32)}`]); + + expect(await access.read.musicAccCanAccess([TRACK_HASH2, listener.account.address])).to.equal(false); + }); + it('NFT owner is the artist; NFT transfer moves ownership', async () => { const { registry, nft, artistA, other, royaltyRecip } = await withArtistRuntime(); @@ -599,7 +676,13 @@ describe('Artist isolation', () => { expect(await registryB.read.musicRegTrackCount()).to.equal(0n); }); - it('personhood granted on artist A has no effect on artist B', async () => { + it('personhood is a property of the person, so it reads the same on every runtime', async () => { + // This deliberately inverts the previous expectation. Personhood used to be + // per-runtime state an artist wrote, so it could differ between two artists for the + // same listener - which is exactly what made it forgeable. It is now one fact about + // a person in Dotify's context, so every runtime reads the same answer and no + // artist can change it. Catalog and payment state stay per-runtime; only the + // question "is this a distinct human" became global. const ctx = await loadFixture(deployDotifySystemFixture); await createArtistRuntime(ctx.factory, ctx.artistA); @@ -608,13 +691,27 @@ describe('Artist isolation', () => { const runtimeAddrA = (await ctx.directory.read.runtimeOf([ctx.artistA.account.address])) as `0x${string}`; const runtimeAddrB = (await ctx.directory.read.runtimeOf([ctx.artistB.account.address])) as `0x${string}`; - // Artist A grants listener DIM1 - const accessA = await hre.viem.getContractAt('MusicAccessPallet', runtimeAddrA, { client: { wallet: ctx.artistA } }); - await accessA.write.musicAccSetPersonhoodLevel([ctx.listener.account.address, PersonhoodLevel.DIM1]); - - // Listener's level on B's runtime is still None + const accessA = await hre.viem.getContractAt('MusicAccessPallet', runtimeAddrA); const accessB = await hre.viem.getContractAt('MusicAccessPallet', runtimeAddrB); + + // No precompile on this chain yet: both runtimes agree the answer is None. + expect(await accessA.read.musicAccPersonhoodLevel([ctx.listener.account.address])).to.equal(PersonhoodLevel.None); expect(await accessB.read.musicAccPersonhoodLevel([ctx.listener.account.address])).to.equal(PersonhoodLevel.None); + + const precompile = await installPersonhoodPrecompile(); + await precompile.write.setPersonhood([ctx.listener.account.address, DOTIFY_CONTEXT, PersonhoodLevel.DIM2, `0x${'11'.repeat(32)}`]); + + expect(await accessA.read.musicAccPersonhoodLevel([ctx.listener.account.address])).to.equal(PersonhoodLevel.DIM2); + expect(await accessB.read.musicAccPersonhoodLevel([ctx.listener.account.address])).to.equal(PersonhoodLevel.DIM2); + + // And neither artist can alter it. + const artistAccessA = await hre.viem.getContractAt('MusicAccessPallet', runtimeAddrA, { client: { wallet: ctx.artistA } }); + try { + await artistAccessA.write.musicAccSetPersonhoodLevel([ctx.listener.account.address, PersonhoodLevel.None]); + expect.fail('Should have reverted'); + } catch (e: unknown) { + expect((e as Error).message).to.include('Individuality precompile'); + } }); }); diff --git a/docs/README.md b/docs/README.md index 9b47fa3..c50c5c6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,6 +15,7 @@ Conceptual documents that help you understand why Dotify works the way it does. | [Content Protection](./explanation/content-protection.md) | All | Audio encryption pipeline, what it protects, and what it does not | | [Royalty Settlement](./explanation/royalty-settlement.md) | All | How DOT payments flow from listener wallet to artist wallet | | [Listening Rooms](./explanation/listening-rooms.md) | All | WebRTC peer-to-peer streaming, signaling protocol, known limitations | +| [Product DevNet Architecture](./explanation/product-devnet-architecture.md) | Maintainers | Dual-host boundaries, Product account capabilities, rooms, storage, and the proposed contract port | --- @@ -40,6 +41,7 @@ Runbooks for hosted configuration and production validation. | Document | Summary | |---|---| | [Deployment Configuration](./operations/deployment-configuration.md) | Netlify and Fly dashboard settings, secrets, catalog persistence, validation, and the update checklist for future env/config changes | +| [Product DevNet Deployment](./operations/product-devnet-deployment.md) | Build, publish, validate, and roll back the `dotify-test01.dot` Product DevNet app | --- diff --git a/docs/backlog/24-access-streaming-v2.md b/docs/backlog/24-access-streaming-v2.md index 556fccd..1267a60 100644 --- a/docs/backlog/24-access-streaming-v2.md +++ b/docs/backlog/24-access-streaming-v2.md @@ -139,12 +139,14 @@ P3 first vertical slice delivered (`agent/audio-v2-p3`): the browser/device validation matrix, startup telemetry export, and the backend read-through gateway decision. -Product SDK replanning note (2026-07-14): +Product SDK adaptation note (2026-07-26): -- Product SDK (`@parity/product-sdk` 0.17.0 at - `2f359bba28ca72855207a0a519d4118b37b4438c`) is prototype/reference/unaudited. +- Product SDK 0.19.1 and deploy tooling 0.13.1 remain + prototype/reference/unaudited. - Host APIs are progressive enhancement for Product containers; standalone web remains a supported mode. +- Host detection, explicit Product account identity, a Product DevNet build, + canonical room links, and dual-origin Fly configuration are implemented. - Product SDK contracts use `pallet-revive`, PolkaVM artifacts, and CDM manifests. Dotify's current Hardhat + viem + Paseo Asset Hub EVM path needs a portability spike before adopting that layer. diff --git a/docs/backlog/README.md b/docs/backlog/README.md index 5afea40..4d45a60 100644 --- a/docs/backlog/README.md +++ b/docs/backlog/README.md @@ -104,18 +104,22 @@ ticket 18 preview assets are consciously retired by access model v2. `improvement-plan.md` tracks the July 2026 review of the implementation against the product/technical/philosophical memory and the current Parity Product SDK direction. The plan is now dual-mode: standalone web remains the -first public listening path, while Product SDK / Playground / Humanity -integration is a gated feasibility track. Nothing in that track may imply live -Host, Statement Store, Product account, Humanity, or `.dot` deployment support -until the relevant spike proves the current API, environment, and security -boundary. +first public listening path, while the Product DevNet build adds +`dotify-test01.dot`, +explicit Host detection, app-scoped Product identity, and canonical +Product-origin room links. The typed runtime ports and experimental +Product CDM/PAPI adapter boundary are implementation preparation only; they do +not imply Product-signed contract writes, Statement Store rooms, or Humanity +decisions. API-side Product-signed key/session verification now exists through +`product-sr25519-v1`, but the Product frontend still needs to send host-signed +requests before protected playback can use that identity path. The Product SDK evidence snapshot used for this replanning is -`paritytech/product-sdk@2f359bba28ca72855207a0a519d4118b37b4438c` -(`@parity/product-sdk` 0.17.0), fetched on 2026-07-14. It is explicitly -prototype / reference / unaudited code. Paseo and Summit are the live preset -environments; Product SDK contracts target `pallet-revive` / PolkaVM CDM flows, -not Dotify's current viem + EVM RPC path; Statement Store is useful for small +`@parity/product-sdk` 0.19.1 and +`@polkadot-community-foundation/polkadot-app-deploy` 0.13.1, +verified on 2026-07-26. They remain prototype / reference / unaudited code. +Product SDK contracts target `pallet-revive` / PolkaVM CDM flows, not Dotify's +current viem + EVM RPC write path; Statement Store is useful for small ephemeral presence, not full chat, SDP/ICE, durable media metadata, or guest reactions. @@ -160,9 +164,10 @@ on `main`. The remaining order is: signaling and production-env evidence are closed through #36/#37. 3. Improve room resilience and shared-listening depth only where it preserves the link-first guest doctrine. -4. Run Product SDK feasibility spikes: Host capability detection, Product - account signing, resource allocation, PolkaVM/CDM contract portability, - Playground/Bulletin/DotNS deployment, and Statement Store presence. +4. Validate the delivered Product host/account and Bulletin/DotNS baseline, + then wire real CDM-installed runtime packages through the Product CDM/PAPI + adapter, wire frontend Product-signed key/session requests, and run bounded + resource-allocation/Statement Store spikes. 5. Build live Humanity / Individuality only after the research ticket proves a privacy-preserving source, proof shape, address-binding story, and fallback UX. diff --git a/docs/backlog/improvement-plan.md b/docs/backlog/improvement-plan.md index 49937b0..23fd81f 100644 --- a/docs/backlog/improvement-plan.md +++ b/docs/backlog/improvement-plan.md @@ -62,25 +62,24 @@ Where it falls short of its own standards: ## Product SDK feasibility track -This track runs in parallel with standalone hardening, but it does not block -first sound and must not be sold as a delivered capability. +This track runs in parallel with standalone hardening and does not block first +sound. Only the baseline rows marked delivered may be presented as live. Product SDK snapshot used for this plan: -- `paritytech/product-sdk@2f359bba28ca72855207a0a519d4118b37b4438c` - (fetched 2026-07-14); -- `@parity/product-sdk` 0.17.0; +- `@parity/product-sdk` 0.19.1 (verified 2026-07-26); +- `@polkadot-community-foundation/polkadot-app-deploy` 0.13.1; - explicit prototype / reference / unaudited status; -- live preset environments: Paseo and Summit; +- Product target: DevNet Asset Hub / People / Bulletin; - contracts package: `pallet-revive`, PolkaVM artifacts, and CDM manifests; - Statement Store: 512-byte statement payload, 1024-byte user total, default 30-second TTL. | Item | Tracking | Status | | --- | --- | --- | -| Product SDK baseline: pin SDK versions, document compatible Host surfaces, and add feature detection for Host local storage, signing, permissions, resource allocation, payments, and chain support. | #85, `polkadot-product-readiness-and-killer-dapp-roadmap.md` | Proposed | +| Product SDK baseline: pin SDK versions, detect Host availability, connect an app-scoped account explicitly, and separate presence identity from EVM signing authority. | #85, `polkadot-product-readiness-and-killer-dapp-roadmap.md` | Delivered on Product adaptation branch | | Contract portability spike: compare Dotify's current Paseo Asset Hub EVM / viem / Hardhat flow with Product SDK contracts on `pallet-revive`, PolkaVM artifacts, and CDM manifests. | #85 | Proposed | -| Playground deployment spike: determine whether Dotify's static build can use Playground/Bulletin/DotNS deploy flows without weakening current secret and publication boundaries. | #85 | Proposed | +| Product deployment baseline: build a browser-safe multi-file bundle, publish through Bulletin/DotNS tooling, preserve backend key custody, and use a canonical public room URL. | #85 | Delivered on Product adaptation branch; live publication pending operator credentials | | Statement Store presence spike: use it for small, signed, ephemeral discovery/presence only. Do not move SDP/ICE, full chat history, media metadata, or link-only guest reactions there until signer, TTL, and size constraints are solved. | #89, `20-room-social-layer.md`, `21-room-collaborative-queue.md` | Proposed | | Humanity / Individuality research rewrite: prove the canonical live source, privacy-preserving proof shape, product-account/identity-account binding, and fallback UX before promoting Human free from research to build. | #12, `11-proof-of-personhood-integration-research.md` | Open | diff --git a/docs/backlog/polkadot-product-readiness-and-killer-dapp-roadmap.md b/docs/backlog/polkadot-product-readiness-and-killer-dapp-roadmap.md index d6d497f..d6ae7ed 100644 --- a/docs/backlog/polkadot-product-readiness-and-killer-dapp-roadmap.md +++ b/docs/backlog/polkadot-product-readiness-and-killer-dapp-roadmap.md @@ -1,15 +1,16 @@ # Polkadot product readiness and killer dapp roadmap -Status: active planning note, supersedes the stale draft from PR #91. +Status: active execution note; the Product DevNet baseline is implemented on +`feat/product-devnet-adaptation`. -Last Product SDK verification: 2026-07-14 against -`paritytech/product-sdk@2f359bba28ca72855207a0a519d4118b37b4438c` -(`@parity/product-sdk` 0.17.0). +Last Product SDK verification: 2026-07-26 against +`@parity/product-sdk` 0.19.1 and +`@polkadot-community-foundation/polkadot-app-deploy` 0.13.1. ## Verdict -Dotify should align with the Polkadot product ecosystem, but it should not -replace its standalone production path with Product SDK assumptions yet. +Dotify should align with the Polkadot product ecosystem without replacing its +standalone production path with Product SDK assumptions. The right product shape is dual-mode: @@ -23,6 +24,19 @@ The right product shape is dual-mode: product failure state. It must not fall back to demo secrets, hidden signers, or bypassed access checks. +The first adaptive slice is now implemented: + +- a separate Product DevNet build and `dotify-test01.dot` manifest; +- explicit Host detection and app-scoped Product account connection; +- Product identity for room presence without claiming EVM/EIP-191 authority; +- canonical `.dev-dot.li` room links; +- shared Fly API/signaling allowlists for Netlify and Product origins; +- a pinned build/deploy workflow and operator rollback guide. + +Typed runtime ports are now extracted in the follow-up branch. Product-native +contract writes, Product-signed key requests, Product personhood, and Product +presence transport remain gated follow-up work. + ## Product ecosystem evidence The current Parity product direction is coherent: Levity for publishing, @@ -35,9 +49,8 @@ The SDK details matter for Dotify: - Product SDK and Playground are explicitly prototype / reference / unaudited code. -- Product SDK preset chains are live for Paseo and Summit. Polkadot and Kusama - preset paths are gated because Bulletin / Individuality descriptors are not - live there. +- Product DevNet exposes the Asset Hub, People, and Bulletin system-chain + topology used by the current Product tooling. - Product SDK contract helpers target `pallet-revive`, PolkaVM artifacts, and CDM manifests. Dotify currently uses Hardhat Solidity, generated EVM ABIs, viem, and Paseo Asset Hub EVM RPCs. @@ -131,24 +144,52 @@ Goal: deepen rooms without breaking the room-guest doctrine. Goal: prove the Product host path with small spikes before committing the app. -- Pin Product SDK versions and add a compatibility matrix. -- Detect Host availability and supported chain/capability surfaces. -- Prototype Product account connection, signing, identity prompt behavior, and - resource allocation. +- Delivered: pin Product SDK/deploy versions and add a compatibility matrix. +- Delivered: detect Host availability without blocking standalone first sound. +- Delivered: connect the app-scoped Product account only on explicit action and + separate identity capability from EVM signing capability. +- Delivered: publishable Bulletin/DotNS build and dual-origin Fly boundary. +- Remaining: prototype host transaction signing and resource allocation. - Compare Dotify's Hardhat/EVM runtime with Product SDK PolkaVM/CDM contracts. -- Prototype Playground deployment against Dotify's single-file build and secret - boundary. - Prototype Statement Store presence with strict payload, TTL, and signer limits. ### Phase 4 - Product integration -Goal: ship Product mode as progressive enhancement. - -- Add Product-mode adapters behind explicit ports, leaving standalone adapters - intact. -- Use Host signing and Product accounts only when the Host path is available. -- Surface Host permission denial as actionable UI state. +Goal: deepen the delivered Product mode one adapter at a time. + +- Delivered: keep standalone adapters intact and lazy-load Product host code. +- Delivered: use the Product account as presence identity only when available. +- Delivered: surface host absence and unsupported signer boundaries explicitly. +- Delivered on the follow-up branch: extract typed runtime read/write ports and + move the current viem runtime implementation behind `RuntimeReadPort` / + `RuntimeWritePort`. +- Delivered on the next follow-up branch: add an experimental CDM/PAPI adapter + behind those ports. It is not selected by default until Dotify has + CDM-installed Product runtime packages and host-signed transaction evidence. +- Delivered on the next follow-up branch: add an API-side Product sr25519 + signature scheme for key delivery and session sign-in. It binds the Product + account public key to the derived H160 requester before nonce consumption and + access checks. +- Delivered on the next follow-up branch: wire Product-host frontend key and + session requests to that signature scheme, while keeping contract writes on + the standalone EVM/passkey signer path. +- Delivered on the next follow-up branch: generate the CDM manifest and typed + contract augmentation from the same Hardhat artifacts as the viem bindings, + and implement the real Product contract resolver behind the runtime ports. + Selection stays opt-in behind `VITE_DOTIFY_RUNTIME_ADAPTER=product-cdm`. +- Settled: the chain question. Product DevNet is a preset over the Paseo system + parachains (Asset Hub 1000, People 1004, Bulletin 1010) at EVM chain + 420420417, not a separate network. Dotify's contracts are already there, + verified by byte-identical ArtistDirectory code served from both the DevNet + and Hub TestNet endpoints. No contract redeploy is needed to port to DevNet. + The SDK's `paseo` preset is Paseo Next (1500/1502), a different network, so + `devnet` is the only environment Dotify can serve a catalog from. +- Next: `pallet-revive` account mapping plus real host-signed transaction smoke + tests before Product writes can replace the EVM wallet path. This is now the + only gate left for Product contract mode. +- Next: run real Product host smoke tests for protected playback and capture the + Product sr25519 request evidence. - Keep backend key delivery authoritative unless a Product-host design proves a stronger key-custody boundary. - Keep `.dot`/Playground deployment separate from access enforcement. @@ -219,8 +260,10 @@ Recommended Project 5 fields: - #36: closed after hosted signaling operation evidence. - #37: closed after #99 and manually checked deploy-host production env evidence. -- #85: split into Product SDK baseline, contract portability, Playground deploy, - Statement Store presence, and integration adapter spikes. +- #85: Product SDK baseline, Product DevNet deployment slice, and typed runtime + port extraction implemented; keep open for CDM/PAPI contract portability, + backend Product signatures, resource allocation, and bounded Statement Store + presence. - #86: implementation active on `codex/86-catalog-read-model`; keep In Progress until review and public performance evidence close the warm/cold budgets. - #87: keep for responsive cover/gateway pipeline. diff --git a/docs/context/dotify-technical-memory.md b/docs/context/dotify-technical-memory.md index a341a1b..11d139b 100644 --- a/docs/context/dotify-technical-memory.md +++ b/docs/context/dotify-technical-memory.md @@ -57,8 +57,9 @@ Wallet-gated onboarding, runtime creation, upload, encryption, IPFS publication, - Product SDK / Playground / Humanity integration is a progressive enhancement track. The current verified Product SDK snapshot is prototype/reference/ unaudited, Host APIs require a compatible container, contracts target - `pallet-revive` / PolkaVM CDM flows, and Statement Store is constrained to - small signed ephemeral data. + `pallet-revive` / PolkaVM CDM flows, Product sr25519 key/session signatures + are wired for Product frontend protected playback after explicit host-account + connection, and Statement Store is constrained to small signed ephemeral data. ## Production spine @@ -87,7 +88,8 @@ Introduce a lean backend service for: - Pinata uploads; - content-key custody and delivery; -- wallet signature verification; +- wallet signature verification, including EIP-191 and Product sr25519 + request schemes; - nonce/replay protection; - access checks against SmartRuntime; - room host key requests; @@ -211,7 +213,9 @@ Contracts already have meaningful tests; frontend and e2e must catch up. - No dev fallback signer in public flows. - Access checks must fail closed. - Backend must not trust frontend-provided access results. -- Wallet signatures must include nonce, chain ID, content hash, requester address, request purpose, and expiration. +- Wallet signatures must include nonce, chain ID, content hash, requester + address, request purpose, and expiration; Product signatures must also bind + the Product account public key to the derived H160 requester. - Replay protection is mandatory for key requests. - Room listeners must never receive content keys or encrypted source files. - Logs must never expose secrets, keys, or raw uploaded contents. diff --git a/docs/design/dotify-product-stack-alignment.md b/docs/design/dotify-product-stack-alignment.md new file mode 100644 index 0000000..22b88d8 --- /dev/null +++ b/docs/design/dotify-product-stack-alignment.md @@ -0,0 +1,278 @@ +# Dotify On The Product Stack: Assessment And Proposed Architecture + +Status: proposal. No code changes implied by this document alone. + +Sources: [Product docs](https://docs.polkadotcommunity.foundation/), +[Product SDK](https://paritytech.github.io/product-sdk/), +[resources](https://docs.polkadotcommunity.foundation/reference/resources/), +[Polkadot Community Foundation](https://github.com/Polkadot-Community-Foundation). +Claims below are quoted or cited; where the documentation is silent, this +document says so rather than guessing. + +## 1. What The Official Stack Actually Is + +Ten architecture layers, each with a defined owner: + +| Layer | What it provides | Where it lives | +| --- | --- | --- | +| Client tier | The Polkadot app; apps run *inside* a host container | Desktop / Mobile / `dev-dot.li` | +| Identity | Device attestation -> JWT, Lite usernames, Full personhood | `identity-backend` (centralized HTTP), `people-lite`, `proof-of-ink` | +| Naming | `.dot` names; usernames mirror into DotNS | DotNS | +| App delivery | build -> Bulletin -> DotNS bind -> Browse listing | Bulletin + DotNS | +| Storage | Content-addressed CIDs; authorization is a byte/tx quota with expiry | Bulletin (para 1010) | +| Contracts | PolkaVM via `pallet-revive`; CDM builds, deploys, registers, resolves | Asset Hub (1000) | +| Identity in contracts | **Personhood precompile** returning a per-app privacy-preserving alias | Asset Hub | +| Money | CASH (pUSD asset 1) spent through Coinage; PAS pays fees | People chain (1004) | +| Messaging & calls | Encrypted chat, 1:1 voice/video; **signaling travels on-chain** | People statement store + platform TURN | +| Discovery | Browse | `browse.dev-dot.li` | + +Three properties matter more than the inventory. + +**The host is the runtime.** `createApp` "requires a host and will throw on boot +without one". The chain client has no direct-WebSocket fallback. An app on this +stack is not a website that talks to chains; it is a guest process inside the +Polkadot app. + +**Writing is gated by personhood.** Statement Store is a custom RPC on People +chain nodes, 512 bytes per statement, 1 KiB per account, ~48h retention, and an +account "MUST have a Statement Store allowance to write - granted via +Individuality runtime registration". Publishing is a privilege attached to an +attested person. + +**The stack keeps its own centralized pieces.** `identity-backend` is "a +centralized HTTP service handling device attestation, username allocation, and +JWT sessions". Calls get "temporary TURN credentials" from platform +infrastructure. This is not hypocrisy; it is an honest admission that some roles +have no decentralized implementation yet. Dotify is entitled to the same honesty. + +## 2. Where Dotify Already Aligns + +More than the roadmap assumed. + +**Contracts are already in the right execution environment.** Dotify's Solidity +contracts are deployed through Asset Hub's `eth-rpc`, which is a compatibility +layer over `pallet-revive` - the exact pallet the stack specifies. Dotify is not +on a neighbouring chain; it is on the same VM, reached through a different +toolchain. Verified: identical ArtistDirectory bytecode from both the DevNet and +Hub TestNet endpoints. + +**App delivery is on-stack.** Bulletin chunked upload, DotNS binding to +`dotify-test01.dot`, `dev-dot.li` gateway. Delivered. + +**Identity is on-stack.** App-scoped Product account, SS58 plus derived H160, +connected only on explicit user action. + +**Content addressing matches.** Dotify already treats audio as immutable CIDs. +Bulletin is the same idea with a different authorizer. + +**Encryption already assumes ungated reads.** Bulletin "reading never needs" +authorization - it gates storing, not retrieval. Dotify's DAV2 encryption is +therefore not redundant with a move to Bulletin; it is the *precondition* for +one. Protected audio on a public content-addressed store must be encrypted, and +Dotify already does that. + +## 3. Where Dotify Diverges + +| Concern | Dotify today | Stack model | Real gap? | +| --- | --- | --- | --- | +| Contract toolchain | Solidity, Hardhat, viem, hand-built manifest | PolkaVM, CDM, `@org/name` resolution | Yes - composability and discoverability | +| Personhood | Dev-operated registrar, unused | Personhood precompile, contextual alias | Yes - and the stack's answer is better | +| Payments | `payForAccess` in native token | CASH via Coinage, host payment APIs | Yes - wrong asset, wrong surface | +| Catalog metadata | Fly read model over EVM logs | Bulletin CIDs + CDM resolution | Partly - a cache is legitimate | +| Audio storage | Pinata / IPFS pinning | Bulletin | Contested - see §6 | +| Room signaling | Socket.IO on Fly | People statement store | **Blocked** - see §4 | +| Content-key custody | Fly, `CONTENT_KEY_MASTER_SECRET` | No equivalent | **No stack answer exists** | + +## 4. The Constraint That Shapes Everything + +Dotify's first product invariant: + +> A room guest can join from a link without a wallet, signature, or payment. + +The stack's messaging model is the opposite by construction: + +- statements require an allowance, granted by Individuality registration; +- official calls are **1:1**, and "voice and video calls are a mobile-only + feature today"; +- signaling rides the People statement store, 512 B per statement, 1 KiB per + account. + +A WebRTC offer is roughly 1.5-4 KB. That is 3-8x the per-statement ceiling, and +the per-account ceiling is 1 KiB - so a peer cannot hold even one SDP in the +store. Chunking does not rescue it; the budget is the wall, not the chunk size. + +And the arithmetic is the *lesser* problem. The greater one is that a guest must +publish an answer to complete a handshake, which requires an attested identity. + +**Moving Dotify's rooms onto the official messaging layer would convert every +listener into a registered, attested person.** That does not degrade the +product; it deletes it. The gesture Dotify exists to protect - "someone lets +another person listen with them" - becomes an onboarding funnel. + +This is where the word *convivial* earns its keep. A convivial tool, in Illich's +sense, is one people can use without first submitting to an institution. A +listening room that demands attestation at the door is a well-engineered +enclosure. The north star is explicit that Web3 here is "invisible trust", not +decoration - and an identity checkpoint is the most visible decoration there is. + +So: **the anonymous guest is not a legacy compromise to be migrated away. It is +the design constraint the architecture must be built around.** + +## 5. Proposed Architecture: Three Rings + +Organise every component by how much trust it requires, and shrink the inner +rings rather than pretending they are empty. + +```text +Ring 1 On-stack, no compromise + contracts (CDM) · personhood (precompile) · payments (CASH) + app delivery (Bulletin/DotNS/Browse) · catalog metadata (Bulletin) + room discovery + presence (statement store) + +Ring 2 Minimal necessary infrastructure + stateless SDP rendezvous · TURN relay + +Ring 3 The stated exception + content-key custody +``` + +The rule: a component may only sit in an outer ring if no inner-ring mechanism +can hold it, and the reason is written down. + +### Ring 1 - move these, they are strictly better on-stack + +**Contracts into CDM.** Register the runtime family as `@dotify/*`. The +generated manifest already exists; CDM registration adds name-based resolution, +Bulletin-hosted ABIs, and composability - another product can resolve +`@dotify/artist-runtime` and read a catalog without asking Dotify. First-writer +-owns makes the name a durable asset. Solidity stays; the target is already +PolkaVM. + +**Personhood onto the precompile.** Replace the dev registrar. The runtime's +`requiredPersonhood` reads the precompile directly, receiving "a +per-application, privacy-preserving pseudonym: the same person yields a +different alias in a different context". This is the single strongest alignment +available: Dotify's `human-free` access mode becomes real, private, and +unlinkable across apps, and the registrar disappears. It also retires the +project's weakest claim. + +**Payments to CASH.** Users see CASH as their balance; PAS is a fee token they +should not think about. Charging in PAS is a category error on this stack. The +runtime stays the authority on entitlement; settlement moves to host payment +APIs. + +*Open problem, stated plainly:* CASH lives on People chain, the runtime lives on +Asset Hub. Cross-chain settlement is unsolved here. Two candidate shapes - a +host-signed payment receipt the runtime verifies, or an Asset-Hub-side +entitlement credited from an attested People-chain transfer. Both need design +work. Do not ship a payment path until this is settled. + +**Catalog metadata to Bulletin.** Release metadata, artwork, and manifests are +small, immutable, and public. Exactly Bulletin's shape. The Fly read model +becomes a cache with a provable source, not the source. + +**Room discovery and presence to the statement store.** A `{room, host, +listeners, ts}` record is ~100 B, well inside 512 B, and `ChannelStore`'s +last-write-wins is the right primitive. The *host* is identified and can hold an +allowance, so this works without touching the guest. Rooms become discoverable +without Dotify's servers - a genuine decentralization win that costs the product +nothing. + +### Ring 2 - shrink, do not eliminate + +The current signaling service does rooms state, presence, chat, reactions, +requests, and SDP relay. Most of that moves to Ring 1. What is left: + +**A stateless SDP rendezvous.** No room registry, no chat, no persistence - +short-TTL mailboxes keyed by room code, so an anonymous guest can hand its +answer to a host. This is the irreducible remainder of "let a stranger connect +without an account". + +**TURN**, for peers behind symmetric NAT. + +The stack does the same thing for its own calls: platform-issued TURN +credentials, because NAT traversal has no on-chain answer. Ring 2 is not +Dotify's deviation from the stack; it is the same concession the stack makes, +kept as small as the product allows. + +*Open question worth asking the Foundation:* can third-party products obtain +TURN credentials from platform infrastructure? If yes, Ring 2 halves. + +### Ring 3 - name the exception + +Content-key custody cannot move. Protected audio must be encrypted at rest on a +publicly readable store, the key must be released only after a server-side +access check, and the stack offers no confidential compute to run that check. +Putting the key in the client defeats the encryption; putting it on-chain +publishes it. + +The honest position is to say so, and to reduce the blast radius rather than +claim it away: + +- **Per-artist custody** - an artist's runtime designates its keyholder, so + Dotify is not one master secret for the whole commons. This follows directly + from artist sovereignty: an artist who controls catalog, access, and rights + should control the key too. +- **Threshold shares**, so no single operator can unilaterally release. +- **Narrow the window** - keys scoped per track, per session, short-lived. + +Ranked by fit with the north star, per-artist custody is the strongest: it turns +the platform's most centralized component into an expression of the project's +central political claim. + +## 6. What I Would Not Do + +**Do not move audio to Bulletin yet.** Bulletin authorization is "a bounded +quota with an expiry, not a permanent grant", and the docs give no size limits +or retention guarantee for MB-scale media. A growing catalog would need +perpetual re-authorization, and an expired quota on a music library is a dead +catalog. Move metadata now; move audio when quota economics for large media and +indefinite retention are demonstrated. Revisit, do not assume. + +**Do not adopt the official calls layer.** 1:1 and mobile-only cannot serve one +host with many listeners. + +**Do not rewrite the contracts to ink!.** They already run on the target VM. +Rewriting spends the project's scarcest resource on zero user-visible gain. + +**Do not delete the Fly API to look decentralized.** It would move key custody +into the browser - strictly worse for artists and listeners, and dishonest about +where trust sits. The stack runs a centralized identity backend for the same +class of reason. + +## 7. Sequence + +Ordered by value per unit of risk: + +1. **Personhood precompile** - retires the weakest claim, unlocks `human-free`, + no user-facing regression. Highest value, self-contained. +2. **CDM registration of `@dotify/*`** - claims the names, makes the catalog + composable. Manifest work already done. +3. **Presence and discovery to the statement store** - real decentralization, + guest path untouched. +4. **Catalog metadata to Bulletin** - Fly read model demoted to cache. +5. **Shrink signaling to a rendezvous** - only after 3 lands. +6. **Per-artist key custody** - the deepest change; do it when the runtime work + above has settled. +7. **CASH settlement** - last, and only after the cross-chain design is proven. + +Steps 1-4 are additive and independently shippable. Nothing before step 5 +touches the walletless guest path. + +## 8. Honest Summary + +Dotify is closer to the official stack than the roadmap assumed - same VM, same +delivery path, same content addressing, and an encryption model that Bulletin +would require anyway. The genuine gaps are personhood, contract registration, +payments, and metadata storage, and all four are improvements Dotify should +want. + +One gap will not close: the stack's messaging assumes attested participants, and +Dotify's rooms assume strangers. That is not a defect on either side. It is two +products with different social contracts. Dotify should adopt the stack +everywhere it fits, and keep the smallest possible amount of infrastructure to +protect the one promise the stack cannot make - that you can send someone a +link, and they can just listen. + +Build infrastructure for relation, not a casino wearing headphones, and not a +turnstile either. diff --git a/docs/design/dotify-v2-access-and-streaming.md b/docs/design/dotify-v2-access-and-streaming.md index 19bb0ab..f794472 100644 --- a/docs/design/dotify-v2-access-and-streaming.md +++ b/docs/design/dotify-v2-access-and-streaming.md @@ -390,7 +390,7 @@ Dotify mapping: | Product SDK / Host API | Replace bespoke chain, signing, storage, and permission glue only where the SDK gives equivalent or better behavior. | | Proof of Personhood | Replace the current admin/personhood mock with the live verified-human source for `human-free`. | | Coinage | Candidate future payment rail for paid access; EVM runtime remains the settlement record until Coinage design is explicit. | -| DotNS | Keep `dotify.dot.li` / `.dot` resolution aligned with the Bulletin single-file build. | +| DotNS | Publish the Product profile as `dotify-test01.dot` / `https://dotify-test01.dev-dot.li`; keep the legacy single-file path separate. | | Bulletin Chain | Continue as a publication and availability layer for product bundles and manifests. | | Statement Store | Future presence/chat/room-discovery layer; Socket.IO remains SDP/ICE relay until a separate migration is designed. | diff --git a/docs/explanation/architecture-overview.md b/docs/explanation/architecture-overview.md index 13d95e2..d3439a1 100644 --- a/docs/explanation/architecture-overview.md +++ b/docs/explanation/architecture-overview.md @@ -74,7 +74,7 @@ Track selected → access checked │ ├── Has access? ──► Content key requested, full audio decrypted and played │ - └── No access? ──► 42 % preview played, access gate shown + └── No access? ──► Unlock/personhood gate shown, no protected audio │ ├── Pay DOT → musicRoyPayAccess() → access granted └── Prove PoP → registrar confirms personhood → access granted @@ -102,6 +102,11 @@ src/ │ ├── useSession.ts # WebRTC + Socket.IO room management │ ├── useArtistConsole.ts # /artists registration, releases, royalties │ └── useWallet.ts # Wallet tiers: passkey → EIP-6963 extension +├── features/runtime/ +│ ├── runtimePorts.ts # RuntimeReadPort / RuntimeWritePort contracts +│ ├── viemRuntimeAdapter.ts # Current EVM implementation behind the ports +│ └── productCdmRuntimeAdapter.ts +│ # Experimental Product CDM/PAPI adapter ├── views/ # One file per screen / tab │ ├── ListenView.tsx │ ├── PlayerView.tsx @@ -174,4 +179,7 @@ The signaling server is a lightweight Socket.IO process (`server/signaling.mjs`) - Your payments — they go directly to your EVM address via smart contract. - Your track records — they live on Paseo Asset Hub (and optionally Bulletin Chain). -The frontend is itself distributed via IPFS/DotNS at `dotify.dot.li`. +The standalone frontend is deployed through Netlify. The Product DevNet build +is publishable through Bulletin/DotNS as `dotify-test01.dot` and resolves +publicly at `https://dotify-test01.dev-dot.li`; the older `dotify.dot.li` +artifact remains legacy deployment evidence. diff --git a/docs/explanation/product-devnet-architecture.md b/docs/explanation/product-devnet-architecture.md new file mode 100644 index 0000000..516c40a --- /dev/null +++ b/docs/explanation/product-devnet-architecture.md @@ -0,0 +1,346 @@ +# Product DevNet Architecture + +## Decision + +Dotify uses an adaptive dual-host architecture: + +- the standalone Netlify app remains a complete public entry point; +- the Product DevNet build publishes the same listener and room experience as + `dotify-test01.dot`; +- Product-host capabilities are added through explicit adapters; +- a missing or denied host capability never enables a demo secret, hidden + signer, or weaker access path. + +This keeps Dotify's north star intact. A guest can still follow a room link and +hear a host without first adopting wallet infrastructure. An artist's access +policy and protected source remain authoritative regardless of which frontend +host serves the app. + +## Why The Host Is An Adapter + +The Product environment and Dotify's existing runtime do not expose the same +signing contract. + +The Product SDK returns an app-scoped account and a PAPI `PolkadotSigner`. +Dotify's deployed contract writes and content-key requests currently use viem, +EIP-1193, and EIP-191. Treating those signers as interchangeable would either +fail at runtime or create an unverifiable access claim. + +The first Product adaptation therefore uses the host account for: + +- an explicit, user-initiated Product account connection; +- an SS58 account for display and future Product-native adapters; +- a derived H160 address for local room-name persistence and read-only + runtime/catalog correlation. + +It does not use that account for: + +- Classic payments; +- artist runtime creation or release publication; +- protected content-key requests; +- Bulletin artist publication through the existing PAPI v1 integration. + +Those actions continue to require the existing passkey or EVM wallet until the +chain and backend adapters described below are delivered. + +## Runtime Topology + +```text +Standalone browser Product host +https://muzinga.netlify.app https://dotify-test01.dev-dot.li + | | + +---------------+----------------------+ + | + same Dotify frontend + | + +-----------+-----------+ + | | + dotify-api.fly.dev dotify-signal.fly.dev + catalog, uploads, room discovery, SDP/ICE, + access, content keys chat and presence + | | + +-----------+-----------+ + | + Product DevNet Asset Hub + existing Dotify runtimes +``` + +The Product build is a normal relative-path Vite bundle. `pad` publishes its +files to Bulletin and binds the result to DotNS. Keeping multiple static chunks +allows incremental uploads; the older single-file Bulletin build remains +available for its original workflow. + +The two frontend origins share the same Fly services. `API_ORIGINS` and +`SIGNAL_ORIGINS` are explicit comma-separated allowlists. This is required for +cross-origin catalog reads, key requests, Socket.IO, and WebRTC signaling. + +## Capability Matrix + +| Capability | Standalone | Product build now | Product-native target | +| --- | --- | --- | --- | +| Browse catalog | Fly cache + EVM RPC | Same | Host-routed read adapter where it improves reliability | +| Play Free track | No wallet | No wallet | Same | +| Join room link | No wallet | No wallet | Same | +| Host room | Socket.IO + WebRTC | Same | Keep until a multiparty replacement proves equivalent UX | +| Product identity | Not applicable | App-scoped SS58/H160 | Host identity with explicit capability grants | +| Classic payment | Passkey/EVM wallet | Passkey/EVM wallet | CDM/PAPI write adapter | +| Protected key request | EIP-191 or session token | `product-sr25519-v1` when a Product account is connected; EIP-191 or session token otherwise | Frontend-host signed Product key/session requests, with captured host signing evidence | +| Artist publication | viem/EVM | viem/EVM | Generated CDM contract adapter | +| Personhood | Current on-chain policy source | No new claim | Privacy-preserving Product proof after verification | +| Static delivery | Netlify | Bulletin + DotNS | Bulletin + DotNS | + +## Rooms Stay Host-Neutral + +Rooms are a product primitive, not a deployment detail. The current signaling +service supports anonymous discovery, one host with multiple listeners, +short-lived chat/reactions/requests, and WebRTC negotiation. Product messaging +and Statement Store do not currently provide a verified drop-in replacement +for that wallet-free multiparty flow. + +The Product build therefore keeps the Socket.IO/WebRTC room layer. It adds one +important boundary: `VITE_PUBLIC_APP_URL` makes every copied room link point to +the public `.dev-dot.li` origin rather than an internal container or content +gateway URL. + +A future Product-native presence spike may mirror a compact host-signed +heartbeat into Statement Store. It must not carry SDP, ICE candidates, audio, +durable chat, or source keys, and it must remain optional for guests. + +## Storage Boundaries + +Product static hosting replaces the web server for the Product build. It does +not replace: + +- Pinata-backed artist uploads; +- DAV2 audio encryption; +- backend-held `CONTENT_KEY_MASTER_SECRET`; +- server-side access verification; +- the durable catalog snapshot. + +Product cloud storage is host-scoped and experimental. Moving encrypted media +or key custody there requires a separate threat model, Range/startup evidence, +and a recovery plan. Until then, Fly remains the security boundary and IPFS +gateways remain the delivery boundary. + +## Runtime Port + +The contract integration is split into two typed ports rather than Product +conditionals throughout feature hooks: + +```text +RuntimeReadPort + resolveArtistRuntime() + listArtistRuntimes() + listRuntimeTracks() + canAccess() + hasPaid() + listRoyaltyPaymentLogs() + +RuntimeWritePort + createRuntime() + installRuntimeStep() + registerTrack() + setAccessMode() + setReleaseActive() + payForAccess() +``` + +Adapters: + +- `ViemRuntimeAdapter`: current standalone EVM implementation behind the typed + ports; +- `ProductCdmRuntimeAdapter`: CDM/PAPI implementation behind the same ports, + now backed by a real contract resolver (`productCdmContracts.ts`) over a + generated snapshot manifest. It remains opt-in behind + `VITE_DOTIFY_RUNTIME_ADAPTER=product-cdm` until host transaction evidence + exists; +- `CatalogApiAdapter`: the existing server-side read model, shared by both + frontends. + +The CDM adapter has one deliberate gap: royalty payment history is not read +through Product contract handles because the current SDK surface exposes +method queries and transactions, not the viem-style historical log query used +by the artist console. Product mode must use the backend catalog/read-model +indexer, or a future Product event/indexer API, for that history. + +### The CDM Manifest Is Generated, Not Installed + +Dotify has no CDM-registered packages, and `cdm install` is not available. It +also does not need them. Dotify's Solidity contracts are deployed through Asset +Hub's `eth-rpc`, which is a compatibility layer over `pallet-revive` - the same +pallet the Product SDK contract helpers target. The deployed H160 addresses are +therefore already reachable through `@parity/product-sdk-contracts` with no +PolkaVM recompilation and no registry entry. + +`CdmJsonContract` needs only `version`, `address`, and `abi` for +`getContract()`, and `new ContractManager(...)` is documented as snapshot-only. +`web/scripts/generate-cdm-manifest.mjs` emits exactly that snapshot from the +same Hardhat artifacts the viem bindings come from, so the two adapters cannot +disagree about an ABI: + +| Output | Contents | +| --- | --- | +| `cdm.json` | `@dotify/artist-directory` and `@dotify/artist-runtime-factory` with their `deployments.json` addresses | +| `smartRuntime.ts` | merged artist-runtime diamond facet ABI, bound to a per-artist address at call time | +| `cdm.d.ts` | `Contracts` module augmentation for typed `getContract()` handles | + +Artist runtimes are deliberately absent from the manifest: a diamond is +deployed per artist, so its address is known at call time, not build time. +Inventing a placeholder address would misrepresent the deployment. +`productCdmContracts.ts` resolves those through `createContract`, which needs no +manifest entry. + +### Two Constraints On Product Contract Mode + +**It only runs inside a Product host.** `createChainClient`/`getChainAPI` route +exclusively through the host provider and throw when none is present - there is +no direct-WebSocket fallback. Product CDM mode is therefore impossible in the +standalone build, and `validateProductionEnvironment` rejects +`VITE_DOTIFY_RUNTIME_ADAPTER=product-cdm` unless `VITE_DOTIFY_HOST_MODE` is +enabled. + +**The host decides which chain an environment resolves to**, and only one +environment is correct. See "DevNet Is Not A Separate Chain" below. +`verifyDeployment()` queries `artistCount` on the directory before any catalog +read, so a wrong-chain connection fails closed with a named error instead of +looking like a catalog of artists with no releases. + +### DevNet Is Not A Separate Chain + +Product DevNet is a *preset*, not a network. It targets the Paseo system +parachains - Asset Hub (1000), People (1004), Bulletin (1010) - with EVM chain +id `420420417` and the `dev-dot.li` web gateway. + +That is the chain Dotify is already deployed on. Verified read-only on +2026-07-29 by querying both endpoints for the ArtistDirectory at +`0xcf1534c6e2b0e43b9436c1e86a076466dc0f2108`: + +| Endpoint | `eth_chainId` | Block | Directory bytecode | +| --- | --- | --- | --- | +| `https://eth-rpc-testnet.polkadot.io/` | `0x190f1b41` | 11546347 | 3660 chars, sha256 `36707b24…` | +| `https://paseo-assethub-rpc.laissez-faire.trade` | `0x190f1b41` | 11546348 | 3660 chars, sha256 `36707b24…` | + +Same chain id, blocks one apart, byte-identical contract code. The two URLs are +different providers for one chain. + +**No contract redeploy is required to port Dotify to Product DevNet.** The +addresses in `deployments.json` are already DevNet addresses. + +The trap is the SDK's `paseo` preset, which points at the Paseo **Next** v2 +deployment (Asset Hub Next 1500 / People Next 1502). The Product documentation +is explicit that those "belong to a different network" and that "funds sent +there will not appear on this Devnet". Dotify has no deployment there, so +`ProductChainEnvironment` admits only `devnet` - a wrong preset is not a +configuration option, it is a bug. + +**Selection is build-time, and reads only.** `VITE_DOTIFY_RUNTIME_ADAPTER` is +inlined by Vite, so a `viem` build tree-shakes the entire Product contract graph +away - 4.4 MB output versus 10 MB when opted in. The difference is +`@parity/product-sdk-descriptors`, whose shared descriptors module references +every chain's metadata; only one chunk is ever fetched, but all are published, +and Bulletin storage is a finite quota. Contract *writes* stay on the viem +signer path in every mode, since routing a payment or a publication through an +unproven signer is not a reasonable default. + +The remaining gate for Product contract *writes* is now narrow: `pallet-revive` +account mapping for the signing account, and real host-signed transaction smoke +evidence from inside the container. The chain question is settled, the manifest +and types exist, and reads are wired. Until that write evidence exists, +`VITE_DOTIFY_RUNTIME_ADAPTER` defaults to `viem`. + +The backend authentication protocol now has an explicit signature scheme field. +Standalone clients use the default `eip191` scheme. Product-host clients can +use `product-sr25519-v1` after signing the same canonical Dotify message bytes +with the app-scoped Product account; the server binds the signature to the +Product public key, derived H160 requester, chain, nonce, purpose, and expiry +before consuming the nonce or running access checks. Unknown schemes fail +closed. The Product frontend now sends this proof shape after explicit +Product-host account connection; real Host smoke evidence is still required +for each Product publication before gated listening is treated as +production-ready. + +### Host Signing Envelope + +The SDK does not pin the `signRaw` wire format. `HostSignPayloadResponse` +carries an untagged signature, and a Substrate host may sign a raw payload +verbatim or inside the conventional `...` envelope. Guessing one +shape would make every Product key request fail on a wrong guess, and the +failure would be indistinguishable from a wrong signer. + +Verification therefore accepts a bounded set: + +- the canonical message verbatim, or wrapped in ``; +- a bare 64-byte sr25519 signature, or a 65-byte value carrying the + MultiSignature sr25519 tag `0x01`. + +This is not a weakening. Every accepted variant carries the identical +domain-bound message, so no new replay, cross-app, cross-chain, or cross-track +surface is created; an ed25519 or ECDSA tag is still rejected. A request whose +key parses and derives to the requester but verifies under no variant returns +`PRODUCT_SIGNATURE_REJECTED`, kept distinct from `SIGNATURE_INVALID` so +operators can separate an envelope problem from a wrong-account problem. + +`product-sr25519-v1` additionally rejects EVM-derived account ids - a 20-byte +H160 padded with `0xee`. Such a value derives straight back to the H160 it +contains, so accepting it would let a caller name any paying EVM listener as +the requester and rest the whole boundary on the curve check alone. A real +Product account is a native `AccountId32`, so that shape is refused outright. + +Once live host evidence records which envelope the host actually produces, the +accepted set can be narrowed to it. + +This avoids a second frontend business model and allows Product mode to replace +one infrastructure adapter at a time. + +## Permission And Failure Rules + +1. Host detection may run on startup; account access only runs after the user + chooses **Use Polkadot app**. +2. The integration does not request a username, identity proof, transaction + permission, or personhood proof before value is visible. +3. If the host is absent, catalog browsing, Free playback, and room links still + work. The wallet modal explains why the Product account is unavailable. +4. A Product account without an EVM signing adapter can request protected keys + only through `product-sr25519-v1`; contract writes still require a + passkey/EVM signer until Product CDM transaction evidence lands. +5. A denied key, RPC failure, or unsupported signature never falls back to a + browser content secret. +6. The Product SDK and deploy tooling are prototype/reference dependencies. + Version changes require the compatibility checks below. + +## Compatibility Gate + +The initial baseline is: + +| Component | Pinned/target value | +| --- | --- | +| Node | 22 | +| `@parity/product-sdk` | `0.19.1` | +| `@polkadot-community-foundation/polkadot-app-deploy` | `0.13.1` in the deploy command | +| Product network | `devnet` | +| Product domain | `dotify-test01.dot` | +| Public gateway | `https://dotify-test01.dev-dot.li` | +| Asset Hub EVM chain ID | `420420417` | + +For every SDK or deploy-tool upgrade: + +1. verify host detection outside and inside the container; +2. connect the Product account only on explicit action; +3. verify SS58 and derived H160 stability; +4. run normal and Product builds; +5. join one room across Netlify and Product origins; +6. verify Free playback remains walletless; +7. verify protected actions still fail closed without a supported signer; +8. inspect the static bundle and npm audit delta; +9. update this document, the environment reference, and the deployment runbook. + +## Source References + +- [Product documentation](https://docs.polkadotcommunity.foundation/) +- [Build and publish guide](https://docs.polkadotcommunity.foundation/guides/build-and-publish/) +- [Deploy and register contracts with CDM](https://docs.polkadotcommunity.foundation/guides/deploy-contracts-cdm/) +- [Smart contracts and CDM](https://docs.polkadotcommunity.foundation/architecture/contracts/) +- [Platform Services SDK guide](https://docs.polkadotcommunity.foundation/guides/platform-services-sdk/) +- [Product network reference](https://docs.polkadotcommunity.foundation/reference/networks/) +- [Product identity architecture](https://docs.polkadotcommunity.foundation/architecture/identity/) +- [Product messaging architecture](https://docs.polkadotcommunity.foundation/architecture/messaging/) diff --git a/docs/index.html b/docs/index.html index 3b9ae43..aeeade6 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1537,11 +1537,16 @@

Operate the spine and validate first sound

  • -

    Sequenced later

    -

    Product SDK, personhood, and cultural transmission

    +

    Adaptive Product path

    +

    Product DevNet now, sovereignty adapters next

    - Product SDK mode, Humanity/Individuality proofs, consented provenance, and ambassador - mechanics remain future work until the current APIs and privacy boundaries are proven. + Dotify now has a publishable dotify-test01.dot build, explicit app-scoped Product + identity, public room links that preserve wallet-free entry, typed runtime ports around the + current viem implementation, an experimental CDM/PAPI adapter boundary, and API-side Product + sr25519 verification with frontend Product proof submission for protected key/session requests. + Product contract writes, real Host smoke evidence for gated playback, Humanity/Individuality + proofs, consented provenance, and ambassador mechanics remain sequenced behind verified security + and privacy boundaries.

  • @@ -1601,11 +1606,20 @@

    Explore the project

    +
  • + + + Product DevNet architecture + What the Product host adapts now, what remains on Fly, and how runtime ports prepare CDM/PAPI. + + + +
  • Product SDK roadmap - How Dotify aligns with Product SDK, Playground, Statement Store, and Humanity. + The delivered Product baseline and the remaining CDM, signature, presence, and Humanity work. diff --git a/docs/operations/deployment-configuration.md b/docs/operations/deployment-configuration.md index a6c239f..f4d7973 100644 --- a/docs/operations/deployment-configuration.md +++ b/docs/operations/deployment-configuration.md @@ -1,7 +1,7 @@ # Deployment Configuration Runbook This runbook is the operator checklist for Dotify's hosted configuration across -Netlify and Fly.io. Use it when changing dashboard values, deploy contexts, +Netlify, Product DevNet, and Fly.io. Use it when changing dashboard values, deploy contexts, `*.toml` settings, hosted origins, secrets, catalog persistence, or production smoke settings. @@ -30,16 +30,18 @@ Keep this document aligned with | Surface | Host | App/project | Source config | Purpose | | --- | --- | --- | --- | --- | | Frontend | Netlify | `muzinga` | `netlify.toml` | Static Vite web app | +| Product frontend | Bulletin + DotNS | `dotify-test01.dot` | `web/.env.product-devnet`, `web/polkadot-app-deploy.config.ts` | Product-host static app | | Backend API | Fly.io | `dotify-api` | `services/api/fly.toml` | Uploads, key delivery, catalog read model, health | | Signaling | Fly.io | `dotify-signal` | `web/fly.signal.toml` | Socket.IO room discovery and WebRTC signaling | Production URLs currently assumed by the app and docs: ```txt -Frontend: https:// +Standalone: https://muzinga.netlify.app +Product: https://dotify-test01.dev-dot.li Backend API: https://dotify-api.fly.dev Signaling: https://dotify-signal.fly.dev -IPFS gateway: https://paseo-ipfs.polkadot.io +Product IPFS: https://devnet-ipfs.api.polkadotcommunity.foundation Asset Hub RPC: https://eth-rpc-testnet.polkadot.io/ ``` @@ -94,6 +96,7 @@ Required production variables: | Key | Value | Notes | | --- | --- | --- | | `VITE_DOTIFY_DEPLOYMENT` | `production` | Enables fail-closed production env validation. | +| `VITE_DOTIFY_HOST_MODE` | `off` | Prevents the standalone build from probing Product host APIs. | | `VITE_SIGNAL_URL` | `https://dotify-signal.fly.dev` | Public Socket.IO signaling origin. | | `VITE_DOTIFY_API_URL` | `https://dotify-api.fly.dev` | Backend API for uploads, key delivery, and cached catalog reads. | | `VITE_PINATA_GATEWAY` | `https://paseo-ipfs.polkadot.io` | Primary browser read gateway. | @@ -115,10 +118,42 @@ Optional production variables: Deploy-preview note: Netlify deploy previews usually have their own origin. The signaling service -can allow multiple origins with `SIGNAL_ORIGINS`, but the backend API currently -accepts one `API_ORIGIN`. For PR evidence, use a stable frontend origin, a -dedicated staging site, or temporarily set `API_ORIGIN` to the deploy-preview -origin and restore it after validation. +and backend both allow multiple exact origins with `SIGNAL_ORIGINS` and +`API_ORIGINS`. Add only the specific preview origin needed for evidence, then +remove it after validation. Never use `*` on the backend. + +## Product DevNet Frontend + +The browser-safe Product build profile is tracked in +`web/.env.product-devnet`. The manifest is +`web/polkadot-app-deploy.config.ts`. + +Required Product values: + +| Key | Current value | +| --- | --- | +| `VITE_DOTIFY_DEPLOYMENT` | `production` | +| `VITE_DOTIFY_HOST_MODE` | `required` | +| `VITE_DOTIFY_PRODUCT_ID` | `dotify-test01.dot` | +| `VITE_PUBLIC_APP_URL` | `https://dotify-test01.dev-dot.li` | +| `VITE_DOTIFY_API_URL` | `https://dotify-api.fly.dev` | +| `VITE_SIGNAL_URL` | `https://dotify-signal.fly.dev` | + +`VITE_PINATA_JWT` and `VITE_CONTENT_SECRET` are explicitly empty in that +profile so a developer's generic local `.env` cannot leak demo credentials +into the Product bundle. + +Build and publication: + +```bash +cd web +npm run build:product-devnet +npm run deploy:product-devnet +``` + +Use +[`docs/operations/product-devnet-deployment.md`](product-devnet-deployment.md) +for authentication, publication, validation, and rollback. ## Fly Backend API @@ -136,6 +171,7 @@ Non-secret runtime values are tracked in `services/api/fly.toml`: | --- | --- | | `API_PORT` | `8790` | | `NODE_ENV` | `production` | +| `API_ORIGINS` | `https://muzinga.netlify.app,https://dotify-test01.dev-dot.li` | | `PASEO_ASSET_HUB_RPC` | `https://eth-rpc-testnet.polkadot.io/` | | `DOTIFY_FACTORY_ADDRESS` | `0xbd1a11cfce8b5ef7a37e507bc5109895f8f42a72` | | `DOTIFY_DIRECTORY_ADDRESS` | `0xcf1534c6e2b0e43b9436c1e86a076466dc0f2108` | @@ -145,7 +181,6 @@ Set server-side values in the app's Secrets area: | Secret | Required | Notes | | --- | --- | --- | -| `API_ORIGIN` | Production | Exact frontend origin allowed by API CORS. One URL only. | | `PINATA_JWT` | Uploads | Backend-only Pinata token. Never expose in Netlify. | | `CONTENT_KEY_MASTER_SECRET` | Audio upload and key delivery | 64+ hex chars, at least 32 random bytes. Do not rotate casually. | | `GIT_COMMIT_SHA` | Optional | Set by CI/build automation when available; `/version` can fall back in dev checkouts. | @@ -175,6 +210,25 @@ For production-grade catalog evidence: - keep at least one machine warm while measuring catalog p75 performance, then record whether the trace was warm or cold. +### Backend Signature Schemes + +No Netlify or Fly dashboard variable enables Product signatures. The API +accepts two explicit schemes on session sign-in and protected key requests: + +| Scheme | Client | Required proof fields | Backend binding | +| --- | --- | --- | --- | +| `eip191` | Standalone EVM/passkey wallet path | `signature` | `viem.verifyMessage` against the requester H160 | +| `product-sr25519-v1` | Product-host app-scoped account path | `signature`, `productPublicKey` | sr25519 signature over the canonical Dotify message bytes, then Product public-key-to-H160 derivation matching the requester | + +Unknown schemes fail at the API schema boundary. Product requests must still +pass the same nonce, chain, purpose, expiry, and `musicAccCanAccess` checks as +standalone requests. The Product frontend submits this proof shape only after +an explicit Product-host account connection; contract writes remain on the +standalone EVM/passkey signer path until the Product CDM transaction adapter has +real host-signed transaction evidence. Validate Product protected playback +through host smoke tests after each Product publication before treating Product +identity as production-ready for gated listening. + ## Fly Signaling Open app `dotify-signal`. @@ -188,12 +242,12 @@ Non-secret runtime values are tracked in `web/fly.signal.toml`: | `SIGNAL_ROOM_TTL_MS` | `21600000` | | `SIGNAL_HOST_TIMEOUT_MS` | `120000` | | `SIGNAL_MAX_LISTENERS` | `24` | +| `SIGNAL_ORIGINS` | `https://muzinga.netlify.app,https://dotify-test01.dev-dot.li` | -Set hosted frontend origins in the app's Secrets area: - -| Secret | Value | -| --- | --- | -| `SIGNAL_ORIGINS` | Exact comma-separated frontend origins, for example `https://muzinga.netlify.app,https://` | +The production origins are public configuration tracked in +`web/fly.signal.toml`; they are not secrets. Temporary preview origins may be +set through Fly configuration, but the tracked production allowlist must be +restored after validation. Keep `dotify-signal` on one active machine until a shared Socket.IO adapter is added. Rooms, chat, reactions, request queues, and solo-presence aggregates are @@ -227,9 +281,14 @@ curl -s https://dotify-signal.fly.dev/status cd web npm run smoke:production-env npm run smoke:signal -- --url https://dotify-signal.fly.dev --origin https:// +npm run build:product-devnet ``` -6. For explicit origin rejection evidence, include a denied origin: +6. For a Product release, complete the cross-origin room and host-account +checks in +[`docs/operations/product-devnet-deployment.md`](product-devnet-deployment.md). + +7. For explicit origin rejection evidence, include a denied origin: ```bash cd web diff --git a/docs/operations/product-devnet-deployment.md b/docs/operations/product-devnet-deployment.md new file mode 100644 index 0000000..8e52341 --- /dev/null +++ b/docs/operations/product-devnet-deployment.md @@ -0,0 +1,267 @@ +# Deploy Dotify To Product DevNet + +This runbook publishes the Product build to Bulletin/DotNS and connects it to +the existing Fly API and signaling services. It does not deploy contracts or +change production secrets. + +## Contracts Need No Redeploy + +Product DevNet is a preset over the Paseo system parachains - Asset Hub (1000), +People (1004), Bulletin (1010) - at EVM chain `420420417`. Dotify's contracts +are already deployed on that chain, so porting to DevNet is a configuration +change, not a migration. The addresses in `deployments.json` are DevNet +addresses. + +Confirm before every publish: + +```bash +cd web +npm run smoke:devnet +``` + +It reads `web/.env.product-devnet` and `deployments.json` and checks, read-only, +that the configured Asset Hub reports chain `420420417`, is producing blocks +past the 2026-07 halt, still serves bytecode for the ArtistDirectory and +ArtistRuntimeFactory, and that the Bulletin RPC and IPFS gateway respond. It +sends no transaction and prints no credential. + +Do not point the build at **Asset Hub Next (1500)** or **People Next (1502)**. +The Product documentation is explicit that those belong to a different network; +Dotify has no contracts there, and the catalog would load empty. + +## Prerequisites + +- Node.js 22 and npm 10+ +- a clean build from the intended commit +- access to the `dotify-test01.dot` deployment account +- Fly access for `dotify-api` and `dotify-signal` +- the current `@polkadot-community-foundation/polkadot-app-deploy` DevNet prerequisites + +The CLI is reference/experimental tooling. Do not store a mnemonic in the +repository, shell history, `.env` files, Netlify, or Fly. + +Before the first publish, the signing account also needs: + +- DevNet native tokens on Asset Hub; +- an EVM account mapping (`dotns account map --env devnet`); +- a live Bulletin storage authorization for the same SS58 account; +- ownership of `dotify-test01.dot`, or eligibility to register it during deploy. + +`dotify.dot` currently requires full personhood on Product DevNet. Until the +project has that proof level, use `dotify-test01.dot` and +`https://dotify-test01.dev-dot.li` for operator deployments. + +Bulletin authorization is a finite quota and may expire. A deploy that starts +failing at the upload stage after previously working should recheck that quota. +See the official +[build and publish guide](https://docs.polkadotcommunity.foundation/guides/build-and-publish/) +for the current faucet, storage console, mapping, and DotNS registration steps. + +## 1. Verify The Fly Origin Boundary + +The tracked Fly configuration must contain: + +```txt +API_ORIGINS=https://muzinga.netlify.app,https://dotify-test01.dev-dot.li,polkadot://app.dotify-test01.dot +SIGNAL_ORIGINS=https://muzinga.netlify.app,https://dotify-test01.dev-dot.li,polkadot://app.dotify-test01.dot +``` + +Three frontends reach these services: Netlify, the DotNS web gateway, and the +app as served inside the Product host container, which uses a custom scheme. +Both lists must carry all three - a container with only the signaling origin +gets rooms but no content keys, because catalog and key delivery go to the API. + +`polkadot:` is a non-special scheme, so its origin is opaque and a browser may +send `Origin: null` rather than the literal value. If a host request is still +refused after this change, read the actual `Origin` header from the Fly log +before widening either list. Never add a bare `null`: that admits every +sandboxed iframe and `file://` page on the web to the authenticated upload and +content-key routes. A regression test in `services/api/src/cors.test.ts` pins +that refusal. + +Deploy both services before publishing the frontend. `cd` into each service +first - this is not cosmetic: + +```bash +cd services/api +flyctl deploy + +cd ../../web +flyctl deploy -c fly.signal.toml +``` + +`-c` selects the config file only; it does not set the Docker build context, +which is always the shell's working directory. Running +`flyctl deploy -c services/api/fly.toml` from the repository root fails at +`COPY src ./src`, because the Dockerfile is written against `services/api` as +its context and there is no `src/` at the root. It also uploads a ~1.3 GB +context, since Docker reads `.dockerignore` from the context root and only the +service directories have one. Passing the directory positionally +(`flyctl deploy services/api`) works too, because that sets the context. + +An earlier cached layer can hide the mistake: `COPY package*.json ./` and +`npm ci` may report `CACHED` from a previous correct build, so the failure +surfaces at the first genuinely uncached step rather than the first wrong one. + +Keep backend secrets unchanged. `API_ORIGINS` supersedes singular +`API_ORIGIN`; the latter remains only as a compatibility fallback. + +## 2. Verify The Browser-Safe Build Profile + +Review `web/.env.product-devnet`. It must contain only public endpoints and +identifiers. In particular: + +```txt +VITE_DOTIFY_HOST_MODE=required +VITE_DOTIFY_PRODUCT_ID=dotify-test01.dot +VITE_PUBLIC_APP_URL=https://dotify-test01.dev-dot.li +VITE_DOTIFY_API_URL=https://dotify-api.fly.dev +VITE_SIGNAL_URL=https://dotify-signal.fly.dev +VITE_PINATA_JWT= +VITE_CONTENT_SECRET= +``` + +`VITE_PUBLIC_APP_URL` is the URL copied for room invitations. Do not replace it +with an internal host URL or a raw CID gateway. + +## 3. Build Locally + +```bash +cd web +npm ci +npm run test:unit +npm run smoke:devnet +npm run build:product-devnet +``` + +Expected output is `web/dist-product`. The production guard must fail if a +browser upload token or content secret is present. + +The default build keeps the viem runtime adapter, which tree-shakes the Product +contract graph away and publishes at roughly 4.4 MB. Building with +`VITE_DOTIFY_RUNTIME_ADAPTER=product-cdm` pulls in the Product SDK descriptors +and roughly doubles that. Bulletin storage is a finite quota, so only opt in +when the Product contract path is actually being exercised. + +## 4. Authenticate The Deploy Tool + +The repository pins the CLI version in the npm deploy command but does not add +the experimental deploy tool to the application dependency tree. + +```bash +npx --yes --package @polkadot-community-foundation/polkadot-app-deploy@0.13.1 pad login --env devnet +npx --yes --package @polkadot-community-foundation/polkadot-app-deploy@0.13.1 pad whoami --env devnet +``` + +Follow the mobile-wallet flow. Confirm the selected account owns, or can +receive, `dotify-test01.dot` and satisfies the DevNet registration/funding +rules. + +## 5. Publish + +```bash +npm run deploy:product-devnet +``` + +The command: + +1. rebuilds `dist-product`; +2. validates `polkadot-app-deploy.config.ts`; +3. creates content-addressed chunks with the JavaScript merkle implementation; +4. uploads changed content to Product DevNet Bulletin; +5. binds `dotify-test01.dot`; +6. writes the Product manifest and executable records. + +Publisher listing is deliberately not part of the default deploy. It requires +the current Product proof-of-personhood level and signer support, and the +0.13.1 CLI help still describes environment-specific limitations. After the +app URL is verified, follow the current official **List it in Browse** guide +and record that result separately. A listing failure must not obscure a +successful static deployment. + +Record the commit, CLI version, resulting CID, DotNS transaction references, +and final public URL in the release evidence. + +## 6. Validate + +Check service CORS from both origins: + +```bash +curl -s -D - -o /dev/null \ + -H 'Origin: https://dotify-test01.dev-dot.li' \ + https://dotify-api.fly.dev/health + +curl -s -D - -o /dev/null \ + -H 'Origin: https://muzinga.netlify.app' \ + https://dotify-api.fly.dev/health +``` + +Then verify in the Product host: + +1. `https://dotify-test01.dev-dot.li` opens and shows catalog tracks. +2. Free playback starts without connecting an account. +3. **Use Polkadot app** connects an app-scoped Product account only after the + button is selected. +4. A protected track requests its key through the Product identity using + `product-sr25519-v1`. Record which happened: + - accepted, and playback starts: capture the request/response pair as the + Product signing evidence this build needs; + - denied with `PRODUCT_SIGNATURE_REJECTED`: the key and requester bound + correctly but the host signing envelope is not one this API accepts. + Capture the Fly log line and the raw host signature length before + changing anything; + - denied with any other code: treat as a normal fail-closed denial. + + In every rejected case, playback must stop and offer a passkey/EVM wallet. + No path may release a key without a verified signature. +5. A Product-origin host creates a room and copies a + `https://dotify-test01.dev-dot.li/#/rooms/` link. +6. A wallet-free browser joins that link from outside the Product host. +7. A Netlify-origin host and Product-origin guest also connect. +8. Closing the host ends the room as before. + +Inspect the browser console and Fly logs for CORS, catalog, Socket.IO, and +WebRTC failures. + +## Rollback + +The Product deployment is static. To roll back: + +1. switch to the last known-good commit; +2. run `npm ci`; +3. run the full build and smoke checks; +4. republish with `npm run deploy:product-devnet`; +5. confirm DotNS resolves to the restored content; +6. record the replacement CID and incident reason. + +Do not roll back Fly origin allowlists while either public frontend remains +active. + +## Known Limits + +- Product account signing is accepted by the API only through the explicit + `product-sr25519-v1` session/key-request scheme. The Product UI now submits + that proof shape after an explicit host-account connection, but each published + Product build still needs real Host smoke evidence before gated playback is + considered production-ready on Product DevNet. +- The Host `signRaw` wire format is not pinned by the SDK: the response + signature is untagged, and a Substrate host may sign the payload verbatim or + inside a `` envelope. The API accepts both envelopes and both a bare + 64-byte and a MultiSignature-tagged 65-byte sr25519 signature, so a correct + host signature verifies regardless of which shape it uses. Step 6.4 above + records which shape the live host actually produced - that observation is the + evidence, and until it is captured the accepted set stays deliberately wide. +- Contract writes still require passkey/EVM signing in the shipped UI. The + Product CDM/PAPI runtime adapter now has its generated manifest, contract + types, and a live resolver, so the only thing still missing before it can be + selected is `pallet-revive` account mapping plus real host-signed transaction + evidence. +- Rooms still depend on one in-memory Fly signaling machine. +- Product-host cloud storage does not hold Dotify audio or content keys. +- Product personhood is not yet an access decision source. +- A durable `CATALOG_SNAPSHOT_PATH` remains recommended for production-grade + catalog recovery but is not required for API startup. +- Product contract mode (`VITE_DOTIFY_RUNTIME_ADAPTER=product-cdm`) covers + catalog reads only, and only inside the Product host. Contract writes stay on + the passkey/EVM signer in every mode until `pallet-revive` account mapping and + host-signed transaction evidence exist. diff --git a/docs/product/ux-signature-flows.md b/docs/product/ux-signature-flows.md index 8f6e892..95b0549 100644 --- a/docs/product/ux-signature-flows.md +++ b/docs/product/ux-signature-flows.md @@ -25,6 +25,21 @@ Dotify must avoid wallet pop-up fatigue. Wallet prompts should appear only when | Human Free unlock | Yes | Maybe session signature | No, unless proving/linking personhood requires one | | Artist publishing | Yes | Yes/transaction depending on step | Yes for runtime/register actions | +## Backend signature schemes + +Signed session and protected key requests carry an explicit `signatureScheme`. +If the field is omitted, the backend treats the request as `eip191` for +backward compatibility. + +| Scheme | Signer | Extra fields | Verification | +| --- | --- | --- | --- | +| `eip191` | Standalone EVM/passkey wallet | `signature` | Verify the canonical Dotify message with the requester H160 address. | +| `product-sr25519-v1` | App-scoped Product account | `signature`, `productPublicKey` | Verify sr25519 over the same canonical message bytes, derive H160 from the Product public key, and require it to match the requester. | + +Unknown schemes and Product public-key mismatches fail closed before nonce +consumption. Every successful signature path still runs the runtime access +check before the backend releases a content key. + ## Individual playback flow ```mermaid diff --git a/docs/reference/environment-variables.md b/docs/reference/environment-variables.md index e255b55..61123c7 100644 --- a/docs/reference/environment-variables.md +++ b/docs/reference/environment-variables.md @@ -36,14 +36,129 @@ production build contract without printing real secret values. --- +### `VITE_DOTIFY_HOST_MODE` + +| Property | Value | +| ------------ | ---------------------------- | +| **Type** | `off`, `auto`, or `required` | +| **Required** | Product builds | +| **Default** | `off` | +| **Example** | `required` | + +Controls Product host discovery. `off` keeps the standalone app independent +from the Product SDK. `auto` enables progressive host detection. `required` +marks a Product-targeted build but does not block catalog, Free playback, or +wallet-free room entry when opened outside the host. + +Host detection does not request an account. The account is requested only when +the listener selects **Use Polkadot app**. + +--- + +### `VITE_DOTIFY_RUNTIME_ADAPTER` + +| Property | Value | +| ------------ | ----------------------- | +| **Type** | `viem` or `product-cdm` | +| **Required** | No | +| **Default** | `viem` | +| **Example** | `viem` | + +Selects which adapter backs the runtime contract ports. `viem` is the only path +with production evidence. `product-cdm` routes reads and writes through the +Product SDK contract handles over the generated `cdm.json` snapshot. + +This selects **reads only**. Contract writes stay on the viem signer path in +every mode, because the Product write path has no host-signed transaction +evidence yet. + +Any unrecognised value falls back to `viem`, so a typo cannot silently disable +contract reads. `product-cdm` additionally requires `VITE_DOTIFY_HOST_MODE` to +be `auto` or `required`: the Product chain client connects only through a host +container and has no direct-WebSocket fallback. The production guard rejects +that combination rather than shipping a frontend that cannot read the catalog. + +**Build size.** This flag is read at build time, not runtime. A `viem` build +tree-shakes the entire Product contract graph away; opting in pulls it back in +along with `@parity/product-sdk-descriptors`, whose shared descriptors module +references every chain's metadata. Measured on this branch: + +| Build | Output size | +| --------------------------------------------- | ----------- | +| `VITE_DOTIFY_RUNTIME_ADAPTER` unset or `viem` | 4.4 MB | +| `VITE_DOTIFY_RUNTIME_ADAPTER=product-cdm` | 10 MB | + +Only one metadata chunk is ever fetched at runtime, but all of them are +published. Weigh that against the Bulletin storage quota before enabling this +for a `.dot` deployment. + +--- + +### `VITE_DOTIFY_PRODUCT_CHAIN` + +| Property | Value | +| ------------ | -------- | +| **Type** | `devnet` | +| **Required** | No | +| **Default** | `devnet` | +| **Example** | `devnet` | + +Product chain preset used only when `VITE_DOTIFY_RUNTIME_ADAPTER=product-cdm`. + +`devnet` is the only accepted value, and that is a correctness constraint. +Product DevNet is a preset over the Paseo system parachains - Asset Hub (1000), +People (1004), Bulletin (1010) - at EVM chain `420420417`, which is exactly +where Dotify's contracts are deployed. + +The SDK's `paseo` preset is *not* an alternative: it targets Paseo Next +(Asset Hub Next 1500 / People Next 1502), which the Product documentation calls +a different network. Selecting it would resolve every manifest address to an +account with no code - indistinguishable from artists with no releases. +`verifyDeployment()` turns that into an explicit error at startup, and the +config layer refuses the value outright. + +Regenerate the manifest with `npm run generate:cdm` after any contract +redeploy, or the addresses in `cdm.json` go stale. + +--- + +### `VITE_DOTIFY_PRODUCT_ID` + +| Property | Value | +| ------------ | ---------------------- | +| **Type** | Lowercase `.dot` name | +| **Required** | Host mode is not `off` | +| **Default** | `dotify-test01.dot` | +| **Example** | `dotify-test01.dot` | + +DotNS identifier used by the Product host to derive Dotify's app-scoped +account. Changing it changes the Product account boundary and requires an +identity/access migration review. + +--- + +### `VITE_PUBLIC_APP_URL` + +| Property | Value | +| ------------ | ---------------------------------- | +| **Type** | HTTPS URL | +| **Required** | Product production builds | +| **Default** | Current browser URL | +| **Example** | `https://dotify-test01.dev-dot.li` | + +Canonical public origin used when copying room links. Product builds must set +this so invitations never expose an internal host/container or raw gateway URL. + +--- + ### `VITE_DOTIFY_DEBUG_PANEL` -| Property | Value | -| ------------ | ----------------- | -| **Type** | Boolean string | -| **Required** | No | -| **Default** | `false` | -| **Example** | `true` | +| Property | Value | +| ------------ | -------------- | +| **Type** | Boolean string | +| **Required** | No | +| **Default** | `false` | +| **Example** | `true` | Enables the optional Production readiness panel under the `You` tab. The panel performs read-only checks for the backend readiness endpoint, signaling health, @@ -78,8 +193,10 @@ Production deployments must use a publicly reachable HTTPS endpoint. Backend API base URL. When set, audio, cover, and metadata uploads go through the backend. Full-track playback can request content keys with wallet-signed -requests. When unset, the web app falls back to local/demo browser-side Pinata -upload and `VITE_CONTENT_SECRET` encryption. +requests. The backend accepts the default `eip191` signature scheme and the +Product-host `product-sr25519-v1` scheme without an additional env flag. When +unset, the web app falls back to local/demo browser-side Pinata upload and +`VITE_CONTENT_SECRET` encryption. --- @@ -241,12 +358,12 @@ Network interface to bind. ### `SIGNAL_ORIGINS` -| Property | Value | -| ------------ | --------------------------------------------------- | -| **Type** | Comma-separated URL list or `*` | -| **Required** | No | -| **Default** | `*` | -| **Example** | `https://muzinga.netlify.app,https://dotify.dot.li` | +| Property | Value | +| ------------ | ----------------------------------------------------------------------------------------------- | +| **Type** | Comma-separated URL list or `*` | +| **Required** | No | +| **Default** | `*` | +| **Example** | `https://muzinga.netlify.app,https://dotify-test01.dev-dot.li,polkadot://app.dotify-test01.dot` | CORS allowed origins for Socket.IO and status endpoints. Set explicit frontend origins in production. `SIGNAL_ORIGIN` is still accepted as a backwards-compatible @@ -327,7 +444,23 @@ Port the backend API listens on. | **Required** | Production | | **Default** | `http://localhost:5273` | -Frontend origin allowed by backend CORS. +Singular frontend origin allowed by backend CORS. This remains as a +backwards-compatible fallback when `API_ORIGINS` is not set. + +--- + +### `API_ORIGINS` + +| Property | Value | +| ------------ | ----------------------------------------------------------------------------------------------- | +| **Type** | Comma-separated HTTPS origin list | +| **Required** | Multiple hosted frontends | +| **Default** | The single `API_ORIGIN` value | +| **Example** | `https://muzinga.netlify.app,https://dotify-test01.dev-dot.li,polkadot://app.dotify-test01.dot` | + +Exact frontend origins accepted by backend CORS. When set, it takes precedence +over `API_ORIGIN`. Do not use `*`: the API carries authenticated upload and +content-key routes. --- diff --git a/services/api/.env.example b/services/api/.env.example index b86aba5..bcdf622 100644 --- a/services/api/.env.example +++ b/services/api/.env.example @@ -6,6 +6,8 @@ API_PORT=8790 # Frontend origin allowed by CORS. API_ORIGIN=http://localhost:5273 +# Comma-separated origins take precedence over API_ORIGIN when set. +# API_ORIGINS=https://muzinga.netlify.app,https://dotify-test01.dev-dot.li # Paseo Asset Hub EVM RPC. Required for wallet-signed content-key requests: # the key route resolves the owning artist runtime via the directory and calls @@ -30,9 +32,10 @@ CATALOG_CONFIRMATIONS=2 # Master secret for per-track content-key derivation. Used by BOTH # /api/uploads/audio (server-side AES-256-GCM encryption before pinning) and -# /api/tracks/:contentHash/key-request (key delivery after a wallet-signed, -# on-chain-verified access check). Must be at least 32 random bytes encoded as -# hex. Never expose this value; rotating it re-keys every track at once. +# /api/tracks/:contentHash/key-request (key delivery after an eip191 or +# product-sr25519-v1 signed, on-chain-verified access check). Must be at least +# 32 random bytes encoded as hex. Never expose this value; rotating it re-keys +# every track at once. # Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" CONTENT_KEY_MASTER_SECRET= diff --git a/services/api/fly.toml b/services/api/fly.toml index 6b061eb..baa8017 100644 --- a/services/api/fly.toml +++ b/services/api/fly.toml @@ -10,6 +10,17 @@ primary_region = "ams" # checks target the owner-guarded factory/directory pair. API_PORT = "8790" NODE_ENV = "production" + # Three frontends reach this API: Netlify, the DotNS web gateway, and the app + # as served inside the Polkadot Product host container, which uses a custom + # scheme. Keep this in step with SIGNAL_ORIGINS in web/fly.signal.toml. + # + # `polkadot:` is a non-special scheme, so `new URL(...).origin` is opaque and + # a browser may send `Origin: null` instead of the literal value below. If a + # host request is still refused, read the actual Origin header off the Fly log + # before widening this list - never add a bare `null`, which would admit every + # sandboxed iframe and file:// page on the web to authenticated upload and + # content-key routes. + API_ORIGINS = "https://muzinga.netlify.app,https://dotify-test01.dev-dot.li,polkadot://app.dotify-test01.dot" PASEO_ASSET_HUB_RPC = "https://eth-rpc-testnet.polkadot.io/" DOTIFY_FACTORY_ADDRESS = "0xbd1a11cfce8b5ef7a37e507bc5109895f8f42a72" DOTIFY_DIRECTORY_ADDRESS = "0xcf1534c6e2b0e43b9436c1e86a076466dc0f2108" @@ -20,7 +31,11 @@ primary_region = "ams" force_https = true auto_stop_machines = "stop" auto_start_machines = true - min_machines_running = 0 + # Keep one machine warm. A stopped machine cold-starts on the first content + # key request, and that request sits directly in front of first sound: a + # measured 8.2s to /health cold against 0.11s warm. Scaling to zero saves + # nothing a listener would trade eight silent seconds for. + min_machines_running = 1 processes = ["app"] [[vm]] diff --git a/services/api/package-lock.json b/services/api/package-lock.json index 9a9f4ca..25f9f9b 100644 --- a/services/api/package-lock.json +++ b/services/api/package-lock.json @@ -12,6 +12,7 @@ "@fastify/multipart": "^10.0.0", "@fastify/rate-limit": "^10.3.0", "@noble/hashes": "1.8.0", + "@scure/sr25519": "1.0.0", "fastify": "^5.8.5", "viem": "^2.52.2", "zod": "^3.23.8" @@ -757,6 +758,49 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@scure/sr25519": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@scure/sr25519/-/sr25519-1.0.0.tgz", + "integrity": "sha512-b+uhK5akMINXZP95F3gJGcb5CMKYxf+q55fwMl0GoBwZDbWolmGNi1FrBSwuaZX5AhqS2byHiAueZgtDNpot2A==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~2.0.0", + "@noble/hashes": "~2.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/sr25519/node_modules/@noble/curves": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz", + "integrity": "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "2.0.1" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/sr25519/node_modules/@noble/hashes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", + "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@types/node": { "version": "22.19.19", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.19.tgz", diff --git a/services/api/package.json b/services/api/package.json index 65f0f21..4fa05fc 100644 --- a/services/api/package.json +++ b/services/api/package.json @@ -12,13 +12,14 @@ "start": "node dist/index.js", "catalog:reindex": "tsx src/scripts/reindexCatalog.ts", "typecheck": "tsc --noEmit", - "test": "node --import tsx --test src/services/audioV2.test.ts src/services/replayProtection.test.ts src/services/signatures.test.ts src/services/sessionTokens.test.ts src/services/catalog/readModel.test.ts src/routes/keys.test.ts src/routes/uploads.test.ts src/routes/auth.test.ts src/routes/health.test.ts src/routes/catalog.test.ts src/app.test.ts" + "test": "node --import tsx --test src/services/audioV2.test.ts src/services/replayProtection.test.ts src/services/signatures.test.ts src/services/sessionTokens.test.ts src/services/catalog/readModel.test.ts src/routes/keys.test.ts src/routes/uploads.test.ts src/routes/auth.test.ts src/routes/health.test.ts src/routes/catalog.test.ts src/app.test.ts src/cors.test.ts" }, "dependencies": { "@fastify/cors": "^11.2.0", "@fastify/multipart": "^10.0.0", "@fastify/rate-limit": "^10.3.0", "@noble/hashes": "1.8.0", + "@scure/sr25519": "1.0.0", "fastify": "^5.8.5", "viem": "^2.52.2", "zod": "^3.23.8" diff --git a/services/api/src/app.ts b/services/api/src/app.ts index 89044c4..09a1111 100644 --- a/services/api/src/app.ts +++ b/services/api/src/app.ts @@ -39,6 +39,7 @@ export type BuildAppOptions = { // Tests disable logging; production always logs. logging?: boolean; catalog?: CatalogReadModel; + apiOrigins?: string[]; }; export async function buildApp(options: BuildAppOptions = {}): Promise { @@ -48,9 +49,9 @@ export async function buildApp(options: BuildAppOptions = {}): Promise + typeof value === 'string' + ? value + .split(',') + .map(origin => origin.trim()) + .filter(Boolean) + : value, + z.array(z.string().url()).min(1).optional(), +); + const envSchema = z.object({ API_PORT: z.coerce.number().int().min(1).max(65535).default(8790), API_ORIGIN: z.string().url().default('http://localhost:5273'), + API_ORIGINS: optionalOriginList, PASEO_ASSET_HUB_RPC: z.string().url().optional(), DOTIFY_FACTORY_ADDRESS: optionalNonEmptyString, DOTIFY_DIRECTORY_ADDRESS: optionalNonEmptyString, @@ -65,7 +77,10 @@ function parseEnv() { console.error(`[dotify-api] Invalid environment configuration:\n${issues}`); process.exit(1); } - return result.data; + return { + ...result.data, + API_ORIGINS: result.data.API_ORIGINS ?? [result.data.API_ORIGIN], + }; } export const config = parseEnv(); diff --git a/services/api/src/cors.test.ts b/services/api/src/cors.test.ts new file mode 100644 index 0000000..f61ac66 --- /dev/null +++ b/services/api/src/cors.test.ts @@ -0,0 +1,68 @@ +import assert from 'node:assert/strict'; +import { afterEach, describe, it } from 'node:test'; +import type { FastifyInstance } from 'fastify'; +import { buildApp } from './app.js'; + +let app: FastifyInstance | null = null; + +afterEach(async () => { + if (app) await app.close(); + app = null; +}); + +describe('frontend origin boundary', () => { + it('allows each configured Dotify frontend and rejects unrelated origins', async () => { + app = await buildApp({ + logging: false, + apiOrigins: ['https://muzinga.netlify.app', 'https://dotify-test01.dev-dot.li'], + }); + const server = app; + + for (const origin of ['https://muzinga.netlify.app', 'https://dotify-test01.dev-dot.li']) { + const response = await server.inject({ + method: 'GET', + url: '/health', + headers: { origin }, + }); + assert.equal(response.headers['access-control-allow-origin'], origin); + } + + const unrelated = await server.inject({ + method: 'GET', + url: '/health', + headers: { origin: 'https://unrelated.example' }, + }); + assert.equal(unrelated.headers['access-control-allow-origin'], undefined); + }); + + it('allows the Product host container origin, which uses a custom scheme', async () => { + // Inside the Product host the app is served from polkadot://, not the DotNS + // web gateway. Without this the container gets rooms but no content keys. + const hostOrigin = 'polkadot://app.dotify-test01.dot'; + app = await buildApp({ logging: false, apiOrigins: [hostOrigin] }); + + const response = await app.inject({ + method: 'GET', + url: '/health', + headers: { origin: hostOrigin }, + }); + + assert.equal(response.headers['access-control-allow-origin'], hostOrigin); + }); + + it('refuses a null origin even when a custom-scheme origin is allowed', async () => { + // `polkadot:` is a non-special scheme, so browsers may send `Origin: null`. + // Answering that would admit every sandboxed iframe and file:// page to the + // authenticated upload and content-key routes, so it must stay refused + // until the real header is observed and allowlisted deliberately. + app = await buildApp({ logging: false, apiOrigins: ['polkadot://app.dotify-test01.dot'] }); + + const response = await app.inject({ + method: 'GET', + url: '/health', + headers: { origin: 'null' }, + }); + + assert.equal(response.headers['access-control-allow-origin'], undefined); + }); +}); diff --git a/services/api/src/routes/auth.test.ts b/services/api/src/routes/auth.test.ts index e3490d8..8bf4c13 100644 --- a/services/api/src/routes/auth.test.ts +++ b/services/api/src/routes/auth.test.ts @@ -2,6 +2,7 @@ import assert from 'node:assert/strict'; import { afterEach, describe, it } from 'node:test'; import Fastify, { type FastifyInstance } from 'fastify'; import { createAuthRoutes, type AuthRouteDeps } from './auth.js'; +import { PRODUCT_SR25519_SIGNATURE_SCHEME, type SignInRequest } from '../services/signatures.js'; const ADDRESS = '0x1111111111111111111111111111111111111111'; @@ -80,6 +81,34 @@ describe('POST /api/auth/session', () => { assert.equal(body.address, ADDRESS); }); + it('passes Product sr25519 proof fields to sign-in verification', async () => { + let verifiedRequest: SignInRequest | null = null; + const server = await buildApp({ + verifySignInRequest: async request => { + verifiedRequest = request; + return { valid: true }; + } + }); + const productPublicKey = `0x${'22'.repeat(32)}`; + const signature = `0x${'33'.repeat(64)}`; + const response = await server.inject({ + method: 'POST', + url: '/api/auth/session', + payload: sessionBody({ + signatureScheme: PRODUCT_SR25519_SIGNATURE_SCHEME, + productPublicKey, + signature + }) + }); + + assert.equal(response.statusCode, 200); + const productRequest = verifiedRequest as Extract | null; + assert.ok(productRequest); + assert.equal(productRequest.signatureScheme, PRODUCT_SR25519_SIGNATURE_SCHEME); + assert.equal(productRequest.productPublicKey, productPublicKey); + assert.equal(productRequest.signature, signature); + }); + it('rejects an invalid signature with 401 and the verification code', async () => { const server = await buildApp({ verifySignInRequest: async () => ({ valid: false, code: 'SIGNATURE_INVALID', reason: 'bad signature' }) @@ -106,6 +135,30 @@ describe('POST /api/auth/session', () => { assert.equal(response.statusCode, 400); }); + it('rejects unknown sign-in signature schemes before verification or token issuance', async () => { + let verificationCalled = false; + let issuanceCalled = false; + const server = await buildApp({ + verifySignInRequest: async () => { + verificationCalled = true; + return { valid: true }; + }, + issueSessionToken: () => { + issuanceCalled = true; + return { ok: true, token: 'payload.signature', expiresAt: new Date(Date.now() + 1000).toISOString() }; + } + }); + const response = await server.inject({ + method: 'POST', + url: '/api/auth/session', + payload: sessionBody({ signatureScheme: 'product-unknown-v1' }) + }); + + assert.equal(response.statusCode, 400); + assert.equal(verificationCalled, false); + assert.equal(issuanceCalled, false); + }); + it('rejects a different-chain sign-in before verification or token issuance', async () => { let verificationCalled = false; let issuanceCalled = false; diff --git a/services/api/src/routes/auth.ts b/services/api/src/routes/auth.ts index 90d0845..9089c1e 100644 --- a/services/api/src/routes/auth.ts +++ b/services/api/src/routes/auth.ts @@ -8,6 +8,8 @@ import { z } from 'zod'; import { config } from '../config.js'; import { checkDotifyChainId } from '../services/chainDomain.js'; import { + EIP191_SIGNATURE_SCHEME, + PRODUCT_SR25519_SIGNATURE_SCHEME, createWalletNonceChallenge, verifySignInRequest as defaultVerifySignInRequest, type SignInRequest, @@ -25,14 +27,28 @@ const nonceRequestSchema = z.object({ chainId: z.number().int().positive().optional() }); -const sessionRequestSchema = z.object({ +const sessionBaseRequestSchema = z.object({ address: z.string().regex(/^0x[0-9a-fA-F]{40}$/, 'Invalid EVM address'), - signature: z.string().regex(/^0x[0-9a-fA-F]+$/, 'Invalid signature'), nonce: z.string().min(16, 'Nonce is required'), chainId: z.number().int().positive(), expiresAt: z.string().datetime() }); +const eip191SessionRequestSchema = sessionBaseRequestSchema.extend({ + signatureScheme: z.literal(EIP191_SIGNATURE_SCHEME).optional(), + signature: z.string().regex(/^0x[0-9a-fA-F]+$/, 'Invalid signature') +}); + +// 128 hex = bare 64-byte sr25519; 130 hex = MultiSignature-tagged 65-byte +// value. The tag itself is validated in verifySignInRequest, not here. +const productSr25519SessionRequestSchema = sessionBaseRequestSchema.extend({ + signatureScheme: z.literal(PRODUCT_SR25519_SIGNATURE_SCHEME), + signature: z.string().regex(/^0x([0-9a-fA-F]{128}|[0-9a-fA-F]{130})$/, 'Invalid Product sr25519 signature'), + productPublicKey: z.string().regex(/^0x[0-9a-fA-F]{64}$/, 'Invalid Product account public key') +}); + +const sessionRequestSchema = z.union([productSr25519SessionRequestSchema, eip191SessionRequestSchema]); + const logoutRequestSchema = z.object({ sessionToken: z.string().min(16, 'Session token is required') }); @@ -103,13 +119,27 @@ export function createAuthRoutes(deps: AuthRouteDeps = defaultDeps) { return reply.status(400).send({ error: domain.reason, code: domain.code }); } - const verification = await deps.verifySignInRequest({ - requester: parsed.data.address, - chainId: parsed.data.chainId, - nonce: parsed.data.nonce, - expiresAt: parsed.data.expiresAt, - signature: parsed.data.signature - }); + const signInRequest: SignInRequest = + parsed.data.signatureScheme === PRODUCT_SR25519_SIGNATURE_SCHEME + ? { + requester: parsed.data.address, + chainId: parsed.data.chainId, + nonce: parsed.data.nonce, + expiresAt: parsed.data.expiresAt, + signature: parsed.data.signature, + signatureScheme: PRODUCT_SR25519_SIGNATURE_SCHEME, + productPublicKey: parsed.data.productPublicKey + } + : { + requester: parsed.data.address, + chainId: parsed.data.chainId, + nonce: parsed.data.nonce, + expiresAt: parsed.data.expiresAt, + signature: parsed.data.signature, + signatureScheme: EIP191_SIGNATURE_SCHEME + }; + + const verification = await deps.verifySignInRequest(signInRequest); if (!verification.valid) { return reply.status(401).send({ error: verification.reason, code: verification.code }); } diff --git a/services/api/src/routes/keys.test.ts b/services/api/src/routes/keys.test.ts index 210abf8..95c8fc4 100644 --- a/services/api/src/routes/keys.test.ts +++ b/services/api/src/routes/keys.test.ts @@ -2,6 +2,7 @@ import assert from 'node:assert/strict'; import { afterEach, describe, it } from 'node:test'; import Fastify, { type FastifyInstance } from 'fastify'; import { createKeyRoutes, type KeyRouteDeps } from './keys.js'; +import { PRODUCT_SR25519_SIGNATURE_SCHEME, type KeySignatureRequest } from '../services/signatures.js'; const CONTENT_HASH = `0x${'ab'.repeat(32)}`; const REQUESTER = '0x1111111111111111111111111111111111111111'; @@ -85,6 +86,105 @@ describe('POST /api/tracks/:contentHash/key-request', () => { assert.equal(response.json().code, 'SIGNATURE_INVALID'); }); + it('passes Product sr25519 proof fields to signature verification', async () => { + let verifiedRequest: KeySignatureRequest | null = null; + const server = await buildApp({ + verifySignedRequest: async request => { + verifiedRequest = request; + return { valid: true }; + } + }); + const productPublicKey = `0x${'22'.repeat(32)}`; + const signature = `0x${'33'.repeat(64)}`; + const response = await server.inject({ + method: 'POST', + url: `/api/tracks/${CONTENT_HASH}/key-request`, + payload: baseBody({ + signatureScheme: PRODUCT_SR25519_SIGNATURE_SCHEME, + productPublicKey, + signature + }) + }); + + assert.equal(response.statusCode, 200); + const productRequest = verifiedRequest as Extract | null; + assert.ok(productRequest); + assert.equal(productRequest.signatureScheme, PRODUCT_SR25519_SIGNATURE_SCHEME); + assert.equal(productRequest.productPublicKey, productPublicKey); + assert.equal(productRequest.signature, signature); + }); + + it('rejects unknown signature schemes before verification or access checks', async () => { + let verificationCalled = false; + let accessChecked = false; + const server = await buildApp({ + verifySignedRequest: async () => { + verificationCalled = true; + return { valid: true }; + }, + checkTrackAccess: async () => { + accessChecked = true; + return { allowed: true, runtime: RUNTIME }; + } + }); + const response = await server.inject({ + method: 'POST', + url: `/api/tracks/${CONTENT_HASH}/key-request`, + payload: baseBody({ signatureScheme: 'product-unknown-v1' }) + }); + + assert.equal(response.statusCode, 400); + assert.equal(verificationCalled, false); + assert.equal(accessChecked, false); + }); + + it('forwards a MultiSignature-tagged Product signature to verification', async () => { + // 65-byte tagged signatures are a legitimate Substrate signRaw shape; the + // route must not reject them at the schema before the verifier can check + // the tag. + let verifiedRequest: KeySignatureRequest | null = null; + const server = await buildApp({ + verifySignedRequest: async request => { + verifiedRequest = request; + return { valid: true }; + } + }); + const signature = `0x01${'33'.repeat(64)}`; + const response = await server.inject({ + method: 'POST', + url: `/api/tracks/${CONTENT_HASH}/key-request`, + payload: baseBody({ + signatureScheme: PRODUCT_SR25519_SIGNATURE_SCHEME, + productPublicKey: `0x${'22'.repeat(32)}`, + signature + }) + }); + + assert.equal(response.statusCode, 200); + assert.equal((verifiedRequest as KeySignatureRequest | null)?.signature, signature); + }); + + it('requires Product public key for Product sr25519 requests', async () => { + let verificationCalled = false; + const server = await buildApp({ + verifySignedRequest: async () => { + verificationCalled = true; + return { valid: true }; + } + }); + const response = await server.inject({ + method: 'POST', + url: `/api/tracks/${CONTENT_HASH}/key-request`, + payload: baseBody({ + signatureScheme: PRODUCT_SR25519_SIGNATURE_SCHEME, + signature: `0x${'33'.repeat(64)}` + }) + }); + + assert.equal(response.statusCode, 400); + assert.equal(verificationCalled, false); + }); + it('answers a denied individual listener with an unlock CTA, never a key or a preview mode', async () => { const server = await buildApp({ checkTrackAccess: async () => ({ allowed: false, code: 'LISTENER_ACCESS_REQUIRED', reason: 'no access' }) diff --git a/services/api/src/routes/keys.ts b/services/api/src/routes/keys.ts index a4e56e4..79a5677 100644 --- a/services/api/src/routes/keys.ts +++ b/services/api/src/routes/keys.ts @@ -21,26 +21,46 @@ import { type TrackAccessResult } from '../services/chainAccess.js'; import { deriveContentKey as defaultDeriveContentKey, type ContentKeyResult } from '../services/keyVault.js'; -import { verifySignedRequest as defaultVerifySignedRequest, type KeySignatureRequest, type SignatureVerification } from '../services/signatures.js'; +import { + EIP191_SIGNATURE_SCHEME, + PRODUCT_SR25519_SIGNATURE_SCHEME, + verifySignedRequest as defaultVerifySignedRequest, + type KeySignatureRequest, + type SignatureVerification +} from '../services/signatures.js'; import { verifySessionToken as defaultVerifySessionToken, type SessionVerification } from '../services/sessionTokens.js'; const paramsSchema = z.object({ contentHash: z.string().regex(/^0x[0-9a-fA-F]{64}$/, 'Invalid content hash') }); -const signedBodySchema = z.object({ +const signedBaseBodySchema = z.object({ requester: z.string().regex(/^0x[0-9a-fA-F]{40}$/, 'Invalid EVM address'), - signature: z.string().regex(/^0x[0-9a-fA-F]+$/, 'Invalid signature'), nonce: z.string().min(16, 'Nonce is required'), chainId: z.number().int().positive(), expiresAt: z.string().datetime() }); // 'room_listener' is intentionally not accepted; room listeners never get keys. -const keyRequestBodySchema = signedBodySchema.extend({ +const keyRequestPurposeSchema = z.object({ purpose: z.enum(['individual', 'room_host']) }); +const eip191KeyRequestBodySchema = signedBaseBodySchema.merge(keyRequestPurposeSchema).extend({ + signatureScheme: z.literal(EIP191_SIGNATURE_SCHEME).optional(), + signature: z.string().regex(/^0x[0-9a-fA-F]+$/, 'Invalid signature') +}); + +// 128 hex = bare 64-byte sr25519; 130 hex = MultiSignature-tagged 65-byte +// value. The tag itself is validated in verifySignedRequest, not here. +const productSr25519KeyRequestBodySchema = signedBaseBodySchema.merge(keyRequestPurposeSchema).extend({ + signatureScheme: z.literal(PRODUCT_SR25519_SIGNATURE_SCHEME), + signature: z.string().regex(/^0x([0-9a-fA-F]{128}|[0-9a-fA-F]{130})$/, 'Invalid Product sr25519 signature'), + productPublicKey: z.string().regex(/^0x[0-9a-fA-F]{64}$/, 'Invalid Product account public key') +}); + +const keyRequestBodySchema = z.union([productSr25519KeyRequestBodySchema, eip191KeyRequestBodySchema]); + // Session path (ticket 24 P2): after the one-per-session sign-in, a key // request carries the bearer token instead of a fresh wallet signature. The // on-chain access check still runs on every request. @@ -145,16 +165,33 @@ export function createKeyRoutes(deps: KeyRouteDeps = defaultDeps) { return reply.status(401).send({ error: domain.reason, code: domain.code }); } - const signature = await deps.verifySignedRequest({ - action: 'REQUEST_CONTENT_KEY', - purpose: body.data.purpose, - contentHash: params.data.contentHash, - requester: body.data.requester, - chainId: body.data.chainId, - nonce: body.data.nonce, - expiresAt: body.data.expiresAt, - signature: body.data.signature - }); + const signatureRequest: KeySignatureRequest = + body.data.signatureScheme === PRODUCT_SR25519_SIGNATURE_SCHEME + ? { + action: 'REQUEST_CONTENT_KEY', + purpose: body.data.purpose, + contentHash: params.data.contentHash, + requester: body.data.requester, + chainId: body.data.chainId, + nonce: body.data.nonce, + expiresAt: body.data.expiresAt, + signature: body.data.signature, + signatureScheme: PRODUCT_SR25519_SIGNATURE_SCHEME, + productPublicKey: body.data.productPublicKey + } + : { + action: 'REQUEST_CONTENT_KEY', + purpose: body.data.purpose, + contentHash: params.data.contentHash, + requester: body.data.requester, + chainId: body.data.chainId, + nonce: body.data.nonce, + expiresAt: body.data.expiresAt, + signature: body.data.signature, + signatureScheme: EIP191_SIGNATURE_SCHEME + }; + + const signature = await deps.verifySignedRequest(signatureRequest); if (!signature.valid) { return reply.status(401).send({ error: signature.reason, code: signature.code }); diff --git a/services/api/src/services/signatures.test.ts b/services/api/src/services/signatures.test.ts index 373066b..3c8943f 100644 --- a/services/api/src/services/signatures.test.ts +++ b/services/api/src/services/signatures.test.ts @@ -1,11 +1,14 @@ import assert from 'node:assert/strict'; import { beforeEach, describe, it } from 'node:test'; +import { getPublicKey, secretFromSeed, sign as signSr25519 } from '@scure/sr25519'; import { privateKeyToAccount } from 'viem/accounts'; import { resetNonceStore } from './replayProtection.js'; import { + PRODUCT_SR25519_SIGNATURE_SCHEME, buildSignedRequestMessage, buildSignInMessage, createWalletNonceChallenge, + deriveProductAccountH160, verifySignInRequest, verifySignedRequest, type SignInPayload, @@ -17,6 +20,15 @@ const signer = privateKeyToAccount('0xac0974bec39a37e36980911eda47a06fcd4ee8d3a8 const CONTENT_HASH = `0x${'ab'.repeat(32)}` as const; const CHAIN_ID = 420420417; +const productSecretKey = secretFromSeed(new Uint8Array(32).fill(7)); +const productPublicKey = getPublicKey(productSecretKey); +const productPublicKeyHex = `0x${bytesToHex(productPublicKey)}` as const; + +function bytesToHex(bytes: Uint8Array): string { + return Array.from(bytes) + .map(byte => byte.toString(16).padStart(2, '0')) + .join(''); +} async function signedPayload(overrides: Partial = {}) { const challenge = createWalletNonceChallenge({ address: signer.address, chainId: CHAIN_ID }); @@ -34,6 +46,40 @@ async function signedPayload(overrides: Partial = {}) { return { payload, signature }; } +// The Host may sign the canonical message verbatim or inside the conventional +// Substrate `` envelope, and may return the signature bare or with a +// MultiSignature tag. Tests cover every shape the verifier accepts. +type ProductEnvelope = 'raw' | 'bytes-wrapped'; +type ProductSignatureShape = 'bare' | 'multisignature'; + +function encodeProductPayload(message: string, envelope: ProductEnvelope): Uint8Array { + return new TextEncoder().encode(envelope === 'bytes-wrapped' ? `${message}` : message); +} + +function encodeProductSignature(raw: Uint8Array, shape: ProductSignatureShape): string { + return shape === 'multisignature' ? `0x01${bytesToHex(raw)}` : `0x${bytesToHex(raw)}`; +} + +async function productSignedPayload( + overrides: Partial = {}, + options: { envelope?: ProductEnvelope; shape?: ProductSignatureShape } = {}, +) { + const requester = overrides.requester ?? deriveProductAccountH160(productPublicKey); + const challenge = createWalletNonceChallenge({ address: requester, chainId: CHAIN_ID }); + const payload: SignedRequestPayload = { + action: 'REQUEST_CONTENT_KEY', + purpose: 'individual', + contentHash: CONTENT_HASH, + requester, + chainId: CHAIN_ID, + nonce: challenge.nonce, + expiresAt: challenge.expiresAt, + ...overrides + }; + const raw = signSr25519(productSecretKey, encodeProductPayload(buildSignedRequestMessage(payload), options.envelope ?? 'raw')); + return { payload, signature: encodeProductSignature(raw, options.shape ?? 'bare'), productPublicKey: productPublicKeyHex }; +} + describe('verifySignedRequest', () => { beforeEach(() => { resetNonceStore(); @@ -45,6 +91,17 @@ describe('verifySignedRequest', () => { assert.equal(result.valid, true); }); + it('accepts a Product sr25519 request bound to the derived H160 requester', async () => { + const { payload, signature, productPublicKey } = await productSignedPayload(); + const result = await verifySignedRequest({ + ...payload, + signatureScheme: PRODUCT_SR25519_SIGNATURE_SCHEME, + signature, + productPublicKey + }); + assert.equal(result.valid, true); + }); + it('rejects a replayed nonce', async () => { const { payload, signature } = await signedPayload(); const first = await verifySignedRequest({ ...payload, signature }); @@ -63,6 +120,118 @@ describe('verifySignedRequest', () => { assert.equal(!result.valid && result.code, 'SIGNATURE_INVALID'); }); + it('accepts a Product signature made over the envelope', async () => { + const { payload, signature, productPublicKey } = await productSignedPayload({}, { envelope: 'bytes-wrapped' }); + const result = await verifySignedRequest({ + ...payload, + signatureScheme: PRODUCT_SR25519_SIGNATURE_SCHEME, + signature, + productPublicKey + }); + assert.equal(result.valid, true); + }); + + it('accepts a MultiSignature-tagged Product signature in either envelope', async () => { + for (const envelope of ['raw', 'bytes-wrapped'] as const) { + resetNonceStore(); + const { payload, signature, productPublicKey } = await productSignedPayload({}, { envelope, shape: 'multisignature' }); + assert.equal(signature.length, 2 + 130, 'expected a 65-byte tagged signature'); + const result = await verifySignedRequest({ + ...payload, + signatureScheme: PRODUCT_SR25519_SIGNATURE_SCHEME, + signature, + productPublicKey + }); + assert.equal(result.valid, true, `envelope ${envelope} should verify`); + } + }); + + it('rejects a 65-byte signature whose MultiSignature tag is not sr25519', async () => { + const { payload, signature, productPublicKey } = await productSignedPayload(); + const ed25519Tagged = `0x00${signature.slice(2)}`; + const result = await verifySignedRequest({ + ...payload, + signatureScheme: PRODUCT_SR25519_SIGNATURE_SCHEME, + signature: ed25519Tagged, + productPublicKey + }); + assert.equal(result.valid, false); + assert.equal(!result.valid && result.code, 'PRODUCT_SIGNATURE_INVALID'); + }); + + it('rejects an EVM-derived account id claiming an arbitrary requester H160', async () => { + // 20-byte H160 padded with 0xee derives straight back to that H160, so + // without this guard a caller could name any paying EVM listener. + const victim = '742d35cc6634c0532925a3b844bc9e7595f0beb0'; + const forgedKey = `0x${victim}${'ee'.repeat(12)}`; + const { payload, signature } = await productSignedPayload({ requester: `0x${victim}` }); + + const result = await verifySignedRequest({ + ...payload, + signatureScheme: PRODUCT_SR25519_SIGNATURE_SCHEME, + signature, + productPublicKey: forgedKey + }); + + assert.equal(result.valid, false); + assert.equal(!result.valid && result.code, 'PRODUCT_KEY_NOT_NATIVE'); + }); + + it('reports an envelope/account failure distinctly from a malformed request', async () => { + const { payload, productPublicKey } = await productSignedPayload(); + const wrongKey = secretFromSeed(new Uint8Array(32).fill(9)); + const signature = `0x${bytesToHex(signSr25519(wrongKey, new TextEncoder().encode(buildSignedRequestMessage(payload))))}`; + + const result = await verifySignedRequest({ + ...payload, + signatureScheme: PRODUCT_SR25519_SIGNATURE_SCHEME, + signature, + productPublicKey + }); + + assert.equal(result.valid, false); + assert.equal(!result.valid && result.code, 'PRODUCT_SIGNATURE_REJECTED'); + }); + + it('rejects a Product signature when the payload changes', async () => { + const { payload, signature, productPublicKey } = await productSignedPayload(); + const result = await verifySignedRequest({ + ...payload, + contentHash: `0x${'cd'.repeat(32)}`, + signatureScheme: PRODUCT_SR25519_SIGNATURE_SCHEME, + signature, + productPublicKey + }); + assert.equal(result.valid, false); + assert.equal(!result.valid && result.code, 'PRODUCT_SIGNATURE_REJECTED'); + }); + + it('rejects a Product public key that does not derive to the requester H160', async () => { + const { payload, signature, productPublicKey } = await productSignedPayload({ + requester: '0x1111111111111111111111111111111111111111' + }); + const result = await verifySignedRequest({ + ...payload, + signatureScheme: PRODUCT_SR25519_SIGNATURE_SCHEME, + signature, + productPublicKey + }); + assert.equal(result.valid, false); + assert.equal(!result.valid && result.code, 'PRODUCT_ADDRESS_MISMATCH'); + }); + + it('rejects malformed Product proof bytes before nonce consumption', async () => { + const { payload, signature } = await productSignedPayload(); + const result = await verifySignedRequest({ + ...payload, + signatureScheme: PRODUCT_SR25519_SIGNATURE_SCHEME, + signature, + productPublicKey: '0x1234' + }); + assert.equal(result.valid, false); + assert.equal(!result.valid && result.code, 'PRODUCT_SIGNATURE_INVALID'); + }); + it('rejects a signature over a different purpose', async () => { const { payload, signature } = await signedPayload({ purpose: 'individual' }); const result = await verifySignedRequest({ ...payload, purpose: 'room_host', signature }); @@ -124,4 +293,34 @@ describe('verifySignedRequest', () => { assert.equal(result.valid, false); assert.equal(!result.valid && result.code, 'CHAIN_ID_MISMATCH'); }); + + it('accepts a Product sr25519 sign-in bound to the derived H160 requester', async () => { + const requester = deriveProductAccountH160(productPublicKey); + const challenge = createWalletNonceChallenge({ address: requester, chainId: CHAIN_ID }); + const payload: SignInPayload = { + requester, + chainId: CHAIN_ID, + nonce: challenge.nonce, + expiresAt: challenge.expiresAt + }; + const signature = `0x${bytesToHex(signSr25519(productSecretKey, new TextEncoder().encode(buildSignInMessage(payload))))}`; + const result = await verifySignInRequest({ + ...payload, + signatureScheme: PRODUCT_SR25519_SIGNATURE_SCHEME, + signature, + productPublicKey: productPublicKeyHex + }); + + assert.equal(result.valid, true); + }); + + it('matches the Product SDK H160 derivation vector for a native Substrate public key', () => { + const alicePublicKey = new Uint8Array([ + 0xd4, 0x35, 0x93, 0xc7, 0x15, 0xfd, 0xd3, 0x1c, 0x61, 0x14, 0x1a, 0xbd, 0x04, 0xa9, 0x9f, + 0xd6, 0x82, 0x2c, 0x85, 0x58, 0x85, 0x4c, 0xcd, 0xe3, 0x9a, 0x56, 0x84, 0xe7, 0xa5, 0x6d, + 0xa2, 0x7d + ]); + + assert.equal(deriveProductAccountH160(alicePublicKey), '0x9621dde636de098b43efb0fa9b61facfe328f99d'); + }); }); diff --git a/services/api/src/services/signatures.ts b/services/api/src/services/signatures.ts index 34c9105..a34901b 100644 --- a/services/api/src/services/signatures.ts +++ b/services/api/src/services/signatures.ts @@ -1,4 +1,4 @@ -// Wallet-signed request verification (EIP-191 personal_sign). +// Wallet-signed request verification. // // The signed payload is a structured, domain-bound text message that binds: // app, action, purpose, content hash, requester address, chain ID, nonce, @@ -6,11 +6,24 @@ // (web/src/services/keyService.ts); any drift between the two breaks // verification, which fails closed. // -// Security boundary: EIP-191 is used instead of EIP-712 for the first -// production spine because it is supported uniformly across the wallets we -// target. The message is structured and domain-bound, so it cannot be -// replayed against another app, chain, purpose, or track. +// Security boundary: standalone wallets use EIP-191 instead of EIP-712 for +// the first production spine because it is supported uniformly across the +// wallets we target. Product-host requests sign the same canonical message +// bytes with the app-scoped Product account and must prove that account's +// public key derives to the requester H160 used by runtime access checks. +// The message is structured and domain-bound, so it cannot be replayed +// against another app, chain, purpose, or track. +// +// The Product Host `signRaw` wire format is not pinned by the SDK: the +// response signature is untagged, and a Substrate host may sign a raw payload +// verbatim or inside the conventional `` envelope. Rather than guess +// one shape and fail every request on a wrong guess, verification accepts the +// bounded set of shapes below. Each still carries the identical domain-bound +// message, so tolerance costs no security - it only removes an unverifiable +// assumption. See docs/explanation/product-devnet-architecture.md. +import { keccak_256 } from '@noble/hashes/sha3'; +import { verify as verifySr25519Signature } from '@scure/sr25519'; import { verifyMessage } from 'viem'; import { config } from '../config.js'; import { checkDotifyChainId } from './chainDomain.js'; @@ -20,6 +33,16 @@ import { consumeNonce, issueNonce } from './replayProtection.js'; // content keys, they only receive the host's ephemeral WebRTC stream. export type KeyRequestPurpose = 'individual' | 'room_host'; export type SignedAction = 'REQUEST_CONTENT_KEY' | 'SIGN_IN'; +export const EIP191_SIGNATURE_SCHEME = 'eip191'; +export const PRODUCT_SR25519_SIGNATURE_SCHEME = 'product-sr25519-v1'; +export type SignatureScheme = typeof EIP191_SIGNATURE_SCHEME | typeof PRODUCT_SR25519_SIGNATURE_SCHEME; + +const PRODUCT_PUBLIC_KEY_BYTES = 32; +const PRODUCT_SR25519_SIGNATURE_BYTES = 64; +const H160_BYTES = 20; +const EVM_DERIVED_MARKER = 0xee; +// Substrate MultiSignature enum tag for sr25519 (0 = ed25519, 1 = sr25519). +const MULTISIGNATURE_SR25519_TAG = 0x01; export type NonceChallengeRequest = { address: string; @@ -42,10 +65,19 @@ export type SignedRequestPayload = { expiresAt: string; }; -export type KeySignatureRequest = SignedRequestPayload & { +export type Eip191SignatureFields = { + signatureScheme?: typeof EIP191_SIGNATURE_SCHEME; + signature: string; +}; + +export type ProductSr25519SignatureFields = { + signatureScheme: typeof PRODUCT_SR25519_SIGNATURE_SCHEME; signature: string; + productPublicKey: string; }; +export type SignatureFields = Eip191SignatureFields | ProductSr25519SignatureFields; +export type KeySignatureRequest = SignedRequestPayload & SignatureFields; export type SignatureVerification = { valid: true } | { valid: false; code: string; reason: string }; /** @@ -73,9 +105,7 @@ export type SignInPayload = { expiresAt: string; }; -export type SignInRequest = SignInPayload & { - signature: string; -}; +export type SignInRequest = SignInPayload & SignatureFields; /** * Canonical EIP-191 message for the one-per-session Dotify sign-in @@ -96,6 +126,196 @@ export function buildSignInMessage(payload: SignInPayload): string { ].join('\n'); } +function bytesToHex(bytes: Uint8Array): string { + return Array.from(bytes) + .map(byte => byte.toString(16).padStart(2, '0')) + .join(''); +} + +function fixedHexToBytes(hex: string, expectedBytes: number): Uint8Array { + const clean = hex.startsWith('0x') ? hex.slice(2) : hex; + if (clean.length !== expectedBytes * 2 || !/^[0-9a-fA-F]+$/.test(clean)) { + throw new Error(`Expected ${expectedBytes} bytes of hex`); + } + return new Uint8Array(Buffer.from(clean, 'hex')); +} + +function hexToBytes(hex: string): Uint8Array { + const clean = hex.startsWith('0x') ? hex.slice(2) : hex; + if (clean.length % 2 !== 0 || !/^[0-9a-fA-F]+$/.test(clean)) { + throw new Error('Expected an even-length hex string'); + } + return new Uint8Array(Buffer.from(clean, 'hex')); +} + +/** + * The Host `signRaw` response carries an opaque signature with no scheme tag + * (truapi `HostSignPayloadResponse.signature`). Accept the two shapes a + * Substrate signer can return for sr25519 - a bare 64-byte signature, or a + * 65-byte MultiSignature-tagged value - and reject everything else. The tag is + * checked, not skipped, so an ed25519 or ECDSA signature still fails closed. + */ +function parseProductSignatureBytes(hex: string): Uint8Array { + const bytes = hexToBytes(hex); + if (bytes.length === PRODUCT_SR25519_SIGNATURE_BYTES) { + return bytes; + } + if (bytes.length === PRODUCT_SR25519_SIGNATURE_BYTES + 1 && bytes[0] === MULTISIGNATURE_SR25519_TAG) { + return bytes.slice(1); + } + throw new Error('Unsupported Product signature length'); +} + +/** + * A Substrate host may sign a raw payload either verbatim or wrapped in the + * conventional `...` envelope. Both variants carry the same + * canonical Dotify message, which is already bound to app, action, purpose, + * content hash, requester, chain, nonce, and expiry - so accepting either + * envelope adds no replay surface, it only removes a guess about host + * behaviour. Nothing outside these two shapes is accepted. + */ +function productSignedMessageVariants(message: string): Uint8Array[] { + const encoder = new TextEncoder(); + return [encoder.encode(message), encoder.encode(`${message}`)]; +} + +/** + * True when the 32-byte account id is a pallet-revive EVM-derived account + * (a 20-byte H160 padded with 0xee). Such an account is not a native + * sr25519 keypair, so it can never legitimately produce a Product signature. + */ +function isEvmDerivedAccountId(publicKey: Uint8Array): boolean { + return publicKey.slice(H160_BYTES).every(byte => byte === EVM_DERIVED_MARKER); +} + +/** + * Match Product SDK / pallet-revive AccountId32 -> H160 derivation: + * native Substrate accounts use keccak256(publicKey), last 20 bytes; accounts + * already derived from H160 strip the trailing 0xee padding. + */ +export function deriveProductAccountH160(publicKey: Uint8Array): `0x${string}` { + if (publicKey.length !== PRODUCT_PUBLIC_KEY_BYTES) { + throw new Error(`Expected ${PRODUCT_PUBLIC_KEY_BYTES}-byte Product public key`); + } + + const addressBytes = isEvmDerivedAccountId(publicKey) + ? publicKey.slice(0, H160_BYTES) + : keccak_256(publicKey).slice(PRODUCT_PUBLIC_KEY_BYTES - H160_BYTES); + return `0x${bytesToHex(addressBytes)}`; +} + +function verifyProductSr25519Payload(args: { + requester: string; + message: string; + signature: string; + productPublicKey: string | undefined; +}): SignatureVerification { + if (!args.productPublicKey) { + return { + valid: false, + code: 'PRODUCT_PUBLIC_KEY_REQUIRED', + reason: 'Product signed requests must include the Product account public key.' + }; + } + + let publicKey: Uint8Array; + let signature: Uint8Array; + try { + publicKey = fixedHexToBytes(args.productPublicKey, PRODUCT_PUBLIC_KEY_BYTES); + signature = parseProductSignatureBytes(args.signature); + } catch { + return { + valid: false, + code: 'PRODUCT_SIGNATURE_INVALID', + reason: 'Product signature payload is malformed.' + }; + } + + // An EVM-derived account id would let a caller name any H160 as the + // requester and lean entirely on the curve check to stop the takeover. + // A real Product account is a native AccountId32, so reject that shape + // before deriving anything from it. + if (isEvmDerivedAccountId(publicKey)) { + return { + valid: false, + code: 'PRODUCT_KEY_NOT_NATIVE', + reason: 'Product signed requests require a native Product account key, not an EVM-derived account id.' + }; + } + + const derivedRequester = deriveProductAccountH160(publicKey); + if (derivedRequester.toLowerCase() !== args.requester.toLowerCase()) { + return { + valid: false, + code: 'PRODUCT_ADDRESS_MISMATCH', + reason: 'Product account public key does not derive to the requester H160 address.' + }; + } + + const signatureValid = productSignedMessageVariants(args.message).some(payload => { + try { + return verifySr25519Signature(payload, signature, publicKey); + } catch { + return false; + } + }); + + if (!signatureValid) { + // Deliberately distinct from SIGNATURE_INVALID: the key parsed and derives + // to the requester, so this is a signing-envelope or wrong-account problem, + // not a malformed request. Operators need those apart in Fly logs. + return { + valid: false, + code: 'PRODUCT_SIGNATURE_REJECTED', + reason: 'Product host signature did not verify against the Dotify request payload in any supported signing envelope.' + }; + } + + return { valid: true }; +} + +async function verifySignatureEnvelope( + request: SignatureFields & { requester: string }, + message: string, + invalidSignatureReason: string +): Promise { + const signatureScheme = request.signatureScheme ?? EIP191_SIGNATURE_SCHEME; + + if (signatureScheme === EIP191_SIGNATURE_SCHEME) { + let signatureValid = false; + try { + signatureValid = await verifyMessage({ + address: request.requester as `0x${string}`, + message, + signature: request.signature as `0x${string}` + }); + } catch { + signatureValid = false; + } + + if (!signatureValid) { + return { valid: false, code: 'SIGNATURE_INVALID', reason: invalidSignatureReason }; + } + + return { valid: true }; + } + + if (signatureScheme === PRODUCT_SR25519_SIGNATURE_SCHEME) { + return verifyProductSr25519Payload({ + requester: request.requester, + message, + signature: request.signature, + productPublicKey: 'productPublicKey' in request ? request.productPublicKey : undefined + }); + } + + return { + valid: false, + code: 'SIGNATURE_SCHEME_UNSUPPORTED', + reason: 'Signature scheme is not supported for Dotify key delivery.' + }; +} + /** * Verify a sign-in request: expiry, signature, then nonce consumption - * the same fail-closed order as verifySignedRequest. @@ -111,19 +331,13 @@ export async function verifySignInRequest(request: SignInRequest): Promise=16.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@ipld/dag-pb/node_modules/multiformats": { + "version": "14.0.5", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-14.0.5.tgz", + "integrity": "sha512-vbIm83F2yZ1pWJGS0yl0ysracIvv56LtbrIyiIQHoLdYDJOMoLfVFsXhh9DUH4SFdkdkFhucyWniihsNzVEjkQ==", + "license": "Apache-2.0 OR MIT" + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.2.0.tgz", + "integrity": "sha512-T/BoHgFXirb0ENSPBquzX0rcjXeM6Lo892a2jlYJkqk83LqZx0l1Of7DzlKJ6jkpvMrkHSnAcgb5JegL8SeIkQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "2.2.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz", + "integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "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, + "license": "MIT", + "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, + "license": "MIT", + "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, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@novasamatech/host-api": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/@novasamatech/host-api/-/host-api-0.6.18.tgz", + "integrity": "sha512-5U5tYRbY/v49BqHH+iHPIP6OH7KJjXUMypaXSXtZK/J3IsQAqDLBlu/LqpDrNxHqEK1sKY5EyYla840mqmkwPg==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "@novasamatech/scale": "0.6.18", + "@polkadot-api/utils": "^0.2.0", + "nanoevents": "9.1.0", + "nanoid": "5.1.7", + "neverthrow": "^8.2.0", + "scale-ts": "1.6.1" + } + }, + "node_modules/@novasamatech/host-api/node_modules/@novasamatech/scale": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/@novasamatech/scale/-/scale-0.6.18.tgz", + "integrity": "sha512-xRvBrzJSvCseQ62zLReS3EtiQjuiTY+c+yOyx6If9dBRzX5FL52OazFLsdSaq3wOe8441TPXL1vediotYFZlRg==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "@polkadot-api/utils": "^0.2.0", + "scale-ts": "1.6.1" + } + }, + "node_modules/@novasamatech/host-api/node_modules/nanoid": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.7.tgz", + "integrity": "sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "optional": true, + "peer": true, + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/@parity/product-sdk": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@parity/product-sdk/-/product-sdk-0.19.1.tgz", + "integrity": "sha512-vZnXh5FUc/KSXBeMYd5v5ZTit9f4PLlXkXD7rvvESdV7L2djTlDij4uOKkr1oWg9NrRmcH4cvkHD1wAbs2Zqzg==", + "license": "Apache-2.0", + "dependencies": { + "@parity/product-sdk-address": "0.1.1", + "@parity/product-sdk-chain-client": "0.9.1", + "@parity/product-sdk-cloud-storage": "0.8.1", + "@parity/product-sdk-contracts": "0.9.2", + "@parity/product-sdk-crypto": "0.1.1", + "@parity/product-sdk-errors": "0.2.0", + "@parity/product-sdk-host": "0.14.1", + "@parity/product-sdk-keys": "0.3.16", + "@parity/product-sdk-local-storage": "0.3.2", + "@parity/product-sdk-logger": "0.1.1", + "@parity/product-sdk-signer": "0.11.1", + "@parity/product-sdk-tx": "0.3.2", + "@parity/result": "0.2.0", + "polkadot-api": "^2.1.6" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/@parity/product-sdk-address": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@parity/product-sdk-address/-/product-sdk-address-0.1.1.tgz", + "integrity": "sha512-sSymun3alNGdvawhdc0Ha0KEkuqMwBZui1bsUVeZIZRJAfWvQzrV1AVaf8aah5JFlcaRdg8FYyp7xL2eP+ZplA==", + "license": "Apache-2.0", + "dependencies": { + "@noble/hashes": "^1.7.1", + "@polkadot-api/substrate-bindings": "^0.12.0" + } + }, + "node_modules/@parity/product-sdk-address/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@parity/product-sdk-address/node_modules/@polkadot-api/substrate-bindings": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.12.0.tgz", + "integrity": "sha512-cIjDeJRHW6g3z+/55UzpoG4LG1N0HbT4x3NvZsQkYg4eoio9Sw7Pw2aZZX86pWemxc7vQbNw7WSz2Gz+ckdX6Q==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.8.0", + "@polkadot-api/utils": "0.1.2", + "@scure/base": "^1.2.5", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@parity/product-sdk-address/node_modules/@polkadot-api/utils": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.1.2.tgz", + "integrity": "sha512-yhs5k2a8N1SBJcz7EthZoazzLQUkZxbf+0271Xzu42C5AEM9K9uFLbsB+ojzHEM72O5X8lPtSwGKNmS7WQyDyg==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-address/node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@parity/product-sdk-chain-client": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@parity/product-sdk-chain-client/-/product-sdk-chain-client-0.9.1.tgz", + "integrity": "sha512-NXMJAYqLGdFp0VAbNfn2HeGhcP6n78jxtVEpcv9084ZlldhuwFKJLGwXOj81xbw2QHATbufCNLDY9IsTM+9Pew==", + "license": "Apache-2.0", + "dependencies": { + "@parity/product-sdk-descriptors": "0.8.0", + "@parity/product-sdk-host": "0.14.1", + "@parity/product-sdk-logger": "0.1.1", + "polkadot-api": "^2.1.6" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@commander-js/extra-typings": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-15.0.0.tgz", + "integrity": "sha512-yeJlba62xqmkgELUsn7356MEnzLLu/fw2x4lofFqGnXh6YysRdEs2BaLeLtg1+KU0AXvMeqQvTTp+3hBEBK+EA==", + "license": "MIT", + "peerDependencies": { + "commander": "~15.0.0" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/cli": { + "version": "0.21.9", + "resolved": "https://registry.npmjs.org/@polkadot-api/cli/-/cli-0.21.9.tgz", + "integrity": "sha512-9aVdF/ocF3DBb8b/gdkcicBIaQkak2kkU4EGW+TunXLIvbIj2X9hH5geOlx8uWNwt0O4O1DIHD/nkXvycG/AcA==", + "license": "MIT", + "dependencies": { + "@commander-js/extra-typings": "^15.0.0", + "@polkadot-api/codegen": "0.22.5", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/wasm-executor": "^0.2.3", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@types/node": "^25.9.4", + "commander": "^15.0.0", + "execa": "^9.6.1", + "fs.promises.exists": "^1.1.4", + "ora": "^9.4.1", + "read-pkg": "^10.1.0", + "rollup": "^4.62.2", + "rollup-plugin-esbuild": "^6.2.1", + "rxjs": "^7.8.2", + "tsc-prog": "^2.3.0", + "typescript": "^6.0.3", + "write-package": "^7.2.0" + }, + "bin": { + "papi": "dist/main/src/main.js", + "polkadot-api": "dist/main/src/main.js" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/codegen": { + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/@polkadot-api/codegen/-/codegen-0.22.5.tgz", + "integrity": "sha512-zwZJAlviI211zhj5i6oXkrr0crrbO3GZjBd2vC9AshY1pRmyiNLV9DZsXw4E6l4JQwdAAyNWtPdnvvB9T3TpKg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/ink-contracts": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/ink-contracts/-/ink-contracts-0.6.3.tgz", + "integrity": "sha512-XqnM1VDzI5L62xgg+f8le2yEoz8QZbUKEfAfPnHMOgBj9tJiyF15FcOJmnLMO/vq3cGixqh18tzJekLG5YTxtA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.2.0.tgz", + "integrity": "sha512-lhkuBS/x06i3djQIN7p8jVkMnYuGsUAEMS6RhdWeEpz7X/8/APER4Wdih7MEBovCuwVSCTjOxl8f+alH7AZHZg==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.4.1.tgz", + "integrity": "sha512-F1Hw01C60jn98KQ0vBbgcwAvEcMcKLsJ5kFzq600sgjc6Rnbn1VF/XjTdrjcIqvfpR9sXyGIrpImrov2Usbrsw==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/known-chains": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/known-chains/-/known-chains-0.12.1.tgz", + "integrity": "sha512-ZW9TYD2y5IhgNy28zi9sByMr0CaP2I9x8HcAI0pwZR2qDC0kysCG6brDlg1UoZKCjRO99SsKO/iU1o434JgMnQ==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/logs-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/logs-provider/-/logs-provider-0.2.0.tgz", + "integrity": "sha512-BH9YdxZu+ZBPPAUwGrvqHPn1hQStL2Im3MmTwYkwXOWW2HlGHULcF65QKvyK+T6/mj2vDvl3MgivnGkUw4pVxg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/merkleize-metadata": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/merkleize-metadata/-/merkleize-metadata-1.2.3.tgz", + "integrity": "sha512-WkPbz0p2XQ9c8yXagdnwCHEB70Gnm91okcsd6IXU393//3aPgkxKgb+/Efnz7C5/KQmg02P0zXo7q/n/W/yVCA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/metadata-builders": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.14.3.tgz", + "integrity": "sha512-m7CACsiqHzgVEh5WBZGkTV8AQ3CBQKR1YpPQMnlsJfCr/IkgKU0UyWM6WxCmBiReLFVkOfXMtGlpN8+GxpHmww==", + "license": "MIT", + "dependencies": { + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/metadata-compatibility": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-compatibility/-/metadata-compatibility-0.6.3.tgz", + "integrity": "sha512-/Y0uF8nDk60ijydp8Bd37YexPFdB8hBXJWwEgOJHsVlhiny8sVKXiMg+UkJ9BiEk2z+yMbZRCKmhNpTpizo7aw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/observable-client": { + "version": "0.18.7", + "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.18.7.tgz", + "integrity": "sha512-/eQi3D8jbXLg/L1hZX4eX0/+nO3kTxkOfYeWEtFhtOvFdfqiiy2DdXG3Zg5QzgHmfG+11p4vSlLiPCC2UvS5Qw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/pjs-signer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/pjs-signer/-/pjs-signer-0.7.3.tgz", + "integrity": "sha512-U7BLFZfnpFMxCh/scJoLXT6oSbfZtZgMTkiu+TbuWQljAb/1ttrQOfshub5VihNyD5rHajp/2Fq0ONZoL5N5PA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/raw-client": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/raw-client/-/raw-client-0.3.0.tgz", + "integrity": "sha512-u/wM9W7ugIXxBSOEV8+zvQI43b5vgSI0pvE0Rg8PV0G65BTLK0SMc2o2SQL0HtS5+bi5sw/gg4reBJ/0a4U0cw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/signer": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signer/-/signer-0.3.3.tgz", + "integrity": "sha512-bmEV65TwgwbMfiecl7ZQ3k5lfkCOx9FPwPYkVvALcPiqb/d3zYwT9LL/E00hj+EB6IKMlMelEQVuchcW5i/92w==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/merkleize-metadata": "1.2.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/signers-common": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signers-common/-/signers-common-0.2.3.tgz", + "integrity": "sha512-SzGLJMxug31Y1P8+0I809ICpDrayztUXFFh2TV02GuwjEnY6mDGnmX56wowZF5yCn+WteeRdcTJTK7Whk3AGYQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/sm-provider": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@polkadot-api/sm-provider/-/sm-provider-0.3.8.tgz", + "integrity": "sha512-jIvzBNsBsh6LIpCrUOa7miZdQPwLPns5IUQyz/8v84R3ON7URSIVbtywGSR8O83BZGyW/DmVkqa5lClLE2fkqw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1" + }, + "peerDependencies": { + "@polkadot-api/smoldot": ">=0.3" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/smoldot": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/@polkadot-api/smoldot/-/smoldot-0.4.6.tgz", + "integrity": "sha512-gOXMJ10fXOub0zPP1cGAYeyf29BA2fB+qukrjXGcGN9b0Ya+lDgV7A7Q7y9JOrlGLeKbd1YdrYLAe/F43V7b9Q==", + "license": "MIT", + "dependencies": { + "@types/node": "^25.9.4", + "smoldot": "~3.3.1" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/substrate-bindings": { + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.20.3.tgz", + "integrity": "sha512-9iqC71fx1ee9ld1NZV8PFime5vryi0kt1bKCSlvNgO6dqMc06sMZuZ8WPjOzWLCHiKHLuphdMs3rVBBaeCP3yg==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/utils": "0.4.0", + "@scure/base": "^2.2.0", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/substrate-client": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.7.0.tgz", + "integrity": "sha512-TWCc4MAMa5SLVQXmomLHknbj+bztQ/Yclgwm8ENBhz8hR7c9rw9FBAkCa02jMBMCAygPhp3ayGRq+UFcF8KIxQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/raw-client": "0.3.0", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/utils": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.4.0.tgz", + "integrity": "sha512-9b/hwRM0UloLWV7SfpNaSD/4k8UQAHoaACAk7Xe+1MlfAm2JtnmPiB1GfGrfTyBlsrJVUIBCZpEmbmxVMaIqBA==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/@polkadot-api/ws-provider": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/ws-provider/-/ws-provider-0.9.1.tgz", + "integrity": "sha512-Ft2QJEjLZgTyKiCEbz3urSOxNfMdqNkg+QKqJhRbOFB7JtR1qMTvLE3TjM+5d2mNlEpOc3j2KDK3APNSj7uQ2Q==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/polkadot-api": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/polkadot-api/-/polkadot-api-2.2.1.tgz", + "integrity": "sha512-eiZVUhI1gY4ycBtsCo/yaDXyohUeuqnyKun+bwj90ADSn+jlfjZ4sajAqIhJFwPxbCT75+Zzbln4ZjuZ5PWNWA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/cli": "0.21.9", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/logs-provider": "0.2.0", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/pjs-signer": "0.7.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signer": "0.3.3", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@rx-state/core": "^0.1.4" + }, + "bin": { + "papi": "bin/cli.js", + "polkadot-api": "bin/cli.js" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/smoldot": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-3.3.2.tgz", + "integrity": "sha512-Zl4h/0gsw8cfTZzuJ7LV7mtR6QjxltwYjMY7MsVw0oXBXrLK8zyOS6DS9Vjsy57pX1vBMg6UVhHxjbH3W905zA==", + "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", + "dependencies": { + "ws": "^8.8.1" + } + }, + "node_modules/@parity/product-sdk-chain-client/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@parity/product-sdk-cloud-storage": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@parity/product-sdk-cloud-storage/-/product-sdk-cloud-storage-0.8.1.tgz", + "integrity": "sha512-yRMY8stewHA/ImbJD+PX/ao7JGniLomj3PPyPE22+aGZXpCcYwXdxMcDFOJgNtDK69Fyr483ejaew9tAFRzXNQ==", + "license": "Apache-2.0", + "dependencies": { + "@parity/bulletin-sdk": "^0.3.0", + "@parity/product-sdk-chain-client": "0.9.1", + "@parity/product-sdk-descriptors": "0.8.0", + "@parity/product-sdk-errors": "0.2.0", + "@parity/product-sdk-host": "0.14.1", + "@parity/product-sdk-logger": "0.1.1", + "@parity/product-sdk-tx": "0.3.2", + "@parity/result": "0.2.0", + "multiformats": "^13.3.0", + "polkadot-api": "^2.1.6" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@commander-js/extra-typings": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-15.0.0.tgz", + "integrity": "sha512-yeJlba62xqmkgELUsn7356MEnzLLu/fw2x4lofFqGnXh6YysRdEs2BaLeLtg1+KU0AXvMeqQvTTp+3hBEBK+EA==", + "license": "MIT", + "peerDependencies": { + "commander": "~15.0.0" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@parity/bulletin-sdk": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@parity/bulletin-sdk/-/bulletin-sdk-0.3.0.tgz", + "integrity": "sha512-sxVwBzyH/egXze1muPXbaGwQuOkP8efVB4Lxunshixf18gJ6WT2tedgUy08QOfQ1848BDQS4wVpRRQfPfb09/g==", + "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", + "dependencies": { + "@ipld/dag-pb": "^4.1.3", + "@noble/hashes": "^2.2.0", + "@polkadot-labs/hdkd-helpers": "^0.0.29", + "ipfs-unixfs": "^12.0.0" + }, + "engines": { + "node": ">=22.0.0" + }, + "peerDependencies": { + "multiformats": "^13.4.1", + "polkadot-api": "^2.1.2" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/cli": { + "version": "0.21.9", + "resolved": "https://registry.npmjs.org/@polkadot-api/cli/-/cli-0.21.9.tgz", + "integrity": "sha512-9aVdF/ocF3DBb8b/gdkcicBIaQkak2kkU4EGW+TunXLIvbIj2X9hH5geOlx8uWNwt0O4O1DIHD/nkXvycG/AcA==", + "license": "MIT", + "dependencies": { + "@commander-js/extra-typings": "^15.0.0", + "@polkadot-api/codegen": "0.22.5", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/wasm-executor": "^0.2.3", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@types/node": "^25.9.4", + "commander": "^15.0.0", + "execa": "^9.6.1", + "fs.promises.exists": "^1.1.4", + "ora": "^9.4.1", + "read-pkg": "^10.1.0", + "rollup": "^4.62.2", + "rollup-plugin-esbuild": "^6.2.1", + "rxjs": "^7.8.2", + "tsc-prog": "^2.3.0", + "typescript": "^6.0.3", + "write-package": "^7.2.0" + }, + "bin": { + "papi": "dist/main/src/main.js", + "polkadot-api": "dist/main/src/main.js" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/codegen": { + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/@polkadot-api/codegen/-/codegen-0.22.5.tgz", + "integrity": "sha512-zwZJAlviI211zhj5i6oXkrr0crrbO3GZjBd2vC9AshY1pRmyiNLV9DZsXw4E6l4JQwdAAyNWtPdnvvB9T3TpKg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/ink-contracts": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/ink-contracts/-/ink-contracts-0.6.3.tgz", + "integrity": "sha512-XqnM1VDzI5L62xgg+f8le2yEoz8QZbUKEfAfPnHMOgBj9tJiyF15FcOJmnLMO/vq3cGixqh18tzJekLG5YTxtA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.2.0.tgz", + "integrity": "sha512-lhkuBS/x06i3djQIN7p8jVkMnYuGsUAEMS6RhdWeEpz7X/8/APER4Wdih7MEBovCuwVSCTjOxl8f+alH7AZHZg==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.4.1.tgz", + "integrity": "sha512-F1Hw01C60jn98KQ0vBbgcwAvEcMcKLsJ5kFzq600sgjc6Rnbn1VF/XjTdrjcIqvfpR9sXyGIrpImrov2Usbrsw==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/known-chains": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/known-chains/-/known-chains-0.12.1.tgz", + "integrity": "sha512-ZW9TYD2y5IhgNy28zi9sByMr0CaP2I9x8HcAI0pwZR2qDC0kysCG6brDlg1UoZKCjRO99SsKO/iU1o434JgMnQ==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/logs-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/logs-provider/-/logs-provider-0.2.0.tgz", + "integrity": "sha512-BH9YdxZu+ZBPPAUwGrvqHPn1hQStL2Im3MmTwYkwXOWW2HlGHULcF65QKvyK+T6/mj2vDvl3MgivnGkUw4pVxg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/merkleize-metadata": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/merkleize-metadata/-/merkleize-metadata-1.2.3.tgz", + "integrity": "sha512-WkPbz0p2XQ9c8yXagdnwCHEB70Gnm91okcsd6IXU393//3aPgkxKgb+/Efnz7C5/KQmg02P0zXo7q/n/W/yVCA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/metadata-builders": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.14.3.tgz", + "integrity": "sha512-m7CACsiqHzgVEh5WBZGkTV8AQ3CBQKR1YpPQMnlsJfCr/IkgKU0UyWM6WxCmBiReLFVkOfXMtGlpN8+GxpHmww==", + "license": "MIT", + "dependencies": { + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/metadata-compatibility": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-compatibility/-/metadata-compatibility-0.6.3.tgz", + "integrity": "sha512-/Y0uF8nDk60ijydp8Bd37YexPFdB8hBXJWwEgOJHsVlhiny8sVKXiMg+UkJ9BiEk2z+yMbZRCKmhNpTpizo7aw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/observable-client": { + "version": "0.18.7", + "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.18.7.tgz", + "integrity": "sha512-/eQi3D8jbXLg/L1hZX4eX0/+nO3kTxkOfYeWEtFhtOvFdfqiiy2DdXG3Zg5QzgHmfG+11p4vSlLiPCC2UvS5Qw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/pjs-signer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/pjs-signer/-/pjs-signer-0.7.3.tgz", + "integrity": "sha512-U7BLFZfnpFMxCh/scJoLXT6oSbfZtZgMTkiu+TbuWQljAb/1ttrQOfshub5VihNyD5rHajp/2Fq0ONZoL5N5PA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/raw-client": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/raw-client/-/raw-client-0.3.0.tgz", + "integrity": "sha512-u/wM9W7ugIXxBSOEV8+zvQI43b5vgSI0pvE0Rg8PV0G65BTLK0SMc2o2SQL0HtS5+bi5sw/gg4reBJ/0a4U0cw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/signer": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signer/-/signer-0.3.3.tgz", + "integrity": "sha512-bmEV65TwgwbMfiecl7ZQ3k5lfkCOx9FPwPYkVvALcPiqb/d3zYwT9LL/E00hj+EB6IKMlMelEQVuchcW5i/92w==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/merkleize-metadata": "1.2.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/signers-common": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signers-common/-/signers-common-0.2.3.tgz", + "integrity": "sha512-SzGLJMxug31Y1P8+0I809ICpDrayztUXFFh2TV02GuwjEnY6mDGnmX56wowZF5yCn+WteeRdcTJTK7Whk3AGYQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/sm-provider": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@polkadot-api/sm-provider/-/sm-provider-0.3.8.tgz", + "integrity": "sha512-jIvzBNsBsh6LIpCrUOa7miZdQPwLPns5IUQyz/8v84R3ON7URSIVbtywGSR8O83BZGyW/DmVkqa5lClLE2fkqw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1" + }, + "peerDependencies": { + "@polkadot-api/smoldot": ">=0.3" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/smoldot": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/@polkadot-api/smoldot/-/smoldot-0.4.6.tgz", + "integrity": "sha512-gOXMJ10fXOub0zPP1cGAYeyf29BA2fB+qukrjXGcGN9b0Ya+lDgV7A7Q7y9JOrlGLeKbd1YdrYLAe/F43V7b9Q==", + "license": "MIT", + "dependencies": { + "@types/node": "^25.9.4", + "smoldot": "~3.3.1" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/substrate-bindings": { + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.20.3.tgz", + "integrity": "sha512-9iqC71fx1ee9ld1NZV8PFime5vryi0kt1bKCSlvNgO6dqMc06sMZuZ8WPjOzWLCHiKHLuphdMs3rVBBaeCP3yg==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/utils": "0.4.0", + "@scure/base": "^2.2.0", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/substrate-client": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.7.0.tgz", + "integrity": "sha512-TWCc4MAMa5SLVQXmomLHknbj+bztQ/Yclgwm8ENBhz8hR7c9rw9FBAkCa02jMBMCAygPhp3ayGRq+UFcF8KIxQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/raw-client": "0.3.0", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/utils": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.4.0.tgz", + "integrity": "sha512-9b/hwRM0UloLWV7SfpNaSD/4k8UQAHoaACAk7Xe+1MlfAm2JtnmPiB1GfGrfTyBlsrJVUIBCZpEmbmxVMaIqBA==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-api/ws-provider": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/ws-provider/-/ws-provider-0.9.1.tgz", + "integrity": "sha512-Ft2QJEjLZgTyKiCEbz3urSOxNfMdqNkg+QKqJhRbOFB7JtR1qMTvLE3TjM+5d2mNlEpOc3j2KDK3APNSj7uQ2Q==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/@polkadot-labs/hdkd-helpers": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@polkadot-labs/hdkd-helpers/-/hdkd-helpers-0.0.29.tgz", + "integrity": "sha512-yiLm1Gj3j5NrQV+VFMlFzkBgcRBNfq2Sd/U3S8iau2bzhDwgsn4gy6FDt94TRPD5xLxOzi1I3wSLOrgOs2eLVw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "^2.2.0", + "@noble/hashes": "^2.2.0", + "@scure/base": "^2.0.0", + "@scure/sr25519": "^1.0.0", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/polkadot-api": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/polkadot-api/-/polkadot-api-2.2.1.tgz", + "integrity": "sha512-eiZVUhI1gY4ycBtsCo/yaDXyohUeuqnyKun+bwj90ADSn+jlfjZ4sajAqIhJFwPxbCT75+Zzbln4ZjuZ5PWNWA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/cli": "0.21.9", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/logs-provider": "0.2.0", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/pjs-signer": "0.7.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signer": "0.3.3", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@rx-state/core": "^0.1.4" + }, + "bin": { + "papi": "bin/cli.js", + "polkadot-api": "bin/cli.js" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/smoldot": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-3.3.2.tgz", + "integrity": "sha512-Zl4h/0gsw8cfTZzuJ7LV7mtR6QjxltwYjMY7MsVw0oXBXrLK8zyOS6DS9Vjsy57pX1vBMg6UVhHxjbH3W905zA==", + "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", + "dependencies": { + "ws": "^8.8.1" + } + }, + "node_modules/@parity/product-sdk-cloud-storage/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@parity/product-sdk-contracts": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@parity/product-sdk-contracts/-/product-sdk-contracts-0.9.2.tgz", + "integrity": "sha512-4svBhyBOoNfV4K5f9feizZSPl1Hn5frYpJvf5hjR9z7zp+t+1ruM/DjUM5QCgwrAmwkpfw7oxHDFTN3SlBo0tw==", + "license": "Apache-2.0", + "dependencies": { + "@parity/product-sdk-address": "0.1.1", + "@parity/product-sdk-errors": "0.2.0", + "@parity/product-sdk-keys": "0.3.16", + "@parity/product-sdk-logger": "0.1.1", + "@parity/product-sdk-signer": "0.11.1", + "@parity/product-sdk-tx": "0.3.2", + "@parity/result": "0.2.0", + "@polkadot-labs/hdkd-helpers": "^0.0.30", + "polkadot-api": "^2.1.6", + "viem": "^2.52.0" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@commander-js/extra-typings": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-15.0.0.tgz", + "integrity": "sha512-yeJlba62xqmkgELUsn7356MEnzLLu/fw2x4lofFqGnXh6YysRdEs2BaLeLtg1+KU0AXvMeqQvTTp+3hBEBK+EA==", + "license": "MIT", + "peerDependencies": { + "commander": "~15.0.0" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/cli": { + "version": "0.21.9", + "resolved": "https://registry.npmjs.org/@polkadot-api/cli/-/cli-0.21.9.tgz", + "integrity": "sha512-9aVdF/ocF3DBb8b/gdkcicBIaQkak2kkU4EGW+TunXLIvbIj2X9hH5geOlx8uWNwt0O4O1DIHD/nkXvycG/AcA==", + "license": "MIT", + "dependencies": { + "@commander-js/extra-typings": "^15.0.0", + "@polkadot-api/codegen": "0.22.5", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/wasm-executor": "^0.2.3", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@types/node": "^25.9.4", + "commander": "^15.0.0", + "execa": "^9.6.1", + "fs.promises.exists": "^1.1.4", + "ora": "^9.4.1", + "read-pkg": "^10.1.0", + "rollup": "^4.62.2", + "rollup-plugin-esbuild": "^6.2.1", + "rxjs": "^7.8.2", + "tsc-prog": "^2.3.0", + "typescript": "^6.0.3", + "write-package": "^7.2.0" + }, + "bin": { + "papi": "dist/main/src/main.js", + "polkadot-api": "dist/main/src/main.js" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/codegen": { + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/@polkadot-api/codegen/-/codegen-0.22.5.tgz", + "integrity": "sha512-zwZJAlviI211zhj5i6oXkrr0crrbO3GZjBd2vC9AshY1pRmyiNLV9DZsXw4E6l4JQwdAAyNWtPdnvvB9T3TpKg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/ink-contracts": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/ink-contracts/-/ink-contracts-0.6.3.tgz", + "integrity": "sha512-XqnM1VDzI5L62xgg+f8le2yEoz8QZbUKEfAfPnHMOgBj9tJiyF15FcOJmnLMO/vq3cGixqh18tzJekLG5YTxtA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.2.0.tgz", + "integrity": "sha512-lhkuBS/x06i3djQIN7p8jVkMnYuGsUAEMS6RhdWeEpz7X/8/APER4Wdih7MEBovCuwVSCTjOxl8f+alH7AZHZg==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.4.1.tgz", + "integrity": "sha512-F1Hw01C60jn98KQ0vBbgcwAvEcMcKLsJ5kFzq600sgjc6Rnbn1VF/XjTdrjcIqvfpR9sXyGIrpImrov2Usbrsw==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/known-chains": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/known-chains/-/known-chains-0.12.1.tgz", + "integrity": "sha512-ZW9TYD2y5IhgNy28zi9sByMr0CaP2I9x8HcAI0pwZR2qDC0kysCG6brDlg1UoZKCjRO99SsKO/iU1o434JgMnQ==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/logs-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/logs-provider/-/logs-provider-0.2.0.tgz", + "integrity": "sha512-BH9YdxZu+ZBPPAUwGrvqHPn1hQStL2Im3MmTwYkwXOWW2HlGHULcF65QKvyK+T6/mj2vDvl3MgivnGkUw4pVxg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/merkleize-metadata": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/merkleize-metadata/-/merkleize-metadata-1.2.3.tgz", + "integrity": "sha512-WkPbz0p2XQ9c8yXagdnwCHEB70Gnm91okcsd6IXU393//3aPgkxKgb+/Efnz7C5/KQmg02P0zXo7q/n/W/yVCA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/metadata-builders": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.14.3.tgz", + "integrity": "sha512-m7CACsiqHzgVEh5WBZGkTV8AQ3CBQKR1YpPQMnlsJfCr/IkgKU0UyWM6WxCmBiReLFVkOfXMtGlpN8+GxpHmww==", + "license": "MIT", + "dependencies": { + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/metadata-compatibility": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-compatibility/-/metadata-compatibility-0.6.3.tgz", + "integrity": "sha512-/Y0uF8nDk60ijydp8Bd37YexPFdB8hBXJWwEgOJHsVlhiny8sVKXiMg+UkJ9BiEk2z+yMbZRCKmhNpTpizo7aw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/observable-client": { + "version": "0.18.7", + "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.18.7.tgz", + "integrity": "sha512-/eQi3D8jbXLg/L1hZX4eX0/+nO3kTxkOfYeWEtFhtOvFdfqiiy2DdXG3Zg5QzgHmfG+11p4vSlLiPCC2UvS5Qw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/pjs-signer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/pjs-signer/-/pjs-signer-0.7.3.tgz", + "integrity": "sha512-U7BLFZfnpFMxCh/scJoLXT6oSbfZtZgMTkiu+TbuWQljAb/1ttrQOfshub5VihNyD5rHajp/2Fq0ONZoL5N5PA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/raw-client": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/raw-client/-/raw-client-0.3.0.tgz", + "integrity": "sha512-u/wM9W7ugIXxBSOEV8+zvQI43b5vgSI0pvE0Rg8PV0G65BTLK0SMc2o2SQL0HtS5+bi5sw/gg4reBJ/0a4U0cw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/signer": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signer/-/signer-0.3.3.tgz", + "integrity": "sha512-bmEV65TwgwbMfiecl7ZQ3k5lfkCOx9FPwPYkVvALcPiqb/d3zYwT9LL/E00hj+EB6IKMlMelEQVuchcW5i/92w==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/merkleize-metadata": "1.2.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/signers-common": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signers-common/-/signers-common-0.2.3.tgz", + "integrity": "sha512-SzGLJMxug31Y1P8+0I809ICpDrayztUXFFh2TV02GuwjEnY6mDGnmX56wowZF5yCn+WteeRdcTJTK7Whk3AGYQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/sm-provider": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@polkadot-api/sm-provider/-/sm-provider-0.3.8.tgz", + "integrity": "sha512-jIvzBNsBsh6LIpCrUOa7miZdQPwLPns5IUQyz/8v84R3ON7URSIVbtywGSR8O83BZGyW/DmVkqa5lClLE2fkqw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1" + }, + "peerDependencies": { + "@polkadot-api/smoldot": ">=0.3" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/smoldot": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/@polkadot-api/smoldot/-/smoldot-0.4.6.tgz", + "integrity": "sha512-gOXMJ10fXOub0zPP1cGAYeyf29BA2fB+qukrjXGcGN9b0Ya+lDgV7A7Q7y9JOrlGLeKbd1YdrYLAe/F43V7b9Q==", + "license": "MIT", + "dependencies": { + "@types/node": "^25.9.4", + "smoldot": "~3.3.1" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/substrate-bindings": { + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.20.3.tgz", + "integrity": "sha512-9iqC71fx1ee9ld1NZV8PFime5vryi0kt1bKCSlvNgO6dqMc06sMZuZ8WPjOzWLCHiKHLuphdMs3rVBBaeCP3yg==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/utils": "0.4.0", + "@scure/base": "^2.2.0", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/substrate-client": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.7.0.tgz", + "integrity": "sha512-TWCc4MAMa5SLVQXmomLHknbj+bztQ/Yclgwm8ENBhz8hR7c9rw9FBAkCa02jMBMCAygPhp3ayGRq+UFcF8KIxQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/raw-client": "0.3.0", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/utils": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.4.0.tgz", + "integrity": "sha512-9b/hwRM0UloLWV7SfpNaSD/4k8UQAHoaACAk7Xe+1MlfAm2JtnmPiB1GfGrfTyBlsrJVUIBCZpEmbmxVMaIqBA==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-api/ws-provider": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/ws-provider/-/ws-provider-0.9.1.tgz", + "integrity": "sha512-Ft2QJEjLZgTyKiCEbz3urSOxNfMdqNkg+QKqJhRbOFB7JtR1qMTvLE3TjM+5d2mNlEpOc3j2KDK3APNSj7uQ2Q==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/@polkadot-labs/hdkd-helpers": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@polkadot-labs/hdkd-helpers/-/hdkd-helpers-0.0.30.tgz", + "integrity": "sha512-qWmmD6ayj14RenDuDFfjF3sHS7ObqPzwIIMPcSVoDeKFSeQV7RY0HwyhC5CG4i6FoguMzak2dbtjYpNN5XQiwQ==", + "license": "MIT", + "dependencies": { + "@noble/curves": "^2.2.0", + "@noble/hashes": "^2.2.0", + "@scure/base": "^2.2.0", + "@scure/sr25519": "^1.0.0", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/polkadot-api": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/polkadot-api/-/polkadot-api-2.2.1.tgz", + "integrity": "sha512-eiZVUhI1gY4ycBtsCo/yaDXyohUeuqnyKun+bwj90ADSn+jlfjZ4sajAqIhJFwPxbCT75+Zzbln4ZjuZ5PWNWA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/cli": "0.21.9", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/logs-provider": "0.2.0", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/pjs-signer": "0.7.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signer": "0.3.3", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@rx-state/core": "^0.1.4" + }, + "bin": { + "papi": "bin/cli.js", + "polkadot-api": "bin/cli.js" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/smoldot": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-3.3.2.tgz", + "integrity": "sha512-Zl4h/0gsw8cfTZzuJ7LV7mtR6QjxltwYjMY7MsVw0oXBXrLK8zyOS6DS9Vjsy57pX1vBMg6UVhHxjbH3W905zA==", + "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", + "dependencies": { + "ws": "^8.8.1" + } + }, + "node_modules/@parity/product-sdk-contracts/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@parity/product-sdk-crypto": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@parity/product-sdk-crypto/-/product-sdk-crypto-0.1.1.tgz", + "integrity": "sha512-No6AyTLw1Nv3ym8SDdXh/tnezdClNOL9pJgaciVr9Ny6hIL5rs6MQiXsP0+1bc1Nwymz5Q4FqsYg/htE4lejNg==", + "license": "Apache-2.0", + "dependencies": { + "@noble/ciphers": "^1.2.1", + "@noble/curves": "^1.8.0", + "@noble/hashes": "^1.7.1", + "tweetnacl": "^1.0.3" + } + }, + "node_modules/@parity/product-sdk-crypto/node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@parity/product-sdk-crypto/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@parity/product-sdk-descriptors": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@parity/product-sdk-descriptors/-/product-sdk-descriptors-0.8.0.tgz", + "integrity": "sha512-DfdrtzjBqiS4A/fnqoDOyW+KiBVKkOtfDDl1/BLHtvYxp3TanPkS952Sd28F7v1Rk/0xnqm8d7shD06/XoLqhg==", + "license": "Apache-2.0", + "dependencies": { + "polkadot-api": "^2.1.6" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@commander-js/extra-typings": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-15.0.0.tgz", + "integrity": "sha512-yeJlba62xqmkgELUsn7356MEnzLLu/fw2x4lofFqGnXh6YysRdEs2BaLeLtg1+KU0AXvMeqQvTTp+3hBEBK+EA==", + "license": "MIT", + "peerDependencies": { + "commander": "~15.0.0" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/cli": { + "version": "0.21.9", + "resolved": "https://registry.npmjs.org/@polkadot-api/cli/-/cli-0.21.9.tgz", + "integrity": "sha512-9aVdF/ocF3DBb8b/gdkcicBIaQkak2kkU4EGW+TunXLIvbIj2X9hH5geOlx8uWNwt0O4O1DIHD/nkXvycG/AcA==", + "license": "MIT", + "dependencies": { + "@commander-js/extra-typings": "^15.0.0", + "@polkadot-api/codegen": "0.22.5", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/wasm-executor": "^0.2.3", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@types/node": "^25.9.4", + "commander": "^15.0.0", + "execa": "^9.6.1", + "fs.promises.exists": "^1.1.4", + "ora": "^9.4.1", + "read-pkg": "^10.1.0", + "rollup": "^4.62.2", + "rollup-plugin-esbuild": "^6.2.1", + "rxjs": "^7.8.2", + "tsc-prog": "^2.3.0", + "typescript": "^6.0.3", + "write-package": "^7.2.0" + }, + "bin": { + "papi": "dist/main/src/main.js", + "polkadot-api": "dist/main/src/main.js" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/codegen": { + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/@polkadot-api/codegen/-/codegen-0.22.5.tgz", + "integrity": "sha512-zwZJAlviI211zhj5i6oXkrr0crrbO3GZjBd2vC9AshY1pRmyiNLV9DZsXw4E6l4JQwdAAyNWtPdnvvB9T3TpKg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/ink-contracts": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/ink-contracts/-/ink-contracts-0.6.3.tgz", + "integrity": "sha512-XqnM1VDzI5L62xgg+f8le2yEoz8QZbUKEfAfPnHMOgBj9tJiyF15FcOJmnLMO/vq3cGixqh18tzJekLG5YTxtA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.2.0.tgz", + "integrity": "sha512-lhkuBS/x06i3djQIN7p8jVkMnYuGsUAEMS6RhdWeEpz7X/8/APER4Wdih7MEBovCuwVSCTjOxl8f+alH7AZHZg==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.4.1.tgz", + "integrity": "sha512-F1Hw01C60jn98KQ0vBbgcwAvEcMcKLsJ5kFzq600sgjc6Rnbn1VF/XjTdrjcIqvfpR9sXyGIrpImrov2Usbrsw==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/known-chains": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/known-chains/-/known-chains-0.12.1.tgz", + "integrity": "sha512-ZW9TYD2y5IhgNy28zi9sByMr0CaP2I9x8HcAI0pwZR2qDC0kysCG6brDlg1UoZKCjRO99SsKO/iU1o434JgMnQ==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/logs-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/logs-provider/-/logs-provider-0.2.0.tgz", + "integrity": "sha512-BH9YdxZu+ZBPPAUwGrvqHPn1hQStL2Im3MmTwYkwXOWW2HlGHULcF65QKvyK+T6/mj2vDvl3MgivnGkUw4pVxg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/merkleize-metadata": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/merkleize-metadata/-/merkleize-metadata-1.2.3.tgz", + "integrity": "sha512-WkPbz0p2XQ9c8yXagdnwCHEB70Gnm91okcsd6IXU393//3aPgkxKgb+/Efnz7C5/KQmg02P0zXo7q/n/W/yVCA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/metadata-builders": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.14.3.tgz", + "integrity": "sha512-m7CACsiqHzgVEh5WBZGkTV8AQ3CBQKR1YpPQMnlsJfCr/IkgKU0UyWM6WxCmBiReLFVkOfXMtGlpN8+GxpHmww==", + "license": "MIT", + "dependencies": { + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/metadata-compatibility": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-compatibility/-/metadata-compatibility-0.6.3.tgz", + "integrity": "sha512-/Y0uF8nDk60ijydp8Bd37YexPFdB8hBXJWwEgOJHsVlhiny8sVKXiMg+UkJ9BiEk2z+yMbZRCKmhNpTpizo7aw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/observable-client": { + "version": "0.18.7", + "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.18.7.tgz", + "integrity": "sha512-/eQi3D8jbXLg/L1hZX4eX0/+nO3kTxkOfYeWEtFhtOvFdfqiiy2DdXG3Zg5QzgHmfG+11p4vSlLiPCC2UvS5Qw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/pjs-signer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/pjs-signer/-/pjs-signer-0.7.3.tgz", + "integrity": "sha512-U7BLFZfnpFMxCh/scJoLXT6oSbfZtZgMTkiu+TbuWQljAb/1ttrQOfshub5VihNyD5rHajp/2Fq0ONZoL5N5PA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/raw-client": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/raw-client/-/raw-client-0.3.0.tgz", + "integrity": "sha512-u/wM9W7ugIXxBSOEV8+zvQI43b5vgSI0pvE0Rg8PV0G65BTLK0SMc2o2SQL0HtS5+bi5sw/gg4reBJ/0a4U0cw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/signer": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signer/-/signer-0.3.3.tgz", + "integrity": "sha512-bmEV65TwgwbMfiecl7ZQ3k5lfkCOx9FPwPYkVvALcPiqb/d3zYwT9LL/E00hj+EB6IKMlMelEQVuchcW5i/92w==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/merkleize-metadata": "1.2.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/signers-common": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signers-common/-/signers-common-0.2.3.tgz", + "integrity": "sha512-SzGLJMxug31Y1P8+0I809ICpDrayztUXFFh2TV02GuwjEnY6mDGnmX56wowZF5yCn+WteeRdcTJTK7Whk3AGYQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/sm-provider": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@polkadot-api/sm-provider/-/sm-provider-0.3.8.tgz", + "integrity": "sha512-jIvzBNsBsh6LIpCrUOa7miZdQPwLPns5IUQyz/8v84R3ON7URSIVbtywGSR8O83BZGyW/DmVkqa5lClLE2fkqw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1" + }, + "peerDependencies": { + "@polkadot-api/smoldot": ">=0.3" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/smoldot": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/@polkadot-api/smoldot/-/smoldot-0.4.6.tgz", + "integrity": "sha512-gOXMJ10fXOub0zPP1cGAYeyf29BA2fB+qukrjXGcGN9b0Ya+lDgV7A7Q7y9JOrlGLeKbd1YdrYLAe/F43V7b9Q==", + "license": "MIT", + "dependencies": { + "@types/node": "^25.9.4", + "smoldot": "~3.3.1" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/substrate-bindings": { + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.20.3.tgz", + "integrity": "sha512-9iqC71fx1ee9ld1NZV8PFime5vryi0kt1bKCSlvNgO6dqMc06sMZuZ8WPjOzWLCHiKHLuphdMs3rVBBaeCP3yg==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/utils": "0.4.0", + "@scure/base": "^2.2.0", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/substrate-client": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.7.0.tgz", + "integrity": "sha512-TWCc4MAMa5SLVQXmomLHknbj+bztQ/Yclgwm8ENBhz8hR7c9rw9FBAkCa02jMBMCAygPhp3ayGRq+UFcF8KIxQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/raw-client": "0.3.0", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/utils": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.4.0.tgz", + "integrity": "sha512-9b/hwRM0UloLWV7SfpNaSD/4k8UQAHoaACAk7Xe+1MlfAm2JtnmPiB1GfGrfTyBlsrJVUIBCZpEmbmxVMaIqBA==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/@polkadot-api/ws-provider": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/ws-provider/-/ws-provider-0.9.1.tgz", + "integrity": "sha512-Ft2QJEjLZgTyKiCEbz3urSOxNfMdqNkg+QKqJhRbOFB7JtR1qMTvLE3TjM+5d2mNlEpOc3j2KDK3APNSj7uQ2Q==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/polkadot-api": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/polkadot-api/-/polkadot-api-2.2.1.tgz", + "integrity": "sha512-eiZVUhI1gY4ycBtsCo/yaDXyohUeuqnyKun+bwj90ADSn+jlfjZ4sajAqIhJFwPxbCT75+Zzbln4ZjuZ5PWNWA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/cli": "0.21.9", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/logs-provider": "0.2.0", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/pjs-signer": "0.7.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signer": "0.3.3", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@rx-state/core": "^0.1.4" + }, + "bin": { + "papi": "bin/cli.js", + "polkadot-api": "bin/cli.js" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/smoldot": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-3.3.2.tgz", + "integrity": "sha512-Zl4h/0gsw8cfTZzuJ7LV7mtR6QjxltwYjMY7MsVw0oXBXrLK8zyOS6DS9Vjsy57pX1vBMg6UVhHxjbH3W905zA==", + "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", + "dependencies": { + "ws": "^8.8.1" + } + }, + "node_modules/@parity/product-sdk-descriptors/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@parity/product-sdk-errors": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@parity/product-sdk-errors/-/product-sdk-errors-0.2.0.tgz", + "integrity": "sha512-2rvJV0iJyNAxSjm+RHcoch3GRGYgfMDd2wCha+LmykIDZ06oUfFo+wY6Jf8z56ZMMqHvBvDO1ZNrstVgUZxlEQ==", + "license": "Apache-2.0" + }, + "node_modules/@parity/product-sdk-host": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@parity/product-sdk-host/-/product-sdk-host-0.14.1.tgz", + "integrity": "sha512-PF87O0Kb35TyZo+sDlcYo9ZvaUedR8NNbcZvfBf8PBL65Yck10jIDuXE386hl9pgpgOn6o0Y1z6iJfEolhNXsg==", + "license": "Apache-2.0", + "dependencies": { + "@parity/product-sdk-errors": "0.2.0", + "@parity/product-sdk-logger": "0.1.1", + "@parity/result": "0.2.0", + "@parity/truapi": "^0.5.0", + "@polkadot-api/json-rpc-provider": "^0.2.0", + "@polkadot-api/substrate-bindings": "^0.20.3", + "neverthrow": "^8.2.0", + "polkadot-api": "^2.1.6" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@commander-js/extra-typings": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-15.0.0.tgz", + "integrity": "sha512-yeJlba62xqmkgELUsn7356MEnzLLu/fw2x4lofFqGnXh6YysRdEs2BaLeLtg1+KU0AXvMeqQvTTp+3hBEBK+EA==", + "license": "MIT", + "peerDependencies": { + "commander": "~15.0.0" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/cli": { + "version": "0.21.9", + "resolved": "https://registry.npmjs.org/@polkadot-api/cli/-/cli-0.21.9.tgz", + "integrity": "sha512-9aVdF/ocF3DBb8b/gdkcicBIaQkak2kkU4EGW+TunXLIvbIj2X9hH5geOlx8uWNwt0O4O1DIHD/nkXvycG/AcA==", + "license": "MIT", + "dependencies": { + "@commander-js/extra-typings": "^15.0.0", + "@polkadot-api/codegen": "0.22.5", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/wasm-executor": "^0.2.3", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@types/node": "^25.9.4", + "commander": "^15.0.0", + "execa": "^9.6.1", + "fs.promises.exists": "^1.1.4", + "ora": "^9.4.1", + "read-pkg": "^10.1.0", + "rollup": "^4.62.2", + "rollup-plugin-esbuild": "^6.2.1", + "rxjs": "^7.8.2", + "tsc-prog": "^2.3.0", + "typescript": "^6.0.3", + "write-package": "^7.2.0" + }, + "bin": { + "papi": "dist/main/src/main.js", + "polkadot-api": "dist/main/src/main.js" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/codegen": { + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/@polkadot-api/codegen/-/codegen-0.22.5.tgz", + "integrity": "sha512-zwZJAlviI211zhj5i6oXkrr0crrbO3GZjBd2vC9AshY1pRmyiNLV9DZsXw4E6l4JQwdAAyNWtPdnvvB9T3TpKg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/ink-contracts": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/ink-contracts/-/ink-contracts-0.6.3.tgz", + "integrity": "sha512-XqnM1VDzI5L62xgg+f8le2yEoz8QZbUKEfAfPnHMOgBj9tJiyF15FcOJmnLMO/vq3cGixqh18tzJekLG5YTxtA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.2.0.tgz", + "integrity": "sha512-lhkuBS/x06i3djQIN7p8jVkMnYuGsUAEMS6RhdWeEpz7X/8/APER4Wdih7MEBovCuwVSCTjOxl8f+alH7AZHZg==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.4.1.tgz", + "integrity": "sha512-F1Hw01C60jn98KQ0vBbgcwAvEcMcKLsJ5kFzq600sgjc6Rnbn1VF/XjTdrjcIqvfpR9sXyGIrpImrov2Usbrsw==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/known-chains": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/known-chains/-/known-chains-0.12.1.tgz", + "integrity": "sha512-ZW9TYD2y5IhgNy28zi9sByMr0CaP2I9x8HcAI0pwZR2qDC0kysCG6brDlg1UoZKCjRO99SsKO/iU1o434JgMnQ==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/logs-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/logs-provider/-/logs-provider-0.2.0.tgz", + "integrity": "sha512-BH9YdxZu+ZBPPAUwGrvqHPn1hQStL2Im3MmTwYkwXOWW2HlGHULcF65QKvyK+T6/mj2vDvl3MgivnGkUw4pVxg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/merkleize-metadata": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/merkleize-metadata/-/merkleize-metadata-1.2.3.tgz", + "integrity": "sha512-WkPbz0p2XQ9c8yXagdnwCHEB70Gnm91okcsd6IXU393//3aPgkxKgb+/Efnz7C5/KQmg02P0zXo7q/n/W/yVCA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/metadata-builders": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.14.3.tgz", + "integrity": "sha512-m7CACsiqHzgVEh5WBZGkTV8AQ3CBQKR1YpPQMnlsJfCr/IkgKU0UyWM6WxCmBiReLFVkOfXMtGlpN8+GxpHmww==", + "license": "MIT", + "dependencies": { + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/metadata-compatibility": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-compatibility/-/metadata-compatibility-0.6.3.tgz", + "integrity": "sha512-/Y0uF8nDk60ijydp8Bd37YexPFdB8hBXJWwEgOJHsVlhiny8sVKXiMg+UkJ9BiEk2z+yMbZRCKmhNpTpizo7aw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/observable-client": { + "version": "0.18.7", + "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.18.7.tgz", + "integrity": "sha512-/eQi3D8jbXLg/L1hZX4eX0/+nO3kTxkOfYeWEtFhtOvFdfqiiy2DdXG3Zg5QzgHmfG+11p4vSlLiPCC2UvS5Qw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/pjs-signer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/pjs-signer/-/pjs-signer-0.7.3.tgz", + "integrity": "sha512-U7BLFZfnpFMxCh/scJoLXT6oSbfZtZgMTkiu+TbuWQljAb/1ttrQOfshub5VihNyD5rHajp/2Fq0ONZoL5N5PA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/raw-client": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/raw-client/-/raw-client-0.3.0.tgz", + "integrity": "sha512-u/wM9W7ugIXxBSOEV8+zvQI43b5vgSI0pvE0Rg8PV0G65BTLK0SMc2o2SQL0HtS5+bi5sw/gg4reBJ/0a4U0cw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/signer": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signer/-/signer-0.3.3.tgz", + "integrity": "sha512-bmEV65TwgwbMfiecl7ZQ3k5lfkCOx9FPwPYkVvALcPiqb/d3zYwT9LL/E00hj+EB6IKMlMelEQVuchcW5i/92w==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/merkleize-metadata": "1.2.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/signers-common": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signers-common/-/signers-common-0.2.3.tgz", + "integrity": "sha512-SzGLJMxug31Y1P8+0I809ICpDrayztUXFFh2TV02GuwjEnY6mDGnmX56wowZF5yCn+WteeRdcTJTK7Whk3AGYQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/sm-provider": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@polkadot-api/sm-provider/-/sm-provider-0.3.8.tgz", + "integrity": "sha512-jIvzBNsBsh6LIpCrUOa7miZdQPwLPns5IUQyz/8v84R3ON7URSIVbtywGSR8O83BZGyW/DmVkqa5lClLE2fkqw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1" + }, + "peerDependencies": { + "@polkadot-api/smoldot": ">=0.3" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/smoldot": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/@polkadot-api/smoldot/-/smoldot-0.4.6.tgz", + "integrity": "sha512-gOXMJ10fXOub0zPP1cGAYeyf29BA2fB+qukrjXGcGN9b0Ya+lDgV7A7Q7y9JOrlGLeKbd1YdrYLAe/F43V7b9Q==", + "license": "MIT", + "dependencies": { + "@types/node": "^25.9.4", + "smoldot": "~3.3.1" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/substrate-bindings": { + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.20.3.tgz", + "integrity": "sha512-9iqC71fx1ee9ld1NZV8PFime5vryi0kt1bKCSlvNgO6dqMc06sMZuZ8WPjOzWLCHiKHLuphdMs3rVBBaeCP3yg==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/utils": "0.4.0", + "@scure/base": "^2.2.0", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/substrate-client": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.7.0.tgz", + "integrity": "sha512-TWCc4MAMa5SLVQXmomLHknbj+bztQ/Yclgwm8ENBhz8hR7c9rw9FBAkCa02jMBMCAygPhp3ayGRq+UFcF8KIxQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/raw-client": "0.3.0", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/utils": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.4.0.tgz", + "integrity": "sha512-9b/hwRM0UloLWV7SfpNaSD/4k8UQAHoaACAk7Xe+1MlfAm2JtnmPiB1GfGrfTyBlsrJVUIBCZpEmbmxVMaIqBA==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-host/node_modules/@polkadot-api/ws-provider": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/ws-provider/-/ws-provider-0.9.1.tgz", + "integrity": "sha512-Ft2QJEjLZgTyKiCEbz3urSOxNfMdqNkg+QKqJhRbOFB7JtR1qMTvLE3TjM+5d2mNlEpOc3j2KDK3APNSj7uQ2Q==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/polkadot-api": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/polkadot-api/-/polkadot-api-2.2.1.tgz", + "integrity": "sha512-eiZVUhI1gY4ycBtsCo/yaDXyohUeuqnyKun+bwj90ADSn+jlfjZ4sajAqIhJFwPxbCT75+Zzbln4ZjuZ5PWNWA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/cli": "0.21.9", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/logs-provider": "0.2.0", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/pjs-signer": "0.7.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signer": "0.3.3", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@rx-state/core": "^0.1.4" + }, + "bin": { + "papi": "bin/cli.js", + "polkadot-api": "bin/cli.js" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/smoldot": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-3.3.2.tgz", + "integrity": "sha512-Zl4h/0gsw8cfTZzuJ7LV7mtR6QjxltwYjMY7MsVw0oXBXrLK8zyOS6DS9Vjsy57pX1vBMg6UVhHxjbH3W905zA==", + "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", + "dependencies": { + "ws": "^8.8.1" + } + }, + "node_modules/@parity/product-sdk-host/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@parity/product-sdk-keys": { + "version": "0.3.16", + "resolved": "https://registry.npmjs.org/@parity/product-sdk-keys/-/product-sdk-keys-0.3.16.tgz", + "integrity": "sha512-dnaHPQVOyxE7yEET/NsHi/0l8rI+vkH0m1OaZQ+qMkv4zwrFqXbhcXO7z6Kj+RHp5hswkmrcpEmjl4DeV5Z2xQ==", + "license": "Apache-2.0", + "dependencies": { + "@parity/product-sdk-address": "0.1.1", + "@parity/product-sdk-crypto": "0.1.1", + "@parity/product-sdk-local-storage": "0.3.2", + "@polkadot-labs/hdkd": "^0.0.28", + "@polkadot-labs/hdkd-helpers": "^0.0.30", + "@scure/sr25519": "^2.2.0", + "polkadot-api": "^2.1.6", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@commander-js/extra-typings": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-15.0.0.tgz", + "integrity": "sha512-yeJlba62xqmkgELUsn7356MEnzLLu/fw2x4lofFqGnXh6YysRdEs2BaLeLtg1+KU0AXvMeqQvTTp+3hBEBK+EA==", + "license": "MIT", + "peerDependencies": { + "commander": "~15.0.0" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/cli": { + "version": "0.21.9", + "resolved": "https://registry.npmjs.org/@polkadot-api/cli/-/cli-0.21.9.tgz", + "integrity": "sha512-9aVdF/ocF3DBb8b/gdkcicBIaQkak2kkU4EGW+TunXLIvbIj2X9hH5geOlx8uWNwt0O4O1DIHD/nkXvycG/AcA==", + "license": "MIT", + "dependencies": { + "@commander-js/extra-typings": "^15.0.0", + "@polkadot-api/codegen": "0.22.5", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/wasm-executor": "^0.2.3", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@types/node": "^25.9.4", + "commander": "^15.0.0", + "execa": "^9.6.1", + "fs.promises.exists": "^1.1.4", + "ora": "^9.4.1", + "read-pkg": "^10.1.0", + "rollup": "^4.62.2", + "rollup-plugin-esbuild": "^6.2.1", + "rxjs": "^7.8.2", + "tsc-prog": "^2.3.0", + "typescript": "^6.0.3", + "write-package": "^7.2.0" + }, + "bin": { + "papi": "dist/main/src/main.js", + "polkadot-api": "dist/main/src/main.js" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/codegen": { + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/@polkadot-api/codegen/-/codegen-0.22.5.tgz", + "integrity": "sha512-zwZJAlviI211zhj5i6oXkrr0crrbO3GZjBd2vC9AshY1pRmyiNLV9DZsXw4E6l4JQwdAAyNWtPdnvvB9T3TpKg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/ink-contracts": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/ink-contracts/-/ink-contracts-0.6.3.tgz", + "integrity": "sha512-XqnM1VDzI5L62xgg+f8le2yEoz8QZbUKEfAfPnHMOgBj9tJiyF15FcOJmnLMO/vq3cGixqh18tzJekLG5YTxtA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.2.0.tgz", + "integrity": "sha512-lhkuBS/x06i3djQIN7p8jVkMnYuGsUAEMS6RhdWeEpz7X/8/APER4Wdih7MEBovCuwVSCTjOxl8f+alH7AZHZg==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.4.1.tgz", + "integrity": "sha512-F1Hw01C60jn98KQ0vBbgcwAvEcMcKLsJ5kFzq600sgjc6Rnbn1VF/XjTdrjcIqvfpR9sXyGIrpImrov2Usbrsw==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/known-chains": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/known-chains/-/known-chains-0.12.1.tgz", + "integrity": "sha512-ZW9TYD2y5IhgNy28zi9sByMr0CaP2I9x8HcAI0pwZR2qDC0kysCG6brDlg1UoZKCjRO99SsKO/iU1o434JgMnQ==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/logs-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/logs-provider/-/logs-provider-0.2.0.tgz", + "integrity": "sha512-BH9YdxZu+ZBPPAUwGrvqHPn1hQStL2Im3MmTwYkwXOWW2HlGHULcF65QKvyK+T6/mj2vDvl3MgivnGkUw4pVxg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/merkleize-metadata": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/merkleize-metadata/-/merkleize-metadata-1.2.3.tgz", + "integrity": "sha512-WkPbz0p2XQ9c8yXagdnwCHEB70Gnm91okcsd6IXU393//3aPgkxKgb+/Efnz7C5/KQmg02P0zXo7q/n/W/yVCA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/metadata-builders": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.14.3.tgz", + "integrity": "sha512-m7CACsiqHzgVEh5WBZGkTV8AQ3CBQKR1YpPQMnlsJfCr/IkgKU0UyWM6WxCmBiReLFVkOfXMtGlpN8+GxpHmww==", + "license": "MIT", + "dependencies": { + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/metadata-compatibility": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-compatibility/-/metadata-compatibility-0.6.3.tgz", + "integrity": "sha512-/Y0uF8nDk60ijydp8Bd37YexPFdB8hBXJWwEgOJHsVlhiny8sVKXiMg+UkJ9BiEk2z+yMbZRCKmhNpTpizo7aw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/observable-client": { + "version": "0.18.7", + "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.18.7.tgz", + "integrity": "sha512-/eQi3D8jbXLg/L1hZX4eX0/+nO3kTxkOfYeWEtFhtOvFdfqiiy2DdXG3Zg5QzgHmfG+11p4vSlLiPCC2UvS5Qw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/pjs-signer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/pjs-signer/-/pjs-signer-0.7.3.tgz", + "integrity": "sha512-U7BLFZfnpFMxCh/scJoLXT6oSbfZtZgMTkiu+TbuWQljAb/1ttrQOfshub5VihNyD5rHajp/2Fq0ONZoL5N5PA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/raw-client": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/raw-client/-/raw-client-0.3.0.tgz", + "integrity": "sha512-u/wM9W7ugIXxBSOEV8+zvQI43b5vgSI0pvE0Rg8PV0G65BTLK0SMc2o2SQL0HtS5+bi5sw/gg4reBJ/0a4U0cw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/signer": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signer/-/signer-0.3.3.tgz", + "integrity": "sha512-bmEV65TwgwbMfiecl7ZQ3k5lfkCOx9FPwPYkVvALcPiqb/d3zYwT9LL/E00hj+EB6IKMlMelEQVuchcW5i/92w==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/merkleize-metadata": "1.2.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/signers-common": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signers-common/-/signers-common-0.2.3.tgz", + "integrity": "sha512-SzGLJMxug31Y1P8+0I809ICpDrayztUXFFh2TV02GuwjEnY6mDGnmX56wowZF5yCn+WteeRdcTJTK7Whk3AGYQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/sm-provider": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@polkadot-api/sm-provider/-/sm-provider-0.3.8.tgz", + "integrity": "sha512-jIvzBNsBsh6LIpCrUOa7miZdQPwLPns5IUQyz/8v84R3ON7URSIVbtywGSR8O83BZGyW/DmVkqa5lClLE2fkqw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1" + }, + "peerDependencies": { + "@polkadot-api/smoldot": ">=0.3" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/smoldot": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/@polkadot-api/smoldot/-/smoldot-0.4.6.tgz", + "integrity": "sha512-gOXMJ10fXOub0zPP1cGAYeyf29BA2fB+qukrjXGcGN9b0Ya+lDgV7A7Q7y9JOrlGLeKbd1YdrYLAe/F43V7b9Q==", + "license": "MIT", + "dependencies": { + "@types/node": "^25.9.4", + "smoldot": "~3.3.1" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/substrate-bindings": { + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.20.3.tgz", + "integrity": "sha512-9iqC71fx1ee9ld1NZV8PFime5vryi0kt1bKCSlvNgO6dqMc06sMZuZ8WPjOzWLCHiKHLuphdMs3rVBBaeCP3yg==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/utils": "0.4.0", + "@scure/base": "^2.2.0", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/substrate-client": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.7.0.tgz", + "integrity": "sha512-TWCc4MAMa5SLVQXmomLHknbj+bztQ/Yclgwm8ENBhz8hR7c9rw9FBAkCa02jMBMCAygPhp3ayGRq+UFcF8KIxQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/raw-client": "0.3.0", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/utils": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.4.0.tgz", + "integrity": "sha512-9b/hwRM0UloLWV7SfpNaSD/4k8UQAHoaACAk7Xe+1MlfAm2JtnmPiB1GfGrfTyBlsrJVUIBCZpEmbmxVMaIqBA==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-api/ws-provider": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/ws-provider/-/ws-provider-0.9.1.tgz", + "integrity": "sha512-Ft2QJEjLZgTyKiCEbz3urSOxNfMdqNkg+QKqJhRbOFB7JtR1qMTvLE3TjM+5d2mNlEpOc3j2KDK3APNSj7uQ2Q==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-labs/hdkd": { + "version": "0.0.28", + "resolved": "https://registry.npmjs.org/@polkadot-labs/hdkd/-/hdkd-0.0.28.tgz", + "integrity": "sha512-LpdqtQRpcgZQ5Mr8J0ddMA5ZufsbI4W3KuJkVdoYMnSmWs4179LigDb1rTYAOtyCg2jWUjf7rWP0mGxQQvNrHw==", + "license": "MIT", + "dependencies": { + "@polkadot-labs/hdkd-helpers": "~0.0.29" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-labs/hdkd-helpers": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@polkadot-labs/hdkd-helpers/-/hdkd-helpers-0.0.30.tgz", + "integrity": "sha512-qWmmD6ayj14RenDuDFfjF3sHS7ObqPzwIIMPcSVoDeKFSeQV7RY0HwyhC5CG4i6FoguMzak2dbtjYpNN5XQiwQ==", + "license": "MIT", + "dependencies": { + "@noble/curves": "^2.2.0", + "@noble/hashes": "^2.2.0", + "@scure/base": "^2.2.0", + "@scure/sr25519": "^1.0.0", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-labs/hdkd-helpers/node_modules/@scure/sr25519": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@scure/sr25519/-/sr25519-1.0.0.tgz", + "integrity": "sha512-b+uhK5akMINXZP95F3gJGcb5CMKYxf+q55fwMl0GoBwZDbWolmGNi1FrBSwuaZX5AhqS2byHiAueZgtDNpot2A==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~2.0.0", + "@noble/hashes": "~2.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-labs/hdkd-helpers/node_modules/@scure/sr25519/node_modules/@noble/curves": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz", + "integrity": "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "2.0.1" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@polkadot-labs/hdkd-helpers/node_modules/@scure/sr25519/node_modules/@noble/hashes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", + "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/@scure/sr25519": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@scure/sr25519/-/sr25519-2.2.0.tgz", + "integrity": "sha512-UTOZb6Hzw44REQdl2SWNBhBFIoqOIhMLNIz3zYyVQLbqdshhuyuuxYoibKHlDg9oqdwdCHQe5LkTsevugPpUbw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~2.2.0", + "@noble/hashes": "~2.2.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/polkadot-api": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/polkadot-api/-/polkadot-api-2.2.1.tgz", + "integrity": "sha512-eiZVUhI1gY4ycBtsCo/yaDXyohUeuqnyKun+bwj90ADSn+jlfjZ4sajAqIhJFwPxbCT75+Zzbln4ZjuZ5PWNWA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/cli": "0.21.9", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/logs-provider": "0.2.0", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/pjs-signer": "0.7.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signer": "0.3.3", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@rx-state/core": "^0.1.4" + }, + "bin": { + "papi": "bin/cli.js", + "polkadot-api": "bin/cli.js" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/smoldot": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-3.3.2.tgz", + "integrity": "sha512-Zl4h/0gsw8cfTZzuJ7LV7mtR6QjxltwYjMY7MsVw0oXBXrLK8zyOS6DS9Vjsy57pX1vBMg6UVhHxjbH3W905zA==", + "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", + "dependencies": { + "ws": "^8.8.1" + } + }, + "node_modules/@parity/product-sdk-keys/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@parity/product-sdk-local-storage": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@parity/product-sdk-local-storage/-/product-sdk-local-storage-0.3.2.tgz", + "integrity": "sha512-1KJGOZrf6pj1P19j8AVbVC5NTmQe5KGE2VJ0gzJDYCHGWPYECtm7Fc0zfq6AAZbyPJkgsuZz/K4+MRijf4lf2A==", + "license": "Apache-2.0", + "dependencies": { + "@parity/product-sdk-host": "0.14.1", + "@parity/product-sdk-logger": "0.1.1" + } + }, + "node_modules/@parity/product-sdk-logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@parity/product-sdk-logger/-/product-sdk-logger-0.1.1.tgz", + "integrity": "sha512-AiSV3TTNlMZJftLQsO78BZsEymGFuJtGMSpGrJ+vUtqaZavWaW/Hc6MICBLnEYgeCrdNpv7QBso3dRsTfnAZXQ==", + "license": "Apache-2.0" + }, + "node_modules/@parity/product-sdk-signer": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/@parity/product-sdk-signer/-/product-sdk-signer-0.11.1.tgz", + "integrity": "sha512-9wGaazcmqVaSkJckcZhHFkhpPQJSNVgvFRbH2qIXkvAmxKMfW9xzdplsEoMnxRcvUree6I1YK2m3kn61/dBpjw==", + "license": "Apache-2.0", + "dependencies": { + "@parity/product-sdk-address": "0.1.1", + "@parity/product-sdk-errors": "0.2.0", + "@parity/product-sdk-host": "0.14.1", + "@parity/product-sdk-keys": "0.3.16", + "@parity/product-sdk-logger": "0.1.1", + "@parity/result": "0.2.0", + "polkadot-api": "^2.1.6" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@commander-js/extra-typings": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-15.0.0.tgz", + "integrity": "sha512-yeJlba62xqmkgELUsn7356MEnzLLu/fw2x4lofFqGnXh6YysRdEs2BaLeLtg1+KU0AXvMeqQvTTp+3hBEBK+EA==", + "license": "MIT", + "peerDependencies": { + "commander": "~15.0.0" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/cli": { + "version": "0.21.9", + "resolved": "https://registry.npmjs.org/@polkadot-api/cli/-/cli-0.21.9.tgz", + "integrity": "sha512-9aVdF/ocF3DBb8b/gdkcicBIaQkak2kkU4EGW+TunXLIvbIj2X9hH5geOlx8uWNwt0O4O1DIHD/nkXvycG/AcA==", + "license": "MIT", + "dependencies": { + "@commander-js/extra-typings": "^15.0.0", + "@polkadot-api/codegen": "0.22.5", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/wasm-executor": "^0.2.3", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@types/node": "^25.9.4", + "commander": "^15.0.0", + "execa": "^9.6.1", + "fs.promises.exists": "^1.1.4", + "ora": "^9.4.1", + "read-pkg": "^10.1.0", + "rollup": "^4.62.2", + "rollup-plugin-esbuild": "^6.2.1", + "rxjs": "^7.8.2", + "tsc-prog": "^2.3.0", + "typescript": "^6.0.3", + "write-package": "^7.2.0" + }, + "bin": { + "papi": "dist/main/src/main.js", + "polkadot-api": "dist/main/src/main.js" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/codegen": { + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/@polkadot-api/codegen/-/codegen-0.22.5.tgz", + "integrity": "sha512-zwZJAlviI211zhj5i6oXkrr0crrbO3GZjBd2vC9AshY1pRmyiNLV9DZsXw4E6l4JQwdAAyNWtPdnvvB9T3TpKg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/ink-contracts": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/ink-contracts/-/ink-contracts-0.6.3.tgz", + "integrity": "sha512-XqnM1VDzI5L62xgg+f8le2yEoz8QZbUKEfAfPnHMOgBj9tJiyF15FcOJmnLMO/vq3cGixqh18tzJekLG5YTxtA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.2.0.tgz", + "integrity": "sha512-lhkuBS/x06i3djQIN7p8jVkMnYuGsUAEMS6RhdWeEpz7X/8/APER4Wdih7MEBovCuwVSCTjOxl8f+alH7AZHZg==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.4.1.tgz", + "integrity": "sha512-F1Hw01C60jn98KQ0vBbgcwAvEcMcKLsJ5kFzq600sgjc6Rnbn1VF/XjTdrjcIqvfpR9sXyGIrpImrov2Usbrsw==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/known-chains": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/known-chains/-/known-chains-0.12.1.tgz", + "integrity": "sha512-ZW9TYD2y5IhgNy28zi9sByMr0CaP2I9x8HcAI0pwZR2qDC0kysCG6brDlg1UoZKCjRO99SsKO/iU1o434JgMnQ==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/logs-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/logs-provider/-/logs-provider-0.2.0.tgz", + "integrity": "sha512-BH9YdxZu+ZBPPAUwGrvqHPn1hQStL2Im3MmTwYkwXOWW2HlGHULcF65QKvyK+T6/mj2vDvl3MgivnGkUw4pVxg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/merkleize-metadata": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/merkleize-metadata/-/merkleize-metadata-1.2.3.tgz", + "integrity": "sha512-WkPbz0p2XQ9c8yXagdnwCHEB70Gnm91okcsd6IXU393//3aPgkxKgb+/Efnz7C5/KQmg02P0zXo7q/n/W/yVCA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/metadata-builders": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.14.3.tgz", + "integrity": "sha512-m7CACsiqHzgVEh5WBZGkTV8AQ3CBQKR1YpPQMnlsJfCr/IkgKU0UyWM6WxCmBiReLFVkOfXMtGlpN8+GxpHmww==", + "license": "MIT", + "dependencies": { + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/metadata-compatibility": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-compatibility/-/metadata-compatibility-0.6.3.tgz", + "integrity": "sha512-/Y0uF8nDk60ijydp8Bd37YexPFdB8hBXJWwEgOJHsVlhiny8sVKXiMg+UkJ9BiEk2z+yMbZRCKmhNpTpizo7aw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/observable-client": { + "version": "0.18.7", + "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.18.7.tgz", + "integrity": "sha512-/eQi3D8jbXLg/L1hZX4eX0/+nO3kTxkOfYeWEtFhtOvFdfqiiy2DdXG3Zg5QzgHmfG+11p4vSlLiPCC2UvS5Qw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/pjs-signer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/pjs-signer/-/pjs-signer-0.7.3.tgz", + "integrity": "sha512-U7BLFZfnpFMxCh/scJoLXT6oSbfZtZgMTkiu+TbuWQljAb/1ttrQOfshub5VihNyD5rHajp/2Fq0ONZoL5N5PA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/raw-client": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/raw-client/-/raw-client-0.3.0.tgz", + "integrity": "sha512-u/wM9W7ugIXxBSOEV8+zvQI43b5vgSI0pvE0Rg8PV0G65BTLK0SMc2o2SQL0HtS5+bi5sw/gg4reBJ/0a4U0cw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/signer": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signer/-/signer-0.3.3.tgz", + "integrity": "sha512-bmEV65TwgwbMfiecl7ZQ3k5lfkCOx9FPwPYkVvALcPiqb/d3zYwT9LL/E00hj+EB6IKMlMelEQVuchcW5i/92w==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/merkleize-metadata": "1.2.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/signers-common": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signers-common/-/signers-common-0.2.3.tgz", + "integrity": "sha512-SzGLJMxug31Y1P8+0I809ICpDrayztUXFFh2TV02GuwjEnY6mDGnmX56wowZF5yCn+WteeRdcTJTK7Whk3AGYQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/sm-provider": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@polkadot-api/sm-provider/-/sm-provider-0.3.8.tgz", + "integrity": "sha512-jIvzBNsBsh6LIpCrUOa7miZdQPwLPns5IUQyz/8v84R3ON7URSIVbtywGSR8O83BZGyW/DmVkqa5lClLE2fkqw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1" + }, + "peerDependencies": { + "@polkadot-api/smoldot": ">=0.3" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/smoldot": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/@polkadot-api/smoldot/-/smoldot-0.4.6.tgz", + "integrity": "sha512-gOXMJ10fXOub0zPP1cGAYeyf29BA2fB+qukrjXGcGN9b0Ya+lDgV7A7Q7y9JOrlGLeKbd1YdrYLAe/F43V7b9Q==", + "license": "MIT", + "dependencies": { + "@types/node": "^25.9.4", + "smoldot": "~3.3.1" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/substrate-bindings": { + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.20.3.tgz", + "integrity": "sha512-9iqC71fx1ee9ld1NZV8PFime5vryi0kt1bKCSlvNgO6dqMc06sMZuZ8WPjOzWLCHiKHLuphdMs3rVBBaeCP3yg==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/utils": "0.4.0", + "@scure/base": "^2.2.0", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/substrate-client": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.7.0.tgz", + "integrity": "sha512-TWCc4MAMa5SLVQXmomLHknbj+bztQ/Yclgwm8ENBhz8hR7c9rw9FBAkCa02jMBMCAygPhp3ayGRq+UFcF8KIxQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/raw-client": "0.3.0", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/utils": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.4.0.tgz", + "integrity": "sha512-9b/hwRM0UloLWV7SfpNaSD/4k8UQAHoaACAk7Xe+1MlfAm2JtnmPiB1GfGrfTyBlsrJVUIBCZpEmbmxVMaIqBA==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-signer/node_modules/@polkadot-api/ws-provider": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/ws-provider/-/ws-provider-0.9.1.tgz", + "integrity": "sha512-Ft2QJEjLZgTyKiCEbz3urSOxNfMdqNkg+QKqJhRbOFB7JtR1qMTvLE3TjM+5d2mNlEpOc3j2KDK3APNSj7uQ2Q==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/polkadot-api": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/polkadot-api/-/polkadot-api-2.2.1.tgz", + "integrity": "sha512-eiZVUhI1gY4ycBtsCo/yaDXyohUeuqnyKun+bwj90ADSn+jlfjZ4sajAqIhJFwPxbCT75+Zzbln4ZjuZ5PWNWA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/cli": "0.21.9", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/logs-provider": "0.2.0", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/pjs-signer": "0.7.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signer": "0.3.3", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@rx-state/core": "^0.1.4" + }, + "bin": { + "papi": "bin/cli.js", + "polkadot-api": "bin/cli.js" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/smoldot": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-3.3.2.tgz", + "integrity": "sha512-Zl4h/0gsw8cfTZzuJ7LV7mtR6QjxltwYjMY7MsVw0oXBXrLK8zyOS6DS9Vjsy57pX1vBMg6UVhHxjbH3W905zA==", + "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", + "dependencies": { + "ws": "^8.8.1" + } + }, + "node_modules/@parity/product-sdk-signer/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@parity/product-sdk-tx": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@parity/product-sdk-tx/-/product-sdk-tx-0.3.2.tgz", + "integrity": "sha512-Y10Sw/ZluIAA6+zB9Ty+y0bSwbrEVIeBKN3umrQXHyseDfBmxWEXkwlhjCxnusJ44wtUgrIN9BsNRGI/51ffKQ==", + "license": "Apache-2.0", + "dependencies": { + "@parity/product-sdk-errors": "0.2.0", + "@parity/product-sdk-keys": "0.3.16", + "@parity/product-sdk-logger": "0.1.1", + "@parity/result": "0.2.0", + "@polkadot-labs/hdkd-helpers": "^0.0.30", + "polkadot-api": "^2.1.6" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@commander-js/extra-typings": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-15.0.0.tgz", + "integrity": "sha512-yeJlba62xqmkgELUsn7356MEnzLLu/fw2x4lofFqGnXh6YysRdEs2BaLeLtg1+KU0AXvMeqQvTTp+3hBEBK+EA==", + "license": "MIT", + "peerDependencies": { + "commander": "~15.0.0" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/cli": { + "version": "0.21.9", + "resolved": "https://registry.npmjs.org/@polkadot-api/cli/-/cli-0.21.9.tgz", + "integrity": "sha512-9aVdF/ocF3DBb8b/gdkcicBIaQkak2kkU4EGW+TunXLIvbIj2X9hH5geOlx8uWNwt0O4O1DIHD/nkXvycG/AcA==", + "license": "MIT", + "dependencies": { + "@commander-js/extra-typings": "^15.0.0", + "@polkadot-api/codegen": "0.22.5", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/wasm-executor": "^0.2.3", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@types/node": "^25.9.4", + "commander": "^15.0.0", + "execa": "^9.6.1", + "fs.promises.exists": "^1.1.4", + "ora": "^9.4.1", + "read-pkg": "^10.1.0", + "rollup": "^4.62.2", + "rollup-plugin-esbuild": "^6.2.1", + "rxjs": "^7.8.2", + "tsc-prog": "^2.3.0", + "typescript": "^6.0.3", + "write-package": "^7.2.0" + }, + "bin": { + "papi": "dist/main/src/main.js", + "polkadot-api": "dist/main/src/main.js" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/codegen": { + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/@polkadot-api/codegen/-/codegen-0.22.5.tgz", + "integrity": "sha512-zwZJAlviI211zhj5i6oXkrr0crrbO3GZjBd2vC9AshY1pRmyiNLV9DZsXw4E6l4JQwdAAyNWtPdnvvB9T3TpKg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/ink-contracts": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/ink-contracts/-/ink-contracts-0.6.3.tgz", + "integrity": "sha512-XqnM1VDzI5L62xgg+f8le2yEoz8QZbUKEfAfPnHMOgBj9tJiyF15FcOJmnLMO/vq3cGixqh18tzJekLG5YTxtA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.2.0.tgz", + "integrity": "sha512-lhkuBS/x06i3djQIN7p8jVkMnYuGsUAEMS6RhdWeEpz7X/8/APER4Wdih7MEBovCuwVSCTjOxl8f+alH7AZHZg==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.4.1.tgz", + "integrity": "sha512-F1Hw01C60jn98KQ0vBbgcwAvEcMcKLsJ5kFzq600sgjc6Rnbn1VF/XjTdrjcIqvfpR9sXyGIrpImrov2Usbrsw==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/known-chains": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/known-chains/-/known-chains-0.12.1.tgz", + "integrity": "sha512-ZW9TYD2y5IhgNy28zi9sByMr0CaP2I9x8HcAI0pwZR2qDC0kysCG6brDlg1UoZKCjRO99SsKO/iU1o434JgMnQ==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/logs-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/logs-provider/-/logs-provider-0.2.0.tgz", + "integrity": "sha512-BH9YdxZu+ZBPPAUwGrvqHPn1hQStL2Im3MmTwYkwXOWW2HlGHULcF65QKvyK+T6/mj2vDvl3MgivnGkUw4pVxg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/merkleize-metadata": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/merkleize-metadata/-/merkleize-metadata-1.2.3.tgz", + "integrity": "sha512-WkPbz0p2XQ9c8yXagdnwCHEB70Gnm91okcsd6IXU393//3aPgkxKgb+/Efnz7C5/KQmg02P0zXo7q/n/W/yVCA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/metadata-builders": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.14.3.tgz", + "integrity": "sha512-m7CACsiqHzgVEh5WBZGkTV8AQ3CBQKR1YpPQMnlsJfCr/IkgKU0UyWM6WxCmBiReLFVkOfXMtGlpN8+GxpHmww==", + "license": "MIT", + "dependencies": { + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/metadata-compatibility": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-compatibility/-/metadata-compatibility-0.6.3.tgz", + "integrity": "sha512-/Y0uF8nDk60ijydp8Bd37YexPFdB8hBXJWwEgOJHsVlhiny8sVKXiMg+UkJ9BiEk2z+yMbZRCKmhNpTpizo7aw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/observable-client": { + "version": "0.18.7", + "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.18.7.tgz", + "integrity": "sha512-/eQi3D8jbXLg/L1hZX4eX0/+nO3kTxkOfYeWEtFhtOvFdfqiiy2DdXG3Zg5QzgHmfG+11p4vSlLiPCC2UvS5Qw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/pjs-signer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/pjs-signer/-/pjs-signer-0.7.3.tgz", + "integrity": "sha512-U7BLFZfnpFMxCh/scJoLXT6oSbfZtZgMTkiu+TbuWQljAb/1ttrQOfshub5VihNyD5rHajp/2Fq0ONZoL5N5PA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/raw-client": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/raw-client/-/raw-client-0.3.0.tgz", + "integrity": "sha512-u/wM9W7ugIXxBSOEV8+zvQI43b5vgSI0pvE0Rg8PV0G65BTLK0SMc2o2SQL0HtS5+bi5sw/gg4reBJ/0a4U0cw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/signer": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signer/-/signer-0.3.3.tgz", + "integrity": "sha512-bmEV65TwgwbMfiecl7ZQ3k5lfkCOx9FPwPYkVvALcPiqb/d3zYwT9LL/E00hj+EB6IKMlMelEQVuchcW5i/92w==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/merkleize-metadata": "1.2.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/signers-common": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signers-common/-/signers-common-0.2.3.tgz", + "integrity": "sha512-SzGLJMxug31Y1P8+0I809ICpDrayztUXFFh2TV02GuwjEnY6mDGnmX56wowZF5yCn+WteeRdcTJTK7Whk3AGYQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/sm-provider": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@polkadot-api/sm-provider/-/sm-provider-0.3.8.tgz", + "integrity": "sha512-jIvzBNsBsh6LIpCrUOa7miZdQPwLPns5IUQyz/8v84R3ON7URSIVbtywGSR8O83BZGyW/DmVkqa5lClLE2fkqw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1" + }, + "peerDependencies": { + "@polkadot-api/smoldot": ">=0.3" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/smoldot": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/@polkadot-api/smoldot/-/smoldot-0.4.6.tgz", + "integrity": "sha512-gOXMJ10fXOub0zPP1cGAYeyf29BA2fB+qukrjXGcGN9b0Ya+lDgV7A7Q7y9JOrlGLeKbd1YdrYLAe/F43V7b9Q==", + "license": "MIT", + "dependencies": { + "@types/node": "^25.9.4", + "smoldot": "~3.3.1" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/substrate-bindings": { + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.20.3.tgz", + "integrity": "sha512-9iqC71fx1ee9ld1NZV8PFime5vryi0kt1bKCSlvNgO6dqMc06sMZuZ8WPjOzWLCHiKHLuphdMs3rVBBaeCP3yg==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/utils": "0.4.0", + "@scure/base": "^2.2.0", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/substrate-client": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.7.0.tgz", + "integrity": "sha512-TWCc4MAMa5SLVQXmomLHknbj+bztQ/Yclgwm8ENBhz8hR7c9rw9FBAkCa02jMBMCAygPhp3ayGRq+UFcF8KIxQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/raw-client": "0.3.0", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/utils": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.4.0.tgz", + "integrity": "sha512-9b/hwRM0UloLWV7SfpNaSD/4k8UQAHoaACAk7Xe+1MlfAm2JtnmPiB1GfGrfTyBlsrJVUIBCZpEmbmxVMaIqBA==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-api/ws-provider": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/ws-provider/-/ws-provider-0.9.1.tgz", + "integrity": "sha512-Ft2QJEjLZgTyKiCEbz3urSOxNfMdqNkg+QKqJhRbOFB7JtR1qMTvLE3TjM+5d2mNlEpOc3j2KDK3APNSj7uQ2Q==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/@polkadot-labs/hdkd-helpers": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@polkadot-labs/hdkd-helpers/-/hdkd-helpers-0.0.30.tgz", + "integrity": "sha512-qWmmD6ayj14RenDuDFfjF3sHS7ObqPzwIIMPcSVoDeKFSeQV7RY0HwyhC5CG4i6FoguMzak2dbtjYpNN5XQiwQ==", + "license": "MIT", + "dependencies": { + "@noble/curves": "^2.2.0", + "@noble/hashes": "^2.2.0", + "@scure/base": "^2.2.0", + "@scure/sr25519": "^1.0.0", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/polkadot-api": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/polkadot-api/-/polkadot-api-2.2.1.tgz", + "integrity": "sha512-eiZVUhI1gY4ycBtsCo/yaDXyohUeuqnyKun+bwj90ADSn+jlfjZ4sajAqIhJFwPxbCT75+Zzbln4ZjuZ5PWNWA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/cli": "0.21.9", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/logs-provider": "0.2.0", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/pjs-signer": "0.7.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signer": "0.3.3", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@rx-state/core": "^0.1.4" + }, + "bin": { + "papi": "bin/cli.js", + "polkadot-api": "bin/cli.js" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/smoldot": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-3.3.2.tgz", + "integrity": "sha512-Zl4h/0gsw8cfTZzuJ7LV7mtR6QjxltwYjMY7MsVw0oXBXrLK8zyOS6DS9Vjsy57pX1vBMg6UVhHxjbH3W905zA==", + "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", + "dependencies": { + "ws": "^8.8.1" + } + }, + "node_modules/@parity/product-sdk-tx/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@parity/product-sdk/node_modules/@commander-js/extra-typings": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-15.0.0.tgz", + "integrity": "sha512-yeJlba62xqmkgELUsn7356MEnzLLu/fw2x4lofFqGnXh6YysRdEs2BaLeLtg1+KU0AXvMeqQvTTp+3hBEBK+EA==", + "license": "MIT", + "peerDependencies": { + "commander": "~15.0.0" + } + }, + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/cli": { + "version": "0.21.9", + "resolved": "https://registry.npmjs.org/@polkadot-api/cli/-/cli-0.21.9.tgz", + "integrity": "sha512-9aVdF/ocF3DBb8b/gdkcicBIaQkak2kkU4EGW+TunXLIvbIj2X9hH5geOlx8uWNwt0O4O1DIHD/nkXvycG/AcA==", + "license": "MIT", + "dependencies": { + "@commander-js/extra-typings": "^15.0.0", + "@polkadot-api/codegen": "0.22.5", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/wasm-executor": "^0.2.3", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@types/node": "^25.9.4", + "commander": "^15.0.0", + "execa": "^9.6.1", + "fs.promises.exists": "^1.1.4", + "ora": "^9.4.1", + "read-pkg": "^10.1.0", + "rollup": "^4.62.2", + "rollup-plugin-esbuild": "^6.2.1", + "rxjs": "^7.8.2", + "tsc-prog": "^2.3.0", + "typescript": "^6.0.3", + "write-package": "^7.2.0" + }, + "bin": { + "papi": "dist/main/src/main.js", + "polkadot-api": "dist/main/src/main.js" + } + }, + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/codegen": { + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/@polkadot-api/codegen/-/codegen-0.22.5.tgz", + "integrity": "sha512-zwZJAlviI211zhj5i6oXkrr0crrbO3GZjBd2vC9AshY1pRmyiNLV9DZsXw4E6l4JQwdAAyNWtPdnvvB9T3TpKg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/ink-contracts": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/ink-contracts/-/ink-contracts-0.6.3.tgz", + "integrity": "sha512-XqnM1VDzI5L62xgg+f8le2yEoz8QZbUKEfAfPnHMOgBj9tJiyF15FcOJmnLMO/vq3cGixqh18tzJekLG5YTxtA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.2.0.tgz", + "integrity": "sha512-lhkuBS/x06i3djQIN7p8jVkMnYuGsUAEMS6RhdWeEpz7X/8/APER4Wdih7MEBovCuwVSCTjOxl8f+alH7AZHZg==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.4.1.tgz", + "integrity": "sha512-F1Hw01C60jn98KQ0vBbgcwAvEcMcKLsJ5kFzq600sgjc6Rnbn1VF/XjTdrjcIqvfpR9sXyGIrpImrov2Usbrsw==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/known-chains": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/known-chains/-/known-chains-0.12.1.tgz", + "integrity": "sha512-ZW9TYD2y5IhgNy28zi9sByMr0CaP2I9x8HcAI0pwZR2qDC0kysCG6brDlg1UoZKCjRO99SsKO/iU1o434JgMnQ==", + "license": "MIT" + }, + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/logs-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/logs-provider/-/logs-provider-0.2.0.tgz", + "integrity": "sha512-BH9YdxZu+ZBPPAUwGrvqHPn1hQStL2Im3MmTwYkwXOWW2HlGHULcF65QKvyK+T6/mj2vDvl3MgivnGkUw4pVxg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/merkleize-metadata": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/merkleize-metadata/-/merkleize-metadata-1.2.3.tgz", + "integrity": "sha512-WkPbz0p2XQ9c8yXagdnwCHEB70Gnm91okcsd6IXU393//3aPgkxKgb+/Efnz7C5/KQmg02P0zXo7q/n/W/yVCA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/metadata-builders": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.14.3.tgz", + "integrity": "sha512-m7CACsiqHzgVEh5WBZGkTV8AQ3CBQKR1YpPQMnlsJfCr/IkgKU0UyWM6WxCmBiReLFVkOfXMtGlpN8+GxpHmww==", + "license": "MIT", + "dependencies": { + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/metadata-compatibility": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-compatibility/-/metadata-compatibility-0.6.3.tgz", + "integrity": "sha512-/Y0uF8nDk60ijydp8Bd37YexPFdB8hBXJWwEgOJHsVlhiny8sVKXiMg+UkJ9BiEk2z+yMbZRCKmhNpTpizo7aw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3" + } + }, + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/observable-client": { + "version": "0.18.7", + "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.18.7.tgz", + "integrity": "sha512-/eQi3D8jbXLg/L1hZX4eX0/+nO3kTxkOfYeWEtFhtOvFdfqiiy2DdXG3Zg5QzgHmfG+11p4vSlLiPCC2UvS5Qw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/pjs-signer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/pjs-signer/-/pjs-signer-0.7.3.tgz", + "integrity": "sha512-U7BLFZfnpFMxCh/scJoLXT6oSbfZtZgMTkiu+TbuWQljAb/1ttrQOfshub5VihNyD5rHajp/2Fq0ONZoL5N5PA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/raw-client": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/raw-client/-/raw-client-0.3.0.tgz", + "integrity": "sha512-u/wM9W7ugIXxBSOEV8+zvQI43b5vgSI0pvE0Rg8PV0G65BTLK0SMc2o2SQL0HtS5+bi5sw/gg4reBJ/0a4U0cw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/signer": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signer/-/signer-0.3.3.tgz", + "integrity": "sha512-bmEV65TwgwbMfiecl7ZQ3k5lfkCOx9FPwPYkVvALcPiqb/d3zYwT9LL/E00hj+EB6IKMlMelEQVuchcW5i/92w==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/merkleize-metadata": "1.2.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.2.3", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/signers-common": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/signers-common/-/signers-common-0.2.3.tgz", + "integrity": "sha512-SzGLJMxug31Y1P8+0I809ICpDrayztUXFFh2TV02GuwjEnY6mDGnmX56wowZF5yCn+WteeRdcTJTK7Whk3AGYQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + } + }, + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/sm-provider": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@polkadot-api/sm-provider/-/sm-provider-0.3.8.tgz", + "integrity": "sha512-jIvzBNsBsh6LIpCrUOa7miZdQPwLPns5IUQyz/8v84R3ON7URSIVbtywGSR8O83BZGyW/DmVkqa5lClLE2fkqw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1" + }, + "peerDependencies": { + "@polkadot-api/smoldot": ">=0.3" + } + }, + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/smoldot": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/@polkadot-api/smoldot/-/smoldot-0.4.6.tgz", + "integrity": "sha512-gOXMJ10fXOub0zPP1cGAYeyf29BA2fB+qukrjXGcGN9b0Ya+lDgV7A7Q7y9JOrlGLeKbd1YdrYLAe/F43V7b9Q==", + "license": "MIT", + "dependencies": { + "@types/node": "^25.9.4", + "smoldot": "~3.3.1" } }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/substrate-bindings": { + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.20.3.tgz", + "integrity": "sha512-9iqC71fx1ee9ld1NZV8PFime5vryi0kt1bKCSlvNgO6dqMc06sMZuZ8WPjOzWLCHiKHLuphdMs3rVBBaeCP3yg==", "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "@noble/hashes": "^2.2.0", + "@polkadot-api/utils": "0.4.0", + "@scure/base": "^2.2.0", + "scale-ts": "^1.6.1" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/substrate-client": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.7.0.tgz", + "integrity": "sha512-TWCc4MAMa5SLVQXmomLHknbj+bztQ/Yclgwm8ENBhz8hR7c9rw9FBAkCa02jMBMCAygPhp3ayGRq+UFcF8KIxQ==", "license": "MIT", - "engines": { - "node": ">=6.0.0" + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/raw-client": "0.3.0", + "@polkadot-api/utils": "0.4.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/utils": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.4.0.tgz", + "integrity": "sha512-9b/hwRM0UloLWV7SfpNaSD/4k8UQAHoaACAk7Xe+1MlfAm2JtnmPiB1GfGrfTyBlsrJVUIBCZpEmbmxVMaIqBA==", "license": "MIT" }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "node_modules/@parity/product-sdk/node_modules/@polkadot-api/ws-provider": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/ws-provider/-/ws-provider-0.9.1.tgz", + "integrity": "sha512-Ft2QJEjLZgTyKiCEbz3urSOxNfMdqNkg+QKqJhRbOFB7JtR1qMTvLE3TjM+5d2mNlEpOc3j2KDK3APNSj7uQ2Q==", "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" } }, - "node_modules/@noble/ciphers": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", - "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "node_modules/@parity/product-sdk/node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", "license": "MIT", "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" + "node": ">=22.12.0" } }, - "node_modules/@noble/curves": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.2.0.tgz", - "integrity": "sha512-T/BoHgFXirb0ENSPBquzX0rcjXeM6Lo892a2jlYJkqk83LqZx0l1Of7DzlKJ6jkpvMrkHSnAcgb5JegL8SeIkQ==", + "node_modules/@parity/product-sdk/node_modules/polkadot-api": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/polkadot-api/-/polkadot-api-2.2.1.tgz", + "integrity": "sha512-eiZVUhI1gY4ycBtsCo/yaDXyohUeuqnyKun+bwj90ADSn+jlfjZ4sajAqIhJFwPxbCT75+Zzbln4ZjuZ5PWNWA==", "license": "MIT", "dependencies": { - "@noble/hashes": "2.2.0" + "@polkadot-api/cli": "0.21.9", + "@polkadot-api/ink-contracts": "0.6.3", + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/known-chains": "0.12.1", + "@polkadot-api/logs-provider": "0.2.0", + "@polkadot-api/metadata-builders": "0.14.3", + "@polkadot-api/metadata-compatibility": "0.6.3", + "@polkadot-api/observable-client": "0.18.7", + "@polkadot-api/pjs-signer": "0.7.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signer": "0.3.3", + "@polkadot-api/sm-provider": "0.3.8", + "@polkadot-api/smoldot": "0.4.6", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/substrate-client": "0.7.0", + "@polkadot-api/utils": "0.4.0", + "@polkadot-api/ws-middleware": "0.3.6", + "@polkadot-api/ws-provider": "0.9.1", + "@rx-state/core": "^0.1.4" }, - "engines": { - "node": ">= 20.19.0" + "bin": { + "papi": "bin/cli.js", + "polkadot-api": "bin/cli.js" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "peerDependencies": { + "rxjs": ">=7.8.0" } }, - "node_modules/@noble/hashes": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz", - "integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==", - "license": "MIT", - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" + "node_modules/@parity/product-sdk/node_modules/smoldot": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-3.3.2.tgz", + "integrity": "sha512-Zl4h/0gsw8cfTZzuJ7LV7mtR6QjxltwYjMY7MsVw0oXBXrLK8zyOS6DS9Vjsy57pX1vBMg6UVhHxjbH3W905zA==", + "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", + "dependencies": { + "ws": "^8.8.1" } }, - "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, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "node_modules/@parity/product-sdk/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">= 8" + "node": ">=14.17" } }, - "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, - "license": "MIT", - "engines": { - "node": ">= 8" - } + "node_modules/@parity/result": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@parity/result/-/result-0.2.0.tgz", + "integrity": "sha512-QCrhYPeVVaPIjnlsfBIk5GHPRqfuCjy6MjqKkoCP4kwS1LIo5YtJCSFeB5mGtvMG7hiaRNl4lHQcd5YqDfJ1tQ==", + "license": "Apache-2.0" }, - "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, + "node_modules/@parity/truapi": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@parity/truapi/-/truapi-0.5.1.tgz", + "integrity": "sha512-5AV6YoqnUKXj2wJ/qt/J2i3jpFawhEVkY165V5fSDccGkgK2oVHxlSfLwscXpZp4DxwFTL1FmvkhwhuqPDIdtA==", "license": "MIT", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" + "@noble/hashes": "^2.2.0", + "neverthrow": "^8.2.0", + "scale-ts": "^1.6.1" } }, "node_modules/@playwright/test": { @@ -1416,6 +4815,61 @@ "integrity": "sha512-B2h1o+Qlo9idpASaHvMSoViB2I5ko5OAfwfhYF8LQDkTADK0B+SeStzNj1Qn+FG34wqTuv7HzBCdjaUgzYINJQ==", "license": "MIT" }, + "node_modules/@polkadot-api/ws-middleware": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@polkadot-api/ws-middleware/-/ws-middleware-0.3.6.tgz", + "integrity": "sha512-IMoJB572DdSYPshCQa2JmmehUEzX2Uwg5vKQafubbTMEFacXbifc6LTTVvi9Ue67rBuDy2VOWXBAm3BBpfKpDA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0", + "@polkadot-api/json-rpc-provider-proxy": "0.4.1", + "@polkadot-api/raw-client": "0.3.0", + "@polkadot-api/substrate-bindings": "0.20.3", + "@polkadot-api/utils": "0.4.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@polkadot-api/ws-middleware/node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.2.0.tgz", + "integrity": "sha512-lhkuBS/x06i3djQIN7p8jVkMnYuGsUAEMS6RhdWeEpz7X/8/APER4Wdih7MEBovCuwVSCTjOxl8f+alH7AZHZg==", + "license": "MIT" + }, + "node_modules/@polkadot-api/ws-middleware/node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.4.1.tgz", + "integrity": "sha512-F1Hw01C60jn98KQ0vBbgcwAvEcMcKLsJ5kFzq600sgjc6Rnbn1VF/XjTdrjcIqvfpR9sXyGIrpImrov2Usbrsw==", + "license": "MIT" + }, + "node_modules/@polkadot-api/ws-middleware/node_modules/@polkadot-api/raw-client": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/raw-client/-/raw-client-0.3.0.tgz", + "integrity": "sha512-u/wM9W7ugIXxBSOEV8+zvQI43b5vgSI0pvE0Rg8PV0G65BTLK0SMc2o2SQL0HtS5+bi5sw/gg4reBJ/0a4U0cw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.2.0" + } + }, + "node_modules/@polkadot-api/ws-middleware/node_modules/@polkadot-api/substrate-bindings": { + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.20.3.tgz", + "integrity": "sha512-9iqC71fx1ee9ld1NZV8PFime5vryi0kt1bKCSlvNgO6dqMc06sMZuZ8WPjOzWLCHiKHLuphdMs3rVBBaeCP3yg==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.2.0", + "@polkadot-api/utils": "0.4.0", + "@scure/base": "^2.2.0", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@polkadot-api/ws-middleware/node_modules/@polkadot-api/utils": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.4.0.tgz", + "integrity": "sha512-9b/hwRM0UloLWV7SfpNaSD/4k8UQAHoaACAk7Xe+1MlfAm2JtnmPiB1GfGrfTyBlsrJVUIBCZpEmbmxVMaIqBA==", + "license": "MIT" + }, "node_modules/@polkadot-api/ws-provider": { "version": "0.7.5", "resolved": "https://registry.npmjs.org/@polkadot-api/ws-provider/-/ws-provider-0.7.5.tgz", @@ -1583,9 +5037,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", - "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.3.tgz", + "integrity": "sha512-c0wdcekXtQvvn5Tsrk/+op/gUArrbWaFduBnTLP2l1cKLSQs4diMWjJw3m6A0DdzT8dAAX95KpkJ3qynCePbmw==", "cpu": [ "arm" ], @@ -1596,9 +5050,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", - "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.3.tgz", + "integrity": "sha512-3YjElDdWN+qXAFbJ/CzPV+0wspLqh54k/I6GfdYtEJRqg7buSgc1yPM3B+93j1M4neobtkATHZTmxK2AMVGfnA==", "cpu": [ "arm64" ], @@ -1609,9 +5063,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", - "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.3.tgz", + "integrity": "sha512-Pch2pFNOxxz1hTjypIdPyRTR6riiwRl84+VcN9djS680fw+Co1nAJINrdpqp7KV0NvyuU8ilZXZCjd7ykJl1GQ==", "cpu": [ "arm64" ], @@ -1622,9 +5076,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", - "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.3.tgz", + "integrity": "sha512-LEuncFUHFiF8t4yZVZvvZA1wk0pjAscRnsrn1EfTEmN4HXotBi2YtcnLRyaK6UbuczW7xZS5ES+81Rdz8Z0T6g==", "cpu": [ "x64" ], @@ -1635,9 +5089,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", - "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.3.tgz", + "integrity": "sha512-zvBUvsQUpOWALdDsk6qbS8bXf2VxmPisuudNDrY7x0p0jBdsoZl8HsHczIOgkQiZldmcacMKtBzpoGVNeIe2bQ==", "cpu": [ "arm64" ], @@ -1648,9 +5102,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", - "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.3.tgz", + "integrity": "sha512-C2KmNrcSem/AMg984H/dev+si0lieQGdXdR/lYGJnuumXnFb9Y7QdiI62obFdLlxRYLBv4P0eUVIDbD4c1vVvw==", "cpu": [ "x64" ], @@ -1661,9 +5115,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", - "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.3.tgz", + "integrity": "sha512-ggXnsTAEzNQx74XpunRsiZ9aBZDsI7XIa0hm2nzR9f4WzH5/f/d73ZSDaC5ejJ8YLY4NW+V3wr0tjOaeCq8hqA==", "cpu": [ "arm" ], @@ -1674,9 +5128,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", - "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.3.tgz", + "integrity": "sha512-2vng+FlzNUhKZxtej3IUqJgbZoQk2M/dwQM20+ULV0R/E/8tr9/P6uEf2iiGIk4HL0zMKh5Jry7mUHdUOvyGgA==", "cpu": [ "arm" ], @@ -1687,9 +5141,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", - "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.3.tgz", + "integrity": "sha512-LLLFZKt4/Nraf9rxDkhiU8QVgLF4WmCkfr0L4fj0fPfIZFBib0DeiFk1hhaYKd03LFAFJcxHslhDFlNJLylf5Q==", "cpu": [ "arm64" ], @@ -1700,9 +5154,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", - "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.3.tgz", + "integrity": "sha512-WJkdQCvS9sWNOUBJZfQRKpZGFBztRzcowI+nndmflKgU4XY+3a420FgTOSKTsVqJbnzSxeT4vaJalpOaPo2YCQ==", "cpu": [ "arm64" ], @@ -1713,9 +5167,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", - "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.3.tgz", + "integrity": "sha512-PwHXCCS2n64/1Ot6rP1YEYA02MGYBcQlr8CSZZyrUG2O7NH6NklYmvr9v3Jy+5e/eDeNchc/ukmKJi9LuflMIQ==", "cpu": [ "loong64" ], @@ -1726,9 +5180,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", - "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.3.tgz", + "integrity": "sha512-vUjxINQu3RC8NZS3ykk1gN65gIz8pAopOq2HXuZhiIxHdx7TFvDG+jgrdSgInu1Eza4/Rfi2VzZgyIgEH4WOaw==", "cpu": [ "loong64" ], @@ -1739,9 +5193,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", - "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.3.tgz", + "integrity": "sha512-wzko4aJ13+0G3kGnviCg5gnXFKd40izKsrf2uOw12US4XqprkDrmwOpeW14aSNa37V8bfPcz5Fkob6LZ3BAPmA==", "cpu": [ "ppc64" ], @@ -1752,9 +5206,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", - "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.3.tgz", + "integrity": "sha512-8120ue0JUMSwy11stlwnfdX3pPd+WZYGCDBwEHWtIHi6pOpZmsEF5QKB7a/UN+XFdqvobxz98kv8RTqikyCEBw==", "cpu": [ "ppc64" ], @@ -1765,9 +5219,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", - "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.3.tgz", + "integrity": "sha512-XLFHnR3tXMjbOCh2vtVJHmxt+995uJsTERQyseFDRA0xxMxyTZPLa3OIUlyFaO4mF/Lu0FjmWHCuPXJT1n/IOg==", "cpu": [ "riscv64" ], @@ -1778,9 +5232,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", - "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.3.tgz", + "integrity": "sha512-se6yXvNGMIl0f+RQzyh7XAmia8/9kplQx424wnG2w0C1oi6XgO6Y8otKhdXFHbHs88Ihavzmvh1NWjuovE76BQ==", "cpu": [ "riscv64" ], @@ -1791,9 +5245,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", - "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.3.tgz", + "integrity": "sha512-gNoxRefktVIiGflpONuxWWXZAzIQG++z9qHO3xKwk4WdDMuQja3JHGfE1u0i3PfPDyvhypdk+WrgIJqLhGG7sg==", "cpu": [ "s390x" ], @@ -1804,9 +5258,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", - "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.3.tgz", + "integrity": "sha512-V4KtWtQfAFMU7+9/A/VDps/VI8CHd3cYz0L8sgJzz8qK7eY7wI4ruFD82UYIYvW9Z4DtlTfhQcsl4XyPHW5uSg==", "cpu": [ "x64" ], @@ -1817,9 +5271,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", - "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.3.tgz", + "integrity": "sha512-LBx9LYXvj2CBkMkjLdNAWLwH0MLMin7do2VcVo9kVPibGLkY0BQQut2fv7NVqkXqZ/CrAu9LqDHVV1xHCMpCPw==", "cpu": [ "x64" ], @@ -1830,9 +5284,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", - "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.3.tgz", + "integrity": "sha512-ABVf3Q0RCu7NcyCCOZQI0pJ3GuSdfSl8EXcy88QtdceIMIoCUdfhsJChZ64L9zVM2aJHjde1Bhn5uqSRcX9ySA==", "cpu": [ "x64" ], @@ -1843,9 +5297,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", - "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.3.tgz", + "integrity": "sha512-+2Cy/ldweGBLlPIKsQLF8U5N44a0KDdbrk1rAjHOM9M2K+kGdIVjHLmmrZIcx+9Ny3ke/1JomCsDI1ocb11+sg==", "cpu": [ "arm64" ], @@ -1856,9 +5310,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", - "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.3.tgz", + "integrity": "sha512-dtZvzc8BedpSaFNy75x6uiWwAGTH+aZHDtdrqP6qk+WcLJrfti6sGje1ZJ9UxyzDLF23d/mV+PaMwuC0hL7UVA==", "cpu": [ "arm64" ], @@ -1869,9 +5323,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", - "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.3.tgz", + "integrity": "sha512-Rj8Ra4noo+aYy7sKBggCx0407mws34kAb1ySyWuq5DAtFBQdkSwnsjCgPrhPe9cvgBKZIukpE+CVHvORCS93kQ==", "cpu": [ "ia32" ], @@ -1882,9 +5336,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", - "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.3.tgz", + "integrity": "sha512-vp7N084ew/odXn2gi/mzm9mUkQu9l6AiN6dt4IeUM2Uvm9o+cVmP+YkqbMOteLbiGgqBBlJZjIMYVCfOOIVbVQ==", "cpu": [ "x64" ], @@ -1895,9 +5349,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", - "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.3.tgz", + "integrity": "sha512-MOG/3gTOn4Fwf574RVOaY61I5o6P90legkFADiTyn1hyjNydT+cerU2rLUwPdZkKKyJ+iT+K9p7WXK4LM1Ka6g==", "cpu": [ "x64" ], @@ -2145,9 +5599,9 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "license": "MIT" }, "node_modules/@types/json-schema": { @@ -2158,12 +5612,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.6.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", - "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "version": "25.9.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.5.tgz", + "integrity": "sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg==", "license": "MIT", "dependencies": { - "undici-types": "~7.19.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "node_modules/@types/normalize-package-data": { @@ -2407,29 +5861,6 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { "version": "10.2.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", @@ -2831,11 +6262,14 @@ } }, "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==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } }, "node_modules/base64id": { "version": "2.0.0", @@ -2872,14 +6306,16 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" } }, "node_modules/braces": { @@ -3203,23 +6639,16 @@ "node": ">=20" } }, - "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, - "license": "MIT" - }, "node_modules/concurrently": { - "version": "9.2.3", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.3.tgz", - "integrity": "sha512-ihjs0E2SxvDgq/MK418hX6YycQgKhsqxpbZuZbHo0yKfqDWdymWMjWYIpCIzqDDLLKClHlXev8whW/8WXmJ0BA==", + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.4.tgz", + "integrity": "sha512-TZ0CEhyzvFjgtAvHTusDMgj7wNdihCh7LLLrzdUOXIhdlnL2JBBGA9eJxR24rtqgmdjh3OA3hrN1rCHj6HM8qA==", "dev": true, "license": "MIT", "dependencies": { "chalk": "4.1.2", "rxjs": "7.8.2", - "shell-quote": "1.8.4", + "shell-quote": "1.9.0", "supports-color": "8.1.1", "tree-kill": "1.2.2", "yargs": "17.7.2" @@ -3474,7 +6903,6 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, "license": "MIT" }, "node_modules/esbuild": { @@ -4026,6 +7454,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-tsconfig": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -4170,6 +7610,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ipfs-unixfs": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/ipfs-unixfs/-/ipfs-unixfs-12.0.2.tgz", + "integrity": "sha512-uZ3rutVVZZ+tw52P+sgDSgOSK6ztExJVlfCjKvSD+NIEVlWQPDeKgdSFm+Kxchmgp7t6g1h+dzir+NgY+VsQXg==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "protons-runtime": "^6.0.1", + "uint8arraylist": "^2.4.8" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -4336,9 +7786,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, "funding": [ { @@ -4633,6 +8083,12 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/multiformats": { + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-13.4.2.tgz", + "integrity": "sha512-eh6eHCrRi1+POZ3dA+Dq1C6jhP1GNtr9CRINMb67OKzqW9I5DUuZM/3jLPlzhgpGeiNUlEGEbkCYChXMCc/8DQ==", + "license": "Apache-2.0 OR MIT" + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -4644,10 +8100,21 @@ "thenify-all": "^1.0.0" } }, + "node_modules/nanoevents": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/nanoevents/-/nanoevents-9.1.0.tgz", + "integrity": "sha512-Jd0fILWG44a9luj8v5kED4WI+zfkkgwKyRQKItTtlPfEsh7Lznfi1kr8/iZ+XAIss4Qq5GqRB0qtWbaz9ceO/A==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", "funding": [ { "type": "github", @@ -4678,6 +8145,18 @@ "node": ">= 0.6" } }, + "node_modules/neverthrow": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/neverthrow/-/neverthrow-8.2.0.tgz", + "integrity": "sha512-kOCT/1MCPAxY5iUV3wytNFUMUolzuwd/VF/1KCx7kf6CutrOsTie+84zTGTpgQycjvfLdBBdvBvFLqFD2c0wkQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "^4.24.0" + } + }, "node_modules/node-releases": { "version": "2.0.37", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", @@ -4801,9 +8280,9 @@ } }, "node_modules/ora": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-9.3.0.tgz", - "integrity": "sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-9.4.1.tgz", + "integrity": "sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw==", "license": "MIT", "dependencies": { "chalk": "^5.6.2", @@ -4812,7 +8291,7 @@ "is-interactive": "^2.0.0", "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", - "stdin-discarder": "^0.3.1", + "stdin-discarder": "^0.3.2", "string-width": "^8.1.0" }, "engines": { @@ -5188,9 +8667,9 @@ } }, "node_modules/postcss": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", - "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", "funding": [ { "type": "opencollective", @@ -5207,7 +8686,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -5389,6 +8868,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/protons-runtime": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/protons-runtime/-/protons-runtime-6.0.2.tgz", + "integrity": "sha512-hiyjyANwGcgmzc+tXc1/ZcSZhKnl5MDjaVNWkISHBgadaU0sjTgKIKZMZ62d9J9zlSTyKHCs/osPkQ/3Z+7yeA==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "uint8-varint": "^2.0.4", + "uint8arraylist": "^2.4.8", + "uint8arrays": "^5.1.0" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5724,6 +9214,15 @@ "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==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/restore-cursor": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", @@ -5752,12 +9251,12 @@ } }, "node_modules/rollup": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz", - "integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.3.tgz", + "integrity": "sha512-Gu0c0iH9FzgX1L1t7ByIbbS3Vmdz+6KHm/EsqmmC71gUQ82yvZRkTK6XzrFObSka91WUVdynqp6nsfilzr5k6Q==", "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@types/estree": "1.0.9" }, "bin": { "rollup": "dist/bin/rollup" @@ -5767,34 +9266,53 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.2", - "@rollup/rollup-android-arm64": "4.60.2", - "@rollup/rollup-darwin-arm64": "4.60.2", - "@rollup/rollup-darwin-x64": "4.60.2", - "@rollup/rollup-freebsd-arm64": "4.60.2", - "@rollup/rollup-freebsd-x64": "4.60.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", - "@rollup/rollup-linux-arm-musleabihf": "4.60.2", - "@rollup/rollup-linux-arm64-gnu": "4.60.2", - "@rollup/rollup-linux-arm64-musl": "4.60.2", - "@rollup/rollup-linux-loong64-gnu": "4.60.2", - "@rollup/rollup-linux-loong64-musl": "4.60.2", - "@rollup/rollup-linux-ppc64-gnu": "4.60.2", - "@rollup/rollup-linux-ppc64-musl": "4.60.2", - "@rollup/rollup-linux-riscv64-gnu": "4.60.2", - "@rollup/rollup-linux-riscv64-musl": "4.60.2", - "@rollup/rollup-linux-s390x-gnu": "4.60.2", - "@rollup/rollup-linux-x64-gnu": "4.60.2", - "@rollup/rollup-linux-x64-musl": "4.60.2", - "@rollup/rollup-openbsd-x64": "4.60.2", - "@rollup/rollup-openharmony-arm64": "4.60.2", - "@rollup/rollup-win32-arm64-msvc": "4.60.2", - "@rollup/rollup-win32-ia32-msvc": "4.60.2", - "@rollup/rollup-win32-x64-gnu": "4.60.2", - "@rollup/rollup-win32-x64-msvc": "4.60.2", + "@rollup/rollup-android-arm-eabi": "4.62.3", + "@rollup/rollup-android-arm64": "4.62.3", + "@rollup/rollup-darwin-arm64": "4.62.3", + "@rollup/rollup-darwin-x64": "4.62.3", + "@rollup/rollup-freebsd-arm64": "4.62.3", + "@rollup/rollup-freebsd-x64": "4.62.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.3", + "@rollup/rollup-linux-arm-musleabihf": "4.62.3", + "@rollup/rollup-linux-arm64-gnu": "4.62.3", + "@rollup/rollup-linux-arm64-musl": "4.62.3", + "@rollup/rollup-linux-loong64-gnu": "4.62.3", + "@rollup/rollup-linux-loong64-musl": "4.62.3", + "@rollup/rollup-linux-ppc64-gnu": "4.62.3", + "@rollup/rollup-linux-ppc64-musl": "4.62.3", + "@rollup/rollup-linux-riscv64-gnu": "4.62.3", + "@rollup/rollup-linux-riscv64-musl": "4.62.3", + "@rollup/rollup-linux-s390x-gnu": "4.62.3", + "@rollup/rollup-linux-x64-gnu": "4.62.3", + "@rollup/rollup-linux-x64-musl": "4.62.3", + "@rollup/rollup-openbsd-x64": "4.62.3", + "@rollup/rollup-openharmony-arm64": "4.62.3", + "@rollup/rollup-win32-arm64-msvc": "4.62.3", + "@rollup/rollup-win32-ia32-msvc": "4.62.3", + "@rollup/rollup-win32-x64-gnu": "4.62.3", + "@rollup/rollup-win32-x64-msvc": "4.62.3", "fsevents": "~2.3.2" } }, + "node_modules/rollup-plugin-esbuild": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-esbuild/-/rollup-plugin-esbuild-6.2.1.tgz", + "integrity": "sha512-jTNOMGoMRhs0JuueJrJqbW8tOwxumaWYq+V5i+PD+8ecSCVkuX27tGW7BXqDgoULQ55rO7IdNxPcnsWtshz3AA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "es-module-lexer": "^1.6.0", + "get-tsconfig": "^4.10.0", + "unplugin-utils": "^0.2.4" + }, + "engines": { + "node": ">=14.18.0" + }, + "peerDependencies": { + "esbuild": ">=0.18.0", + "rollup": "^1.20.0 || ^2.0.0 || ^3.0.0 || ^4.0.0" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -5880,9 +9398,9 @@ } }, "node_modules/shell-quote": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz", - "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.9.0.tgz", + "integrity": "sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==", "dev": true, "license": "MIT", "engines": { @@ -6599,10 +10117,38 @@ "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", "license": "MIT" }, + "node_modules/uint8-varint": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/uint8-varint/-/uint8-varint-2.0.5.tgz", + "integrity": "sha512-jeFLbL/x30wBRnWjKE1qVBXeumG46r7XmYkpis955lTQ+blccGKFrOsSMHlxePwYB1pI7L8YPHz1t4jLxEs3nA==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "uint8arraylist": "^2.0.0", + "uint8arrays": "^5.0.0" + } + }, + "node_modules/uint8arraylist": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/uint8arraylist/-/uint8arraylist-2.4.9.tgz", + "integrity": "sha512-KxWjyEFzchzik3aoQlK66oaoxIReoMo5bQRm1fcjBUZvE8xv/tyR3CTKhjh6K/faV8VaF6hd5pjr45CzbwuwkA==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "uint8arrays": "^5.0.1" + } + }, + "node_modules/uint8arrays": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-5.1.1.tgz", + "integrity": "sha512-9muQwa4wZG4dKi9gMAIBtnk2Pw87SRpvWTH6lOGm19V2Uqxr4uomUf2PGqPnWc+qs06sN8owUU4jfcoWOcfwVQ==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "multiformats": "^13.0.0" + } + }, "node_modules/undici-types": { - "version": "7.19.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", - "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "license": "MIT" }, "node_modules/unicorn-magic": { @@ -6617,6 +10163,34 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/unplugin-utils": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.2.5.tgz", + "integrity": "sha512-gwXJnPRewT4rT7sBi/IvxKTjsms7jX7QIDLOClApuZwR49SXbrB1z2NLUZ+vDHyqCj/n58OzRRqaW+B8OZi8vg==", + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/unplugin-utils/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -7277,6 +10851,23 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "license": "ISC" }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/web/package.json b/web/package.json index e221bc7..22ea1f5 100644 --- a/web/package.json +++ b/web/package.json @@ -16,17 +16,23 @@ "dev:listen": "concurrently -n signal,web -c magenta,cyan \"npm:signal\" \"npm:dev\"", "build": "tsc -b && vite build", "build:bulletin": "tsc -b && vite build --config vite.bulletin.config.ts", + "build:product-devnet": "tsc -b && vite build --config vite.product.config.ts --mode product-devnet", "deploy:bulletin": "node scripts/deploy-bulletin.cjs", + "deploy:product-devnet": "npm run build:product-devnet && npx --yes --package @polkadot-community-foundation/polkadot-app-deploy@0.13.1 pad ./dist-product dotify-test01.dot --env devnet --js-merkle --config ./polkadot-app-deploy.config.ts", "smoke:production-env": "node scripts/production-env-smoke.mjs", + "smoke:devnet": "node scripts/devnet-endpoint-smoke.mjs", "smoke:signal": "node scripts/signaling-smoke.mjs", "lint": "eslint .", "fmt": "prettier --write 'src/**/*.{ts,tsx}' 'server/**/*.mjs' 'scripts/**/*.mjs' README.md", "fmt:check": "prettier --check 'src/**/*.{ts,tsx}' 'server/**/*.mjs' 'scripts/**/*.mjs' README.md", + "generate:cdm": "node scripts/generate-cdm-manifest.mjs", "update-types": "papi update", "codegen": "papi generate", "preview": "vite preview --host 0.0.0.0 --port 4273" }, "dependencies": { + "@parity/product-sdk": "0.19.1", + "@parity/product-sdk-descriptors": "0.8.0", "@polkadot-api/descriptors": "file:.papi/descriptors", "@polkadot-apps/chain-client": "^2.0.5", "@polkadot-apps/descriptors": "^1.0.1", @@ -45,6 +51,7 @@ "viem": "^2.53.1" }, "overrides": { + "brace-expansion": "5.0.8", "ws": "8.21.0" }, "devDependencies": { diff --git a/web/polkadot-app-deploy.config.ts b/web/polkadot-app-deploy.config.ts new file mode 100644 index 0000000..4f90ead --- /dev/null +++ b/web/polkadot-app-deploy.config.ts @@ -0,0 +1,16 @@ +export default { + domain: 'dotify-test01.dot', + displayName: 'Dotify', + description: 'Shared musical presence with artist-owned access and value flows.', + icon: { + path: './product-icon.png', + format: 'png' + }, + executables: [ + { + kind: 'app', + path: './dist-product', + appVersion: [0, 1, 0] + } + ] +}; diff --git a/web/product-icon.png b/web/product-icon.png new file mode 100644 index 0000000..24c57db Binary files /dev/null and b/web/product-icon.png differ diff --git a/web/product-icon.svg b/web/product-icon.svg new file mode 100644 index 0000000..5827921 --- /dev/null +++ b/web/product-icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/web/scripts/devnet-endpoint-smoke.mjs b/web/scripts/devnet-endpoint-smoke.mjs new file mode 100644 index 0000000..b736c14 --- /dev/null +++ b/web/scripts/devnet-endpoint-smoke.mjs @@ -0,0 +1,145 @@ +// Read-only DevNet endpoint smoke check. +// +// Answers one question with evidence rather than assertion: does the Product +// DevNet build profile point at a chain that actually holds Dotify's contracts? +// +// Product DevNet is a preset over the Paseo system parachains - Asset Hub +// (1000), People (1004), Bulletin (1010) - at EVM chain 420420417. Dotify is +// already deployed there, so porting to DevNet is a configuration question, not +// a redeploy. This check proves the configuration. +// +// Run: npm run smoke:devnet +// +// Network-dependent and therefore not part of `npm run test:unit`. It performs +// only eth_chainId / eth_getCode reads and unauthenticated GETs; it sends no +// transaction, reads no secret, and prints no credential. + +import { readFileSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const scriptDir = dirname(fileURLToPath(import.meta.url)); +const repoRoot = resolve(scriptDir, '../..'); + +const EXPECTED_CHAIN_ID = 420420417; +const REQUEST_TIMEOUT_MS = 20_000; + +function parseEnvFile(path) { + const env = {}; + for (const line of readFileSync(path, 'utf8').split('\n')) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('#')) continue; + const eq = trimmed.indexOf('='); + if (eq === -1) continue; + env[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1).trim(); + } + return env; +} + +async function withTimeout(run) { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS); + try { + return await run(controller.signal); + } finally { + clearTimeout(timer); + } +} + +async function ethCall(rpcUrl, method, params) { + return withTimeout(async signal => { + const response = await fetch(rpcUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }), + signal + }); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + const body = await response.json(); + if (body.error) throw new Error(body.error.message ?? 'RPC error'); + return body.result; + }); +} + +async function reachable(url) { + return withTimeout(async signal => { + const response = await fetch(url, { method: 'GET', signal }); + // A Substrate WS RPC answers a plain GET with 405, and an IPFS gateway + // redirects. Both prove the endpoint is serving. + return response.status; + }); +} + +const results = []; +function record(ok, label, detail) { + results.push({ ok, label, detail }); + console.log(`${ok ? 'ok ' : 'FAIL'} - ${label}${detail ? ` (${detail})` : ''}`); +} + +const env = parseEnvFile(resolve(repoRoot, 'web/.env.product-devnet')); +const deployments = JSON.parse(readFileSync(resolve(repoRoot, 'deployments.json'), 'utf8')); +const rpcUrl = env.VITE_ETH_RPC_URL; + +if (!rpcUrl) { + console.error('VITE_ETH_RPC_URL is not set in web/.env.product-devnet'); + process.exit(1); +} + +console.log(`Dotify Product DevNet endpoint smoke\nAsset Hub RPC: ${rpcUrl}\n`); + +try { + const chainIdHex = await ethCall(rpcUrl, 'eth_chainId', []); + const chainId = Number.parseInt(chainIdHex, 16); + record(chainId === EXPECTED_CHAIN_ID, `Asset Hub reports EVM chain ${EXPECTED_CHAIN_ID}`, `got ${chainId}`); +} catch (error) { + record(false, 'Asset Hub reports the expected EVM chain', error.message); +} + +try { + const blockHex = await ethCall(rpcUrl, 'eth_blockNumber', []); + const block = Number.parseInt(blockHex, 16); + // The chain stalled at 10612201 on 2026-07-01 and later resumed. A head at or + // below that is the signature of a frozen chain, not a healthy one. + record(block > 10_612_201, 'Asset Hub is producing blocks past the 2026-07 halt', `head ${block}`); +} catch (error) { + record(false, 'Asset Hub is producing blocks', error.message); +} + +// The contracts Dotify reads on every catalog load. Code present here is the +// evidence that no redeploy is needed to serve the catalog on DevNet. +for (const [label, address] of [ + ['ArtistDirectory', deployments.directory], + ['ArtistRuntimeFactory', deployments.factory] +]) { + try { + const code = await ethCall(rpcUrl, 'eth_getCode', [address, 'latest']); + const deployed = typeof code === 'string' && code !== '0x' && code.length > 2; + record(deployed, `${label} is deployed at ${address}`, deployed ? `${code.length} chars of bytecode` : 'no code'); + } catch (error) { + record(false, `${label} is deployed at ${address}`, error.message); + } +} + +for (const [label, url] of [ + ['Bulletin RPC', env.VITE_BULLETIN_WS_URL?.replace(/^wss:/, 'https:')], + ['IPFS gateway', env.VITE_PINATA_GATEWAY] +]) { + if (!url) { + record(false, `${label} is configured`, 'missing'); + continue; + } + try { + const status = await reachable(url); + record(status > 0 && status < 500, `${label} responds`, `HTTP ${status}`); + } catch (error) { + record(false, `${label} responds`, error.message); + } +} + +const failed = results.filter(result => !result.ok); +if (failed.length > 0) { + console.error(`\nDotify DevNet endpoint smoke failed: ${failed.length} of ${results.length} checks.`); + process.exit(1); +} + +console.log(`\nDotify DevNet endpoint smoke passed (${results.length} checks).`); diff --git a/web/scripts/generate-cdm-manifest.mjs b/web/scripts/generate-cdm-manifest.mjs new file mode 100644 index 0000000..dfe5469 --- /dev/null +++ b/web/scripts/generate-cdm-manifest.mjs @@ -0,0 +1,157 @@ +// Generate the Product CDM manifest and typed contract augmentation for the +// frontend from Hardhat artifacts + deployments.json. +// +// Run via `npm run generate:cdm` from web/. Plain Node ESM, like +// contracts/evm/scripts/generate-abis.mjs: this only reads JSON and writes +// JSON/TS, so it needs no Hardhat runtime. +// +// It lives in web/ rather than next to generate-abis.mjs because it needs +// @parity/product-sdk-contracts/codegen, which is a frontend dependency. +// Adding the Product SDK tree to contracts/evm just to emit types would be a +// worse trade. Compile first: `cd contracts/evm && npm run compile`. +// +// Why a hand-generated manifest instead of `cdm install`: +// +// Dotify's contracts are Solidity deployed through Asset Hub's eth-rpc, which +// is a compatibility layer over pallet-revive - the same pallet the Product SDK +// contract helpers target. So the deployed H160 addresses are already +// addressable through @parity/product-sdk-contracts without recompiling to +// PolkaVM or registering CDM packages. `CdmJsonContract` only needs `version`, +// `address`, and `abi` for getContract(), and `new ContractManager(...)` is +// documented as snapshot-only. This produces exactly that snapshot, from the +// same artifacts the viem bindings come from, so the two adapters can never +// disagree about an ABI. +// +// The manifest carries only fixed-address contracts. Artist runtimes are +// diamonds deployed per artist, so their address is known at call time, not +// build time; their merged facet ABI is emitted separately for +// createContract(runtime, artistRuntimeAddress, abi). + +import { readFileSync, writeFileSync, mkdirSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { generateContractTypes } from '@parity/product-sdk-contracts/codegen'; + +const scriptDir = dirname(fileURLToPath(import.meta.url)); +const repoRoot = resolve(scriptDir, '../..'); +const artifactsRoot = resolve(repoRoot, 'contracts/evm/artifacts/contracts'); +const outDir = resolve(repoRoot, 'web/src/generated/contracts'); +const deploymentsPath = resolve(repoRoot, 'deployments.json'); + +const HEADER = '// Auto-generated by web/scripts/generate-cdm-manifest.mjs. Do not edit manually.'; + +// CDM library name -> { deployments.json key, artifact path }. +const FIXED_CONTRACTS = [ + { library: '@dotify/artist-directory', deployment: 'directory', artifact: 'ArtistDirectory.sol/ArtistDirectory.json' }, + { library: '@dotify/artist-runtime-factory', deployment: 'factory', artifact: 'ArtistRuntimeFactory.sol/ArtistRuntimeFactory.json' } +]; + +// Facets whose selectors are installed on every artist runtime diamond. Merged +// into one ABI so a runtime address can be called through a single handle, the +// same way the viem adapter calls facet ABIs at the runtime address. +const RUNTIME_FACETS = [ + 'pallets/MusicRegistryPallet.sol/MusicRegistryPallet.json', + 'pallets/MusicRoyaltiesPallet.sol/MusicRoyaltiesPallet.json', + 'pallets/MusicAccessPallet.sol/MusicAccessPallet.json', + 'pallets/MusicNFTPallet.sol/MusicNFTPallet.json' +]; + +const RUNTIME_LIBRARY = '@dotify/smart-runtime'; + +function readArtifactAbi(artifact) { + const artifactPath = resolve(artifactsRoot, artifact); + let parsed; + try { + parsed = JSON.parse(readFileSync(artifactPath, 'utf8')); + } catch { + throw new Error(`Missing artifact ${artifact}. Run "cd contracts/evm && npm run compile" first.`); + } + if (!Array.isArray(parsed.abi)) { + throw new Error(`Artifact ${artifact} has no abi array.`); + } + return parsed.abi; +} + +/** Stable identity for an ABI entry: selector-equivalent for functions, plus type and name. */ +function entryKey(entry) { + const inputs = (entry.inputs ?? []).map(input => input.type).join(','); + return `${entry.type}:${entry.name ?? ''}(${inputs})`; +} + +/** + * Merge facet ABIs into one runtime ABI. A diamond cannot install two facets + * with the same selector, so a collision here means the facet set is wrong - + * fail loudly rather than silently keeping whichever came first. + */ +function mergeRuntimeAbi(facets) { + const merged = new Map(); + for (const { artifact, abi } of facets) { + for (const entry of abi) { + const key = entryKey(entry); + const existing = merged.get(key); + if (!existing) { + merged.set(key, { entry, artifact }); + continue; + } + // Shared constructors/events across facets are expected and identical. + if (JSON.stringify(existing.entry) === JSON.stringify(entry)) continue; + throw new Error(`Runtime facet collision on ${key}: ${existing.artifact} and ${artifact} disagree. Check the diamond facet set.`); + } + } + return Array.from(merged.values(), ({ entry }) => entry); +} + +/** + * Solidity leaves auto-generated getter params unnamed, and + * generateContractTypes interpolates the name straight into a tuple label - + * emitting `args: [: HexString]`, which does not parse. Name them positionally + * for codegen only. The manifest ABI stays byte-faithful to the artifact: + * argument encoding is positional, so names there are cosmetic, and rewriting + * them would make cdm.json diverge from the compiled contract. + */ +function withNamedParams(abi) { + return abi.map(entry => ({ + ...entry, + inputs: (entry.inputs ?? []).map((input, index) => ({ ...input, name: input.name || `arg${index}` })) + })); +} + +function requireAddress(deployments, key) { + const address = deployments[key]; + if (typeof address !== 'string' || !/^0x[0-9a-fA-F]{40}$/.test(address)) { + throw new Error(`deployments.json has no valid "${key}" address. Deploy the contracts before generating the CDM manifest.`); + } + return address.toLowerCase(); +} + +const deployments = JSON.parse(readFileSync(deploymentsPath, 'utf8')); + +const contracts = {}; +const typeInputs = []; + +for (const { library, deployment, artifact } of FIXED_CONTRACTS) { + const abi = readArtifactAbi(artifact); + contracts[library] = { version: 1, address: requireAddress(deployments, deployment), abi }; + typeInputs.push({ library, abi: withNamedParams(abi) }); +} + +const runtimeAbi = mergeRuntimeAbi(RUNTIME_FACETS.map(artifact => ({ artifact, abi: readArtifactAbi(artifact) }))); +typeInputs.push({ library: RUNTIME_LIBRARY, abi: withNamedParams(runtimeAbi) }); + +mkdirSync(outDir, { recursive: true }); + +// The manifest deliberately omits the artist runtime: it has no build-time +// address, and inventing a placeholder one would misrepresent the deployment. +const manifest = { dependencies: Object.fromEntries(Object.keys(contracts).map(library => [library, 1])), contracts }; +writeFileSync(resolve(outDir, 'cdm.json'), `${JSON.stringify(manifest, null, 2)}\n`); + +writeFileSync( + resolve(outDir, 'smartRuntime.ts'), + `${HEADER}\n// Merged artist-runtime diamond facet ABI. Bound to a per-artist address at\n// call time via createContract(), so it carries no address of its own.\n// Source facets:\n${RUNTIME_FACETS.map(artifact => `// contracts/evm/artifacts/contracts/${artifact}`).join('\n')}\n\nexport const SMART_RUNTIME_LIBRARY = '${RUNTIME_LIBRARY}';\n\nexport const smartRuntimeAbi = ${JSON.stringify(runtimeAbi, null, 2)} as const;\n` +); + +writeFileSync(resolve(outDir, 'cdm.d.ts'), `${HEADER}\n\n${generateContractTypes(typeInputs)}`); + +console.log( + `Generated cdm.json (${Object.keys(contracts).length} fixed contracts), smartRuntime.ts (${runtimeAbi.length} merged entries), and cdm.d.ts into web/src/generated/contracts/` +); diff --git a/web/src/app/providers/SessionProvider.tsx b/web/src/app/providers/SessionProvider.tsx index 49c01fa..968b4ca 100644 --- a/web/src/app/providers/SessionProvider.tsx +++ b/web/src/app/providers/SessionProvider.tsx @@ -13,19 +13,21 @@ import { useNavigation } from './NavigationProvider'; import { useCatalogContext } from './CatalogProvider'; const signalUrl = import.meta.env.VITE_SIGNAL_URL ?? `${window.location.protocol}//${window.location.hostname}:8788`; +const publicAppUrl = import.meta.env.VITE_PUBLIC_APP_URL?.trim() || null; type SessionValue = ReturnType; const SessionContext = createContext(null); export function SessionProvider({ children }: { children: ReactNode }) { - const { listenerEvmAddress } = useWalletContext(); + const { activeIdentityAddress } = useWalletContext(); const { navigateToView } = useNavigation(); const catalog = useCatalogContext(); const session = useSession({ signalUrl, - identityAddress: listenerEvmAddress, + publicAppUrl, + identityAddress: activeIdentityAddress, audioSource: catalog.audioSource, trackInfo: catalog.trackInfo, setTrackInfo: catalog.setTrackInfo, @@ -50,7 +52,7 @@ export function SessionProvider({ children }: { children: ReactNode }) { useEffect(() => { const initialRoomCode = getInitialRoomCode(); if (!initialRoomCode || session.roomId) return; - const remembered = getStoredDisplayName(listenerEvmAddress); + const remembered = getStoredDisplayName(activeIdentityAddress); if (!remembered) return; session.setDisplayName(remembered); session.joinRoom(initialRoomCode, { displayName: remembered }); @@ -68,11 +70,11 @@ export function SessionProvider({ children }: { children: ReactNode }) { // write a partial name to storage on every keystroke. const setDisplayName = session.setDisplayName; useEffect(() => { - const stored = getStoredDisplayName(listenerEvmAddress); + const stored = getStoredDisplayName(activeIdentityAddress); if (stored) setDisplayName(stored); // Re-run only when the connected address changes. // eslint-disable-next-line react-hooks/exhaustive-deps - }, [listenerEvmAddress]); + }, [activeIdentityAddress]); return {children}; } diff --git a/web/src/app/providers/WalletProvider.tsx b/web/src/app/providers/WalletProvider.tsx index aef0e7d..5619848 100644 --- a/web/src/app/providers/WalletProvider.tsx +++ b/web/src/app/providers/WalletProvider.tsx @@ -18,6 +18,7 @@ import { devAccounts, type DevAccount } from '../../hooks/useDevAccounts'; import { getDefaultEthRpcUrl } from '../../shared/config/network'; import { resolveEvmChain, getWalletClient } from '../../shared/config/contracts'; import { chainMismatchMessage } from '../../features/wallet/network'; +import type { ProductHostMode, ProductHostStatus } from '../../features/productHost/productHost'; import { useUiFeedback } from './UiFeedbackProvider'; type WalletContextValue = { @@ -25,6 +26,7 @@ type WalletContextValue = { connectedWallet: ConnectedWallet | null; activeEvmAddress: `0x${string}`; listenerEvmAddress: `0x${string}` | null; + activeIdentityAddress: string | null; activeSubstrateAddress: string | null; activeSubstrateSigner: PolkadotSigner | null; currentBulletinAccount: DevAccount; @@ -37,25 +39,35 @@ type WalletContextValue = { switchNetwork: () => Promise; connectPasskey: () => Promise; connectExtension: () => Promise; + connectProductHost: () => Promise; disconnect: () => void; forgetPasskey: () => void; hasPrfSupport: boolean; hasStoredPasskey: boolean; + productHostMode: ProductHostMode; + productHostStatus: ProductHostStatus; }; const WalletContext = createContext(null); +function canRequestProtectedPlayback(wallet: ConnectedWallet | null): boolean { + return Boolean(wallet?.createEvmClient || wallet?.keyRequestSigner); +} + export function WalletProvider({ children }: { children: ReactNode }) { const { setTransactionFeedback, setShowWalletModal } = useUiFeedback(); const { state: walletState, connectPasskey, connectExtension, + connectProductHost, switchExtensionNetwork, disconnect: disconnectWalletOnly, hasPrfSupport, hasStoredPasskey, - forgetPasskey + forgetPasskey, + productHostMode, + productHostStatus } = useWallet(); const [ethRpcUrl] = useState(getDefaultEthRpcUrl); @@ -68,7 +80,7 @@ export function WalletProvider({ children }: { children: ReactNode }) { // Disconnecting the wallet also signs out of the Dotify session (ticket 24 // P2): revoke the server-side token and forget the stored one, so a shared // machine does not keep listening rights after the wallet leaves. - const connectedAddress = connectedWallet?.evmAddress; + const connectedAddress = canRequestProtectedPlayback(connectedWallet) ? connectedWallet?.evmAddress : undefined; const lastConnectedAddressRef = useRef<`0x${string}` | null>(null); const disconnect = useCallback(() => { if (connectedAddress) void signOutOfDotifySession(connectedAddress); @@ -88,7 +100,10 @@ export function WalletProvider({ children }: { children: ReactNode }) { const currentBulletinAccount = devAccounts[bulletinAccountIndex]; const activeEvmAddress = connectedWallet?.evmAddress ?? zeroAddress; - const listenerEvmAddress = connectedWallet?.evmAddress ?? null; + const listenerEvmAddress = canRequestProtectedPlayback(connectedWallet) ? (connectedWallet?.evmAddress ?? null) : null; + // Local room-name persistence lowercases its key, so use the H160 identity + // for both EVM wallets and Product accounts rather than case-sensitive SS58. + const activeIdentityAddress = connectedWallet?.evmAddress ?? null; const devBulletinFallback = import.meta.env.DEV ? currentBulletinAccount : null; const activeSubstrateAddress = connectedWallet ? (connectedWallet.substrateAddress ?? null) : (devBulletinFallback?.address ?? null); const activeSubstrateSigner = connectedWallet ? (connectedWallet.substrateSigner ?? null) : (devBulletinFallback?.signer ?? null); @@ -97,6 +112,9 @@ export function WalletProvider({ children }: { children: ReactNode }) { if (!connectedWallet) { throw new Error('Connect a wallet before signing this transaction.'); } + if (!connectedWallet.createEvmClient) { + throw new Error('This action still requires a passkey or EVM wallet while Dotify contracts are being ported to the Product DevNet host signer.'); + } const chain = await resolveEvmChain(ethRpcUrl); if (connectedWallet.chainId !== undefined && connectedWallet.chainId !== chain.id) { throw new Error(chainMismatchMessage(chain.id, connectedWallet.chainId)); @@ -166,6 +184,7 @@ export function WalletProvider({ children }: { children: ReactNode }) { connectedWallet, activeEvmAddress, listenerEvmAddress, + activeIdentityAddress, activeSubstrateAddress, activeSubstrateSigner, currentBulletinAccount, @@ -178,16 +197,20 @@ export function WalletProvider({ children }: { children: ReactNode }) { switchNetwork, connectPasskey, connectExtension, + connectProductHost, disconnect, forgetPasskey, hasPrfSupport, - hasStoredPasskey + hasStoredPasskey, + productHostMode, + productHostStatus }), [ walletState, connectedWallet, activeEvmAddress, listenerEvmAddress, + activeIdentityAddress, activeSubstrateAddress, activeSubstrateSigner, currentBulletinAccount, @@ -199,10 +222,13 @@ export function WalletProvider({ children }: { children: ReactNode }) { switchNetwork, connectPasskey, connectExtension, + connectProductHost, disconnect, forgetPasskey, hasPrfSupport, - hasStoredPasskey + hasStoredPasskey, + productHostMode, + productHostStatus ] ); diff --git a/web/src/components/WalletModal.tsx b/web/src/components/WalletModal.tsx index 989d2e1..58416bc 100644 --- a/web/src/components/WalletModal.tsx +++ b/web/src/components/WalletModal.tsx @@ -1,4 +1,4 @@ -import { ExternalLink, KeyRound, LockKeyhole, Music2, Power, RefreshCw, Users, Wallet, X } from 'lucide-react'; +import { Box, ExternalLink, KeyRound, LockKeyhole, Music2, Power, RefreshCw, Users, Wallet, X } from 'lucide-react'; import { Dialog } from './Dialog'; import type { WalletState } from '../hooks/useWallet'; import type { CatalogTrack } from '../shared/types'; @@ -58,6 +58,9 @@ export function WalletModal({ isSwitchingNetwork, connectPasskey, connectExtension, + connectProductHost, + productHostMode, + productHostStatus, switchNetwork, forgetPasskey: onForgetPasskey, disconnect: onDisconnect @@ -69,6 +72,7 @@ export function WalletModal({ const onClose = () => setShowWalletModal(false); const onPasskey = () => void connectPasskey(); const onExtension = () => void connectExtension(); + const onProductHost = () => void connectProductHost(); const onSwitchNetwork = () => void switchNetwork(); if (state.status === 'connected') { @@ -95,7 +99,7 @@ export function WalletModal({
    {wallet.label} - {wallet.evmAddress ? ( + {wallet.method !== 'product-host' ? ( {shortenAddress(identityAddress)} @@ -108,7 +112,15 @@ export function WalletModal({
    Connection - {walletChainMismatch ? 'Needs attention' : wallet.method === 'passkey' ? 'This device' : 'Wallet app'} + + {walletChainMismatch + ? 'Needs attention' + : wallet.method === 'passkey' + ? 'This device' + : wallet.method === 'product-host' + ? 'Product host' + : 'Wallet app'} + {walletChainMismatch && Choose the right network to continue} {walletChainMismatch && wallet.method === 'extension' && onSwitchNetwork && (
    + {wallet.method === 'product-host' && ( + <> +

    + Your app-scoped Polkadot identity is active for presence, rooms, and protected playback. If the host signature is rejected, protected playback + fails closed - add a passkey or EVM wallet below. Paying for access and artist publishing still require an EVM signer during the contract port. +

    +
    + {hasPrfSupport && ( + + )} + +
    + + )} +
    {unlockedCount} @@ -254,15 +297,39 @@ export function WalletModal({ {state.status === 'error' &&

    {state.message}

    } {state.status === 'connecting' && ( -

    {state.via === 'passkey' ? 'Check your browser prompt to continue.' : 'Check your wallet to approve the connection.'}

    +

    + {state.via === 'passkey' + ? 'Check your browser prompt to continue.' + : state.via === 'product-host' + ? 'Check the Polkadot Product host to continue.' + : 'Check your wallet to approve the connection.'} +

    )} {state.status === 'needs-reconnect' && state.via === 'passkey' && (

    Your saved passkey is ready. Use passkey to reconnect when you are ready.

    )}
    + {productHostMode !== 'off' && ( + + )} + {hasPrfSupport && ( -