From 1c956174132648a6a2d9e8828646179ee4302cce Mon Sep 17 00:00:00 2001 From: Leonardo Custodio Date: Fri, 10 Jul 2026 12:30:39 -0300 Subject: [PATCH] Invites --- docs/rfcs/00XX-contacts.md | 370 ++ generated/client.ts | 952 +++++ generated/index.ts | 2 + generated/types.ts | 3581 +++++++++++++++++ generated/wire-table.ts | 378 ++ .../truapi-codegen/tests/golden/dispatcher.rs | 108 + .../truapi-codegen/tests/golden/wire_table.rs | 42 + .../truapi-server/src/generated/dispatcher.rs | 124 +- .../truapi-server/src/generated/wire_table.rs | 42 + rust/crates/truapi/src/api.rs | 4 + rust/crates/truapi/src/api/contacts.rs | 94 + rust/crates/truapi/src/v01.rs | 2 + rust/crates/truapi/src/v01/contacts.rs | 134 + rust/crates/truapi/src/v01/permissions.rs | 8 +- rust/crates/truapi/src/versioned.rs | 1 + rust/crates/truapi/src/versioned/contacts.rs | 16 + 16 files changed, 5854 insertions(+), 4 deletions(-) create mode 100644 docs/rfcs/00XX-contacts.md create mode 100644 generated/client.ts create mode 100644 generated/index.ts create mode 100644 generated/types.ts create mode 100644 generated/wire-table.ts create mode 100644 rust/crates/truapi/src/api/contacts.rs create mode 100644 rust/crates/truapi/src/v01/contacts.rs create mode 100644 rust/crates/truapi/src/versioned/contacts.rs diff --git a/docs/rfcs/00XX-contacts.md b/docs/rfcs/00XX-contacts.md new file mode 100644 index 00000000..fdd9c5da --- /dev/null +++ b/docs/rfcs/00XX-contacts.md @@ -0,0 +1,370 @@ +# RFC-00XX: Contacts \- Host-Mediated Invitations and Product-Separated Key Agreement + +| RFC Number | 00XX | +| :---- | :---- | +| **Start Date** | 2026-07-06 | +| **Description** | Reachable-by-default, end-to-end-encrypted contact requests between users, plus product-separated symmetric key agreement on host-managed exchange keys | +| **Authors** | | +| **Status** | Draft | + +## Summary + +This RFC adds a `Contacts` service to TrUAPI with four methods: `resolve`, `derive_shared_key`, `send`, and `subscribe`. Together they let a product deliver an end-to-end-encrypted payload (an invitation, a share, a contact request) to any user identified by their DotNS username or identity key \- **including users who have never opened the product**. The host derives a per-identity X25519 *exchange key* from the root account entropy, publishes its public half on the Statement Store once per identity, watches the identity's contact inbox on the user's behalf, and delivers decrypted payloads to the addressed product when it runs. Product isolation comes from domain-separating the KDF from the product identifier rather than from per-product keypairs, so one published key makes the user reachable across every product. The user agent \- not the product \- decides how incoming contact attempts are surfaced. + +## Motivation + +Products need to deliver secret-bearing or trust-establishing payloads to users identified by their identity, end-to-end encrypted. link3 (shared-profiles) is the concrete case; chat contact requests, HackM3, and Mark3t have the same shape. + +Because the host exposes only signing over identity keys, link3 must build this itself: derive a per-recipient keypair via `host_derive_entropy` and ship its secret (`skR`) to the recipient; have every user mint an "inbox" keypair and publish it on the Statement Store before anyone can reach them; and seal invitations to that published key. The publication happens inside the product, so it requires the recipient to have opened the product, granted `StatementSubmit`, and obtained a `StatementStoreAllowance` \- in practice "A deploys, B deploys, A deploys again" before an invitation lands. Product-side fixes softened this, but the hard floor remains: **you cannot invite someone into a product they have never opened.** Users should instead learn about a product *because* a friend contacted them about it. The approach also costs each product several hundred lines of generic, delicate crypto and statement-store plumbing. + +Requirements, distilled from the design discussion between the link3 and TrUAPI teams: + +1. **E2E encryption on host-managed exchange keys, domain-separated per product.** Products get authenticated encryption to a peer without holding the exchange secret; a ciphertext or derived key for product P is useless to product Q. +2. **A generalized incoming-contact mechanism in the host.** Chat requests and product invitations are the same thing: an attempt by one user to reach another through a product. The host receives them for all products and hands each payload to its product when the product runs. +3. **Reachable by default.** Reachability must be a property of *having an identity*, not of having opened a product. The user agent decides how to *report* a contact attempt, but contact must not be impossible by default. +4. **A simple product-facing API.** The common case \- "invite this username, deliver this payload" \- must be one call on each side. + +## Detailed Design + +### Overview + +``` + One-time, per identity (no product involved): + Host(Bob) ── publishes ──▶ Statement Store: ExchangeKeyRecord @ exchangeKeyTopic(bob) + + Alice invites Bob to product P (Bob has never opened P): + P@Alice ── send(bob, payload) ──▶ Host(Alice) + Host(Alice) ── resolves bob's exchange key, seals envelope ──▶ Statement Store @ contactInboxTopic(bob) + Host(Bob) ── watching bob's inbox ── decrypts, verifies, persists, applies UA policy + ── (e.g.) notifies: "alice invited you on P" + Bob opens P for the first time: + P@Bob ── subscribe() ──▶ Host(Bob) ──▶ { sender: alice, payload } +``` + +Three ideas carry the design: + +1. **One exchange key per identity, published by the host.** Every identity is reachable for every product, past and future, with zero product involvement \- this removes the bootstrap. +2. **Product separation in the KDF, not in the keys.** All products seal to the same exchange key, but every derived symmetric key mixes the product identifier into the KDF. +3. **The host is the recipient.** The host holds the exchange secret and watches the inbox continuously, so it can receive and triage contacts for products that are not running \- or never ran. Products receive plaintext payloads; they perform no envelope cryptography. + +### The exchange key + +*Identity* means the account owning the user's primary DotNS username (RFC-0015); its 32-byte public key is denoted `identity`. The host derives the exchange keypair from `rootEntropySource` \- the root material RFC-0007 shares with every conforming host at SSO \- under a dedicated domain separator: + +```rust +let exchangeSeed: [u8; 32] = blake2b256_keyed( + message: rootEntropySource, // the SSO-shared root, as in RFC-0007 + key: b"truapi-exchange-key-v1", +); +let exchangeSecret = x25519_clamp(exchangeSeed); +let exchangePublic = x25519_basepoint_mul(exchangeSecret); +``` + +The derivation is deterministic, so the key is identical on every device and every conforming host \- no key storage, no sync. No product can compute it: a product only ever sees `blake2b256_keyed(perProductEntropy, key)`, and the 22-byte exchange separator can never equal a product's 32-byte `blake2b256(productId)` key, so no product's derivation tree contains it. + +#### Publication + +| Field | Value | +| :---- | :---- | +| `topics` | `[ blake2b256(b"truapi:exchange-key:v1:" ++ identity) ]` | +| `channel` | `blake2b256(b"truapi:exchange-key-channel:v1")` | +| `data` | SCALE-encoded `ExchangeKeyRecord` | +| `proof` | `Sr25519`, signed by the **identity account itself** | + +```rust +struct ExchangeKeyRecord { + version: u8, // = 1 + exchange_public: [u8; 32], // X25519 public key + issued_at: u64, // unix seconds +} +``` + +The statement proof is the authenticity binding: verifiers MUST check that the proof is valid and that `proof.signer` equals the `identity` the topic derives from, and MUST ignore records failing either check. (App-level schemes publish inbox keys with no such binding and are open to key substitution by anyone with store quota.) + +Hosts MUST publish the record when an identity becomes available to them (login/session establishment) unless the store already holds the current record on the LWW channel; the check-then-publish is idempotent. The record's identity signature is produced once per identity \- approved at first login and cached, since the record is deterministic \- so publication is a single one-time approval per identity, never a per-product prompt, and never contingent on any product being open. A user agent MAY offer an unreachability opt-out (publishing nothing); unreachable is never the default. + +#### Relationship to the chat `identifier_key` + +The chat stack already publishes a per-user P-256 encryption key on the People chain (`Resources.Consumers.identifier_key`), and existing product designs wrap per-recipient keys against it. The exchange key is the protocol-level successor of that pattern for cross-product use: host-held rather than product-computed, X25519 (matching the envelope construction), and device-portable without key sync. The two coexist during migration \- hosts that implement chat keep publishing `identifier_key`; new invitation and sharing flows SHOULD address peers through this RFC's surface, and a later chat migration onto this rail (see Future Directions) would allow `identifier_key` to be retired. + +### The contact envelope + +`send` seals the payload into a `ContactEnvelope` addressed to the recipient's published exchange key. All hashes are BLAKE2b-256, keyed/unkeyed exactly as in RFC-0007: + +```rust +struct ContactEnvelope { + version: u8, // = 1 + ephemeral_public: [u8; 32], + ciphertext: Vec, // ChaCha20-Poly1305 +} +``` + +```rust +let (ephemeral_secret, ephemeral_public) = x25519_generate(); // fresh per envelope +let ss = x25519(ephemeral_secret, recipient_exchange_public); +let k_env = blake2b256_keyed( + message: ss ++ ephemeral_public ++ recipient_exchange_public, + key: b"truapi-contact-envelope-v1", +); +ciphertext = chacha20poly1305_encrypt( + key: k_env, + nonce: [0u8; 12], // fresh key per envelope; nonce reuse impossible + aad: version_byte ++ ephemeral_public, + plaintext: scale_encode(ContactMessage { ... }), +); +``` + +```rust +struct ContactMessage { + product_id: String, // sending product's normalized DotNS identifier + sender_identity: [u8; 32], + sent_at: u64, // unix seconds + payload: Vec, // opaque product bytes + signature: [u8; 64], // sr25519 by the sender identity account +} +``` + +The signature covers, in order: + +``` +blake2b256( + b"truapi-contact-sig-v1" + ++ recipient_identity + ++ blake2b256(product_id) + ++ ephemeral_public + ++ blake2b256(payload) + ++ scale_encode(sent_at) +) +``` + +Binding the recipient prevents re-sealing a captured message to another recipient; binding `ephemeral_public` ties the signature to this envelope; binding `product_id` stops cross-product impersonation. The receiving host derives the exchange secret, MUST check the decrypted `recipient_identity` is its own, and MUST verify the signature against `sender_identity`, discarding anything that fails to decrypt or verify. + +Receipts, accept/decline, and request/response flows are ordinary contacts addressed back to the sender's identity (delivered to the recipient's product as `sender_identity`/`sender_username`); the host emits no automatic acknowledgment (see Privacy). + +#### The statement carrying an envelope + +| Field | Value | +| :---- | :---- | +| `topics` | `[ blake2b256(b"truapi:contact-inbox:v1:" ++ recipient_identity) ]` | +| `channel` | `blake2b256(b"truapi:contact-channel:v1:" ++ blake2b256(product_id) ++ recipient_identity)` | +| `data` | SCALE-encoded `ContactEnvelope` | +| `proof` | any account controlled by the sending host with statement admission | + +Channels are last-write-wins per signing account, so re-sending to the same recipient for the same product *replaces* the previous contact \- invitation refresh for free, bounded sender footprint. At most one contact per `(sender, recipient, product)` is outstanding in the store at a time. + +The proof exists solely for store admission; verifiers MUST NOT infer the sender from it \- the authenticated sender is `ContactMessage.sender_identity`. Hosts SHOULD sign contact submissions with a dedicated product-scoped account covered by the product's `StatementStoreAllowance` (RFC-0010), keeping the identity and primary product accounts out of observable metadata. Because replacement correlates by `(channel, submitting account)`, that account MUST be stable per `(recipient, product)`; scoping it per recipient keeps it stable without letting a store observer link a sender's contacts across recipients. Hosts SHOULD set expiry using the established ceiling-expiry practice (footprint is bounded by the LWW channel; a genuine TTL loses admission races when the account's slots are full). + +### Protocol surface + +A new `Contacts` service trait is added to the `TrUApi` super-trait, following the standard pattern: `v01` wire types, single-`V1` `versioned_type!` envelopes, fresh append-only wire ids (the highest allocated today is 163): + +```rust +/// Contact requests and invitations between users, mediated by the host (RFC-00XX). +pub trait Contacts: Send + Sync { + /// Resolve a peer to their identity key and published exchange key. + #[wire(request_id = 164)] + async fn resolve(&self, _cx: &CallContext, _request: HostContactsResolveRequest) + -> Result>; + + /// Derive a symmetric key shared with a peer, scoped to the calling + /// product and a caller-chosen context. + #[wire(request_id = 166)] + async fn derive_shared_key(&self, _cx: &CallContext, _request: HostContactsDeriveSharedKeyRequest) + -> Result>; + + /// Seal a payload to a recipient and submit it to their contact inbox. + #[wire(request_id = 168)] + async fn send(&self, _cx: &CallContext, _request: HostContactsSendRequest) + -> Result<(), CallError>; + + /// Receive contact payloads addressed to the calling product. + #[wire(start_id = 170)] + async fn subscribe(&self, _cx: &CallContext) + -> Result, CallError>; +} +``` + +```rust +/// A peer addressed by DotNS username or identity public key. +pub enum ContactPeer { + Username(String), + Identity([u8; 32]), +} + +pub struct HostContactsResolveRequest { pub peer: ContactPeer } +pub struct HostContactsResolveResponse { + pub identity: [u8; 32], + /// `None`: the identity exists but has no published exchange key + /// (e.g. its host predates this RFC); the peer is not yet reachable. + pub exchange_key: Option<[u8; 32]>, +} +pub enum HostContactsResolveError { + NotFound, + Unknown { reason: String }, +} + +pub struct HostContactsDeriveSharedKeyRequest { + pub peer: ContactPeer, + /// Domain-separation context, at most 32 bytes (as in RFC-0007, + /// callers hash longer contexts down). + pub context: Vec, +} +pub struct HostContactsDeriveSharedKeyResponse { + pub key: [u8; 32], +} +pub enum HostContactsDeriveSharedKeyError { + NotFound, + NotReachable, + ContextTooLong, + NotConnected, + Unknown { reason: String }, +} + +pub struct HostContactsSendRequest { + pub recipient: ContactPeer, + pub payload: Vec, +} +pub enum HostContactsSendError { + NotFound, + NotReachable, + PayloadTooLarge, + PermissionDenied, + NotConnected, + Unknown { reason: String }, +} + +pub struct HostContactsSubscribeItem { + pub contacts: Vec, + /// `false` while replaying contacts received before this subscription, + /// `true` once caught up and live (as in RFC-0008). + pub is_complete: bool, +} +pub struct ContactDelivery { + pub sender_identity: [u8; 32], + pub sender_username: Option, + pub payload: Vec, + pub sent_at: u64, +} +pub enum HostContactsSubscribeError { + NotConnected, + Unknown { reason: String }, +} +``` + +(`response_id`s follow the `request_id + 1` convention; the subscription occupies 170–173. Ids to be confirmed against the wire table at merge time.) + +#### `resolve` + +Resolves a username to its identity key (People-chain username owner) and looks up the published record (verifying the proof binding). No prompt: everything returned is public. + +#### `derive_shared_key` + +Returns a 32-byte symmetric key shared between the calling user and the peer, unique to the (user pair, product, context) tuple. Derivation mirrors the three-layer structure of RFC-0007: + +```rust +let ss = x25519(own_exchange_secret, peer_exchange_public); +// lo/hi: the two exchange public keys in byte-wise lexicographic order, +// so both sides compute identical input +let pairSecret: [u8; 32] = blake2b256_keyed( + message: ss ++ lo ++ hi, + key: b"truapi-contact-shared-v1", +); +let perProductSecret: [u8; 32] = blake2b256_keyed( + message: pairSecret, + key: blake2b256(productId), // normalized as in RFC-0007 +); +let sharedKey: [u8; 32] = blake2b256_keyed( + message: perProductSecret, + key: context, +); +``` + +Core invariant: for users A and B and product P, `derive_shared_key` called by A with peer B and by B with peer A, with the same `context`, MUST return the same key on every conforming host (X25519 is symmetric in the pair; the remaining inputs are order-normalized). Different products obtain unrelated keys; `pairSecret` and `perProductSecret` never leave the host. + +This primitive replaces per-recipient `skR` shipping entirely: both sides *derive* the pairwise key, so invitations carry no key material (see the worked example). It is also the intended root secret for richer product protocols (sessions, ratchets). A peer with no published exchange key yields `NotReachable`; a `context` longer than 32 bytes is rejected with `ContextTooLong`. No prompt: the result is product-scoped key material, equivalent in sensitivity to `host_derive_entropy` output. + +#### `send` + +Resolves the recipient, builds and signs the `ContactMessage`, seals the envelope, and submits the statement \- one call. Failure modes: `NotFound` (no such identity), `NotReachable` (no exchange key published), `PayloadTooLarge` (statement would exceed the store's size limit; payloads SHOULD stay small and carry references \- CIDs, names \- rather than bulk data). + +A send to the sender's own identity MUST be delivered locally without a store round-trip. Because channels key on identity, a product that will later re-send SHOULD pin the identity `resolve` returned rather than re-passing a `Username`, which can rebind to a new owner. + +`send` is gated by a new remote permission, `RemotePermission::ContactSend` (variant appended \- wire-compatible), requested implicitly on first use per the RFC-0002 lifecycle: one prompt, decision persisted. Admission economics (allowances, quotas) are handled by the host internally. + +#### `subscribe` + +Delivers `ContactDelivery` items whose envelope named the calling product (`ContactMessage.product_id` equals the caller's normalized product identifier \- the host enforces this; a product can never observe another product's contacts). The host first replays persisted contacts in pages with `is_complete = false`, then a page with `is_complete = true`, then live deliveries \- the RFC-0008 pattern. A later delivery from the same `sender_identity` supersedes the earlier one (invitation refresh). Receiving requires no prompt: the UA policy layer has already decided what reaches the product. + +### Worked example: link3 + +Today, after link3's own best-effort fixes: + +``` +Alice: open link3, deploy, whitelist bob → invite queued "pending" (bob unreachable) +Bob: open link3 (must know about it!), grant allowance + StatementSubmit, + click "Let others invite you" → inbox key published +Alice: reopen link3 → pending invite retried, sealed, submitted +Bob: reopen link3 → invite received, skR stored, links decrypt +``` + +With this RFC (Bob has never opened link3): + +``` +Alice (in link3): + contentKey = derive_shared_key(bob, blake2b256("link3:content:v1")) + // encrypt bob's private-links slot under contentKey + send(bob, { profileName, profileCid }) + +Bob's host: receives, verifies, notifies "alice invited you on link3" +Bob (opens link3 for the first time): + subscribe() → { sender: alice, payload: {...} } + contentKey = derive_shared_key(alice, "link3:content:v1") + // fetch envelope by profileCid, decrypt private links + // optionally: send(alice, accepted) // reaches alice, reachable by default +``` + +No inbox keypair, no key publication, no pending-invite queue, no sealed-box code \- and because both sides derive `contentKey`, no key material in the invitation at all: an intercepted invitation leaks a CID, not a decryption key. Multi-recipient products wrap their content key under each pairwise `sharedKey` (or use it as the per-recipient slot-key seed) \- the same structure as today, minus the shipping. + +### Codegen impact + +A standard additive change: `v01/contacts.rs` types, `versioned/contacts.rs` single-`V1` envelopes, `api/contacts.rs` trait, `Contacts` in the `TrUApi` super-trait, then `./scripts/codegen.sh` regenerates the TS client, Rust dispatcher/wire table, Dart host interfaces, and playground/explorer metadata. + +## Relationship to RFC-0014 (Contacts API) + +RFC-0014 exposes the host-managed address book to products: *who the user knows*, as local display names plus per-context alias/account entries, gated by `DevicePermission::Contacts` and `ContactsCrossContext`. This RFC is the other half \- *how a user reaches a peer* and establishes keys. The two remain separate services and compose: + +- **Permissions.** Reading the address book is a device permission (RFC-0014); sending on the user's behalf is a remote permission (`ContactSend`). Neither implies the other. +- **Ingestion.** RFC-0014 leaves open how contacts enter the address book. Accepted contact requests are the natural path: when the user acts on a delivered contact, the host SHOULD offer to add the sender to the address book. +- **Privacy-preserving sends.** RFC-0014's products see peers only as context-scoped aliases, while `send` takes a global identity. A future `ContactPeer` variant carrying an opaque address-book reference would let the host resolve alias → identity internally, so a product can "invite the friends who also use this app" without learning anyone's global identity. Deferred until RFC-0014's handle format settles; `ContactPeer` appends compatibly. +- **Wire naming.** This RFC's methods generate `contacts_*` wire names (including `contacts_subscribe`); RFC-0014's methods should take non-colliding names (e.g. `contacts_list` / `contacts_list_subscribe`) when it lands. + +## Drawbacks + +- **No forward secrecy or rotation.** The exchange key is static; root-entropy compromise exposes everything (as in RFC-0007), and a leaked exchange secret cannot be replaced without re-deriving the identity's root entropy. Products needing forward secrecy should treat `derive_shared_key` as a session-bootstrap secret, not a long-term message key. +- **No withdrawal or threading.** A submitted contact cannot be recalled: a later send to the same peer replaces it under LWW, but a contact already delivered is beyond recall. At most one contact per `(sender, recipient, product)` is outstanding, so coexisting invitations and reply-correlation are left to the product payload. +- **Identity requirement.** Users without a registered primary username are unaddressable. + +## Security Considerations + +- **Trust model** is the RFC-0007 triangle: the host already holds root entropy, so the exchange secret adds no new class of secret. Products never see the exchange secret, `pairSecret`, or `perProductSecret` \- only leaf keys scoped to themselves. +- **Exchange-key authenticity** rests on the proof-signer-equals-topic-identity check. +- **Sender authenticity and replay.** The in-envelope sr25519 signature binds sender, recipient, product, ephemeral key, payload, and timestamp; replay to another recipient fails the recipient binding, replay to the same recipient is absorbed by dedup. A malicious product can only send *as itself*, and only after `ContactSend` is granted. +- **Product isolation** is enforced twice: cryptographically (the product-keyed KDF layer) and at delivery (routing by the authenticated `product_id`). +- **Spam and DoS.** Store admission costs sender-side quota/allowance; the LWW channel bounds per-sender-per-product footprint to one statement per recipient; UA policy bounds attention. The residual risk \- a funded adversary spamming a topic \- costs the recipient's host verification work only. + +### Privacy + +- A store observer sees that *some account* submitted to *this identity's* inbox topic, its timing, and size. Sender, product, and payload are inside the AEAD; the proof account has no protocol-level link to the sender's identity. This improves on today's practice, where product-specific topics reveal which product an invitation belongs to. Hosts SHOULD pad envelopes to coarse-size buckets. +- The exchange key is public and identity-bound \- the same linkability class as the username; per-product unlinkability of accounts (RFC-0010) is untouched. +- Traffic analysis of a recipient topic reveals contact *frequency*; mitigations (cover traffic, private retrieval) are out of scope. +- Hosts MUST NOT emit automatic acknowledgments; replying is a product/user action. +- The recipient's host necessarily learns the incoming contact graph \- the cost of host mediation, consistent with the trust model. + +## Unresolved Questions + +1. **Attestation registry.** Which source(s) feed the "notify eagerly" set \- on-chain attestations, host-curated lists, both? A shared registry format may deserve its own RFC. +2. **Delivery status.** Is an opt-in host-level delivery signal ("recipient's host has seen it") wanted \- so a sender can tell a delivered contact from one evicted before a dormant recipient ever came online \- or is product-level reply the only acknowledgement this protocol should have? +3. **Withdrawal.** Is LWW-replacement enough, or do products need best-effort retraction (tombstones) to recall an undelivered contact? +4. **Payload ceiling.** The maximum payload size should be stated normatively once pinned against the store's chain constants. +5. **Multiple identities.** `subscribe`/`send` bind to the active session identity (RFC-0009); is per-call identity selection needed? +6. **HPKE.** Whether to adopt HPKE verbatim for the envelope's KEM/KDF step instead of the BLAKE2b construction. diff --git a/generated/client.ts b/generated/client.ts new file mode 100644 index 00000000..94fb8e62 --- /dev/null +++ b/generated/client.ts @@ -0,0 +1,952 @@ +// Auto-generated by truapi-codegen. Do not edit. + +import { ResultAsync, type Result } from 'neverthrow'; +import * as S from '../scale.js'; +import type { HexString } from '../scale.js'; +import { SubscriptionError } from '../transport.js'; +import type { ObservableLike, Observer, Subscription, SubscriptionFrameIds, TrUApiTransport } from '../transport.js'; +import * as T from './types.js'; +import * as W from './wire-table.js'; + +export { ResultAsync, SubscriptionError }; +export type { ObservableLike, Observer, Result, Subscription, TrUApiTransport }; +export const TRUAPI_VERSION = 1 as const; +export const TRUAPI_CODEC_VERSION = 1 as const; + +function toSubscriptionError(error: unknown): SubscriptionError { + if (error instanceof SubscriptionError) return error as SubscriptionError; + const cause = error instanceof Error ? error : new Error(String(error)); + return new SubscriptionError(cause.message, { cause }); +} + +// ES Observable interop key (rxjs reads Symbol.observable, falling +// back to "@@observable" on platforms without the well-known symbol). +const OBSERVABLE_INTEROP: symbol | string = + (typeof Symbol === "function" && (Symbol as { observable?: symbol }).observable) || + "@@observable"; + +function createObservable({ + transport, + ids, + payload, + decodeItem, + decodeInterrupt, +}: { + transport: TrUApiTransport; + ids: SubscriptionFrameIds; + payload: Uint8Array; + decodeItem: (payload: Uint8Array) => Item; + decodeInterrupt?: (payload: Uint8Array) => Reason; +}): ObservableLike { + const observable: ObservableLike = { + subscribe(observer: Partial> = {}): Subscription { + let closed = false; + let raw: Subscription | undefined; + + const fail = (error: unknown, stop = true) => { + if (closed) return; + closed = true; + try { + if (stop) raw?.unsubscribe(); + } finally { + observer.error?.(toSubscriptionError(error)); + } + }; + + raw = transport.subscribeRaw({ + ids, + payload, + onReceive: (payload) => { + if (closed) return; + try { + observer.next?.(decodeItem(payload)); + } catch (error) { + fail(error); + } + }, + onInterrupt: (payload) => { + if (closed) return; + if (decodeInterrupt) { + let reason: unknown; + try { + reason = decodeInterrupt(payload); + } catch (error) { + fail(error, false); + return; + } + fail(new SubscriptionError("Subscription interrupted", { reason }), false); + return; + } + closed = true; + observer.complete?.(); + }, + onClose: fail, + }); + + return { + get subscriptionId() { + return raw?.subscriptionId ?? ""; + }, + unsubscribe: () => { + if (closed) return; + closed = true; + raw?.unsubscribe(); + }, + }; + }, + [OBSERVABLE_INTEROP as typeof Symbol.observable]() { + return observable; + }, + }; + return observable; +} + +/** Account lookup, aliasing, and proof generation. */ +export class AccountClient { + constructor(private readonly transport: TrUApiTransport) {} + + /** Subscribe to account connection status changes. */ + connectionStatusSubscribe(): ObservableLike { + return createObservable({ + transport: this.transport, + ids: W.ACCOUNT_CONNECTION_STATUS_SUBSCRIBE, + payload: S.indexedTaggedUnion({V1: [0, S._void] as const}).enc({ tag: "V1", value: undefined }), + decodeItem: (payload) => (T.VersionedHostAccountConnectionStatusSubscribeItem.dec(payload) as { tag: "V1"; value: T.HostAccountConnectionStatusSubscribeItem } & T.VersionedHostAccountConnectionStatusSubscribeItem).value, + }); +} + + /** Retrieve a product-scoped account. */ +getAccount(request: T.HostAccountGetRequest): ResultAsync> { + return this.transport.request>({ + ids: W.ACCOUNT_GET_ACCOUNT, + payload: T.VersionedHostAccountGetRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostAccountGetResponse, S.CallError(T.VersionedHostAccountGetError))] as const}).dec(payload).value, + }); +} + + /** Retrieve a contextual alias for a product account. */ +getAccountAlias(request: T.HostAccountGetAliasRequest): ResultAsync> { + return this.transport.request>({ + ids: W.ACCOUNT_GET_ACCOUNT_ALIAS, + payload: T.VersionedHostAccountGetAliasRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostAccountGetAliasResponse, S.CallError(T.VersionedHostAccountGetAliasError))] as const}).dec(payload).value, + }); +} + + /** Generate a ring VRF proof for a product account. */ +createAccountProof(request: T.HostAccountCreateProofRequest): ResultAsync> { + return this.transport.request>({ + ids: W.ACCOUNT_CREATE_ACCOUNT_PROOF, + payload: T.VersionedHostAccountCreateProofRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostAccountCreateProofResponse, S.CallError(T.VersionedHostAccountCreateProofError))] as const}).dec(payload).value, + }); +} + + /** List non-product accounts the user owns. */ +getLegacyAccounts(): ResultAsync> { + return this.transport.request>({ + ids: W.ACCOUNT_GET_LEGACY_ACCOUNTS, + payload: T.VersionedHostGetLegacyAccountsRequest.enc({ tag: "V1", value: undefined }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostGetLegacyAccountsResponse, S.CallError(T.VersionedHostGetLegacyAccountsError))] as const}).dec(payload).value, + }); +} + + /** Fetch the user's primary identity. */ +getUserId(): ResultAsync> { + return this.transport.request>({ + ids: W.ACCOUNT_GET_USER_ID, + payload: T.VersionedHostGetUserIdRequest.enc({ tag: "V1", value: undefined }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostGetUserIdResponse, S.CallError(T.VersionedHostGetUserIdError))] as const}).dec(payload).value, + }); +} + + /** + * Request the host to present the login flow to the user. + * + * Products should call this in response to a user action (e.g. tapping a + * "Sign in" button), not automatically on load. + */ +requestLogin(request: T.HostRequestLoginRequest): ResultAsync> { + return this.transport.request>({ + ids: W.ACCOUNT_REQUEST_LOGIN, + payload: T.VersionedHostRequestLoginRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostRequestLoginResponse, S.CallError(T.VersionedHostRequestLoginError))] as const}).dec(payload).value, + }); +} + +} + +/** Chain interaction methods. */ +export class ChainClient { + constructor(private readonly transport: TrUApiTransport) {} + + /** Follow the chain head and receive block events. */ + followHeadSubscribe({ request }: { request: T.RemoteChainHeadFollowRequest }): ObservableLike { + return createObservable({ + transport: this.transport, + ids: W.CHAIN_FOLLOW_HEAD_SUBSCRIBE, + payload: T.VersionedRemoteChainHeadFollowRequest.enc({ tag: "V1", value: request }), + decodeItem: (payload) => (T.VersionedRemoteChainHeadFollowItem.dec(payload) as { tag: "V1"; value: T.RemoteChainHeadFollowItem } & T.VersionedRemoteChainHeadFollowItem).value, + }); +} + + /** Fetch a block header. */ +getHeadHeader(request: T.RemoteChainHeadHeaderRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CHAIN_GET_HEAD_HEADER, + payload: T.VersionedRemoteChainHeadHeaderRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.RemoteChainHeadHeaderResponse, S.CallError(T.VersionedRemoteChainHeadHeaderError))] as const}).dec(payload).value, + }); +} + + /** Fetch a block body. */ +getHeadBody(request: T.RemoteChainHeadBodyRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CHAIN_GET_HEAD_BODY, + payload: T.VersionedRemoteChainHeadBodyRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.RemoteChainHeadBodyResponse, S.CallError(T.VersionedRemoteChainHeadBodyError))] as const}).dec(payload).value, + }); +} + + /** Query runtime storage at a specific block. */ +getHeadStorage(request: T.RemoteChainHeadStorageRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CHAIN_GET_HEAD_STORAGE, + payload: T.VersionedRemoteChainHeadStorageRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.RemoteChainHeadStorageResponse, S.CallError(T.VersionedRemoteChainHeadStorageError))] as const}).dec(payload).value, + }); +} + + /** Invoke a runtime call at a specific block. */ +callHead(request: T.RemoteChainHeadCallRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CHAIN_CALL_HEAD, + payload: T.VersionedRemoteChainHeadCallRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.RemoteChainHeadCallResponse, S.CallError(T.VersionedRemoteChainHeadCallError))] as const}).dec(payload).value, + }); +} + + /** Release pinned blocks. */ +unpinHead(request: T.RemoteChainHeadUnpinRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CHAIN_UNPIN_HEAD, + payload: T.VersionedRemoteChainHeadUnpinRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(S._void, S.CallError(T.VersionedRemoteChainHeadUnpinError))] as const}).dec(payload).value, + }); +} + + /** Continue a paused chain-head operation. */ +continueHead(request: T.RemoteChainHeadContinueRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CHAIN_CONTINUE_HEAD, + payload: T.VersionedRemoteChainHeadContinueRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(S._void, S.CallError(T.VersionedRemoteChainHeadContinueError))] as const}).dec(payload).value, + }); +} + + /** Stop a chain-head operation. */ +stopHeadOperation(request: T.RemoteChainHeadStopOperationRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CHAIN_STOP_HEAD_OPERATION, + payload: T.VersionedRemoteChainHeadStopOperationRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(S._void, S.CallError(T.VersionedRemoteChainHeadStopOperationError))] as const}).dec(payload).value, + }); +} + + /** Fetch the canonical genesis hash for a chain. */ +getSpecGenesisHash(request: T.RemoteChainSpecGenesisHashRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CHAIN_GET_SPEC_GENESIS_HASH, + payload: T.VersionedRemoteChainSpecGenesisHashRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.RemoteChainSpecGenesisHashResponse, S.CallError(T.VersionedRemoteChainSpecGenesisHashError))] as const}).dec(payload).value, + }); +} + + /** Fetch the display name of a chain. */ +getSpecChainName(request: T.RemoteChainSpecChainNameRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CHAIN_GET_SPEC_CHAIN_NAME, + payload: T.VersionedRemoteChainSpecChainNameRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.RemoteChainSpecChainNameResponse, S.CallError(T.VersionedRemoteChainSpecChainNameError))] as const}).dec(payload).value, + }); +} + + /** Fetch the JSON-encoded properties of a chain. */ +getSpecProperties(request: T.RemoteChainSpecPropertiesRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CHAIN_GET_SPEC_PROPERTIES, + payload: T.VersionedRemoteChainSpecPropertiesRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.RemoteChainSpecPropertiesResponse, S.CallError(T.VersionedRemoteChainSpecPropertiesError))] as const}).dec(payload).value, + }); +} + + /** Broadcast a signed transaction. */ +broadcastTransaction(request: T.RemoteChainTransactionBroadcastRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CHAIN_BROADCAST_TRANSACTION, + payload: T.VersionedRemoteChainTransactionBroadcastRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.RemoteChainTransactionBroadcastResponse, S.CallError(T.VersionedRemoteChainTransactionBroadcastError))] as const}).dec(payload).value, + }); +} + + /** Stop a transaction broadcast. */ +stopTransaction(request: T.RemoteChainTransactionStopRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CHAIN_STOP_TRANSACTION, + payload: T.VersionedRemoteChainTransactionStopRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(S._void, S.CallError(T.VersionedRemoteChainTransactionStopError))] as const}).dec(payload).value, + }); +} + +} + +/** Chat room, bot, and message APIs. */ +export class ChatClient { + constructor(private readonly transport: TrUApiTransport) {} + + /** Create a chat room. */ +createRoom(request: T.HostChatCreateRoomRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CHAT_CREATE_ROOM, + payload: T.VersionedHostChatCreateRoomRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostChatCreateRoomResponse, S.CallError(T.VersionedHostChatCreateRoomError))] as const}).dec(payload).value, + }); +} + + /** Register a chat bot. */ +registerBot(request: T.HostChatRegisterBotRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CHAT_REGISTER_BOT, + payload: T.VersionedHostChatRegisterBotRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostChatRegisterBotResponse, S.CallError(T.VersionedHostChatRegisterBotError))] as const}).dec(payload).value, + }); +} + + /** Subscribe to the list of chat rooms. */ + listSubscribe(): ObservableLike { + return createObservable({ + transport: this.transport, + ids: W.CHAT_LIST_SUBSCRIBE, + payload: S.indexedTaggedUnion({V1: [0, S._void] as const}).enc({ tag: "V1", value: undefined }), + decodeItem: (payload) => (T.VersionedHostChatListSubscribeItem.dec(payload) as { tag: "V1"; value: T.HostChatListSubscribeItem } & T.VersionedHostChatListSubscribeItem).value, + }); +} + + /** Post a message to a chat room. */ +postMessage(request: T.HostChatPostMessageRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CHAT_POST_MESSAGE, + payload: T.VersionedHostChatPostMessageRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostChatPostMessageResponse, S.CallError(T.VersionedHostChatPostMessageError))] as const}).dec(payload).value, + }); +} + + /** Subscribe to received chat actions. */ + actionSubscribe(): ObservableLike { + return createObservable({ + transport: this.transport, + ids: W.CHAT_ACTION_SUBSCRIBE, + payload: S.indexedTaggedUnion({V1: [0, S._void] as const}).enc({ tag: "V1", value: undefined }), + decodeItem: (payload) => (T.VersionedHostChatActionSubscribeItem.dec(payload) as { tag: "V1"; value: T.HostChatActionSubscribeItem } & T.VersionedHostChatActionSubscribeItem).value, + }); +} + + /** + * Subscribe to custom message render requests from the host. Each + * emitted item is a [`CustomRendererNode`](crate::v01::CustomRendererNode) + * tree describing the rendered UI. + */ + customMessageRenderSubscribe({ request }: { request: T.ProductChatCustomMessageRenderSubscribeRequest }): ObservableLike { + return createObservable({ + transport: this.transport, + ids: W.CHAT_CUSTOM_MESSAGE_RENDER_SUBSCRIBE, + payload: T.VersionedProductChatCustomMessageRenderSubscribeRequest.enc({ tag: "V1", value: request }), + decodeItem: (payload) => (T.VersionedProductChatCustomMessageRenderSubscribeItem.dec(payload) as { tag: "V1"; value: T.CustomRendererNode } & T.VersionedProductChatCustomMessageRenderSubscribeItem).value, + }); +} + +} + +/** + * CoinPayment operations. + * + * RFC 0017 describes `Resolvable` values for long-running operations. + * TrUAPI represents those as subscriptions whose items are the RFC status + * updates. + */ +export class CoinPaymentClient { + constructor(private readonly transport: TrUApiTransport) {} + + /** Create a new firewalled CoinPayment purse. */ +createPurse(request: T.HostCoinPaymentCreatePurseRequest): ResultAsync> { + return this.transport.request>({ + ids: W.COIN_PAYMENT_CREATE_PURSE, + payload: T.VersionedHostCoinPaymentCreatePurseRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostCoinPaymentCreatePurseResponse, S.CallError(T.VersionedHostCoinPaymentCreatePurseError))] as const}).dec(payload).value, + }); +} + + /** Query product-visible purse metadata and balance. */ +queryPurse(request: T.HostCoinPaymentQueryPurseRequest): ResultAsync> { + return this.transport.request>({ + ids: W.COIN_PAYMENT_QUERY_PURSE, + payload: T.VersionedHostCoinPaymentQueryPurseRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostCoinPaymentQueryPurseResponse, S.CallError(T.VersionedHostCoinPaymentQueryPurseError))] as const}).dec(payload).value, + }); +} + + /** Transfer balance between local purses. */ + rebalancePurse({ request }: { request: T.HostCoinPaymentRebalancePurseRequest }): ObservableLike> { + return createObservable>({ + transport: this.transport, + ids: W.COIN_PAYMENT_REBALANCE_PURSE, + payload: T.VersionedHostCoinPaymentRebalancePurseRequest.enc({ tag: "V1", value: request }), + decodeItem: (payload) => (T.VersionedHostCoinPaymentRebalancePurseItem.dec(payload) as { tag: "V1"; value: T.CoinPaymentStatus } & T.VersionedHostCoinPaymentRebalancePurseItem).value, + decodeInterrupt: (payload) => (S.indexedTaggedUnion({V1: [0, S.CallError(T.VersionedHostCoinPaymentRebalancePurseError)] as const}).dec(payload) as { tag: "V1"; value: S.CallErrorValue } & { tag: "V1"; value: S.CallErrorValue }).value, + }); +} + + /** Delete a purse after draining its balance into another local purse. */ + deletePurse({ request }: { request: T.HostCoinPaymentDeletePurseRequest }): ObservableLike> { + return createObservable>({ + transport: this.transport, + ids: W.COIN_PAYMENT_DELETE_PURSE, + payload: T.VersionedHostCoinPaymentDeletePurseRequest.enc({ tag: "V1", value: request }), + decodeItem: (payload) => (T.VersionedHostCoinPaymentDeletePurseItem.dec(payload) as { tag: "V1"; value: T.CoinPaymentStatus } & T.VersionedHostCoinPaymentDeletePurseItem).value, + decodeInterrupt: (payload) => (S.indexedTaggedUnion({V1: [0, S.CallError(T.VersionedHostCoinPaymentDeletePurseError)] as const}).dec(payload) as { tag: "V1"; value: S.CallErrorValue } & { tag: "V1"; value: S.CallErrorValue }).value, + }); +} + + /** Create a receivable public key for depositing into a purse. */ +createReceivable(request: T.HostCoinPaymentCreateReceivableRequest): ResultAsync> { + return this.transport.request>({ + ids: W.COIN_PAYMENT_CREATE_RECEIVABLE, + payload: T.VersionedHostCoinPaymentCreateReceivableRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostCoinPaymentCreateReceivableResponse, S.CallError(T.VersionedHostCoinPaymentCreateReceivableError))] as const}).dec(payload).value, + }); +} + + /** Create a cheque paying from a local purse to a receivable. */ +createCheque(request: T.HostCoinPaymentCreateChequeRequest): ResultAsync> { + return this.transport.request>({ + ids: W.COIN_PAYMENT_CREATE_CHEQUE, + payload: T.VersionedHostCoinPaymentCreateChequeRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostCoinPaymentCreateChequeResponse, S.CallError(T.VersionedHostCoinPaymentCreateChequeError))] as const}).dec(payload).value, + }); +} + + /** Claim coins from a cheque into the receivable's purse. */ + deposit({ request }: { request: T.HostCoinPaymentDepositRequest }): ObservableLike> { + return createObservable>({ + transport: this.transport, + ids: W.COIN_PAYMENT_DEPOSIT, + payload: T.VersionedHostCoinPaymentDepositRequest.enc({ tag: "V1", value: request }), + decodeItem: (payload) => (T.VersionedHostCoinPaymentDepositItem.dec(payload) as { tag: "V1"; value: T.CoinPaymentStatus } & T.VersionedHostCoinPaymentDepositItem).value, + decodeInterrupt: (payload) => (S.indexedTaggedUnion({V1: [0, S.CallError(T.VersionedHostCoinPaymentDepositError)] as const}).dec(payload) as { tag: "V1"; value: S.CallErrorValue } & { tag: "V1"; value: S.CallErrorValue }).value, + }); +} + + /** Attempt to return coins associated with a receivable. */ + refund({ request }: { request: T.HostCoinPaymentRefundRequest }): ObservableLike> { + return createObservable>({ + transport: this.transport, + ids: W.COIN_PAYMENT_REFUND, + payload: T.VersionedHostCoinPaymentRefundRequest.enc({ tag: "V1", value: request }), + decodeItem: (payload) => (T.VersionedHostCoinPaymentRefundItem.dec(payload) as { tag: "V1"; value: T.CoinPaymentStatus } & T.VersionedHostCoinPaymentRefundItem).value, + decodeInterrupt: (payload) => (S.indexedTaggedUnion({V1: [0, S.CallError(T.VersionedHostCoinPaymentRefundError)] as const}).dec(payload) as { tag: "V1"; value: S.CallErrorValue } & { tag: "V1"; value: S.CallErrorValue }).value, + }); +} + + /** Listen for a cheque delivered through a standard transmission channel. */ + listenForPayment({ request }: { request: T.HostCoinPaymentListenForRequest }): ObservableLike> { + return createObservable>({ + transport: this.transport, + ids: W.COIN_PAYMENT_LISTEN_FOR_PAYMENT, + payload: T.VersionedHostCoinPaymentListenForRequest.enc({ tag: "V1", value: request }), + decodeItem: (payload) => (T.VersionedHostCoinPaymentListenForItem.dec(payload) as { tag: "V1"; value: T.HostCoinPaymentListenForItem } & T.VersionedHostCoinPaymentListenForItem).value, + decodeInterrupt: (payload) => (S.indexedTaggedUnion({V1: [0, S.CallError(T.VersionedHostCoinPaymentListenForError)] as const}).dec(payload) as { tag: "V1"; value: S.CallErrorValue } & { tag: "V1"; value: S.CallErrorValue }).value, + }); +} + +} + +/** + * Contact requests and invitations between users, mediated by the host + * (RFC 0022). + */ +export class ContactsClient { + constructor(private readonly transport: TrUApiTransport) {} + + /** Resolve a peer to their identity key and published exchange key. */ +resolve(request: T.HostContactsResolveRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CONTACTS_RESOLVE, + payload: T.VersionedHostContactsResolveRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostContactsResolveResponse, S.CallError(T.VersionedHostContactsResolveError))] as const}).dec(payload).value, + }); +} + + /** + * Derive a symmetric key shared with a peer, scoped to the calling + * product and a caller-chosen context. Both sides derive the same key + * from the same context; the exchange secrets never leave the host. + */ +deriveSharedKey(request: T.HostContactsDeriveSharedKeyRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CONTACTS_DERIVE_SHARED_KEY, + payload: T.VersionedHostContactsDeriveSharedKeyRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostContactsDeriveSharedKeyResponse, S.CallError(T.VersionedHostContactsDeriveSharedKeyError))] as const}).dec(payload).value, + }); +} + + /** + * Seal a payload to a recipient and submit it to their contact inbox. + * Triggers the `ContactSend` permission prompt on first use (RFC 0002). + */ +send(request: T.HostContactsSendRequest): ResultAsync> { + return this.transport.request>({ + ids: W.CONTACTS_SEND, + payload: T.VersionedHostContactsSendRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(S._void, S.CallError(T.VersionedHostContactsSendError))] as const}).dec(payload).value, + }); +} + + /** + * Receive contact payloads addressed to the calling product. Replays + * persisted contacts first (`isComplete: false`), then streams live + * ones (`isComplete: true`), as in RFC 0008. + */ + subscribe(): ObservableLike> { + return createObservable>({ + transport: this.transport, + ids: W.CONTACTS_SUBSCRIBE, + payload: S.indexedTaggedUnion({V1: [0, S._void] as const}).enc({ tag: "V1", value: undefined }), + decodeItem: (payload) => (T.VersionedHostContactsSubscribeItem.dec(payload) as { tag: "V1"; value: T.HostContactsSubscribeItem } & T.VersionedHostContactsSubscribeItem).value, + decodeInterrupt: (payload) => (S.indexedTaggedUnion({V1: [0, S.CallError(T.VersionedHostContactsSubscribeError)] as const}).dec(payload) as { tag: "V1"; value: S.CallErrorValue } & { tag: "V1"; value: S.CallErrorValue }).value, + }); +} + +} + +/** Deterministic entropy derivation. */ +export class EntropyClient { + constructor(private readonly transport: TrUApiTransport) {} + + /** Derive deterministic entropy. */ +derive(request: T.HostDeriveEntropyRequest): ResultAsync> { + return this.transport.request>({ + ids: W.ENTROPY_DERIVE, + payload: T.VersionedHostDeriveEntropyRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostDeriveEntropyResponse, S.CallError(T.VersionedHostDeriveEntropyError))] as const}).dec(payload).value, + }); +} + +} + +/** Local key/value storage scoped to the calling product. */ +export class LocalStorageClient { + constructor(private readonly transport: TrUApiTransport) {} + + /** Read a value by key. */ +read(request: T.HostLocalStorageReadRequest): ResultAsync> { + return this.transport.request>({ + ids: W.LOCAL_STORAGE_READ, + payload: T.VersionedHostLocalStorageReadRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostLocalStorageReadResponse, S.CallError(T.VersionedHostLocalStorageReadError))] as const}).dec(payload).value, + }); +} + + /** Write a value to a key. */ +write(request: T.HostLocalStorageWriteRequest): ResultAsync> { + return this.transport.request>({ + ids: W.LOCAL_STORAGE_WRITE, + payload: T.VersionedHostLocalStorageWriteRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(S._void, S.CallError(T.VersionedHostLocalStorageWriteError))] as const}).dec(payload).value, + }); +} + + /** Clear a value by key. */ +clear(request: T.HostLocalStorageClearRequest): ResultAsync> { + return this.transport.request>({ + ids: W.LOCAL_STORAGE_CLEAR, + payload: T.VersionedHostLocalStorageClearRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(S._void, S.CallError(T.VersionedHostLocalStorageClearError))] as const}).dec(payload).value, + }); +} + +} + +/** Notification methods for locally-rendered push notifications. */ +export class NotificationsClient { + constructor(private readonly transport: TrUApiTransport) {} + + /** + * Send a push notification to the user. + * + * Returns a [`NotificationId`](crate::v01::NotificationId) that can be + * passed to [`cancel_push_notification`](Self::cancel_push_notification) + * to retract a scheduled notification. When `scheduled_at` is set the host + * persists the notification across restarts and fires it through the + * platform-native scheduler. See [RFC 0019]. + * + * [RFC 0019]: https://github.com/paritytech/truapi/blob/main/docs/rfcs/0019-scheduled-notifications.md + */ +sendPushNotification(request: T.HostPushNotificationRequest): ResultAsync> { + return this.transport.request>({ + ids: W.NOTIFICATIONS_SEND_PUSH_NOTIFICATION, + payload: T.VersionedHostPushNotificationRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostPushNotificationResponse, S.CallError(T.VersionedHostPushNotificationError))] as const}).dec(payload).value, + }); +} + + /** + * Cancels a previously issued push notification. + * + * Cancellation is idempotent: returns `Ok(())` whether the notification is + * still pending, already fired, or was never issued. See [RFC 0019]. + * + * [RFC 0019]: https://github.com/paritytech/truapi/blob/main/docs/rfcs/0019-scheduled-notifications.md + */ +cancelPushNotification(request: T.HostPushNotificationCancelRequest): ResultAsync> { + return this.transport.request>({ + ids: W.NOTIFICATIONS_CANCEL_PUSH_NOTIFICATION, + payload: T.VersionedHostPushNotificationCancelRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(S._void, S.CallError(T.VersionedHostPushNotificationCancelError))] as const}).dec(payload).value, + }); +} + +} + +/** Payment request and balance/status subscription methods. */ +export class PaymentClient { + constructor(private readonly transport: TrUApiTransport) {} + + /** Subscribe to payment balance updates. */ + balanceSubscribe({ request }: { request: T.HostPaymentBalanceSubscribeRequest }): ObservableLike> { + return createObservable>({ + transport: this.transport, + ids: W.PAYMENT_BALANCE_SUBSCRIBE, + payload: T.VersionedHostPaymentBalanceSubscribeRequest.enc({ tag: "V1", value: request }), + decodeItem: (payload) => (T.VersionedHostPaymentBalanceSubscribeItem.dec(payload) as { tag: "V1"; value: T.HostPaymentBalanceSubscribeItem } & T.VersionedHostPaymentBalanceSubscribeItem).value, + decodeInterrupt: (payload) => (S.indexedTaggedUnion({V1: [0, S.CallError(T.VersionedHostPaymentBalanceSubscribeError)] as const}).dec(payload) as { tag: "V1"; value: S.CallErrorValue } & { tag: "V1"; value: S.CallErrorValue }).value, + }); +} + + /** Request a payment from the user. */ +request(request: T.HostPaymentRequest): ResultAsync> { + return this.transport.request>({ + ids: W.PAYMENT_REQUEST, + payload: T.VersionedHostPaymentRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostPaymentResponse, S.CallError(T.VersionedHostPaymentError))] as const}).dec(payload).value, + }); +} + + /** Subscribe to payment lifecycle updates for a specific payment. */ + statusSubscribe({ request }: { request: T.HostPaymentStatusSubscribeRequest }): ObservableLike> { + return createObservable>({ + transport: this.transport, + ids: W.PAYMENT_STATUS_SUBSCRIBE, + payload: T.VersionedHostPaymentStatusSubscribeRequest.enc({ tag: "V1", value: request }), + decodeItem: (payload) => (T.VersionedHostPaymentStatusSubscribeItem.dec(payload) as { tag: "V1"; value: T.HostPaymentStatusSubscribeItem } & T.VersionedHostPaymentStatusSubscribeItem).value, + decodeInterrupt: (payload) => (S.indexedTaggedUnion({V1: [0, S.CallError(T.VersionedHostPaymentStatusSubscribeError)] as const}).dec(payload) as { tag: "V1"; value: S.CallErrorValue } & { tag: "V1"; value: S.CallErrorValue }).value, + }); +} + + /** Top up the user's payment balance. */ +topUp(request: T.HostPaymentTopUpRequest): ResultAsync> { + return this.transport.request>({ + ids: W.PAYMENT_TOP_UP, + payload: T.VersionedHostPaymentTopUpRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(S._void, S.CallError(T.VersionedHostPaymentTopUpError))] as const}).dec(payload).value, + }); +} + +} + +/** Permission request methods. */ +export class PermissionsClient { + constructor(private readonly transport: TrUApiTransport) {} + + /** Request a device-capability permission from the user. */ +requestDevicePermission(request: T.HostDevicePermissionRequest): ResultAsync> { + return this.transport.request>({ + ids: W.PERMISSIONS_REQUEST_DEVICE_PERMISSION, + payload: T.VersionedHostDevicePermissionRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostDevicePermissionResponse, S.CallError(T.VersionedHostDevicePermissionError))] as const}).dec(payload).value, + }); +} + + /** Request a remote-operation permission. */ +requestRemotePermission(request: T.RemotePermissionRequest): ResultAsync> { + return this.transport.request>({ + ids: W.PERMISSIONS_REQUEST_REMOTE_PERMISSION, + payload: T.VersionedRemotePermissionRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.RemotePermissionResponse, S.CallError(T.VersionedRemotePermissionError))] as const}).dec(payload).value, + }); +} + +} + +/** Preimage lookup and submission methods. */ +export class PreimageClient { + constructor(private readonly transport: TrUApiTransport) {} + + /** Subscribe to preimage lookups for a given key. */ + lookupSubscribe({ request }: { request: T.RemotePreimageLookupSubscribeRequest }): ObservableLike { + return createObservable({ + transport: this.transport, + ids: W.PREIMAGE_LOOKUP_SUBSCRIBE, + payload: T.VersionedRemotePreimageLookupSubscribeRequest.enc({ tag: "V1", value: request }), + decodeItem: (payload) => (T.VersionedRemotePreimageLookupSubscribeItem.dec(payload) as { tag: "V1"; value: T.RemotePreimageLookupSubscribeItem } & T.VersionedRemotePreimageLookupSubscribeItem).value, + }); +} + + /** Submit a preimage. Returns the preimage key (hash) on success. */ +submit(request: HexString): ResultAsync> { + return this.transport.request>({ + ids: W.PREIMAGE_SUBMIT, + payload: T.VersionedRemotePreimageSubmitRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(S.Hex(), S.CallError(T.VersionedRemotePreimageSubmitError))] as const}).dec(payload).value, + }); +} + +} + +/** Resource pre-allocation (allowance management). */ +export class ResourceAllocationClient { + constructor(private readonly transport: TrUApiTransport) {} + + /** Request the host to pre-allocate one or more resources. */ +request(request: T.HostRequestResourceAllocationRequest): ResultAsync> { + return this.transport.request>({ + ids: W.RESOURCE_ALLOCATION_REQUEST, + payload: T.VersionedHostRequestResourceAllocationRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostRequestResourceAllocationResponse, S.CallError(T.VersionedHostRequestResourceAllocationError))] as const}).dec(payload).value, + }); +} + +} + +/** Signing operations. */ +export class SigningClient { + constructor(private readonly transport: TrUApiTransport) {} + + /** Construct a signed transaction for a product account. */ +createTransaction(request: T.ProductAccountTxPayload): ResultAsync> { + return this.transport.request>({ + ids: W.SIGNING_CREATE_TRANSACTION, + payload: T.VersionedHostCreateTransactionRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostCreateTransactionResponse, S.CallError(T.VersionedHostCreateTransactionError))] as const}).dec(payload).value, + }); +} + + /** Construct a signed transaction for a non-product (legacy) account. */ +createTransactionWithLegacyAccount(request: T.LegacyAccountTxPayload): ResultAsync> { + return this.transport.request>({ + ids: W.SIGNING_CREATE_TRANSACTION_WITH_LEGACY_ACCOUNT, + payload: T.VersionedHostCreateTransactionWithLegacyAccountRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostCreateTransactionWithLegacyAccountResponse, S.CallError(T.VersionedHostCreateTransactionWithLegacyAccountError))] as const}).dec(payload).value, + }); +} + + /** Sign raw bytes with a non-product account. */ +signRawWithLegacyAccount(request: T.HostSignRawWithLegacyAccountRequest): ResultAsync> { + return this.transport.request>({ + ids: W.SIGNING_SIGN_RAW_WITH_LEGACY_ACCOUNT, + payload: T.VersionedHostSignRawWithLegacyAccountRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostSignPayloadResponse, S.CallError(T.VersionedHostSignRawWithLegacyAccountError))] as const}).dec(payload).value, + }); +} + + /** Sign an extrinsic payload with a non-product account. */ +signPayloadWithLegacyAccount(request: T.HostSignPayloadWithLegacyAccountRequest): ResultAsync> { + return this.transport.request>({ + ids: W.SIGNING_SIGN_PAYLOAD_WITH_LEGACY_ACCOUNT, + payload: T.VersionedHostSignPayloadWithLegacyAccountRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostSignPayloadResponse, S.CallError(T.VersionedHostSignPayloadWithLegacyAccountError))] as const}).dec(payload).value, + }); +} + + /** Sign raw bytes or a message. */ +signRaw(request: T.HostSignRawRequest): ResultAsync> { + return this.transport.request>({ + ids: W.SIGNING_SIGN_RAW, + payload: T.VersionedHostSignRawRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostSignPayloadResponse, S.CallError(T.VersionedHostSignRawError))] as const}).dec(payload).value, + }); +} + + /** Sign an extrinsic payload. */ +signPayload(request: T.HostSignPayloadRequest): ResultAsync> { + return this.transport.request>({ + ids: W.SIGNING_SIGN_PAYLOAD, + payload: T.VersionedHostSignPayloadRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostSignPayloadResponse, S.CallError(T.VersionedHostSignPayloadError))] as const}).dec(payload).value, + }); +} + +} + +/** Statement store methods. */ +export class StatementStoreClient { + constructor(private readonly transport: TrUApiTransport) {} + + /** Subscribe to statements matching a topic filter. */ + subscribe({ request }: { request: T.RemoteStatementStoreSubscribeRequest }): ObservableLike> { + return createObservable>({ + transport: this.transport, + ids: W.STATEMENT_STORE_SUBSCRIBE, + payload: T.VersionedRemoteStatementStoreSubscribeRequest.enc({ tag: "V1", value: request }), + decodeItem: (payload) => (T.VersionedRemoteStatementStoreSubscribeItem.dec(payload) as { tag: "V1"; value: T.RemoteStatementStoreSubscribeItem } & T.VersionedRemoteStatementStoreSubscribeItem).value, + decodeInterrupt: (payload) => (S.indexedTaggedUnion({V1: [0, S.CallError(T.VersionedRemoteStatementStoreSubscribeError)] as const}).dec(payload) as { tag: "V1"; value: S.CallErrorValue } & { tag: "V1"; value: S.CallErrorValue }).value, + }); +} + + /** + * Create a proof for a statement. + * + * **Deprecated:** use [`create_proof_authorized`](Self::create_proof_authorized) + * instead, which uses a pre-allocated allowance account and does not + * require a per-call signing prompt. + */ +createProof(request: T.RemoteStatementStoreCreateProofRequest): ResultAsync> { + return this.transport.request>({ + ids: W.STATEMENT_STORE_CREATE_PROOF, + payload: T.VersionedRemoteStatementStoreCreateProofRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.RemoteStatementStoreCreateProofResponse, S.CallError(T.VersionedRemoteStatementStoreCreateProofError))] as const}).dec(payload).value, + }); +} + + /** + * Create a proof for a statement using a pre-allocated allowance account, + * bypassing the per-call signing prompt. + */ +createProofAuthorized(request: T.Statement): ResultAsync> { + return this.transport.request>({ + ids: W.STATEMENT_STORE_CREATE_PROOF_AUTHORIZED, + payload: T.VersionedRemoteStatementStoreCreateProofAuthorizedRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.RemoteStatementStoreCreateProofResponse, S.CallError(T.VersionedRemoteStatementStoreCreateProofAuthorizedError))] as const}).dec(payload).value, + }); +} + + /** + * Submit a signed statement to the network. The request body is the + * [`SignedStatement`](crate::v01::SignedStatement) directly (no wrapping + * struct), matching upstream `triangle-js-sdks`. + */ +submit(request: T.SignedStatement): ResultAsync> { + return this.transport.request>({ + ids: W.STATEMENT_STORE_SUBMIT, + payload: T.VersionedRemoteStatementStoreSubmitRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(S._void, S.CallError(T.VersionedRemoteStatementStoreSubmitError))] as const}).dec(payload).value, + }); +} + +} + +/** + * General-purpose TrUAPI methods for handshake, feature detection, + * and navigation. + */ +export class SystemClient { + constructor(private readonly transport: TrUApiTransport) {} + + /** Negotiate the wire codec version with the product. */ +handshake(): ResultAsync> { + return this.transport.request>({ + ids: W.SYSTEM_HANDSHAKE, + payload: T.VersionedHostHandshakeRequest.enc({ tag: "V1", value: { codecVersion: this.transport.codecVersion } }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(S._void, S.CallError(T.VersionedHostHandshakeError))] as const}).dec(payload).value, + }); +} + + /** Query whether the host supports a specific feature. */ +featureSupported(request: T.HostFeatureSupportedRequest): ResultAsync> { + return this.transport.request>({ + ids: W.SYSTEM_FEATURE_SUPPORTED, + payload: T.VersionedHostFeatureSupportedRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(T.HostFeatureSupportedResponse, S.CallError(T.VersionedHostFeatureSupportedError))] as const}).dec(payload).value, + }); +} + + /** Request the host to open a URL. */ +navigateTo(request: T.HostNavigateToRequest): ResultAsync> { + return this.transport.request>({ + ids: W.SYSTEM_NAVIGATE_TO, + payload: T.VersionedHostNavigateToRequest.enc({ tag: "V1", value: request }), + decodeResponse: (payload) => S.indexedTaggedUnion({V1: [0, S.Result(S._void, S.CallError(T.VersionedHostNavigateToError))] as const}).dec(payload).value, + }); +} + +} + +/** Host theme subscription. */ +export class ThemeClient { + constructor(private readonly transport: TrUApiTransport) {} + + /** Subscribe to host theme changes. */ + subscribe(): ObservableLike { + return createObservable({ + transport: this.transport, + ids: W.THEME_SUBSCRIBE, + payload: S.indexedTaggedUnion({V1: [0, S._void] as const}).enc({ tag: "V1", value: undefined }), + decodeItem: (payload) => (T.VersionedHostThemeSubscribeItem.dec(payload) as { tag: "V1"; value: T.HostThemeSubscribeItem } & T.VersionedHostThemeSubscribeItem).value, + }); +} + +} + +export interface TrUApiClient { + readonly account: AccountClient; + readonly chain: ChainClient; + readonly chat: ChatClient; + readonly coinPayment: CoinPaymentClient; + readonly contacts: ContactsClient; + readonly entropy: EntropyClient; + readonly localStorage: LocalStorageClient; + readonly notifications: NotificationsClient; + readonly payment: PaymentClient; + readonly permissions: PermissionsClient; + readonly preimage: PreimageClient; + readonly resourceAllocation: ResourceAllocationClient; + readonly signing: SigningClient; + readonly statementStore: StatementStoreClient; + readonly system: SystemClient; + readonly theme: ThemeClient; +} + +export type Client = TrUApiClient; + +export type GeneratedClientTransport = Omit & + Partial>; + +function withGeneratedCodecVersion(transport: GeneratedClientTransport): TrUApiTransport { + return { + ...transport, + codecVersion: transport.codecVersion ?? TRUAPI_CODEC_VERSION, + }; +} + +/** Creates the generated client facade by binding each service namespace to the + * shared transport instance. */ +export function createClient(transport: GeneratedClientTransport): TrUApiClient { + const transportWithCodecVersion = withGeneratedCodecVersion(transport); + return { + account: new AccountClient(transportWithCodecVersion), + chain: new ChainClient(transportWithCodecVersion), + chat: new ChatClient(transportWithCodecVersion), + coinPayment: new CoinPaymentClient(transportWithCodecVersion), + contacts: new ContactsClient(transportWithCodecVersion), + entropy: new EntropyClient(transportWithCodecVersion), + localStorage: new LocalStorageClient(transportWithCodecVersion), + notifications: new NotificationsClient(transportWithCodecVersion), + payment: new PaymentClient(transportWithCodecVersion), + permissions: new PermissionsClient(transportWithCodecVersion), + preimage: new PreimageClient(transportWithCodecVersion), + resourceAllocation: new ResourceAllocationClient(transportWithCodecVersion), + signing: new SigningClient(transportWithCodecVersion), + statementStore: new StatementStoreClient(transportWithCodecVersion), + system: new SystemClient(transportWithCodecVersion), + theme: new ThemeClient(transportWithCodecVersion), + }; +} diff --git a/generated/index.ts b/generated/index.ts new file mode 100644 index 00000000..87d18788 --- /dev/null +++ b/generated/index.ts @@ -0,0 +1,2 @@ +export * from './types.js'; +export * from './client.js'; diff --git a/generated/types.ts b/generated/types.ts new file mode 100644 index 00000000..83e38c3b --- /dev/null +++ b/generated/types.ts @@ -0,0 +1,3581 @@ +// Auto-generated by truapi-codegen. Do not edit. + +import * as S from '../scale.js'; +import type { HexString } from '../scale.js'; + +/** A 32-byte raw account identifier used for legacy (non-product) accounts. */ +export type AccountId = HexString; + +export const AccountId: S.Codec = S.lazy((): S.Codec => S.Hex(32)); + +/** Payload when a user clicks an action button. */ +export interface ActionTrigger { + /** Message containing the action. */ + messageId: string; + /** Which action was triggered. */ + actionId: string; + /** Optional additional data. */ + payload?: HexString; +} + +export const ActionTrigger: S.Codec = S.lazy((): S.Codec => S.Struct({messageId: S.str, actionId: S.str, payload: S.Option(S.Hex())}) as S.Codec); + +/** + * A resource the host can pre-allocate on behalf of the product (RFC 0010). + * + * For the slot-table allowances (`StatementStoreAllowance`, + * `BulletinAllowance`, `SmartContractAllowance`), pre-allocation is + * opportunistic and the host may also fulfil the allowance implicitly on the + * first submission. `AutoSigning` must be requested explicitly through this + * call. + */ +export type AllocatableResource = + /** Statement Store slot allowance for the product's own allowance account. */ + | { tag: "StatementStoreAllowance"; value?: undefined } + /** Bulletin chain slot allowance for the product's own allowance account. */ + | { tag: "BulletinAllowance"; value?: undefined } + /** + * Pre-warmed PGAS balance for the smart-contract account at the given + * derivation index. + */ + | { tag: "SmartContractAllowance"; value: number } + /** Permission to sign on the product's behalf without per-call user prompts. */ + | { tag: "AutoSigning"; value?: undefined } +; + +export const AllocatableResource: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({StatementStoreAllowance: S._void, BulletinAllowance: S._void, SmartContractAllowance: S.u32, AutoSigning: S._void})); + +/** Outcome of allocating a single resource (RFC 0010). */ +export type AllocationOutcome = "Allocated" | "Rejected" | "NotAvailable"; + +export const AllocationOutcome: S.Codec = S.lazy((): S.Codec => S.Status("Allocated", "Rejected", "NotAvailable")); + +/** Layout arrangement (like CSS flexbox `justify-content`). */ +export type Arrangement = "Start" | "End" | "Center" | "SpaceBetween" | "SpaceAround" | "SpaceEvenly"; + +export const Arrangement: S.Codec = S.lazy((): S.Codec => S.Status("Start", "End", "Center", "SpaceBetween", "SpaceAround", "SpaceEvenly")); + +/** Background styling. */ +export interface Background { + /** Background color. */ + color: ColorToken; + /** Background shape. */ + shape?: Shape; +} + +export const Background: S.Codec = S.lazy((): S.Codec => S.Struct({color: ColorToken, shape: S.Option(Shape)}) as S.Codec); + +/** + * Balance amount for payment operations. Interpreted according to the host's + * single fixed payment asset (e.g. pUSD). + */ +export type Balance = bigint; + +export const Balance: S.Codec = S.lazy((): S.Codec => S.u128); + +/** Border styling. */ +export interface BorderStyle { + /** Border width. */ + width: Size; + /** Border color. */ + color: ColorToken; + /** Border shape. */ + shape?: Shape; +} + +export const BorderStyle: S.Codec = S.lazy((): S.Codec => S.Struct({width: Size, color: ColorToken, shape: S.Option(Shape)}) as S.Codec); + +/** Properties for a [`CustomRendererNode::Box`] container. */ +export interface BoxProps { + /** Content alignment within the box. */ + contentAlignment?: ContentAlignment; +} + +export const BoxProps: S.Codec = S.lazy((): S.Codec => S.Struct({contentAlignment: S.Option(ContentAlignment)}) as S.Codec); + +/** Properties for a [`CustomRendererNode::Button`]. */ +export interface ButtonProps { + /** Button label text. */ + text: string; + /** Button style variant. */ + variant?: ButtonVariant; + /** Whether the button is enabled. Absent leaves the default to the host. */ + enabled: boolean | undefined; + /** Whether the button shows a loading state. Absent leaves the default to the host. */ + loading: boolean | undefined; + /** Action identifier triggered on click. */ + clickAction?: string; +} + +export const ButtonProps: S.Codec = S.lazy((): S.Codec => S.Struct({text: S.str, variant: S.Option(ButtonVariant), enabled: S.OptionBool, loading: S.OptionBool, clickAction: S.Option(S.str)}) as S.Codec); + +/** Button style variants. */ +export type ButtonVariant = "Primary" | "Secondary" | "Text"; + +export const ButtonVariant: S.Codec = S.lazy((): S.Codec => S.Status("Primary", "Secondary", "Text")); + +/** A clickable action button in a chat message. */ +export interface ChatAction { + /** Action identifier. */ + actionId: string; + /** Button label. */ + title: string; +} + +export const ChatAction: S.Codec = S.lazy((): S.Codec => S.Struct({actionId: S.str, title: S.str}) as S.Codec); + +/** Layout for action buttons. */ +export type ChatActionLayout = "Column" | "Grid"; + +export const ChatActionLayout: S.Codec = S.lazy((): S.Codec => S.Status("Column", "Grid")); + +/** Payload of a received chat action. */ +export type ChatActionPayload = + /** A peer posted a message. */ + | { tag: "MessagePosted"; value: ChatMessageContent } + /** A user triggered an action button. */ + | { tag: "ActionTriggered"; value: ActionTrigger } + /** A user issued a command. */ + | { tag: "Command"; value: ChatCommand } +; + +export const ChatActionPayload: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({MessagePosted: ChatMessageContent, ActionTriggered: ActionTrigger, Command: ChatCommand})); + +/** A set of action buttons with optional text. */ +export interface ChatActions { + /** Optional message text. */ + text?: string; + /** List of action buttons. */ + actions: Array; + /** `Column` or `Grid` layout. */ + layout: ChatActionLayout; +} + +export const ChatActions: S.Codec = S.lazy((): S.Codec => S.Struct({text: S.Option(S.str), actions: S.Vector(ChatAction), layout: ChatActionLayout}) as S.Codec); + +/** Whether the bot was newly registered or already existed. */ +export type ChatBotRegistrationStatus = "New" | "Exists"; + +export const ChatBotRegistrationStatus: S.Codec = S.lazy((): S.Codec => S.Status("New", "Exists")); + +/** A slash command from a chat user. */ +export interface ChatCommand { + /** Command name. */ + command: string; + /** Command arguments. */ + payload: string; +} + +export const ChatCommand: S.Codec = S.lazy((): S.Codec => S.Struct({command: S.str, payload: S.str}) as S.Codec); + +/** A custom message with application-defined type and binary payload. */ +export interface ChatCustomMessage { + /** Application-defined type key. */ + messageType: string; + /** Binary payload. */ + payload: HexString; +} + +export const ChatCustomMessage: S.Codec = S.lazy((): S.Codec => S.Struct({messageType: S.str, payload: S.Hex()}) as S.Codec); + +/** A file attachment in a chat message. */ +export interface ChatFile { + /** File download URL. */ + url: string; + /** File name. */ + fileName: string; + /** MIME type. */ + mimeType: string; + /** File size in bytes. */ + sizeBytes: bigint; + /** Optional caption text. */ + text?: string; +} + +export const ChatFile: S.Codec = S.lazy((): S.Codec => S.Struct({url: S.str, fileName: S.str, mimeType: S.str, sizeBytes: S.u64, text: S.Option(S.str)}) as S.Codec); + +/** A media attachment. */ +export interface ChatMedia { + /** Media URL. */ + url: string; +} + +export const ChatMedia: S.Codec = S.lazy((): S.Codec => S.Struct({url: S.str}) as S.Codec); + +/** Content of a chat message -- one of several types. */ +export type ChatMessageContent = + /** Plain text message. */ + | { tag: "Text"; value: { text: string } } + /** Rich text with media. */ + | { tag: "RichText"; value: ChatRichText } + /** Action button set. */ + | { tag: "Actions"; value: ChatActions } + /** File attachment. */ + | { tag: "File"; value: ChatFile } + /** Emoji reaction. */ + | { tag: "Reaction"; value: ChatReaction } + /** Reaction removal. */ + | { tag: "ReactionRemoved"; value: ChatReaction } + /** Custom message. */ + | { tag: "Custom"; value: ChatCustomMessage } +; + +export const ChatMessageContent: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Text: S.Struct({text: S.str}) as S.Codec<{ text: string }>, RichText: ChatRichText, Actions: ChatActions, File: ChatFile, Reaction: ChatReaction, ReactionRemoved: ChatReaction, Custom: ChatCustomMessage})); + +/** A reaction to a chat message. */ +export interface ChatReaction { + /** Message being reacted to. */ + messageId: string; + /** Emoji reaction. */ + emoji: string; +} + +export const ChatReaction: S.Codec = S.lazy((): S.Codec => S.Struct({messageId: S.str, emoji: S.str}) as S.Codec); + +/** Rich text message with optional media. */ +export interface ChatRichText { + /** Optional text content. */ + text?: string; + /** Attached media items. */ + media: Array; +} + +export const ChatRichText: S.Codec = S.lazy((): S.Codec => S.Struct({text: S.Option(S.str), media: S.Vector(ChatMedia)}) as S.Codec); + +/** A chat room the product participates in. */ +export interface ChatRoom { + /** Room identifier. */ + roomId: string; + /** `RoomHost` or `Bot`. */ + participatingAs: ChatRoomParticipation; +} + +export const ChatRoom: S.Codec = S.lazy((): S.Codec => S.Struct({roomId: S.str, participatingAs: ChatRoomParticipation}) as S.Codec); + +/** How the product participates in a chat room. */ +export type ChatRoomParticipation = "RoomHost" | "Bot"; + +export const ChatRoomParticipation: S.Codec = S.lazy((): S.Codec => S.Status("RoomHost", "Bot")); + +/** Whether the room was newly created or already existed. */ +export type ChatRoomRegistrationStatus = "New" | "Exists"; + +export const ChatRoomRegistrationStatus: S.Codec = S.lazy((): S.Codec => S.Status("New", "Exists")); + +/** Balance amount for CoinPayment operations. */ +export type CoinPaymentBalance = number; + +export const CoinPaymentBalance: S.Codec = S.lazy((): S.Codec => S.u32); + +/** Standardized encrypted Coinage secret transmission payload. */ +export interface CoinPaymentCheque { + /** Receivable public key protecting the cheque contents. */ + id: CoinPaymentReceivable; + /** Claimed payment amount. */ + amount: CoinPaymentBalance; + /** Concatenated coin secrets encrypted to the receivable. */ + encryptedSecrets: HexString; +} + +export const CoinPaymentCheque: S.Codec = S.lazy((): S.Codec => S.Struct({id: CoinPaymentReceivable, amount: CoinPaymentBalance, encryptedSecrets: S.Hex()}) as S.Codec); + +/** Product-visible clearing reference for reconciliation and receipts. */ +export interface CoinPaymentClearingReference { + /** Clearing Merkle root. */ + root: CoinPaymentMerkleRoot; + /** Product-visible coin key and transaction hash leaves. */ + leaves: Array<[CoinPaymentCoinagePubKey, CoinPaymentTransactionHash]>; +} + +export const CoinPaymentClearingReference: S.Codec = S.lazy((): S.Codec => S.Struct({root: CoinPaymentMerkleRoot, leaves: S.Vector(S.Tuple(CoinPaymentCoinagePubKey, CoinPaymentTransactionHash))}) as S.Codec); + +/** Public Coinage key referenced by clearing evidence. */ +export type CoinPaymentCoinagePubKey = HexString; + +export const CoinPaymentCoinagePubKey: S.Codec = S.lazy((): S.Codec => S.Hex(32)); + +/** Errors returned by CoinPayment host operations. */ +export type CoinPaymentError = "BalanceLow" | "Denied" | "BadCoins" | "SnipedCoins" | "PurseNotFound" | "ReceivableNotFound" | "UnsupportedChannel" | "UserAgentCapabilityUnavailable" | "Internal"; + +export const CoinPaymentError: S.Codec = S.lazy((): S.Codec => S.Status("BalanceLow", "Denied", "BadCoins", "SnipedCoins", "PurseNotFound", "ReceivableNotFound", "UnsupportedChannel", "UserAgentCapabilityUnavailable", "Internal")); + +/** Merkle root for a product-visible clearing reference. */ +export type CoinPaymentMerkleRoot = HexString; + +export const CoinPaymentMerkleRoot: S.Codec = S.lazy((): S.Codec => S.Hex(32)); + +/** Authenticated product identifier recorded for a product-created purse. */ +export type CoinPaymentProductId = string; + +export const CoinPaymentProductId: S.Codec = S.lazy((): S.Codec => S.str); + +/** RFC 0017 CoinPayment purse identifier. */ +export type CoinPaymentPurseId = number; + +export const CoinPaymentPurseId: S.Codec = S.lazy((): S.Codec => S.u32); + +/** Product-visible metadata and balance state for a CoinPayment purse. */ +export interface CoinPaymentPurseInfo { + /** Human-readable purse name supplied by the creating product. */ + name: string; + /** Creation timestamp. */ + created: CoinPaymentTimestamp; + /** Product that created the purse. */ + creator: CoinPaymentProductId; + /** Current product-visible balance. */ + balance: CoinPaymentBalance; +} + +export const CoinPaymentPurseInfo: S.Codec = S.lazy((): S.Codec => S.Struct({name: S.str, created: CoinPaymentTimestamp, creator: CoinPaymentProductId, balance: CoinPaymentBalance}) as S.Codec); + +/** Public key identifying a CoinPayment receivable. */ +export type CoinPaymentReceivable = HexString; + +export const CoinPaymentReceivable: S.Codec = S.lazy((): S.Codec => S.Hex(32)); + +/** Clearing status stream item. */ +export type CoinPaymentStatus = + /** More coins have cleared. */ + | { tag: "Clearing"; value: { clearing: CoinPaymentBalance; cleared: CoinPaymentBalance } } + /** Some or all coins failed to transfer. */ + | { tag: "Failed"; value: { error: CoinPaymentError; cleared: CoinPaymentBalance; reference: CoinPaymentClearingReference } } + /** All coins cleared. */ + | { tag: "Done"; value: { cleared: CoinPaymentBalance; reference: CoinPaymentClearingReference } } +; + +export const CoinPaymentStatus: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Clearing: S.Struct({clearing: CoinPaymentBalance, cleared: CoinPaymentBalance}) as S.Codec<{ clearing: CoinPaymentBalance; cleared: CoinPaymentBalance }>, Failed: S.Struct({error: CoinPaymentError, cleared: CoinPaymentBalance, reference: CoinPaymentClearingReference}) as S.Codec<{ error: CoinPaymentError; cleared: CoinPaymentBalance; reference: CoinPaymentClearingReference }>, Done: S.Struct({cleared: CoinPaymentBalance, reference: CoinPaymentClearingReference}) as S.Codec<{ cleared: CoinPaymentBalance; reference: CoinPaymentClearingReference }>})); + +/** Milliseconds since Unix epoch. */ +export type CoinPaymentTimestamp = bigint; + +export const CoinPaymentTimestamp: S.Codec = S.lazy((): S.Codec => S.u64); + +/** Transaction hash for a product-visible clearing reference. */ +export type CoinPaymentTransactionHash = HexString; + +export const CoinPaymentTransactionHash: S.Codec = S.lazy((): S.Codec => S.Hex(32)); + +/** Standardized cheque transmission channel. */ +export type CoinPaymentTransmissionChannel = + /** Statement-store/HOP handoff identified by an SSS topic. */ + | { tag: "Standard"; value: { sssTopic: HexString } } +; + +export const CoinPaymentTransmissionChannel: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Standard: S.Struct({sssTopic: S.Hex(32)}) as S.Codec<{ sssTopic: HexString }>})); + +/** Semantic color tokens for theming. */ +export type ColorToken = "FgPrimary" | "FgSecondary" | "FgTertiary" | "BgSurfaceMain" | "BgSurfaceContainer" | "BgSurfaceNested" | "FgSuccess" | "FgError" | "FgWarning"; + +export const ColorToken: S.Codec = S.lazy((): S.Codec => S.Status("FgPrimary", "FgSecondary", "FgTertiary", "BgSurfaceMain", "BgSurfaceContainer", "BgSurfaceNested", "FgSuccess", "FgError", "FgWarning")); + +/** Properties for a [`CustomRendererNode::Column`] layout. */ +export interface ColumnProps { + /** Horizontal alignment of children. */ + horizontalAlignment?: HorizontalAlignment; + /** Vertical arrangement of children. */ + verticalArrangement?: Arrangement; +} + +export const ColumnProps: S.Codec = S.lazy((): S.Codec => S.Struct({horizontalAlignment: S.Option(HorizontalAlignment), verticalArrangement: S.Option(Arrangement)}) as S.Codec); + +/** + * A component in the custom renderer UI tree, combining modifiers, typed props, + * and recursive children. + */ +export interface Component

