Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .cdm/cdm.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Auto-generated by cdm install -- do not edit
import type { HexString, SizedHex } from "polkadot-api";

declare module "@parity/cdm-codegen" {
interface CdmContracts {
"@w3s/playground-registry": {
methods: {
getAppCount: { args: []; response: number };
getDomainAt: { args: [index: number]; response: string };
getMetadataUri: { args: [domain: string]; response: string };
getOwner: { args: [domain: string]; response: HexString };
getVisibility: { args: [domain: string]; response: number };
getOwnerAppCount: { args: [owner: HexString]; response: number };
getOwnerDomainAt: { args: [owner: HexString, index: number]; response: string };
getSudo: { args: []; response: HexString };
isAdmin: { args: [addr: HexString]; response: boolean };
isBlacklisted: { args: [account: HexString]; response: boolean };
getPoints: { args: [account: HexString]; response: bigint };
getApps: { args: [start: number, count: number]; response: { total: number; scanned: number; entries: { index: number; domain: string; metadata_uri: string; owner: HexString; visibility: number; publisher: HexString }[] } };
getStarCount: { args: [domain: string]; response: number };
getModCount: { args: [domain: string]; response: number };
getLineageCount: { args: []; response: number };
getLineage: { args: [start: number, count: number]; response: { child: string; source: string }[] };
hasStarred: { args: [voter: HexString, domain: string]; response: boolean };
getAppData: { args: [domains: string[], voter: HexString]; response: { domain: string; metadata_uri: string; owner: HexString; visibility: number; publisher: HexString; star_count: number; mod_count: number; has_starred: boolean }[] };
getPointBreakdown: { args: [account: HexString]; response: { mod_points: bigint; star_points: bigint; total: bigint } };
isPinned: { args: [domain: string]; response: boolean };
getPinnedApps: { args: []; response: { index: number; domain: string; metadata_uri: string; owner: HexString; visibility: number; publisher: HexString }[] };
getBuilderCount: { args: []; response: number };
getBuilders: { args: [start: number, count: number]; response: { total: number; scanned: number; entries: { account: HexString; score: bigint }[] } };
pin: { args: [domain: string]; response: undefined };
unpin: { args: [domain: string]; response: undefined };
star: { args: [domain: string]; response: undefined };
publish: { args: [domain: string, metadata_uri: string, visibility: number, modded_from: string]; response: undefined };
setVisibility: { args: [domain: string, visibility: number]; response: undefined };
unpublish: { args: [domain: string]; response: undefined };
addAdmin: { args: [admin: HexString]; response: undefined };
removeAdmin: { args: [admin: HexString]; response: undefined };
adminSetDeployAwardCount: { args: [account: HexString, count: number]; response: undefined };
adminSetAppSocialCounts: { args: [domain: string, star_count: number, mod_count: number]; response: undefined };
setBlacklisted: { args: [accounts: HexString[], value: boolean]; response: undefined };
setFrozen: { args: [value: boolean]; response: undefined };
isFrozen: { args: []; response: boolean };
setVerifier: { args: [verifier: HexString]; response: undefined };
getVerifier: { args: []; response: HexString };
importApp: { args: [domain: string, owner: HexString, publisher: HexString, visibility: number, metadata_uri: string]; response: undefined };
importApps: { args: [apps: { domain: string; owner: HexString; publisher: HexString; visibility: number; metadata_uri: string }[]]; response: undefined };
importPinned: { args: [domain: string]; response: undefined };
importLineage: { args: [entries: { child: string; source: string }[]]; response: undefined };
importPoints: { args: [entries: { account: HexString; total: bigint }[]]; response: undefined };
importSocialCounts: { args: [entries: { domain: string; star_count: number; mod_count: number }[]]; response: undefined };
};
};
"@w3s/playground-identity": {
methods: {
setIdentity: { args: [root_pubkey: SizedHex<32>, signature: number[]]; response: undefined };
clearIdentity: { args: []; response: undefined };
isVerified: { args: [account: HexString]; response: boolean };
rootOf: { args: [account: HexString]; response: SizedHex<32> };
getRootAccount: { args: [account: HexString]; response: SizedHex<32> };
getRootAccounts: { args: [accounts: HexString[]]; response: SizedHex<32>[] };
adminSetIdentity: { args: [account: HexString, root_pubkey: SizedHex<32>]; response: undefined };
adminClearIdentity: { args: [account: HexString]; response: undefined };
addAdmin: { args: [admin: HexString]; response: undefined };
removeAdmin: { args: [admin: HexString]; response: undefined };
setFrozen: { args: [value: boolean]; response: undefined };
importIdentities: { args: [accounts: HexString[], roots: SizedHex<32>[]]; response: undefined };
};
};
"@w3s/playground-open-verifier": {
methods: {
isVerified: { args: [_account: HexString]; response: boolean };
rootOf: { args: [account: HexString]; response: SizedHex<32> };
getRootAccount: { args: [account: HexString]; response: SizedHex<32> };
getRootAccounts: { args: [accounts: HexString[]]; response: SizedHex<32>[] };
faucet: { args: []; response: undefined };
canClaim: { args: [account: HexString]; response: boolean };
lastClaimAt: { args: [account: HexString]; response: number };
setFrozen: { args: [value: boolean]; response: undefined };
getSudo: { args: []; response: HexString };
isFrozen: { args: []; response: boolean };
};
};
}
}
85 changes: 85 additions & 0 deletions .cdm/contracts.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Auto-generated by cdm install -- do not edit
import type { HexString, SizedHex } from "polkadot-api";

