@novasamatech/host-papp 0.8.10 → 0.8.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,10 +35,6 @@ const papp = createPappAdapter({
35
35
  // otherwise existing pairings will be lost.
36
36
  appId: 'my-host-app',
37
37
 
38
- // URL to a JSON document describing the host: { name: string, icon: string }.
39
- // The icon should be a rasterized image at least 256x256 px.
40
- metadata: 'https://my-host-app.example/papp-metadata.json',
41
-
42
38
  // Optional environment metadata shown on the wallet's confirmation screen.
43
39
  hostMetadata: {
44
40
  hostVersion: '1.4.0',
@@ -48,14 +44,15 @@ const papp = createPappAdapter({
48
44
  });
49
45
  ```
50
46
 
51
- `createPappAdapter` returns four sub-modules:
47
+ `createPappAdapter` returns five sub-modules:
52
48
 
53
- | Module | Purpose |
54
- | --------------- | ---------------------------------------------------------------------- |
55
- | `papp.sso` | Authentication / pairing flow with a remote wallet. |
56
- | `papp.sessions` | List of paired user sessions and per-session messaging (sign, etc.). |
57
- | `papp.secrets` | Local secret storage for the derived guest accounts. |
58
- | `papp.identity` | On-chain identity lookups for arbitrary account ids. |
49
+ | Module | Purpose |
50
+ | ---------------- | ------------------------------------------------------------------- |
51
+ | `papp.sso` | Authentication / pairing flow with a remote wallet. |
52
+ | `papp.sessions` | List of paired user sessions and per-session messaging (sign, etc.).|
53
+ | `papp.secrets` | Local secret storage for the derived guest accounts. |
54
+ | `papp.identity` | On-chain identity lookups for arbitrary account ids. |
55
+ | `papp.allowance` | Resource allowances (bulletin / statement-store signers) per product.|
59
56
 
60
57
  Custom adapters (statement store, identity RPC, storage, lazy chain client) can be supplied
61
58
  via the `adapters` option for testing or non-browser environments.
@@ -200,6 +197,59 @@ await currentSession.signRaw({
200
197
  });
201
198
  ```
202
199
 
200
+ `signVrf` asks the wallet for an sr25519 (schnorrkel) VRF signature from a product account
201
+ (RFC-0023). The transcript travels as a recipe — a root domain-separation label plus an
202
+ ordered list of `(label, value)` items — which the wallet replays verbatim into a Merlin
203
+ transcript and signs. Callers that need a `signer` item must supply their own public key;
204
+ the host never injects it.
205
+
206
+ ```ts
207
+ const encoder = new TextEncoder();
208
+
209
+ const vrf = await currentSession.signVrf({
210
+ productAccountId: ['product.dot', 0],
211
+ productId: 'product.dot',
212
+ transcriptLabel: encoder.encode('pop:airdrop'),
213
+ items: [
214
+ { label: encoder.encode('domain'), value: domainBytes },
215
+ { label: encoder.encode('signer'), value: accountPublicKey },
216
+ ],
217
+ });
218
+
219
+ vrf.match(
220
+ ({ preOutput, proof }) => submitLotteryTicket(preOutput, proof),
221
+ error => console.error('VRF signing failed:', error),
222
+ );
223
+ ```
224
+
225
+ This is the non-`AutoSigning` path only: when `AutoSigning` covers the account the host
226
+ signs locally and never round-trips to the wallet.
227
+
228
+ ## Ring VRF proofs and aliases
229
+
230
+ A `UserSession` can ask the paired device for a privacy-preserving contextual alias, or a
231
+ ring VRF proof, for a product-scoped `context` and a `ring` location. The device
232
+ selects the member key for the ring; `callingProductId` names the product the host is acting
233
+ for. Both take the same `(context, ring)` so the alias in the proof matches `getRingVrfAlias`.
234
+
235
+ ```ts
236
+ const context = ['product.dot', '0x00']; // [productId, suffix] — suffix is a 0x-hex string
237
+ const ring = {
238
+ chainId: '0x…', // 32-byte chain genesis hash
239
+ junctions: [{ tag: 'PalletInstance', value: 42 }],
240
+ };
241
+
242
+ const alias = await currentSession.getRingVrfAlias('caller.dot', context, ring);
243
+
244
+ const proof = await currentSession.createRingVrfProof('caller.dot', context, ring, new Uint8Array([0x48, 0x69]));
245
+ proof.match(
246
+ ({ proof, contextualAlias, ringIndex, ringRevision }) =>
247
+ console.log('proof at ring', ringIndex, 'revision', ringRevision),
248
+ // failures decode to a structured `RingVrfError` (RingNotFound / NotMember / Rejected / Unknown)
249
+ error => console.error('proof failed:', error),
250
+ );
251
+ ```
252
+
203
253
  ## Identity lookups
204
254
 
205
255
  `papp.identity` resolves on-chain identity data (lite / full username, credibility, slots)
@@ -221,6 +271,13 @@ const lookup = async (accountId: string) => {
221
271
  await papp.identity.getIdentities([accountIdA, accountIdB]);
222
272
  ```
223
273
 
274
+ A paired `UserSession` also exposes `getIdentity()` as a shortcut that looks up the identity
275
+ of its own user identity account — no account id to pass:
276
+
277
+ ```ts
278
+ const identity = await session.getIdentity(); // Result<Identity | null, Error>
279
+ ```
280
+
224
281
  ## V2 SSO handshake
225
282
 
226
283
  V2 is a redesign of the SSO pairing flow that supports the same user identity across
@@ -233,38 +290,15 @@ identity, so contacts, chats, and roster events are shared between them.
233
290
  V2 is **not interoperable with V1**: a V1-only peer can't decode a V2 proposal QR and vice
234
291
  versa. Hosts that want to support both should branch on which protocol the peer advertises.
235
292
 
236
- ### Shape of the flow
237
-
238
- ```
239
- host peer (authorising device)
240
- ────────────────────────────────────────────────────────────────────────
241
- buildPairingDeeplink(device, metadata)
242
- → polkadotapp://pair?handshake=<hex>
243
- scan QR, decode proposal
244
- compute pairing topic from
245
- the host's pubkeys
246
- ECDH-encrypt + post:
247
- Pending(AllowanceAllocation)
248
- Success { encryptionKey,
249
- accountId,
250
- identitySignature }
251
- Failed(reason)
252
- service.subscribeStatements(topic) +
253
- poll the topic every 2s
254
-
255
- decode VersionedHandshakeResponse::V2
256
- → ECDH-decrypt envelope with the
257
- device encryption private key
258
- → SCALE-decode inner payload
259
- → state machine: Submitted → Pending →
260
- Success | Failed
261
- → on Success persist user identity
262
- ```
293
+ ### The flow
263
294
 
264
- The user identity carried in `Success` is the chat encryption pubkey + the user's identity
265
- sr25519 accountId. The host verifies the 64-byte sr25519 `identitySignature` against the
266
- canonical 97 bytes `statementAccountId || encryptionPublicKey`
267
- (see `IDENTITY_SIGNATURE_PAYLOAD_BYTES`).
295
+ 1. The host builds a pairing deeplink from its device keypair and shows it as a QR code.
296
+ 2. The authorising device scans it and posts its response to the Statement Store: first a
297
+ `Pending` acknowledgement, then either `Success` carrying the user's identity keys,
298
+ signed to authorise this device — or `Failed`.
299
+ 3. The host polls the pairing topic, decrypts and verifies each response, and drives a
300
+ `Submitted → Pending → Success | Failed` state machine. On `Success` it persists the
301
+ user identity.
268
302
 
269
303
  ### Building and rendering the QR
270
304
 
@@ -293,7 +327,7 @@ renderQrCode(deeplink); // 'polkadotapp://pair?handshake=<hex>'
293
327
  import { startPairingV2 } from '@novasamatech/host-papp';
294
328
 
295
329
  const pairing = startPairingV2({
296
- statementStore: papp.adapters.statementStore, // any StatementStoreAdapter
330
+ statementStore, // any StatementStoreAdapter
297
331
  deviceIdentity: {
298
332
  statementAccountPublicKey: device.statementAccountPublicKey,
299
333
  encryptionPublicKey: device.encryptionPublicKey,
@@ -355,33 +389,14 @@ The service skips any incoming statement whose bytes match `initialProcessedData
355
389
  re-encrypts every Success with a fresh ephemeral key + AES-GCM nonce, so a genuine re-pair
356
390
  always produces different bytes and passes the dedupe.
357
391
 
358
- ### Pairing topic / channel
392
+ ## Reading allowances
359
393
 
360
- If the host needs to derive the pairing topic or channel itself (for example to subscribe
361
- in-line, or to verify a statement source):
394
+ Each `UserSession` can read its own persisted allowance slot-account key for a given
395
+ product and resource. The session id is implicit — you only pass the product and resource:
362
396
 
363
397
  ```ts
364
- import { computePairingTopic, computePairingChannel } from '@novasamatech/host-papp';
398
+ const session = papp.sessions.sessions.read().at(0);
365
399
 
366
- const topic = computePairingTopic(statementAccountId, encryptionPublicKey);
367
- const channel = computePairingChannel(statementAccountId, encryptionPublicKey);
368
- // topic = blake2b256_keyed(encryptionPublicKey || "topic", key=statementAccountId)
369
- // channel = blake2b256_keyed(encryptionPublicKey || "channel", key=statementAccountId)
400
+ // resource: 'bulletin' | 'statementStore'
401
+ const key = await session.readAllowance(productId, 'statementStore'); // Result<Uint8Array | null, Error>
370
402
  ```
371
-
372
- ### Codec exports
373
-
374
- The SCALE codecs are exported as plain `Codec<T>` values for callers that need to
375
- encode/decode statements outside the orchestrator:
376
-
377
- | Export | Description |
378
- | --------------------------------- | -------------------------------------------------------------------------------------------- |
379
- | `VersionedHandshakeProposal` | Outer enum; V2 at SCALE discriminant 1, with `_v1Reserved` at 0. |
380
- | `HandshakeProposalV2` | `{ device, metadata }` — what the QR encodes. |
381
- | `Device` | `{ statementAccountId(32), encryptionPublicKey(65) }`. |
382
- | `MetadataKey`, `MetadataEntry` | Metadata enum + `(MetadataKey, str)` tuple. |
383
- | `VersionedHandshakeResponse` | Outer enum for the answer; `V1` legacy + `V2`. |
384
- | `HandshakeResponseV2` | `{ encrypted, tmpKey(65) }` — the ECDH-wrapped envelope. |
385
- | `EncryptedHandshakeResponseV2` | Inner payload after envelope decrypt: `Pending` (1 byte), `Success` (161 bytes), `Failed`. |
386
- | `HandshakeSuccessV2` | `{ encryptionKey(65), accountId(32), identitySignature(64) }`. |
387
- | `IDENTITY_SIGNATURE_PAYLOAD_BYTES`| `97` — the bytes the user identity sr25519 signs over. |
@@ -1,5 +1,5 @@
1
1
  import { setTimeout } from 'node:timers/promises';
2
- import { err, fromPromise, ok, okAsync } from 'neverthrow';
2
+ import { fromPromise, okAsync } from 'neverthrow';
3
3
  import { describe, expect, it, vi } from 'vitest';
4
4
  import { createAsyncTaskPool } from './createAsyncTaskPool.js';
5
5
  import { toError } from './utils.js';
@@ -8,7 +8,7 @@ describe('asyncTaskPool', () => {
8
8
  it('should exec async task', async () => {
9
9
  const pool = createAsyncTaskPool({ poolSize: 1, retryCount: 0, retryDelay: () => 0 });
10
10
  const result = await pool.call(() => fromPromise(delay().then(() => 'test'), toError));
11
- expect(result).toEqual(ok('test'));
11
+ await expect(result).toBeOkWith('test');
12
12
  });
13
13
  it('should handle sync errors', async () => {
14
14
  const pool = createAsyncTaskPool({ poolSize: 1, retryCount: 0, retryDelay: () => 0 });
@@ -16,13 +16,13 @@ describe('asyncTaskPool', () => {
16
16
  const result = await pool.call(() => {
17
17
  throw error;
18
18
  });
19
- expect(result).toEqual(err(error));
19
+ await expect(result).toBeErrWith(error);
20
20
  });
21
21
  it('should handle async errors', async () => {
22
22
  const pool = createAsyncTaskPool({ poolSize: 1, retryCount: 0, retryDelay: () => 0 });
23
23
  const error = new Error('test');
24
24
  const result = await pool.call(() => fromPromise(Promise.reject(error), toError));
25
- return expect(result).toEqual(err(error));
25
+ await expect(result).toBeErrWith(error);
26
26
  });
27
27
  it('should handle queue', async () => {
28
28
  const pool = createAsyncTaskPool({ poolSize: 2, retryCount: 0, retryDelay: () => 0 });
@@ -52,7 +52,7 @@ describe('asyncTaskPool', () => {
52
52
  tries++;
53
53
  throw new Error();
54
54
  });
55
- expect(result).toEqual(ok('test'));
55
+ await expect(result).toBeOkWith('test');
56
56
  });
57
57
  it('should throw on retry limit exceeding', async () => {
58
58
  const spy = vi.fn(() => 0);
@@ -66,7 +66,7 @@ describe('asyncTaskPool', () => {
66
66
  throw new Error();
67
67
  });
68
68
  expect(spy).toBeCalledTimes(1);
69
- expect(result).toEqual(err(new Error()));
69
+ await expect(result).toBeErrWith(new Error());
70
70
  });
71
71
  it('should correctly calculate retry delay', async () => {
72
72
  const spy = vi.fn((retry) => retry * 10);
@@ -125,9 +125,9 @@ describe('asyncTaskPool', () => {
125
125
  const queued = pool.call(queuedSpy, { signal: controller.signal });
126
126
  controller.abort();
127
127
  const queuedResult = await queued;
128
- expect(queuedResult.isErr()).toBe(true);
128
+ await expect(queuedResult).toBeErr();
129
129
  expect(queuedSpy).not.toHaveBeenCalled();
130
- expect((await active).isOk()).toBe(true);
130
+ await expect(await active).toBeOk();
131
131
  });
132
132
  it('rejects the in-flight active task when the signal aborts', async () => {
133
133
  const pool = createAsyncTaskPool({ poolSize: 1, retryCount: 0, retryDelay: 0 });
@@ -136,7 +136,7 @@ describe('asyncTaskPool', () => {
136
136
  signal: controller.signal,
137
137
  });
138
138
  controller.abort();
139
- expect((await active).isErr()).toBe(true);
139
+ await expect(await active).toBeErr();
140
140
  });
141
141
  it('frees the slot for later tasks after an abort', async () => {
142
142
  const pool = createAsyncTaskPool({ poolSize: 1, retryCount: 0, retryDelay: 0 });
@@ -147,7 +147,7 @@ describe('asyncTaskPool', () => {
147
147
  controller.abort();
148
148
  await aborted;
149
149
  const next = await pool.call(() => okAsync('next'));
150
- expect(next).toEqual(ok('next'));
150
+ await expect(next).toBeOkWith('next');
151
151
  });
152
152
  it('rejects immediately when called with an already-aborted signal', async () => {
153
153
  const pool = createAsyncTaskPool({ poolSize: 1, retryCount: 0, retryDelay: 0 });
@@ -155,7 +155,7 @@ describe('asyncTaskPool', () => {
155
155
  controller.abort();
156
156
  const spy = vi.fn(() => okAsync('x'));
157
157
  const result = await pool.call(spy, { signal: controller.signal });
158
- expect(result.isErr()).toBe(true);
158
+ await expect(result).toBeErr();
159
159
  expect(spy).not.toHaveBeenCalled();
160
160
  });
161
161
  });
package/dist/index.d.ts CHANGED
@@ -6,9 +6,11 @@ export type { PairingStatus } from './sso/auth/types.js';
6
6
  export type { DeviceIdentityForPairing } from './sso/auth/v2/service.js';
7
7
  export type { AllowanceErrorReason, AllowanceService } from './sso/allowance/index.js';
8
8
  export { AllowanceError } from './sso/allowance/index.js';
9
+ export type { AllowanceResourceKind } from './sso/allowance/index.js';
9
10
  export type { UserSession } from './sso/sessionManager/userSession.js';
10
11
  export type { StoredUserSession } from './sso/userSessionRepository.js';
11
12
  export type { Identity } from './identity/types.js';
12
13
  export type { SignRawLegacyRequest, SignRawLegacyResponse, SigningPayloadRequest, SigningPayloadResponse, SigningRawRequest, SigningRequest, } from './sso/sessionManager/scale/signing.js';
13
- export type { RingVrfAliasRequest, RingVrfAliasResponse } from './sso/sessionManager/scale/ringVrf.js';
14
+ export type { RingVrfAliasRequest, RingVrfAliasResponse, RingVrfProofRequest, RingVrfProofResponse, } from './sso/sessionManager/scale/ringVrf.js';
15
+ export type { SignVrfErr, SignVrfRequest, SignVrfResponse } from './sso/sessionManager/scale/signVrf.js';
14
16
  export type { CreateTransactionLegacyRequest, CreateTransactionRequest, CreateTransactionResponse, } from './sso/sessionManager/scale/createTransaction.js';
package/dist/papp.js CHANGED
@@ -19,6 +19,7 @@ export function createPappAdapter({ appId, hostMetadata, deviceIdentity, onAuthS
19
19
  const ssoSessionRepository = createUserSessionRepository(storage);
20
20
  const userSecretRepository = createUserSecretRepository(appId, storage);
21
21
  const allowanceRepository = createAllowanceRepository(appId, storage);
22
+ const identityRepository = createIdentityRepository({ adapter: identities, storage });
22
23
  const deviceIdentityStore = createDeviceIdentityStore(appId, storage);
23
24
  const sessions = createSsoSessionManager({
24
25
  storage,
@@ -26,6 +27,7 @@ export function createPappAdapter({ appId, hostMetadata, deviceIdentity, onAuthS
26
27
  ssoSessionRepository,
27
28
  userSecretRepository,
28
29
  allowanceRepository,
30
+ identityRepository,
29
31
  });
30
32
  return {
31
33
  sso: createAuth({
@@ -39,7 +41,7 @@ export function createPappAdapter({ appId, hostMetadata, deviceIdentity, onAuthS
39
41
  }),
40
42
  sessions,
41
43
  secrets: userSecretRepository,
42
- identity: createIdentityRepository({ adapter: identities, storage }),
44
+ identity: identityRepository,
43
45
  allowance: createAllowanceService({ sessions: sessions.sessions, repository: allowanceRepository }),
44
46
  };
45
47
  }
@@ -1,5 +1,6 @@
1
1
  import type { StatementStoreAdapter } from '@novasamatech/statement-store';
2
2
  import type { StorageAdapter } from '@novasamatech/storage-adapter';
3
+ import type { IdentityRepository } from '../../identity/types.js';
3
4
  import type { Callback } from '../../types.js';
4
5
  import type { AllowanceRepository } from '../allowance/index.js';
5
6
  import type { UserSecretRepository } from '../userSecretRepository.js';
@@ -12,8 +13,9 @@ type Params = {
12
13
  ssoSessionRepository: UserSessionRepository;
13
14
  userSecretRepository: UserSecretRepository;
14
15
  allowanceRepository: AllowanceRepository;
16
+ identityRepository: IdentityRepository;
15
17
  };
16
- export declare function createSsoSessionManager({ ssoSessionRepository, userSecretRepository, allowanceRepository, statementStore, storage, }: Params): {
18
+ export declare function createSsoSessionManager({ ssoSessionRepository, userSecretRepository, allowanceRepository, identityRepository, statementStore, storage, }: Params): {
17
19
  sessions: {
18
20
  read: () => UserSession[];
19
21
  subscribe: (callback: Callback<UserSession[]>) => () => void;
@@ -4,7 +4,7 @@ import { emitHostPappDebugMessage } from '../../debugBus.js';
4
4
  import { createState } from '../../helpers/state.js';
5
5
  import { createSsoStatementProver } from '../ssoSessionProver.js';
6
6
  import { createUserSession } from './userSession.js';
7
- export function createSsoSessionManager({ ssoSessionRepository, userSecretRepository, allowanceRepository, statementStore, storage, }) {
7
+ export function createSsoSessionManager({ ssoSessionRepository, userSecretRepository, allowanceRepository, identityRepository, statementStore, storage, }) {
8
8
  const localSessions = createState({});
9
9
  const sessionUnsubscribes = new Map();
10
10
  const releaseSession = (id) => {
@@ -22,7 +22,7 @@ export function createSsoSessionManager({ ssoSessionRepository, userSecretReposi
22
22
  toRemove.delete(userSession.id);
23
23
  if (userSession.id in activeSessions)
24
24
  continue;
25
- const session = createSession(userSession, statementStore, storage, userSecretRepository);
25
+ const session = createSession(userSession, statementStore, storage, userSecretRepository, allowanceRepository, identityRepository);
26
26
  toAdd.add(session);
27
27
  emitHostPappDebugMessage({
28
28
  layer: 'session',
@@ -73,7 +73,7 @@ export function createSsoSessionManager({ ssoSessionRepository, userSecretReposi
73
73
  subscribe: (callback) => localSessions.subscribe(sessions => callback(Object.values(sessions))),
74
74
  },
75
75
  disconnect(userSession) {
76
- const session = createSession(userSession, statementStore, storage, userSecretRepository);
76
+ const session = createSession(userSession, statementStore, storage, userSecretRepository, allowanceRepository, identityRepository);
77
77
  return session
78
78
  .sendDisconnectMessage()
79
79
  .andThen(() => disconnect(userSession))
@@ -88,7 +88,7 @@ export function createSsoSessionManager({ ssoSessionRepository, userSecretReposi
88
88
  },
89
89
  };
90
90
  }
91
- function createSession(userSession, statementStore, storage, userSecretRepository) {
91
+ function createSession(userSession, statementStore, storage, userSecretRepository, allowanceRepository, identityRepository) {
92
92
  const encryption = createEncryption(userSession.remoteAccount.publicKey);
93
93
  const prover = createSsoStatementProver(userSession, userSecretRepository);
94
94
  return createUserSession({
@@ -97,5 +97,7 @@ function createSession(userSession, statementStore, storage, userSecretRepositor
97
97
  encryption,
98
98
  storage,
99
99
  prover,
100
+ allowanceRepository,
101
+ identityRepository,
100
102
  });
101
103
  }
@@ -0,0 +1,12 @@
1
+ import type { CodecType } from 'scale-ts';
2
+ export type ProductSubtreeRequest = CodecType<typeof ProductSubtreeRequestCodec>;
3
+ export declare const ProductSubtreeRequestCodec: import("scale-ts").Codec<{
4
+ productId: string;
5
+ }>;
6
+ export type ProductSubtreeResponse = CodecType<typeof ProductSubtreeResponseCodec>;
7
+ export declare const ProductSubtreeResponseCodec: import("scale-ts").Codec<{
8
+ respondingTo: string;
9
+ payload: import("scale-ts").ResultPayload<{
10
+ productPublicKey: Uint8Array<ArrayBufferLike>;
11
+ }, string>;
12
+ }>;
@@ -0,0 +1,21 @@
1
+ import { DotNsIdentifier } from '@novasamatech/host-api';
2
+ import { Bytes, Result, Struct, str } from 'scale-ts';
3
+ // RFC-0022 made `//product//{productId}` a hard junction, so the root public
4
+ // key alone no longer determines product account public keys. This request
5
+ // closes the gap: the Account Holder returns the product-subtree public key,
6
+ // from which the Host soft-derives account public keys locally.
7
+ //
8
+ // Consent-free — the response carries no secret material. Fetch once per
9
+ // product and cache; only `AutoSigning` (secret material) requires consent.
10
+ /** 32-byte sr25519 public key of `//product//{productId}`. */
11
+ const Sr25519PublicKey = Bytes(32);
12
+ export const ProductSubtreeRequestCodec = Struct({
13
+ productId: DotNsIdentifier,
14
+ });
15
+ export const ProductSubtreeResponseCodec = Struct({
16
+ // referencing to RemoteMessage.messageId
17
+ respondingTo: str,
18
+ payload: Result(Struct({
19
+ productPublicKey: Sr25519PublicKey,
20
+ }), str),
21
+ });
@@ -54,8 +54,18 @@ export declare const RemoteMessageCodec: import("scale-ts").Codec<{
54
54
  } | {
55
55
  tag: "RingVrfAliasRequest";
56
56
  value: {
57
- productAccountId: [string, number];
58
- productId: string;
57
+ callingProductId: string;
58
+ context: [string, `0x${string}`];
59
+ ring: {
60
+ chainId: `0x${string}`;
61
+ junctions: ({
62
+ tag: "PalletInstance";
63
+ value: number;
64
+ } | {
65
+ tag: "CollectionId";
66
+ value: Uint8Array<ArrayBufferLike>;
67
+ })[];
68
+ };
59
69
  };
60
70
  } | {
61
71
  tag: "RingVrfAliasResponse";
@@ -64,7 +74,9 @@ export declare const RemoteMessageCodec: import("scale-ts").Codec<{
64
74
  payload: import("scale-ts").ResultPayload<{
65
75
  context: Uint8Array<ArrayBufferLike>;
66
76
  alias: Uint8Array<ArrayBufferLike>;
67
- }, string>;
77
+ }, import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::Rejected"> | import("@novasamatech/scale").CodecError<{
78
+ reason: string;
79
+ }, "GetAliasErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::NotMember">>;
68
80
  };
69
81
  } | {
70
82
  tag: "ResourceAllocationRequest";
@@ -179,6 +191,61 @@ export declare const RemoteMessageCodec: import("scale-ts").Codec<{
179
191
  respondingTo: string;
180
192
  signature: import("scale-ts").ResultPayload<Uint8Array<ArrayBufferLike>, string>;
181
193
  };
194
+ } | {
195
+ tag: "RingVrfProofRequest";
196
+ value: {
197
+ callingProductId: string;
198
+ context: [string, `0x${string}`];
199
+ ring: {
200
+ chainId: `0x${string}`;
201
+ junctions: ({
202
+ tag: "PalletInstance";
203
+ value: number;
204
+ } | {
205
+ tag: "CollectionId";
206
+ value: Uint8Array<ArrayBufferLike>;
207
+ })[];
208
+ };
209
+ message: Uint8Array<ArrayBufferLike>;
210
+ };
211
+ } | {
212
+ tag: "RingVrfProofResponse";
213
+ value: {
214
+ respondingTo: string;
215
+ payload: import("scale-ts").ResultPayload<{
216
+ proof: Uint8Array<ArrayBufferLike>;
217
+ contextualAlias: {
218
+ context: Uint8Array<ArrayBufferLike>;
219
+ alias: Uint8Array<ArrayBufferLike>;
220
+ };
221
+ ringIndex: number;
222
+ ringRevision: number;
223
+ }, import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::Rejected"> | import("@novasamatech/scale").CodecError<{
224
+ reason: string;
225
+ }, "CreateProofErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::NotMember">>;
226
+ };
227
+ } | {
228
+ tag: "SignVrfRequest";
229
+ value: {
230
+ productAccountId: [string, number];
231
+ productId: string;
232
+ transcriptLabel: Uint8Array<ArrayBufferLike>;
233
+ items: {
234
+ label: Uint8Array<ArrayBufferLike>;
235
+ value: Uint8Array<ArrayBufferLike>;
236
+ }[];
237
+ };
238
+ } | {
239
+ tag: "SignVrfResponse";
240
+ value: {
241
+ respondingTo: string;
242
+ payload: import("scale-ts").ResultPayload<{
243
+ preOutput: Uint8Array<ArrayBufferLike>;
244
+ proof: Uint8Array<ArrayBufferLike>;
245
+ }, import("@novasamatech/scale").CodecError<undefined, "SignVrfErr::Rejected"> | import("@novasamatech/scale").CodecError<{
246
+ reason: string;
247
+ }, "SignVrfErr::Unknown">>;
248
+ };
182
249
  };
183
250
  };
184
251
  }>;
@@ -1,11 +1,15 @@
1
- import { Enum, Struct, _void, str } from 'scale-ts';
1
+ import { Enum } from '@novasamatech/scale';
2
+ import { Struct, _void, str } from 'scale-ts';
2
3
  import { CreateTransactionLegacyRequestCodec, CreateTransactionRequestCodec, CreateTransactionResponseCodec, } from './createTransaction.js';
3
4
  import { ResourceAllocationRequestCodec, ResourceAllocationResponseCodec } from './resourceAllocation.js';
4
- import { RingVrfAliasRequestCodec, RingVrfAliasResponseCodec } from './ringVrf.js';
5
+ import { RingVrfAliasRequestCodec, RingVrfAliasResponseCodec, RingVrfProofRequestCodec, RingVrfProofResponseCodec, } from './ringVrf.js';
6
+ import { SignVrfRequestCodec, SignVrfResponseCodec } from './signVrf.js';
5
7
  import { SignRawLegacyRequestCodec, SignRawLegacyResponseCodec, SigningRequestCodec, SigningResponseCodec, } from './signing.js';
6
8
  export const RemoteMessageCodec = Struct({
7
9
  messageId: str,
8
10
  data: Enum({
11
+ // Declaration order is the SCALE wire order and must stay in lockstep with the
12
+ // truapi `host_logic::sso::messages::v1::RemoteMessage` enum. Append only.
9
13
  v1: Enum({
10
14
  Disconnected: _void,
11
15
  SignRequest: SigningRequestCodec,
@@ -19,6 +23,10 @@ export const RemoteMessageCodec = Struct({
19
23
  CreateTransactionLegacyRequest: CreateTransactionLegacyRequestCodec,
20
24
  SignRawLegacyRequest: SignRawLegacyRequestCodec,
21
25
  SignRawLegacyResponse: SignRawLegacyResponseCodec,
26
+ RingVrfProofRequest: RingVrfProofRequestCodec,
27
+ RingVrfProofResponse: RingVrfProofResponseCodec,
28
+ SignVrfRequest: SignVrfRequestCodec,
29
+ SignVrfResponse: SignVrfResponseCodec,
22
30
  }),
23
31
  }),
24
32
  });
@@ -1,8 +1,18 @@
1
1
  import type { CodecType } from 'scale-ts';
2
2
  export type RingVrfAliasRequest = CodecType<typeof RingVrfAliasRequestCodec>;
3
3
  export declare const RingVrfAliasRequestCodec: import("scale-ts").Codec<{
4
- productAccountId: [string, number];
5
- productId: string;
4
+ callingProductId: string;
5
+ context: [string, `0x${string}`];
6
+ ring: {
7
+ chainId: `0x${string}`;
8
+ junctions: ({
9
+ tag: "PalletInstance";
10
+ value: number;
11
+ } | {
12
+ tag: "CollectionId";
13
+ value: Uint8Array<ArrayBufferLike>;
14
+ })[];
15
+ };
6
16
  }>;
7
17
  export type RingVrfAliasResponse = CodecType<typeof RingVrfAliasResponseCodec>;
8
18
  export declare const RingVrfAliasResponseCodec: import("scale-ts").Codec<{
@@ -10,5 +20,38 @@ export declare const RingVrfAliasResponseCodec: import("scale-ts").Codec<{
10
20
  payload: import("scale-ts").ResultPayload<{
11
21
  context: Uint8Array<ArrayBufferLike>;
12
22
  alias: Uint8Array<ArrayBufferLike>;
13
- }, string>;
23
+ }, import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::Rejected"> | import("@novasamatech/scale").CodecError<{
24
+ reason: string;
25
+ }, "GetAliasErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::NotMember">>;
26
+ }>;
27
+ export type RingVrfProofRequest = CodecType<typeof RingVrfProofRequestCodec>;
28
+ export declare const RingVrfProofRequestCodec: import("scale-ts").Codec<{
29
+ callingProductId: string;
30
+ context: [string, `0x${string}`];
31
+ ring: {
32
+ chainId: `0x${string}`;
33
+ junctions: ({
34
+ tag: "PalletInstance";
35
+ value: number;
36
+ } | {
37
+ tag: "CollectionId";
38
+ value: Uint8Array<ArrayBufferLike>;
39
+ })[];
40
+ };
41
+ message: Uint8Array<ArrayBufferLike>;
42
+ }>;
43
+ export type RingVrfProofResponse = CodecType<typeof RingVrfProofResponseCodec>;
44
+ export declare const RingVrfProofResponseCodec: import("scale-ts").Codec<{
45
+ respondingTo: string;
46
+ payload: import("scale-ts").ResultPayload<{
47
+ proof: Uint8Array<ArrayBufferLike>;
48
+ contextualAlias: {
49
+ context: Uint8Array<ArrayBufferLike>;
50
+ alias: Uint8Array<ArrayBufferLike>;
51
+ };
52
+ ringIndex: number;
53
+ ringRevision: number;
54
+ }, import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::Rejected"> | import("@novasamatech/scale").CodecError<{
55
+ reason: string;
56
+ }, "CreateProofErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::NotMember">>;
14
57
  }>;
@@ -1,10 +1,21 @@
1
- import { ContextualAlias, ProductAccountId } from '@novasamatech/host-api';
2
- import { Result, Struct, str } from 'scale-ts';
1
+ import { ContextualAlias, CreateProofErr, DotNsIdentifier, GetAliasErr, ProductProofContext, RingLocation, RingVrfProof, } from '@novasamatech/host-api';
2
+ import { Bytes, Result, Struct, str } from 'scale-ts';
3
3
  export const RingVrfAliasRequestCodec = Struct({
4
- productAccountId: ProductAccountId,
5
- productId: str,
4
+ callingProductId: DotNsIdentifier,
5
+ context: ProductProofContext,
6
+ ring: RingLocation,
6
7
  });
7
8
  export const RingVrfAliasResponseCodec = Struct({
8
9
  respondingTo: str,
9
- payload: Result(ContextualAlias, str),
10
+ payload: Result(ContextualAlias, GetAliasErr),
11
+ });
12
+ export const RingVrfProofRequestCodec = Struct({
13
+ callingProductId: DotNsIdentifier,
14
+ context: ProductProofContext,
15
+ ring: RingLocation,
16
+ message: Bytes(),
17
+ });
18
+ export const RingVrfProofResponseCodec = Struct({
19
+ respondingTo: str,
20
+ payload: Result(RingVrfProof, CreateProofErr),
10
21
  });
@@ -0,0 +1,56 @@
1
+ import type { CodecType } from 'scale-ts';
2
+ /**
3
+ * Host → Account Holder request for an sr25519 (schnorrkel) VRF signature over a
4
+ * caller-supplied Merlin transcript (RFC-0023, "Accounts Protocol companion").
5
+ *
6
+ * The Account Holder derives `productAccountId`, presents the signing confirmation,
7
+ * replays the transcript verbatim — `Transcript::new(transcriptLabel)` then one
8
+ * `append_message(label, value)` per item, in order — and signs it. It performs no
9
+ * interpretation of labels or values.
10
+ *
11
+ * This is the non-`AutoSigning` path; when `AutoSigning` covers the account the host
12
+ * signs locally and never sends this message.
13
+ */
14
+ export type SignVrfRequest = CodecType<typeof SignVrfRequestCodec>;
15
+ export declare const SignVrfRequestCodec: import("scale-ts").Codec<{
16
+ productAccountId: [string, number];
17
+ productId: string;
18
+ transcriptLabel: Uint8Array<ArrayBufferLike>;
19
+ items: {
20
+ label: Uint8Array<ArrayBufferLike>;
21
+ value: Uint8Array<ArrayBufferLike>;
22
+ }[];
23
+ }>;
24
+ /** Failure returned by the Account Holder for a VRF signing request. */
25
+ export type SignVrfErr = CodecType<typeof SignVrfErrCodec>;
26
+ export declare const SignVrfErrCodec: [import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<undefined, "SignVrfErr::Rejected"> | import("@novasamatech/scale").CodecError<{
27
+ reason: string;
28
+ }, "SignVrfErr::Unknown">>, import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<undefined, "SignVrfErr::Rejected"> | import("@novasamatech/scale").CodecError<{
29
+ reason: string;
30
+ }, "SignVrfErr::Unknown">>] & {
31
+ enc: import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<undefined, "SignVrfErr::Rejected"> | import("@novasamatech/scale").CodecError<{
32
+ reason: string;
33
+ }, "SignVrfErr::Unknown">>;
34
+ dec: import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<undefined, "SignVrfErr::Rejected"> | import("@novasamatech/scale").CodecError<{
35
+ reason: string;
36
+ }, "SignVrfErr::Unknown">>;
37
+ } & {
38
+ readonly Rejected: import("@novasamatech/scale").ErrCodec<undefined, "SignVrfErr::Rejected">;
39
+ readonly Unknown: import("@novasamatech/scale").ErrCodec<{
40
+ reason: string;
41
+ }, "SignVrfErr::Unknown">;
42
+ } & {
43
+ [Symbol.hasInstance](v: unknown): v is import("@novasamatech/scale").CodecError<undefined, "SignVrfErr::Rejected"> | import("@novasamatech/scale").CodecError<{
44
+ reason: string;
45
+ }, "SignVrfErr::Unknown">;
46
+ };
47
+ export type SignVrfResponse = CodecType<typeof SignVrfResponseCodec>;
48
+ export declare const SignVrfResponseCodec: import("scale-ts").Codec<{
49
+ respondingTo: string;
50
+ payload: import("scale-ts").ResultPayload<{
51
+ preOutput: Uint8Array<ArrayBufferLike>;
52
+ proof: Uint8Array<ArrayBufferLike>;
53
+ }, import("@novasamatech/scale").CodecError<undefined, "SignVrfErr::Rejected"> | import("@novasamatech/scale").CodecError<{
54
+ reason: string;
55
+ }, "SignVrfErr::Unknown">>;
56
+ }>;
@@ -0,0 +1,18 @@
1
+ import { ProductAccountId, VrfSignature, VrfTranscriptItem } from '@novasamatech/host-api';
2
+ import { ErrEnum } from '@novasamatech/scale';
3
+ import { Bytes, Result, Struct, Vector, _void, str } from 'scale-ts';
4
+ export const SignVrfRequestCodec = Struct({
5
+ productAccountId: ProductAccountId,
6
+ productId: str,
7
+ transcriptLabel: Bytes(),
8
+ items: Vector(VrfTranscriptItem),
9
+ });
10
+ export const SignVrfErrCodec = ErrEnum('SignVrfErr', {
11
+ Rejected: [_void, 'Rejected'],
12
+ Unknown: [Struct({ reason: str }), ({ reason }) => reason],
13
+ });
14
+ export const SignVrfResponseCodec = Struct({
15
+ // referencing to RemoteMessage.messageId
16
+ respondingTo: str,
17
+ payload: Result(VrfSignature, SignVrfErrCodec),
18
+ });
@@ -1,15 +1,22 @@
1
- import { ContextualAlias, ProductAccountId } from '@novasamatech/host-api';
1
+ import { ContextualAlias, ProductProofContext, RingLocation, RingVrfProof, VrfSignature } from '@novasamatech/host-api';
2
2
  import type { Encryption, StatementProver, StatementStoreAdapter } from '@novasamatech/statement-store';
3
3
  import type { StorageAdapter } from '@novasamatech/storage-adapter';
4
4
  import { ResultAsync } from 'neverthrow';
5
5
  import type { CodecType } from 'scale-ts';
6
+ import type { Identity, IdentityRepository } from '../../identity/types.js';
6
7
  import type { Callback } from '../../types.js';
8
+ import type { AllowanceRepository, AllowanceResourceKind } from '../allowance/index.js';
7
9
  import type { StoredUserSession } from '../userSessionRepository.js';
8
10
  import type { CreateTransactionLegacyRequest, CreateTransactionRequest } from './scale/createTransaction.js';
9
11
  import { RemoteMessageCodec } from './scale/remoteMessage.js';
10
12
  import type { ApAllocationOutcome, ResourceAllocationRequest } from './scale/resourceAllocation.js';
13
+ import type { SignVrfRequest } from './scale/signVrf.js';
11
14
  import type { SignRawLegacyRequest, SigningPayloadRequest, SigningPayloadResponseData, SigningRawRequest } from './scale/signing.js';
12
15
  export type UserSession = StoredUserSession & {
16
+ /** Read this session's persisted allowance slot-account key for a product/resource. */
17
+ readAllowance(productId: string, resource: AllowanceResourceKind): ResultAsync<Uint8Array | null, Error>;
18
+ /** Look up the on-chain identity of this session's user identity account. */
19
+ getIdentity(): ResultAsync<Identity | null, Error>;
13
20
  sendDisconnectMessage(): ResultAsync<void, Error>;
14
21
  abortPendingRequests(): ResultAsync<void, Error>;
15
22
  signPayload(payload: SigningPayloadRequest): ResultAsync<SigningPayloadResponseData, Error>;
@@ -17,15 +24,19 @@ export type UserSession = StoredUserSession & {
17
24
  signRawLegacy(payload: SignRawLegacyRequest): ResultAsync<Uint8Array, Error>;
18
25
  createTransaction(payload: CreateTransactionRequest): ResultAsync<Uint8Array, Error>;
19
26
  createTransactionLegacy(payload: CreateTransactionLegacyRequest): ResultAsync<Uint8Array, Error>;
20
- getRingVrfAlias(productAccountId: CodecType<typeof ProductAccountId>, productId: string): ResultAsync<CodecType<typeof ContextualAlias>, Error>;
27
+ getRingVrfAlias(callingProductId: string, context: CodecType<typeof ProductProofContext>, ring: CodecType<typeof RingLocation>): ResultAsync<CodecType<typeof ContextualAlias>, Error>;
28
+ createRingVrfProof(callingProductId: string, context: CodecType<typeof ProductProofContext>, ring: CodecType<typeof RingLocation>, message: Uint8Array): ResultAsync<CodecType<typeof RingVrfProof>, Error>;
29
+ signVrf(payload: SignVrfRequest): ResultAsync<CodecType<typeof VrfSignature>, Error>;
21
30
  requestResourceAllocation(request: ResourceAllocationRequest): ResultAsync<ApAllocationOutcome[], Error>;
22
31
  subscribe(callback: Callback<CodecType<typeof RemoteMessageCodec>, ResultAsync<boolean, Error>>): VoidFunction;
23
32
  dispose(): void;
24
33
  };
25
- export declare function createUserSession({ userSession, statementStore, encryption, storage, prover, }: {
34
+ export declare function createUserSession({ userSession, statementStore, encryption, storage, prover, allowanceRepository, identityRepository, }: {
26
35
  userSession: StoredUserSession;
27
36
  statementStore: StatementStoreAdapter;
28
37
  encryption: Encryption;
29
38
  storage: StorageAdapter;
30
39
  prover: StatementProver;
40
+ allowanceRepository: AllowanceRepository;
41
+ identityRepository: IdentityRepository;
31
42
  }): UserSession;
@@ -1,9 +1,10 @@
1
- import { ContextualAlias, ProductAccountId } from '@novasamatech/host-api';
1
+ import { ContextualAlias, ProductProofContext, RingLocation, RingVrfProof, VrfSignature } from '@novasamatech/host-api';
2
2
  import { enumValue } from '@novasamatech/scale';
3
3
  import { createSession } from '@novasamatech/statement-store';
4
4
  import { fieldListView } from '@novasamatech/storage-adapter';
5
5
  import { nanoid } from 'nanoid';
6
6
  import { ResultAsync, err, ok, okAsync } from 'neverthrow';
7
+ import { toHex } from 'polkadot-api/utils';
7
8
  import { emitHostPappDebugMessage } from '../../debugBus.js';
8
9
  import { createAsyncTaskPool } from '../../helpers/createAsyncTaskPool.js';
9
10
  import { toError } from '../../helpers/utils.js';
@@ -80,7 +81,7 @@ function withHostActionTrace(result, messageId, sessionId) {
80
81
  });
81
82
  });
82
83
  }
83
- export function createUserSession({ userSession, statementStore, encryption, storage, prover, }) {
84
+ export function createUserSession({ userSession, statementStore, encryption, storage, prover, allowanceRepository, identityRepository, }) {
84
85
  const requestQueue = createAsyncTaskPool({ poolSize: 1, retryCount: 0, retryDelay: 0 });
85
86
  // Shared abort handle for everything currently on the request queue.
86
87
  // abortPendingRequests() fires it to drop the in-flight task plus anything
@@ -111,6 +112,12 @@ export function createUserSession({ userSession, statementStore, encryption, sto
111
112
  });
112
113
  return {
113
114
  ...userSession,
115
+ readAllowance(productId, resource) {
116
+ return allowanceRepository.read(userSession.id, productId, resource);
117
+ },
118
+ getIdentity() {
119
+ return identityRepository.getIdentity(toHex(userSession.identityAccountId));
120
+ },
114
121
  signPayload(payload) {
115
122
  return enqueue(() => {
116
123
  const messageId = nanoid();
@@ -237,12 +244,13 @@ export function createUserSession({ userSession, statementStore, encryption, sto
237
244
  return withHostActionTrace(withQueueTimeout(inner, 'createTransactionLegacy'), messageId, userSession.id);
238
245
  });
239
246
  },
240
- getRingVrfAlias(productAccountId, productId) {
247
+ getRingVrfAlias(callingProductId, context, ring) {
241
248
  return enqueue(() => {
242
249
  const messageId = nanoid();
243
250
  const data = enumValue('v1', enumValue('RingVrfAliasRequest', {
244
- productAccountId,
245
- productId,
251
+ callingProductId,
252
+ context,
253
+ ring,
246
254
  }));
247
255
  emitHostAction(messageId, actionKindFromMessageData(data), userSession.id);
248
256
  const responseFilter = (message) => {
@@ -254,7 +262,47 @@ export function createUserSession({ userSession, statementStore, encryption, sto
254
262
  };
255
263
  const request = session.request(RemoteMessageCodec, { messageId, data });
256
264
  const reply = session.waitForRequestMessage(RemoteMessageCodec, responseFilter);
257
- return withHostActionTrace(awaitReplyOrAckFailure(request, reply).andThen(result => result.success ? ok(result.value) : err(new Error(result.value))), messageId, userSession.id);
265
+ return withHostActionTrace(awaitReplyOrAckFailure(request, reply).andThen(result => result.success ? ok(result.value) : err(result.value)), messageId, userSession.id);
266
+ });
267
+ },
268
+ createRingVrfProof(callingProductId, context, ring, message) {
269
+ return enqueue(() => {
270
+ const messageId = nanoid();
271
+ const data = enumValue('v1', enumValue('RingVrfProofRequest', {
272
+ callingProductId,
273
+ context,
274
+ ring,
275
+ message,
276
+ }));
277
+ emitHostAction(messageId, actionKindFromMessageData(data), userSession.id);
278
+ const responseFilter = (incoming) => {
279
+ if (incoming.data.tag === 'v1' &&
280
+ incoming.data.value.tag === 'RingVrfProofResponse' &&
281
+ incoming.data.value.value.respondingTo === messageId) {
282
+ return incoming.data.value.value.payload;
283
+ }
284
+ };
285
+ const request = session.request(RemoteMessageCodec, { messageId, data });
286
+ const reply = session.waitForRequestMessage(RemoteMessageCodec, responseFilter);
287
+ return withHostActionTrace(awaitReplyOrAckFailure(request, reply).andThen(result => result.success ? ok(result.value) : err(result.value)), messageId, userSession.id);
288
+ });
289
+ },
290
+ signVrf(payload) {
291
+ return enqueue(() => {
292
+ const messageId = nanoid();
293
+ const data = enumValue('v1', enumValue('SignVrfRequest', payload));
294
+ emitHostAction(messageId, actionKindFromMessageData(data), userSession.id);
295
+ const responseFilter = (message) => {
296
+ if (message.data.tag === 'v1' &&
297
+ message.data.value.tag === 'SignVrfResponse' &&
298
+ message.data.value.value.respondingTo === messageId) {
299
+ return message.data.value.value.payload;
300
+ }
301
+ };
302
+ const request = session.request(RemoteMessageCodec, { messageId, data });
303
+ const reply = session.waitForRequestMessage(RemoteMessageCodec, responseFilter);
304
+ const inner = awaitReplyOrAckFailure(request, reply).andThen(result => result.success ? ok(result.value) : err(result.value));
305
+ return withHostActionTrace(withQueueTimeout(inner, 'signVrf'), messageId, userSession.id);
258
306
  });
259
307
  },
260
308
  requestResourceAllocation(payload) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@novasamatech/host-papp",
3
3
  "type": "module",
4
- "version": "0.8.10",
4
+ "version": "0.8.12",
5
5
  "description": "Polkadot app integration",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
@@ -34,14 +34,14 @@
34
34
  "@noble/ciphers": "2.2.0",
35
35
  "@noble/curves": "2.2.0",
36
36
  "@noble/hashes": "2.2.0",
37
- "@novasamatech/host-api": "0.8.10",
38
- "@novasamatech/scale": "0.8.10",
39
- "@novasamatech/statement-store": "0.8.10",
40
- "@novasamatech/storage-adapter": "0.8.10",
37
+ "@novasamatech/host-api": "0.8.12",
38
+ "@novasamatech/scale": "0.8.12",
39
+ "@novasamatech/statement-store": "0.8.12",
40
+ "@novasamatech/storage-adapter": "0.8.12",
41
41
  "@polkadot-api/utils": "^0.4.0",
42
- "@polkadot-labs/hdkd-helpers": "^0.0.30",
43
- "nanoevents": "9.1.0",
44
- "nanoid": "5.1.11",
42
+ "@polkadot-labs/hdkd-helpers": "^0.0.31",
43
+ "nanoevents": "10.0.0",
44
+ "nanoid": "6.0.0",
45
45
  "neverthrow": "^8.2.0",
46
46
  "polkadot-api": ">=2",
47
47
  "rxjs": "^7.8.2",