@novasamatech/product-sdk 0.5.3-0 → 0.5.4-0

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
@@ -6,8 +6,9 @@ An easy way to embed Polkadot host functionality into your dapp.
6
6
 
7
7
  Product SDK provides a set of tools to integrate your application with any Polkadot host application.
8
8
  Core features:
9
- - Generic account provider similar to [polkadot-js extension](https://polkadot.js.org/extension/)
9
+ - Generic injectWeb3 provider similar to [polkadot-js extension](https://polkadot.js.org/extension/)
10
10
  - Chat module integration
11
+ - Statement store integration
11
12
  - Redirect [PAPI](https://papi.how/) requests to host application
12
13
  - Receive additional information from host application - supported chains, theme, etc.
13
14
 
@@ -114,3 +115,51 @@ const subscriber = chat.subscribeAction((action) => {
114
115
 
115
116
  **Note:** Messages sent before registration will be queued and sent automatically after successful registration.
116
117
 
118
+ ### Statement Store
119
+
120
+ The Statement Store provides a decentralized way to store statements (messages).
121
+ It can be used for various purposes like p2p communication, storing temp data, etc.
122
+
123
+ ```ts
124
+ import { createStatementStore } from '@novasamatech/product-sdk';
125
+ import type { Topic, Statement, SignedStatement } from '@novasamatech/product-sdk';
126
+
127
+ // Create statement store instance
128
+ const statementStore = createStatementStore();
129
+
130
+ // Define topics (32-byte identifiers) to categorize statements
131
+ const topic: Topic = new Uint8Array(32);
132
+
133
+ // Query existing statements by topics
134
+ const statements: SignedStatement[] = await statementStore.query([topic]);
135
+
136
+ // Subscribe to statement updates for specific topics
137
+ const subscription = statementStore.subscribe([topic], (statements) => {
138
+ console.log('Received statement updates:', statements);
139
+ });
140
+
141
+ // Create a proof for a new statement
142
+ const accountId = ['product.dot', 0]; // [DotNS identifier, derivation index]
143
+ const statement: Statement = {
144
+ proof: undefined,
145
+ decryptionKey: undefined,
146
+ priority: undefined,
147
+ channel: undefined,
148
+ topics: [topic],
149
+ data: new Uint8Array([/* your data */]),
150
+ };
151
+
152
+ const proof = await statementStore.createProof(accountId, statement);
153
+
154
+ // Submit a signed statement
155
+ const signedStatement: SignedStatement = {
156
+ ...statement,
157
+ proof,
158
+ };
159
+
160
+ await statementStore.submit(signedStatement);
161
+
162
+ // Unsubscribe when done
163
+ subscription.unsubscribe();
164
+ ```
165
+
@@ -0,0 +1,41 @@
1
+ import type { CodecType, Transport } from '@novasamatech/host-api';
2
+ import { RingLocation } from '@novasamatech/host-api';
3
+ import type { PolkadotSigner } from 'polkadot-api';
4
+ export type ProductAccount = {
5
+ dotNsIdentifier: string;
6
+ derivationIndex: number;
7
+ publicKey: Uint8Array;
8
+ };
9
+ export declare const createAccountsProvider: (transport?: Transport) => {
10
+ getProductAccount(dotNsIdentifier: string, derivationIndex?: number): import("neverthrow").ResultAsync<{
11
+ publicKey: Uint8Array<ArrayBufferLike>;
12
+ name: string | undefined;
13
+ }, import("packages/scale/dist/err.js").CodecError<undefined, "RequestCredentialsErr::NotConnected"> | import("packages/scale/dist/err.js").CodecError<undefined, "RequestCredentialsErr::Rejected"> | import("packages/scale/dist/err.js").CodecError<undefined, "RequestCredentialsErr::DomainNotValid"> | import("packages/scale/dist/err.js").CodecError<{
14
+ reason: string;
15
+ }, "RequestCredentialsErr::Unknown"> | import("packages/scale/dist/err.js").CodecError<{
16
+ reason: string;
17
+ }, "RequestCredentialsErr::Unknown">>;
18
+ getProductAccountAlias(dotNsIdentifier: string, derivationIndex?: number): import("neverthrow").ResultAsync<{
19
+ context: Uint8Array<ArrayBufferLike>;
20
+ alias: Uint8Array<ArrayBufferLike>;
21
+ }, import("packages/scale/dist/err.js").CodecError<undefined, "RequestCredentialsErr::NotConnected"> | import("packages/scale/dist/err.js").CodecError<undefined, "RequestCredentialsErr::Rejected"> | import("packages/scale/dist/err.js").CodecError<undefined, "RequestCredentialsErr::DomainNotValid"> | import("packages/scale/dist/err.js").CodecError<{
22
+ reason: string;
23
+ }, "RequestCredentialsErr::Unknown"> | import("packages/scale/dist/err.js").CodecError<{
24
+ reason: string;
25
+ }, "RequestCredentialsErr::Unknown">>;
26
+ getNonProductAccounts(): import("neverthrow").ResultAsync<{
27
+ publicKey: Uint8Array<ArrayBufferLike>;
28
+ name: string | undefined;
29
+ }[], import("packages/scale/dist/err.js").CodecError<undefined, "RequestCredentialsErr::NotConnected"> | import("packages/scale/dist/err.js").CodecError<undefined, "RequestCredentialsErr::Rejected"> | import("packages/scale/dist/err.js").CodecError<undefined, "RequestCredentialsErr::DomainNotValid"> | import("packages/scale/dist/err.js").CodecError<{
30
+ reason: string;
31
+ }, "RequestCredentialsErr::Unknown"> | import("packages/scale/dist/err.js").CodecError<{
32
+ reason: string;
33
+ }, "RequestCredentialsErr::Unknown">>;
34
+ createRingVRFProof(dotNsIdentifier: string, derivationIndex: number | undefined, location: CodecType<typeof RingLocation>, message: Uint8Array): import("neverthrow").ResultAsync<Uint8Array<ArrayBufferLike>, import("packages/scale/dist/err.js").CodecError<undefined, "CreateProofErr::Rejected"> | import("packages/scale/dist/err.js").CodecError<{
35
+ reason: string;
36
+ }, "CreateProofErr::Unknown"> | import("packages/scale/dist/err.js").CodecError<undefined, "CreateProofErr::RingNotFound"> | import("packages/scale/dist/err.js").CodecError<{
37
+ reason: string;
38
+ }, "CreateProofErr::Unknown">>;
39
+ getProductAccountSigner(account: ProductAccount): PolkadotSigner;
40
+ getNonProductAccountSigner(account: ProductAccount): PolkadotSigner;
41
+ };
@@ -0,0 +1,172 @@
1
+ import { CreateProofErr, RequestCredentialsErr, RingLocation, assertEnumVariant, createHostApi, enumValue, fromHex, isEnumVariant, toHex, } from '@novasamatech/host-api';
2
+ import { getPolkadotSignerFromPjs } from '@polkadot-api/pjs-signer';
3
+ import { err, ok } from 'neverthrow';
4
+ import { sandboxTransport } from './sandboxTransport.js';
5
+ const UNSUPPORTED_VERSION_ERROR = 'Unsupported message version';
6
+ export const createAccountsProvider = (transport = sandboxTransport) => {
7
+ const hostApi = createHostApi(transport);
8
+ return {
9
+ getProductAccount(dotNsIdentifier, derivationIndex = 0) {
10
+ return hostApi
11
+ .accountGet(enumValue('v1', [dotNsIdentifier, derivationIndex]))
12
+ .mapErr(e => e.value)
13
+ .andThen(response => {
14
+ if (isEnumVariant(response, 'v1')) {
15
+ return ok(response.value);
16
+ }
17
+ // @ts-expect-error response.tag is never here
18
+ return err(new RequestCredentialsErr.Unknown({ reason: `Unsupported response version ${response.tag}` }));
19
+ });
20
+ },
21
+ getProductAccountAlias(dotNsIdentifier, derivationIndex = 0) {
22
+ return hostApi
23
+ .accountGetAlias(enumValue('v1', [dotNsIdentifier, derivationIndex]))
24
+ .mapErr(e => e.value)
25
+ .andThen(response => {
26
+ if (isEnumVariant(response, 'v1')) {
27
+ return ok(response.value);
28
+ }
29
+ // @ts-expect-error response.tag is never here
30
+ return err(new RequestCredentialsErr.Unknown({ reason: `Unsupported response version ${response.tag}` }));
31
+ });
32
+ },
33
+ getNonProductAccounts() {
34
+ return hostApi
35
+ .getNonProductAccounts(enumValue('v1', undefined))
36
+ .mapErr(e => e.value)
37
+ .andThen(response => {
38
+ if (isEnumVariant(response, 'v1')) {
39
+ return ok(response.value);
40
+ }
41
+ // @ts-expect-error response.tag is never here
42
+ return err(new RequestCredentialsErr.Unknown({ reason: `Unsupported response version ${response.tag}` }));
43
+ });
44
+ },
45
+ createRingVRFProof(dotNsIdentifier, derivationIndex = 0, location, message) {
46
+ return hostApi
47
+ .accountCreateProof(enumValue('v1', [[dotNsIdentifier, derivationIndex], location, message]))
48
+ .mapErr(e => e.value)
49
+ .andThen(response => {
50
+ if (isEnumVariant(response, 'v1')) {
51
+ return ok(response.value);
52
+ }
53
+ // @ts-expect-error response.tag is never here
54
+ return err(new CreateProofErr.Unknown({ reason: `Unsupported response version ${response.tag}` }));
55
+ });
56
+ },
57
+ getProductAccountSigner(account) {
58
+ return getPolkadotSignerFromPjs(toHex(account.publicKey), async (payload) => {
59
+ const codecPayload = {
60
+ ...payload,
61
+ blockHash: payload.blockHash,
62
+ blockNumber: payload.blockNumber,
63
+ era: payload.era,
64
+ genesisHash: payload.genesisHash,
65
+ nonce: payload.nonce,
66
+ method: payload.method,
67
+ specVersion: payload.specVersion,
68
+ transactionVersion: payload.transactionVersion,
69
+ metadataHash: payload.metadataHash,
70
+ tip: payload.tip,
71
+ assetId: payload.assetId,
72
+ mode: payload.mode,
73
+ withSignedTransaction: payload.withSignedTransaction,
74
+ };
75
+ const response = await hostApi.signPayload(enumValue('v1', codecPayload));
76
+ return response.match(response => {
77
+ assertEnumVariant(response, 'v1', UNSUPPORTED_VERSION_ERROR);
78
+ return {
79
+ id: 0,
80
+ signature: response.value.signature,
81
+ signedTransaction: response.value.signedTransaction,
82
+ };
83
+ }, err => {
84
+ assertEnumVariant(err, 'v1', UNSUPPORTED_VERSION_ERROR);
85
+ throw err.value;
86
+ });
87
+ }, async (raw) => {
88
+ const payload = {
89
+ address: raw.address,
90
+ data: raw.type === 'bytes'
91
+ ? {
92
+ tag: 'Bytes',
93
+ value: fromHex(raw.data),
94
+ }
95
+ : {
96
+ tag: 'Payload',
97
+ value: raw.data,
98
+ },
99
+ };
100
+ const response = await hostApi.signRaw(enumValue('v1', payload));
101
+ return response.match(response => {
102
+ assertEnumVariant(response, 'v1', UNSUPPORTED_VERSION_ERROR);
103
+ return {
104
+ id: 0,
105
+ signature: response.value.signature,
106
+ signedTransaction: response.value.signedTransaction,
107
+ };
108
+ }, err => {
109
+ assertEnumVariant(err, 'v1', UNSUPPORTED_VERSION_ERROR);
110
+ throw err.value;
111
+ });
112
+ });
113
+ },
114
+ getNonProductAccountSigner(account) {
115
+ return getPolkadotSignerFromPjs(toHex(account.publicKey), async (payload) => {
116
+ const codecPayload = {
117
+ ...payload,
118
+ blockHash: payload.blockHash,
119
+ blockNumber: payload.blockNumber,
120
+ era: payload.era,
121
+ genesisHash: payload.genesisHash,
122
+ nonce: payload.nonce,
123
+ method: payload.method,
124
+ specVersion: payload.specVersion,
125
+ transactionVersion: payload.transactionVersion,
126
+ metadataHash: payload.metadataHash,
127
+ tip: payload.tip,
128
+ assetId: payload.assetId,
129
+ mode: payload.mode,
130
+ withSignedTransaction: payload.withSignedTransaction,
131
+ };
132
+ const response = await hostApi.signPayload(enumValue('v1', codecPayload));
133
+ return response.match(response => {
134
+ assertEnumVariant(response, 'v1', UNSUPPORTED_VERSION_ERROR);
135
+ return {
136
+ id: 0,
137
+ signature: response.value.signature,
138
+ signedTransaction: response.value.signedTransaction,
139
+ };
140
+ }, err => {
141
+ assertEnumVariant(err, 'v1', UNSUPPORTED_VERSION_ERROR);
142
+ throw err.value;
143
+ });
144
+ }, async (raw) => {
145
+ const payload = {
146
+ address: raw.address,
147
+ data: raw.type === 'bytes'
148
+ ? {
149
+ tag: 'Bytes',
150
+ value: fromHex(raw.data),
151
+ }
152
+ : {
153
+ tag: 'Payload',
154
+ value: raw.data,
155
+ },
156
+ };
157
+ const response = await hostApi.signRaw(enumValue('v1', payload));
158
+ return response.match(response => {
159
+ assertEnumVariant(response, 'v1', UNSUPPORTED_VERSION_ERROR);
160
+ return {
161
+ id: 0,
162
+ signature: response.value.signature,
163
+ signedTransaction: response.value.signedTransaction,
164
+ };
165
+ }, err => {
166
+ assertEnumVariant(err, 'v1', UNSUPPORTED_VERSION_ERROR);
167
+ throw err.value;
168
+ });
169
+ });
170
+ },
171
+ };
172
+ };
package/dist/chat.d.ts CHANGED
@@ -1,14 +1,19 @@
1
- import type { ChatContactRegistrationStatus as ChatContactRegistrationStatusCodec, ChatMessage as ChatMessageCodec, CodecType, ReceivedChatAction as ReceivedChatActionCodec, Transport } from '@novasamatech/host-api';
2
- export type ChatMessage = CodecType<typeof ChatMessageCodec>;
1
+ import type { ChatMessageContent as ChatMessageContentCodec, ChatRoom as ChatRoomCodec, ChatRoomRegistrationResult as ChatRoomRegistrationResultCodec, CodecType, ReceivedChatAction as ReceivedChatActionCodec, Transport } from '@novasamatech/host-api';
2
+ export type ChatMessageContent = CodecType<typeof ChatMessageContentCodec>;
3
3
  export type ReceivedChatAction = CodecType<typeof ReceivedChatActionCodec>;
4
- export type ChatContactRegistrationStatus = CodecType<typeof ChatContactRegistrationStatusCodec>;
4
+ export type ChatRoomRegistrationResult = CodecType<typeof ChatRoomRegistrationResultCodec>;
5
+ export type ChatRoom = CodecType<typeof ChatRoomCodec>;
5
6
  export declare const createChat: (transport?: Transport) => {
6
7
  register(params: {
8
+ roomId: string;
7
9
  name: string;
8
10
  icon: string;
9
- }): Promise<"New" | "Exists">;
10
- sendMessage(message: ChatMessage): Promise<{
11
+ }): Promise<{
12
+ status: "New" | "Exists";
13
+ }>;
14
+ sendMessage(roomId: string, payload: ChatMessageContent): Promise<{
11
15
  messageId: string;
12
16
  }>;
13
- subscribeAction(callback: (action: ReceivedChatAction) => void): import("packages/host-api/dist/types.js").Subscription;
17
+ subscribeChatList(callback: (rooms: ChatRoom[]) => void): import("@novasamatech/host-api").Subscription;
18
+ subscribeAction(callback: (action: ReceivedChatAction) => void): import("@novasamatech/host-api").Subscription;
14
19
  };
package/dist/chat.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { createHostApi, enumValue } from '@novasamatech/host-api';
2
- import { defaultTransport } from './defaultTransport.js';
3
2
  import { promiseWithResolvers } from './helpers.js';
3
+ import { sandboxTransport } from './sandboxTransport.js';
4
4
  promiseWithResolvers();
5
- export const createChat = (transport = defaultTransport) => {
5
+ export const createChat = (transport = sandboxTransport) => {
6
6
  const hostApi = createHostApi(transport);
7
7
  let registrationStatus = null;
8
8
  const messageQueue = [];
@@ -11,13 +11,13 @@ export const createChat = (transport = defaultTransport) => {
11
11
  if (registrationStatus) {
12
12
  return registrationStatus;
13
13
  }
14
- const result = await hostApi.chat_create_contact(enumValue('v1', params));
14
+ const result = await hostApi.chatCreateRoom(enumValue('v1', params));
15
15
  return result.match(payload => {
16
16
  if (payload.tag === 'v1') {
17
17
  registrationStatus = payload.value;
18
18
  if (messageQueue.length > 0) {
19
- messageQueue.forEach(({ message, resolve, reject }) => {
20
- chat.sendMessage(message).then(resolve, reject);
19
+ messageQueue.forEach(({ roomId, content, resolve, reject }) => {
20
+ chat.sendMessage(roomId, content).then(resolve, reject);
21
21
  });
22
22
  messageQueue.length = 0;
23
23
  }
@@ -30,9 +30,9 @@ export const createChat = (transport = defaultTransport) => {
30
30
  throw err.value;
31
31
  });
32
32
  },
33
- async sendMessage(message) {
33
+ async sendMessage(roomId, payload) {
34
34
  if (registrationStatus) {
35
- const result = await hostApi.chat_post_message(enumValue('v1', message));
35
+ const result = await hostApi.chatPostMessage(enumValue('v1', { roomId, payload }));
36
36
  return result.match(payload => {
37
37
  if (payload.tag === 'v1') {
38
38
  return { messageId: payload.value.messageId };
@@ -46,12 +46,19 @@ export const createChat = (transport = defaultTransport) => {
46
46
  }
47
47
  else {
48
48
  const { promise, resolve, reject } = promiseWithResolvers();
49
- messageQueue.push({ message, resolve, reject });
49
+ messageQueue.push({ roomId, content: payload, resolve, reject });
50
50
  return promise;
51
51
  }
52
52
  },
53
+ subscribeChatList(callback) {
54
+ return hostApi.chatListSubscribe(enumValue('v1', undefined), action => {
55
+ if (action.tag === 'v1') {
56
+ callback(action.value);
57
+ }
58
+ });
59
+ },
53
60
  subscribeAction(callback) {
54
- return hostApi.chat_action_subscribe(enumValue('v1', undefined), action => {
61
+ return hostApi.chatActionSubscribe(enumValue('v1', undefined), action => {
55
62
  if (action.tag === 'v1') {
56
63
  callback(action.value);
57
64
  }
@@ -1,11 +1,7 @@
1
1
  import type { HexString, Transport } from '@novasamatech/host-api';
2
2
  import type { JsonRpcProvider } from '@polkadot-api/json-rpc-provider';
3
- type Params = {
4
- chainId: HexString;
5
- fallback: JsonRpcProvider;
6
- };
7
3
  type InternalParams = {
8
4
  transport?: Transport;
9
5
  };
10
- export declare function createPapiProvider({ chainId: genesisHash, fallback }: Params, internal?: InternalParams): JsonRpcProvider;
6
+ export declare function createPapiProvider(genesisHash: HexString, internal?: InternalParams): JsonRpcProvider;
11
7
  export {};
@@ -1,14 +1,15 @@
1
1
  import { createHostApi, enumValue, unwrapResultOrThrow } from '@novasamatech/host-api';
2
2
  import { getSyncProvider } from '@polkadot-api/json-rpc-provider-proxy';
3
3
  import { defaultTransport } from './defaultTransport.js';
4
- export function createPapiProvider({ chainId: genesisHash, fallback }, internal) {
4
+ export function createPapiProvider(genesisHash, internal) {
5
5
  const version = 'v1';
6
6
  const transport = internal?.transport ?? defaultTransport;
7
- if (!transport.isCorrectEnvironment())
8
- return fallback;
7
+ if (!transport.isCorrectEnvironment()) {
8
+ throw new Error('PapiProvider can only be used in a product environment');
9
+ }
9
10
  const hostApi = createHostApi(transport);
10
11
  const spektrProvider = onMessage => {
11
- const subscription = hostApi.jsonrpc_message_subscribe(enumValue(version, genesisHash), payload => {
12
+ const subscription = hostApi.jsonrpcMessageSubscribe(enumValue(version, genesisHash), payload => {
12
13
  switch (payload.tag) {
13
14
  case version:
14
15
  onMessage(payload.value);
@@ -19,7 +20,7 @@ export function createPapiProvider({ chainId: genesisHash, fallback }, internal)
19
20
  });
20
21
  return {
21
22
  send(message) {
22
- hostApi.jsonrpc_message_send(enumValue(version, [genesisHash, message]));
23
+ hostApi.jsonrpcMessageSend(enumValue(version, [genesisHash, message]));
23
24
  },
24
25
  disconnect() {
25
26
  subscription.unsubscribe();
@@ -47,5 +48,9 @@ export function createPapiProvider({ chainId: genesisHash, fallback }, internal)
47
48
  });
48
49
  });
49
50
  }
50
- return getSyncProvider(() => checkIfReady().then(ready => (ready ? spektrProvider : fallback)));
51
+ return getSyncProvider(() => checkIfReady().then(ready => {
52
+ if (ready)
53
+ return spektrProvider;
54
+ throw new Error(`Chain ${genesisHash} not supported by host`);
55
+ }));
51
56
  }