declare module "@parity/product-sdk-contracts" {
interface Contracts {
"@w3s/playground-registry": {
methods: {
getAppCount: { args: []; response: number };
getDomainAt: { args: [index: number]; response: string };
getMetadataUri: { args: [domain: string]; response: string };
getOwner: { args: [domain: string]; response: HexString };
getVisibility: { args: [domain: string]; response: number };
getOwnerAppCount: { args: [owner: HexString]; response: number };
getOwnerDomainAt: { args: [owner: HexString, index: number]; response: string };
getSudo: { args: []; response: HexString };
isAdmin: { args: [addr: HexString]; response: boolean };
isBlacklisted: { args: [account: HexString]; response: boolean };
getPoints: { args: [account: HexString]; response: bigint };
getApps: { args: [start: number, count: number]; response: { total: number; scanned: number; entries: { index: number; domain: string; metadata_uri: string; owner: HexString; visibility: number; publisher: HexString }[] } };
getStarCount: { args: [domain: string]; response: number };
getModCount: { args: [domain: string]; response: number };
getLineageCount: { args: []; response: number };
getLineage: { args: [start: number, count: number]; response: { child: string; source: string }[] };
hasStarred: { args: [voter: HexString, domain: string]; response: boolean };
getAppData: { args: [domains: string[], voter: HexString]; response: { domain: string; metadata_uri: string; owner: HexString; visibility: number; publisher: HexString; star_count: number; mod_count: number; has_starred: boolean }[] };
getPointBreakdown: { args: [account: HexString]; response: { mod_points: bigint; star_points: bigint; total: bigint } };
isPinned: { args: [domain: string]; response: boolean };
getPinnedApps: { args: []; response: { index: number; domain: string; metadata_uri: string; owner: HexString; visibility: number; publisher: HexString }[] };
getBuilderCount: { args: []; response: number };
getBuilders: { args: [start: number, count: number]; response: { total: number; scanned: number; entries: { account: HexString; score: bigint }[] } };
pin: { args: [domain: string]; response: undefined };
unpin: { args: [domain: string]; response: undefined };
star: { args: [domain: string]; response: undefined };
publish: { args: [domain: string, metadata_uri: string, visibility: number, modded_from: string]; response: undefined };
setVisibility: { args: [domain: string, visibility: number]; response: undefined };
unpublish: { args: [domain: string]; response: undefined };
addAdmin: { args: [admin: HexString]; response: undefined };
removeAdmin: { args: [admin: HexString]; response: undefined };
adminSetDeployAwardCount: { args: [account: HexString, count: number]; response: undefined };
adminSetAppSocialCounts: { args: [domain: string, star_count: number, mod_count: number]; response: undefined };
setBlacklisted: { args: [accounts: HexString[], value: boolean]; response: undefined };
setFrozen: { args: [value: boolean]; response: undefined };
isFrozen: { args: []; response: boolean };
setVerifier: { args: [verifier: HexString]; response: undefined };
getVerifier: { args: []; response: HexString };
importApp: { args: [domain: string, owner: HexString, publisher: HexString, visibility: number, metadata_uri: string]; response: undefined };
importApps: { args: [apps: { domain: string; owner: HexString; publisher: HexString; visibility: number; metadata_uri: string }[]]; response: undefined };
importPinned: { args: [domain: string]; response: undefined };
importLineage: { args: [entries: { child: string; source: string }[]]; response: undefined };
importPoints: { args: [entries: { account: HexString; total: bigint }[]]; response: undefined };
importSocialCounts: { args: [entries: { domain: string; star_count: number; mod_count: number }[]]; response: undefined };
};
};
"@w3s/playground-identity": {
methods: {
setIdentity: { args: [root_pubkey: SizedHex<32>, signature: number[]]; response: undefined };
clearIdentity: { args: []; response: undefined };
isVerified: { args: [account: HexString]; response: boolean };
rootOf: { args: [account: HexString]; response: SizedHex<32> };
getRootAccount: { args: [account: HexString]; response: SizedHex<32> };
getRootAccounts: { args: [accounts: HexString[]]; response: SizedHex<32>[] };
adminSetIdentity: { args: [account: HexString, root_pubkey: SizedHex<32>]; response: undefined };
adminClearIdentity: { args: [account: HexString]; response: undefined };
addAdmin: { args: [admin: HexString]; response: undefined };
removeAdmin: { args: [admin: HexString]; response: undefined };
setFrozen: { args: [value: boolean]; response: undefined };
importIdentities: { args: [accounts: HexString[], roots: SizedHex<32>[]]; response: undefined };
};
};
"@w3s/playground-open-verifier": {
methods: {
isVerified: { args: [_account: HexString]; response: boolean };
rootOf: { args: [account: HexString]; response: SizedHex<32> };
getRootAccount: { args: [account: HexString]; response: SizedHex<32> };
getRootAccounts: { args: [accounts: HexString[]]; response: SizedHex<32>[] };
faucet: { args: []; response: undefined };
canClaim: { args: [account: HexString]; response: boolean };
lastClaimAt: { args: [account: HexString]; response: number };
setFrozen: { args: [value: boolean]; response: undefined };
getSudo: { args: []; response: HexString };
isFrozen: { args: []; response: boolean };
};
};
}
}
22 changes: 22 additions & 0 deletions .changeset/bulletin-deploy-0200.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
"playground-cli": patch
---

