@thru/wallet 0.2.27 → 0.2.29
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 +3 -2
- package/app.plugin.cjs +1 -1
- package/dist/{BrowserSDK-CpRFiJsW.d.ts → BrowserSDK-CRQTOT8S.d.ts} +178 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +376 -12
- package/dist/index.js.map +1 -1
- package/dist/native/react/transparent.d.ts +104 -0
- package/dist/native/react/transparent.js +2210 -0
- package/dist/native/react/transparent.js.map +1 -0
- package/dist/native/react.d.ts +5 -90
- package/dist/native/react.js +768 -35
- package/dist/native/react.js.map +1 -1
- package/dist/native.d.ts +106 -2
- package/dist/native.js +524 -34
- package/dist/native.js.map +1 -1
- package/dist/react-ui.js +5 -0
- package/dist/react-ui.js.map +1 -1
- package/dist/react.d.ts +2 -2
- package/dist/react.js +376 -12
- package/dist/react.js.map +1 -1
- package/package.json +8 -2
- package/src/BrowserSDK.ts +32 -1
- package/src/encoding.ts +39 -0
- package/src/index.ts +5 -1
- package/src/interfaces/IThruChain.ts +50 -1
- package/src/interfaces/types.ts +52 -0
- package/src/native/NativeSDK.test.ts +200 -1
- package/src/native/NativeSDK.ts +125 -11
- package/src/native/index.ts +12 -0
- package/src/native/provider/NativeProvider.ts +109 -8
- package/src/native/provider/WebViewBridge.test.ts +24 -3
- package/src/native/provider/WebViewBridge.ts +18 -8
- package/src/native/provider/chains/ThruChain.ts +215 -5
- package/src/native/provider/shell.test.ts +3 -3
- package/src/native/provider/shell.ts +1 -1
- package/src/native/react/ThruContext.ts +3 -1
- package/src/native/react/ThruProvider.tsx +25 -0
- package/src/native/react/ThruTransparentWalletBridge.tsx +281 -0
- package/src/native/react/hooks/useWallet.ts +12 -1
- package/src/native/react/index.ts +11 -0
- package/src/native/react/transparent.ts +35 -0
- package/src/protocol/postMessage.ts +127 -2
- package/src/provider/EmbeddedProvider.ts +7 -1
- package/src/provider/IframeManager.test.ts +18 -0
- package/src/provider/IframeManager.ts +8 -1
- package/src/provider/chains/ThruChain.ts +210 -4
- package/src/provider/types/messages.ts +16 -0
- package/src/react/index.ts +6 -0
- package/src/signing-sessions.test.ts +182 -0
- package/src/signing-sessions.ts +204 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ThruSigningSessionCreateOptions,
|
|
3
|
+
ThruSigningSessionDescriptor,
|
|
4
|
+
ThruSigningSessionTimestamp,
|
|
5
|
+
} from "./interfaces";
|
|
6
|
+
|
|
7
|
+
export interface SigningSessionStorage {
|
|
8
|
+
getItem: (key: string) => string | null | Promise<string | null>;
|
|
9
|
+
setItem: (key: string, value: string) => void | Promise<void>;
|
|
10
|
+
removeItem: (key: string) => void | Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface SigningSessionStorePayload {
|
|
14
|
+
version: 1;
|
|
15
|
+
sessions: ThruSigningSessionDescriptor[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const STORAGE_VERSION = 1;
|
|
19
|
+
const KEY_PREFIX = "thru.wallet.signing-sessions.v1";
|
|
20
|
+
|
|
21
|
+
function encodeKeyPart(input: string): string {
|
|
22
|
+
return encodeURIComponent(input).replace(/[!'()*]/g, (char) =>
|
|
23
|
+
`%${char.charCodeAt(0).toString(16).toUpperCase()}`,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function nowSeconds(): number {
|
|
28
|
+
return Math.floor(Date.now() / 1000);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function resolveSigningSessionStorageKey(params: {
|
|
32
|
+
walletOrigin: string;
|
|
33
|
+
appOrigin: string;
|
|
34
|
+
storageKey?: string;
|
|
35
|
+
}): string {
|
|
36
|
+
if (params.storageKey) return params.storageKey;
|
|
37
|
+
return `${KEY_PREFIX}:${encodeKeyPart(params.walletOrigin)}:${encodeKeyPart(params.appOrigin)}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getDefaultBrowserSigningSessionStorage(): SigningSessionStorage | null {
|
|
41
|
+
if (typeof window === "undefined") return null;
|
|
42
|
+
try {
|
|
43
|
+
return window.localStorage ?? null;
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function normalizeExpiresAt(
|
|
50
|
+
value: ThruSigningSessionTimestamp,
|
|
51
|
+
label = "expiresAt",
|
|
52
|
+
): number {
|
|
53
|
+
if (value instanceof Date) {
|
|
54
|
+
const millis = value.getTime();
|
|
55
|
+
if (!Number.isFinite(millis)) throw new Error(`${label} must be a valid Date`);
|
|
56
|
+
return Math.floor(millis / 1000);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (typeof value === "bigint") {
|
|
60
|
+
if (value < 0n || value > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
61
|
+
throw new Error(`${label} must fit in a JavaScript safe integer`);
|
|
62
|
+
}
|
|
63
|
+
return Number(value);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (typeof value === "string") {
|
|
67
|
+
const trimmed = value.trim();
|
|
68
|
+
if (!/^\d+$/.test(trimmed)) {
|
|
69
|
+
throw new Error(`${label} must be a Unix timestamp in seconds`);
|
|
70
|
+
}
|
|
71
|
+
return normalizeExpiresAt(BigInt(trimmed), label);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
75
|
+
throw new Error(`${label} must be a finite positive Unix timestamp`);
|
|
76
|
+
}
|
|
77
|
+
return Math.floor(value);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function resolveSessionExpirySeconds(
|
|
81
|
+
options: ThruSigningSessionCreateOptions,
|
|
82
|
+
): number {
|
|
83
|
+
const hasDuration = options.durationSeconds !== undefined;
|
|
84
|
+
const hasExpiresAt = options.expiresAt !== undefined;
|
|
85
|
+
if (hasDuration === hasExpiresAt) {
|
|
86
|
+
throw new Error("Provide exactly one of durationSeconds or expiresAt");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (hasDuration) {
|
|
90
|
+
const duration = options.durationSeconds;
|
|
91
|
+
if (
|
|
92
|
+
typeof duration !== "number" ||
|
|
93
|
+
!Number.isFinite(duration) ||
|
|
94
|
+
duration <= 0
|
|
95
|
+
) {
|
|
96
|
+
throw new Error("durationSeconds must be a positive number");
|
|
97
|
+
}
|
|
98
|
+
return nowSeconds() + Math.floor(duration);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return normalizeExpiresAt(options.expiresAt!, "expiresAt");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function assertSigningSessionWalletAccountIdx(walletAccountIdx: number): void {
|
|
105
|
+
if (!Number.isInteger(walletAccountIdx) || walletAccountIdx < 2 || walletAccountIdx > 0xffff) {
|
|
106
|
+
throw new Error("walletAccountIdx must be an account index between 2 and 65535");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function normalizeDescriptor(
|
|
111
|
+
descriptor: ThruSigningSessionDescriptor,
|
|
112
|
+
): ThruSigningSessionDescriptor {
|
|
113
|
+
return {
|
|
114
|
+
id: descriptor.id,
|
|
115
|
+
walletAddress: descriptor.walletAddress,
|
|
116
|
+
publicKey: descriptor.publicKey,
|
|
117
|
+
authIdx: Number(descriptor.authIdx),
|
|
118
|
+
expiresAt: normalizeExpiresAt(descriptor.expiresAt, "descriptor.expiresAt"),
|
|
119
|
+
createdAt: normalizeExpiresAt(descriptor.createdAt, "descriptor.createdAt"),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function isActive(descriptor: ThruSigningSessionDescriptor): boolean {
|
|
124
|
+
return nowSeconds() < descriptor.expiresAt;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export class SigningSessionDescriptorStore {
|
|
128
|
+
private readonly storage: SigningSessionStorage;
|
|
129
|
+
private readonly key: string;
|
|
130
|
+
|
|
131
|
+
constructor(storage: SigningSessionStorage, key: string) {
|
|
132
|
+
this.storage = storage;
|
|
133
|
+
this.key = key;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async list(): Promise<ThruSigningSessionDescriptor[]> {
|
|
137
|
+
const sessions = await this.read();
|
|
138
|
+
const active = sessions.filter(isActive);
|
|
139
|
+
if (active.length !== sessions.length) {
|
|
140
|
+
await this.write(active);
|
|
141
|
+
}
|
|
142
|
+
return active;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async get(id: string): Promise<ThruSigningSessionDescriptor | null> {
|
|
146
|
+
const sessions = await this.list();
|
|
147
|
+
return sessions.find((session) => session.id === id) ?? null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async save(descriptor: ThruSigningSessionDescriptor): Promise<void> {
|
|
151
|
+
const normalized = normalizeDescriptor(descriptor);
|
|
152
|
+
const sessions = (await this.list()).filter((session) => session.id !== normalized.id);
|
|
153
|
+
sessions.push(normalized);
|
|
154
|
+
await this.write(sessions);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async saveReplacingWalletSessions(
|
|
158
|
+
descriptor: ThruSigningSessionDescriptor,
|
|
159
|
+
): Promise<void> {
|
|
160
|
+
const normalized = normalizeDescriptor(descriptor);
|
|
161
|
+
const sessions = (await this.list()).filter(
|
|
162
|
+
(session) =>
|
|
163
|
+
session.id === normalized.id ||
|
|
164
|
+
session.walletAddress !== normalized.walletAddress,
|
|
165
|
+
);
|
|
166
|
+
const withoutCurrent = sessions.filter((session) => session.id !== normalized.id);
|
|
167
|
+
withoutCurrent.push(normalized);
|
|
168
|
+
await this.write(withoutCurrent);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async remove(id: string): Promise<void> {
|
|
172
|
+
const sessions = (await this.list()).filter((session) => session.id !== id);
|
|
173
|
+
if (sessions.length === 0) {
|
|
174
|
+
await this.storage.removeItem(this.key);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
await this.write(sessions);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private async read(): Promise<ThruSigningSessionDescriptor[]> {
|
|
181
|
+
const raw = await this.storage.getItem(this.key);
|
|
182
|
+
if (!raw) return [];
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
const parsed = JSON.parse(raw) as Partial<SigningSessionStorePayload>;
|
|
186
|
+
if (parsed.version !== STORAGE_VERSION || !Array.isArray(parsed.sessions)) {
|
|
187
|
+
await this.storage.removeItem(this.key);
|
|
188
|
+
return [];
|
|
189
|
+
}
|
|
190
|
+
return parsed.sessions.map(normalizeDescriptor);
|
|
191
|
+
} catch {
|
|
192
|
+
await this.storage.removeItem(this.key);
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private async write(sessions: ThruSigningSessionDescriptor[]): Promise<void> {
|
|
198
|
+
const payload: SigningSessionStorePayload = {
|
|
199
|
+
version: STORAGE_VERSION,
|
|
200
|
+
sessions: sessions.map(normalizeDescriptor),
|
|
201
|
+
};
|
|
202
|
+
await this.storage.setItem(this.key, JSON.stringify(payload));
|
|
203
|
+
}
|
|
204
|
+
}
|