{ + /** Layout and styling modifiers. */ + modifiers: Array; + /** Component-specific properties. */ + props: P; + /** Child nodes. */ + children: Array; +} + +/** Builds a codec for renderer components parameterized by the codec of their + * `props` payload. */ +export function Component

(pCodec: S.Codec

): S.Codec> { + return S.lazy((): S.Codec> => S.Struct({modifiers: S.Vector(Modifier), props: pCodec, children: S.Vector(CustomRendererNode)}) as S.Codec>); +} + +/** One contact delivered to the calling product. */ +export interface ContactDelivery { + /** Authenticated sender identity public key. */ + senderIdentity: HexString; + /** Sender's DotNS username, when the host can resolve one. */ + senderUsername?: string; + /** Opaque product payload. */ + payload: HexString; + /** Unix-seconds timestamp the sender stamped on the contact. */ + sentAt: bigint; +} + +export const ContactDelivery: S.Codec = S.lazy((): S.Codec => S.Struct({senderIdentity: S.Hex(32), senderUsername: S.Option(S.str), payload: S.Hex(), sentAt: S.u64}) as S.Codec); + +/** + * A contact peer addressed by DotNS username or identity public key + * (RFC 0022). + */ +export type ContactPeer = + /** DotNS username, resolved to its owning identity account. */ + | { tag: "Username"; value: string } + /** Identity account public key (the primary-username owner). */ + | { tag: "Identity"; value: HexString } +; + +export const ContactPeer: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Username: S.str, Identity: S.Hex(32)})); + +/** 2D content alignment. */ +export type ContentAlignment = "TopStart" | "TopCenter" | "TopEnd" | "CenterStart" | "Center" | "CenterEnd" | "BottomStart" | "BottomCenter" | "BottomEnd"; + +export const ContentAlignment: S.Codec = S.lazy((): S.Codec => S.Status("TopStart", "TopCenter", "TopEnd", "CenterStart", "Center", "CenterEnd", "BottomStart", "BottomCenter", "BottomEnd")); + +/** + * A node in the custom renderer UI tree. Can be nested recursively via the + * `children` field of each [`Component`]. + */ +export type CustomRendererNode = + /** Empty node. */ + | { tag: "Nil"; value?: undefined } + /** Raw text string. */ + | { tag: "String"; value: { text: string } } + /** Generic container. */ + | { tag: "Box"; value: Component } + /** Vertical layout. */ + | { tag: "Column"; value: Component } + /** Horizontal layout. */ + | { tag: "Row"; value: Component } + /** Flexible space. */ + | { tag: "Spacer"; value: Component } + /** Text display. */ + | { tag: "Text"; value: Component } + /** Interactive button. */ + | { tag: "Button"; value: Component } + /** Text input. */ + | { tag: "TextField"; value: Component } +; + +export const CustomRendererNode: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Nil: S._void, String: S.Struct({text: S.str}) as S.Codec<{ text: string }>, Box: Component(BoxProps), Column: Component(ColumnProps), Row: Component(RowProps), Spacer: Component(S._void), Text: Component(TextProps), Button: Component(ButtonProps), TextField: Component(TextFieldProps)})); + +/** + * CSS-like dimensions: (top, end, bottom, start). + * Bottom defaults to top, start defaults to end when `None`. + */ +export interface Dimensions { + /** Top dimension. */ + top: Size; + /** End dimension. */ + end: Size; + /** Bottom dimension. Defaults to top when absent. */ + bottom?: Size; + /** Start dimension. Defaults to end when absent. */ + start?: Size; +} + +export const Dimensions: S.Codec = S.lazy((): S.Codec => S.Struct({top: Size, end: Size, bottom: S.Option(Size), start: S.Option(Size)}) as S.Codec); + +/** + * Generic error payload carrying a human-readable reason string. Used by many + * methods as a catch-all error type. + */ +export interface GenericError { + reason: string; +} + +export const GenericError: S.Codec = S.lazy((): S.Codec => S.Struct({reason: S.str}) as S.Codec); + +/** A 32-byte chain genesis hash used to identify the target chain. */ +export type GenesisHash = HexString; + +export const GenesisHash: S.Codec = S.lazy((): S.Codec => S.Hex(32)); + +/** Horizontal alignment options. */ +export type HorizontalAlignment = "Start" | "Center" | "End"; + +export const HorizontalAlignment: S.Codec = S.lazy((): S.Codec => S.Status("Start", "Center", "End")); + +/** Versioned envelope for [`HostAccountConnectionStatusSubscribeItem`]. */ +export type VersionedHostAccountConnectionStatusSubscribeItem = + | { tag: "V1"; value: HostAccountConnectionStatusSubscribeItem } +; + +export const VersionedHostAccountConnectionStatusSubscribeItem: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostAccountConnectionStatusSubscribeItem] as const})); + +/** Versioned envelope for [`HostAccountCreateProofError`]. */ +export type VersionedHostAccountCreateProofError = + | { tag: "V1"; value: HostAccountCreateProofError } +; + +export const VersionedHostAccountCreateProofError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostAccountCreateProofError] as const})); + +/** Versioned envelope for [`HostAccountCreateProofRequest`]. */ +export type VersionedHostAccountCreateProofRequest = + | { tag: "V1"; value: HostAccountCreateProofRequest } +; + +export const VersionedHostAccountCreateProofRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostAccountCreateProofRequest] as const})); + +/** Versioned envelope for [`HostAccountCreateProofResponse`]. */ +export type VersionedHostAccountCreateProofResponse = + | { tag: "V1"; value: HostAccountCreateProofResponse } +; + +export const VersionedHostAccountCreateProofResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostAccountCreateProofResponse] as const})); + +/** Versioned envelope for [`HostAccountGetAliasError`]. */ +export type VersionedHostAccountGetAliasError = + | { tag: "V1"; value: HostAccountGetError } +; + +export const VersionedHostAccountGetAliasError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostAccountGetError] as const})); + +/** Versioned envelope for [`HostAccountGetAliasRequest`]. */ +export type VersionedHostAccountGetAliasRequest = + | { tag: "V1"; value: HostAccountGetAliasRequest } +; + +export const VersionedHostAccountGetAliasRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostAccountGetAliasRequest] as const})); + +/** Versioned envelope for [`HostAccountGetAliasResponse`]. */ +export type VersionedHostAccountGetAliasResponse = + | { tag: "V1"; value: HostAccountGetAliasResponse } +; + +export const VersionedHostAccountGetAliasResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostAccountGetAliasResponse] as const})); + +/** Versioned envelope for [`HostAccountGetError`]. */ +export type VersionedHostAccountGetError = + | { tag: "V1"; value: HostAccountGetError } +; + +export const VersionedHostAccountGetError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostAccountGetError] as const})); + +/** Versioned envelope for [`HostAccountGetRequest`]. */ +export type VersionedHostAccountGetRequest = + | { tag: "V1"; value: HostAccountGetRequest } +; + +export const VersionedHostAccountGetRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostAccountGetRequest] as const})); + +/** Versioned envelope for [`HostAccountGetResponse`]. */ +export type VersionedHostAccountGetResponse = + | { tag: "V1"; value: HostAccountGetResponse } +; + +export const VersionedHostAccountGetResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostAccountGetResponse] as const})); + +/** Versioned envelope for [`HostChatActionSubscribeItem`]. */ +export type VersionedHostChatActionSubscribeItem = + | { tag: "V1"; value: HostChatActionSubscribeItem } +; + +export const VersionedHostChatActionSubscribeItem: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostChatActionSubscribeItem] as const})); + +/** Versioned envelope for [`HostChatCreateRoomError`]. */ +export type VersionedHostChatCreateRoomError = + | { tag: "V1"; value: HostChatCreateRoomError } +; + +export const VersionedHostChatCreateRoomError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostChatCreateRoomError] as const})); + +/** Versioned envelope for [`HostChatCreateRoomRequest`]. */ +export type VersionedHostChatCreateRoomRequest = + | { tag: "V1"; value: HostChatCreateRoomRequest } +; + +export const VersionedHostChatCreateRoomRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostChatCreateRoomRequest] as const})); + +/** Versioned envelope for [`HostChatCreateRoomResponse`]. */ +export type VersionedHostChatCreateRoomResponse = + | { tag: "V1"; value: HostChatCreateRoomResponse } +; + +export const VersionedHostChatCreateRoomResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostChatCreateRoomResponse] as const})); + +/** Versioned envelope for [`HostChatListSubscribeItem`]. */ +export type VersionedHostChatListSubscribeItem = + | { tag: "V1"; value: HostChatListSubscribeItem } +; + +export const VersionedHostChatListSubscribeItem: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostChatListSubscribeItem] as const})); + +/** Versioned envelope for [`HostChatPostMessageError`]. */ +export type VersionedHostChatPostMessageError = + | { tag: "V1"; value: HostChatPostMessageError } +; + +export const VersionedHostChatPostMessageError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostChatPostMessageError] as const})); + +/** Versioned envelope for [`HostChatPostMessageRequest`]. */ +export type VersionedHostChatPostMessageRequest = + | { tag: "V1"; value: HostChatPostMessageRequest } +; + +export const VersionedHostChatPostMessageRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostChatPostMessageRequest] as const})); + +/** Versioned envelope for [`HostChatPostMessageResponse`]. */ +export type VersionedHostChatPostMessageResponse = + | { tag: "V1"; value: HostChatPostMessageResponse } +; + +export const VersionedHostChatPostMessageResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostChatPostMessageResponse] as const})); + +/** Versioned envelope for [`HostChatRegisterBotError`]. */ +export type VersionedHostChatRegisterBotError = + | { tag: "V1"; value: HostChatRegisterBotError } +; + +export const VersionedHostChatRegisterBotError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostChatRegisterBotError] as const})); + +/** Versioned envelope for [`HostChatRegisterBotRequest`]. */ +export type VersionedHostChatRegisterBotRequest = + | { tag: "V1"; value: HostChatRegisterBotRequest } +; + +export const VersionedHostChatRegisterBotRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostChatRegisterBotRequest] as const})); + +/** Versioned envelope for [`HostChatRegisterBotResponse`]. */ +export type VersionedHostChatRegisterBotResponse = + | { tag: "V1"; value: HostChatRegisterBotResponse } +; + +export const VersionedHostChatRegisterBotResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostChatRegisterBotResponse] as const})); + +/** Versioned envelope for [`HostCoinPaymentCreateChequeError`]. */ +export type VersionedHostCoinPaymentCreateChequeError = + | { tag: "V1"; value: CoinPaymentError } +; + +export const VersionedHostCoinPaymentCreateChequeError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, CoinPaymentError] as const})); + +/** Versioned envelope for [`HostCoinPaymentCreateChequeRequest`]. */ +export type VersionedHostCoinPaymentCreateChequeRequest = + | { tag: "V1"; value: HostCoinPaymentCreateChequeRequest } +; + +export const VersionedHostCoinPaymentCreateChequeRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCoinPaymentCreateChequeRequest] as const})); + +/** Versioned envelope for [`HostCoinPaymentCreateChequeResponse`]. */ +export type VersionedHostCoinPaymentCreateChequeResponse = + | { tag: "V1"; value: HostCoinPaymentCreateChequeResponse } +; + +export const VersionedHostCoinPaymentCreateChequeResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCoinPaymentCreateChequeResponse] as const})); + +/** Versioned envelope for [`HostCoinPaymentCreatePurseError`]. */ +export type VersionedHostCoinPaymentCreatePurseError = + | { tag: "V1"; value: CoinPaymentError } +; + +export const VersionedHostCoinPaymentCreatePurseError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, CoinPaymentError] as const})); + +/** Versioned envelope for [`HostCoinPaymentCreatePurseRequest`]. */ +export type VersionedHostCoinPaymentCreatePurseRequest = + | { tag: "V1"; value: HostCoinPaymentCreatePurseRequest } +; + +export const VersionedHostCoinPaymentCreatePurseRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCoinPaymentCreatePurseRequest] as const})); + +/** Versioned envelope for [`HostCoinPaymentCreatePurseResponse`]. */ +export type VersionedHostCoinPaymentCreatePurseResponse = + | { tag: "V1"; value: HostCoinPaymentCreatePurseResponse } +; + +export const VersionedHostCoinPaymentCreatePurseResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCoinPaymentCreatePurseResponse] as const})); + +/** Versioned envelope for [`HostCoinPaymentCreateReceivableError`]. */ +export type VersionedHostCoinPaymentCreateReceivableError = + | { tag: "V1"; value: CoinPaymentError } +; + +export const VersionedHostCoinPaymentCreateReceivableError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, CoinPaymentError] as const})); + +/** Versioned envelope for [`HostCoinPaymentCreateReceivableRequest`]. */ +export type VersionedHostCoinPaymentCreateReceivableRequest = + | { tag: "V1"; value: HostCoinPaymentCreateReceivableRequest } +; + +export const VersionedHostCoinPaymentCreateReceivableRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCoinPaymentCreateReceivableRequest] as const})); + +/** Versioned envelope for [`HostCoinPaymentCreateReceivableResponse`]. */ +export type VersionedHostCoinPaymentCreateReceivableResponse = + | { tag: "V1"; value: HostCoinPaymentCreateReceivableResponse } +; + +export const VersionedHostCoinPaymentCreateReceivableResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCoinPaymentCreateReceivableResponse] as const})); + +/** Versioned envelope for [`HostCoinPaymentDeletePurseError`]. */ +export type VersionedHostCoinPaymentDeletePurseError = + | { tag: "V1"; value: CoinPaymentError } +; + +export const VersionedHostCoinPaymentDeletePurseError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, CoinPaymentError] as const})); + +/** Versioned envelope for [`HostCoinPaymentDeletePurseItem`]. */ +export type VersionedHostCoinPaymentDeletePurseItem = + | { tag: "V1"; value: CoinPaymentStatus } +; + +export const VersionedHostCoinPaymentDeletePurseItem: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, CoinPaymentStatus] as const})); + +/** Versioned envelope for [`HostCoinPaymentDeletePurseRequest`]. */ +export type VersionedHostCoinPaymentDeletePurseRequest = + | { tag: "V1"; value: HostCoinPaymentDeletePurseRequest } +; + +export const VersionedHostCoinPaymentDeletePurseRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCoinPaymentDeletePurseRequest] as const})); + +/** Versioned envelope for [`HostCoinPaymentDepositError`]. */ +export type VersionedHostCoinPaymentDepositError = + | { tag: "V1"; value: CoinPaymentError } +; + +export const VersionedHostCoinPaymentDepositError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, CoinPaymentError] as const})); + +/** Versioned envelope for [`HostCoinPaymentDepositItem`]. */ +export type VersionedHostCoinPaymentDepositItem = + | { tag: "V1"; value: CoinPaymentStatus } +; + +export const VersionedHostCoinPaymentDepositItem: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, CoinPaymentStatus] as const})); + +/** Versioned envelope for [`HostCoinPaymentDepositRequest`]. */ +export type VersionedHostCoinPaymentDepositRequest = + | { tag: "V1"; value: HostCoinPaymentDepositRequest } +; + +export const VersionedHostCoinPaymentDepositRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCoinPaymentDepositRequest] as const})); + +/** Versioned envelope for [`HostCoinPaymentListenForError`]. */ +export type VersionedHostCoinPaymentListenForError = + | { tag: "V1"; value: CoinPaymentError } +; + +export const VersionedHostCoinPaymentListenForError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, CoinPaymentError] as const})); + +/** Versioned envelope for [`HostCoinPaymentListenForItem`]. */ +export type VersionedHostCoinPaymentListenForItem = + | { tag: "V1"; value: HostCoinPaymentListenForItem } +; + +export const VersionedHostCoinPaymentListenForItem: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCoinPaymentListenForItem] as const})); + +/** Versioned envelope for [`HostCoinPaymentListenForRequest`]. */ +export type VersionedHostCoinPaymentListenForRequest = + | { tag: "V1"; value: HostCoinPaymentListenForRequest } +; + +export const VersionedHostCoinPaymentListenForRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCoinPaymentListenForRequest] as const})); + +/** Versioned envelope for [`HostCoinPaymentQueryPurseError`]. */ +export type VersionedHostCoinPaymentQueryPurseError = + | { tag: "V1"; value: CoinPaymentError } +; + +export const VersionedHostCoinPaymentQueryPurseError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, CoinPaymentError] as const})); + +/** Versioned envelope for [`HostCoinPaymentQueryPurseRequest`]. */ +export type VersionedHostCoinPaymentQueryPurseRequest = + | { tag: "V1"; value: HostCoinPaymentQueryPurseRequest } +; + +export const VersionedHostCoinPaymentQueryPurseRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCoinPaymentQueryPurseRequest] as const})); + +/** Versioned envelope for [`HostCoinPaymentQueryPurseResponse`]. */ +export type VersionedHostCoinPaymentQueryPurseResponse = + | { tag: "V1"; value: HostCoinPaymentQueryPurseResponse } +; + +export const VersionedHostCoinPaymentQueryPurseResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCoinPaymentQueryPurseResponse] as const})); + +/** Versioned envelope for [`HostCoinPaymentRebalancePurseError`]. */ +export type VersionedHostCoinPaymentRebalancePurseError = + | { tag: "V1"; value: CoinPaymentError } +; + +export const VersionedHostCoinPaymentRebalancePurseError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, CoinPaymentError] as const})); + +/** Versioned envelope for [`HostCoinPaymentRebalancePurseItem`]. */ +export type VersionedHostCoinPaymentRebalancePurseItem = + | { tag: "V1"; value: CoinPaymentStatus } +; + +export const VersionedHostCoinPaymentRebalancePurseItem: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, CoinPaymentStatus] as const})); + +/** Versioned envelope for [`HostCoinPaymentRebalancePurseRequest`]. */ +export type VersionedHostCoinPaymentRebalancePurseRequest = + | { tag: "V1"; value: HostCoinPaymentRebalancePurseRequest } +; + +export const VersionedHostCoinPaymentRebalancePurseRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCoinPaymentRebalancePurseRequest] as const})); + +/** Versioned envelope for [`HostCoinPaymentRefundError`]. */ +export type VersionedHostCoinPaymentRefundError = + | { tag: "V1"; value: CoinPaymentError } +; + +export const VersionedHostCoinPaymentRefundError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, CoinPaymentError] as const})); + +/** Versioned envelope for [`HostCoinPaymentRefundItem`]. */ +export type VersionedHostCoinPaymentRefundItem = + | { tag: "V1"; value: CoinPaymentStatus } +; + +export const VersionedHostCoinPaymentRefundItem: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, CoinPaymentStatus] as const})); + +/** Versioned envelope for [`HostCoinPaymentRefundRequest`]. */ +export type VersionedHostCoinPaymentRefundRequest = + | { tag: "V1"; value: HostCoinPaymentRefundRequest } +; + +export const VersionedHostCoinPaymentRefundRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCoinPaymentRefundRequest] as const})); + +/** Versioned envelope for [`HostContactsDeriveSharedKeyError`]. */ +export type VersionedHostContactsDeriveSharedKeyError = + | { tag: "V1"; value: HostContactsDeriveSharedKeyError } +; + +export const VersionedHostContactsDeriveSharedKeyError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostContactsDeriveSharedKeyError] as const})); + +/** Versioned envelope for [`HostContactsDeriveSharedKeyRequest`]. */ +export type VersionedHostContactsDeriveSharedKeyRequest = + | { tag: "V1"; value: HostContactsDeriveSharedKeyRequest } +; + +export const VersionedHostContactsDeriveSharedKeyRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostContactsDeriveSharedKeyRequest] as const})); + +/** Versioned envelope for [`HostContactsDeriveSharedKeyResponse`]. */ +export type VersionedHostContactsDeriveSharedKeyResponse = + | { tag: "V1"; value: HostContactsDeriveSharedKeyResponse } +; + +export const VersionedHostContactsDeriveSharedKeyResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostContactsDeriveSharedKeyResponse] as const})); + +/** Versioned envelope for [`HostContactsResolveError`]. */ +export type VersionedHostContactsResolveError = + | { tag: "V1"; value: HostContactsResolveError } +; + +export const VersionedHostContactsResolveError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostContactsResolveError] as const})); + +/** Versioned envelope for [`HostContactsResolveRequest`]. */ +export type VersionedHostContactsResolveRequest = + | { tag: "V1"; value: HostContactsResolveRequest } +; + +export const VersionedHostContactsResolveRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostContactsResolveRequest] as const})); + +/** Versioned envelope for [`HostContactsResolveResponse`]. */ +export type VersionedHostContactsResolveResponse = + | { tag: "V1"; value: HostContactsResolveResponse } +; + +export const VersionedHostContactsResolveResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostContactsResolveResponse] as const})); + +/** Versioned envelope for [`HostContactsSendError`]. */ +export type VersionedHostContactsSendError = + | { tag: "V1"; value: HostContactsSendError } +; + +export const VersionedHostContactsSendError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostContactsSendError] as const})); + +/** Versioned envelope for [`HostContactsSendRequest`]. */ +export type VersionedHostContactsSendRequest = + | { tag: "V1"; value: HostContactsSendRequest } +; + +export const VersionedHostContactsSendRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostContactsSendRequest] as const})); + +/** Versioned envelope for [`HostContactsSubscribeError`]. */ +export type VersionedHostContactsSubscribeError = + | { tag: "V1"; value: HostContactsSubscribeError } +; + +export const VersionedHostContactsSubscribeError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostContactsSubscribeError] as const})); + +/** Versioned envelope for [`HostContactsSubscribeItem`]. */ +export type VersionedHostContactsSubscribeItem = + | { tag: "V1"; value: HostContactsSubscribeItem } +; + +export const VersionedHostContactsSubscribeItem: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostContactsSubscribeItem] as const})); + +/** Versioned envelope for [`HostCreateTransactionError`]. */ +export type VersionedHostCreateTransactionError = + | { tag: "V1"; value: HostCreateTransactionError } +; + +export const VersionedHostCreateTransactionError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCreateTransactionError] as const})); + +/** Versioned envelope for [`HostCreateTransactionRequest`]. */ +export type VersionedHostCreateTransactionRequest = + | { tag: "V1"; value: ProductAccountTxPayload } +; + +export const VersionedHostCreateTransactionRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, ProductAccountTxPayload] as const})); + +/** Versioned envelope for [`HostCreateTransactionResponse`]. */ +export type VersionedHostCreateTransactionResponse = + | { tag: "V1"; value: HostCreateTransactionResponse } +; + +export const VersionedHostCreateTransactionResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCreateTransactionResponse] as const})); + +/** Versioned envelope for [`HostCreateTransactionWithLegacyAccountError`]. */ +export type VersionedHostCreateTransactionWithLegacyAccountError = + | { tag: "V1"; value: HostCreateTransactionError } +; + +export const VersionedHostCreateTransactionWithLegacyAccountError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCreateTransactionError] as const})); + +/** Versioned envelope for [`HostCreateTransactionWithLegacyAccountRequest`]. */ +export type VersionedHostCreateTransactionWithLegacyAccountRequest = + | { tag: "V1"; value: LegacyAccountTxPayload } +; + +export const VersionedHostCreateTransactionWithLegacyAccountRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, LegacyAccountTxPayload] as const})); + +/** Versioned envelope for [`HostCreateTransactionWithLegacyAccountResponse`]. */ +export type VersionedHostCreateTransactionWithLegacyAccountResponse = + | { tag: "V1"; value: HostCreateTransactionWithLegacyAccountResponse } +; + +export const VersionedHostCreateTransactionWithLegacyAccountResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostCreateTransactionWithLegacyAccountResponse] as const})); + +/** Versioned envelope for [`HostDeriveEntropyError`]. */ +export type VersionedHostDeriveEntropyError = + | { tag: "V1"; value: HostDeriveEntropyError } +; + +export const VersionedHostDeriveEntropyError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostDeriveEntropyError] as const})); + +/** Versioned envelope for [`HostDeriveEntropyRequest`]. */ +export type VersionedHostDeriveEntropyRequest = + | { tag: "V1"; value: HostDeriveEntropyRequest } +; + +export const VersionedHostDeriveEntropyRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostDeriveEntropyRequest] as const})); + +/** Versioned envelope for [`HostDeriveEntropyResponse`]. */ +export type VersionedHostDeriveEntropyResponse = + | { tag: "V1"; value: HostDeriveEntropyResponse } +; + +export const VersionedHostDeriveEntropyResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostDeriveEntropyResponse] as const})); + +/** Versioned envelope for [`HostDevicePermissionError`]. */ +export type VersionedHostDevicePermissionError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedHostDevicePermissionError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`HostDevicePermissionRequest`]. */ +export type VersionedHostDevicePermissionRequest = + | { tag: "V1"; value: HostDevicePermissionRequest } +; + +export const VersionedHostDevicePermissionRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostDevicePermissionRequest] as const})); + +/** Versioned envelope for [`HostDevicePermissionResponse`]. */ +export type VersionedHostDevicePermissionResponse = + | { tag: "V1"; value: HostDevicePermissionResponse } +; + +export const VersionedHostDevicePermissionResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostDevicePermissionResponse] as const})); + +/** Versioned envelope for [`HostFeatureSupportedError`]. */ +export type VersionedHostFeatureSupportedError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedHostFeatureSupportedError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`HostFeatureSupportedRequest`]. */ +export type VersionedHostFeatureSupportedRequest = + | { tag: "V1"; value: HostFeatureSupportedRequest } +; + +export const VersionedHostFeatureSupportedRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostFeatureSupportedRequest] as const})); + +/** Versioned envelope for [`HostFeatureSupportedResponse`]. */ +export type VersionedHostFeatureSupportedResponse = + | { tag: "V1"; value: HostFeatureSupportedResponse } +; + +export const VersionedHostFeatureSupportedResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostFeatureSupportedResponse] as const})); + +/** Versioned envelope for [`HostGetLegacyAccountsError`]. */ +export type VersionedHostGetLegacyAccountsError = + | { tag: "V1"; value: HostAccountGetError } +; + +export const VersionedHostGetLegacyAccountsError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostAccountGetError] as const})); + +/** Versioned envelope for [`HostGetLegacyAccountsRequest`]. */ +export type VersionedHostGetLegacyAccountsRequest = + | { tag: "V1"; value?: undefined } +; + +export const VersionedHostGetLegacyAccountsRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, S._void] as const})); + +/** Versioned envelope for [`HostGetLegacyAccountsResponse`]. */ +export type VersionedHostGetLegacyAccountsResponse = + | { tag: "V1"; value: HostGetLegacyAccountsResponse } +; + +export const VersionedHostGetLegacyAccountsResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostGetLegacyAccountsResponse] as const})); + +/** Versioned envelope for [`HostGetUserIdError`]. */ +export type VersionedHostGetUserIdError = + | { tag: "V1"; value: HostGetUserIdError } +; + +export const VersionedHostGetUserIdError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostGetUserIdError] as const})); + +/** Versioned envelope for [`HostGetUserIdRequest`]. */ +export type VersionedHostGetUserIdRequest = + | { tag: "V1"; value?: undefined } +; + +export const VersionedHostGetUserIdRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, S._void] as const})); + +/** Versioned envelope for [`HostGetUserIdResponse`]. */ +export type VersionedHostGetUserIdResponse = + | { tag: "V1"; value: HostGetUserIdResponse } +; + +export const VersionedHostGetUserIdResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostGetUserIdResponse] as const})); + +/** Versioned envelope for [`HostHandshakeError`]. */ +export type VersionedHostHandshakeError = + | { tag: "V1"; value: HostHandshakeError } +; + +export const VersionedHostHandshakeError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostHandshakeError] as const})); + +/** Versioned envelope for [`HostHandshakeRequest`]. */ +export type VersionedHostHandshakeRequest = + | { tag: "V1"; value: HostHandshakeRequest } +; + +export const VersionedHostHandshakeRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostHandshakeRequest] as const})); + +/** Versioned envelope for [`HostHandshakeResponse`]. */ +export type VersionedHostHandshakeResponse = + | { tag: "V1"; value?: undefined } +; + +export const VersionedHostHandshakeResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, S._void] as const})); + +/** Versioned envelope for [`HostLocalStorageClearError`]. */ +export type VersionedHostLocalStorageClearError = + | { tag: "V1"; value: HostLocalStorageReadError } +; + +export const VersionedHostLocalStorageClearError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostLocalStorageReadError] as const})); + +/** Versioned envelope for [`HostLocalStorageClearRequest`]. */ +export type VersionedHostLocalStorageClearRequest = + | { tag: "V1"; value: HostLocalStorageClearRequest } +; + +export const VersionedHostLocalStorageClearRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostLocalStorageClearRequest] as const})); + +/** Versioned envelope for [`HostLocalStorageClearResponse`]. */ +export type VersionedHostLocalStorageClearResponse = + | { tag: "V1"; value?: undefined } +; + +export const VersionedHostLocalStorageClearResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, S._void] as const})); + +/** Versioned envelope for [`HostLocalStorageReadError`]. */ +export type VersionedHostLocalStorageReadError = + | { tag: "V1"; value: HostLocalStorageReadError } +; + +export const VersionedHostLocalStorageReadError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostLocalStorageReadError] as const})); + +/** Versioned envelope for [`HostLocalStorageReadRequest`]. */ +export type VersionedHostLocalStorageReadRequest = + | { tag: "V1"; value: HostLocalStorageReadRequest } +; + +export const VersionedHostLocalStorageReadRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostLocalStorageReadRequest] as const})); + +/** Versioned envelope for [`HostLocalStorageReadResponse`]. */ +export type VersionedHostLocalStorageReadResponse = + | { tag: "V1"; value: HostLocalStorageReadResponse } +; + +export const VersionedHostLocalStorageReadResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostLocalStorageReadResponse] as const})); + +/** Versioned envelope for [`HostLocalStorageWriteError`]. */ +export type VersionedHostLocalStorageWriteError = + | { tag: "V1"; value: HostLocalStorageReadError } +; + +export const VersionedHostLocalStorageWriteError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostLocalStorageReadError] as const})); + +/** Versioned envelope for [`HostLocalStorageWriteRequest`]. */ +export type VersionedHostLocalStorageWriteRequest = + | { tag: "V1"; value: HostLocalStorageWriteRequest } +; + +export const VersionedHostLocalStorageWriteRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostLocalStorageWriteRequest] as const})); + +/** Versioned envelope for [`HostLocalStorageWriteResponse`]. */ +export type VersionedHostLocalStorageWriteResponse = + | { tag: "V1"; value?: undefined } +; + +export const VersionedHostLocalStorageWriteResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, S._void] as const})); + +/** Versioned envelope for [`HostNavigateToError`]. */ +export type VersionedHostNavigateToError = + | { tag: "V1"; value: HostNavigateToError } +; + +export const VersionedHostNavigateToError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostNavigateToError] as const})); + +/** Versioned envelope for [`HostNavigateToRequest`]. */ +export type VersionedHostNavigateToRequest = + | { tag: "V1"; value: HostNavigateToRequest } +; + +export const VersionedHostNavigateToRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostNavigateToRequest] as const})); + +/** Versioned envelope for [`HostNavigateToResponse`]. */ +export type VersionedHostNavigateToResponse = + | { tag: "V1"; value?: undefined } +; + +export const VersionedHostNavigateToResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, S._void] as const})); + +/** Versioned envelope for [`HostPaymentBalanceSubscribeError`]. */ +export type VersionedHostPaymentBalanceSubscribeError = + | { tag: "V1"; value: HostPaymentBalanceSubscribeError } +; + +export const VersionedHostPaymentBalanceSubscribeError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostPaymentBalanceSubscribeError] as const})); + +/** Versioned envelope for [`HostPaymentBalanceSubscribeItem`]. */ +export type VersionedHostPaymentBalanceSubscribeItem = + | { tag: "V1"; value: HostPaymentBalanceSubscribeItem } +; + +export const VersionedHostPaymentBalanceSubscribeItem: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostPaymentBalanceSubscribeItem] as const})); + +/** Versioned envelope for [`HostPaymentBalanceSubscribeRequest`]. */ +export type VersionedHostPaymentBalanceSubscribeRequest = + | { tag: "V1"; value: HostPaymentBalanceSubscribeRequest } +; + +export const VersionedHostPaymentBalanceSubscribeRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostPaymentBalanceSubscribeRequest] as const})); + +/** Versioned envelope for [`HostPaymentError`]. */ +export type VersionedHostPaymentError = + | { tag: "V1"; value: HostPaymentError } +; + +export const VersionedHostPaymentError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostPaymentError] as const})); + +/** Versioned envelope for [`HostPaymentRequest`]. */ +export type VersionedHostPaymentRequest = + | { tag: "V1"; value: HostPaymentRequest } +; + +export const VersionedHostPaymentRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostPaymentRequest] as const})); + +/** Versioned envelope for [`HostPaymentResponse`]. */ +export type VersionedHostPaymentResponse = + | { tag: "V1"; value: HostPaymentResponse } +; + +export const VersionedHostPaymentResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostPaymentResponse] as const})); + +/** Versioned envelope for [`HostPaymentStatusSubscribeError`]. */ +export type VersionedHostPaymentStatusSubscribeError = + | { tag: "V1"; value: HostPaymentStatusSubscribeError } +; + +export const VersionedHostPaymentStatusSubscribeError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostPaymentStatusSubscribeError] as const})); + +/** Versioned envelope for [`HostPaymentStatusSubscribeItem`]. */ +export type VersionedHostPaymentStatusSubscribeItem = + | { tag: "V1"; value: HostPaymentStatusSubscribeItem } +; + +export const VersionedHostPaymentStatusSubscribeItem: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostPaymentStatusSubscribeItem] as const})); + +/** Versioned envelope for [`HostPaymentStatusSubscribeRequest`]. */ +export type VersionedHostPaymentStatusSubscribeRequest = + | { tag: "V1"; value: HostPaymentStatusSubscribeRequest } +; + +export const VersionedHostPaymentStatusSubscribeRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostPaymentStatusSubscribeRequest] as const})); + +/** Versioned envelope for [`HostPaymentTopUpError`]. */ +export type VersionedHostPaymentTopUpError = + | { tag: "V1"; value: HostPaymentTopUpError } +; + +export const VersionedHostPaymentTopUpError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostPaymentTopUpError] as const})); + +/** Versioned envelope for [`HostPaymentTopUpRequest`]. */ +export type VersionedHostPaymentTopUpRequest = + | { tag: "V1"; value: HostPaymentTopUpRequest } +; + +export const VersionedHostPaymentTopUpRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostPaymentTopUpRequest] as const})); + +/** Versioned envelope for [`HostPaymentTopUpResponse`]. */ +export type VersionedHostPaymentTopUpResponse = + | { tag: "V1"; value?: undefined } +; + +export const VersionedHostPaymentTopUpResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, S._void] as const})); + +/** Versioned envelope for [`HostPushNotificationCancelError`]. */ +export type VersionedHostPushNotificationCancelError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedHostPushNotificationCancelError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`HostPushNotificationCancelRequest`]. */ +export type VersionedHostPushNotificationCancelRequest = + | { tag: "V1"; value: HostPushNotificationCancelRequest } +; + +export const VersionedHostPushNotificationCancelRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostPushNotificationCancelRequest] as const})); + +/** Versioned envelope for [`HostPushNotificationCancelResponse`]. */ +export type VersionedHostPushNotificationCancelResponse = + | { tag: "V1"; value?: undefined } +; + +export const VersionedHostPushNotificationCancelResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, S._void] as const})); + +/** Versioned envelope for [`HostPushNotificationError`]. */ +export type VersionedHostPushNotificationError = + | { tag: "V1"; value: HostPushNotificationError } +; + +export const VersionedHostPushNotificationError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostPushNotificationError] as const})); + +/** Versioned envelope for [`HostPushNotificationRequest`]. */ +export type VersionedHostPushNotificationRequest = + | { tag: "V1"; value: HostPushNotificationRequest } +; + +export const VersionedHostPushNotificationRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostPushNotificationRequest] as const})); + +/** Versioned envelope for [`HostPushNotificationResponse`]. */ +export type VersionedHostPushNotificationResponse = + | { tag: "V1"; value: HostPushNotificationResponse } +; + +export const VersionedHostPushNotificationResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostPushNotificationResponse] as const})); + +/** Versioned envelope for [`HostRequestLoginError`]. */ +export type VersionedHostRequestLoginError = + | { tag: "V1"; value: HostRequestLoginError } +; + +export const VersionedHostRequestLoginError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostRequestLoginError] as const})); + +/** Versioned envelope for [`HostRequestLoginRequest`]. */ +export type VersionedHostRequestLoginRequest = + | { tag: "V1"; value: HostRequestLoginRequest } +; + +export const VersionedHostRequestLoginRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostRequestLoginRequest] as const})); + +/** Versioned envelope for [`HostRequestLoginResponse`]. */ +export type VersionedHostRequestLoginResponse = + | { tag: "V1"; value: HostRequestLoginResponse } +; + +export const VersionedHostRequestLoginResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostRequestLoginResponse] as const})); + +/** Versioned envelope for [`HostRequestResourceAllocationError`]. */ +export type VersionedHostRequestResourceAllocationError = + | { tag: "V1"; value: ResourceAllocationError } +; + +export const VersionedHostRequestResourceAllocationError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, ResourceAllocationError] as const})); + +/** Versioned envelope for [`HostRequestResourceAllocationRequest`]. */ +export type VersionedHostRequestResourceAllocationRequest = + | { tag: "V1"; value: HostRequestResourceAllocationRequest } +; + +export const VersionedHostRequestResourceAllocationRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostRequestResourceAllocationRequest] as const})); + +/** Versioned envelope for [`HostRequestResourceAllocationResponse`]. */ +export type VersionedHostRequestResourceAllocationResponse = + | { tag: "V1"; value: HostRequestResourceAllocationResponse } +; + +export const VersionedHostRequestResourceAllocationResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostRequestResourceAllocationResponse] as const})); + +/** + * Full Substrate extrinsic signing payload with all fields needed for signature + * generation. + */ +export interface HostSignPayloadData { + /** Reference block hash. */ + blockHash: HexString; + /** Reference block number. */ + blockNumber: HexString; + /** Mortality era encoding. */ + era: HexString; + /** Chain genesis hash. */ + genesisHash: HexString; + /** SCALE-encoded call data. */ + method: HexString; + /** Account nonce. */ + nonce: HexString; + /** Runtime spec version. */ + specVersion: HexString; + /** Transaction tip. */ + tip: HexString; + /** Transaction format version. */ + transactionVersion: HexString; + /** Extension identifiers. */ + signedExtensions: Array; + /** Extrinsic version. */ + version: number; + /** For multi-asset tips. */ + assetId?: HexString; + /** CheckMetadataHash extension. */ + metadataHash?: HexString; + /** Metadata mode. */ + mode?: number; + /** Request signed transaction back. */ + withSignedTransaction?: boolean; +} + +export const HostSignPayloadData: S.Codec = S.lazy((): S.Codec => S.Struct({blockHash: S.Hex(), blockNumber: S.Hex(), era: S.Hex(), genesisHash: S.Hex(), method: S.Hex(), nonce: S.Hex(), specVersion: S.Hex(), tip: S.Hex(), transactionVersion: S.Hex(), signedExtensions: S.Vector(S.str), version: S.u32, assetId: S.Option(S.Hex()), metadataHash: S.Option(S.Hex()), mode: S.Option(S.u32), withSignedTransaction: S.Option(S.bool)}) as S.Codec); + +/** Versioned envelope for [`HostSignPayloadError`]. */ +export type VersionedHostSignPayloadError = + | { tag: "V1"; value: HostSignPayloadError } +; + +export const VersionedHostSignPayloadError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostSignPayloadError] as const})); + +/** Versioned envelope for [`HostSignPayloadRequest`]. */ +export type VersionedHostSignPayloadRequest = + | { tag: "V1"; value: HostSignPayloadRequest } +; + +export const VersionedHostSignPayloadRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostSignPayloadRequest] as const})); + +/** Versioned envelope for [`HostSignPayloadResponse`]. */ +export type VersionedHostSignPayloadResponse = + | { tag: "V1"; value: HostSignPayloadResponse } +; + +export const VersionedHostSignPayloadResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostSignPayloadResponse] as const})); + +/** Versioned envelope for [`HostSignPayloadWithLegacyAccountError`]. */ +export type VersionedHostSignPayloadWithLegacyAccountError = + | { tag: "V1"; value: HostSignPayloadError } +; + +export const VersionedHostSignPayloadWithLegacyAccountError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostSignPayloadError] as const})); + +/** Versioned envelope for [`HostSignPayloadWithLegacyAccountRequest`]. */ +export type VersionedHostSignPayloadWithLegacyAccountRequest = + | { tag: "V1"; value: HostSignPayloadWithLegacyAccountRequest } +; + +export const VersionedHostSignPayloadWithLegacyAccountRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostSignPayloadWithLegacyAccountRequest] as const})); + +/** Versioned envelope for [`HostSignPayloadWithLegacyAccountResponse`]. */ +export type VersionedHostSignPayloadWithLegacyAccountResponse = + | { tag: "V1"; value: HostSignPayloadResponse } +; + +export const VersionedHostSignPayloadWithLegacyAccountResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostSignPayloadResponse] as const})); + +/** Versioned envelope for [`HostSignRawError`]. */ +export type VersionedHostSignRawError = + | { tag: "V1"; value: HostSignPayloadError } +; + +export const VersionedHostSignRawError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostSignPayloadError] as const})); + +/** Versioned envelope for [`HostSignRawRequest`]. */ +export type VersionedHostSignRawRequest = + | { tag: "V1"; value: HostSignRawRequest } +; + +export const VersionedHostSignRawRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostSignRawRequest] as const})); + +/** Versioned envelope for [`HostSignRawResponse`]. */ +export type VersionedHostSignRawResponse = + | { tag: "V1"; value: HostSignPayloadResponse } +; + +export const VersionedHostSignRawResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostSignPayloadResponse] as const})); + +/** Versioned envelope for [`HostSignRawWithLegacyAccountError`]. */ +export type VersionedHostSignRawWithLegacyAccountError = + | { tag: "V1"; value: HostSignPayloadError } +; + +export const VersionedHostSignRawWithLegacyAccountError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostSignPayloadError] as const})); + +/** Versioned envelope for [`HostSignRawWithLegacyAccountRequest`]. */ +export type VersionedHostSignRawWithLegacyAccountRequest = + | { tag: "V1"; value: HostSignRawWithLegacyAccountRequest } +; + +export const VersionedHostSignRawWithLegacyAccountRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostSignRawWithLegacyAccountRequest] as const})); + +/** Versioned envelope for [`HostSignRawWithLegacyAccountResponse`]. */ +export type VersionedHostSignRawWithLegacyAccountResponse = + | { tag: "V1"; value: HostSignPayloadResponse } +; + +export const VersionedHostSignRawWithLegacyAccountResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostSignPayloadResponse] as const})); + +/** Versioned envelope for [`HostThemeSubscribeItem`]. */ +export type VersionedHostThemeSubscribeItem = + | { tag: "V1"; value: HostThemeSubscribeItem } +; + +export const VersionedHostThemeSubscribeItem: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, HostThemeSubscribeItem] as const})); + +/** + * A user-imported (legacy) account: public key plus an optional user-chosen + * display name. + * + * Returned by [`HostGetLegacyAccountsResponse`]. Distinct from + * [`ProductAccount`], which is protocol-derived and never carries a label. + */ +export interface LegacyAccount { + /** The account public key (variable-length bytes). */ + publicKey: HexString; + /** Optional user-chosen display name. */ + name?: string; +} + +export const LegacyAccount: S.Codec = S.lazy((): S.Codec => S.Struct({publicKey: S.Hex(), name: S.Option(S.str)}) as S.Codec); + +/** + * Transaction payload for a legacy (non-product) account. + * + * Identical to [`ProductAccountTxPayload`] except the signer is a raw + * 32-byte [`AccountId`]. + */ +export interface LegacyAccountTxPayload { + /** Raw 32-byte public key of the legacy account. */ + signer: AccountId; + /** Chain where the transaction will execute. */ + genesisHash: GenesisHash; + /** SCALE-encoded Call data. */ + callData: HexString; + /** Transaction extensions supplied by the caller. */ + extensions: Array; + /** 0 for Extrinsic V4, runtime-supported value for V5. */ + txExtVersion: number; +} + +export const LegacyAccountTxPayload: S.Codec = S.lazy((): S.Codec => S.Struct({signer: AccountId, genesisHash: GenesisHash, callData: S.Hex(), extensions: S.Vector(TxPayloadExtension), txExtVersion: S.u8}) as S.Codec); + +/** Layout and styling modifiers applied to custom renderer components. */ +export type Modifier = + /** Outer spacing. */ + | { tag: "Margin"; value: Dimensions } + /** Inner spacing. */ + | { tag: "Padding"; value: Dimensions } + /** Background fill. */ + | { tag: "Background"; value: Background } + /** Border style. */ + | { tag: "Border"; value: BorderStyle } + /** Fixed height. */ + | { tag: "Height"; value: { height: Size } } + /** Fixed width. */ + | { tag: "Width"; value: { width: Size } } + /** Minimum width. */ + | { tag: "MinWidth"; value: { width: Size } } + /** Minimum height. */ + | { tag: "MinHeight"; value: { height: Size } } + /** Fill available width. */ + | { tag: "FillWidth"; value: { enabled: boolean } } + /** Fill available height. */ + | { tag: "FillHeight"; value: { enabled: boolean } } +; + +export const Modifier: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Margin: Dimensions, Padding: Dimensions, Background: Background, Border: BorderStyle, Height: S.Struct({height: Size}) as S.Codec<{ height: Size }>, Width: S.Struct({width: Size}) as S.Codec<{ width: Size }>, MinWidth: S.Struct({width: Size}) as S.Codec<{ width: Size }>, MinHeight: S.Struct({height: Size}) as S.Codec<{ height: Size }>, FillWidth: S.Struct({enabled: S.bool}) as S.Codec<{ enabled: boolean }>, FillHeight: S.Struct({enabled: S.bool}) as S.Codec<{ enabled: boolean }>})); + +/** Opaque identifier for a push notification, unique per product. */ +export type NotificationId = number; + +export const NotificationId: S.Codec = S.lazy((): S.Codec => S.u32); + +export type OperationStartedResult = + | { tag: "Started"; value: { operationId: string } } + | { tag: "LimitReached"; value?: undefined } +; + +export const OperationStartedResult: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Started: S.Struct({operationId: S.str}) as S.Codec<{ operationId: string }>, LimitReached: S._void})); + +/** + * Source for a payment top-up operation. + * + * See [RFC 0006]. + * + * [RFC 0006]: https://github.com/paritytech/triangle-js-sdks/pull/94 + */ +export type PaymentTopUpSource = + /** Fund from one of the calling product's scoped accounts. */ + | { tag: "ProductAccount"; value: { derivationIndex: number } } + /** + * Fund from a one-time account represented by its private key. This is a + * standard account holding public funds, not a coin key. + */ + | { tag: "PrivateKey"; value: { sr25519SecretKey: HexString } } + /** + * Fund directly from coin secret keys. Each key is an sr25519 secret + * controlling a single coin. + */ + | { tag: "Coins"; value: { sr25519SecretKeys: Array } } +; + +export const PaymentTopUpSource: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({ProductAccount: S.Struct({derivationIndex: S.u32}) as S.Codec<{ derivationIndex: number }>, PrivateKey: S.Struct({sr25519SecretKey: S.Hex(64)}) as S.Codec<{ sr25519SecretKey: HexString }>, Coins: S.Struct({sr25519SecretKeys: S.Vector(S.Hex(64))}) as S.Codec<{ sr25519SecretKeys: Array }>})); + +/** Preimage submission error. */ +export type PreimageSubmitError = + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const PreimageSubmitError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** A product account: public key only, no display name. */ +export interface ProductAccount { + /** The account public key (variable-length bytes). */ + publicKey: HexString; +} + +export const ProductAccount: S.Codec = S.lazy((): S.Codec => S.Struct({publicKey: S.Hex()}) as S.Codec); + +/** + * Identifies a product-specific account by combining a dotNS domain name with a + * derivation index. + */ +export interface ProductAccountId { + /** A dotNS domain name identifier (e.g., `"my-product.dot"`). */ + dotNsIdentifier: string; + /** Key derivation index for generating product-specific accounts. */ + derivationIndex: number; +} + +export const ProductAccountId: S.Codec = S.lazy((): S.Codec => S.Struct({dotNsIdentifier: S.str, derivationIndex: S.u32}) as S.Codec); + +/** + * Transaction payload for a product account. + * + * Contains everything the host needs to construct a signed extrinsic. + * The signer is a [`ProductAccountId`]; the host resolves the + * corresponding key pair through its account management layer. + */ +export interface ProductAccountTxPayload { + /** Product account that will sign the transaction. */ + signer: ProductAccountId; + /** Chain where the transaction will execute. */ + genesisHash: GenesisHash; + /** SCALE-encoded Call data. */ + callData: HexString; + /** Transaction extensions supplied by the caller. */ + extensions: Array; + /** 0 for Extrinsic V4, runtime-supported value for V5. */ + txExtVersion: number; +} + +export const ProductAccountTxPayload: S.Codec = S.lazy((): S.Codec => S.Struct({signer: ProductAccountId, genesisHash: GenesisHash, callData: S.Hex(), extensions: S.Vector(TxPayloadExtension), txExtVersion: S.u8}) as S.Codec); + +/** Versioned envelope for [`ProductChatCustomMessageRenderSubscribeItem`]. */ +export type VersionedProductChatCustomMessageRenderSubscribeItem = + | { tag: "V1"; value: CustomRendererNode } +; + +export const VersionedProductChatCustomMessageRenderSubscribeItem: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, CustomRendererNode] as const})); + +/** Versioned envelope for [`ProductChatCustomMessageRenderSubscribeRequest`]. */ +export type VersionedProductChatCustomMessageRenderSubscribeRequest = + | { tag: "V1"; value: ProductChatCustomMessageRenderSubscribeRequest } +; + +export const VersionedProductChatCustomMessageRenderSubscribeRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, ProductChatCustomMessageRenderSubscribeRequest] as const})); + +/** Raw data to sign -- either binary bytes or a string message. */ +export type RawPayload = + /** Raw binary data to sign. */ + | { tag: "Bytes"; value: { bytes: HexString } } + /** String message to sign. */ + | { tag: "Payload"; value: { payload: string } } +; + +export const RawPayload: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Bytes: S.Struct({bytes: S.Hex()}) as S.Codec<{ bytes: HexString }>, Payload: S.Struct({payload: S.str}) as S.Codec<{ payload: string }>})); + +/** Versioned envelope for [`RemoteChainHeadBodyError`]. */ +export type VersionedRemoteChainHeadBodyError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedRemoteChainHeadBodyError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`RemoteChainHeadBodyRequest`]. */ +export type VersionedRemoteChainHeadBodyRequest = + | { tag: "V1"; value: RemoteChainHeadBodyRequest } +; + +export const VersionedRemoteChainHeadBodyRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainHeadBodyRequest] as const})); + +/** Versioned envelope for [`RemoteChainHeadBodyResponse`]. */ +export type VersionedRemoteChainHeadBodyResponse = + | { tag: "V1"; value: RemoteChainHeadBodyResponse } +; + +export const VersionedRemoteChainHeadBodyResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainHeadBodyResponse] as const})); + +/** Versioned envelope for [`RemoteChainHeadCallError`]. */ +export type VersionedRemoteChainHeadCallError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedRemoteChainHeadCallError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`RemoteChainHeadCallRequest`]. */ +export type VersionedRemoteChainHeadCallRequest = + | { tag: "V1"; value: RemoteChainHeadCallRequest } +; + +export const VersionedRemoteChainHeadCallRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainHeadCallRequest] as const})); + +/** Versioned envelope for [`RemoteChainHeadCallResponse`]. */ +export type VersionedRemoteChainHeadCallResponse = + | { tag: "V1"; value: RemoteChainHeadCallResponse } +; + +export const VersionedRemoteChainHeadCallResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainHeadCallResponse] as const})); + +/** Versioned envelope for [`RemoteChainHeadContinueError`]. */ +export type VersionedRemoteChainHeadContinueError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedRemoteChainHeadContinueError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`RemoteChainHeadContinueRequest`]. */ +export type VersionedRemoteChainHeadContinueRequest = + | { tag: "V1"; value: RemoteChainHeadContinueRequest } +; + +export const VersionedRemoteChainHeadContinueRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainHeadContinueRequest] as const})); + +/** Versioned envelope for [`RemoteChainHeadContinueResponse`]. */ +export type VersionedRemoteChainHeadContinueResponse = + | { tag: "V1"; value?: undefined } +; + +export const VersionedRemoteChainHeadContinueResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, S._void] as const})); + +/** Versioned envelope for [`RemoteChainHeadFollowItem`]. */ +export type VersionedRemoteChainHeadFollowItem = + | { tag: "V1"; value: RemoteChainHeadFollowItem } +; + +export const VersionedRemoteChainHeadFollowItem: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainHeadFollowItem] as const})); + +/** Versioned envelope for [`RemoteChainHeadFollowRequest`]. */ +export type VersionedRemoteChainHeadFollowRequest = + | { tag: "V1"; value: RemoteChainHeadFollowRequest } +; + +export const VersionedRemoteChainHeadFollowRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainHeadFollowRequest] as const})); + +/** Versioned envelope for [`RemoteChainHeadHeaderError`]. */ +export type VersionedRemoteChainHeadHeaderError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedRemoteChainHeadHeaderError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`RemoteChainHeadHeaderRequest`]. */ +export type VersionedRemoteChainHeadHeaderRequest = + | { tag: "V1"; value: RemoteChainHeadHeaderRequest } +; + +export const VersionedRemoteChainHeadHeaderRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainHeadHeaderRequest] as const})); + +/** Versioned envelope for [`RemoteChainHeadHeaderResponse`]. */ +export type VersionedRemoteChainHeadHeaderResponse = + | { tag: "V1"; value: RemoteChainHeadHeaderResponse } +; + +export const VersionedRemoteChainHeadHeaderResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainHeadHeaderResponse] as const})); + +/** Versioned envelope for [`RemoteChainHeadStopOperationError`]. */ +export type VersionedRemoteChainHeadStopOperationError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedRemoteChainHeadStopOperationError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`RemoteChainHeadStopOperationRequest`]. */ +export type VersionedRemoteChainHeadStopOperationRequest = + | { tag: "V1"; value: RemoteChainHeadStopOperationRequest } +; + +export const VersionedRemoteChainHeadStopOperationRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainHeadStopOperationRequest] as const})); + +/** Versioned envelope for [`RemoteChainHeadStopOperationResponse`]. */ +export type VersionedRemoteChainHeadStopOperationResponse = + | { tag: "V1"; value?: undefined } +; + +export const VersionedRemoteChainHeadStopOperationResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, S._void] as const})); + +/** Versioned envelope for [`RemoteChainHeadStorageError`]. */ +export type VersionedRemoteChainHeadStorageError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedRemoteChainHeadStorageError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`RemoteChainHeadStorageRequest`]. */ +export type VersionedRemoteChainHeadStorageRequest = + | { tag: "V1"; value: RemoteChainHeadStorageRequest } +; + +export const VersionedRemoteChainHeadStorageRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainHeadStorageRequest] as const})); + +/** Versioned envelope for [`RemoteChainHeadStorageResponse`]. */ +export type VersionedRemoteChainHeadStorageResponse = + | { tag: "V1"; value: RemoteChainHeadStorageResponse } +; + +export const VersionedRemoteChainHeadStorageResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainHeadStorageResponse] as const})); + +/** Versioned envelope for [`RemoteChainHeadUnpinError`]. */ +export type VersionedRemoteChainHeadUnpinError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedRemoteChainHeadUnpinError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`RemoteChainHeadUnpinRequest`]. */ +export type VersionedRemoteChainHeadUnpinRequest = + | { tag: "V1"; value: RemoteChainHeadUnpinRequest } +; + +export const VersionedRemoteChainHeadUnpinRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainHeadUnpinRequest] as const})); + +/** Versioned envelope for [`RemoteChainHeadUnpinResponse`]. */ +export type VersionedRemoteChainHeadUnpinResponse = + | { tag: "V1"; value?: undefined } +; + +export const VersionedRemoteChainHeadUnpinResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, S._void] as const})); + +/** Versioned envelope for [`RemoteChainSpecChainNameError`]. */ +export type VersionedRemoteChainSpecChainNameError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedRemoteChainSpecChainNameError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`RemoteChainSpecChainNameRequest`]. */ +export type VersionedRemoteChainSpecChainNameRequest = + | { tag: "V1"; value: RemoteChainSpecChainNameRequest } +; + +export const VersionedRemoteChainSpecChainNameRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainSpecChainNameRequest] as const})); + +/** Versioned envelope for [`RemoteChainSpecChainNameResponse`]. */ +export type VersionedRemoteChainSpecChainNameResponse = + | { tag: "V1"; value: RemoteChainSpecChainNameResponse } +; + +export const VersionedRemoteChainSpecChainNameResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainSpecChainNameResponse] as const})); + +/** Versioned envelope for [`RemoteChainSpecGenesisHashError`]. */ +export type VersionedRemoteChainSpecGenesisHashError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedRemoteChainSpecGenesisHashError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`RemoteChainSpecGenesisHashRequest`]. */ +export type VersionedRemoteChainSpecGenesisHashRequest = + | { tag: "V1"; value: RemoteChainSpecGenesisHashRequest } +; + +export const VersionedRemoteChainSpecGenesisHashRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainSpecGenesisHashRequest] as const})); + +/** Versioned envelope for [`RemoteChainSpecGenesisHashResponse`]. */ +export type VersionedRemoteChainSpecGenesisHashResponse = + | { tag: "V1"; value: RemoteChainSpecGenesisHashResponse } +; + +export const VersionedRemoteChainSpecGenesisHashResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainSpecGenesisHashResponse] as const})); + +/** Versioned envelope for [`RemoteChainSpecPropertiesError`]. */ +export type VersionedRemoteChainSpecPropertiesError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedRemoteChainSpecPropertiesError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`RemoteChainSpecPropertiesRequest`]. */ +export type VersionedRemoteChainSpecPropertiesRequest = + | { tag: "V1"; value: RemoteChainSpecPropertiesRequest } +; + +export const VersionedRemoteChainSpecPropertiesRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainSpecPropertiesRequest] as const})); + +/** Versioned envelope for [`RemoteChainSpecPropertiesResponse`]. */ +export type VersionedRemoteChainSpecPropertiesResponse = + | { tag: "V1"; value: RemoteChainSpecPropertiesResponse } +; + +export const VersionedRemoteChainSpecPropertiesResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainSpecPropertiesResponse] as const})); + +/** Versioned envelope for [`RemoteChainTransactionBroadcastError`]. */ +export type VersionedRemoteChainTransactionBroadcastError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedRemoteChainTransactionBroadcastError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`RemoteChainTransactionBroadcastRequest`]. */ +export type VersionedRemoteChainTransactionBroadcastRequest = + | { tag: "V1"; value: RemoteChainTransactionBroadcastRequest } +; + +export const VersionedRemoteChainTransactionBroadcastRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainTransactionBroadcastRequest] as const})); + +/** Versioned envelope for [`RemoteChainTransactionBroadcastResponse`]. */ +export type VersionedRemoteChainTransactionBroadcastResponse = + | { tag: "V1"; value: RemoteChainTransactionBroadcastResponse } +; + +export const VersionedRemoteChainTransactionBroadcastResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainTransactionBroadcastResponse] as const})); + +/** Versioned envelope for [`RemoteChainTransactionStopError`]. */ +export type VersionedRemoteChainTransactionStopError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedRemoteChainTransactionStopError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`RemoteChainTransactionStopRequest`]. */ +export type VersionedRemoteChainTransactionStopRequest = + | { tag: "V1"; value: RemoteChainTransactionStopRequest } +; + +export const VersionedRemoteChainTransactionStopRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteChainTransactionStopRequest] as const})); + +/** Versioned envelope for [`RemoteChainTransactionStopResponse`]. */ +export type VersionedRemoteChainTransactionStopResponse = + | { tag: "V1"; value?: undefined } +; + +export const VersionedRemoteChainTransactionStopResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, S._void] as const})); + +/** + * One remote-operation permission requested by the product (RFC 0002). + * + * `ChainSubmit`, `PreimageSubmit`, `StatementSubmit`, and `ContactSend` are + * also triggered implicitly by the corresponding business calls when not yet + * granted. + */ +export type RemotePermission = + /** Outbound HTTP/WebSocket access to a set of domains. */ + | { tag: "Remote"; value: { domains: Array } } + /** WebRTC media access. */ + | { tag: "WebRtc"; value?: undefined } + /** Submitting transactions on behalf of the user via `remote_chain_transaction_broadcast`. */ + | { tag: "ChainSubmit"; value?: undefined } + /** Submitting preimages on behalf of the user via `remote_preimage_submit`. */ + | { tag: "PreimageSubmit"; value?: undefined } + /** Submitting statements on behalf of the user via `remote_statement_store_submit`. */ + | { tag: "StatementSubmit"; value?: undefined } + /** Sending contact requests on behalf of the user via `host_contacts_send` (RFC 0022). */ + | { tag: "ContactSend"; value?: undefined } +; + +export const RemotePermission: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Remote: S.Struct({domains: S.Vector(S.str)}) as S.Codec<{ domains: Array }>, WebRtc: S._void, ChainSubmit: S._void, PreimageSubmit: S._void, StatementSubmit: S._void, ContactSend: S._void})); + +/** Versioned envelope for [`RemotePermissionError`]. */ +export type VersionedRemotePermissionError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedRemotePermissionError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`RemotePermissionRequest`]. */ +export type VersionedRemotePermissionRequest = + | { tag: "V1"; value: RemotePermissionRequest } +; + +export const VersionedRemotePermissionRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemotePermissionRequest] as const})); + +/** Versioned envelope for [`RemotePermissionResponse`]. */ +export type VersionedRemotePermissionResponse = + | { tag: "V1"; value: RemotePermissionResponse } +; + +export const VersionedRemotePermissionResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemotePermissionResponse] as const})); + +/** Versioned envelope for [`RemotePreimageLookupSubscribeItem`]. */ +export type VersionedRemotePreimageLookupSubscribeItem = + | { tag: "V1"; value: RemotePreimageLookupSubscribeItem } +; + +export const VersionedRemotePreimageLookupSubscribeItem: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemotePreimageLookupSubscribeItem] as const})); + +/** Versioned envelope for [`RemotePreimageLookupSubscribeRequest`]. */ +export type VersionedRemotePreimageLookupSubscribeRequest = + | { tag: "V1"; value: RemotePreimageLookupSubscribeRequest } +; + +export const VersionedRemotePreimageLookupSubscribeRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemotePreimageLookupSubscribeRequest] as const})); + +/** Versioned envelope for [`RemotePreimageSubmitError`]. */ +export type VersionedRemotePreimageSubmitError = + | { tag: "V1"; value: PreimageSubmitError } +; + +export const VersionedRemotePreimageSubmitError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, PreimageSubmitError] as const})); + +/** Versioned envelope for [`RemotePreimageSubmitRequest`]. */ +export type VersionedRemotePreimageSubmitRequest = + | { tag: "V1"; value: HexString } +; + +export const VersionedRemotePreimageSubmitRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, S.Hex()] as const})); + +/** Versioned envelope for [`RemotePreimageSubmitResponse`]. */ +export type VersionedRemotePreimageSubmitResponse = + | { tag: "V1"; value: HexString } +; + +export const VersionedRemotePreimageSubmitResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, S.Hex()] as const})); + +/** Versioned envelope for [`RemoteStatementStoreCreateProofAuthorizedError`]. */ +export type VersionedRemoteStatementStoreCreateProofAuthorizedError = + | { tag: "V1"; value: RemoteStatementStoreCreateProofError } +; + +export const VersionedRemoteStatementStoreCreateProofAuthorizedError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteStatementStoreCreateProofError] as const})); + +/** Versioned envelope for [`RemoteStatementStoreCreateProofAuthorizedRequest`]. */ +export type VersionedRemoteStatementStoreCreateProofAuthorizedRequest = + | { tag: "V1"; value: Statement } +; + +export const VersionedRemoteStatementStoreCreateProofAuthorizedRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, Statement] as const})); + +/** Versioned envelope for [`RemoteStatementStoreCreateProofAuthorizedResponse`]. */ +export type VersionedRemoteStatementStoreCreateProofAuthorizedResponse = + | { tag: "V1"; value: RemoteStatementStoreCreateProofResponse } +; + +export const VersionedRemoteStatementStoreCreateProofAuthorizedResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteStatementStoreCreateProofResponse] as const})); + +/** Versioned envelope for [`RemoteStatementStoreCreateProofError`]. */ +export type VersionedRemoteStatementStoreCreateProofError = + | { tag: "V1"; value: RemoteStatementStoreCreateProofError } +; + +export const VersionedRemoteStatementStoreCreateProofError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteStatementStoreCreateProofError] as const})); + +/** Versioned envelope for [`RemoteStatementStoreCreateProofRequest`]. */ +export type VersionedRemoteStatementStoreCreateProofRequest = + | { tag: "V1"; value: RemoteStatementStoreCreateProofRequest } +; + +export const VersionedRemoteStatementStoreCreateProofRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteStatementStoreCreateProofRequest] as const})); + +/** Versioned envelope for [`RemoteStatementStoreCreateProofResponse`]. */ +export type VersionedRemoteStatementStoreCreateProofResponse = + | { tag: "V1"; value: RemoteStatementStoreCreateProofResponse } +; + +export const VersionedRemoteStatementStoreCreateProofResponse: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteStatementStoreCreateProofResponse] as const})); + +/** Versioned envelope for [`RemoteStatementStoreSubmitError`]. */ +export type VersionedRemoteStatementStoreSubmitError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedRemoteStatementStoreSubmitError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`RemoteStatementStoreSubmitRequest`]. */ +export type VersionedRemoteStatementStoreSubmitRequest = + | { tag: "V1"; value: SignedStatement } +; + +export const VersionedRemoteStatementStoreSubmitRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, SignedStatement] as const})); + +/** Versioned envelope for [`RemoteStatementStoreSubscribeError`]. */ +export type VersionedRemoteStatementStoreSubscribeError = + | { tag: "V1"; value: GenericError } +; + +export const VersionedRemoteStatementStoreSubscribeError: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, GenericError] as const})); + +/** Versioned envelope for [`RemoteStatementStoreSubscribeItem`]. */ +export type VersionedRemoteStatementStoreSubscribeItem = + | { tag: "V1"; value: RemoteStatementStoreSubscribeItem } +; + +export const VersionedRemoteStatementStoreSubscribeItem: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteStatementStoreSubscribeItem] as const})); + +/** Versioned envelope for [`RemoteStatementStoreSubscribeRequest`]. */ +export type VersionedRemoteStatementStoreSubscribeRequest = + | { tag: "V1"; value: RemoteStatementStoreSubscribeRequest } +; + +export const VersionedRemoteStatementStoreSubscribeRequest: S.Codec = S.lazy((): S.Codec => S.indexedTaggedUnion({V1: [0, RemoteStatementStoreSubscribeRequest] as const})); + +/** Error from [`crate::api::ResourceAllocation::request`]. */ +export type ResourceAllocationError = + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const ResourceAllocationError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** Locates a specific ring on a specific chain for ring VRF operations. */ +export interface RingLocation { + /** Chain genesis hash. */ + genesisHash: HexString; + /** Root hash of the ring. */ + ringRootHash: HexString; + /** Optional location hints. */ + hints?: RingLocationHint; +} + +export const RingLocation: S.Codec = S.lazy((): S.Codec => S.Struct({genesisHash: S.Hex(), ringRootHash: S.Hex(), hints: S.Option(RingLocationHint)}) as S.Codec); + +/** Hints for locating a ring on-chain. */ +export interface RingLocationHint { + /** Optional pallet instance index. */ + palletInstance?: number; +} + +export const RingLocationHint: S.Codec = S.lazy((): S.Codec => S.Struct({palletInstance: S.Option(S.u32)}) as S.Codec); + +/** Properties for a [`CustomRendererNode::Row`] layout. */ +export interface RowProps { + /** Vertical alignment of children. */ + verticalAlignment?: VerticalAlignment; + /** Horizontal arrangement of children. */ + horizontalArrangement?: Arrangement; +} + +export const RowProps: S.Codec = S.lazy((): S.Codec => S.Struct({verticalAlignment: S.Option(VerticalAlignment), horizontalArrangement: S.Option(Arrangement)}) as S.Codec); + +export interface RuntimeApi { + /** Runtime API name. */ + name: string; + /** Runtime API version. */ + version: number; +} + +export const RuntimeApi: S.Codec = S.lazy((): S.Codec => S.Struct({name: S.str, version: S.u32}) as S.Codec); + +export interface RuntimeSpec { + /** Specification name. */ + specName: string; + /** Implementation name. */ + implName: string; + /** Spec version number. */ + specVersion: number; + /** Implementation version. */ + implVersion: number; + /** Transaction format version. */ + transactionVersion?: number; + /** Supported runtime APIs. */ + apis: Array; +} + +export const RuntimeSpec: S.Codec = S.lazy((): S.Codec => S.Struct({specName: S.str, implName: S.str, specVersion: S.u32, implVersion: S.u32, transactionVersion: S.Option(S.u32), apis: S.Vector(RuntimeApi)}) as S.Codec); + +export type RuntimeType = + | { tag: "Valid"; value: RuntimeSpec } + | { tag: "Invalid"; value: { error: string } } +; + +export const RuntimeType: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Valid: RuntimeSpec, Invalid: S.Struct({error: S.str}) as S.Codec<{ error: string }>})); + +/** Shape for borders and backgrounds. */ +export type Shape = + /** Border radius value. */ + | { tag: "Rounded"; value: { radius: Size } } + /** Circular shape. */ + | { tag: "Circle"; value?: undefined } +; + +export const Shape: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Rounded: S.Struct({radius: Size}) as S.Codec<{ radius: Size }>, Circle: S._void})); + +/** A statement with a required (not optional) proof. */ +export interface SignedStatement { + /** Required cryptographic proof. */ + proof: StatementProof; + /** Optional decryption key. */ + decryptionKey?: HexString; + /** Optional Unix timestamp expiry. */ + expiry?: bigint; + /** Optional channel. */ + channel?: HexString; + /** [u8; 32] tags. */ + topics: Array; + /** Optional data payload. */ + data?: HexString; +} + +export const SignedStatement: S.Codec = S.lazy((): S.Codec => S.Struct({proof: StatementProof, decryptionKey: S.Option(S.Hex(32)), expiry: S.Option(S.u64), channel: S.Option(S.Hex(32)), topics: S.Vector(S.Hex(32)), data: S.Option(S.Hex())}) as S.Codec); + +/** + * A size/dimension value (logical pixels) used across the custom renderer. + * + * Encoded as a SCALE `Compact`: the common small values cost a single + * byte on the wire instead of eight. + */ +export type Size = number | bigint; + +export const Size: S.Codec = S.lazy((): S.Codec => S.compact); + +/** A statement with optional proof and metadata. */ +export interface Statement { + /** Optional cryptographic proof. */ + proof?: StatementProof; + /** Optional decryption key. */ + decryptionKey?: HexString; + /** Optional Unix timestamp expiry. */ + expiry?: bigint; + /** Optional channel. */ + channel?: HexString; + /** [u8; 32] tags. */ + topics: Array; + /** Optional data payload. */ + data?: HexString; +} + +export const Statement: S.Codec = S.lazy((): S.Codec => S.Struct({proof: S.Option(StatementProof), decryptionKey: S.Option(S.Hex(32)), expiry: S.Option(S.u64), channel: S.Option(S.Hex(32)), topics: S.Vector(S.Hex(32)), data: S.Option(S.Hex())}) as S.Codec); + +/** Cryptographic proof for a statement. */ +export type StatementProof = + /** Sr25519 signature proof. */ + | { tag: "Sr25519"; value: { signature: HexString; signer: HexString } } + /** Ed25519 signature proof. */ + | { tag: "Ed25519"; value: { signature: HexString; signer: HexString } } + /** ECDSA signature proof. */ + | { tag: "Ecdsa"; value: { signature: HexString; signer: HexString } } + /** On-chain event proof. */ + | { tag: "OnChain"; value: { who: HexString; blockHash: HexString; event: bigint } } +; + +export const StatementProof: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Sr25519: S.Struct({signature: S.Hex(64), signer: S.Hex(32)}) as S.Codec<{ signature: HexString; signer: HexString }>, Ed25519: S.Struct({signature: S.Hex(64), signer: S.Hex(32)}) as S.Codec<{ signature: HexString; signer: HexString }>, Ecdsa: S.Struct({signature: S.Hex(65), signer: S.Hex(33)}) as S.Codec<{ signature: HexString; signer: HexString }>, OnChain: S.Struct({who: S.Hex(32), blockHash: S.Hex(32), event: S.u64}) as S.Codec<{ who: HexString; blockHash: HexString; event: bigint }>})); + +export interface StorageQueryItem { + /** Storage key to query. */ + key: HexString; + /** What to return. */ + queryType: StorageQueryType; +} + +export const StorageQueryItem: S.Codec = S.lazy((): S.Codec => S.Struct({key: S.Hex(), queryType: StorageQueryType}) as S.Codec); + +export type StorageQueryType = "Value" | "Hash" | "ClosestDescendantMerkleValue" | "DescendantsValues" | "DescendantsHashes"; + +export const StorageQueryType: S.Codec = S.lazy((): S.Codec => S.Status("Value", "Hash", "ClosestDescendantMerkleValue", "DescendantsValues", "DescendantsHashes")); + +export interface StorageResultItem { + /** The queried key. */ + key: HexString; + /** Value, if requested. */ + value?: HexString; + /** Hash, if requested. */ + hash?: HexString; + /** Merkle value, if requested. */ + closestDescendantMerkleValue?: HexString; +} + +export const StorageResultItem: S.Codec = S.lazy((): S.Codec => S.Struct({key: S.Hex(), value: S.Option(S.Hex()), hash: S.Option(S.Hex()), closestDescendantMerkleValue: S.Option(S.Hex())}) as S.Codec); + +/** Properties for a [`CustomRendererNode::TextField`]. */ +export interface TextFieldProps { + /** Current text value. */ + text: string; + /** Placeholder text. */ + placeholder?: string; + /** Field label. */ + label?: string; + /** Whether the field is enabled. Absent leaves the default to the host. */ + enabled: boolean | undefined; + /** Action identifier triggered when the value changes. */ + valueChangeAction?: string; +} + +export const TextFieldProps: S.Codec = S.lazy((): S.Codec => S.Struct({text: S.str, placeholder: S.Option(S.str), label: S.Option(S.str), enabled: S.OptionBool, valueChangeAction: S.Option(S.str)}) as S.Codec); + +/** Properties for a [`CustomRendererNode::Text`] display. */ +export interface TextProps { + /** Typography preset. */ + style?: TypographyStyle; + /** Text color. */ + color?: ColorToken; +} + +export const TextProps: S.Codec = S.lazy((): S.Codec => S.Struct({style: S.Option(TypographyStyle), color: S.Option(ColorToken)}) as S.Codec); + +/** Identifies a named theme. */ +export type ThemeName = + /** A custom named theme. */ + | { tag: "Custom"; value: string } + /** The host's default theme. */ + | { tag: "Default"; value?: undefined } +; + +export const ThemeName: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Custom: S.str, Default: S._void})); + +/** Light or dark variant. */ +export type ThemeVariant = "Light" | "Dark"; + +export const ThemeVariant: S.Codec = S.lazy((): S.Codec => S.Status("Light", "Dark")); + +/** 32-byte statement topic. */ +export type Topic = HexString; + +export const Topic: S.Codec = S.lazy((): S.Codec => S.Hex(32)); + +/** A signed extension for a transaction payload. */ +export interface TxPayloadExtension { + /** Extension name (e.g., `"CheckSpecVersion"`). */ + id: string; + /** SCALE-encoded extra data (in extrinsic body). */ + extra: HexString; + /** SCALE-encoded implicit data (signed, not in body). */ + additionalSigned: HexString; +} + +export const TxPayloadExtension: S.Codec = S.lazy((): S.Codec => S.Struct({id: S.str, extra: S.Hex(), additionalSigned: S.Hex()}) as S.Codec); + +/** Text typography presets. */ +export type TypographyStyle = "HeadlineLarge" | "TitleMediumRegular" | "BodyLargeRegular" | "BodyMediumRegular" | "BodySmallRegular"; + +export const TypographyStyle: S.Codec = S.lazy((): S.Codec => S.Status("HeadlineLarge", "TitleMediumRegular", "BodyLargeRegular", "BodyMediumRegular", "BodySmallRegular")); + +/** User's authentication state. */ +export type HostAccountConnectionStatusSubscribeItem = "Disconnected" | "Connected"; + +export const HostAccountConnectionStatusSubscribeItem: S.Codec = S.lazy((): S.Codec => S.Status("Disconnected", "Connected")); + +/** Error returned when ring VRF proof creation fails. */ +export type HostAccountCreateProofError = + /** Ring not available at the specified location. */ + | { tag: "RingNotFound"; value?: undefined } + /** User or host rejected. */ + | { tag: "Rejected"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostAccountCreateProofError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({RingNotFound: S._void, Rejected: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** Request to create a ring VRF proof for a product account. */ +export interface HostAccountCreateProofRequest { + /** Product account that should create the proof. */ + productAccountId: ProductAccountId; + /** Ring location to use for proof generation. */ + ringLocation: RingLocation; + /** Context bytes bound to the proof. */ + context: HexString; +} + +export const HostAccountCreateProofRequest: S.Codec = S.lazy((): S.Codec => S.Struct({productAccountId: ProductAccountId, ringLocation: RingLocation, context: S.Hex()}) as S.Codec); + +/** Response containing a ring VRF proof. */ +export interface HostAccountCreateProofResponse { + /** Variable-length ring VRF proof bytes. */ + proof: HexString; +} + +export const HostAccountCreateProofResponse: S.Codec = S.lazy((): S.Codec => S.Struct({proof: S.Hex()}) as S.Codec); + +/** Request to retrieve a contextual alias for a product account. */ +export interface HostAccountGetAliasRequest { + /** Product account to derive the alias for. */ + productAccountId: ProductAccountId; +} + +export const HostAccountGetAliasRequest: S.Codec = S.lazy((): S.Codec => S.Struct({productAccountId: ProductAccountId}) as S.Codec); + +/** A privacy-preserving alias derived via ring VRF, bound to a specific context. */ +export interface HostAccountGetAliasResponse { + /** 32-byte context identifier. */ + context: HexString; + /** Ring VRF alias (variable length). */ + alias: HexString; +} + +export const HostAccountGetAliasResponse: S.Codec = S.lazy((): S.Codec => S.Struct({context: S.Hex(32), alias: S.Hex()}) as S.Codec); + +/** Error returned when credential/account requests fail. */ +export type HostAccountGetError = + /** User is not logged in. */ + | { tag: "NotConnected"; value?: undefined } + /** User or host rejected the request. */ + | { tag: "Rejected"; value?: undefined } + /** Domain identifier is invalid. */ + | { tag: "DomainNotValid"; value?: undefined } + /** Catch-all error with reason. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostAccountGetError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({NotConnected: S._void, Rejected: S._void, DomainNotValid: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** Request to retrieve a product-scoped account. */ +export interface HostAccountGetRequest { + /** Product account to retrieve. */ + productAccountId: ProductAccountId; +} + +export const HostAccountGetRequest: S.Codec = S.lazy((): S.Codec => S.Struct({productAccountId: ProductAccountId}) as S.Codec); + +/** Response containing a product-scoped account. */ +export interface HostAccountGetResponse { + /** Retrieved product account. */ + account: ProductAccount; +} + +export const HostAccountGetResponse: S.Codec = S.lazy((): S.Codec => S.Struct({account: ProductAccount}) as S.Codec); + +/** A chat action received from the host. */ +export interface HostChatActionSubscribeItem { + /** Room where the action occurred. */ + roomId: string; + /** Peer who initiated the action. */ + peer: string; + /** The action payload. */ + payload: ChatActionPayload; +} + +export const HostChatActionSubscribeItem: S.Codec = S.lazy((): S.Codec => S.Struct({roomId: S.str, peer: S.str, payload: ChatActionPayload}) as S.Codec); + +/** Chat room registration error. */ +export type HostChatCreateRoomError = + /** Not allowed. */ + | { tag: "PermissionDenied"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostChatCreateRoomError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({PermissionDenied: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** Request to create a chat room. */ +export interface HostChatCreateRoomRequest { + /** Unique room identifier. */ + roomId: string; + /** Room display name. */ + name: string; + /** URL or base64 image. */ + icon: string; +} + +export const HostChatCreateRoomRequest: S.Codec = S.lazy((): S.Codec => S.Struct({roomId: S.str, name: S.str, icon: S.str}) as S.Codec); + +/** Result of a room registration. */ +export interface HostChatCreateRoomResponse { + /** `New` or `Exists`. */ + status: ChatRoomRegistrationStatus; +} + +export const HostChatCreateRoomResponse: S.Codec = S.lazy((): S.Codec => S.Struct({status: ChatRoomRegistrationStatus}) as S.Codec); + +/** Item containing the current chat rooms. */ +export interface HostChatListSubscribeItem { + /** Chat rooms the product participates in. */ + rooms: Array; +} + +export const HostChatListSubscribeItem: S.Codec = S.lazy((): S.Codec => S.Struct({rooms: S.Vector(ChatRoom)}) as S.Codec); + +/** Chat message posting error. */ +export type HostChatPostMessageError = + /** Message exceeded size limit. */ + | { tag: "MessageTooLarge"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostChatPostMessageError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({MessageTooLarge: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** Request to post a message to a chat room. */ +export interface HostChatPostMessageRequest { + /** Room to post to. */ + roomId: string; + /** Message content. */ + payload: ChatMessageContent; +} + +export const HostChatPostMessageRequest: S.Codec = S.lazy((): S.Codec => S.Struct({roomId: S.str, payload: ChatMessageContent}) as S.Codec); + +/** Result of posting a message. */ +export interface HostChatPostMessageResponse { + /** Assigned message ID. */ + messageId: string; +} + +export const HostChatPostMessageResponse: S.Codec = S.lazy((): S.Codec => S.Struct({messageId: S.str}) as S.Codec); + +/** Chat bot registration error. */ +export type HostChatRegisterBotError = + /** Not allowed. */ + | { tag: "PermissionDenied"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostChatRegisterBotError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({PermissionDenied: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** Request to register a chat bot. */ +export interface HostChatRegisterBotRequest { + /** Unique bot identifier. */ + botId: string; + /** Bot display name. */ + name: string; + /** URL or base64 image. */ + icon: string; +} + +export const HostChatRegisterBotRequest: S.Codec = S.lazy((): S.Codec => S.Struct({botId: S.str, name: S.str, icon: S.str}) as S.Codec); + +/** Result of a bot registration. */ +export interface HostChatRegisterBotResponse { + /** `New` or `Exists`. */ + status: ChatBotRegistrationStatus; +} + +export const HostChatRegisterBotResponse: S.Codec = S.lazy((): S.Codec => S.Struct({status: ChatBotRegistrationStatus}) as S.Codec); + +/** Request to create a cheque from a local purse to a receivable. */ +export interface HostCoinPaymentCreateChequeRequest { + /** Source purse. */ + from: CoinPaymentPurseId; + /** Destination receivable. */ + to: CoinPaymentReceivable; + /** Payment amount. */ + amount: CoinPaymentBalance; +} + +export const HostCoinPaymentCreateChequeRequest: S.Codec = S.lazy((): S.Codec => S.Struct({from: CoinPaymentPurseId, to: CoinPaymentReceivable, amount: CoinPaymentBalance}) as S.Codec); + +/** Created cheque response. */ +export interface HostCoinPaymentCreateChequeResponse { + /** Encrypted cheque. */ + cheque: CoinPaymentCheque; +} + +export const HostCoinPaymentCreateChequeResponse: S.Codec = S.lazy((): S.Codec => S.Struct({cheque: CoinPaymentCheque}) as S.Codec); + +/** Request to create a new firewalled CoinPayment purse. */ +export interface HostCoinPaymentCreatePurseRequest { + /** Human-readable purse name. */ + name: string; +} + +export const HostCoinPaymentCreatePurseRequest: S.Codec = S.lazy((): S.Codec => S.Struct({name: S.str}) as S.Codec); + +/** Created purse identifier. */ +export interface HostCoinPaymentCreatePurseResponse { + /** Assigned purse identifier. */ + purse: CoinPaymentPurseId; +} + +export const HostCoinPaymentCreatePurseResponse: S.Codec = S.lazy((): S.Codec => S.Struct({purse: CoinPaymentPurseId}) as S.Codec); + +/** Request to create a fresh receivable for a purse. */ +export interface HostCoinPaymentCreateReceivableRequest { + /** Target purse for future deposits. */ + into: CoinPaymentPurseId; +} + +export const HostCoinPaymentCreateReceivableRequest: S.Codec = S.lazy((): S.Codec => S.Struct({into: CoinPaymentPurseId}) as S.Codec); + +/** Created receivable response. */ +export interface HostCoinPaymentCreateReceivableResponse { + /** Receivable public key. */ + receivable: CoinPaymentReceivable; +} + +export const HostCoinPaymentCreateReceivableResponse: S.Codec = S.lazy((): S.Codec => S.Struct({receivable: CoinPaymentReceivable}) as S.Codec); + +/** Request to delete a purse after draining its balance. */ +export interface HostCoinPaymentDeletePurseRequest { + /** Purse to delete. */ + target: CoinPaymentPurseId; + /** Purse that receives drained funds. */ + drainInto: CoinPaymentPurseId; +} + +export const HostCoinPaymentDeletePurseRequest: S.Codec = S.lazy((): S.Codec => S.Struct({target: CoinPaymentPurseId, drainInto: CoinPaymentPurseId}) as S.Codec); + +/** Request to deposit a cheque into the purse associated with its receivable. */ +export interface HostCoinPaymentDepositRequest { + /** Cheque to deposit. */ + cheque: CoinPaymentCheque; +} + +export const HostCoinPaymentDepositRequest: S.Codec = S.lazy((): S.Codec => S.Struct({cheque: CoinPaymentCheque}) as S.Codec); + +/** Stream item for `host_coin_payment_listen_for`. */ +export type HostCoinPaymentListenForItem = + /** Handoff channel suitable for inclusion in an invoice. */ + | { tag: "Channel"; value: CoinPaymentTransmissionChannel } + /** Cheque received through the handoff channel. */ + | { tag: "Cheque"; value: CoinPaymentCheque } +; + +export const HostCoinPaymentListenForItem: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Channel: CoinPaymentTransmissionChannel, Cheque: CoinPaymentCheque})); + +/** Request to listen for a cheque delivered to a receivable. */ +export interface HostCoinPaymentListenForRequest { + /** Receivable to listen for. */ + receivable: CoinPaymentReceivable; +} + +export const HostCoinPaymentListenForRequest: S.Codec = S.lazy((): S.Codec => S.Struct({receivable: CoinPaymentReceivable}) as S.Codec); + +/** Request to query product-visible purse metadata. */ +export interface HostCoinPaymentQueryPurseRequest { + /** Purse to query. */ + purse: CoinPaymentPurseId; +} + +export const HostCoinPaymentQueryPurseRequest: S.Codec = S.lazy((): S.Codec => S.Struct({purse: CoinPaymentPurseId}) as S.Codec); + +/** Product-visible purse metadata response. */ +export interface HostCoinPaymentQueryPurseResponse { + /** Purse information. */ + info: CoinPaymentPurseInfo; +} + +export const HostCoinPaymentQueryPurseResponse: S.Codec = S.lazy((): S.Codec => S.Struct({info: CoinPaymentPurseInfo}) as S.Codec); + +/** Request to transfer balance between local purses. */ +export interface HostCoinPaymentRebalancePurseRequest { + /** Source purse. */ + from: CoinPaymentPurseId; + /** Destination purse. */ + to: CoinPaymentPurseId; + /** Amount to move. */ + amount: CoinPaymentBalance; +} + +export const HostCoinPaymentRebalancePurseRequest: S.Codec = S.lazy((): S.Codec => S.Struct({from: CoinPaymentPurseId, to: CoinPaymentPurseId, amount: CoinPaymentBalance}) as S.Codec); + +/** Request to refund coins associated with a receivable. */ +export interface HostCoinPaymentRefundRequest { + /** Receivable to refund. */ + receivable: CoinPaymentReceivable; +} + +export const HostCoinPaymentRefundRequest: S.Codec = S.lazy((): S.Codec => S.Struct({receivable: CoinPaymentReceivable}) as S.Codec); + +/** Shared-key derivation error. */ +export type HostContactsDeriveSharedKeyError = + /** No identity is registered for the peer. */ + | { tag: "NotFound"; value?: undefined } + /** The peer has no published exchange key. */ + | { tag: "NotReachable"; value?: undefined } + /** The context exceeds 32 bytes. */ + | { tag: "ContextTooLong"; value?: undefined } + /** No identity session is active. */ + | { tag: "NotConnected"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostContactsDeriveSharedKeyError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({NotFound: S._void, NotReachable: S._void, ContextTooLong: S._void, NotConnected: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** + * Request to derive a symmetric key shared with a peer, scoped to the + * calling product and a caller-chosen context. + */ +export interface HostContactsDeriveSharedKeyRequest { + /** Peer to derive the shared key with. */ + peer: ContactPeer; + /** + * Domain-separation context, at most 32 bytes (as in RFC 0007, callers + * hash longer contexts down). + */ + context: HexString; +} + +export const HostContactsDeriveSharedKeyRequest: S.Codec = S.lazy((): S.Codec => S.Struct({peer: ContactPeer, context: S.Hex()}) as S.Codec); + +/** A derived shared key. */ +export interface HostContactsDeriveSharedKeyResponse { + /** The derived 32-byte symmetric key. */ + key: HexString; +} + +export const HostContactsDeriveSharedKeyResponse: S.Codec = S.lazy((): S.Codec => S.Struct({key: S.Hex(32)}) as S.Codec); + +/** Peer resolution error. */ +export type HostContactsResolveError = + /** No identity is registered for the peer. */ + | { tag: "NotFound"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostContactsResolveError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({NotFound: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** Request to resolve a peer's identity and published exchange key. */ +export interface HostContactsResolveRequest { + /** Peer to resolve. */ + peer: ContactPeer; +} + +export const HostContactsResolveRequest: S.Codec = S.lazy((): S.Codec => S.Struct({peer: ContactPeer}) as S.Codec); + +/** A peer's reachability record. */ +export interface HostContactsResolveResponse { + /** Identity account public key. */ + identity: HexString; + /** + * Published X25519 exchange public key. `None`: the identity exists but + * has no published exchange key (e.g. its host predates RFC 0022); the + * peer is not yet reachable. + */ + exchangeKey?: HexString; +} + +export const HostContactsResolveResponse: S.Codec = S.lazy((): S.Codec => S.Struct({identity: S.Hex(32), exchangeKey: S.Option(S.Hex(32))}) as S.Codec); + +/** Contact send error. */ +export type HostContactsSendError = + /** No identity is registered for the recipient. */ + | { tag: "NotFound"; value?: undefined } + /** The recipient has no published exchange key. */ + | { tag: "NotReachable"; value?: undefined } + /** The sealed statement would exceed the store's statement size limit. */ + | { tag: "PayloadTooLarge"; value?: undefined } + /** The user denied the `ContactSend` permission. */ + | { tag: "PermissionDenied"; value?: undefined } + /** No identity session is active. */ + | { tag: "NotConnected"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostContactsSendError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({NotFound: S._void, NotReachable: S._void, PayloadTooLarge: S._void, PermissionDenied: S._void, NotConnected: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** + * Request to seal a payload to a recipient and submit it to their contact + * inbox. + */ +export interface HostContactsSendRequest { + /** Recipient of the contact. */ + recipient: ContactPeer; + /** Opaque product payload carried inside the sealed envelope. */ + payload: HexString; +} + +export const HostContactsSendRequest: S.Codec = S.lazy((): S.Codec => S.Struct({recipient: ContactPeer, payload: S.Hex()}) as S.Codec); + +/** Contacts subscription error. */ +export type HostContactsSubscribeError = + /** No identity session is active. */ + | { tag: "NotConnected"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostContactsSubscribeError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({NotConnected: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** + * Page of contacts delivered by the contacts subscription. The + * `is_complete` flag distinguishes the replay of contacts received before + * this subscription (`false`) from the live-update phase (`true`), as in + * RFC 0008. + */ +export interface HostContactsSubscribeItem { + /** Contacts addressed to the calling product. */ + contacts: Array; + /** + * `false` while the host is still replaying persisted contacts (more + * pages to follow). `true` once the replay is complete; all subsequent + * pages are also `true` and carry only newly-arrived contacts. + */ + isComplete: boolean; +} + +export const HostContactsSubscribeItem: S.Codec = S.lazy((): S.Codec => S.Struct({contacts: S.Vector(ContactDelivery), isComplete: S.bool}) as S.Codec); + +/** Transaction creation error. */ +export type HostCreateTransactionError = + /** Payload could not be deserialized. */ + | { tag: "FailedToDecode"; value?: undefined } + /** User rejected. */ + | { tag: "Rejected"; value?: undefined } + /** Unsupported payload version or extension. */ + | { tag: "NotSupported"; value: { reason: string } } + /** Not authenticated. */ + | { tag: "PermissionDenied"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostCreateTransactionError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({FailedToDecode: S._void, Rejected: S._void, NotSupported: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>, PermissionDenied: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** Response containing a created transaction. */ +export interface HostCreateTransactionResponse { + /** SCALE-encoded signed transaction. */ + transaction: HexString; +} + +export const HostCreateTransactionResponse: S.Codec = S.lazy((): S.Codec => S.Struct({transaction: S.Hex()}) as S.Codec); + +/** Response containing a transaction created with a non-product account. */ +export interface HostCreateTransactionWithLegacyAccountResponse { + /** SCALE-encoded signed transaction. */ + transaction: HexString; +} + +export const HostCreateTransactionWithLegacyAccountResponse: S.Codec = S.lazy((): S.Codec => S.Struct({transaction: S.Hex()}) as S.Codec); + +/** Error from [`crate::api::Entropy::derive`] (RFC 0007). */ +export type HostDeriveEntropyError = + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostDeriveEntropyError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** + * Request to derive deterministic per-product entropy (RFC 0007). + * + * The host derives 32 bytes from product-scoped seed material and `context`. + * Repeated calls with the same `context` for the same product yield the same + * entropy. + */ +export interface HostDeriveEntropyRequest { + /** Domain-separated derivation context. */ + context: HexString; +} + +export const HostDeriveEntropyRequest: S.Codec = S.lazy((): S.Codec => S.Struct({context: S.Hex()}) as S.Codec); + +/** Response carrying 32 bytes of deterministically derived entropy. */ +export interface HostDeriveEntropyResponse { + /** 32 bytes of derived entropy. */ + entropy: HexString; +} + +export const HostDeriveEntropyResponse: S.Codec = S.lazy((): S.Codec => S.Struct({entropy: S.Hex(32)}) as S.Codec); + +/** + * Device-capability permission requested from the host (RFC 0002). + * + * The user's decision is persisted indefinitely after the first prompt and + * survives app restarts, whether the decision was grant or deny; the host + * does not re-prompt on subsequent requests for the same capability. + */ +export type HostDevicePermissionRequest = "Notifications" | "Camera" | "Microphone" | "Bluetooth" | "NFC" | "Location" | "Clipboard" | "OpenUrl" | "Biometrics"; + +export const HostDevicePermissionRequest: S.Codec = S.lazy((): S.Codec => S.Status("Notifications", "Camera", "Microphone", "Bluetooth", "NFC", "Location", "Clipboard", "OpenUrl", "Biometrics")); + +/** Outcome of a device-permission request. */ +export interface HostDevicePermissionResponse { + /** Whether the permission was granted. */ + granted: boolean; +} + +export const HostDevicePermissionResponse: S.Codec = S.lazy((): S.Codec => S.Struct({granted: S.bool}) as S.Codec); + +/** Request to query whether a feature is supported by the host. */ +export type HostFeatureSupportedRequest = + /** Ask whether the host can interact with the chain identified by genesis hash. */ + | { tag: "Chain"; value: { genesisHash: HexString } } +; + +export const HostFeatureSupportedRequest: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Chain: S.Struct({genesisHash: S.Hex()}) as S.Codec<{ genesisHash: HexString }>})); + +/** Response to a feature-support query. */ +export interface HostFeatureSupportedResponse { + /** Whether the feature is supported. */ + supported: boolean; +} + +export const HostFeatureSupportedResponse: S.Codec = S.lazy((): S.Codec => S.Struct({supported: S.bool}) as S.Codec); + +/** Response containing all legacy (user-imported) accounts owned by the user. */ +export interface HostGetLegacyAccountsResponse { + /** Legacy accounts. */ + accounts: Array; +} + +export const HostGetLegacyAccountsResponse: S.Codec = S.lazy((): S.Codec => S.Struct({accounts: S.Vector(LegacyAccount)}) as S.Codec); + +/** Error from [`crate::api::Account::get_user_id`]. */ +export type HostGetUserIdError = + /** User denied the identity disclosure request. */ + | { tag: "PermissionDenied"; value?: undefined } + /** User is not logged in. */ + | { tag: "NotConnected"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostGetUserIdError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({PermissionDenied: S._void, NotConnected: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** The user's primary DotNS account identity. */ +export interface HostGetUserIdResponse { + /** The user's primary DotNS username. */ + primaryUsername: string; +} + +export const HostGetUserIdResponse: S.Codec = S.lazy((): S.Codec => S.Struct({primaryUsername: S.str}) as S.Codec); + +/** + * Error from [`crate::api::System::handshake`] (RFC 0009). + * + * The handshake is the first call on a fresh connection; it does not require + * user authentication and is used to negotiate the wire codec version. + */ +export type HostHandshakeError = + /** Host did not complete the handshake in time. */ + | { tag: "Timeout"; value?: undefined } + /** Host does not speak the codec version requested by the product. */ + | { tag: "UnsupportedProtocolVersion"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: GenericError } +; + +export const HostHandshakeError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Timeout: S._void, UnsupportedProtocolVersion: S._void, Unknown: GenericError})); + +/** Wire-codec negotiation payload sent by the product (RFC 0009). */ +export interface HostHandshakeRequest { + /** Wire codec version requested by the product. */ + codecVersion: number; +} + +export const HostHandshakeRequest: S.Codec = S.lazy((): S.Codec => S.Struct({codecVersion: S.u8}) as S.Codec); + +/** Request to clear a local storage key. */ +export interface HostLocalStorageClearRequest { + /** Storage key to clear. */ + key: string; +} + +export const HostLocalStorageClearRequest: S.Codec = S.lazy((): S.Codec => S.Struct({key: S.str}) as S.Codec); + +/** Local storage operation error. */ +export type HostLocalStorageReadError = + /** Storage quota exceeded. */ + | { tag: "Full"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostLocalStorageReadError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Full: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** Request to read a local storage value. */ +export interface HostLocalStorageReadRequest { + /** Storage key to read. */ + key: string; +} + +export const HostLocalStorageReadRequest: S.Codec = S.lazy((): S.Codec => S.Struct({key: S.str}) as S.Codec); + +/** Response containing an optional local storage value. */ +export interface HostLocalStorageReadResponse { + /** Stored value, if present. */ + value?: HexString; +} + +export const HostLocalStorageReadResponse: S.Codec = S.lazy((): S.Codec => S.Struct({value: S.Option(S.Hex())}) as S.Codec); + +/** Request to write a value into local storage. */ +export interface HostLocalStorageWriteRequest { + /** Storage key to write. */ + key: string; + /** Value to store at the key. */ + value: HexString; +} + +export const HostLocalStorageWriteRequest: S.Codec = S.lazy((): S.Codec => S.Struct({key: S.str, value: S.Hex()}) as S.Codec); + +/** Error from [`crate::api::System::navigate_to`]. */ +export type HostNavigateToError = + /** User denied the navigation prompt. */ + | { tag: "PermissionDenied"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostNavigateToError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({PermissionDenied: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** Request to navigate the host to an external URL. */ +export interface HostNavigateToRequest { + /** URL to open. */ + url: string; +} + +export const HostNavigateToRequest: S.Codec = S.lazy((): S.Codec => S.Struct({url: S.str}) as S.Codec); + +/** + * Error from [`crate::api::Payment::balance_subscribe`]. + * + * See [RFC 0006]. + * + * [RFC 0006]: https://github.com/paritytech/triangle-js-sdks/pull/94 + */ +export type HostPaymentBalanceSubscribeError = + /** User denied the balance disclosure request. */ + | { tag: "PermissionDenied"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostPaymentBalanceSubscribeError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({PermissionDenied: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** + * Current payment balance state pushed to subscribers. + * + * See [RFC 0006]. + * + * [RFC 0006]: https://github.com/paritytech/triangle-js-sdks/pull/94 + */ +export interface HostPaymentBalanceSubscribeItem { + /** Balance that can be spent right now. */ + available: Balance; +} + +export const HostPaymentBalanceSubscribeItem: S.Codec = S.lazy((): S.Codec => S.Struct({available: Balance}) as S.Codec); + +/** Request to subscribe to payment balance updates. */ +export interface HostPaymentBalanceSubscribeRequest { + /** Optional purse selector. `None` means MAIN_PURSE. */ + purse?: CoinPaymentPurseId; +} + +export const HostPaymentBalanceSubscribeRequest: S.Codec = S.lazy((): S.Codec => S.Struct({purse: S.Option(CoinPaymentPurseId)}) as S.Codec); + +/** + * Error from [`crate::api::Payment::request`]. + * + * See [RFC 0006]. + * + * [RFC 0006]: https://github.com/paritytech/triangle-js-sdks/pull/94 + */ +export type HostPaymentError = + /** User rejected the payment request. */ + | { tag: "Rejected"; value?: undefined } + /** User's available balance is not sufficient for the requested amount. */ + | { tag: "InsufficientBalance"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostPaymentError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Rejected: S._void, InsufficientBalance: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** Request to initiate a payment to another account. */ +export interface HostPaymentRequest { + /** Optional purse selector. `None` means MAIN_PURSE. */ + from?: CoinPaymentPurseId; + /** Amount to pay. */ + amount: Balance; + /** Destination account. */ + destination: HexString; +} + +export const HostPaymentRequest: S.Codec = S.lazy((): S.Codec => S.Struct({from: S.Option(CoinPaymentPurseId), amount: Balance, destination: S.Hex(32)}) as S.Codec); + +/** + * Receipt returned after a successful payment request. + * + * See [RFC 0006]. + * + * [RFC 0006]: https://github.com/paritytech/triangle-js-sdks/pull/94 + */ +export interface HostPaymentResponse { + /** The assigned payment identifier. */ + id: string; +} + +export const HostPaymentResponse: S.Codec = S.lazy((): S.Codec => S.Struct({id: S.str}) as S.Codec); + +/** + * Error from [`crate::api::Payment::status_subscribe`]. + * + * See [RFC 0006]. + * + * [RFC 0006]: https://github.com/paritytech/triangle-js-sdks/pull/94 + */ +export type HostPaymentStatusSubscribeError = + /** Payment ID was not found or does not belong to the current product. */ + | { tag: "PaymentNotFound"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostPaymentStatusSubscribeError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({PaymentNotFound: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** + * Payment lifecycle status pushed to subscribers. + * + * Once a terminal state (`Completed` or `Failed`) is reached, the host + * delivers it and may close the subscription. + * + * See [RFC 0006]. + * + * [RFC 0006]: https://github.com/paritytech/triangle-js-sdks/pull/94 + */ +export type HostPaymentStatusSubscribeItem = + /** Payment is being processed. */ + | { tag: "Processing"; value?: undefined } + /** Payment has been settled successfully. */ + | { tag: "Completed"; value?: undefined } + /** Payment has failed. */ + | { tag: "Failed"; value: { reason: string } } +; + +export const HostPaymentStatusSubscribeItem: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Processing: S._void, Completed: S._void, Failed: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** Request to subscribe to a payment status. */ +export interface HostPaymentStatusSubscribeRequest { + /** Payment identifier to watch. */ + paymentId: string; +} + +export const HostPaymentStatusSubscribeRequest: S.Codec = S.lazy((): S.Codec => S.Struct({paymentId: S.str}) as S.Codec); + +/** + * Error from [`crate::api::Payment::top_up`]. + * + * See [RFC 0006]. + * + * [RFC 0006]: https://github.com/paritytech/triangle-js-sdks/pull/94 + */ +export type HostPaymentTopUpError = + /** The source account does not hold sufficient funds. */ + | { tag: "InsufficientFunds"; value?: undefined } + /** The source account was not found or is invalid. */ + | { tag: "InvalidSource"; value?: undefined } + /** Some coins were claimed but the total fell short of the requested amount. */ + | { tag: "PartialPayment"; value: { credited: Balance } } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostPaymentTopUpError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({InsufficientFunds: S._void, InvalidSource: S._void, PartialPayment: S.Struct({credited: Balance}) as S.Codec<{ credited: Balance }>, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** Request to top up the product payment balance. */ +export interface HostPaymentTopUpRequest { + /** Optional purse selector. `None` means MAIN_PURSE. */ + into?: CoinPaymentPurseId; + /** Amount to top up. */ + amount: Balance; + /** Funding source for the top-up. */ + source: PaymentTopUpSource; +} + +export const HostPaymentTopUpRequest: S.Codec = S.lazy((): S.Codec => S.Struct({into: S.Option(CoinPaymentPurseId), amount: Balance, source: PaymentTopUpSource}) as S.Codec); + +/** Request to cancel a previously scheduled notification. */ +export interface HostPushNotificationCancelRequest { + /** The notification identifier returned by [`HostPushNotificationResponse`]. */ + id: NotificationId; +} + +export const HostPushNotificationCancelRequest: S.Codec = S.lazy((): S.Codec => S.Struct({id: NotificationId}) as S.Codec); + +/** Push notification error. */ +export type HostPushNotificationError = + /** The host-wide queue of pending scheduled notifications is full. */ + | { tag: "ScheduleLimitReached"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostPushNotificationError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({ScheduleLimitReached: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** + * Push notification payload. + * + * When `scheduled_at` is `Some`, the notification is deferred to the given + * wall-clock instant (Unix milliseconds UTC). `None` fires immediately, + * preserving prior behaviour. See [RFC 0019]. + * + * [RFC 0019]: https://github.com/paritytech/truapi/blob/main/docs/rfcs/0019-scheduled-notifications.md + */ +export interface HostPushNotificationRequest { + /** Notification text. */ + text: string; + /** Optional URL to open on tap. */ + deeplink?: string; + /** + * Optional Unix timestamp in milliseconds (UTC) at which the notification + * should fire. `None` fires immediately. + */ + scheduledAt?: bigint; +} + +export const HostPushNotificationRequest: S.Codec = S.lazy((): S.Codec => S.Struct({text: S.str, deeplink: S.Option(S.str), scheduledAt: S.Option(S.u64)}) as S.Codec); + +/** Successful push notification response carrying the assigned id. */ +export interface HostPushNotificationResponse { + /** Host-assigned notification identifier. */ + id: NotificationId; +} + +export const HostPushNotificationResponse: S.Codec = S.lazy((): S.Codec => S.Struct({id: NotificationId}) as S.Codec); + +/** Login request error. */ +export type HostRequestLoginError = + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostRequestLoginError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** Request to present the host login flow. */ +export interface HostRequestLoginRequest { + /** Optional human-readable reason shown in the login UI. */ + reason?: string; +} + +export const HostRequestLoginRequest: S.Codec = S.lazy((): S.Codec => S.Struct({reason: S.Option(S.str)}) as S.Codec); + +/** Result of a login request. */ +export type HostRequestLoginResponse = "Success" | "AlreadyConnected" | "Rejected"; + +export const HostRequestLoginResponse: S.Codec = S.lazy((): S.Codec => S.Status("Success", "AlreadyConnected", "Rejected")); + +/** Batched resource pre-allocation request (RFC 0010). */ +export interface HostRequestResourceAllocationRequest { + /** Resources to allocate. */ + resources: Array; +} + +export const HostRequestResourceAllocationRequest: S.Codec = S.lazy((): S.Codec => S.Struct({resources: S.Vector(AllocatableResource)}) as S.Codec); + +/** Per-resource outcomes for a batched allocation request (RFC 0010). */ +export interface HostRequestResourceAllocationResponse { + /** Per-resource allocation outcomes, in the same order as the request. */ + outcomes: Array; +} + +export const HostRequestResourceAllocationResponse: S.Codec = S.lazy((): S.Codec => S.Struct({outcomes: S.Vector(AllocationOutcome)}) as S.Codec); + +/** Signing operation error. */ +export type HostSignPayloadError = + /** Payload could not be deserialized. */ + | { tag: "FailedToDecode"; value?: undefined } + /** User rejected signing. */ + | { tag: "Rejected"; value?: undefined } + /** Not authenticated. */ + | { tag: "PermissionDenied"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const HostSignPayloadError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({FailedToDecode: S._void, Rejected: S._void, PermissionDenied: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** Request to sign an extrinsic payload with a product account. */ +export interface HostSignPayloadRequest { + /** Product account that will sign this payload. */ + account: ProductAccountId; + /** The extrinsic payload to sign. */ + payload: HostSignPayloadData; +} + +export const HostSignPayloadRequest: S.Codec = S.lazy((): S.Codec => S.Struct({account: ProductAccountId, payload: HostSignPayloadData}) as S.Codec); + +/** Result of a signing operation. */ +export interface HostSignPayloadResponse { + /** The cryptographic signature. */ + signature: HexString; + /** Full signed transaction, if requested. */ + signedTransaction?: HexString; +} + +export const HostSignPayloadResponse: S.Codec = S.lazy((): S.Codec => S.Struct({signature: S.Hex(), signedTransaction: S.Option(S.Hex())}) as S.Codec); + +/** + * Sign a Substrate extrinsic payload with a non-product (legacy) account. + * Contains the same fields as [`HostSignPayloadRequest`] minus `address` + * (replaced by `signer`). + */ +export interface HostSignPayloadWithLegacyAccountRequest { + /** Signer address (SS58 or hex) of the legacy account. */ + signer: string; + /** The extrinsic payload to sign. */ + payload: HostSignPayloadData; +} + +export const HostSignPayloadWithLegacyAccountRequest: S.Codec = S.lazy((): S.Codec => S.Struct({signer: S.str, payload: HostSignPayloadData}) as S.Codec); + +/** A raw signing request pairing an account with the payload to sign. */ +export interface HostSignRawRequest { + /** Product account that will sign this payload. */ + account: ProductAccountId; + /** The payload to sign. */ + payload: RawPayload; +} + +export const HostSignRawRequest: S.Codec = S.lazy((): S.Codec => S.Struct({account: ProductAccountId, payload: RawPayload}) as S.Codec); + +/** + * Sign raw bytes with a non-product (legacy) account. The signer field + * identifies which legacy account to use. + */ +export interface HostSignRawWithLegacyAccountRequest { + /** Signer address (SS58 or hex) of the legacy account. */ + signer: string; + /** The data to sign. */ + payload: RawPayload; +} + +export const HostSignRawWithLegacyAccountRequest: S.Codec = S.lazy((): S.Codec => S.Struct({signer: S.str, payload: RawPayload}) as S.Codec); + +/** Current theme state pushed to subscribers. */ +export interface HostThemeSubscribeItem { + /** Theme name. */ + name: ThemeName; + /** Light or dark variant. */ + variant: ThemeVariant; +} + +export const HostThemeSubscribeItem: S.Codec = S.lazy((): S.Codec => S.Struct({name: ThemeName, variant: ThemeVariant}) as S.Codec); + +/** + * Subscribe payload identifying the chat message to render. The host responds + * with a stream of [`CustomRendererNode`] trees describing the rendered UI. + */ +export interface ProductChatCustomMessageRenderSubscribeRequest { + /** Message identifier. */ + messageId: string; + /** Application-defined message type. */ + messageType: string; + /** Binary payload. */ + payload: HexString; +} + +export const ProductChatCustomMessageRenderSubscribeRequest: S.Codec = S.lazy((): S.Codec => S.Struct({messageId: S.str, messageType: S.str, payload: S.Hex()}) as S.Codec); + +export interface RemoteChainHeadBodyRequest { + /** Chain genesis hash. */ + genesisHash: HexString; + /** Follow subscription identifier. */ + followSubscriptionId: string; + /** Block hash. */ + hash: HexString; +} + +export const RemoteChainHeadBodyRequest: S.Codec = S.lazy((): S.Codec => S.Struct({genesisHash: S.Hex(), followSubscriptionId: S.str, hash: S.Hex()}) as S.Codec); + +export interface RemoteChainHeadBodyResponse { + /** Started operation result. */ + operation: OperationStartedResult; +} + +export const RemoteChainHeadBodyResponse: S.Codec = S.lazy((): S.Codec => S.Struct({operation: OperationStartedResult}) as S.Codec); + +export interface RemoteChainHeadCallRequest { + /** Chain genesis hash. */ + genesisHash: HexString; + /** Follow subscription identifier. */ + followSubscriptionId: string; + /** Block hash. */ + hash: HexString; + /** Runtime API function name. */ + function: string; + /** SCALE-encoded call parameters. */ + callParameters: HexString; +} + +export const RemoteChainHeadCallRequest: S.Codec = S.lazy((): S.Codec => S.Struct({genesisHash: S.Hex(), followSubscriptionId: S.str, hash: S.Hex(), function: S.str, callParameters: S.Hex()}) as S.Codec); + +export interface RemoteChainHeadCallResponse { + /** Started operation result. */ + operation: OperationStartedResult; +} + +export const RemoteChainHeadCallResponse: S.Codec = S.lazy((): S.Codec => S.Struct({operation: OperationStartedResult}) as S.Codec); + +export interface RemoteChainHeadContinueRequest { + /** Chain genesis hash. */ + genesisHash: HexString; + /** Follow subscription identifier. */ + followSubscriptionId: string; + /** Operation identifier. */ + operationId: string; +} + +export const RemoteChainHeadContinueRequest: S.Codec = S.lazy((): S.Codec => S.Struct({genesisHash: S.Hex(), followSubscriptionId: S.str, operationId: S.str}) as S.Codec); + +export type RemoteChainHeadFollowItem = + | { tag: "Initialized"; value: { finalizedBlockHashes: Array; finalizedBlockRuntime?: RuntimeType } } + | { tag: "NewBlock"; value: { blockHash: HexString; parentBlockHash: HexString; newRuntime?: RuntimeType } } + | { tag: "BestBlockChanged"; value: { bestBlockHash: HexString } } + | { tag: "Finalized"; value: { finalizedBlockHashes: Array; prunedBlockHashes: Array } } + | { tag: "OperationBodyDone"; value: { operationId: string; value: Array } } + | { tag: "OperationCallDone"; value: { operationId: string; output: HexString } } + | { tag: "OperationStorageItems"; value: { operationId: string; items: Array } } + | { tag: "OperationStorageDone"; value: { operationId: string } } + | { tag: "OperationWaitingForContinue"; value: { operationId: string } } + | { tag: "OperationInaccessible"; value: { operationId: string } } + | { tag: "OperationError"; value: { operationId: string; error: string } } + | { tag: "Stop"; value?: undefined } +; + +export const RemoteChainHeadFollowItem: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({Initialized: S.Struct({finalizedBlockHashes: S.Vector(S.Hex()), finalizedBlockRuntime: S.Option(RuntimeType)}) as S.Codec<{ finalizedBlockHashes: Array; finalizedBlockRuntime?: RuntimeType }>, NewBlock: S.Struct({blockHash: S.Hex(), parentBlockHash: S.Hex(), newRuntime: S.Option(RuntimeType)}) as S.Codec<{ blockHash: HexString; parentBlockHash: HexString; newRuntime?: RuntimeType }>, BestBlockChanged: S.Struct({bestBlockHash: S.Hex()}) as S.Codec<{ bestBlockHash: HexString }>, Finalized: S.Struct({finalizedBlockHashes: S.Vector(S.Hex()), prunedBlockHashes: S.Vector(S.Hex())}) as S.Codec<{ finalizedBlockHashes: Array; prunedBlockHashes: Array }>, OperationBodyDone: S.Struct({operationId: S.str, value: S.Vector(S.Hex())}) as S.Codec<{ operationId: string; value: Array }>, OperationCallDone: S.Struct({operationId: S.str, output: S.Hex()}) as S.Codec<{ operationId: string; output: HexString }>, OperationStorageItems: S.Struct({operationId: S.str, items: S.Vector(StorageResultItem)}) as S.Codec<{ operationId: string; items: Array }>, OperationStorageDone: S.Struct({operationId: S.str}) as S.Codec<{ operationId: string }>, OperationWaitingForContinue: S.Struct({operationId: S.str}) as S.Codec<{ operationId: string }>, OperationInaccessible: S.Struct({operationId: S.str}) as S.Codec<{ operationId: string }>, OperationError: S.Struct({operationId: S.str, error: S.str}) as S.Codec<{ operationId: string; error: string }>, Stop: S._void})); + +export interface RemoteChainHeadFollowRequest { + /** Chain genesis hash. */ + genesisHash: HexString; + /** Whether to include runtime information in events. */ + withRuntime: boolean; +} + +export const RemoteChainHeadFollowRequest: S.Codec = S.lazy((): S.Codec => S.Struct({genesisHash: S.Hex(), withRuntime: S.bool}) as S.Codec); + +export interface RemoteChainHeadHeaderRequest { + /** Chain genesis hash. */ + genesisHash: HexString; + /** Follow subscription identifier. */ + followSubscriptionId: string; + /** Block hash. */ + hash: HexString; +} + +export const RemoteChainHeadHeaderRequest: S.Codec = S.lazy((): S.Codec => S.Struct({genesisHash: S.Hex(), followSubscriptionId: S.str, hash: S.Hex()}) as S.Codec); + +export interface RemoteChainHeadHeaderResponse { + /** SCALE-encoded block header. */ + header?: HexString; +} + +export const RemoteChainHeadHeaderResponse: S.Codec = S.lazy((): S.Codec => S.Struct({header: S.Option(S.Hex())}) as S.Codec); + +export interface RemoteChainHeadStopOperationRequest { + /** Chain genesis hash. */ + genesisHash: HexString; + /** Follow subscription identifier. */ + followSubscriptionId: string; + /** Operation identifier. */ + operationId: string; +} + +export const RemoteChainHeadStopOperationRequest: S.Codec = S.lazy((): S.Codec => S.Struct({genesisHash: S.Hex(), followSubscriptionId: S.str, operationId: S.str}) as S.Codec); + +export interface RemoteChainHeadStorageRequest { + /** Chain genesis hash. */ + genesisHash: HexString; + /** Follow subscription identifier. */ + followSubscriptionId: string; + /** Block hash. */ + hash: HexString; + /** Storage items to query. */ + items: Array; + /** Optional child trie. */ + childTrie?: HexString; +} + +export const RemoteChainHeadStorageRequest: S.Codec = S.lazy((): S.Codec => S.Struct({genesisHash: S.Hex(), followSubscriptionId: S.str, hash: S.Hex(), items: S.Vector(StorageQueryItem), childTrie: S.Option(S.Hex())}) as S.Codec); + +export interface RemoteChainHeadStorageResponse { + /** Started operation result. */ + operation: OperationStartedResult; +} + +export const RemoteChainHeadStorageResponse: S.Codec = S.lazy((): S.Codec => S.Struct({operation: OperationStartedResult}) as S.Codec); + +export interface RemoteChainHeadUnpinRequest { + /** Chain genesis hash. */ + genesisHash: HexString; + /** Follow subscription identifier. */ + followSubscriptionId: string; + /** Block hashes to unpin. */ + hashes: Array; +} + +export const RemoteChainHeadUnpinRequest: S.Codec = S.lazy((): S.Codec => S.Struct({genesisHash: S.Hex(), followSubscriptionId: S.str, hashes: S.Vector(S.Hex())}) as S.Codec); + +export interface RemoteChainSpecChainNameRequest { + /** Chain genesis hash. */ + genesisHash: HexString; +} + +export const RemoteChainSpecChainNameRequest: S.Codec = S.lazy((): S.Codec => S.Struct({genesisHash: S.Hex()}) as S.Codec); + +export interface RemoteChainSpecChainNameResponse { + /** Chain display name. */ + chainName: string; +} + +export const RemoteChainSpecChainNameResponse: S.Codec = S.lazy((): S.Codec => S.Struct({chainName: S.str}) as S.Codec); + +export interface RemoteChainSpecGenesisHashRequest { + /** Chain genesis hash requested by the product. */ + genesisHash: HexString; +} + +export const RemoteChainSpecGenesisHashRequest: S.Codec = S.lazy((): S.Codec => S.Struct({genesisHash: S.Hex()}) as S.Codec); + +export interface RemoteChainSpecGenesisHashResponse { + /** Chain genesis hash. */ + genesisHash: HexString; +} + +export const RemoteChainSpecGenesisHashResponse: S.Codec = S.lazy((): S.Codec => S.Struct({genesisHash: S.Hex()}) as S.Codec); + +export interface RemoteChainSpecPropertiesRequest { + /** Chain genesis hash. */ + genesisHash: HexString; +} + +export const RemoteChainSpecPropertiesRequest: S.Codec = S.lazy((): S.Codec => S.Struct({genesisHash: S.Hex()}) as S.Codec); + +export interface RemoteChainSpecPropertiesResponse { + /** JSON-encoded properties. */ + properties: string; +} + +export const RemoteChainSpecPropertiesResponse: S.Codec = S.lazy((): S.Codec => S.Struct({properties: S.str}) as S.Codec); + +export interface RemoteChainTransactionBroadcastRequest { + /** Chain genesis hash. */ + genesisHash: HexString; + /** Signed transaction bytes. */ + transaction: HexString; +} + +export const RemoteChainTransactionBroadcastRequest: S.Codec = S.lazy((): S.Codec => S.Struct({genesisHash: S.Hex(), transaction: S.Hex()}) as S.Codec); + +export interface RemoteChainTransactionBroadcastResponse { + /** Broadcast operation identifier, if available. */ + operationId?: string; +} + +export const RemoteChainTransactionBroadcastResponse: S.Codec = S.lazy((): S.Codec => S.Struct({operationId: S.Option(S.str)}) as S.Codec); + +export interface RemoteChainTransactionStopRequest { + /** Chain genesis hash. */ + genesisHash: HexString; + /** Operation identifier of the broadcast to stop. */ + operationId: string; +} + +export const RemoteChainTransactionStopRequest: S.Codec = S.lazy((): S.Codec => S.Struct({genesisHash: S.Hex(), operationId: S.str}) as S.Codec); + +/** remote-permission request (RFC 0002). */ +export interface RemotePermissionRequest { + /** Permission requested by the product. */ + permission: RemotePermission; +} + +export const RemotePermissionRequest: S.Codec = S.lazy((): S.Codec => S.Struct({permission: RemotePermission}) as S.Codec); + +/** Outcome of a remote-permission request. */ +export interface RemotePermissionResponse { + /** Whether the permission was granted. */ + granted: boolean; +} + +export const RemotePermissionResponse: S.Codec = S.lazy((): S.Codec => S.Struct({granted: S.bool}) as S.Codec); + +/** Item containing an optional preimage lookup result. */ +export interface RemotePreimageLookupSubscribeItem { + /** Preimage data, if found. */ + value?: HexString; +} + +export const RemotePreimageLookupSubscribeItem: S.Codec = S.lazy((): S.Codec => S.Struct({value: S.Option(S.Hex())}) as S.Codec); + +/** Request to subscribe to preimage lookup results. */ +export interface RemotePreimageLookupSubscribeRequest { + /** Hash of the preimage. */ + key: HexString; +} + +export const RemotePreimageLookupSubscribeRequest: S.Codec = S.lazy((): S.Codec => S.Struct({key: S.Hex()}) as S.Codec); + +/** Statement proof creation error. */ +export type RemoteStatementStoreCreateProofError = + /** Signing operation failed. */ + | { tag: "UnableToSign"; value?: undefined } + /** Account not recognized. */ + | { tag: "UnknownAccount"; value?: undefined } + /** Catch-all. */ + | { tag: "Unknown"; value: { reason: string } } +; + +export const RemoteStatementStoreCreateProofError: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({UnableToSign: S._void, UnknownAccount: S._void, Unknown: S.Struct({reason: S.str}) as S.Codec<{ reason: string }>})); + +/** Request to create a cryptographic proof for a statement. */ +export interface RemoteStatementStoreCreateProofRequest { + /** Product account that should create the proof. */ + productAccountId: ProductAccountId; + /** Statement to prove. */ + statement: Statement; +} + +export const RemoteStatementStoreCreateProofRequest: S.Codec = S.lazy((): S.Codec => S.Struct({productAccountId: ProductAccountId, statement: Statement}) as S.Codec); + +/** Response containing a statement proof. */ +export interface RemoteStatementStoreCreateProofResponse { + /** Created statement proof. */ + proof: StatementProof; +} + +export const RemoteStatementStoreCreateProofResponse: S.Codec = S.lazy((): S.Codec => S.Struct({proof: StatementProof}) as S.Codec); + +/** + * Page of signed statements delivered by the statement store subscription + * (RFC 0008). The `is_complete` flag distinguishes the historical-dump phase + * (`false`) from the live-update phase (`true`). + */ +export interface RemoteStatementStoreSubscribeItem { + /** Signed statements matching the subscription. */ + statements: Array; + /** + * `false` while the host is still streaming the historical dump (more + * pages to follow). `true` once the dump is complete; all subsequent + * pages are also `true` and carry only newly-arrived statements. + */ + isComplete: boolean; +} + +export const RemoteStatementStoreSubscribeItem: S.Codec = S.lazy((): S.Codec => S.Struct({statements: S.Vector(SignedStatement), isComplete: S.bool}) as S.Codec); + +/** Request to subscribe to statements via a topic filter (RFC 0008). */ +export type RemoteStatementStoreSubscribeRequest = + /** AND: statement must contain every listed topic. */ + | { tag: "MatchAll"; value: Array } + /** OR: statement must contain at least one listed topic. */ + | { tag: "MatchAny"; value: Array } +; + +export const RemoteStatementStoreSubscribeRequest: S.Codec = S.lazy((): S.Codec => S.TaggedUnion({MatchAll: S.Vector(Topic), MatchAny: S.Vector(Topic)})); + +/** Vertical alignment options. */ +export type VerticalAlignment = "Top" | "Center" | "Bottom"; + +export const VerticalAlignment: S.Codec = S.lazy((): S.Codec => S.Status("Top", "Center", "Bottom")); + diff --git a/generated/wire-table.ts b/generated/wire-table.ts new file mode 100644 index 00000000..85ebf146 --- /dev/null +++ b/generated/wire-table.ts @@ -0,0 +1,378 @@ +// Auto-generated by truapi-codegen. Do not edit. + +import type { RequestFrameIds, SubscriptionFrameIds } from '../transport.js'; + +// Wire-protocol discriminants. Method ordering is part of the +// protocol; only ever append or explicitly reserve gaps. + +export const SYSTEM_HANDSHAKE = { + request: 0, + response: 1, +} as const satisfies RequestFrameIds; + +export const SYSTEM_FEATURE_SUPPORTED = { + request: 2, + response: 3, +} as const satisfies RequestFrameIds; + +export const NOTIFICATIONS_SEND_PUSH_NOTIFICATION = { + request: 4, + response: 5, +} as const satisfies RequestFrameIds; + +export const SYSTEM_NAVIGATE_TO = { + request: 6, + response: 7, +} as const satisfies RequestFrameIds; + +export const PERMISSIONS_REQUEST_DEVICE_PERMISSION = { + request: 8, + response: 9, +} as const satisfies RequestFrameIds; + +export const PERMISSIONS_REQUEST_REMOTE_PERMISSION = { + request: 10, + response: 11, +} as const satisfies RequestFrameIds; + +export const LOCAL_STORAGE_READ = { + request: 12, + response: 13, +} as const satisfies RequestFrameIds; + +export const LOCAL_STORAGE_WRITE = { + request: 14, + response: 15, +} as const satisfies RequestFrameIds; + +export const LOCAL_STORAGE_CLEAR = { + request: 16, + response: 17, +} as const satisfies RequestFrameIds; + +export const ACCOUNT_CONNECTION_STATUS_SUBSCRIBE = { + start: 18, + stop: 19, + interrupt: 20, + receive: 21, +} as const satisfies SubscriptionFrameIds; + +export const ACCOUNT_GET_ACCOUNT = { + request: 22, + response: 23, +} as const satisfies RequestFrameIds; + +export const ACCOUNT_GET_ACCOUNT_ALIAS = { + request: 24, + response: 25, +} as const satisfies RequestFrameIds; + +export const ACCOUNT_CREATE_ACCOUNT_PROOF = { + request: 26, + response: 27, +} as const satisfies RequestFrameIds; + +export const ACCOUNT_GET_LEGACY_ACCOUNTS = { + request: 28, + response: 29, +} as const satisfies RequestFrameIds; + +export const SIGNING_CREATE_TRANSACTION = { + request: 30, + response: 31, +} as const satisfies RequestFrameIds; + +export const SIGNING_CREATE_TRANSACTION_WITH_LEGACY_ACCOUNT = { + request: 32, + response: 33, +} as const satisfies RequestFrameIds; + +export const SIGNING_SIGN_RAW_WITH_LEGACY_ACCOUNT = { + request: 34, + response: 35, +} as const satisfies RequestFrameIds; + +export const SIGNING_SIGN_PAYLOAD_WITH_LEGACY_ACCOUNT = { + request: 36, + response: 37, +} as const satisfies RequestFrameIds; + +export const CHAT_CREATE_ROOM = { + request: 38, + response: 39, +} as const satisfies RequestFrameIds; + +export const CHAT_REGISTER_BOT = { + request: 40, + response: 41, +} as const satisfies RequestFrameIds; + +export const CHAT_LIST_SUBSCRIBE = { + start: 42, + stop: 43, + interrupt: 44, + receive: 45, +} as const satisfies SubscriptionFrameIds; + +export const CHAT_POST_MESSAGE = { + request: 46, + response: 47, +} as const satisfies RequestFrameIds; + +export const CHAT_ACTION_SUBSCRIBE = { + start: 48, + stop: 49, + interrupt: 50, + receive: 51, +} as const satisfies SubscriptionFrameIds; + +export const CHAT_CUSTOM_MESSAGE_RENDER_SUBSCRIBE = { + start: 52, + stop: 53, + interrupt: 54, + receive: 55, +} as const satisfies SubscriptionFrameIds; + +export const STATEMENT_STORE_SUBSCRIBE = { + start: 56, + stop: 57, + interrupt: 58, + receive: 59, +} as const satisfies SubscriptionFrameIds; + +export const STATEMENT_STORE_CREATE_PROOF = { + request: 60, + response: 61, +} as const satisfies RequestFrameIds; + +export const STATEMENT_STORE_SUBMIT = { + request: 62, + response: 63, +} as const satisfies RequestFrameIds; + +export const PREIMAGE_LOOKUP_SUBSCRIBE = { + start: 64, + stop: 65, + interrupt: 66, + receive: 67, +} as const satisfies SubscriptionFrameIds; + +export const PREIMAGE_SUBMIT = { + request: 68, + response: 69, +} as const satisfies RequestFrameIds; + +export const CHAIN_FOLLOW_HEAD_SUBSCRIBE = { + start: 76, + stop: 77, + interrupt: 78, + receive: 79, +} as const satisfies SubscriptionFrameIds; + +export const CHAIN_GET_HEAD_HEADER = { + request: 80, + response: 81, +} as const satisfies RequestFrameIds; + +export const CHAIN_GET_HEAD_BODY = { + request: 82, + response: 83, +} as const satisfies RequestFrameIds; + +export const CHAIN_GET_HEAD_STORAGE = { + request: 84, + response: 85, +} as const satisfies RequestFrameIds; + +export const CHAIN_CALL_HEAD = { + request: 86, + response: 87, +} as const satisfies RequestFrameIds; + +export const CHAIN_UNPIN_HEAD = { + request: 88, + response: 89, +} as const satisfies RequestFrameIds; + +export const CHAIN_CONTINUE_HEAD = { + request: 90, + response: 91, +} as const satisfies RequestFrameIds; + +export const CHAIN_STOP_HEAD_OPERATION = { + request: 92, + response: 93, +} as const satisfies RequestFrameIds; + +export const CHAIN_GET_SPEC_GENESIS_HASH = { + request: 94, + response: 95, +} as const satisfies RequestFrameIds; + +export const CHAIN_GET_SPEC_CHAIN_NAME = { + request: 96, + response: 97, +} as const satisfies RequestFrameIds; + +export const CHAIN_GET_SPEC_PROPERTIES = { + request: 98, + response: 99, +} as const satisfies RequestFrameIds; + +export const CHAIN_BROADCAST_TRANSACTION = { + request: 100, + response: 101, +} as const satisfies RequestFrameIds; + +export const CHAIN_STOP_TRANSACTION = { + request: 102, + response: 103, +} as const satisfies RequestFrameIds; + +export const THEME_SUBSCRIBE = { + start: 104, + stop: 105, + interrupt: 106, + receive: 107, +} as const satisfies SubscriptionFrameIds; + +export const ENTROPY_DERIVE = { + request: 108, + response: 109, +} as const satisfies RequestFrameIds; + +export const ACCOUNT_GET_USER_ID = { + request: 110, + response: 111, +} as const satisfies RequestFrameIds; + +export const ACCOUNT_REQUEST_LOGIN = { + request: 112, + response: 113, +} as const satisfies RequestFrameIds; + +export const SIGNING_SIGN_RAW = { + request: 114, + response: 115, +} as const satisfies RequestFrameIds; + +export const SIGNING_SIGN_PAYLOAD = { + request: 116, + response: 117, +} as const satisfies RequestFrameIds; + +export const PAYMENT_BALANCE_SUBSCRIBE = { + start: 118, + stop: 119, + interrupt: 120, + receive: 121, +} as const satisfies SubscriptionFrameIds; + +export const PAYMENT_TOP_UP = { + request: 122, + response: 123, +} as const satisfies RequestFrameIds; + +export const PAYMENT_REQUEST = { + request: 124, + response: 125, +} as const satisfies RequestFrameIds; + +export const PAYMENT_STATUS_SUBSCRIBE = { + start: 126, + stop: 127, + interrupt: 128, + receive: 129, +} as const satisfies SubscriptionFrameIds; + +export const RESOURCE_ALLOCATION_REQUEST = { + request: 130, + response: 131, +} as const satisfies RequestFrameIds; + +export const STATEMENT_STORE_CREATE_PROOF_AUTHORIZED = { + request: 132, + response: 133, +} as const satisfies RequestFrameIds; + +export const NOTIFICATIONS_CANCEL_PUSH_NOTIFICATION = { + request: 134, + response: 135, +} as const satisfies RequestFrameIds; + +export const COIN_PAYMENT_CREATE_PURSE = { + request: 136, + response: 137, +} as const satisfies RequestFrameIds; + +export const COIN_PAYMENT_QUERY_PURSE = { + request: 138, + response: 139, +} as const satisfies RequestFrameIds; + +export const COIN_PAYMENT_REBALANCE_PURSE = { + start: 140, + stop: 141, + interrupt: 142, + receive: 143, +} as const satisfies SubscriptionFrameIds; + +export const COIN_PAYMENT_DELETE_PURSE = { + start: 144, + stop: 145, + interrupt: 146, + receive: 147, +} as const satisfies SubscriptionFrameIds; + +export const COIN_PAYMENT_CREATE_RECEIVABLE = { + request: 148, + response: 149, +} as const satisfies RequestFrameIds; + +export const COIN_PAYMENT_CREATE_CHEQUE = { + request: 150, + response: 151, +} as const satisfies RequestFrameIds; + +export const COIN_PAYMENT_DEPOSIT = { + start: 152, + stop: 153, + interrupt: 154, + receive: 155, +} as const satisfies SubscriptionFrameIds; + +export const COIN_PAYMENT_REFUND = { + start: 156, + stop: 157, + interrupt: 158, + receive: 159, +} as const satisfies SubscriptionFrameIds; + +export const COIN_PAYMENT_LISTEN_FOR_PAYMENT = { + start: 160, + stop: 161, + interrupt: 162, + receive: 163, +} as const satisfies SubscriptionFrameIds; + +export const CONTACTS_RESOLVE = { + request: 164, + response: 165, +} as const satisfies RequestFrameIds; + +export const CONTACTS_DERIVE_SHARED_KEY = { + request: 166, + response: 167, +} as const satisfies RequestFrameIds; + +export const CONTACTS_SEND = { + request: 168, + response: 169, +} as const satisfies RequestFrameIds; + +export const CONTACTS_SUBSCRIBE = { + start: 170, + stop: 171, + interrupt: 172, + receive: 173, +} as const satisfies SubscriptionFrameIds; diff --git a/rust/crates/truapi-codegen/tests/golden/dispatcher.rs b/rust/crates/truapi-codegen/tests/golden/dispatcher.rs index 25f2fb13..77024d93 100644 --- a/rust/crates/truapi-codegen/tests/golden/dispatcher.rs +++ b/rust/crates/truapi-codegen/tests/golden/dispatcher.rs @@ -12,6 +12,7 @@ use truapi::api::{ Chain, Chat, CoinPayment, + Contacts, Entropy, LocalStorage, Notifications, @@ -43,6 +44,7 @@ where register_chain(dispatcher, host.clone()); register_chat(dispatcher, host.clone()); register_coin_payment(dispatcher, host.clone()); + register_contacts(dispatcher, host.clone()); register_entropy(dispatcher, host.clone()); register_local_storage(dispatcher, host.clone()); register_notifications(dispatcher, host.clone()); @@ -996,6 +998,112 @@ where } } +fn register_contacts

(dispatcher: &mut Dispatcher, host: Arc

) +where + P: Contacts + Send + Sync + 'static, +{ + { + let host = host.clone(); + dispatcher.on_request(wire_table::CONTACTS_RESOLVE, move |request_id: String, bytes: Vec| { + let host = host.clone(); + Box::pin(async move { + let request: versioned::contacts::HostContactsResolveRequest = match Decode::decode(&mut &bytes[..]) { + Ok(request) => request, + Err(err) => { + let error: truapi::CallError = + truapi::CallError::MalformedFrame { reason: err.to_string() }; + return Ok(encode_versioned_err_payload( + error, + ::LATEST, + )); + } + }; + let target_version = request.version(); + let cx = CallContext::with_request_id(request_id.clone()); + let response: versioned::contacts::HostContactsResolveResponse = match host.resolve(&cx, request).await { + Ok(value) => value, + Err(err) => { + return Ok(encode_versioned_err_payload(err, target_version)); + } + }; + Ok(encode_versioned_ok_payload(response)) + }) + }); + } + { + let host = host.clone(); + dispatcher.on_request(wire_table::CONTACTS_DERIVE_SHARED_KEY, move |request_id: String, bytes: Vec| { + let host = host.clone(); + Box::pin(async move { + let request: versioned::contacts::HostContactsDeriveSharedKeyRequest = match Decode::decode(&mut &bytes[..]) { + Ok(request) => request, + Err(err) => { + let error: truapi::CallError = + truapi::CallError::MalformedFrame { reason: err.to_string() }; + return Ok(encode_versioned_err_payload( + error, + ::LATEST, + )); + } + }; + let target_version = request.version(); + let cx = CallContext::with_request_id(request_id.clone()); + let response: versioned::contacts::HostContactsDeriveSharedKeyResponse = match host.derive_shared_key(&cx, request).await { + Ok(value) => value, + Err(err) => { + return Ok(encode_versioned_err_payload(err, target_version)); + } + }; + Ok(encode_versioned_ok_payload(response)) + }) + }); + } + { + let host = host.clone(); + dispatcher.on_request(wire_table::CONTACTS_SEND, move |request_id: String, bytes: Vec| { + let host = host.clone(); + Box::pin(async move { + let request: versioned::contacts::HostContactsSendRequest = match Decode::decode(&mut &bytes[..]) { + Ok(request) => request, + Err(err) => { + let error: truapi::CallError = + truapi::CallError::MalformedFrame { reason: err.to_string() }; + return Ok(encode_versioned_err_payload( + error, + ::LATEST, + )); + } + }; + let target_version = request.version(); + let cx = CallContext::with_request_id(request_id.clone()); + match host.send(&cx, request).await { + Ok(()) => Ok(encode_versioned_unit_ok_payload(target_version)), + Err(err) => { + Ok(encode_versioned_err_payload(err, target_version)) + } + } + }) + }); + } + { + let host = host; + dispatcher.on_subscription(wire_table::CONTACTS_SUBSCRIBE, move |request_id: String, bytes: Vec| { + let host = host.clone(); + Box::pin(async move { + let _ = bytes; + let cx = CallContext::with_request_id(request_id.clone()); + let stream = match host.subscribe(&cx).await { + Ok(sub) => sub, + Err(err) => { + return Err(encode_versioned_interrupt_payload(err, ::LATEST)); + } + }; + Ok(subscription_stream::(stream)) + }) + }); + } +} + fn register_entropy

(dispatcher: &mut Dispatcher, host: Arc

) where P: Entropy + Send + Sync + 'static, diff --git a/rust/crates/truapi-codegen/tests/golden/wire_table.rs b/rust/crates/truapi-codegen/tests/golden/wire_table.rs index 12a72a94..213a37d3 100644 --- a/rust/crates/truapi-codegen/tests/golden/wire_table.rs +++ b/rust/crates/truapi-codegen/tests/golden/wire_table.rs @@ -460,6 +460,32 @@ pub const COIN_PAYMENT_LISTEN_FOR_PAYMENT: SubscriptionFrameIds = SubscriptionFr receive_id: 163, }; +/// Wire discriminants for `contacts_resolve`. +pub const CONTACTS_RESOLVE: RequestFrameIds = RequestFrameIds { + request_id: 164, + response_id: 165, +}; + +/// Wire discriminants for `contacts_derive_shared_key`. +pub const CONTACTS_DERIVE_SHARED_KEY: RequestFrameIds = RequestFrameIds { + request_id: 166, + response_id: 167, +}; + +/// Wire discriminants for `contacts_send`. +pub const CONTACTS_SEND: RequestFrameIds = RequestFrameIds { + request_id: 168, + response_id: 169, +}; + +/// Wire discriminants for `contacts_subscribe`. +pub const CONTACTS_SUBSCRIBE: SubscriptionFrameIds = SubscriptionFrameIds { + start_id: 170, + stop_id: 171, + interrupt_id: 172, + receive_id: 173, +}; + /// The full wire table. Ordering is part of the wire protocol; /// only ever append. Removed methods leave their slot empty. pub const WIRE_TABLE: &[WireEntry] = &[ @@ -719,4 +745,20 @@ pub const WIRE_TABLE: &[WireEntry] = &[ method: "coin_payment_listen_for_payment", kind: WireKind::Subscription(COIN_PAYMENT_LISTEN_FOR_PAYMENT), }, + WireEntry { + method: "contacts_resolve", + kind: WireKind::Request(CONTACTS_RESOLVE), + }, + WireEntry { + method: "contacts_derive_shared_key", + kind: WireKind::Request(CONTACTS_DERIVE_SHARED_KEY), + }, + WireEntry { + method: "contacts_send", + kind: WireKind::Request(CONTACTS_SEND), + }, + WireEntry { + method: "contacts_subscribe", + kind: WireKind::Subscription(CONTACTS_SUBSCRIBE), + }, ]; diff --git a/rust/crates/truapi-server/src/generated/dispatcher.rs b/rust/crates/truapi-server/src/generated/dispatcher.rs index 231dd362..8ec8dac2 100644 --- a/rust/crates/truapi-server/src/generated/dispatcher.rs +++ b/rust/crates/truapi-server/src/generated/dispatcher.rs @@ -8,8 +8,8 @@ use parity_scale_codec::Decode; use truapi::CallContext; use truapi::api::{ - Account, Chain, Chat, CoinPayment, Entropy, LocalStorage, Notifications, Payment, Permissions, - Preimage, ResourceAllocation, Signing, StatementStore, System, Theme, + Account, Chain, Chat, CoinPayment, Contacts, Entropy, LocalStorage, Notifications, Payment, + Permissions, Preimage, ResourceAllocation, Signing, StatementStore, System, Theme, }; use truapi::versioned::{self, Versioned}; @@ -30,6 +30,7 @@ where register_chain(dispatcher, host.clone()); register_chat(dispatcher, host.clone()); register_coin_payment(dispatcher, host.clone()); + register_contacts(dispatcher, host.clone()); register_entropy(dispatcher, host.clone()); register_local_storage(dispatcher, host.clone()); register_notifications(dispatcher, host.clone()); @@ -1151,6 +1152,125 @@ where } } +fn register_contacts

(dispatcher: &mut Dispatcher, host: Arc

) +where + P: Contacts + Send + Sync + 'static, +{ + { + let host = host.clone(); + dispatcher.on_request( + wire_table::CONTACTS_RESOLVE, + move |request_id: String, bytes: Vec| { + let host = host.clone(); + Box::pin(async move { + let request: versioned::contacts::HostContactsResolveRequest = + match Decode::decode(&mut &bytes[..]) { + Ok(request) => request, + Err(err) => { + let error: truapi::CallError< + versioned::contacts::HostContactsResolveError, + > = truapi::CallError::MalformedFrame { + reason: err.to_string(), + }; + return Ok(encode_versioned_err_payload( + error, + ::LATEST, + )); + } + }; + let target_version = request.version(); + let cx = CallContext::with_request_id(request_id.clone()); + let response: versioned::contacts::HostContactsResolveResponse = + match host.resolve(&cx, request).await { + Ok(value) => value, + Err(err) => { + return Ok(encode_versioned_err_payload(err, target_version)); + } + }; + Ok(encode_versioned_ok_payload(response)) + }) + }, + ); + } + { + let host = host.clone(); + dispatcher.on_request(wire_table::CONTACTS_DERIVE_SHARED_KEY, move |request_id: String, bytes: Vec| { + let host = host.clone(); + Box::pin(async move { + let request: versioned::contacts::HostContactsDeriveSharedKeyRequest = match Decode::decode(&mut &bytes[..]) { + Ok(request) => request, + Err(err) => { + let error: truapi::CallError = + truapi::CallError::MalformedFrame { reason: err.to_string() }; + return Ok(encode_versioned_err_payload( + error, + ::LATEST, + )); + } + }; + let target_version = request.version(); + let cx = CallContext::with_request_id(request_id.clone()); + let response: versioned::contacts::HostContactsDeriveSharedKeyResponse = match host.derive_shared_key(&cx, request).await { + Ok(value) => value, + Err(err) => { + return Ok(encode_versioned_err_payload(err, target_version)); + } + }; + Ok(encode_versioned_ok_payload(response)) + }) + }); + } + { + let host = host.clone(); + dispatcher.on_request( + wire_table::CONTACTS_SEND, + move |request_id: String, bytes: Vec| { + let host = host.clone(); + Box::pin(async move { + let request: versioned::contacts::HostContactsSendRequest = + match Decode::decode(&mut &bytes[..]) { + Ok(request) => request, + Err(err) => { + let error: truapi::CallError< + versioned::contacts::HostContactsSendError, + > = truapi::CallError::MalformedFrame { + reason: err.to_string(), + }; + return Ok(encode_versioned_err_payload( + error, + ::LATEST, + )); + } + }; + let target_version = request.version(); + let cx = CallContext::with_request_id(request_id.clone()); + match host.send(&cx, request).await { + Ok(()) => Ok(encode_versioned_unit_ok_payload(target_version)), + Err(err) => Ok(encode_versioned_err_payload(err, target_version)), + } + }) + }, + ); + } + { + let host = host; + dispatcher.on_subscription(wire_table::CONTACTS_SUBSCRIBE, move |request_id: String, bytes: Vec| { + let host = host.clone(); + Box::pin(async move { + let _ = bytes; + let cx = CallContext::with_request_id(request_id.clone()); + let stream = match host.subscribe(&cx).await { + Ok(sub) => sub, + Err(err) => { + return Err(encode_versioned_interrupt_payload(err, ::LATEST)); + } + }; + Ok(subscription_stream::(stream)) + }) + }); + } +} + fn register_entropy

(dispatcher: &mut Dispatcher, host: Arc

) where P: Entropy + Send + Sync + 'static, diff --git a/rust/crates/truapi-server/src/generated/wire_table.rs b/rust/crates/truapi-server/src/generated/wire_table.rs index 12a72a94..213a37d3 100644 --- a/rust/crates/truapi-server/src/generated/wire_table.rs +++ b/rust/crates/truapi-server/src/generated/wire_table.rs @@ -460,6 +460,32 @@ pub const COIN_PAYMENT_LISTEN_FOR_PAYMENT: SubscriptionFrameIds = SubscriptionFr receive_id: 163, }; +/// Wire discriminants for `contacts_resolve`. +pub const CONTACTS_RESOLVE: RequestFrameIds = RequestFrameIds { + request_id: 164, + response_id: 165, +}; + +/// Wire discriminants for `contacts_derive_shared_key`. +pub const CONTACTS_DERIVE_SHARED_KEY: RequestFrameIds = RequestFrameIds { + request_id: 166, + response_id: 167, +}; + +/// Wire discriminants for `contacts_send`. +pub const CONTACTS_SEND: RequestFrameIds = RequestFrameIds { + request_id: 168, + response_id: 169, +}; + +/// Wire discriminants for `contacts_subscribe`. +pub const CONTACTS_SUBSCRIBE: SubscriptionFrameIds = SubscriptionFrameIds { + start_id: 170, + stop_id: 171, + interrupt_id: 172, + receive_id: 173, +}; + /// The full wire table. Ordering is part of the wire protocol; /// only ever append. Removed methods leave their slot empty. pub const WIRE_TABLE: &[WireEntry] = &[ @@ -719,4 +745,20 @@ pub const WIRE_TABLE: &[WireEntry] = &[ method: "coin_payment_listen_for_payment", kind: WireKind::Subscription(COIN_PAYMENT_LISTEN_FOR_PAYMENT), }, + WireEntry { + method: "contacts_resolve", + kind: WireKind::Request(CONTACTS_RESOLVE), + }, + WireEntry { + method: "contacts_derive_shared_key", + kind: WireKind::Request(CONTACTS_DERIVE_SHARED_KEY), + }, + WireEntry { + method: "contacts_send", + kind: WireKind::Request(CONTACTS_SEND), + }, + WireEntry { + method: "contacts_subscribe", + kind: WireKind::Subscription(CONTACTS_SUBSCRIBE), + }, ]; diff --git a/rust/crates/truapi/src/api.rs b/rust/crates/truapi/src/api.rs index 957509e4..c961782d 100644 --- a/rust/crates/truapi/src/api.rs +++ b/rust/crates/truapi/src/api.rs @@ -4,6 +4,7 @@ pub mod account; pub mod chain; pub mod chat; pub mod coin_payment; +pub mod contacts; pub mod entropy; pub mod local_storage; pub mod notifications; @@ -20,6 +21,7 @@ pub use account::Account; pub use chain::Chain; pub use chat::Chat; pub use coin_payment::CoinPayment; +pub use contacts::Contacts; pub use entropy::Entropy; pub use local_storage::LocalStorage; pub use notifications::Notifications; @@ -38,6 +40,7 @@ pub trait TrUApi: + Chain + Chat + CoinPayment + + Contacts + Entropy + LocalStorage + Notifications @@ -59,6 +62,7 @@ impl TrUApi for T where + Chain + Chat + CoinPayment + + Contacts + Entropy + LocalStorage + Notifications diff --git a/rust/crates/truapi/src/api/contacts.rs b/rust/crates/truapi/src/api/contacts.rs new file mode 100644 index 00000000..9ba3fec0 --- /dev/null +++ b/rust/crates/truapi/src/api/contacts.rs @@ -0,0 +1,94 @@ +//! Unified [`Contacts`] trait. + +use crate::versioned::contacts::{ + HostContactsDeriveSharedKeyError, HostContactsDeriveSharedKeyRequest, + HostContactsDeriveSharedKeyResponse, HostContactsResolveError, HostContactsResolveRequest, + HostContactsResolveResponse, HostContactsSendError, HostContactsSendRequest, + HostContactsSubscribeError, HostContactsSubscribeItem, +}; +use crate::wire; +use crate::{CallContext, CallError, Subscription}; + +/// Contact requests and invitations between users, mediated by the host +/// (RFC 0022). +pub trait Contacts: Send + Sync { + /// Resolve a peer to their identity key and published exchange key. + /// + /// ```ts + /// const result = await truapi.contacts.resolve({ + /// peer: { tag: "Username", value: "alice" }, + /// }); + /// assert(result.isOk(), "resolve failed:", result); + /// console.log("peer resolved:", result.value); + /// ``` + #[wire(request_id = 164)] + async fn resolve( + &self, + _cx: &CallContext, + _request: HostContactsResolveRequest, + ) -> Result> { + Err(CallError::unavailable()) + } + + /// Derive a symmetric key shared with a peer, scoped to the calling + /// product and a caller-chosen context. Both sides derive the same key + /// from the same context; the exchange secrets never leave the host. + /// + /// ```ts + /// // context: blake2b256("link3:content:v1") — at most 32 bytes. + /// const result = await truapi.contacts.deriveSharedKey({ + /// peer: { tag: "Username", value: "alice" }, + /// context: "0x6c696e6b333a636f6e74656e743a7631", + /// }); + /// assert(result.isOk(), "deriveSharedKey failed:", result); + /// console.log("shared key derived:", result.value); + /// ``` + #[wire(request_id = 166)] + async fn derive_shared_key( + &self, + _cx: &CallContext, + _request: HostContactsDeriveSharedKeyRequest, + ) -> Result> + { + Err(CallError::unavailable()) + } + + /// Seal a payload to a recipient and submit it to their contact inbox. + /// Triggers the `ContactSend` permission prompt on first use (RFC 0002). + /// + /// ```ts + /// const result = await truapi.contacts.send({ + /// recipient: { tag: "Username", value: "alice" }, + /// payload: "0x68656c6c6f", + /// }); + /// assert(result.isOk(), "send failed:", result); + /// console.log("contact sent"); + /// ``` + #[wire(request_id = 168)] + async fn send( + &self, + _cx: &CallContext, + _request: HostContactsSendRequest, + ) -> Result<(), CallError> { + Err(CallError::unavailable()) + } + + /// Receive contact payloads addressed to the calling product. Replays + /// persisted contacts first (`isComplete: false`), then streams live + /// ones (`isComplete: true`), as in RFC 0008. + /// + /// ```ts + /// import { firstValueFrom, from } from "rxjs"; + /// + /// const item = await firstValueFrom(from(truapi.contacts.subscribe())); + /// console.log("contacts received:", item); + /// ``` + #[wire(start_id = 170)] + async fn subscribe( + &self, + _cx: &CallContext, + ) -> Result, CallError> + { + Err(CallError::unavailable()) + } +} diff --git a/rust/crates/truapi/src/v01.rs b/rust/crates/truapi/src/v01.rs index 8b34df5a..667746d9 100644 --- a/rust/crates/truapi/src/v01.rs +++ b/rust/crates/truapi/src/v01.rs @@ -5,6 +5,7 @@ mod chain; mod chat; mod coin_payment; mod common; +mod contacts; mod entropy; mod local_storage; mod notifications; @@ -23,6 +24,7 @@ pub use chain::*; pub use chat::*; pub use coin_payment::*; pub use common::*; +pub use contacts::*; pub use entropy::*; pub use local_storage::*; pub use notifications::*; diff --git a/rust/crates/truapi/src/v01/contacts.rs b/rust/crates/truapi/src/v01/contacts.rs new file mode 100644 index 00000000..d7dc2dc9 --- /dev/null +++ b/rust/crates/truapi/src/v01/contacts.rs @@ -0,0 +1,134 @@ +use parity_scale_codec::{Decode, Encode}; + +/// A contact peer addressed by DotNS username or identity public key +/// (RFC 0022). +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +pub enum ContactPeer { + /// DotNS username, resolved to its owning identity account. + Username(String), + /// Identity account public key (the primary-username owner). + Identity([u8; 32]), +} + +/// Request to resolve a peer's identity and published exchange key. +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +pub struct HostContactsResolveRequest { + /// Peer to resolve. + pub peer: ContactPeer, +} + +/// A peer's reachability record. +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +pub struct HostContactsResolveResponse { + /// Identity account public key. + pub identity: [u8; 32], + /// Published X25519 exchange public key. `None`: the identity exists but + /// has no published exchange key (e.g. its host predates RFC 0022); the + /// peer is not yet reachable. + pub exchange_key: Option<[u8; 32]>, +} + +/// Peer resolution error. +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +pub enum HostContactsResolveError { + /// No identity is registered for the peer. + NotFound, + /// Catch-all. + Unknown { reason: String }, +} + +/// Request to derive a symmetric key shared with a peer, scoped to the +/// calling product and a caller-chosen context. +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +pub struct HostContactsDeriveSharedKeyRequest { + /// Peer to derive the shared key with. + pub peer: ContactPeer, + /// Domain-separation context, at most 32 bytes (as in RFC 0007, callers + /// hash longer contexts down). + pub context: Vec, +} + +/// A derived shared key. +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +pub struct HostContactsDeriveSharedKeyResponse { + /// The derived 32-byte symmetric key. + pub key: [u8; 32], +} + +/// Shared-key derivation error. +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +pub enum HostContactsDeriveSharedKeyError { + /// No identity is registered for the peer. + NotFound, + /// The peer has no published exchange key. + NotReachable, + /// The context exceeds 32 bytes. + ContextTooLong, + /// No identity session is active. + NotConnected, + /// Catch-all. + Unknown { reason: String }, +} + +/// Request to seal a payload to a recipient and submit it to their contact +/// inbox. +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +pub struct HostContactsSendRequest { + /// Recipient of the contact. + pub recipient: ContactPeer, + /// Opaque product payload carried inside the sealed envelope. + pub payload: Vec, +} + +/// Contact send error. +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +pub enum HostContactsSendError { + /// No identity is registered for the recipient. + NotFound, + /// The recipient has no published exchange key. + NotReachable, + /// The sealed statement would exceed the store's statement size limit. + PayloadTooLarge, + /// The user denied the `ContactSend` permission. + PermissionDenied, + /// No identity session is active. + NotConnected, + /// Catch-all. + Unknown { reason: String }, +} + +/// One contact delivered to the calling product. +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +pub struct ContactDelivery { + /// Authenticated sender identity public key. + pub sender_identity: [u8; 32], + /// Sender's DotNS username, when the host can resolve one. + pub sender_username: Option, + /// Opaque product payload. + pub payload: Vec, + /// Unix-seconds timestamp the sender stamped on the contact. + pub sent_at: u64, +} + +/// Page of contacts delivered by the contacts subscription. The +/// `is_complete` flag distinguishes the replay of contacts received before +/// this subscription (`false`) from the live-update phase (`true`), as in +/// RFC 0008. +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +pub struct HostContactsSubscribeItem { + /// Contacts addressed to the calling product. + pub contacts: Vec, + /// `false` while the host is still replaying persisted contacts (more + /// pages to follow). `true` once the replay is complete; all subsequent + /// pages are also `true` and carry only newly-arrived contacts. + pub is_complete: bool, +} + +/// Contacts subscription error. +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +pub enum HostContactsSubscribeError { + /// No identity session is active. + NotConnected, + /// Catch-all. + Unknown { reason: String }, +} diff --git a/rust/crates/truapi/src/v01/permissions.rs b/rust/crates/truapi/src/v01/permissions.rs index c4873a6f..7831e69a 100644 --- a/rust/crates/truapi/src/v01/permissions.rs +++ b/rust/crates/truapi/src/v01/permissions.rs @@ -31,8 +31,9 @@ pub enum HostDevicePermissionRequest { /// One remote-operation permission requested by the product (RFC 0002). /// -/// `ChainSubmit`, `PreimageSubmit`, and `StatementSubmit` are also triggered -/// implicitly by the corresponding business calls when not yet granted. +/// `ChainSubmit`, `PreimageSubmit`, `StatementSubmit`, and `ContactSend` are +/// also triggered implicitly by the corresponding business calls when not yet +/// granted. #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Display)] pub enum RemotePermission { /// Outbound HTTP/WebSocket access to a set of domains. @@ -53,6 +54,9 @@ pub enum RemotePermission { /// Submitting statements on behalf of the user via `remote_statement_store_submit`. #[display("submit statements")] StatementSubmit, + /// Sending contact requests on behalf of the user via `host_contacts_send` (RFC 0022). + #[display("send contact requests")] + ContactSend, } /// remote-permission request (RFC 0002). diff --git a/rust/crates/truapi/src/versioned.rs b/rust/crates/truapi/src/versioned.rs index 9da72067..2014917d 100644 --- a/rust/crates/truapi/src/versioned.rs +++ b/rust/crates/truapi/src/versioned.rs @@ -34,6 +34,7 @@ pub mod account; pub mod chain; pub mod chat; pub mod coin_payment; +pub mod contacts; pub mod entropy; pub mod local_storage; pub mod notifications; diff --git a/rust/crates/truapi/src/versioned/contacts.rs b/rust/crates/truapi/src/versioned/contacts.rs new file mode 100644 index 00000000..2b813b3d --- /dev/null +++ b/rust/crates/truapi/src/versioned/contacts.rs @@ -0,0 +1,16 @@ +//! Versioned wrappers for [`Contacts`](crate::api::Contacts) methods. + +use crate::v01; + +truapi_macros::versioned_type! { + pub enum HostContactsResolveRequest { V1 => v01::HostContactsResolveRequest } + pub enum HostContactsResolveResponse { V1 => v01::HostContactsResolveResponse } + pub enum HostContactsResolveError { V1 => v01::HostContactsResolveError } + pub enum HostContactsDeriveSharedKeyRequest { V1 => v01::HostContactsDeriveSharedKeyRequest } + pub enum HostContactsDeriveSharedKeyResponse { V1 => v01::HostContactsDeriveSharedKeyResponse } + pub enum HostContactsDeriveSharedKeyError { V1 => v01::HostContactsDeriveSharedKeyError } + pub enum HostContactsSendRequest { V1 => v01::HostContactsSendRequest } + pub enum HostContactsSendError { V1 => v01::HostContactsSendError } + pub enum HostContactsSubscribeItem { V1 => v01::HostContactsSubscribeItem } + pub enum HostContactsSubscribeError { V1 => v01::HostContactsSubscribeError } +}