Update `bulletin-deploy` to 0.20.0.

Fixes two crashes that ended a deploy after the content was already uploaded.
One of them is a crash we hit in practice: `JSON.parse` on incoming RPC data was
unguarded and threw inside the socket handler, where nothing upstream could
catch it, killing the process mid-deploy. The other is `hasIPFS()` passing when
`ipfs` is installed but was never initialised.

Also brings, from 0.19.x: the storage-deposit floor for registering a name drops
from a hardcoded 200 PAS to 5 PAS (measured cost is ~0.15), registers no longer
revert when the chain moves between dry-run and inclusion, duplicate files in a
bundle upload once instead of twice, an interrupted register no longer strands
the commitment fee, Bulletin re-authorizes on an exhausted-but-unexpired quota,
and deploying refuses to silently fall back to the public dev key when a stored
session can't be read.

Additive for us: `deploy()`'s signature is unchanged, every option we pass still
exists, and the log banners the progress bar parses are intact.
20 changes: 20 additions & 0 deletions .changeset/gate-asks-wired-verifier.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
"playground-cli": patch
---

Ask the personhood verifier the registry actually uses before blocking a
command.

`playground deploy`, `deploy-all`, `mod` and `decentralize` refused with
"Join the competition first" for users whose publish the chain would have
accepted. The gate read `getRootAccount` from the identity spine, but the
registry does not consult the spine — it consults whatever `set_verifier`
names, which on the current deployment is an open verifier that accepts every
account. The CLI was enforcing a stricter rule than the chain, against a
different contract.

The gate now resolves `registry.getVerifier()` and calls `isVerified` on that
address, so it is correct both today and on a competition deployment that
wires the spine, with no flag to keep in sync. It asks the verifier a
question rather than comparing its address — an address comparison would be
correct only until the next verifier implementation.
21 changes: 21 additions & 0 deletions .changeset/product-id-follows-env-tld.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
"playground-cli": minor
---

