unwallet-client-sdk 0.20.1 → 0.22.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
@@ -1,6 +1,8 @@
1
1
  # unWallet client-side SDK
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/unwallet-client-sdk.svg?logo=npm)](https://npm.im/unwallet-client-sdk)
4
+ [![test](https://github.com/SIVIRA/unwallet-client-sdk-js/actions/workflows/test.yaml/badge.svg)](https://github.com/SIVIRA/unwallet-client-sdk-js/actions/workflows/test.yaml)
5
+ [![codecov](https://codecov.io/gh/SIVIRA/unwallet-client-sdk-js/graph/badge.svg?token=15NG0897GZ)](https://codecov.io/gh/SIVIRA/unwallet-client-sdk-js)
4
6
 
5
7
  :warning: This SDK works correctly only on web browsers.
6
8
 
package/dist/index.cjs CHANGED
@@ -3,85 +3,138 @@ let viem = require("viem");
3
3
  let zod = require("zod");
4
4
  //#region src/error.ts
5
5
  var UWError = class extends Error {
6
- constructor(code, message) {
7
- super(message !== void 0 ? `${code}: ${message}` : code);
8
- this.code = code;
9
- }
10
6
  static {
11
7
  this.prototype.name = "UWError";
12
8
  }
9
+ constructor(code, msg) {
10
+ super(msg !== void 0 ? `${code}: ${msg}` : code);
11
+ this.code = code;
12
+ }
13
13
  };
14
14
  //#endregion
15
15
  //#region src/config.ts
16
- function getUnWalletConfigByEnv(env) {
17
- switch (env) {
18
- case "prod": return {
19
- frontend: { baseURL: "https://id.unwallet.world" },
20
- xapi: {
21
- url: "wss://xapi.id.unwallet.world",
22
- connectionTimeout: 1e4
23
- }
24
- };
25
- case "dev": return {
26
- frontend: { baseURL: "http://localhost:4200" },
27
- xapi: {
28
- url: "wss://xapi.id.test.unwallet.dev",
29
- connectionTimeout: 1e4
30
- }
31
- };
16
+ const envToUnWalletConfig = {
17
+ prod: {
18
+ frontend: { origin: "https://id.unwallet.world" },
19
+ xAPI: {
20
+ url: "wss://xapi.id.unwallet.world",
21
+ connectionTimeout: 1e4
22
+ }
23
+ },
24
+ dev: {
25
+ frontend: { origin: "http://localhost:4200" },
26
+ xAPI: {
27
+ url: "wss://xapi.id.test.unwallet.dev",
28
+ connectionTimeout: 1e4
29
+ }
32
30
  }
33
- }
34
- //#endregion
35
- //#region src/x.ts
36
- const X_RESPONSE_TYPES = [
37
- "connectionID",
38
- "signature",
39
- "transactionID",
40
- "error"
41
- ];
31
+ };
32
+ const xRequestSchema = zod.z.object({ action: zod.z.enum(["getConnectionID"]) }).readonly();
33
+ zod.z.string().transform((val, ctx) => {
34
+ try {
35
+ return JSON.parse(val);
36
+ } catch {
37
+ ctx.addIssue({
38
+ code: "custom",
39
+ message: "Invalid JSON string"
40
+ });
41
+ return zod.z.NEVER;
42
+ }
43
+ }).pipe(xRequestSchema);
44
+ const xResponseSchema = zod.z.union([
45
+ zod.z.object({
46
+ type: zod.z.literal("connectionID"),
47
+ value: zod.z.string().nonempty()
48
+ }),
49
+ zod.z.object({
50
+ type: zod.z.literal("signature"),
51
+ value: zod.z.string().refine(viem.isHex, { error: "Invalid hex" })
52
+ }),
53
+ zod.z.object({
54
+ type: zod.z.literal("transactionID"),
55
+ value: zod.z.string().nonempty()
56
+ }),
57
+ zod.z.object({
58
+ type: zod.z.literal("error"),
59
+ value: zod.z.string().nonempty()
60
+ })
61
+ ]).readonly();
62
+ const xResponsePayloadSchema = zod.z.string().transform((val, ctx) => {
63
+ try {
64
+ return JSON.parse(val);
65
+ } catch {
66
+ ctx.addIssue({
67
+ code: "custom",
68
+ message: "Invalid JSON string"
69
+ });
70
+ return zod.z.NEVER;
71
+ }
72
+ }).pipe(xResponseSchema);
42
73
  var XConnection = class XConnection {
43
74
  constructor(args) {
44
75
  this.responseHandler = null;
45
- this.socket = args.socket;
46
76
  this.id = args.id;
77
+ this.socket = args.socket;
78
+ this.debugOptions = args.debugOptions ?? null;
47
79
  this.initListeners();
48
80
  }
49
81
  static async init(config) {
50
82
  const socket = new WebSocket(config.url);
51
83
  const id = await new Promise((resolve, reject) => {
52
- setInterval(() => reject(new UWError("CONNECTION_TIMEOUT")), config.connectionTimeout);
84
+ const timeoutID = setTimeout(() => abortHandshake(new UWError("CONNECTION_TIMEOUT")), config.connectionTimeout);
85
+ const detachListeners = () => {
86
+ socket.onopen = null;
87
+ socket.onmessage = null;
88
+ socket.onerror = null;
89
+ socket.onclose = null;
90
+ };
91
+ const completeHandshake = (id) => {
92
+ clearTimeout(timeoutID);
93
+ detachListeners();
94
+ resolve(id);
95
+ };
96
+ const abortHandshake = (err) => {
97
+ clearTimeout(timeoutID);
98
+ detachListeners();
99
+ socket.close();
100
+ reject(err);
101
+ };
53
102
  socket.onopen = () => socket.send(JSON.stringify({ action: "getConnectionID" }));
54
103
  socket.onmessage = (event) => {
55
104
  let resp;
56
105
  {
57
- const result = safeParseMessageEventDataToXResponse(event.data);
106
+ const result = safeParseXResponsePayload(event.data);
58
107
  if (!result.success) {
59
- reject(result.error);
108
+ abortHandshake(result.error);
60
109
  return;
61
110
  }
62
111
  resp = result.data;
63
112
  }
64
113
  if (resp.type !== "connectionID") {
65
- reject(new UWError("INVALID_RESPONSE", `unexpected response type: ${resp.type}`));
114
+ abortHandshake(newUnexpectedXResponseTypeError(resp));
66
115
  return;
67
116
  }
68
- resolve(resp.value);
117
+ completeHandshake(resp.value);
69
118
  };
70
- socket.onerror = () => reject(new UWError("CONNECTION_FAILED"));
71
- socket.onclose = (event) => reject(new UWError("CONNECTION_CLOSED", event.reason));
119
+ socket.onerror = () => abortHandshake(new UWError("CONNECTION_FAILED"));
120
+ socket.onclose = (event) => abortHandshake(newConnectionClosedError(event));
72
121
  });
73
122
  return new XConnection({
74
123
  id,
75
- socket
124
+ socket,
125
+ debugOptions: config.debug
76
126
  });
77
127
  }
78
128
  initListeners() {
79
129
  this.socket.onopen = null;
80
130
  this.socket.onmessage = (event) => {
81
- if (this.responseHandler === null) return;
131
+ if (this.responseHandler === null) {
132
+ this.debugOptions?.handlers?.onMessageEventDropped?.(event);
133
+ return;
134
+ }
82
135
  let resp;
83
136
  {
84
- const result = safeParseMessageEventDataToXResponse(event.data);
137
+ const result = safeParseXResponsePayload(event.data);
85
138
  if (!result.success) {
86
139
  this.responseHandler.reject(result.error);
87
140
  return;
@@ -94,7 +147,7 @@ var XConnection = class XConnection {
94
147
  case "rejected":
95
148
  this.responseHandler.reject(new UWError("REQUEST_REJECTED"));
96
149
  break;
97
- default: this.responseHandler.reject(new UWError("INVALID_RESPONSE", `unexpected error response value: ${resp.value}`));
150
+ default: this.responseHandler.reject(new UWError("INVALID_RESPONSE", `unexpected error value: ${resp.value}`));
98
151
  }
99
152
  break;
100
153
  default: this.responseHandler.resolve(resp);
@@ -102,8 +155,11 @@ var XConnection = class XConnection {
102
155
  };
103
156
  this.socket.onerror = null;
104
157
  this.socket.onclose = (event) => {
105
- if (this.responseHandler === null) return;
106
- this.responseHandler.reject(new UWError("CONNECTION_CLOSED", event.reason));
158
+ if (this.responseHandler === null) {
159
+ this.debugOptions?.handlers?.onCloseEventDropped?.(event);
160
+ return;
161
+ }
162
+ this.responseHandler.reject(newConnectionClosedError(event));
107
163
  };
108
164
  }
109
165
  get readyState() {
@@ -112,34 +168,33 @@ var XConnection = class XConnection {
112
168
  get hasResponseHandler() {
113
169
  return this.responseHandler !== null;
114
170
  }
115
- setResponseHandler(handler) {
116
- this.responseHandler = handler;
171
+ setResponseHandler(respHandler) {
172
+ this.responseHandler = respHandler;
173
+ }
174
+ close() {
175
+ this.socket.close();
117
176
  }
118
177
  };
119
- function safeParseMessageEventDataToXResponse(data) {
120
- const result = zod.z.string().refine((val) => {
121
- try {
122
- JSON.parse(val);
123
- } catch (e) {
124
- return false;
125
- }
126
- return true;
127
- }, {
128
- abort: true,
129
- message: "Invalid JSON string"
130
- }).transform((val) => JSON.parse(val)).pipe(zod.z.object({
131
- type: zod.z.enum(X_RESPONSE_TYPES),
132
- value: zod.z.string()
133
- })).safeParse(data);
178
+ function safeParseXResponsePayload(data) {
179
+ const result = xResponsePayloadSchema.safeParse(data);
134
180
  if (!result.success) return {
135
181
  success: false,
136
- error: new UWError("INVALID_RESPONSE", `invalid message event data: ${result.error.message}`)
182
+ error: new UWError("INVALID_RESPONSE", `invalid payload: ${zod.z.prettifyError(result.error)}`)
137
183
  };
138
184
  return {
139
185
  success: true,
140
186
  data: result.data
141
187
  };
142
188
  }
189
+ function newUnexpectedXResponseTypeError(resp) {
190
+ const msgs = [];
191
+ msgs.push(`unexpected type: ${resp.type}`);
192
+ if (resp.type === "error") msgs.push(`(value: ${resp.value})`);
193
+ return new UWError("INVALID_RESPONSE", msgs.join(" "));
194
+ }
195
+ function newConnectionClosedError(event) {
196
+ return new UWError("CONNECTION_CLOSED", event.reason.length > 0 ? event.reason : void 0);
197
+ }
143
198
  //#endregion
144
199
  //#region src/unwallet.ts
145
200
  var UnWallet = class UnWallet {
@@ -153,14 +208,14 @@ var UnWallet = class UnWallet {
153
208
  return new UnWallet({
154
209
  env,
155
210
  clientID: config.clientID,
156
- xConnection: await XConnection.init(getUnWalletConfigByEnv(env).xapi)
211
+ xConnection: await XConnection.init(envToUnWalletConfig[env].xAPI)
157
212
  });
158
213
  }
159
214
  get unWalletConfig() {
160
- return getUnWalletConfigByEnv(this.env);
215
+ return envToUnWalletConfig[this.env];
161
216
  }
162
217
  authorize(args) {
163
- const url = new URL(`${this.unWalletConfig.frontend.baseURL}/${args.isVirtual !== void 0 ? args.isVirtual ? "v" : "" : "v"}authorize`);
218
+ const url = new URL(`/${args.isVirtual === false ? "" : "v"}authorize`, this.unWalletConfig.frontend.origin);
164
219
  url.searchParams.set("response_type", "id_token");
165
220
  url.searchParams.set("response_mode", args.responseMode ?? "fragment");
166
221
  url.searchParams.set("client_id", this.clientID);
@@ -170,90 +225,57 @@ var UnWallet = class UnWallet {
170
225
  if (args.chainID !== void 0) url.searchParams.set("chain_id", args.chainID.toString());
171
226
  location.assign(url);
172
227
  }
173
- sign(args) {
174
- return new Promise((resolve, reject) => {
175
- if (this.xConnection.readyState !== WebSocket.OPEN) {
176
- reject(new UWError("CONNECTION_NOT_OPENED"));
177
- return;
178
- }
179
- if (this.xConnection.hasResponseHandler) {
180
- reject(new UWError("REQUEST_IN_PROGRESS"));
181
- return;
182
- }
183
- const digest = (0, viem.sha256)((0, viem.toBytes)(args.message));
184
- this.xConnection.setResponseHandler({
185
- resolve: (resp) => {
186
- this.xConnection.setResponseHandler(null);
187
- if (resp.type !== "signature") {
188
- reject(new UWError("INVALID_RESPONSE", `unexpected response type: ${resp.type}`));
189
- return;
190
- }
191
- resolve({
192
- digest,
193
- signature: resp.value
194
- });
195
- },
196
- reject: (err) => {
197
- this.xConnection.setResponseHandler(null);
198
- reject(err);
228
+ async sign(args) {
229
+ return {
230
+ digest: (0, viem.sha256)((0, viem.toBytes)(args.message)),
231
+ signature: await this.request({
232
+ responseType: "signature",
233
+ path: "/x/sign",
234
+ params: {
235
+ message: args.message,
236
+ ticketToken: args.ticketToken
199
237
  }
200
- });
201
- const url = new URL(`${this.unWalletConfig.frontend.baseURL}/x/sign`);
202
- url.searchParams.set("connectionID", this.xConnection.id);
203
- url.searchParams.set("clientID", this.clientID);
204
- url.searchParams.set("message", args.message);
205
- url.searchParams.set("ticketToken", args.ticketToken);
206
- openWindow(url);
207
- });
238
+ })
239
+ };
208
240
  }
209
- signEIP712TypedData(args) {
210
- return new Promise((resolve, reject) => {
211
- if (this.xConnection.readyState !== WebSocket.OPEN) {
212
- reject(new UWError("CONNECTION_NOT_OPENED"));
213
- return;
214
- }
215
- if (this.xConnection.hasResponseHandler) {
216
- reject(new UWError("REQUEST_IN_PROGRESS"));
217
- return;
218
- }
219
- const { EIP712Domain: _, ...typedDataTypes } = args.typedData.types;
220
- const typedData = {
221
- ...args.typedData,
222
- types: typedDataTypes
223
- };
224
- try {
225
- (0, viem.validateTypedData)(typedData);
226
- } catch (e) {
227
- reject(new UWError("INVALID_REQUEST", `invalid typed data: ${e.message}`));
228
- return;
229
- }
230
- const digest = (0, viem.hashTypedData)(typedData);
231
- this.xConnection.setResponseHandler({
232
- resolve: (resp) => {
233
- this.xConnection.setResponseHandler(null);
234
- if (resp.type !== "signature") {
235
- reject(new UWError("INVALID_RESPONSE", `unexpected response type: ${resp.type}`));
236
- return;
237
- }
238
- resolve({
239
- digest,
240
- signature: resp.value
241
- });
242
- },
243
- reject: (err) => {
244
- this.xConnection.setResponseHandler(null);
245
- reject(err);
241
+ async signEIP712TypedData(args) {
242
+ const { EIP712Domain: _, ...typedDataTypes } = args.typedData.types;
243
+ const typedData = {
244
+ ...args.typedData,
245
+ types: typedDataTypes
246
+ };
247
+ try {
248
+ (0, viem.validateTypedData)(typedData);
249
+ } catch (e) {
250
+ throw new UWError("INVALID_REQUEST", `invalid typed data: ${e instanceof viem.BaseError ? e.shortMessage : String(e)}`);
251
+ }
252
+ return {
253
+ digest: (0, viem.hashTypedData)(typedData),
254
+ signature: await this.request({
255
+ responseType: "signature",
256
+ path: "/x/signEIP712TypedData",
257
+ params: {
258
+ typedData: JSON.stringify(args.typedData),
259
+ ticketToken: args.ticketToken
246
260
  }
247
- });
248
- const url = new URL(`${this.unWalletConfig.frontend.baseURL}/x/signEIP712TypedData`);
249
- url.searchParams.set("connectionID", this.xConnection.id);
250
- url.searchParams.set("clientID", this.clientID);
251
- url.searchParams.set("typedData", JSON.stringify(args.typedData));
252
- url.searchParams.set("ticketToken", args.ticketToken);
253
- openWindow(url);
254
- });
261
+ })
262
+ };
255
263
  }
256
- sendTransaction(args) {
264
+ async sendTransaction(args) {
265
+ if (args.value === void 0 && args.data === void 0) throw new UWError("INVALID_REQUEST", "either value or data is required");
266
+ return { transactionID: await this.request({
267
+ responseType: "transactionID",
268
+ path: "/x/sendTransaction",
269
+ params: {
270
+ chainID: args.chainID.toString(),
271
+ toAddress: args.toAddress,
272
+ value: args.value ?? "0x0",
273
+ data: args.data ?? "0x",
274
+ ticketToken: args.ticketToken
275
+ }
276
+ }) };
277
+ }
278
+ request(args) {
257
279
  return new Promise((resolve, reject) => {
258
280
  if (this.xConnection.readyState !== WebSocket.OPEN) {
259
281
  reject(new UWError("CONNECTION_NOT_OPENED"));
@@ -263,42 +285,31 @@ var UnWallet = class UnWallet {
263
285
  reject(new UWError("REQUEST_IN_PROGRESS"));
264
286
  return;
265
287
  }
266
- if (args.value === void 0 && args.data === void 0) {
267
- reject(new UWError("INVALID_REQUEST", "either value or data is required"));
268
- return;
269
- }
270
288
  this.xConnection.setResponseHandler({
271
289
  resolve: (resp) => {
272
290
  this.xConnection.setResponseHandler(null);
273
- if (resp.type !== "transactionID") {
274
- reject(new UWError("INVALID_RESPONSE", `unexpected response type: ${resp.type}`));
291
+ if (resp.type !== args.responseType) {
292
+ reject(newUnexpectedXResponseTypeError(resp));
275
293
  return;
276
294
  }
277
- resolve({ transactionID: resp.value });
295
+ resolve(resp.value);
278
296
  },
279
297
  reject: (err) => {
280
298
  this.xConnection.setResponseHandler(null);
281
299
  reject(err);
282
300
  }
283
301
  });
284
- const url = new URL(`${this.unWalletConfig.frontend.baseURL}/x/sendTransaction`);
302
+ const url = new URL(args.path, this.unWalletConfig.frontend.origin);
285
303
  url.searchParams.set("connectionID", this.xConnection.id);
286
304
  url.searchParams.set("clientID", this.clientID);
287
- url.searchParams.set("chainID", args.chainID.toString());
288
- url.searchParams.set("toAddress", args.toAddress);
289
- url.searchParams.set("value", args.value ?? "0x0");
290
- url.searchParams.set("data", args.data ?? "0x");
291
- url.searchParams.set("ticketToken", args.ticketToken);
292
- openWindow(url);
305
+ for (const [key, value] of Object.entries(args.params)) url.searchParams.set(key, value);
306
+ window.open(url.toString());
293
307
  });
294
308
  }
309
+ close() {
310
+ this.xConnection.close();
311
+ }
295
312
  };
296
- function openWindow(url) {
297
- const width = screen.width / 2;
298
- const height = screen.height;
299
- const left = screen.width / 4;
300
- window.open(url.toString(), "_blank", `width=${width},height=${height},left=${left},top=0`);
301
- }
302
313
  //#endregion
303
314
  exports.UWError = UWError;
304
315
  exports.UnWallet = UnWallet;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,5 @@
1
- import { Address, Hex } from "viem";
1
+ import { Address, Hash, Hex } from "viem";
2
+ import { z } from "zod";
2
3
  //#region src/config.d.ts
3
4
  type Env = "prod" | "dev";
4
5
  interface Config {
@@ -33,77 +34,99 @@ interface EIP712TypedDataField {
33
34
  type UWErrorCode = "CONNECTION_TIMEOUT" | "CONNECTION_FAILED" | "CONNECTION_NOT_OPENED" | "CONNECTION_CLOSED" | "INVALID_REQUEST" | "REQUEST_IN_PROGRESS" | "REQUEST_REJECTED" | "INVALID_RESPONSE";
34
35
  declare class UWError extends Error {
35
36
  readonly code: UWErrorCode;
36
- constructor(code: UWErrorCode, message?: string);
37
+ constructor(code: UWErrorCode, msg?: string);
37
38
  }
38
39
  //#endregion
39
- //#region src/x.d.ts
40
- declare const X_RESPONSE_TYPES: readonly ["connectionID", "signature", "transactionID", "error"];
41
- type XResponseType = (typeof X_RESPONSE_TYPES)[number];
42
- interface XResponse {
43
- readonly type: XResponseType;
44
- readonly value: string;
45
- }
46
- interface XResponseHandler {
47
- resolve: (resp: XResponse) => void;
48
- reject: (err: UWError) => void;
49
- }
40
+ //#region src/xconn.d.ts
41
+ declare const xResponseSchema: z.ZodReadonly<z.ZodUnion<readonly [z.ZodObject<{
42
+ type: z.ZodLiteral<"connectionID">;
43
+ value: z.ZodString;
44
+ }, z.core.$strip>, z.ZodObject<{
45
+ type: z.ZodLiteral<"signature">;
46
+ value: z.ZodString & z.ZodType<`0x${string}`, string, z.core.$ZodTypeInternals<`0x${string}`, string>>;
47
+ }, z.core.$strip>, z.ZodObject<{
48
+ type: z.ZodLiteral<"transactionID">;
49
+ value: z.ZodString;
50
+ }, z.core.$strip>, z.ZodObject<{
51
+ type: z.ZodLiteral<"error">;
52
+ value: z.ZodString;
53
+ }, z.core.$strip>]>>;
54
+ type XResponse = z.infer<typeof xResponseSchema>;
55
+ type XResponseHandler = {
56
+ readonly resolve: (resp: XResponse) => void;
57
+ readonly reject: (err: UWError) => void;
58
+ };
59
+ type XConnectionDebugOptions = {
60
+ readonly handlers?: XConnectionDebugHandlers;
61
+ };
62
+ type XConnectionDebugHandlers = {
63
+ readonly onMessageEventDropped?: (event: MessageEvent) => void;
64
+ readonly onCloseEventDropped?: (event: CloseEvent) => void;
65
+ };
50
66
  declare class XConnection {
51
67
  readonly id: string;
52
- private socket;
68
+ private readonly socket;
69
+ private readonly debugOptions;
53
70
  private responseHandler;
54
71
  constructor(args: {
55
- id: string;
56
- socket: WebSocket;
72
+ readonly id: string;
73
+ readonly socket: WebSocket;
74
+ readonly debugOptions?: XConnectionDebugOptions | undefined;
57
75
  });
58
- static init(config: UnWalletXAPIConfig): Promise<XConnection>;
76
+ static init(config: UnWalletXAPIConfig & {
77
+ readonly debug?: XConnectionDebugOptions | undefined;
78
+ }): Promise<XConnection>;
59
79
  private initListeners;
60
80
  get readyState(): number;
61
81
  get hasResponseHandler(): boolean;
62
- setResponseHandler(handler: XResponseHandler | null): void;
82
+ setResponseHandler(respHandler: XResponseHandler | null): void;
83
+ close(): void;
63
84
  }
64
85
  //#endregion
65
86
  //#region src/unwallet.d.ts
66
87
  type AuthorizationResponseMode = "fragment" | "form_post";
67
88
  interface SignResult {
68
- digest: string;
69
- signature: string;
89
+ digest: Hash;
90
+ signature: Hex;
70
91
  }
71
92
  interface SendTransactionResult {
72
93
  transactionID: string;
73
94
  }
74
95
  declare class UnWallet {
75
- private env;
76
- private clientID;
77
- private xConnection;
96
+ private readonly env;
97
+ private readonly clientID;
98
+ private readonly xConnection;
78
99
  constructor(args: {
79
- env: Env;
80
- clientID: string;
81
- xConnection: XConnection;
100
+ readonly env: Env;
101
+ readonly clientID: string;
102
+ readonly xConnection: XConnection;
82
103
  });
83
104
  static init(config: Config): Promise<UnWallet>;
84
105
  private get unWalletConfig();
85
106
  authorize(args: {
86
- responseMode?: AuthorizationResponseMode;
87
- redirectURL: string;
88
- nonce?: string;
89
- isVirtual?: boolean;
90
- chainID?: number;
107
+ readonly responseMode?: AuthorizationResponseMode;
108
+ readonly redirectURL: string;
109
+ readonly nonce?: string;
110
+ readonly isVirtual?: boolean;
111
+ readonly chainID?: number;
91
112
  }): void;
92
113
  sign(args: {
93
- message: string;
94
- ticketToken: string;
114
+ readonly message: string;
115
+ readonly ticketToken: string;
95
116
  }): Promise<SignResult>;
96
117
  signEIP712TypedData(args: {
97
- typedData: EIP712TypedData;
98
- ticketToken: string;
118
+ readonly typedData: EIP712TypedData;
119
+ readonly ticketToken: string;
99
120
  }): Promise<SignResult>;
100
121
  sendTransaction(args: {
101
- chainID: number;
102
- toAddress: string;
103
- value?: string;
104
- data?: string;
105
- ticketToken: string;
122
+ readonly chainID: number;
123
+ readonly toAddress: string;
124
+ readonly value?: string;
125
+ readonly data?: string;
126
+ readonly ticketToken: string;
106
127
  }): Promise<SendTransactionResult>;
128
+ private request;
129
+ close(): void;
107
130
  }
108
131
  //#endregion
109
132
  export { type AuthorizationResponseMode, type Config, type EIP712TypedData, type EIP712TypedDataDomain, type EIP712TypedDataField, type Env, type SendTransactionResult, type SignResult, UWError, type UWErrorCode, UnWallet };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { Address, Hex } from "viem";
1
+ import { Address, Hash, Hex } from "viem";
2
+ import { z } from "zod";
2
3
  //#region src/config.d.ts
3
4
  type Env = "prod" | "dev";
4
5
  interface Config {
@@ -33,77 +34,99 @@ interface EIP712TypedDataField {
33
34
  type UWErrorCode = "CONNECTION_TIMEOUT" | "CONNECTION_FAILED" | "CONNECTION_NOT_OPENED" | "CONNECTION_CLOSED" | "INVALID_REQUEST" | "REQUEST_IN_PROGRESS" | "REQUEST_REJECTED" | "INVALID_RESPONSE";
34
35
  declare class UWError extends Error {
35
36
  readonly code: UWErrorCode;
36
- constructor(code: UWErrorCode, message?: string);
37
+ constructor(code: UWErrorCode, msg?: string);
37
38
  }
38
39
  //#endregion
39
- //#region src/x.d.ts
40
- declare const X_RESPONSE_TYPES: readonly ["connectionID", "signature", "transactionID", "error"];
41
- type XResponseType = (typeof X_RESPONSE_TYPES)[number];
42
- interface XResponse {
43
- readonly type: XResponseType;
44
- readonly value: string;
45
- }
46
- interface XResponseHandler {
47
- resolve: (resp: XResponse) => void;
48
- reject: (err: UWError) => void;
49
- }
40
+ //#region src/xconn.d.ts
41
+ declare const xResponseSchema: z.ZodReadonly<z.ZodUnion<readonly [z.ZodObject<{
42
+ type: z.ZodLiteral<"connectionID">;
43
+ value: z.ZodString;
44
+ }, z.core.$strip>, z.ZodObject<{
45
+ type: z.ZodLiteral<"signature">;
46
+ value: z.ZodString & z.ZodType<`0x${string}`, string, z.core.$ZodTypeInternals<`0x${string}`, string>>;
47
+ }, z.core.$strip>, z.ZodObject<{
48
+ type: z.ZodLiteral<"transactionID">;
49
+ value: z.ZodString;
50
+ }, z.core.$strip>, z.ZodObject<{
51
+ type: z.ZodLiteral<"error">;
52
+ value: z.ZodString;
53
+ }, z.core.$strip>]>>;
54
+ type XResponse = z.infer<typeof xResponseSchema>;
55
+ type XResponseHandler = {
56
+ readonly resolve: (resp: XResponse) => void;
57
+ readonly reject: (err: UWError) => void;
58
+ };
59
+ type XConnectionDebugOptions = {
60
+ readonly handlers?: XConnectionDebugHandlers;
61
+ };
62
+ type XConnectionDebugHandlers = {
63
+ readonly onMessageEventDropped?: (event: MessageEvent) => void;
64
+ readonly onCloseEventDropped?: (event: CloseEvent) => void;
65
+ };
50
66
  declare class XConnection {
51
67
  readonly id: string;
52
- private socket;
68
+ private readonly socket;
69
+ private readonly debugOptions;
53
70
  private responseHandler;
54
71
  constructor(args: {
55
- id: string;
56
- socket: WebSocket;
72
+ readonly id: string;
73
+ readonly socket: WebSocket;
74
+ readonly debugOptions?: XConnectionDebugOptions | undefined;
57
75
  });
58
- static init(config: UnWalletXAPIConfig): Promise<XConnection>;
76
+ static init(config: UnWalletXAPIConfig & {
77
+ readonly debug?: XConnectionDebugOptions | undefined;
78
+ }): Promise<XConnection>;
59
79
  private initListeners;
60
80
  get readyState(): number;
61
81
  get hasResponseHandler(): boolean;
62
- setResponseHandler(handler: XResponseHandler | null): void;
82
+ setResponseHandler(respHandler: XResponseHandler | null): void;
83
+ close(): void;
63
84
  }
64
85
  //#endregion
65
86
  //#region src/unwallet.d.ts
66
87
  type AuthorizationResponseMode = "fragment" | "form_post";
67
88
  interface SignResult {
68
- digest: string;
69
- signature: string;
89
+ digest: Hash;
90
+ signature: Hex;
70
91
  }
71
92
  interface SendTransactionResult {
72
93
  transactionID: string;
73
94
  }
74
95
  declare class UnWallet {
75
- private env;
76
- private clientID;
77
- private xConnection;
96
+ private readonly env;
97
+ private readonly clientID;
98
+ private readonly xConnection;
78
99
  constructor(args: {
79
- env: Env;
80
- clientID: string;
81
- xConnection: XConnection;
100
+ readonly env: Env;
101
+ readonly clientID: string;
102
+ readonly xConnection: XConnection;
82
103
  });
83
104
  static init(config: Config): Promise<UnWallet>;
84
105
  private get unWalletConfig();
85
106
  authorize(args: {
86
- responseMode?: AuthorizationResponseMode;
87
- redirectURL: string;
88
- nonce?: string;
89
- isVirtual?: boolean;
90
- chainID?: number;
107
+ readonly responseMode?: AuthorizationResponseMode;
108
+ readonly redirectURL: string;
109
+ readonly nonce?: string;
110
+ readonly isVirtual?: boolean;
111
+ readonly chainID?: number;
91
112
  }): void;
92
113
  sign(args: {
93
- message: string;
94
- ticketToken: string;
114
+ readonly message: string;
115
+ readonly ticketToken: string;
95
116
  }): Promise<SignResult>;
96
117
  signEIP712TypedData(args: {
97
- typedData: EIP712TypedData;
98
- ticketToken: string;
118
+ readonly typedData: EIP712TypedData;
119
+ readonly ticketToken: string;
99
120
  }): Promise<SignResult>;
100
121
  sendTransaction(args: {
101
- chainID: number;
102
- toAddress: string;
103
- value?: string;
104
- data?: string;
105
- ticketToken: string;
122
+ readonly chainID: number;
123
+ readonly toAddress: string;
124
+ readonly value?: string;
125
+ readonly data?: string;
126
+ readonly ticketToken: string;
106
127
  }): Promise<SendTransactionResult>;
128
+ private request;
129
+ close(): void;
107
130
  }
108
131
  //#endregion
109
132
  export { type AuthorizationResponseMode, type Config, type EIP712TypedData, type EIP712TypedDataDomain, type EIP712TypedDataField, type Env, type SendTransactionResult, type SignResult, UWError, type UWErrorCode, UnWallet };
package/dist/index.js CHANGED
@@ -1,86 +1,139 @@
1
- import { hashTypedData, sha256, toBytes, validateTypedData } from "viem";
1
+ import { BaseError, hashTypedData, isHex, sha256, toBytes, validateTypedData } from "viem";
2
2
  import { z } from "zod";
3
3
  //#region src/error.ts
4
4
  var UWError = class extends Error {
5
- constructor(code, message) {
6
- super(message !== void 0 ? `${code}: ${message}` : code);
7
- this.code = code;
8
- }
9
5
  static {
10
6
  this.prototype.name = "UWError";
11
7
  }
8
+ constructor(code, msg) {
9
+ super(msg !== void 0 ? `${code}: ${msg}` : code);
10
+ this.code = code;
11
+ }
12
12
  };
13
13
  //#endregion
14
14
  //#region src/config.ts
15
- function getUnWalletConfigByEnv(env) {
16
- switch (env) {
17
- case "prod": return {
18
- frontend: { baseURL: "https://id.unwallet.world" },
19
- xapi: {
20
- url: "wss://xapi.id.unwallet.world",
21
- connectionTimeout: 1e4
22
- }
23
- };
24
- case "dev": return {
25
- frontend: { baseURL: "http://localhost:4200" },
26
- xapi: {
27
- url: "wss://xapi.id.test.unwallet.dev",
28
- connectionTimeout: 1e4
29
- }
30
- };
15
+ const envToUnWalletConfig = {
16
+ prod: {
17
+ frontend: { origin: "https://id.unwallet.world" },
18
+ xAPI: {
19
+ url: "wss://xapi.id.unwallet.world",
20
+ connectionTimeout: 1e4
21
+ }
22
+ },
23
+ dev: {
24
+ frontend: { origin: "http://localhost:4200" },
25
+ xAPI: {
26
+ url: "wss://xapi.id.test.unwallet.dev",
27
+ connectionTimeout: 1e4
28
+ }
31
29
  }
32
- }
33
- //#endregion
34
- //#region src/x.ts
35
- const X_RESPONSE_TYPES = [
36
- "connectionID",
37
- "signature",
38
- "transactionID",
39
- "error"
40
- ];
30
+ };
31
+ const xRequestSchema = z.object({ action: z.enum(["getConnectionID"]) }).readonly();
32
+ z.string().transform((val, ctx) => {
33
+ try {
34
+ return JSON.parse(val);
35
+ } catch {
36
+ ctx.addIssue({
37
+ code: "custom",
38
+ message: "Invalid JSON string"
39
+ });
40
+ return z.NEVER;
41
+ }
42
+ }).pipe(xRequestSchema);
43
+ const xResponseSchema = z.union([
44
+ z.object({
45
+ type: z.literal("connectionID"),
46
+ value: z.string().nonempty()
47
+ }),
48
+ z.object({
49
+ type: z.literal("signature"),
50
+ value: z.string().refine(isHex, { error: "Invalid hex" })
51
+ }),
52
+ z.object({
53
+ type: z.literal("transactionID"),
54
+ value: z.string().nonempty()
55
+ }),
56
+ z.object({
57
+ type: z.literal("error"),
58
+ value: z.string().nonempty()
59
+ })
60
+ ]).readonly();
61
+ const xResponsePayloadSchema = z.string().transform((val, ctx) => {
62
+ try {
63
+ return JSON.parse(val);
64
+ } catch {
65
+ ctx.addIssue({
66
+ code: "custom",
67
+ message: "Invalid JSON string"
68
+ });
69
+ return z.NEVER;
70
+ }
71
+ }).pipe(xResponseSchema);
41
72
  var XConnection = class XConnection {
42
73
  constructor(args) {
43
74
  this.responseHandler = null;
44
- this.socket = args.socket;
45
75
  this.id = args.id;
76
+ this.socket = args.socket;
77
+ this.debugOptions = args.debugOptions ?? null;
46
78
  this.initListeners();
47
79
  }
48
80
  static async init(config) {
49
81
  const socket = new WebSocket(config.url);
50
82
  const id = await new Promise((resolve, reject) => {
51
- setInterval(() => reject(new UWError("CONNECTION_TIMEOUT")), config.connectionTimeout);
83
+ const timeoutID = setTimeout(() => abortHandshake(new UWError("CONNECTION_TIMEOUT")), config.connectionTimeout);
84
+ const detachListeners = () => {
85
+ socket.onopen = null;
86
+ socket.onmessage = null;
87
+ socket.onerror = null;
88
+ socket.onclose = null;
89
+ };
90
+ const completeHandshake = (id) => {
91
+ clearTimeout(timeoutID);
92
+ detachListeners();
93
+ resolve(id);
94
+ };
95
+ const abortHandshake = (err) => {
96
+ clearTimeout(timeoutID);
97
+ detachListeners();
98
+ socket.close();
99
+ reject(err);
100
+ };
52
101
  socket.onopen = () => socket.send(JSON.stringify({ action: "getConnectionID" }));
53
102
  socket.onmessage = (event) => {
54
103
  let resp;
55
104
  {
56
- const result = safeParseMessageEventDataToXResponse(event.data);
105
+ const result = safeParseXResponsePayload(event.data);
57
106
  if (!result.success) {
58
- reject(result.error);
107
+ abortHandshake(result.error);
59
108
  return;
60
109
  }
61
110
  resp = result.data;
62
111
  }
63
112
  if (resp.type !== "connectionID") {
64
- reject(new UWError("INVALID_RESPONSE", `unexpected response type: ${resp.type}`));
113
+ abortHandshake(newUnexpectedXResponseTypeError(resp));
65
114
  return;
66
115
  }
67
- resolve(resp.value);
116
+ completeHandshake(resp.value);
68
117
  };
69
- socket.onerror = () => reject(new UWError("CONNECTION_FAILED"));
70
- socket.onclose = (event) => reject(new UWError("CONNECTION_CLOSED", event.reason));
118
+ socket.onerror = () => abortHandshake(new UWError("CONNECTION_FAILED"));
119
+ socket.onclose = (event) => abortHandshake(newConnectionClosedError(event));
71
120
  });
72
121
  return new XConnection({
73
122
  id,
74
- socket
123
+ socket,
124
+ debugOptions: config.debug
75
125
  });
76
126
  }
77
127
  initListeners() {
78
128
  this.socket.onopen = null;
79
129
  this.socket.onmessage = (event) => {
80
- if (this.responseHandler === null) return;
130
+ if (this.responseHandler === null) {
131
+ this.debugOptions?.handlers?.onMessageEventDropped?.(event);
132
+ return;
133
+ }
81
134
  let resp;
82
135
  {
83
- const result = safeParseMessageEventDataToXResponse(event.data);
136
+ const result = safeParseXResponsePayload(event.data);
84
137
  if (!result.success) {
85
138
  this.responseHandler.reject(result.error);
86
139
  return;
@@ -93,7 +146,7 @@ var XConnection = class XConnection {
93
146
  case "rejected":
94
147
  this.responseHandler.reject(new UWError("REQUEST_REJECTED"));
95
148
  break;
96
- default: this.responseHandler.reject(new UWError("INVALID_RESPONSE", `unexpected error response value: ${resp.value}`));
149
+ default: this.responseHandler.reject(new UWError("INVALID_RESPONSE", `unexpected error value: ${resp.value}`));
97
150
  }
98
151
  break;
99
152
  default: this.responseHandler.resolve(resp);
@@ -101,8 +154,11 @@ var XConnection = class XConnection {
101
154
  };
102
155
  this.socket.onerror = null;
103
156
  this.socket.onclose = (event) => {
104
- if (this.responseHandler === null) return;
105
- this.responseHandler.reject(new UWError("CONNECTION_CLOSED", event.reason));
157
+ if (this.responseHandler === null) {
158
+ this.debugOptions?.handlers?.onCloseEventDropped?.(event);
159
+ return;
160
+ }
161
+ this.responseHandler.reject(newConnectionClosedError(event));
106
162
  };
107
163
  }
108
164
  get readyState() {
@@ -111,34 +167,33 @@ var XConnection = class XConnection {
111
167
  get hasResponseHandler() {
112
168
  return this.responseHandler !== null;
113
169
  }
114
- setResponseHandler(handler) {
115
- this.responseHandler = handler;
170
+ setResponseHandler(respHandler) {
171
+ this.responseHandler = respHandler;
172
+ }
173
+ close() {
174
+ this.socket.close();
116
175
  }
117
176
  };
118
- function safeParseMessageEventDataToXResponse(data) {
119
- const result = z.string().refine((val) => {
120
- try {
121
- JSON.parse(val);
122
- } catch (e) {
123
- return false;
124
- }
125
- return true;
126
- }, {
127
- abort: true,
128
- message: "Invalid JSON string"
129
- }).transform((val) => JSON.parse(val)).pipe(z.object({
130
- type: z.enum(X_RESPONSE_TYPES),
131
- value: z.string()
132
- })).safeParse(data);
177
+ function safeParseXResponsePayload(data) {
178
+ const result = xResponsePayloadSchema.safeParse(data);
133
179
  if (!result.success) return {
134
180
  success: false,
135
- error: new UWError("INVALID_RESPONSE", `invalid message event data: ${result.error.message}`)
181
+ error: new UWError("INVALID_RESPONSE", `invalid payload: ${z.prettifyError(result.error)}`)
136
182
  };
137
183
  return {
138
184
  success: true,
139
185
  data: result.data
140
186
  };
141
187
  }
188
+ function newUnexpectedXResponseTypeError(resp) {
189
+ const msgs = [];
190
+ msgs.push(`unexpected type: ${resp.type}`);
191
+ if (resp.type === "error") msgs.push(`(value: ${resp.value})`);
192
+ return new UWError("INVALID_RESPONSE", msgs.join(" "));
193
+ }
194
+ function newConnectionClosedError(event) {
195
+ return new UWError("CONNECTION_CLOSED", event.reason.length > 0 ? event.reason : void 0);
196
+ }
142
197
  //#endregion
143
198
  //#region src/unwallet.ts
144
199
  var UnWallet = class UnWallet {
@@ -152,14 +207,14 @@ var UnWallet = class UnWallet {
152
207
  return new UnWallet({
153
208
  env,
154
209
  clientID: config.clientID,
155
- xConnection: await XConnection.init(getUnWalletConfigByEnv(env).xapi)
210
+ xConnection: await XConnection.init(envToUnWalletConfig[env].xAPI)
156
211
  });
157
212
  }
158
213
  get unWalletConfig() {
159
- return getUnWalletConfigByEnv(this.env);
214
+ return envToUnWalletConfig[this.env];
160
215
  }
161
216
  authorize(args) {
162
- const url = new URL(`${this.unWalletConfig.frontend.baseURL}/${args.isVirtual !== void 0 ? args.isVirtual ? "v" : "" : "v"}authorize`);
217
+ const url = new URL(`/${args.isVirtual === false ? "" : "v"}authorize`, this.unWalletConfig.frontend.origin);
163
218
  url.searchParams.set("response_type", "id_token");
164
219
  url.searchParams.set("response_mode", args.responseMode ?? "fragment");
165
220
  url.searchParams.set("client_id", this.clientID);
@@ -169,90 +224,57 @@ var UnWallet = class UnWallet {
169
224
  if (args.chainID !== void 0) url.searchParams.set("chain_id", args.chainID.toString());
170
225
  location.assign(url);
171
226
  }
172
- sign(args) {
173
- return new Promise((resolve, reject) => {
174
- if (this.xConnection.readyState !== WebSocket.OPEN) {
175
- reject(new UWError("CONNECTION_NOT_OPENED"));
176
- return;
177
- }
178
- if (this.xConnection.hasResponseHandler) {
179
- reject(new UWError("REQUEST_IN_PROGRESS"));
180
- return;
181
- }
182
- const digest = sha256(toBytes(args.message));
183
- this.xConnection.setResponseHandler({
184
- resolve: (resp) => {
185
- this.xConnection.setResponseHandler(null);
186
- if (resp.type !== "signature") {
187
- reject(new UWError("INVALID_RESPONSE", `unexpected response type: ${resp.type}`));
188
- return;
189
- }
190
- resolve({
191
- digest,
192
- signature: resp.value
193
- });
194
- },
195
- reject: (err) => {
196
- this.xConnection.setResponseHandler(null);
197
- reject(err);
227
+ async sign(args) {
228
+ return {
229
+ digest: sha256(toBytes(args.message)),
230
+ signature: await this.request({
231
+ responseType: "signature",
232
+ path: "/x/sign",
233
+ params: {
234
+ message: args.message,
235
+ ticketToken: args.ticketToken
198
236
  }
199
- });
200
- const url = new URL(`${this.unWalletConfig.frontend.baseURL}/x/sign`);
201
- url.searchParams.set("connectionID", this.xConnection.id);
202
- url.searchParams.set("clientID", this.clientID);
203
- url.searchParams.set("message", args.message);
204
- url.searchParams.set("ticketToken", args.ticketToken);
205
- openWindow(url);
206
- });
237
+ })
238
+ };
207
239
  }
208
- signEIP712TypedData(args) {
209
- return new Promise((resolve, reject) => {
210
- if (this.xConnection.readyState !== WebSocket.OPEN) {
211
- reject(new UWError("CONNECTION_NOT_OPENED"));
212
- return;
213
- }
214
- if (this.xConnection.hasResponseHandler) {
215
- reject(new UWError("REQUEST_IN_PROGRESS"));
216
- return;
217
- }
218
- const { EIP712Domain: _, ...typedDataTypes } = args.typedData.types;
219
- const typedData = {
220
- ...args.typedData,
221
- types: typedDataTypes
222
- };
223
- try {
224
- validateTypedData(typedData);
225
- } catch (e) {
226
- reject(new UWError("INVALID_REQUEST", `invalid typed data: ${e.message}`));
227
- return;
228
- }
229
- const digest = hashTypedData(typedData);
230
- this.xConnection.setResponseHandler({
231
- resolve: (resp) => {
232
- this.xConnection.setResponseHandler(null);
233
- if (resp.type !== "signature") {
234
- reject(new UWError("INVALID_RESPONSE", `unexpected response type: ${resp.type}`));
235
- return;
236
- }
237
- resolve({
238
- digest,
239
- signature: resp.value
240
- });
241
- },
242
- reject: (err) => {
243
- this.xConnection.setResponseHandler(null);
244
- reject(err);
240
+ async signEIP712TypedData(args) {
241
+ const { EIP712Domain: _, ...typedDataTypes } = args.typedData.types;
242
+ const typedData = {
243
+ ...args.typedData,
244
+ types: typedDataTypes
245
+ };
246
+ try {
247
+ validateTypedData(typedData);
248
+ } catch (e) {
249
+ throw new UWError("INVALID_REQUEST", `invalid typed data: ${e instanceof BaseError ? e.shortMessage : String(e)}`);
250
+ }
251
+ return {
252
+ digest: hashTypedData(typedData),
253
+ signature: await this.request({
254
+ responseType: "signature",
255
+ path: "/x/signEIP712TypedData",
256
+ params: {
257
+ typedData: JSON.stringify(args.typedData),
258
+ ticketToken: args.ticketToken
245
259
  }
246
- });
247
- const url = new URL(`${this.unWalletConfig.frontend.baseURL}/x/signEIP712TypedData`);
248
- url.searchParams.set("connectionID", this.xConnection.id);
249
- url.searchParams.set("clientID", this.clientID);
250
- url.searchParams.set("typedData", JSON.stringify(args.typedData));
251
- url.searchParams.set("ticketToken", args.ticketToken);
252
- openWindow(url);
253
- });
260
+ })
261
+ };
254
262
  }
255
- sendTransaction(args) {
263
+ async sendTransaction(args) {
264
+ if (args.value === void 0 && args.data === void 0) throw new UWError("INVALID_REQUEST", "either value or data is required");
265
+ return { transactionID: await this.request({
266
+ responseType: "transactionID",
267
+ path: "/x/sendTransaction",
268
+ params: {
269
+ chainID: args.chainID.toString(),
270
+ toAddress: args.toAddress,
271
+ value: args.value ?? "0x0",
272
+ data: args.data ?? "0x",
273
+ ticketToken: args.ticketToken
274
+ }
275
+ }) };
276
+ }
277
+ request(args) {
256
278
  return new Promise((resolve, reject) => {
257
279
  if (this.xConnection.readyState !== WebSocket.OPEN) {
258
280
  reject(new UWError("CONNECTION_NOT_OPENED"));
@@ -262,41 +284,30 @@ var UnWallet = class UnWallet {
262
284
  reject(new UWError("REQUEST_IN_PROGRESS"));
263
285
  return;
264
286
  }
265
- if (args.value === void 0 && args.data === void 0) {
266
- reject(new UWError("INVALID_REQUEST", "either value or data is required"));
267
- return;
268
- }
269
287
  this.xConnection.setResponseHandler({
270
288
  resolve: (resp) => {
271
289
  this.xConnection.setResponseHandler(null);
272
- if (resp.type !== "transactionID") {
273
- reject(new UWError("INVALID_RESPONSE", `unexpected response type: ${resp.type}`));
290
+ if (resp.type !== args.responseType) {
291
+ reject(newUnexpectedXResponseTypeError(resp));
274
292
  return;
275
293
  }
276
- resolve({ transactionID: resp.value });
294
+ resolve(resp.value);
277
295
  },
278
296
  reject: (err) => {
279
297
  this.xConnection.setResponseHandler(null);
280
298
  reject(err);
281
299
  }
282
300
  });
283
- const url = new URL(`${this.unWalletConfig.frontend.baseURL}/x/sendTransaction`);
301
+ const url = new URL(args.path, this.unWalletConfig.frontend.origin);
284
302
  url.searchParams.set("connectionID", this.xConnection.id);
285
303
  url.searchParams.set("clientID", this.clientID);
286
- url.searchParams.set("chainID", args.chainID.toString());
287
- url.searchParams.set("toAddress", args.toAddress);
288
- url.searchParams.set("value", args.value ?? "0x0");
289
- url.searchParams.set("data", args.data ?? "0x");
290
- url.searchParams.set("ticketToken", args.ticketToken);
291
- openWindow(url);
304
+ for (const [key, value] of Object.entries(args.params)) url.searchParams.set(key, value);
305
+ window.open(url.toString());
292
306
  });
293
307
  }
308
+ close() {
309
+ this.xConnection.close();
310
+ }
294
311
  };
295
- function openWindow(url) {
296
- const width = screen.width / 2;
297
- const height = screen.height;
298
- const left = screen.width / 4;
299
- window.open(url.toString(), "_blank", `width=${width},height=${height},left=${left},top=0`);
300
- }
301
312
  //#endregion
302
313
  export { UWError, UnWallet };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unwallet-client-sdk",
3
- "version": "0.20.1",
3
+ "version": "0.22.0",
4
4
  "description": "unWallet client-side SDK",
5
5
  "license": "MIT",
6
6
  "author": "SIVIRA",
@@ -29,7 +29,7 @@
29
29
  "url": "git+https://github.com/SIVIRA/unwallet-client-sdk-js.git"
30
30
  },
31
31
  "dependencies": {
32
- "viem": "2.55.10",
32
+ "viem": "2.55.11",
33
33
  "zod": "4.4.3"
34
34
  },
35
35
  "devDependencies": {
@@ -37,10 +37,14 @@
37
37
  "@commitlint/config-conventional": "21.2.0",
38
38
  "@commitlint/types": "21.2.0",
39
39
  "@tsconfig/strictest": "2.0.8",
40
+ "@vitest/coverage-v8": "4.1.10",
40
41
  "czg": "1.13.1",
41
42
  "husky": "9.1.7",
42
- "publint": "0.3.22",
43
- "tsdown": "0.22.14"
43
+ "msw": "2.15.0",
44
+ "publint": "0.3.23",
45
+ "tsdown": "0.22.14",
46
+ "typescript": "7.0.2",
47
+ "vitest": "4.1.10"
44
48
  },
45
49
  "scripts": {}
46
50
  }