@novasamatech/host-api-wrapper 0.10.0 → 0.10.1

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.
@@ -1,6 +1,6 @@
1
1
  import type { AccountConnectionStatus as AccountConnectionStatusCodec, AccountSelector, CodecType, LegacyAccount as LegacyAccountCodec, ProductAccountId as ProductAccountIdCodec, RegisteredRingVrfKey as RegisteredRingVrfKeyCodec, RingVrfKeyDisclosure as RingVrfKeyDisclosureCodec, RingVrfKeyHandle as RingVrfKeyHandleCodec, Subscription, Transport, VrfTranscriptItem as VrfTranscriptItemCodec } from '@novasamatech/host-api';
2
2
  import { RingLocation } from '@novasamatech/host-api';
3
- import type { PolkadotSigner } from 'polkadot-api';
3
+ import type { SignerTxCreator } from 'polkadot-api/tx-creator';
4
4
  export type { AccountSelector } from '@novasamatech/host-api';
5
5
  export type ProductAccountId = CodecType<typeof ProductAccountIdCodec>;
6
6
  export type ProductAccount = {
@@ -182,14 +182,16 @@ export declare const createAccountsProvider: (transport?: Transport) => {
182
182
  reason: string;
183
183
  }, "SignVrfErr::Unknown">>;
184
184
  /**
185
- * Builds a `PolkadotSigner` that delegates to the host via `host_create_transaction`.
185
+ * Builds a `TxCreator` (polkadot-api v3) that delegates to the host via
186
+ * `host_create_transaction`. The host keeps ownership of extension inference
187
+ * and signing; the creator maps the v3 `TxPayloadV1` onto the host codec.
186
188
  *
187
- * The factory is async because `PolkadotSigner.publicKey` must be a synchronous
188
- * `Uint8Array` on the returned object — it is fetched up front via `host_account_get`.
189
+ * `publicKey` is exposed synchronously on the returned object and is taken
190
+ * from `account`, so no up-front `host_account_get` is needed.
189
191
  */
190
- getProductAccountSigner(account: ProductAccount, signerType?: "signPayload" | "createTransaction"): PolkadotSigner;
192
+ getProductAccountSigner(account: ProductAccount, signerType?: "signPayload" | "createTransaction"): SignerTxCreator;
191
193
  subscribeAccountConnectionStatus(callback: (status: AccountConnectionStatus) => void): Subscription<void>;
192
- getLegacyAccountSigner(account: LegacyAccount): PolkadotSigner;
194
+ getLegacyAccountSigner(account: LegacyAccount): SignerTxCreator;
193
195
  };
