@novasamatech/host-papp 0.5.0-0 → 0.5.0-10
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/.papi/descriptors/dist/common-types.d.ts +8667 -0
- package/.papi/descriptors/dist/common.d.ts +1 -0
- package/.papi/descriptors/dist/descriptors-UUEW32EL.mjs +27 -0
- package/.papi/descriptors/dist/descriptors.d.ts +1 -0
- package/.papi/descriptors/dist/index.d.ts +10 -0
- package/.papi/descriptors/dist/index.js +237 -0
- package/.papi/descriptors/dist/index.mjs +148 -0
- package/.papi/descriptors/dist/metadataTypes-E4AQJDJR.mjs +6 -0
- package/.papi/descriptors/dist/metadataTypes.d.ts +2 -0
- package/.papi/descriptors/dist/people_lite.d.ts +7757 -0
- package/.papi/descriptors/dist/people_lite_metadata-EIVHV27X.mjs +6 -0
- package/.papi/descriptors/dist/people_lite_metadata.d.ts +2 -0
- package/.papi/descriptors/package.json +24 -0
- package/.papi/metadata/people_lite.scale +0 -0
- package/.papi/polkadot-api.json +15 -0
- package/dist/adapters/identity/rpc.d.ts +6 -4
- package/dist/adapters/identity/rpc.js +96 -26
- package/dist/adapters/identity/types.d.ts +3 -1
- package/dist/adapters/lazyClient/papi.js +5 -0
- package/dist/adapters/lazyClient/types.d.ts +1 -0
- package/dist/adapters/statement/rpc.js +58 -10
- package/dist/adapters/statement/types.d.ts +6 -3
- package/dist/adapters/storage/localStorage.js +26 -4
- package/dist/adapters/storage/memory.js +14 -4
- package/dist/adapters/storage/types.d.ts +5 -2
- package/dist/adapters/storage/types.js +1 -1
- package/dist/components/auth/codec.d.ts +9 -0
- package/dist/components/auth/codec.js +10 -0
- package/dist/components/auth/codecs.d.ts +9 -0
- package/dist/components/auth/codecs.js +10 -0
- package/dist/components/auth/index.d.ts +36 -0
- package/dist/components/auth/index.js +150 -0
- package/dist/components/auth/types.d.ts +15 -0
- package/dist/components/auth/types.js +1 -0
- package/dist/components/session.d.ts +34 -0
- package/dist/components/session.js +54 -0
- package/dist/components/sso/index.d.ts +36 -0
- package/dist/components/sso/index.js +150 -0
- package/dist/components/sso/scale/handshake.d.ts +9 -0
- package/dist/components/sso/scale/handshake.js +10 -0
- package/dist/components/sso/types.d.ts +15 -0
- package/dist/components/sso/types.js +1 -0
- package/dist/components/transport.d.ts +27 -0
- package/dist/components/transport.js +57 -0
- package/dist/components/user/codec.d.ts +16 -0
- package/dist/components/user/codec.js +13 -0
- package/dist/components/user/index.d.ts +22 -0
- package/dist/components/user/index.js +58 -0
- package/dist/components/user/ssoMessageStream.d.ts +10 -0
- package/dist/components/user/ssoMessageStream.js +8 -0
- package/dist/components/user/ssoSession.d.ts +5 -0
- package/dist/components/user/ssoSession.js +5 -0
- package/dist/components/user/storage.d.ts +27 -0
- package/dist/components/user/storage.js +143 -0
- package/dist/components/user/types.d.ts +6 -0
- package/dist/components/user/types.js +1 -0
- package/dist/components/user/userSessionStorage.d.ts +20 -0
- package/dist/components/user/userSessionStorage.js +24 -0
- package/dist/components/user.d.ts +74 -0
- package/dist/components/user.js +188 -0
- package/dist/constants.d.ts +2 -1
- package/dist/constants.js +5 -1
- package/dist/crypto.d.ts +29 -0
- package/dist/crypto.js +86 -0
- package/dist/helpers/abortError.d.ts +4 -0
- package/dist/helpers/abortError.js +8 -0
- package/dist/helpers/callbackRaceResolver.d.ts +1 -0
- package/dist/helpers/callbackRaceResolver.js +17 -0
- package/dist/helpers/result.d.ts +12 -0
- package/dist/helpers/result.js +15 -0
- package/dist/helpers/result.spec.d.ts +1 -0
- package/dist/helpers/result.spec.js +23 -0
- package/dist/helpers/state.d.ts +16 -0
- package/dist/helpers/state.js +51 -0
- package/dist/helpers/utils.d.ts +2 -1
- package/dist/helpers/utils.js +11 -2
- package/dist/helpers/zipWith.d.ts +4 -0
- package/dist/helpers/zipWith.js +11 -0
- package/dist/identity/impl.d.ts +6 -0
- package/dist/identity/impl.js +68 -0
- package/dist/identity/rpcAdapter.d.ts +3 -0
- package/dist/identity/rpcAdapter.js +46 -0
- package/dist/identity/types.d.ts +21 -0
- package/dist/identity/types.js +1 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.js +2 -7
- package/dist/modules/crypto.d.ts +8 -9
- package/dist/modules/crypto.js +20 -42
- package/dist/modules/secretStorage.d.ts +13 -12
- package/dist/modules/secretStorage.js +34 -43
- package/dist/modules/session/helpers.d.ts +5 -0
- package/dist/modules/session/helpers.js +29 -0
- package/dist/modules/session/session.d.ts +12 -0
- package/dist/modules/session/session.js +50 -0
- package/dist/modules/session/types.d.ts +12 -0
- package/dist/modules/session/types.js +1 -0
- package/dist/modules/signIn.d.ts +32 -11
- package/dist/modules/signIn.js +98 -101
- package/dist/modules/state.d.ts +16 -0
- package/dist/modules/state.js +50 -0
- package/dist/modules/statementStore.d.ts +10 -11
- package/dist/modules/statementStore.js +16 -14
- package/dist/modules/statementTopic.d.ts +34 -0
- package/dist/modules/statementTopic.js +46 -0
- package/dist/modules/storageView.d.ts +25 -0
- package/dist/modules/storageView.js +51 -0
- package/dist/modules/syncStorage.d.ts +25 -0
- package/dist/modules/syncStorage.js +76 -0
- package/dist/modules/transport/codec.d.ts +24 -0
- package/dist/modules/transport/codec.js +36 -0
- package/dist/modules/transport/crypto.d.ts +2 -0
- package/dist/modules/transport/crypto.js +20 -0
- package/dist/modules/transport/transport.d.ts +42 -0
- package/dist/modules/transport/transport.js +66 -0
- package/dist/modules/user.d.ts +67 -0
- package/dist/modules/user.js +188 -0
- package/dist/modules/userManager.d.ts +15 -0
- package/dist/modules/userManager.js +105 -0
- package/dist/modules/userStorage.d.ts +19 -0
- package/dist/modules/userStorage.js +108 -0
- package/dist/modules/userStore.d.ts +15 -0
- package/dist/modules/userStore.js +105 -0
- package/dist/papp.d.ts +25 -13
- package/dist/papp.js +19 -50
- package/dist/sso/auth/attestationService.d.ts +18 -0
- package/dist/sso/auth/attestationService.js +171 -0
- package/dist/sso/auth/impl.d.ts +53 -0
- package/dist/sso/auth/impl.js +161 -0
- package/dist/sso/auth/scale/handshake.d.ts +9 -0
- package/dist/sso/auth/scale/handshake.js +10 -0
- package/dist/sso/auth/types.d.ts +17 -0
- package/dist/sso/auth/types.js +1 -0
- package/dist/sso/session/impl.d.ts +23 -0
- package/dist/sso/session/impl.js +57 -0
- package/dist/sso/session/scale/remoteMessage.d.ts +10 -0
- package/dist/sso/session/scale/remoteMessage.js +13 -0
- package/dist/sso/session/sessionManager.d.ts +23 -0
- package/dist/sso/session/sessionManager.js +58 -0
- package/dist/sso/session/ssoSession.d.ts +8 -0
- package/dist/sso/session/ssoSession.js +5 -0
- package/dist/sso/session/ssoSessionStorage.d.ts +21 -0
- package/dist/sso/session/ssoSessionStorage.js +20 -0
- package/dist/sso/session/types.d.ts +6 -0
- package/dist/sso/session/types.js +1 -0
- package/dist/sso/session/userSessionStorage.d.ts +21 -0
- package/dist/sso/session/userSessionStorage.js +20 -0
- package/dist/sso/sessionManager/attestationService.d.ts +5 -0
- package/dist/sso/sessionManager/attestationService.js +15 -0
- package/dist/sso/sessionManager/impl.d.ts +22 -0
- package/dist/sso/sessionManager/impl.js +71 -0
- package/dist/sso/sessionManager/repository/ssoSessionRepository.d.ts +22 -0
- package/dist/sso/sessionManager/repository/ssoSessionRepository.js +27 -0
- package/dist/sso/sessionManager/scale/hex.d.ts +1 -0
- package/dist/sso/sessionManager/scale/hex.js +3 -0
- package/dist/sso/sessionManager/scale/remoteMessage.d.ts +41 -0
- package/dist/sso/sessionManager/scale/remoteMessage.js +13 -0
- package/dist/sso/sessionManager/scale/signPayloadRequest.d.ts +19 -0
- package/dist/sso/sessionManager/scale/signPayloadRequest.js +19 -0
- package/dist/sso/sessionManager/scale/signPayloadResponse.d.ts +12 -0
- package/dist/sso/sessionManager/scale/signPayloadResponse.js +9 -0
- package/dist/sso/sessionManager/scale/signRequest.d.ts +19 -0
- package/dist/sso/sessionManager/scale/signRequest.js +19 -0
- package/dist/sso/sessionManager/scale/signResponse.d.ts +6 -0
- package/dist/sso/sessionManager/scale/signResponse.js +5 -0
- package/dist/sso/sessionManager/ssoSession.d.ts +23 -0
- package/dist/sso/sessionManager/ssoSession.js +69 -0
- package/dist/sso/sessionManager/ssoSessionProver.d.ts +4 -0
- package/dist/sso/sessionManager/ssoSessionProver.js +35 -0
- package/dist/sso/sessionManager/types.d.ts +6 -0
- package/dist/sso/sessionManager/types.js +1 -0
- package/dist/sso/sessionManager/userSession.d.ts +22 -0
- package/dist/sso/sessionManager/userSession.js +106 -0
- package/dist/sso/ssoSessionProver.d.ts +4 -0
- package/dist/sso/ssoSessionProver.js +35 -0
- package/dist/sso/ssoSessionRepository.d.ts +18 -0
- package/dist/sso/ssoSessionRepository.js +27 -0
- package/dist/sso/userSecretRepository.d.ts +17 -0
- package/dist/sso/userSecretRepository.js +45 -0
- package/dist/sso/userSessionRepository.d.ts +18 -0
- package/dist/sso/userSessionRepository.js +26 -0
- package/dist/structs.d.ts +10 -10
- package/dist/structs.js +17 -13
- package/dist/types.d.ts +1 -1
- package/package.json +14 -7
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { err, ok, seq } from '../helpers/result.js';
|
|
2
|
+
import { createSecretStorage } from './secretStorage.js';
|
|
3
|
+
export const createUserManager = (appId, storage) => {
|
|
4
|
+
const secretStorage = createSecretStorage(appId, storage);
|
|
5
|
+
const usersStorage = createUserStorage(storage);
|
|
6
|
+
const selectedUserStorage = createSelectedUserStorage(storage);
|
|
7
|
+
const manager = {
|
|
8
|
+
async readSelectedUser() {
|
|
9
|
+
const selectedUser = await selectedUserStorage.read();
|
|
10
|
+
return selectedUser.andThenPromise(async (selectedUser) => {
|
|
11
|
+
if (selectedUser === null) {
|
|
12
|
+
return ok(null);
|
|
13
|
+
}
|
|
14
|
+
const user = await manager.readUser(selectedUser);
|
|
15
|
+
return user.andThenPromise(async (user) => {
|
|
16
|
+
if (user === null) {
|
|
17
|
+
await selectedUserStorage.clear();
|
|
18
|
+
}
|
|
19
|
+
return ok(user);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
async readUser(accountId) {
|
|
24
|
+
const user = await secretStorage.readSessionTopic(accountId).then(existingSessionTopic => existingSessionTopic.map(sessionTopic => {
|
|
25
|
+
return sessionTopic ? { sessionTopic, accountId } : null;
|
|
26
|
+
}));
|
|
27
|
+
await user
|
|
28
|
+
.andThen(v => (v ? ok(undefined) : err('User not found')))
|
|
29
|
+
.orElsePromise(() => manager.removeUser(accountId));
|
|
30
|
+
return user;
|
|
31
|
+
},
|
|
32
|
+
async createUser(user) {
|
|
33
|
+
return (await usersStorage.add(user.accountId)).andThenPromise(async () => seq(await selectedUserStorage.write(user.accountId), await secretStorage.writeSessionTopic(user.accountId, user.sessionTopic)).map(() => user));
|
|
34
|
+
},
|
|
35
|
+
async removeUser(accountId) {
|
|
36
|
+
const op = seq(await selectedUserStorage.read(), await usersStorage.remove(accountId));
|
|
37
|
+
return op.andThenPromise(async ([selectedUser, users]) => {
|
|
38
|
+
if (selectedUser === accountId) {
|
|
39
|
+
const nextSelectedUser = users.at(0);
|
|
40
|
+
if (nextSelectedUser) {
|
|
41
|
+
await selectedUserStorage.write(nextSelectedUser);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
await selectedUserStorage.clear();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return secretStorage.clearSessionTopic(accountId);
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
async readAccounts() {
|
|
51
|
+
return usersStorage.read();
|
|
52
|
+
},
|
|
53
|
+
async selectAccount(accountId) {
|
|
54
|
+
return selectedUserStorage.write(accountId);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
return manager;
|
|
58
|
+
};
|
|
59
|
+
const createUserStorage = (storage) => {
|
|
60
|
+
const KEY = 'Users';
|
|
61
|
+
return {
|
|
62
|
+
async read() {
|
|
63
|
+
const users = await storage.read(KEY);
|
|
64
|
+
return users.map(users => {
|
|
65
|
+
return users === null ? [] : JSON.parse(users);
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
async add(user) {
|
|
69
|
+
const usersRaw = await storage.read(KEY);
|
|
70
|
+
const users = usersRaw.map(x => (x ? JSON.parse(x) : []));
|
|
71
|
+
return users.andThenPromise(async (users) => {
|
|
72
|
+
if (users.some(x => x === user)) {
|
|
73
|
+
throw new Error(`User ${user} already exists.`);
|
|
74
|
+
}
|
|
75
|
+
const newUsers = users.concat(user);
|
|
76
|
+
return storage.write(KEY, JSON.stringify(newUsers)).then(x => x.map(() => newUsers));
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
async remove(user) {
|
|
80
|
+
const usersRaw = await storage.read(KEY);
|
|
81
|
+
const users = usersRaw.map(x => (x ? JSON.parse(x) : []));
|
|
82
|
+
return users.andThenPromise(async (users) => {
|
|
83
|
+
const newUsers = users.filter(x => x !== user);
|
|
84
|
+
if (newUsers.length !== users.length) {
|
|
85
|
+
return storage.write(KEY, JSON.stringify(newUsers)).then(x => x.map(() => newUsers));
|
|
86
|
+
}
|
|
87
|
+
return ok([]);
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
const createSelectedUserStorage = (storage) => {
|
|
93
|
+
const KEY = 'SelectedUser';
|
|
94
|
+
return {
|
|
95
|
+
read() {
|
|
96
|
+
return storage.read(KEY);
|
|
97
|
+
},
|
|
98
|
+
write(accountId) {
|
|
99
|
+
return storage.write(KEY, accountId);
|
|
100
|
+
},
|
|
101
|
+
clear() {
|
|
102
|
+
return storage.clear(KEY);
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { StorageAdapter } from '../adapters/storage/types.js';
|
|
2
|
+
import type { Result } from '../helpers/result.js';
|
|
3
|
+
import type { SessionTopic } from '../types.js';
|
|
4
|
+
export type UserSession = {
|
|
5
|
+
accountId: string;
|
|
6
|
+
sessionTopic: SessionTopic;
|
|
7
|
+
};
|
|
8
|
+
export declare const createUserStorage: (appId: string, storage: StorageAdapter) => {
|
|
9
|
+
sessions: {
|
|
10
|
+
read(accountId: string): Promise<Result<UserSession | null>>;
|
|
11
|
+
readSelectedUser(): Promise<Result<UserSession | null>>;
|
|
12
|
+
create(user: UserSession): Promise<Result<UserSession>>;
|
|
13
|
+
remove(accountId: string): Promise<Result<void, Error>>;
|
|
14
|
+
};
|
|
15
|
+
accounts: {
|
|
16
|
+
read(): Promise<Result<string[], Error>>;
|
|
17
|
+
select(accountId: string): Promise<Result<string | null, Error> | Result<null, Error>>;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { err, ok, seq } from '../helpers/result.js';
|
|
2
|
+
import { createSecretStorage } from './secretStorage.js';
|
|
3
|
+
import { reactiveStorage } from './syncStorage.js';
|
|
4
|
+
export const createUserStorage = (appId, storage) => {
|
|
5
|
+
const secretStorage = createSecretStorage(appId, storage);
|
|
6
|
+
const usersStorage = createSessionsStorage(storage);
|
|
7
|
+
const selectedUserStorage = createSelectedUserStorage(storage);
|
|
8
|
+
const store = {
|
|
9
|
+
sessions: {
|
|
10
|
+
async read(accountId) {
|
|
11
|
+
const user = await secretStorage.readSessionTopic(accountId).then(existingSessionTopic => existingSessionTopic.map(sessionTopic => {
|
|
12
|
+
return sessionTopic ? { sessionTopic, accountId } : null;
|
|
13
|
+
}));
|
|
14
|
+
await user
|
|
15
|
+
.andThen(v => (v ? ok(undefined) : err('User not found')))
|
|
16
|
+
.orElsePromise(() => store.sessions.remove(accountId));
|
|
17
|
+
return user;
|
|
18
|
+
},
|
|
19
|
+
async readSelectedUser() {
|
|
20
|
+
const selectedUser = await selectedUserStorage.read();
|
|
21
|
+
return selectedUser.andThenPromise(async (selectedUser) => {
|
|
22
|
+
if (selectedUser === null) {
|
|
23
|
+
return ok(null);
|
|
24
|
+
}
|
|
25
|
+
const user = await store.sessions.read(selectedUser);
|
|
26
|
+
return user.andThenPromise(async (user) => {
|
|
27
|
+
if (user === null) {
|
|
28
|
+
await selectedUserStorage.clear();
|
|
29
|
+
}
|
|
30
|
+
return ok(user);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
async create(user) {
|
|
35
|
+
return (await usersStorage.add(user.accountId)).andThenPromise(async () => seq(await selectedUserStorage.write(user.accountId), await secretStorage.writeSessionTopic(user.accountId, user.sessionTopic)).map(() => user));
|
|
36
|
+
},
|
|
37
|
+
async remove(accountId) {
|
|
38
|
+
const op = seq(await selectedUserStorage.read(), await usersStorage.remove(accountId));
|
|
39
|
+
return op.andThenPromise(async ([selectedUser, users]) => {
|
|
40
|
+
if (selectedUser === accountId) {
|
|
41
|
+
const nextSelectedUser = users.at(0);
|
|
42
|
+
if (nextSelectedUser) {
|
|
43
|
+
await selectedUserStorage.write(nextSelectedUser);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
await selectedUserStorage.clear();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return secretStorage.clearSessionTopic(accountId);
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
accounts: {
|
|
54
|
+
async read() {
|
|
55
|
+
return usersStorage.read();
|
|
56
|
+
},
|
|
57
|
+
async select(accountId) {
|
|
58
|
+
return selectedUserStorage.write(accountId);
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
return store;
|
|
63
|
+
};
|
|
64
|
+
const createSessionsStorage = (storage) => {
|
|
65
|
+
const reactive = reactiveStorage({
|
|
66
|
+
storage,
|
|
67
|
+
key: 'Users',
|
|
68
|
+
autosync: true,
|
|
69
|
+
initial: [],
|
|
70
|
+
from: x => JSON.parse(x),
|
|
71
|
+
to: x => JSON.stringify(x),
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
async read() {
|
|
75
|
+
return reactive.read();
|
|
76
|
+
},
|
|
77
|
+
async add(user) {
|
|
78
|
+
const users = await reactive.read();
|
|
79
|
+
return users.andThenPromise(async (users) => {
|
|
80
|
+
if (users.some(x => x === user)) {
|
|
81
|
+
throw new Error(`User ${user} already exists.`);
|
|
82
|
+
}
|
|
83
|
+
const newUsers = users.concat(user);
|
|
84
|
+
return reactive.write(newUsers).then(x => x.map(() => newUsers));
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
async remove(user) {
|
|
88
|
+
const users = await reactive.read();
|
|
89
|
+
return users.andThenPromise(async (users) => {
|
|
90
|
+
const newUsers = users.filter(x => x !== user);
|
|
91
|
+
if (newUsers.length !== users.length) {
|
|
92
|
+
return reactive.write(newUsers).then(x => x.map(() => newUsers));
|
|
93
|
+
}
|
|
94
|
+
return ok([]);
|
|
95
|
+
});
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
const createSelectedUserStorage = (storage) => {
|
|
100
|
+
return reactiveStorage({
|
|
101
|
+
storage,
|
|
102
|
+
key: 'SelectedUser',
|
|
103
|
+
autosync: true,
|
|
104
|
+
initial: null,
|
|
105
|
+
from: x => x,
|
|
106
|
+
to: x => x,
|
|
107
|
+
});
|
|
108
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { StorageAdapter } from '../adapters/storage/types.js';
|
|
2
|
+
import type { Result } from '../helpers/result.js';
|
|
3
|
+
import type { SessionTopic } from '../types.js';
|
|
4
|
+
export type UserSession = {
|
|
5
|
+
accountId: string;
|
|
6
|
+
sessionTopic: SessionTopic;
|
|
7
|
+
};
|
|
8
|
+
export declare const createUserStore: (appId: string, storage: StorageAdapter) => {
|
|
9
|
+
readSelectedUserSession(): Promise<Result<UserSession | null>>;
|
|
10
|
+
readUserSession(accountId: string): Promise<Result<UserSession | null>>;
|
|
11
|
+
createUserSession(user: UserSession): Promise<Result<UserSession>>;
|
|
12
|
+
removeUserSession(accountId: string): Promise<Result<void, Error>>;
|
|
13
|
+
readAccounts(): Promise<Result<string[], Error>>;
|
|
14
|
+
selectAccount(accountId: string): Promise<Result<void, Error>>;
|
|
15
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { err, ok, seq } from '../helpers/result.js';
|
|
2
|
+
import { createSecretStorage } from './secretStorage.js';
|
|
3
|
+
export const createUserStore = (appId, storage) => {
|
|
4
|
+
const secretStorage = createSecretStorage(appId, storage);
|
|
5
|
+
const usersStorage = createUserStorage(storage);
|
|
6
|
+
const selectedUserStorage = createSelectedUserStorage(storage);
|
|
7
|
+
const manager = {
|
|
8
|
+
async readSelectedUserSession() {
|
|
9
|
+
const selectedUser = await selectedUserStorage.read();
|
|
10
|
+
return selectedUser.andThenPromise(async (selectedUser) => {
|
|
11
|
+
if (selectedUser === null) {
|
|
12
|
+
return ok(null);
|
|
13
|
+
}
|
|
14
|
+
const user = await manager.readUserSession(selectedUser);
|
|
15
|
+
return user.andThenPromise(async (user) => {
|
|
16
|
+
if (user === null) {
|
|
17
|
+
await selectedUserStorage.clear();
|
|
18
|
+
}
|
|
19
|
+
return ok(user);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
async readUserSession(accountId) {
|
|
24
|
+
const user = await secretStorage.readSessionTopic(accountId).then(existingSessionTopic => existingSessionTopic.map(sessionTopic => {
|
|
25
|
+
return sessionTopic ? { sessionTopic, accountId } : null;
|
|
26
|
+
}));
|
|
27
|
+
await user
|
|
28
|
+
.andThen(v => (v ? ok(undefined) : err('User not found')))
|
|
29
|
+
.orElsePromise(() => manager.removeUserSession(accountId));
|
|
30
|
+
return user;
|
|
31
|
+
},
|
|
32
|
+
async createUserSession(user) {
|
|
33
|
+
return (await usersStorage.add(user.accountId)).andThenPromise(async () => seq(await selectedUserStorage.write(user.accountId), await secretStorage.writeSessionTopic(user.accountId, user.sessionTopic)).map(() => user));
|
|
34
|
+
},
|
|
35
|
+
async removeUserSession(accountId) {
|
|
36
|
+
const op = seq(await selectedUserStorage.read(), await usersStorage.remove(accountId));
|
|
37
|
+
return op.andThenPromise(async ([selectedUser, users]) => {
|
|
38
|
+
if (selectedUser === accountId) {
|
|
39
|
+
const nextSelectedUser = users.at(0);
|
|
40
|
+
if (nextSelectedUser) {
|
|
41
|
+
await selectedUserStorage.write(nextSelectedUser);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
await selectedUserStorage.clear();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return secretStorage.clearSessionTopic(accountId);
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
async readAccounts() {
|
|
51
|
+
return usersStorage.read();
|
|
52
|
+
},
|
|
53
|
+
async selectAccount(accountId) {
|
|
54
|
+
return selectedUserStorage.write(accountId);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
return manager;
|
|
58
|
+
};
|
|
59
|
+
const createUserStorage = (storage) => {
|
|
60
|
+
const KEY = 'Users';
|
|
61
|
+
return {
|
|
62
|
+
async read() {
|
|
63
|
+
const users = await storage.read(KEY);
|
|
64
|
+
return users.map(users => {
|
|
65
|
+
return users === null ? [] : JSON.parse(users);
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
async add(user) {
|
|
69
|
+
const usersRaw = await storage.read(KEY);
|
|
70
|
+
const users = usersRaw.map(x => (x ? JSON.parse(x) : []));
|
|
71
|
+
return users.andThenPromise(async (users) => {
|
|
72
|
+
if (users.some(x => x === user)) {
|
|
73
|
+
throw new Error(`User ${user} already exists.`);
|
|
74
|
+
}
|
|
75
|
+
const newUsers = users.concat(user);
|
|
76
|
+
return storage.write(KEY, JSON.stringify(newUsers)).then(x => x.map(() => newUsers));
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
async remove(user) {
|
|
80
|
+
const usersRaw = await storage.read(KEY);
|
|
81
|
+
const users = usersRaw.map(x => (x ? JSON.parse(x) : []));
|
|
82
|
+
return users.andThenPromise(async (users) => {
|
|
83
|
+
const newUsers = users.filter(x => x !== user);
|
|
84
|
+
if (newUsers.length !== users.length) {
|
|
85
|
+
return storage.write(KEY, JSON.stringify(newUsers)).then(x => x.map(() => newUsers));
|
|
86
|
+
}
|
|
87
|
+
return ok([]);
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
const createSelectedUserStorage = (storage) => {
|
|
93
|
+
const KEY = 'SelectedUser';
|
|
94
|
+
return {
|
|
95
|
+
read() {
|
|
96
|
+
return storage.read(KEY);
|
|
97
|
+
},
|
|
98
|
+
write(accountId) {
|
|
99
|
+
return storage.write(KEY, accountId);
|
|
100
|
+
},
|
|
101
|
+
clear() {
|
|
102
|
+
return storage.clear(KEY);
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
};
|
package/dist/papp.d.ts
CHANGED
|
@@ -1,25 +1,37 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
1
|
+
import type { LazyClient, StatementStoreAdapter } from '@novasamatech/statement-store';
|
|
2
|
+
import type { StorageAdapter } from '@novasamatech/storage-adapter';
|
|
3
|
+
import type { IdentityAdapter, IdentityRepository } from './identity/types.js';
|
|
4
|
+
import type { AuthComponent } from './sso/auth/impl.js';
|
|
5
|
+
import type { SsoSessionManager } from './sso/sessionManager/impl.js';
|
|
5
6
|
export type PappAdapter = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
getCurrentUser(): Promise<Identity | null>;
|
|
10
|
-
getSignInStatus(): SignInStatus;
|
|
11
|
-
onSignInStatusChange(callback: (status: SignInStatus) => void): VoidFunction;
|
|
12
|
-
};
|
|
7
|
+
sso: AuthComponent;
|
|
8
|
+
sessions: SsoSessionManager;
|
|
9
|
+
identity: IdentityRepository;
|
|
13
10
|
};
|
|
14
11
|
type Adapters = {
|
|
15
|
-
|
|
12
|
+
statementStore: StatementStoreAdapter;
|
|
16
13
|
identities: IdentityAdapter;
|
|
17
14
|
storage: StorageAdapter;
|
|
15
|
+
lazyClient: LazyClient;
|
|
18
16
|
};
|
|
19
17
|
type Params = {
|
|
18
|
+
/**
|
|
19
|
+
* Host app Id.
|
|
20
|
+
* CAUTION! This value should be stable.
|
|
21
|
+
*/
|
|
20
22
|
appId: string;
|
|
23
|
+
/**
|
|
24
|
+
* URL for additional metadata that will be displayed during pairing process.
|
|
25
|
+
* Content of provided json shound be
|
|
26
|
+
* ```ts
|
|
27
|
+
* interface Metadata {
|
|
28
|
+
* name: string;
|
|
29
|
+
* icon: string; // url for icon. Icon should be a rasterized image with min size 256x256 px.
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
21
33
|
metadata: string;
|
|
22
|
-
adapters?: Adapters
|
|
34
|
+
adapters?: Partial<Adapters>;
|
|
23
35
|
};
|
|
24
36
|
export declare function createPappAdapter({ appId, metadata, adapters }: Params): PappAdapter;
|
|
25
37
|
export {};
|
package/dist/papp.js
CHANGED
|
@@ -1,54 +1,23 @@
|
|
|
1
|
+
import { createLazyClient, createPapiStatementStoreAdapter } from '@novasamatech/statement-store';
|
|
2
|
+
import { createLocalStorageAdapter } from '@novasamatech/storage-adapter';
|
|
1
3
|
import { getWsProvider } from '@polkadot-api/ws-provider';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
4
|
+
import { SS_STABLE_STAGE_ENDPOINTS } from './constants.js';
|
|
5
|
+
import { createIdentityRepository } from './identity/impl.js';
|
|
6
|
+
import { createIdentityRpcAdapter } from './identity/rpcAdapter.js';
|
|
7
|
+
import { createAuth } from './sso/auth/impl.js';
|
|
8
|
+
import { createSsoSessionManager } from './sso/sessionManager/impl.js';
|
|
9
|
+
import { createUserSecretRepository } from './sso/userSecretRepository.js';
|
|
10
|
+
import { createUserSessionRepository } from './sso/userSessionRepository.js';
|
|
8
11
|
export function createPappAdapter({ appId, metadata, adapters }) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
storage = createMemoryAdapter();
|
|
20
|
-
identities = createIdentityRpcAdapter(lazyPapiAdapter, storage);
|
|
21
|
-
statements = createPapiStatementAdapter(lazyPapiAdapter);
|
|
22
|
-
}
|
|
23
|
-
const signInFlow = createSignInFlow({ appId, metadata, statements, storage });
|
|
24
|
-
const papp = {
|
|
25
|
-
auth: {
|
|
26
|
-
signIn() {
|
|
27
|
-
return signInFlow.signIn().then(result => {
|
|
28
|
-
if (result) {
|
|
29
|
-
return identities.getIdentity(result.pappAccountId);
|
|
30
|
-
}
|
|
31
|
-
return null;
|
|
32
|
-
});
|
|
33
|
-
},
|
|
34
|
-
getCurrentUser() {
|
|
35
|
-
return signInFlow.getSignedUser().then(result => {
|
|
36
|
-
if (result) {
|
|
37
|
-
return identities.getIdentity(result.pappAccountId);
|
|
38
|
-
}
|
|
39
|
-
return null;
|
|
40
|
-
});
|
|
41
|
-
},
|
|
42
|
-
getSignInStatus() {
|
|
43
|
-
return signInFlow.getSignInStatus();
|
|
44
|
-
},
|
|
45
|
-
onSignInStatusChange(callback) {
|
|
46
|
-
return signInFlow.onStatusChange(callback);
|
|
47
|
-
},
|
|
48
|
-
abortSignIn() {
|
|
49
|
-
return signInFlow.abortSignIn();
|
|
50
|
-
},
|
|
51
|
-
},
|
|
12
|
+
const lazyClient = adapters?.lazyClient ?? createLazyClient(getWsProvider(SS_STABLE_STAGE_ENDPOINTS));
|
|
13
|
+
const statementStore = adapters?.statementStore ?? createPapiStatementStoreAdapter(lazyClient);
|
|
14
|
+
const identities = adapters?.identities ?? createIdentityRpcAdapter(lazyClient);
|
|
15
|
+
const storage = adapters?.storage ?? createLocalStorageAdapter(appId);
|
|
16
|
+
const ssoSessionRepository = createUserSessionRepository(storage);
|
|
17
|
+
const userSecretRepository = createUserSecretRepository(appId, storage);
|
|
18
|
+
return {
|
|
19
|
+
sso: createAuth({ metadata, statementStore, ssoSessionRepository, userSecretRepository, lazyClient }),
|
|
20
|
+
sessions: createSsoSessionManager({ storage, statementStore, ssoSessionRepository, userSecretRepository }),
|
|
21
|
+
identity: createIdentityRepository({ adapter: identities, storage }),
|
|
52
22
|
};
|
|
53
|
-
return papp;
|
|
54
23
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { LazyClient } from '@novasamatech/statement-store';
|
|
2
|
+
import type { ResultAsync } from 'neverthrow';
|
|
3
|
+
import type { DerivedSr25519Account } from '../../crypto.js';
|
|
4
|
+
export declare function createAliceVerifier(): DerivedSr25519Account;
|
|
5
|
+
export declare const createAttestationService: (lazyClient: LazyClient) => {
|
|
6
|
+
claimUsername(): string;
|
|
7
|
+
grantVerifierAllowance(verifier: DerivedSr25519Account): ResultAsync<void, Error>;
|
|
8
|
+
getRingRfKey(candidate: DerivedSr25519Account): Uint8Array<ArrayBufferLike>;
|
|
9
|
+
getProofMessage(candidate: DerivedSr25519Account, ringVrfKey: Uint8Array): Uint8Array<ArrayBufferLike>;
|
|
10
|
+
deriveAttestationParams(username: string, candidate: DerivedSr25519Account, verifier: DerivedSr25519Account): ResultAsync<{
|
|
11
|
+
candidateSignature: Uint8Array<ArrayBufferLike>;
|
|
12
|
+
ringVrfKey: Uint8Array<ArrayBufferLike>;
|
|
13
|
+
proofOfOwnership: Uint8Array<ArrayBufferLike>;
|
|
14
|
+
identifierKey: import("../../crypto.js").EncrPublicKey;
|
|
15
|
+
consumerRegistrationSignature: Uint8Array<ArrayBufferLike>;
|
|
16
|
+
}, never>;
|
|
17
|
+
registerLitePerson(username: string, candidate: DerivedSr25519Account, verifier: DerivedSr25519Account): ResultAsync<void, Error>;
|
|
18
|
+
};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { createAccountId } from '@novasamatech/statement-store';
|
|
2
|
+
import { AccountId, Binary } from '@polkadot-api/substrate-bindings';
|
|
3
|
+
import { mergeUint8 } from '@polkadot-api/utils';
|
|
4
|
+
import { blake2b256 } from '@polkadot-labs/hdkd-helpers';
|
|
5
|
+
import { customAlphabet } from 'nanoid';
|
|
6
|
+
import { errAsync, fromAsyncThrowable, fromPromise, okAsync } from 'neverthrow';
|
|
7
|
+
import { getPolkadotSigner } from 'polkadot-api/signer';
|
|
8
|
+
import { Bytes, Option, Tuple, str } from 'scale-ts';
|
|
9
|
+
import { member_from_entropy, sign } from 'verifiablejs/bundler';
|
|
10
|
+
import { deriveSr25519Account, getEncrPub, stringToBytes } from '../../crypto.js';
|
|
11
|
+
import { toError } from '../../helpers/utils.js';
|
|
12
|
+
const accountId = AccountId();
|
|
13
|
+
export function createAliceVerifier() {
|
|
14
|
+
return deriveSr25519Account('bottom drive obey lake curtain smoke basket hold race lonely fit walk', '//Alice');
|
|
15
|
+
}
|
|
16
|
+
export const createAttestationService = (lazyClient) => {
|
|
17
|
+
const service = {
|
|
18
|
+
claimUsername() {
|
|
19
|
+
const nameSuffixFactory = customAlphabet('abcdefghijklmnopqrstuvwxyz', 4);
|
|
20
|
+
return `guest${nameSuffixFactory()}.${createNumericSuffix(4)}`;
|
|
21
|
+
},
|
|
22
|
+
grantVerifierAllowance(verifier) {
|
|
23
|
+
const client = lazyClient.getClient();
|
|
24
|
+
const api = client.getUnsafeApi();
|
|
25
|
+
const verifierAddress = accountId.dec(verifier.publicKey);
|
|
26
|
+
if (!api.query.PeopleLite || !api.query.PeopleLite.AttestationAllowance) {
|
|
27
|
+
return errAsync(new Error('Query PeopleLite.AttestationAllowance not found.'));
|
|
28
|
+
}
|
|
29
|
+
const verifierAllowance = fromPromise(api.query.PeopleLite.AttestationAllowance.getValue(verifierAddress), toError);
|
|
30
|
+
const getAllowance = fromAsyncThrowable(async () => {
|
|
31
|
+
const increaseAllowanceCall = api.tx.PeopleLite.increase_attestation_allowance({
|
|
32
|
+
account: verifierAddress,
|
|
33
|
+
count: 10,
|
|
34
|
+
});
|
|
35
|
+
const sudoCall = api.tx.Sudo.sudo({
|
|
36
|
+
call: increaseAllowanceCall.decodedCall,
|
|
37
|
+
});
|
|
38
|
+
return sudoCall.signAndSubmit(createPeopleSigner(verifier)).then(() => undefined);
|
|
39
|
+
}, toError);
|
|
40
|
+
return verifierAllowance.andThen(verifierAllowance => (verifierAllowance > 0 ? okAsync() : getAllowance()));
|
|
41
|
+
},
|
|
42
|
+
getRingRfKey(candidate) {
|
|
43
|
+
const verifiableEntropy = blake2b256(candidate.entropy);
|
|
44
|
+
return member_from_entropy(verifiableEntropy);
|
|
45
|
+
},
|
|
46
|
+
getProofMessage(candidate, ringVrfKey) {
|
|
47
|
+
return mergeUint8([stringToBytes('pop:people-lite:register using'), candidate.publicKey, ringVrfKey]);
|
|
48
|
+
},
|
|
49
|
+
deriveAttestationParams(username, candidate, verifier) {
|
|
50
|
+
const verifiableEntropy = blake2b256(candidate.entropy);
|
|
51
|
+
const ringVrfKey = service.getRingRfKey(candidate);
|
|
52
|
+
const identifierKey = getEncrPub(blake2b256(candidate.secret));
|
|
53
|
+
const message = service.getProofMessage(candidate, ringVrfKey);
|
|
54
|
+
// Extract username without the `.` separator and any following digits
|
|
55
|
+
// For lite person usernames like "ceainnhgidpj.39642086", we only use "ceainnhgidpj"
|
|
56
|
+
const usernameWithoutDigits = username.split('.')[0] ?? username;
|
|
57
|
+
const candidateSignature = candidate.sign(message);
|
|
58
|
+
const proofOfOwnership = sign(verifiableEntropy, message);
|
|
59
|
+
const ResourceSignatureCodec = Tuple(
|
|
60
|
+
// candidate PublicKey (32 bytes)
|
|
61
|
+
Bytes(32),
|
|
62
|
+
// verifier AccountId (32 bytes)
|
|
63
|
+
Bytes(32),
|
|
64
|
+
// identifierKey
|
|
65
|
+
Bytes(65),
|
|
66
|
+
// username without digits
|
|
67
|
+
str,
|
|
68
|
+
// reserved_username
|
|
69
|
+
Option(Bytes()));
|
|
70
|
+
const resourcesSignatureData = ResourceSignatureCodec.enc([
|
|
71
|
+
candidate.publicKey,
|
|
72
|
+
createAccountId(verifier.publicKey),
|
|
73
|
+
identifierKey,
|
|
74
|
+
usernameWithoutDigits,
|
|
75
|
+
undefined,
|
|
76
|
+
]);
|
|
77
|
+
const consumerRegistrationSignature = candidate.sign(resourcesSignatureData);
|
|
78
|
+
return okAsync({
|
|
79
|
+
candidateSignature: candidateSignature,
|
|
80
|
+
ringVrfKey,
|
|
81
|
+
proofOfOwnership,
|
|
82
|
+
identifierKey,
|
|
83
|
+
consumerRegistrationSignature,
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
registerLitePerson(username, candidate, verifier) {
|
|
87
|
+
const client = lazyClient.getClient();
|
|
88
|
+
const api = client.getUnsafeApi();
|
|
89
|
+
return service
|
|
90
|
+
.deriveAttestationParams(username, candidate, verifier)
|
|
91
|
+
.andThen(params => {
|
|
92
|
+
const attestCall = api.tx.PeopleLite.attest({
|
|
93
|
+
candidate: accountId.dec(candidate.publicKey),
|
|
94
|
+
candidate_signature: {
|
|
95
|
+
type: 'Sr25519',
|
|
96
|
+
value: Binary.fromBytes(params.candidateSignature),
|
|
97
|
+
},
|
|
98
|
+
ring_vrf_key: Binary.fromBytes(params.ringVrfKey),
|
|
99
|
+
proof_of_ownership: Binary.fromBytes(params.proofOfOwnership),
|
|
100
|
+
consumer_registration: {
|
|
101
|
+
signature: {
|
|
102
|
+
type: 'Sr25519',
|
|
103
|
+
value: Binary.fromBytes(params.consumerRegistrationSignature),
|
|
104
|
+
},
|
|
105
|
+
account: accountId.dec(candidate.publicKey),
|
|
106
|
+
identifier_key: Binary.fromBytes(params.identifierKey),
|
|
107
|
+
username: Binary.fromText(username),
|
|
108
|
+
reserved_username: undefined,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
return fromPromise(new Promise((resolve, reject) => {
|
|
112
|
+
const subscription = attestCall.signSubmitAndWatch(createPeopleSigner(verifier)).subscribe({
|
|
113
|
+
next(event) {
|
|
114
|
+
if (event.type === 'finalized') {
|
|
115
|
+
// Check if transaction was successful
|
|
116
|
+
if (event.ok) {
|
|
117
|
+
subscription.unsubscribe();
|
|
118
|
+
resolve();
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
// Extract error details
|
|
122
|
+
let errorMessage = 'Transaction failed';
|
|
123
|
+
if (event.dispatchError?.type === 'Module') {
|
|
124
|
+
const moduleError = event.dispatchError.value;
|
|
125
|
+
errorMessage = `${moduleError.type}.${moduleError.value?.type || 'Unknown'}`;
|
|
126
|
+
}
|
|
127
|
+
subscription.unsubscribe();
|
|
128
|
+
reject(errorMessage);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
error: reject,
|
|
133
|
+
});
|
|
134
|
+
}), toError).map(() => undefined);
|
|
135
|
+
})
|
|
136
|
+
.andTee(() => console.log(`Attestation for ${accountId.dec(candidate.publicKey)} successfully passed.`));
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
return service;
|
|
140
|
+
};
|
|
141
|
+
function createNumericSuffix(length) {
|
|
142
|
+
let suffix = '';
|
|
143
|
+
for (let i = 0; i < length; i++) {
|
|
144
|
+
suffix += (Math.random() * 9).toFixed();
|
|
145
|
+
}
|
|
146
|
+
return suffix;
|
|
147
|
+
}
|
|
148
|
+
function createPeopleSigner(verifier) {
|
|
149
|
+
const baseSigner = getPolkadotSigner(verifier.publicKey, 'Sr25519', verifier.sign);
|
|
150
|
+
return {
|
|
151
|
+
publicKey: baseSigner.publicKey,
|
|
152
|
+
signBytes: baseSigner.signBytes,
|
|
153
|
+
signTx: async (callData, signedExtensions, metadata, atBlockNumber, hasher) => {
|
|
154
|
+
// Add People chain custom signed extensions
|
|
155
|
+
const extensionsWithCustom = {
|
|
156
|
+
...signedExtensions,
|
|
157
|
+
VerifyMultiSignature: {
|
|
158
|
+
identifier: 'VerifyMultiSignature',
|
|
159
|
+
value: new Uint8Array([1]), // 1u8 = Option::Some with empty data
|
|
160
|
+
additionalSigned: new Uint8Array([]), // Empty additional data
|
|
161
|
+
},
|
|
162
|
+
AsPerson: {
|
|
163
|
+
identifier: 'AsPerson',
|
|
164
|
+
value: new Uint8Array([0]), // 0u8 = Option::None
|
|
165
|
+
additionalSigned: new Uint8Array([]), // Empty additional data
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
return baseSigner.signTx(callData, extensionsWithCustom, metadata, atBlockNumber, hasher);
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|