@fivenorth/loop-sdk 0.9.0 → 0.11.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 +16 -7
- package/dist/connection.d.ts +40 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/errors.d.ts +13 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/extensions/usdc/index.d.ts +11 -0
- package/dist/extensions/usdc/index.d.ts.map +1 -0
- package/dist/extensions/usdc/types.d.ts +25 -0
- package/dist/extensions/usdc/types.d.ts.map +1 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +391 -260
- package/dist/provider.d.ts +61 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/server/index.d.ts +23 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +35344 -0
- package/dist/server/signer.d.ts +15 -0
- package/dist/server/signer.d.ts.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/session.d.ts +39 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/types.d.ts +116 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/wallet.d.ts +10 -0
- package/dist/wallet.d.ts.map +1 -0
- package/package.json +17 -3
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ var __create = Object.create;
|
|
|
2
2
|
var __getProtoOf = Object.getPrototypeOf;
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
7
|
var __toESM = (mod, isNodeMode, target) => {
|
|
7
8
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
@@ -14,7 +15,38 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
14
15
|
});
|
|
15
16
|
return to;
|
|
16
17
|
};
|
|
18
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
19
|
+
var __toCommonJS = (from) => {
|
|
20
|
+
var entry = __moduleCache.get(from), desc;
|
|
21
|
+
if (entry)
|
|
22
|
+
return entry;
|
|
23
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
24
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
25
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
26
|
+
get: () => from[key],
|
|
27
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
28
|
+
}));
|
|
29
|
+
__moduleCache.set(from, entry);
|
|
30
|
+
return entry;
|
|
31
|
+
};
|
|
17
32
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
var __export = (target, all) => {
|
|
34
|
+
for (var name in all)
|
|
35
|
+
__defProp(target, name, {
|
|
36
|
+
get: all[name],
|
|
37
|
+
enumerable: true,
|
|
38
|
+
configurable: true,
|
|
39
|
+
set: (newValue) => all[name] = () => newValue
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
43
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
44
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
45
|
+
}) : x)(function(x) {
|
|
46
|
+
if (typeof require !== "undefined")
|
|
47
|
+
return require.apply(this, arguments);
|
|
48
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
49
|
+
});
|
|
18
50
|
|
|
19
51
|
// node_modules/qrcode/lib/can-promise.js
|
|
20
52
|
var require_can_promise = __commonJS((exports, module) => {
|
|
@@ -2092,229 +2124,6 @@ function isUnauthCode(code) {
|
|
|
2092
2124
|
return UNAUTH_CODES.has(code);
|
|
2093
2125
|
}
|
|
2094
2126
|
|
|
2095
|
-
// src/connection.ts
|
|
2096
|
-
class Connection {
|
|
2097
|
-
walletUrl = "https://cantonloop.com";
|
|
2098
|
-
apiUrl = "https://cantonloop.com";
|
|
2099
|
-
ws = null;
|
|
2100
|
-
network = "main";
|
|
2101
|
-
ticketId = null;
|
|
2102
|
-
onMessageHandler = null;
|
|
2103
|
-
reconnectPromise = null;
|
|
2104
|
-
status = "disconnected";
|
|
2105
|
-
constructor({ network, walletUrl, apiUrl }) {
|
|
2106
|
-
this.network = network || "main";
|
|
2107
|
-
switch (this.network) {
|
|
2108
|
-
case "local":
|
|
2109
|
-
this.walletUrl = "http://localhost:3000";
|
|
2110
|
-
this.apiUrl = "http://localhost:8080";
|
|
2111
|
-
break;
|
|
2112
|
-
case "devnet":
|
|
2113
|
-
case "dev":
|
|
2114
|
-
this.walletUrl = "https://devnet.cantonloop.com";
|
|
2115
|
-
this.apiUrl = "https://devnet.cantonloop.com";
|
|
2116
|
-
break;
|
|
2117
|
-
case "testnet":
|
|
2118
|
-
case "test":
|
|
2119
|
-
this.walletUrl = "https://testnet.cantonloop.com";
|
|
2120
|
-
this.apiUrl = "https://testnet.cantonloop.com";
|
|
2121
|
-
break;
|
|
2122
|
-
case "mainnet":
|
|
2123
|
-
case "main":
|
|
2124
|
-
this.walletUrl = "https://cantonloop.com";
|
|
2125
|
-
this.apiUrl = "https://cantonloop.com";
|
|
2126
|
-
break;
|
|
2127
|
-
}
|
|
2128
|
-
if (walletUrl) {
|
|
2129
|
-
this.walletUrl = walletUrl;
|
|
2130
|
-
}
|
|
2131
|
-
if (apiUrl) {
|
|
2132
|
-
this.apiUrl = apiUrl;
|
|
2133
|
-
}
|
|
2134
|
-
}
|
|
2135
|
-
connectInProgress() {
|
|
2136
|
-
return this.status === "connecting" || this.status === "connected";
|
|
2137
|
-
}
|
|
2138
|
-
async getTicket(appName, sessionId, version) {
|
|
2139
|
-
const response = await fetch(`${this.apiUrl}/api/v1/.connect/pair/tickets`, {
|
|
2140
|
-
method: "POST",
|
|
2141
|
-
headers: {
|
|
2142
|
-
"Content-Type": "application/json"
|
|
2143
|
-
},
|
|
2144
|
-
body: JSON.stringify({
|
|
2145
|
-
app_name: appName,
|
|
2146
|
-
session_id: sessionId,
|
|
2147
|
-
version
|
|
2148
|
-
})
|
|
2149
|
-
});
|
|
2150
|
-
if (!response.ok) {
|
|
2151
|
-
throw new Error("Failed to get ticket from server.");
|
|
2152
|
-
}
|
|
2153
|
-
return response.json();
|
|
2154
|
-
}
|
|
2155
|
-
async getHolding(authToken) {
|
|
2156
|
-
const response = await fetch(`${this.apiUrl}/api/v1/.connect/pair/account/holding`, {
|
|
2157
|
-
method: "GET",
|
|
2158
|
-
headers: {
|
|
2159
|
-
"Content-Type": "application/json",
|
|
2160
|
-
Authorization: `Bearer ${authToken}`
|
|
2161
|
-
}
|
|
2162
|
-
});
|
|
2163
|
-
if (!response.ok) {
|
|
2164
|
-
throw new Error("Failed to get holdings.");
|
|
2165
|
-
}
|
|
2166
|
-
return response.json();
|
|
2167
|
-
}
|
|
2168
|
-
async getActiveContracts(authToken, params) {
|
|
2169
|
-
const url = new URL(`${this.apiUrl}/api/v1/.connect/pair/account/active-contracts`);
|
|
2170
|
-
if (params?.templateId) {
|
|
2171
|
-
url.searchParams.append("templateId", params.templateId);
|
|
2172
|
-
}
|
|
2173
|
-
if (params?.interfaceId) {
|
|
2174
|
-
url.searchParams.append("interfaceId", params.interfaceId);
|
|
2175
|
-
}
|
|
2176
|
-
const response = await fetch(url.toString(), {
|
|
2177
|
-
method: "GET",
|
|
2178
|
-
headers: {
|
|
2179
|
-
"Content-Type": "application/json",
|
|
2180
|
-
Authorization: `Bearer ${authToken}`
|
|
2181
|
-
}
|
|
2182
|
-
});
|
|
2183
|
-
if (!response.ok) {
|
|
2184
|
-
throw new Error("Failed to get active contracts.");
|
|
2185
|
-
}
|
|
2186
|
-
return response.json();
|
|
2187
|
-
}
|
|
2188
|
-
async prepareTransfer(authToken, params) {
|
|
2189
|
-
const payload = {
|
|
2190
|
-
recipient: params.recipient,
|
|
2191
|
-
amount: params.amount
|
|
2192
|
-
};
|
|
2193
|
-
if (params.instrument) {
|
|
2194
|
-
if (params.instrument.instrument_admin) {
|
|
2195
|
-
payload.instrument_admin = params.instrument.instrument_admin;
|
|
2196
|
-
}
|
|
2197
|
-
if (params.instrument.instrument_id) {
|
|
2198
|
-
payload.instrument_id = params.instrument.instrument_id;
|
|
2199
|
-
}
|
|
2200
|
-
}
|
|
2201
|
-
if (params.requested_at) {
|
|
2202
|
-
payload.requested_at = params.requested_at;
|
|
2203
|
-
}
|
|
2204
|
-
if (params.execute_before) {
|
|
2205
|
-
payload.execute_before = params.execute_before;
|
|
2206
|
-
}
|
|
2207
|
-
const response = await fetch(`${this.apiUrl}/api/v1/.connect/pair/transfer`, {
|
|
2208
|
-
method: "POST",
|
|
2209
|
-
headers: {
|
|
2210
|
-
"Content-Type": "application/json",
|
|
2211
|
-
Authorization: `Bearer ${authToken}`
|
|
2212
|
-
},
|
|
2213
|
-
body: JSON.stringify(payload)
|
|
2214
|
-
});
|
|
2215
|
-
if (!response.ok) {
|
|
2216
|
-
throw new Error("Failed to prepare transfer.");
|
|
2217
|
-
}
|
|
2218
|
-
const data = await response.json();
|
|
2219
|
-
return data.payload;
|
|
2220
|
-
}
|
|
2221
|
-
async verifySession(authToken) {
|
|
2222
|
-
const response = await fetch(`${this.apiUrl}/api/v1/.connect/pair/account`, {
|
|
2223
|
-
method: "GET",
|
|
2224
|
-
headers: {
|
|
2225
|
-
"Content-Type": "application/json",
|
|
2226
|
-
Authorization: `Bearer ${authToken}`
|
|
2227
|
-
}
|
|
2228
|
-
});
|
|
2229
|
-
if (!response.ok) {
|
|
2230
|
-
if (response.status === 401 || response.status === 403) {
|
|
2231
|
-
throw new UnauthorizedError;
|
|
2232
|
-
}
|
|
2233
|
-
throw new Error(`Session verification failed with status ${response.status}.`);
|
|
2234
|
-
}
|
|
2235
|
-
const data = await response.json();
|
|
2236
|
-
const email = data?.email;
|
|
2237
|
-
if (!data?.party_id || !data?.public_key) {
|
|
2238
|
-
throw new Error("Invalid session verification response.");
|
|
2239
|
-
}
|
|
2240
|
-
const account = {
|
|
2241
|
-
party_id: data?.party_id,
|
|
2242
|
-
auth_token: authToken,
|
|
2243
|
-
public_key: data?.public_key,
|
|
2244
|
-
email,
|
|
2245
|
-
has_preapproval: data?.has_preapproval,
|
|
2246
|
-
has_merge_delegation: data?.has_merge_delegation,
|
|
2247
|
-
usdc_bridge_access: data?.usdc_bridge_access
|
|
2248
|
-
};
|
|
2249
|
-
return account;
|
|
2250
|
-
}
|
|
2251
|
-
connectWebSocket(ticketId, onMessage) {
|
|
2252
|
-
if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) && this.ticketId !== ticketId) {
|
|
2253
|
-
this.ws.close();
|
|
2254
|
-
this.ws = null;
|
|
2255
|
-
}
|
|
2256
|
-
if (this.status === "connecting" || this.status === "connected") {
|
|
2257
|
-
return;
|
|
2258
|
-
}
|
|
2259
|
-
this.onMessageHandler = onMessage;
|
|
2260
|
-
this.ticketId = ticketId;
|
|
2261
|
-
this.status = "connecting";
|
|
2262
|
-
this.attachWebSocket(ticketId, onMessage);
|
|
2263
|
-
}
|
|
2264
|
-
reconnect() {
|
|
2265
|
-
if (!this.ticketId || !this.onMessageHandler) {
|
|
2266
|
-
return Promise.reject(new Error("Cannot reconnect without a known ticket."));
|
|
2267
|
-
}
|
|
2268
|
-
return new Promise((resolve, reject) => {
|
|
2269
|
-
let opened = false;
|
|
2270
|
-
this.attachWebSocket(this.ticketId, this.onMessageHandler, () => {
|
|
2271
|
-
opened = true;
|
|
2272
|
-
resolve();
|
|
2273
|
-
}, () => {
|
|
2274
|
-
if (opened) {
|
|
2275
|
-
return;
|
|
2276
|
-
}
|
|
2277
|
-
reject(new Error("Failed to reconnect to ticket server."));
|
|
2278
|
-
}, () => {
|
|
2279
|
-
if (opened) {
|
|
2280
|
-
return;
|
|
2281
|
-
}
|
|
2282
|
-
reject(new Error("Failed to reconnect to ticket server."));
|
|
2283
|
-
});
|
|
2284
|
-
});
|
|
2285
|
-
}
|
|
2286
|
-
websocketUrl(ticketId) {
|
|
2287
|
-
return `${this.network === "local" ? "ws" : "wss"}://${this.apiUrl.replace("https://", "").replace("http://", "")}/api/v1/.connect/pair/ws/${encodeURIComponent(ticketId)}`;
|
|
2288
|
-
}
|
|
2289
|
-
attachWebSocket(ticketId, onMessage, onOpen, onError, onClose) {
|
|
2290
|
-
const wsUrl = this.websocketUrl(ticketId);
|
|
2291
|
-
const ws = new WebSocket(wsUrl);
|
|
2292
|
-
ws.onmessage = onMessage;
|
|
2293
|
-
ws.onopen = () => {
|
|
2294
|
-
this.status = "connected";
|
|
2295
|
-
console.log("[LoopSDK] Connected to ticket server.");
|
|
2296
|
-
onOpen?.();
|
|
2297
|
-
};
|
|
2298
|
-
ws.onclose = (event) => {
|
|
2299
|
-
this.status = "disconnected";
|
|
2300
|
-
if (this.ws === ws) {
|
|
2301
|
-
this.ws = null;
|
|
2302
|
-
}
|
|
2303
|
-
console.log("[LoopSDK] Disconnected from ticket server.");
|
|
2304
|
-
onClose?.(event);
|
|
2305
|
-
};
|
|
2306
|
-
ws.onerror = (event) => {
|
|
2307
|
-
this.status = "disconnected";
|
|
2308
|
-
ws.close();
|
|
2309
|
-
if (this.ws === ws) {
|
|
2310
|
-
this.ws = null;
|
|
2311
|
-
}
|
|
2312
|
-
onError?.(event);
|
|
2313
|
-
};
|
|
2314
|
-
this.ws = ws;
|
|
2315
|
-
}
|
|
2316
|
-
}
|
|
2317
|
-
|
|
2318
2127
|
// src/types.ts
|
|
2319
2128
|
var MessageType;
|
|
2320
2129
|
((MessageType2) => {
|
|
@@ -2396,14 +2205,18 @@ class Provider {
|
|
|
2396
2205
|
return this.connection.getActiveContracts(this.auth_token, params);
|
|
2397
2206
|
}
|
|
2398
2207
|
async submitTransaction(payload, options) {
|
|
2399
|
-
|
|
2208
|
+
const requestPayload = options?.estimateTraffic ? { ...payload, estimate_traffic: true } : payload;
|
|
2209
|
+
const executionMode = options?.executionMode;
|
|
2210
|
+
const finalPayload = executionMode === "wait" ? { ...requestPayload, execution_mode: "wait" } : requestPayload;
|
|
2211
|
+
return this.sendRequest("run_transaction" /* RUN_TRANSACTION */, finalPayload, options);
|
|
2400
2212
|
}
|
|
2401
2213
|
async submitAndWaitForTransaction(payload, options) {
|
|
2402
|
-
|
|
2214
|
+
const requestPayload = options?.estimateTraffic ? { ...payload, estimate_traffic: true } : payload;
|
|
2215
|
+
return this.sendRequest("run_transaction" /* RUN_TRANSACTION */, { ...requestPayload, execution_mode: "wait" }, options);
|
|
2403
2216
|
}
|
|
2404
2217
|
async transfer(recipient, amount, instrument, options) {
|
|
2405
2218
|
const amountStr = typeof amount === "number" ? amount.toString() : amount;
|
|
2406
|
-
const { requestedAt, executeBefore, requestTimeout } = options || {};
|
|
2219
|
+
const { requestedAt, executeBefore, requestTimeout, estimateTraffic, memo } = options || {};
|
|
2407
2220
|
const message = options?.message;
|
|
2408
2221
|
const resolveDate = (value, fallbackMs) => {
|
|
2409
2222
|
if (value instanceof Date) {
|
|
@@ -2429,6 +2242,9 @@ class Provider {
|
|
|
2429
2242
|
requested_at: requestedAtIso,
|
|
2430
2243
|
execute_before: executeBeforeIso
|
|
2431
2244
|
};
|
|
2245
|
+
if (memo) {
|
|
2246
|
+
transferRequest.memo = memo;
|
|
2247
|
+
}
|
|
2432
2248
|
const preparedPayload = await this.connection.prepareTransfer(this.auth_token, transferRequest);
|
|
2433
2249
|
const submitFn = options?.executionMode === "wait" ? this.submitAndWaitForTransaction.bind(this) : this.submitTransaction.bind(this);
|
|
2434
2250
|
return submitFn({
|
|
@@ -2438,7 +2254,7 @@ class Provider {
|
|
|
2438
2254
|
actAs: preparedPayload.actAs,
|
|
2439
2255
|
readAs: preparedPayload.readAs,
|
|
2440
2256
|
synchronizerId: preparedPayload.synchronizerId
|
|
2441
|
-
}, { requestTimeout, message });
|
|
2257
|
+
}, { requestTimeout, message, estimateTraffic });
|
|
2442
2258
|
}
|
|
2443
2259
|
async signMessage(message) {
|
|
2444
2260
|
return this.sendRequest("sign_raw_message" /* SIGN_RAW_MESSAGE */, message);
|
|
@@ -2551,6 +2367,283 @@ class Provider {
|
|
|
2551
2367
|
}
|
|
2552
2368
|
}
|
|
2553
2369
|
|
|
2370
|
+
// src/connection.ts
|
|
2371
|
+
class Connection {
|
|
2372
|
+
walletUrl = "https://cantonloop.com";
|
|
2373
|
+
apiUrl = "https://cantonloop.com";
|
|
2374
|
+
ws = null;
|
|
2375
|
+
network = "main";
|
|
2376
|
+
ticketId = null;
|
|
2377
|
+
onMessageHandler = null;
|
|
2378
|
+
reconnectPromise = null;
|
|
2379
|
+
status = "disconnected";
|
|
2380
|
+
constructor({ network, walletUrl, apiUrl }) {
|
|
2381
|
+
this.network = network || "main";
|
|
2382
|
+
switch (this.network) {
|
|
2383
|
+
case "local":
|
|
2384
|
+
this.walletUrl = "http://localhost:3000";
|
|
2385
|
+
this.apiUrl = "http://localhost:8080";
|
|
2386
|
+
break;
|
|
2387
|
+
case "devnet":
|
|
2388
|
+
case "dev":
|
|
2389
|
+
this.walletUrl = "https://devnet.cantonloop.com";
|
|
2390
|
+
this.apiUrl = "https://devnet.cantonloop.com";
|
|
2391
|
+
break;
|
|
2392
|
+
case "testnet":
|
|
2393
|
+
case "test":
|
|
2394
|
+
this.walletUrl = "https://testnet.cantonloop.com";
|
|
2395
|
+
this.apiUrl = "https://testnet.cantonloop.com";
|
|
2396
|
+
break;
|
|
2397
|
+
case "mainnet":
|
|
2398
|
+
case "main":
|
|
2399
|
+
this.walletUrl = "https://cantonloop.com";
|
|
2400
|
+
this.apiUrl = "https://cantonloop.com";
|
|
2401
|
+
break;
|
|
2402
|
+
}
|
|
2403
|
+
if (walletUrl) {
|
|
2404
|
+
this.walletUrl = walletUrl;
|
|
2405
|
+
}
|
|
2406
|
+
if (apiUrl) {
|
|
2407
|
+
this.apiUrl = apiUrl;
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
connectInProgress() {
|
|
2411
|
+
return this.status === "connecting" || this.status === "connected";
|
|
2412
|
+
}
|
|
2413
|
+
async getTicket(appName, sessionId, version) {
|
|
2414
|
+
const response = await fetch(`${this.apiUrl}/api/v1/.connect/pair/tickets`, {
|
|
2415
|
+
method: "POST",
|
|
2416
|
+
headers: {
|
|
2417
|
+
"Content-Type": "application/json"
|
|
2418
|
+
},
|
|
2419
|
+
body: JSON.stringify({
|
|
2420
|
+
app_name: appName,
|
|
2421
|
+
session_id: sessionId,
|
|
2422
|
+
version
|
|
2423
|
+
})
|
|
2424
|
+
});
|
|
2425
|
+
if (!response.ok) {
|
|
2426
|
+
throw new Error("Failed to get ticket from server.");
|
|
2427
|
+
}
|
|
2428
|
+
return response.json();
|
|
2429
|
+
}
|
|
2430
|
+
async getHolding(authToken) {
|
|
2431
|
+
const response = await fetch(`${this.apiUrl}/api/v1/.connect/pair/account/holding`, {
|
|
2432
|
+
method: "GET",
|
|
2433
|
+
headers: {
|
|
2434
|
+
"Content-Type": "application/json",
|
|
2435
|
+
Authorization: `Bearer ${authToken}`
|
|
2436
|
+
}
|
|
2437
|
+
});
|
|
2438
|
+
if (!response.ok) {
|
|
2439
|
+
throw new Error("Failed to get holdings. " + await response.text());
|
|
2440
|
+
}
|
|
2441
|
+
return response.json();
|
|
2442
|
+
}
|
|
2443
|
+
async getActiveContracts(authToken, params) {
|
|
2444
|
+
const url = new URL(`${this.apiUrl}/api/v1/.connect/pair/account/active-contracts`);
|
|
2445
|
+
if (params?.templateId) {
|
|
2446
|
+
url.searchParams.append("templateId", params.templateId);
|
|
2447
|
+
}
|
|
2448
|
+
if (params?.interfaceId) {
|
|
2449
|
+
url.searchParams.append("interfaceId", params.interfaceId);
|
|
2450
|
+
}
|
|
2451
|
+
const response = await fetch(url.toString(), {
|
|
2452
|
+
method: "GET",
|
|
2453
|
+
headers: {
|
|
2454
|
+
"Content-Type": "application/json",
|
|
2455
|
+
Authorization: `Bearer ${authToken}`
|
|
2456
|
+
}
|
|
2457
|
+
});
|
|
2458
|
+
if (!response.ok) {
|
|
2459
|
+
throw new Error("Failed to get active contracts.");
|
|
2460
|
+
}
|
|
2461
|
+
return response.json();
|
|
2462
|
+
}
|
|
2463
|
+
async prepareTransfer(authToken, params) {
|
|
2464
|
+
const payload = {
|
|
2465
|
+
recipient: params.recipient,
|
|
2466
|
+
amount: params.amount
|
|
2467
|
+
};
|
|
2468
|
+
if (params.instrument) {
|
|
2469
|
+
if (params.instrument.instrument_admin) {
|
|
2470
|
+
payload.instrument_admin = params.instrument.instrument_admin;
|
|
2471
|
+
}
|
|
2472
|
+
if (params.instrument.instrument_id) {
|
|
2473
|
+
payload.instrument_id = params.instrument.instrument_id;
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
if (params.requested_at) {
|
|
2477
|
+
payload.requested_at = params.requested_at;
|
|
2478
|
+
}
|
|
2479
|
+
if (params.execute_before) {
|
|
2480
|
+
payload.execute_before = params.execute_before;
|
|
2481
|
+
}
|
|
2482
|
+
if (params.memo) {
|
|
2483
|
+
payload.memo = params.memo;
|
|
2484
|
+
}
|
|
2485
|
+
const response = await fetch(`${this.apiUrl}/api/v1/.connect/pair/transfer`, {
|
|
2486
|
+
method: "POST",
|
|
2487
|
+
headers: {
|
|
2488
|
+
"Content-Type": "application/json",
|
|
2489
|
+
Authorization: `Bearer ${authToken}`
|
|
2490
|
+
},
|
|
2491
|
+
body: JSON.stringify(payload)
|
|
2492
|
+
});
|
|
2493
|
+
if (!response.ok) {
|
|
2494
|
+
console.error("Failed to prepare transfer.", await response.text());
|
|
2495
|
+
throw new Error("Failed to prepare transfer.");
|
|
2496
|
+
}
|
|
2497
|
+
const data = await response.json();
|
|
2498
|
+
return data.payload;
|
|
2499
|
+
}
|
|
2500
|
+
async verifySession(authToken) {
|
|
2501
|
+
const response = await fetch(`${this.apiUrl}/api/v1/.connect/pair/account`, {
|
|
2502
|
+
method: "GET",
|
|
2503
|
+
headers: {
|
|
2504
|
+
"Content-Type": "application/json",
|
|
2505
|
+
Authorization: `Bearer ${authToken}`
|
|
2506
|
+
}
|
|
2507
|
+
});
|
|
2508
|
+
if (!response.ok) {
|
|
2509
|
+
if (response.status === 401 || response.status === 403) {
|
|
2510
|
+
throw new UnauthorizedError;
|
|
2511
|
+
}
|
|
2512
|
+
throw new Error(`Session verification failed with status ${response.status}.`);
|
|
2513
|
+
}
|
|
2514
|
+
const data = await response.json();
|
|
2515
|
+
const email = data?.email;
|
|
2516
|
+
if (!data?.party_id || !data?.public_key) {
|
|
2517
|
+
throw new Error("Invalid session verification response.");
|
|
2518
|
+
}
|
|
2519
|
+
const account = {
|
|
2520
|
+
party_id: data?.party_id,
|
|
2521
|
+
auth_token: authToken,
|
|
2522
|
+
public_key: data?.public_key,
|
|
2523
|
+
email,
|
|
2524
|
+
has_preapproval: data?.has_preapproval,
|
|
2525
|
+
has_merge_delegation: data?.has_merge_delegation,
|
|
2526
|
+
usdc_bridge_access: data?.usdc_bridge_access
|
|
2527
|
+
};
|
|
2528
|
+
return account;
|
|
2529
|
+
}
|
|
2530
|
+
connectWebSocket(ticketId, onMessage) {
|
|
2531
|
+
if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) && this.ticketId !== ticketId) {
|
|
2532
|
+
this.ws.close();
|
|
2533
|
+
this.ws = null;
|
|
2534
|
+
}
|
|
2535
|
+
if (this.status === "connecting" || this.status === "connected") {
|
|
2536
|
+
return;
|
|
2537
|
+
}
|
|
2538
|
+
this.onMessageHandler = onMessage;
|
|
2539
|
+
this.ticketId = ticketId;
|
|
2540
|
+
this.status = "connecting";
|
|
2541
|
+
this.attachWebSocket(ticketId, onMessage);
|
|
2542
|
+
}
|
|
2543
|
+
reconnect() {
|
|
2544
|
+
if (!this.ticketId || !this.onMessageHandler) {
|
|
2545
|
+
return Promise.reject(new Error("Cannot reconnect without a known ticket."));
|
|
2546
|
+
}
|
|
2547
|
+
return new Promise((resolve, reject) => {
|
|
2548
|
+
let opened = false;
|
|
2549
|
+
this.attachWebSocket(this.ticketId, this.onMessageHandler, () => {
|
|
2550
|
+
opened = true;
|
|
2551
|
+
resolve();
|
|
2552
|
+
}, () => {
|
|
2553
|
+
if (opened) {
|
|
2554
|
+
return;
|
|
2555
|
+
}
|
|
2556
|
+
reject(new Error("Failed to reconnect to ticket server."));
|
|
2557
|
+
}, () => {
|
|
2558
|
+
if (opened) {
|
|
2559
|
+
return;
|
|
2560
|
+
}
|
|
2561
|
+
reject(new Error("Failed to reconnect to ticket server."));
|
|
2562
|
+
});
|
|
2563
|
+
});
|
|
2564
|
+
}
|
|
2565
|
+
async exchangeApiKey({ publicKey, signature, epoch }) {
|
|
2566
|
+
const response = await fetch(`${this.apiUrl}/api/v1/.connect/pair/apikey`, {
|
|
2567
|
+
method: "POST",
|
|
2568
|
+
headers: {
|
|
2569
|
+
"Content-Type": "application/json"
|
|
2570
|
+
},
|
|
2571
|
+
body: JSON.stringify({
|
|
2572
|
+
public_key: publicKey,
|
|
2573
|
+
signature,
|
|
2574
|
+
epoch
|
|
2575
|
+
})
|
|
2576
|
+
});
|
|
2577
|
+
if (!response.ok) {
|
|
2578
|
+
throw new Error("Failed to get API key from server.");
|
|
2579
|
+
}
|
|
2580
|
+
return response.json();
|
|
2581
|
+
}
|
|
2582
|
+
prepareTransaction(session, params) {
|
|
2583
|
+
return fetch(`${this.apiUrl}/api/v1/.connect/tickets/prepare-transaction`, {
|
|
2584
|
+
method: "POST",
|
|
2585
|
+
headers: {
|
|
2586
|
+
"Content-Type": "application/json",
|
|
2587
|
+
Authorization: `Bearer ${session.userApiKey}`
|
|
2588
|
+
},
|
|
2589
|
+
body: JSON.stringify({
|
|
2590
|
+
payload: params,
|
|
2591
|
+
ticket_id: session.ticketId
|
|
2592
|
+
})
|
|
2593
|
+
}).then((response) => response.json());
|
|
2594
|
+
}
|
|
2595
|
+
async executeTransaction(session, params) {
|
|
2596
|
+
if (!session.ticketId) {
|
|
2597
|
+
throw new Error("Ticket ID is required");
|
|
2598
|
+
}
|
|
2599
|
+
const resp = fetch(`${this.apiUrl}/api/v1/.connect/tickets/execute-transaction`, {
|
|
2600
|
+
method: "POST",
|
|
2601
|
+
headers: {
|
|
2602
|
+
"Content-Type": "application/json",
|
|
2603
|
+
Authorization: `Bearer ${session.userApiKey}`
|
|
2604
|
+
},
|
|
2605
|
+
body: JSON.stringify({
|
|
2606
|
+
ticket_id: session.ticketId,
|
|
2607
|
+
request_id: generateRequestId(),
|
|
2608
|
+
command_id: params.command_id,
|
|
2609
|
+
signature: params.signature,
|
|
2610
|
+
transaction_data: params.transaction_data
|
|
2611
|
+
})
|
|
2612
|
+
});
|
|
2613
|
+
return (await resp).json();
|
|
2614
|
+
}
|
|
2615
|
+
websocketUrl(ticketId) {
|
|
2616
|
+
return `${this.network === "local" ? "ws" : "wss"}://${this.apiUrl.replace("https://", "").replace("http://", "")}/api/v1/.connect/pair/ws/${encodeURIComponent(ticketId)}`;
|
|
2617
|
+
}
|
|
2618
|
+
attachWebSocket(ticketId, onMessage, onOpen, onError, onClose) {
|
|
2619
|
+
const wsUrl = this.websocketUrl(ticketId);
|
|
2620
|
+
const ws = new WebSocket(wsUrl);
|
|
2621
|
+
ws.onmessage = onMessage;
|
|
2622
|
+
ws.onopen = () => {
|
|
2623
|
+
this.status = "connected";
|
|
2624
|
+
console.log("[LoopSDK] Connected to ticket server.");
|
|
2625
|
+
onOpen?.();
|
|
2626
|
+
};
|
|
2627
|
+
ws.onclose = (event) => {
|
|
2628
|
+
this.status = "disconnected";
|
|
2629
|
+
if (this.ws === ws) {
|
|
2630
|
+
this.ws = null;
|
|
2631
|
+
}
|
|
2632
|
+
console.log("[LoopSDK] Disconnected from ticket server.");
|
|
2633
|
+
onClose?.(event);
|
|
2634
|
+
};
|
|
2635
|
+
ws.onerror = (event) => {
|
|
2636
|
+
this.status = "disconnected";
|
|
2637
|
+
ws.close();
|
|
2638
|
+
if (this.ws === ws) {
|
|
2639
|
+
this.ws = null;
|
|
2640
|
+
}
|
|
2641
|
+
onError?.(event);
|
|
2642
|
+
};
|
|
2643
|
+
this.ws = ws;
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
|
|
2554
2647
|
// src/session.ts
|
|
2555
2648
|
var STORAGE_KEY_LOOP_CONNECT = "loop_connect";
|
|
2556
2649
|
|
|
@@ -2561,14 +2654,16 @@ class SessionInfo {
|
|
|
2561
2654
|
partyId;
|
|
2562
2655
|
publicKey;
|
|
2563
2656
|
email;
|
|
2657
|
+
userApiKey;
|
|
2564
2658
|
_isAuthorized = false;
|
|
2565
|
-
constructor({ sessionId, ticketId, authToken, partyId, publicKey, email }) {
|
|
2659
|
+
constructor({ sessionId, ticketId, authToken, partyId, publicKey, email, userApiKey }) {
|
|
2566
2660
|
this.sessionId = sessionId;
|
|
2567
2661
|
this.ticketId = ticketId;
|
|
2568
2662
|
this.authToken = authToken;
|
|
2569
2663
|
this.partyId = partyId;
|
|
2570
2664
|
this.publicKey = publicKey;
|
|
2571
2665
|
this.email = email;
|
|
2666
|
+
this.userApiKey = userApiKey;
|
|
2572
2667
|
}
|
|
2573
2668
|
setTicketId(ticketId) {
|
|
2574
2669
|
this.ticketId = ticketId;
|
|
@@ -2950,28 +3045,31 @@ class LoopSDK {
|
|
|
2950
3045
|
.loop-connect {
|
|
2951
3046
|
position: fixed;
|
|
2952
3047
|
inset: 0;
|
|
2953
|
-
background:
|
|
3048
|
+
background: rgba(0, 0, 0, 0.85);
|
|
2954
3049
|
backdrop-filter: blur(8px);
|
|
2955
3050
|
display: flex;
|
|
2956
3051
|
justify-content: center;
|
|
2957
3052
|
align-items: center;
|
|
2958
3053
|
z-index: 10000;
|
|
2959
|
-
font-family: system-ui, -apple-system, sans-serif;
|
|
3054
|
+
font-family: "Inter", system-ui, -apple-system, sans-serif;
|
|
2960
3055
|
animation: fadeIn 0.2s ease-out;
|
|
2961
3056
|
}
|
|
2962
3057
|
.loop-connect dialog {
|
|
2963
3058
|
position: relative;
|
|
2964
3059
|
overflow: hidden;
|
|
2965
|
-
background:
|
|
2966
|
-
box-shadow: 0
|
|
2967
|
-
border:
|
|
2968
|
-
border
|
|
2969
|
-
|
|
3060
|
+
background: #080808;
|
|
3061
|
+
box-shadow: 0 24px 60px -12px rgba(0, 0, 0, 0.5);
|
|
3062
|
+
border-radius: 40px;
|
|
3063
|
+
border: none;
|
|
3064
|
+
width: 340px;
|
|
3065
|
+
height: 534px;
|
|
3066
|
+
box-sizing: border-box;
|
|
3067
|
+
padding: 32px;
|
|
2970
3068
|
display: flex;
|
|
2971
3069
|
flex-direction: column;
|
|
2972
3070
|
align-items: center;
|
|
2973
|
-
gap:
|
|
2974
|
-
color:
|
|
3071
|
+
gap: 0;
|
|
3072
|
+
color: #ffffff;
|
|
2975
3073
|
}
|
|
2976
3074
|
.loop-connect .bg-logo {
|
|
2977
3075
|
position: absolute;
|
|
@@ -2983,56 +3081,89 @@ class LoopSDK {
|
|
|
2983
3081
|
pointer-events: none;
|
|
2984
3082
|
}
|
|
2985
3083
|
.loop-connect h3 {
|
|
3084
|
+
position: absolute;
|
|
3085
|
+
top: 32px;
|
|
3086
|
+
left: 32px;
|
|
3087
|
+
right: 32px;
|
|
2986
3088
|
margin: 0;
|
|
2987
3089
|
font-size: 18px;
|
|
2988
|
-
font-weight:
|
|
2989
|
-
|
|
3090
|
+
font-weight: 700;
|
|
3091
|
+
line-height: 27px;
|
|
3092
|
+
letter-spacing: -0.45px;
|
|
3093
|
+
text-align: center;
|
|
2990
3094
|
}
|
|
2991
3095
|
.loop-connect figure {
|
|
3096
|
+
position: absolute;
|
|
3097
|
+
top: 91px;
|
|
3098
|
+
left: 32px;
|
|
3099
|
+
width: 276px;
|
|
3100
|
+
height: 276px;
|
|
2992
3101
|
margin: 0;
|
|
2993
|
-
background:
|
|
2994
|
-
padding:
|
|
2995
|
-
border-radius:
|
|
3102
|
+
background: #ffffff;
|
|
3103
|
+
padding: 20px;
|
|
3104
|
+
border-radius: 8px;
|
|
2996
3105
|
display: flex;
|
|
2997
3106
|
justify-content: center;
|
|
2998
|
-
border:
|
|
2999
|
-
box-shadow: 0
|
|
3107
|
+
border: none;
|
|
3108
|
+
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.1);
|
|
3109
|
+
box-sizing: border-box;
|
|
3000
3110
|
}
|
|
3001
3111
|
.loop-connect img {
|
|
3002
3112
|
display: block;
|
|
3003
|
-
width:
|
|
3004
|
-
height:
|
|
3113
|
+
width: 236px;
|
|
3114
|
+
height: 236px;
|
|
3115
|
+
object-fit: contain;
|
|
3116
|
+
border-radius: 12px;
|
|
3005
3117
|
}
|
|
3006
3118
|
.loop-connect .divider {
|
|
3007
|
-
|
|
3119
|
+
position: absolute;
|
|
3120
|
+
top: 399px;
|
|
3121
|
+
left: 36px;
|
|
3122
|
+
right: 36px;
|
|
3123
|
+
width: auto;
|
|
3008
3124
|
display: flex;
|
|
3009
3125
|
align-items: center;
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
font-
|
|
3126
|
+
justify-content: center;
|
|
3127
|
+
gap: 12px;
|
|
3128
|
+
color: #64748b;
|
|
3129
|
+
font-size: 11px;
|
|
3130
|
+
font-weight: 700;
|
|
3131
|
+
letter-spacing: 0.15em;
|
|
3132
|
+
text-transform: uppercase;
|
|
3133
|
+
text-align: center;
|
|
3014
3134
|
}
|
|
3015
3135
|
.loop-connect .divider::before,
|
|
3016
3136
|
.loop-connect .divider::after {
|
|
3017
3137
|
content: "";
|
|
3018
3138
|
flex: 1;
|
|
3019
3139
|
height: 1px;
|
|
3020
|
-
background:
|
|
3140
|
+
background: #1e293b;
|
|
3021
3141
|
}
|
|
3022
3142
|
.loop-connect button {
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3143
|
+
position: absolute;
|
|
3144
|
+
top: 447.5px;
|
|
3145
|
+
left: 32px;
|
|
3146
|
+
right: 32px;
|
|
3147
|
+
background: #f2ff96;
|
|
3148
|
+
border: none;
|
|
3149
|
+
color: #0f172a;
|
|
3150
|
+
text-align: center;
|
|
3151
|
+
font-family: "Inter", system-ui, -apple-system, sans-serif;
|
|
3152
|
+
font-style: normal;
|
|
3153
|
+
padding: 0 24px;
|
|
3154
|
+
border-radius: 8px;
|
|
3028
3155
|
font-size: 15px;
|
|
3029
3156
|
font-weight: 600;
|
|
3157
|
+
line-height: 22.5px;
|
|
3030
3158
|
cursor: pointer;
|
|
3031
3159
|
transition: all 0.2s ease;
|
|
3032
|
-
width:
|
|
3160
|
+
width: auto;
|
|
3161
|
+
height: 54.5px;
|
|
3162
|
+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.2),
|
|
3163
|
+
0 4px 6px -4px rgba(0, 0, 0, 0.2);
|
|
3033
3164
|
}
|
|
3034
3165
|
.loop-connect button:hover {
|
|
3035
|
-
background:
|
|
3166
|
+
background: #f6ffb4;
|
|
3036
3167
|
}
|
|
3037
3168
|
@keyframes fadeIn {
|
|
3038
3169
|
from { opacity: 0; }
|
|
@@ -3043,7 +3174,7 @@ class LoopSDK {
|
|
|
3043
3174
|
}
|
|
3044
3175
|
showQrCode(url) {
|
|
3045
3176
|
this.injectModalStyles();
|
|
3046
|
-
import_qrcode.default.toDataURL(url, (err, dataUrl) => {
|
|
3177
|
+
import_qrcode.default.toDataURL(url, { margin: 0 }, (err, dataUrl) => {
|
|
3047
3178
|
if (err) {
|
|
3048
3179
|
console.error("Failed to generate QR code", err);
|
|
3049
3180
|
return;
|