Derive the product account from an environment-scoped product id
(`playground.paseo` on paseo-next-v2) instead of a fixed `playground.dot`.

This is an identity migration, not a cosmetic change: the product id is an
input to the account derivation, so **your product account address changes**.
Apps published from the CLI before this release are owned by the old address
and will no longer show as yours; anything keyed to the old product id (cached
subtrees, the SDK allowance cache under `~/.polkadot-apps/`) is simply not
found under the new one and is re-granted at your next `playground login`.

The change is required for the CLI to work at all against a phone on a
`paseo` environment. The mobile host validates the product id against its own
TLD and drops a mismatch silently, so a `.dot` id produced no prompt, no error
and no reply — only a ~180 second timeout. It also keeps the CLI and the
playground web app on one identity: the app already ships the
environment-suffixed form, and two different ids for the same person would
split app ownership and XP across two accounts.
32 changes: 32 additions & 0 deletions .changeset/rfc0022-product-accounts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
"playground-cli": minor
---

Fix QR pairing, and sign as the RFC-0022 product account.

`playground login` has been unable to pair with current phones since
mid-August: `@novasamatech/host-papp` 0.9.0 moved the pairing envelope from
P-256/AES-GCM to X25519/ChaCha20-Poly1305, shrinking the device key from 65 to
32 bytes. The handshake codec is fixed-width, so there was no graceful
degrade — iOS reported "Something went wrong" and Android "invalid QR code".
Moving to `@parity/product-sdk-terminal@0.10.0` (host-papp 0.10.0) puts the CLI
back on the wire both shipping apps speak.

The same release changes how the product account is derived. RFC-0022 places it
at `//product//{productId}/{index}`, where the two `//product//{productId}`
junctions are HARD — and a public key cannot cross a hard junction, so the
account is now derived from a subtree key the wallet provides rather than from
the session's root key locally.

**Your product account address changes, and you must pair again.** Run
`playground logout` and then `playground login`. Apps published from the old
address stay owned by it; allowances and PGAS are re-granted at the next login.
The new derivation is what current phones and the playground web app already
use, so this brings the CLI back into agreement with them rather than away.

Deploy also stops presenting a retry as progress. The phone-approval counter
was counting signature *requests*, so a request that timed out and was re-sent
appeared as the next step — and contradicted the "Phone approvals expected"
plan, which counts operations. A deploy could print "step 2: Link content"
while the plan said step 2 was the registry publish. Repeats are now labelled
as retries, with the remedy worth checking when no prompt appeared at all.
21 changes: 21 additions & 0 deletions .changeset/v2-registry-contracts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
"playground-cli": minor
---

Move to the v2 playground contracts (registry + identity + verifier).

`registry.publish` now takes four arguments (`domain`, `metadata_uri`,
`visibility`, `modded_from`) and has a new selector, so this release is
required to publish at all — the previous seven-argument form reverts against
the deployed contract. The dropped `owner` parameter changes who owns an app:
the registry now always records the caller, so a dev-mode deploy is owned by
the dev signer and will not appear in your MyApps. `playground deploy` says so
explicitly in its summary instead of implying otherwise.

Also fixes `playground mod` and `playground init`, which failed with
`App "<domain>" not found in registry` for every app, including ones that
demonstrably existed. The v2 registry returns `getMetadataUri` as a plain
string (empty when absent) rather than an `Option`, and the old
`value.isSome` narrowing read `undefined` every time. Decoding now lives in
one place and treats an unexpected payload as an error rather than silently
reporting "not found".
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
node_modules/
dist/

.cdm/
# Generated by `cdm install`. The TYPE augmentations ARE committed (below) so
# CI typechecks contract call sites — without them the registry handle is a bare
# `Contract<ContractDef>` and every call silently goes unchecked. The resolved
# artifacts under .cdm/contracts/ are NOT committed: they snapshot per-version
# contract addresses we never read (addresses resolve live from the CDM
# meta-registry), so they would be a second, stale source of truth.
.cdm/*
!.cdm/cdm.d.ts
!.cdm/contracts.d.ts
docs-internal/
docs/superpowers/
.worktrees/
Expand Down
Loading
Loading