194
196
  export declare const accounts: {
195
197
  getUserId(): import("neverthrow").ResultAsync<{
@@ -328,12 +330,14 @@ export declare const accounts: {
328
330
  reason: string;
329
331
  }, "SignVrfErr::Unknown">>;
330
332
  /**
331
- * Builds a `PolkadotSigner` that delegates to the host via `host_create_transaction`.
333
+ * Builds a `TxCreator` (polkadot-api v3) that delegates to the host via
334
+ * `host_create_transaction`. The host keeps ownership of extension inference
335
+ * and signing; the creator maps the v3 `TxPayloadV1` onto the host codec.
332
336
  *
333
- * The factory is async because `PolkadotSigner.publicKey` must be a synchronous
334
- * `Uint8Array` on the returned object — it is fetched up front via `host_account_get`.
337
+ * `publicKey` is exposed synchronously on the returned object and is taken
338
+ * from `account`, so no up-front `host_account_get` is needed.
335
339
  */
336
- getProductAccountSigner(account: ProductAccount, signerType?: "signPayload" | "createTransaction"): PolkadotSigner;
340
+ getProductAccountSigner(account: ProductAccount, signerType?: "signPayload" | "createTransaction"): SignerTxCreator;
337
341
  subscribeAccountConnectionStatus(callback: (status: AccountConnectionStatus) => void): Subscription<void>;
338
- getLegacyAccountSigner(account: LegacyAccount): PolkadotSigner;
342
+ getLegacyAccountSigner(account: LegacyAccount): SignerTxCreator;
339
343
  };
package/dist/accounts.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { CreateProofErr, GetAliasErr, GetUserIdErr, ListRingVrfKeysErr, LoginErr, ProductProofContext, RegisterRingVrfKeyErr, RequestCredentialsErr, RingLocation, RingVrfSignErr, SignVrfErr, SigningPayload, SigningPayloadWithoutAccount, SigningRawPayload, SigningRawPayloadWithoutAccount, assertEnumVariant, createHostApi, derivationIndexOf, enumValue, fromHex, isEnumVariant, toHex, } from '@novasamatech/host-api';
2
+ import { createV4Tx } from '@polkadot-api/signers-common';
2
3
  import { decAnyMetadata, unifyMetadata } from '@polkadot-api/substrate-bindings';
3
4
  import { err, ok } from 'neverthrow';
4
5
  import { AccountId } from 'polkadot-api';
5
- import { getPolkadotSignerFromPjs } from 'polkadot-api/pjs-signer';
6
+ import { getTxCreatorFromPjs } from 'polkadot-api/pjs-signer';
6
7
  import { sandboxTransport } from './sandboxTransport.js';
7
8
  /**
8
9
  * Builds a {@link RingVrfKeyHandle} for a key inside `owner`'s ring VRF domain.
@@ -206,10 +207,12 @@ export const createAccountsProvider = (transport = sandboxTransport) => {
206
207
  });
207
208
  },
208
209
  /**
209
- * Builds a `PolkadotSigner` that delegates to the host via `host_create_transaction`.
210
+ * Builds a `TxCreator` (polkadot-api v3) that delegates to the host via
211
+ * `host_create_transaction`. The host keeps ownership of extension inference
212
+ * and signing; the creator maps the v3 `TxPayloadV1` onto the host codec.
210
213
  *
211
- * The factory is async because `PolkadotSigner.publicKey` must be a synchronous
212
- * `Uint8Array` on the returned object — it is fetched up front via `host_account_get`.
214
+ * `publicKey` is exposed synchronously on the returned object and is taken
215
+ * from `account`, so no up-front `host_account_get` is needed.
213
216
  */
214
217
  getProductAccountSigner(account, signerType = 'createTransaction') {
215
218
  const hostApi = createHostApi(transport);
@@ -218,7 +221,7 @@ export const createAccountsProvider = (transport = sandboxTransport) => {
218
221
  * @deprecated added for backward compatibility
219
222
  */
220
223
  if (signerType === 'signPayload') {
221
- return getPolkadotSignerFromPjs(toHex(account.publicKey), async (payload) => {
224
+ return getTxCreatorFromPjs(toHex(account.publicKey), async (payload) => {
222
225
  const codecPayload = {
223
226
  account: productAccountId,
224
227
  payload: buildSigningPayloadFields(payload),
@@ -262,51 +265,66 @@ export const createAccountsProvider = (transport = sandboxTransport) => {
262
265
  });
263
266
  });
264
267
  }
265
- return {
266
- publicKey: account.publicKey,
267
- async signTx(callData, signedExtensions, metadata) {
268
- const decMeta = unifyMetadata(decAnyMetadata(metadata));
269
- const { version: versions } = decMeta.extrinsic;
270
- const latestVersion = versions.reduce((acc, v) => Math.max(acc, v), 0);
271
- const txExtVersion = latestVersion === 4 ? 0 : latestVersion;
272
- const checkGenesis = signedExtensions['CheckGenesis'];
273
- if (!checkGenesis) {
274
- throw new Error("Can't find genesis hash on transaction");
268
+ // v3 `TxCreator`: a callable that turns a `TxPayloadV1` into a
269
+ // SCALE-encoded extrinsic (hex). A real signature is produced by the host
270
+ // via `host_create_transaction`; a mocked signature (fee estimation) is
271
+ // built locally with a zero-filled signature and no host round-trip,
272
+ // mirroring polkadot-api's own `getTxCreator`.
273
+ const createTx = async (payload, _opts, _bindings, mockedSignature) => {
274
+ // Fee estimation on a v4 extrinsic: build a zero-signed extrinsic
275
+ // locally, with no host round-trip and no real signature. polkadot-api
276
+ // has no local builder for other versions (e.g. v5), so those fall
277
+ // through to the host path below, which returns a well-formed extrinsic
278
+ // of the right size for estimation.
279
+ if (mockedSignature && (payload.txExtVersion ?? 0) === 0) {
280
+ const decMeta = unifyMetadata(decAnyMetadata(payload.context.metadata));
281
+ const v4Extensions = decMeta.extrinsic.extensionsByVersion[0];
282
+ if (v4Extensions) {
283
+ const extra = v4Extensions.map(({ identifier }) => {
284
+ const extension = payload.extensions.find(({ id }) => id === identifier);
285
+ if (!extension) {
286
+ throw new Error(`Missing ${identifier} signed extension`);
287
+ }
288
+ return fromHex(extension.extra);
289
+ });
290
+ const mockSr25519Signature = new Uint8Array(64);
291
+ return toHex(createV4Tx(decMeta, account.publicKey, mockSr25519Signature, extra, fromHex(payload.callData), 'Sr25519'));
275
292
  }
276
- const txPayload = {
277
- signer: productAccountId,
278
- genesisHash: toHex(checkGenesis.additionalSigned),
279
- callData,
280
- extensions: Object.values(signedExtensions).map(({ identifier, value, additionalSigned }) => ({
281
- id: identifier,
282
- extra: value,
283
- additionalSigned: additionalSigned,
284
- })),
285
- txExtVersion,
286
- };
287
- const response = await hostApi.createTransaction(enumValue('v1', txPayload));
288
- return response.match(response => {
289
- assertEnumVariant(response, 'v1', UNSUPPORTED_VERSION_ERROR);
290
- return response.value;
291
- }, err => {
292
- assertEnumVariant(err, 'v1', UNSUPPORTED_VERSION_ERROR);
293
- throw err.value;
294
- });
295
- },
296
- async signBytes(data) {
297
- const response = await hostApi.signRaw(enumValue('v1', {
298
- account: productAccountId,
299
- payload: { tag: 'Bytes', value: data },
300
- }));
301
- return response.match(response => {
302
- assertEnumVariant(response, 'v1', UNSUPPORTED_VERSION_ERROR);
303
- return fromHex(response.value.signature);
304
- }, err => {
305
- assertEnumVariant(err, 'v1', UNSUPPORTED_VERSION_ERROR);
306
- throw err.value;
307
- });
308
- },
293
+ }
294
+ const txPayload = {
295
+ signer: productAccountId,
296
+ genesisHash: payload.context.genesisHash,
297
+ callData: fromHex(payload.callData),
298
+ extensions: payload.extensions.map(({ id, extra, additionalSigned }) => ({
299
+ id,
300
+ extra: fromHex(extra),
301
+ additionalSigned: fromHex(additionalSigned),
302
+ })),
303
+ txExtVersion: payload.txExtVersion ?? deriveDefaultTxExtVersion(payload.context.metadata),
304
+ };
305
+ const response = await hostApi.createTransaction(enumValue('v1', txPayload));
306
+ return response.match(response => {
307
+ assertEnumVariant(response, 'v1', UNSUPPORTED_VERSION_ERROR);
308
+ return toHex(response.value);
309
+ }, err => {
310
+ assertEnumVariant(err, 'v1', UNSUPPORTED_VERSION_ERROR);
311
+ throw err.value;
312
+ });
309
313
  };
314
+ const signBytes = async (data) => {
315
+ const response = await hostApi.signRaw(enumValue('v1', {
316
+ account: productAccountId,
317
+ payload: { tag: 'Bytes', value: data },
318
+ }));
319
+ return response.match(response => {
320
+ assertEnumVariant(response, 'v1', UNSUPPORTED_VERSION_ERROR);
321
+ return fromHex(response.value.signature);
322
+ }, err => {
323
+ assertEnumVariant(err, 'v1', UNSUPPORTED_VERSION_ERROR);
324
+ throw err.value;
325
+ });
326
+ };
327
+ return Object.assign(createTx, { publicKey: account.publicKey, signBytes });
310
328
  },
311
329
  subscribeAccountConnectionStatus(callback) {
312
330
  const subscriber = hostApi.accountConnectionStatusSubscribe(enumValue('v1', undefined), status => {
@@ -325,7 +343,7 @@ export const createAccountsProvider = (transport = sandboxTransport) => {
325
343
  // must be an SS58 address the wallet can match — not a raw hex public
326
344
  // key. Mirrors the injected-extension path, which uses accountId.dec.
327
345
  const accountId = AccountId();
328
- return getPolkadotSignerFromPjs(accountId.dec(account.publicKey), async (payload) => {
346
+ return getTxCreatorFromPjs(accountId.dec(account.publicKey), async (payload) => {
329
347
  const codecPayload = {
330
348
  signer: payload.address,
331
349
  payload: buildSigningPayloadFields(payload),
@@ -372,6 +390,13 @@ function asHex(v) {
372
390
  return v;
373
391
  return `0x${v}`;
374
392
  }
393
+ // Default transaction-extension version to request when the caller leaves it
394
+ // unset: 0 for extrinsic v4, the version itself otherwise (e.g. v5).
395
+ function deriveDefaultTxExtVersion(metadata) {
396
+ const { version: versions } = unifyMetadata(decAnyMetadata(metadata)).extrinsic;
397
+ const latestVersion = versions.reduce((acc, v) => Math.max(acc, v), 0);
398
+ return latestVersion === 4 ? 0 : latestVersion;
399
+ }
375
400
  function buildSigningPayloadFields(payload) {
376
401
  return {
377
402
  blockHash: asHex(payload.blockHash),
package/dist/index.d.ts CHANGED
@@ -10,10 +10,14 @@ export type { ProductAccountId, ProductAccountRef, SignedStatement, Statement, S
10
10
  export { createStatementStore } from './statementStore.js';
11
11
  export type { AccountConnectionStatus, AccountSelector, LegacyAccount, ProductAccount, ProofContext, RegisteredRingVrfKey, RingVrfKeyDisclosure, RingVrfKeyHandle, VrfTranscriptItem, } from './accounts.js';
12
12
  export { accounts, createAccountsProvider, ringVrfKeyHandle } from './accounts.js';
13
+ export type { Locale } from './locale.js';
14
+ export { createLocaleProvider } from './locale.js';
13
15
  export type { ThemeMode } from './theme.js';
14
16
  export { createThemeProvider } from './theme.js';
15
17
  export { createLocalStorage, hostLocalStorage } from './localStorage.js';
16
18
  export { createWorker, hostWorker } from './worker.js';
19
+ export type { HostInfo, HostPlatform, ProductContext } from './system.js';
20
+ export { createSystem, hostSystem } from './system.js';
17
21
  export type { Cheque, ClearingStatus, PaymentDelivery, PurseInfo, Receivable } from './coinPayment.js';
18
22
  export { createCoinPayment, hostCoinPayment } from './coinPayment.js';
19
23
  export type { NotificationId, PushNotificationInput } from './notification.js';
package/dist/index.js CHANGED
@@ -7,9 +7,11 @@ export { createPapiProvider } from './papiProvider.js';
7
7
  export { createProductChatManager, matchChatCustomRenderers } from './chat.js';
8
8
  export { createStatementStore } from './statementStore.js';
9
9
  export { accounts, createAccountsProvider, ringVrfKeyHandle } from './accounts.js';
10
+ export { createLocaleProvider } from './locale.js';
10
11
  export { createThemeProvider } from './theme.js';
11
12
  export { createLocalStorage, hostLocalStorage } from './localStorage.js';
12
13
  export { createWorker, hostWorker } from './worker.js';
14
+ export { createSystem, hostSystem } from './system.js';
13
15
  export { createCoinPayment, hostCoinPayment } from './coinPayment.js';
14
16
  export { createNotificationManager, notificationManager } from './notification.js';
15
17
  export { createPreimageManager, preimageManager } from './preimage.js';
@@ -0,0 +1,6 @@
1
+ import type { CodecType, Subscription, Transport } from '@novasamatech/host-api';
2
+ import { HostLocale } from '@novasamatech/host-api';
3
+ export type Locale = CodecType<typeof HostLocale>;
4
+ export declare function createLocaleProvider(transport?: Transport): {
5
+ subscribeLocale(callback: (locale: Locale) => void): Subscription<void>;
6
+ };
package/dist/locale.js ADDED
@@ -0,0 +1,18 @@
1
+ import { HostLocale, createHostApi, enumValue } from '@novasamatech/host-api';
2
+ import { sandboxTransport } from './sandboxTransport.js';
3
+ export function createLocaleProvider(transport = sandboxTransport) {
4
+ const hostApi = createHostApi(transport);
5
+ return {
6
+ subscribeLocale(callback) {
7
+ const subscriber = hostApi.localeSubscribe(enumValue('v1', undefined), value => {
8
+ if (value.tag === 'v1') {
9
+ callback(value.value);
10
+ }
11
+ });
12
+ return {
13
+ unsubscribe: subscriber.unsubscribe,
14
+ onInterrupt: cb => subscriber.onInterrupt(v => cb(v.value)),
15
+ };
16
+ },
17
+ };
18
+ }
@@ -0,0 +1,13 @@
1
+ import type { CodecType, Transport } from '@novasamatech/host-api';
2
+ import { HostInfo, HostPlatform, ProductContext } from '@novasamatech/host-api';
3
+ export type HostPlatform = CodecType<typeof HostPlatform>;
4
+ export type HostInfo = CodecType<typeof HostInfo>;
5
+ export type ProductContext = CodecType<typeof ProductContext>;
6
+ export declare const createSystem: (transport?: Transport) => {
7
+ info(): Promise<HostInfo>;
8
+ getProductContext(): Promise<ProductContext>;
9
+ };
10
+ export declare const hostSystem: {
11
+ info(): Promise<HostInfo>;
12
+ getProductContext(): Promise<ProductContext>;
13
+ };
package/dist/system.js ADDED
@@ -0,0 +1,18 @@
1
+ import { HostInfo, HostPlatform, ProductContext, createHostApi, enumValue } from '@novasamatech/host-api';
2
+ import { resultToPromise, unwrapVersionedResult } from './helpers.js';
3
+ import { sandboxTransport } from './sandboxTransport.js';
4
+ export const createSystem = (transport = sandboxTransport) => {
5
+ const supportedVersion = 'v1';
6
+ const hostApi = createHostApi(transport);
7
+ return {
8
+ // Identity and version of the host currently running the product.
9
+ async info() {
10
+ return resultToPromise(unwrapVersionedResult(supportedVersion, hostApi.info(enumValue(supportedVersion, undefined))));
11
+ },
12
+ // Product context bound to the current host runtime.
13
+ async getProductContext() {
14
+ return resultToPromise(unwrapVersionedResult(supportedVersion, hostApi.getProductContext(enumValue(supportedVersion, undefined))));
15
+ },
16
+ };
17
+ };
18
+ export const hostSystem = createSystem();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@novasamatech/host-api-wrapper",
3
3
  "type": "module",
4
- "version": "0.10.0",
4
+ "version": "0.10.1",
5
5
  "description": "Host API wrapper: integrate and run your product inside Polkadot browser.",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
@@ -27,9 +27,10 @@
27
27
  "dependencies": {
28
28
  "@polkadot/extension-inject": "^0.63.1",
29
29
  "@polkadot-api/json-rpc-provider-proxy": "^0.4.0",
30
- "@polkadot-api/substrate-bindings": "^0.20.3",
31
- "@novasamatech/host-api": "0.10.0",
32
- "polkadot-api": ">=2",
30
+ "@polkadot-api/signers-common": "^0.3.0",
31
+ "@polkadot-api/substrate-bindings": "^0.21.0",
32
+ "@novasamatech/host-api": "0.10.1",
33
+ "polkadot-api": ">=3",
33
34
  "neverthrow": "^8.2.0"
34
35
  },
35
36
  "publishConfig": {