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 +2 -0
- package/dist/index.cjs +175 -164
- package/dist/index.d.cts +63 -40
- package/dist/index.d.ts +63 -40
- package/dist/index.js +176 -165
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# unWallet client-side SDK
|
|
2
2
|
|
|
3
3
|
[](https://npm.im/unwallet-client-sdk)
|
|
4
|
+
[](https://github.com/SIVIRA/unwallet-client-sdk-js/actions/workflows/test.yaml)
|
|
5
|
+
[](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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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 =
|
|
106
|
+
const result = safeParseXResponsePayload(event.data);
|
|
58
107
|
if (!result.success) {
|
|
59
|
-
|
|
108
|
+
abortHandshake(result.error);
|
|
60
109
|
return;
|
|
61
110
|
}
|
|
62
111
|
resp = result.data;
|
|
63
112
|
}
|
|
64
113
|
if (resp.type !== "connectionID") {
|
|
65
|
-
|
|
114
|
+
abortHandshake(newUnexpectedXResponseTypeError(resp));
|
|
66
115
|
return;
|
|
67
116
|
}
|
|
68
|
-
|
|
117
|
+
completeHandshake(resp.value);
|
|
69
118
|
};
|
|
70
|
-
socket.onerror = () =>
|
|
71
|
-
socket.onclose = (event) =>
|
|
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)
|
|
131
|
+
if (this.responseHandler === null) {
|
|
132
|
+
this.debugOptions?.handlers?.onMessageEventDropped?.(event);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
82
135
|
let resp;
|
|
83
136
|
{
|
|
84
|
-
const result =
|
|
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
|
|
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)
|
|
106
|
-
|
|
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(
|
|
116
|
-
this.responseHandler =
|
|
171
|
+
setResponseHandler(respHandler) {
|
|
172
|
+
this.responseHandler = respHandler;
|
|
173
|
+
}
|
|
174
|
+
close() {
|
|
175
|
+
this.socket.close();
|
|
117
176
|
}
|
|
118
177
|
};
|
|
119
|
-
function
|
|
120
|
-
const result =
|
|
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
|
|
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(
|
|
211
|
+
xConnection: await XConnection.init(envToUnWalletConfig[env].xAPI)
|
|
157
212
|
});
|
|
158
213
|
}
|
|
159
214
|
get unWalletConfig() {
|
|
160
|
-
return
|
|
215
|
+
return envToUnWalletConfig[this.env];
|
|
161
216
|
}
|
|
162
217
|
authorize(args) {
|
|
163
|
-
const url = new URL(
|
|
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
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
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 !==
|
|
274
|
-
reject(
|
|
291
|
+
if (resp.type !== args.responseType) {
|
|
292
|
+
reject(newUnexpectedXResponseTypeError(resp));
|
|
275
293
|
return;
|
|
276
294
|
}
|
|
277
|
-
resolve(
|
|
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(
|
|
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(
|
|
288
|
-
url.
|
|
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,
|
|
37
|
+
constructor(code: UWErrorCode, msg?: string);
|
|
37
38
|
}
|
|
38
39
|
//#endregion
|
|
39
|
-
//#region src/
|
|
40
|
-
declare const
|
|
41
|
-
type
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
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(
|
|
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:
|
|
69
|
-
signature:
|
|
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,
|
|
37
|
+
constructor(code: UWErrorCode, msg?: string);
|
|
37
38
|
}
|
|
38
39
|
//#endregion
|
|
39
|
-
//#region src/
|
|
40
|
-
declare const
|
|
41
|
-
type
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
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(
|
|
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:
|
|
69
|
-
signature:
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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 =
|
|
105
|
+
const result = safeParseXResponsePayload(event.data);
|
|
57
106
|
if (!result.success) {
|
|
58
|
-
|
|
107
|
+
abortHandshake(result.error);
|
|
59
108
|
return;
|
|
60
109
|
}
|
|
61
110
|
resp = result.data;
|
|
62
111
|
}
|
|
63
112
|
if (resp.type !== "connectionID") {
|
|
64
|
-
|
|
113
|
+
abortHandshake(newUnexpectedXResponseTypeError(resp));
|
|
65
114
|
return;
|
|
66
115
|
}
|
|
67
|
-
|
|
116
|
+
completeHandshake(resp.value);
|
|
68
117
|
};
|
|
69
|
-
socket.onerror = () =>
|
|
70
|
-
socket.onclose = (event) =>
|
|
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)
|
|
130
|
+
if (this.responseHandler === null) {
|
|
131
|
+
this.debugOptions?.handlers?.onMessageEventDropped?.(event);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
81
134
|
let resp;
|
|
82
135
|
{
|
|
83
|
-
const result =
|
|
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
|
|
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)
|
|
105
|
-
|
|
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(
|
|
115
|
-
this.responseHandler =
|
|
170
|
+
setResponseHandler(respHandler) {
|
|
171
|
+
this.responseHandler = respHandler;
|
|
172
|
+
}
|
|
173
|
+
close() {
|
|
174
|
+
this.socket.close();
|
|
116
175
|
}
|
|
117
176
|
};
|
|
118
|
-
function
|
|
119
|
-
const result =
|
|
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
|
|
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(
|
|
210
|
+
xConnection: await XConnection.init(envToUnWalletConfig[env].xAPI)
|
|
156
211
|
});
|
|
157
212
|
}
|
|
158
213
|
get unWalletConfig() {
|
|
159
|
-
return
|
|
214
|
+
return envToUnWalletConfig[this.env];
|
|
160
215
|
}
|
|
161
216
|
authorize(args) {
|
|
162
|
-
const url = new URL(
|
|
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
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
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 !==
|
|
273
|
-
reject(
|
|
290
|
+
if (resp.type !== args.responseType) {
|
|
291
|
+
reject(newUnexpectedXResponseTypeError(resp));
|
|
274
292
|
return;
|
|
275
293
|
}
|
|
276
|
-
resolve(
|
|
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(
|
|
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(
|
|
287
|
-
url.
|
|
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.
|
|
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.
|
|
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
|
-
"
|
|
43
|
-
"
|
|
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
|
}
|