@rhinestone/deposit-modal 0.12.2 → 0.13.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 +30 -16
- package/dist/ClaimModal-CREZO27I.mjs +11 -0
- package/dist/ClaimModal-JRIA32U2.cjs +11 -0
- package/dist/{DepositModalReown-TEC3MEZ4.mjs → DepositModalReown-3H75G3XQ.mjs} +44 -22
- package/dist/{DepositModalReown-VG66OTIU.cjs → DepositModalReown-YNNHLAV2.cjs} +57 -35
- package/dist/{QRCode-WUC652SH.mjs → QRCode-5RGIZZHL.mjs} +1 -1
- package/dist/{QRCode-5DXFNKI2.cjs → QRCode-JKXG4RHN.cjs} +2 -2
- package/dist/WithdrawModal-NVRX44IZ.mjs +10 -0
- package/dist/WithdrawModal-YYSBPCJJ.cjs +10 -0
- package/dist/{chunk-OIN4CXCP.cjs → chunk-2G2EIN4A.cjs} +1510 -896
- package/dist/{chunk-O7BCVQ6L.cjs → chunk-4KCSH4T3.cjs} +157 -199
- package/dist/{chunk-TM7VGHAG.cjs → chunk-5NUIKYSF.cjs} +83 -209
- package/dist/{chunk-UQIJU6HV.cjs → chunk-7N77GIP4.cjs} +102 -88
- package/dist/{chunk-J46WDSRD.mjs → chunk-COCBCQQC.mjs} +121 -86
- package/dist/chunk-CVGNCUVU.cjs +154 -0
- package/dist/{chunk-SDSKIJC7.mjs → chunk-DSIZNRWA.mjs} +1540 -926
- package/dist/{chunk-75DVGSAX.cjs → chunk-DW2EQA2U.cjs} +6 -3
- package/dist/{chunk-FXXUIK4E.mjs → chunk-EOMT5LKX.mjs} +374 -151
- package/dist/chunk-FZ5FZFIG.mjs +31 -0
- package/dist/chunk-GACIGCSN.cjs +31 -0
- package/dist/{chunk-HL5NUKZW.cjs → chunk-HOGZKHHY.cjs} +3 -3
- package/dist/{chunk-FLVLYO64.mjs → chunk-LTMMETAB.mjs} +29 -15
- package/dist/{chunk-2TPSF77V.mjs → chunk-OLCD75JK.mjs} +20 -146
- package/dist/{chunk-PWLEBV5V.cjs → chunk-RAADLTSW.cjs} +240 -205
- package/dist/{chunk-4ZXQPFNU.mjs → chunk-RN5VKWI6.mjs} +4 -1
- package/dist/{chunk-3STTWRAN.mjs → chunk-VUATPOWJ.mjs} +147 -189
- package/dist/{chunk-JE4R5VA3.mjs → chunk-XYJFEN5D.mjs} +1 -1
- package/dist/chunk-YREVFB64.mjs +154 -0
- package/dist/{chunk-BRYNGLDM.cjs → chunk-ZMT4ATHR.cjs} +437 -214
- package/dist/claim.cjs +7 -6
- package/dist/claim.d.cts +11 -3
- package/dist/claim.d.ts +11 -3
- package/dist/claim.mjs +6 -5
- package/dist/{constants-FuzG3h4m.d.cts → constants-DBrWhJWE.d.cts} +12 -7
- package/dist/{constants-FuzG3h4m.d.ts → constants-DBrWhJWE.d.ts} +12 -7
- package/dist/constants.cjs +3 -9
- package/dist/constants.d.cts +2 -3
- package/dist/constants.d.ts +2 -3
- package/dist/constants.mjs +4 -10
- package/dist/deposit.cjs +8 -8
- package/dist/deposit.d.cts +2 -2
- package/dist/deposit.d.ts +2 -2
- package/dist/deposit.mjs +7 -7
- package/dist/embed.cjs +1268 -0
- package/dist/embed.d.cts +177 -0
- package/dist/embed.d.ts +177 -0
- package/dist/embed.mjs +1268 -0
- package/dist/index.cjs +11 -16
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +14 -19
- package/dist/modal-ui-state-OD1RmKkK.d.cts +30 -0
- package/dist/modal-ui-state-OD1RmKkK.d.ts +30 -0
- package/dist/polymarket.cjs +17 -7
- package/dist/polymarket.mjs +13 -3
- package/dist/styles.css +124 -98
- package/dist/{types-oFblf65g.d.ts → types-B89I8_8H.d.ts} +27 -1
- package/dist/{types-BO4iP6fG.d.cts → types-nnDR4tqA.d.cts} +27 -1
- package/dist/withdraw.cjs +6 -6
- package/dist/withdraw.d.cts +11 -3
- package/dist/withdraw.d.ts +11 -3
- package/dist/withdraw.mjs +5 -5
- package/package.json +33 -1
- package/dist/chunk-IAGFDSNC.mjs +0 -11
- package/dist/chunk-L32UI7LA.cjs +0 -11
package/dist/embed.mjs
ADDED
|
@@ -0,0 +1,1268 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DepositModalInner
|
|
3
|
+
} from "./chunk-DSIZNRWA.mjs";
|
|
4
|
+
import {
|
|
5
|
+
markSubmissionUncertain
|
|
6
|
+
} from "./chunk-COCBCQQC.mjs";
|
|
7
|
+
import "./chunk-YREVFB64.mjs";
|
|
8
|
+
import "./chunk-XYJFEN5D.mjs";
|
|
9
|
+
import {
|
|
10
|
+
primeRuntimeChains
|
|
11
|
+
} from "./chunk-EOMT5LKX.mjs";
|
|
12
|
+
import {
|
|
13
|
+
MODAL_VERSION,
|
|
14
|
+
setEmbedHost
|
|
15
|
+
} from "./chunk-FZ5FZFIG.mjs";
|
|
16
|
+
import "./chunk-RN5VKWI6.mjs";
|
|
17
|
+
import {
|
|
18
|
+
HYPERCORE_SPOT_CAIP2,
|
|
19
|
+
getChainName
|
|
20
|
+
} from "./chunk-VUATPOWJ.mjs";
|
|
21
|
+
|
|
22
|
+
// src/embed.tsx
|
|
23
|
+
import { Suspense, lazy, useCallback, useEffect, useRef, useState } from "react";
|
|
24
|
+
|
|
25
|
+
// src/core/bridge/protocol.ts
|
|
26
|
+
var PROTOCOL_VERSION = 2;
|
|
27
|
+
var PAGE_TO_HOST_CHANNEL = "rhinestoneBridge";
|
|
28
|
+
var HOST_TO_PAGE_CHANNEL = "__rhinestone_bridge";
|
|
29
|
+
var MAX_FRAME_LENGTH = 256 * 1024;
|
|
30
|
+
var BRIDGE_METHOD = {
|
|
31
|
+
/** Page→host, first frame of the session. Answers `HelloResult`. */
|
|
32
|
+
HELLO: "hello",
|
|
33
|
+
/** Page→host: a CAIP-27 wallet request, `Caip27Params`. */
|
|
34
|
+
WALLET_REQUEST: "wallet.request",
|
|
35
|
+
/** Page→host: withdraw's transfer. Gated by `CAPABILITY.SEND_TRANSACTION`. */
|
|
36
|
+
SEND_TRANSACTION: "host.sendTransaction",
|
|
37
|
+
/** Page→host: claim's signature. Gated by `CAPABILITY.SIGN_RECOVERY`. */
|
|
38
|
+
SIGN_RECOVERY: "host.signRecovery",
|
|
39
|
+
/** Page→host: show a payment page outside the web view. Gated by
|
|
40
|
+
* `CAPABILITY.OPEN_URL`. */
|
|
41
|
+
OPEN_URL: "host.openUrl"
|
|
42
|
+
};
|
|
43
|
+
var HOST_METHOD = {
|
|
44
|
+
/** Host→page: Android's hardware back and iOS's interactive swipe. Answers
|
|
45
|
+
* `UiBackResult`. */
|
|
46
|
+
BACK: "ui.back"
|
|
47
|
+
};
|
|
48
|
+
var CAPABILITY = {
|
|
49
|
+
SEND_TRANSACTION: BRIDGE_METHOD.SEND_TRANSACTION,
|
|
50
|
+
SIGN_RECOVERY: BRIDGE_METHOD.SIGN_RECOVERY,
|
|
51
|
+
OPEN_URL: BRIDGE_METHOD.OPEN_URL
|
|
52
|
+
};
|
|
53
|
+
var ALLOWED_WALLET_METHODS = [
|
|
54
|
+
"eth_chainId",
|
|
55
|
+
"eth_accounts",
|
|
56
|
+
"eth_sendTransaction",
|
|
57
|
+
"wallet_sendTransaction",
|
|
58
|
+
"eth_signTypedData_v4",
|
|
59
|
+
"wallet_switchEthereumChain"
|
|
60
|
+
];
|
|
61
|
+
var SUBMITTING_WALLET_METHODS = [
|
|
62
|
+
"eth_sendTransaction",
|
|
63
|
+
"wallet_sendTransaction"
|
|
64
|
+
];
|
|
65
|
+
var BRIDGE_ERROR_DOMAIN = "bridge";
|
|
66
|
+
var BridgeErrorCode = {
|
|
67
|
+
/** The host has a wallet but cannot reach it — app not installed, session
|
|
68
|
+
* dropped mid-request. Distinct from 4900, which means not connected. */
|
|
69
|
+
WALLET_UNAVAILABLE: 1,
|
|
70
|
+
/**
|
|
71
|
+
* The host cannot say whether the transaction reached the network.
|
|
72
|
+
*
|
|
73
|
+
* An app switch can be killed by the OS between wallet submission and the
|
|
74
|
+
* return trip. Without a code for it the host must lie in one direction or
|
|
75
|
+
* the other, and the safe-looking lie — reporting failure — is the one that
|
|
76
|
+
* double-spends. The page shows its existing "status is uncertain" copy and
|
|
77
|
+
* must not offer a retry.
|
|
78
|
+
*/
|
|
79
|
+
SUBMISSION_UNCERTAIN: 2,
|
|
80
|
+
/** The page's own deadline expired. Raised page-side, never sent by a host. */
|
|
81
|
+
REQUEST_TIMEOUT: 3,
|
|
82
|
+
/** The channel went away with the request outstanding. */
|
|
83
|
+
CHANNEL_CLOSED: 4,
|
|
84
|
+
/** The host answered, but not with something the method can return — a null
|
|
85
|
+
* transaction hash, an `eth_accounts` that is not an array. */
|
|
86
|
+
MALFORMED_RESULT: 5
|
|
87
|
+
};
|
|
88
|
+
function bridgeError(code, message, data) {
|
|
89
|
+
return { domain: BRIDGE_ERROR_DOMAIN, code, message, data };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/core/bridge/channel.ts
|
|
93
|
+
var NoHostChannelError = class extends Error {
|
|
94
|
+
constructor() {
|
|
95
|
+
super(
|
|
96
|
+
`No host channel: window.${PAGE_TO_HOST_CHANNEL}.postMessage is not installed`
|
|
97
|
+
);
|
|
98
|
+
this.name = "NoHostChannelError";
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
function readHostChannel(target) {
|
|
102
|
+
const candidate = target[PAGE_TO_HOST_CHANNEL];
|
|
103
|
+
if (!candidate || typeof candidate.postMessage !== "function") {
|
|
104
|
+
return void 0;
|
|
105
|
+
}
|
|
106
|
+
return candidate;
|
|
107
|
+
}
|
|
108
|
+
function createWindowChannel(target, onInvalidFrame) {
|
|
109
|
+
return {
|
|
110
|
+
post(json) {
|
|
111
|
+
const host = readHostChannel(target);
|
|
112
|
+
if (!host) throw new NoHostChannelError();
|
|
113
|
+
host.postMessage(json);
|
|
114
|
+
},
|
|
115
|
+
subscribe(onFrame) {
|
|
116
|
+
const receiver = (frame) => {
|
|
117
|
+
if (typeof frame !== "string") {
|
|
118
|
+
onInvalidFrame?.();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
onFrame(frame);
|
|
122
|
+
};
|
|
123
|
+
target[HOST_TO_PAGE_CHANNEL] = receiver;
|
|
124
|
+
return () => {
|
|
125
|
+
if (target[HOST_TO_PAGE_CHANNEL] === receiver) {
|
|
126
|
+
delete target[HOST_TO_PAGE_CHANNEL];
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/core/bridge/transport.ts
|
|
134
|
+
import { BaseError } from "viem";
|
|
135
|
+
|
|
136
|
+
// src/core/bridge/envelope.ts
|
|
137
|
+
function isRecord(value) {
|
|
138
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
139
|
+
}
|
|
140
|
+
function isFiniteNumber(value) {
|
|
141
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
142
|
+
}
|
|
143
|
+
function isNonEmptyString(value) {
|
|
144
|
+
return typeof value === "string" && value.length > 0;
|
|
145
|
+
}
|
|
146
|
+
function isBridgeError(value) {
|
|
147
|
+
if (!isRecord(value)) return false;
|
|
148
|
+
if (!isFiniteNumber(value.code)) return false;
|
|
149
|
+
if (typeof value.message !== "string") return false;
|
|
150
|
+
if (value.data !== void 0 && typeof value.data !== "string") return false;
|
|
151
|
+
if (value.domain !== void 0 && value.domain !== BRIDGE_ERROR_DOMAIN) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
function parseEnvelope(raw) {
|
|
157
|
+
if (raw.length > MAX_FRAME_LENGTH) return { ok: false, reason: "too-large" };
|
|
158
|
+
let value;
|
|
159
|
+
try {
|
|
160
|
+
value = JSON.parse(raw);
|
|
161
|
+
} catch {
|
|
162
|
+
return { ok: false, reason: "not-json" };
|
|
163
|
+
}
|
|
164
|
+
if (!isRecord(value)) return { ok: false, reason: "not-an-object" };
|
|
165
|
+
switch (value.kind) {
|
|
166
|
+
case "event": {
|
|
167
|
+
if (!isNonEmptyString(value.type)) {
|
|
168
|
+
return { ok: false, reason: "bad-event" };
|
|
169
|
+
}
|
|
170
|
+
return { ok: true, envelope: value };
|
|
171
|
+
}
|
|
172
|
+
case "request": {
|
|
173
|
+
if (!isNonEmptyString(value.id) || !isNonEmptyString(value.method)) {
|
|
174
|
+
return { ok: false, reason: "bad-request" };
|
|
175
|
+
}
|
|
176
|
+
return { ok: true, envelope: value };
|
|
177
|
+
}
|
|
178
|
+
case "response": {
|
|
179
|
+
if (!isNonEmptyString(value.id)) {
|
|
180
|
+
return { ok: false, reason: "bad-response" };
|
|
181
|
+
}
|
|
182
|
+
if (value.ok === true) {
|
|
183
|
+
if (!("result" in value)) {
|
|
184
|
+
return { ok: false, reason: "bad-response" };
|
|
185
|
+
}
|
|
186
|
+
return { ok: true, envelope: value };
|
|
187
|
+
}
|
|
188
|
+
if (value.ok === false && isBridgeError(value.error)) {
|
|
189
|
+
return { ok: true, envelope: value };
|
|
190
|
+
}
|
|
191
|
+
return { ok: false, reason: "bad-response" };
|
|
192
|
+
}
|
|
193
|
+
default:
|
|
194
|
+
return { ok: false, reason: "unknown-kind" };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// src/core/bridge/transport.ts
|
|
199
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
200
|
+
var USER_APPROVAL_TIMEOUT_MS = 18e4;
|
|
201
|
+
function emptyStats() {
|
|
202
|
+
return {
|
|
203
|
+
"too-large": 0,
|
|
204
|
+
"not-json": 0,
|
|
205
|
+
"not-an-object": 0,
|
|
206
|
+
"unknown-kind": 0,
|
|
207
|
+
"bad-event": 0,
|
|
208
|
+
"bad-request": 0,
|
|
209
|
+
"bad-response": 0,
|
|
210
|
+
"not-a-string": 0,
|
|
211
|
+
"unknown-event": 0,
|
|
212
|
+
"unknown-method": 0,
|
|
213
|
+
"unmatched-response": 0,
|
|
214
|
+
"duplicate-request": 0,
|
|
215
|
+
closed: 0
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
var BridgeRequestError = class extends BaseError {
|
|
219
|
+
constructor(error) {
|
|
220
|
+
super(error.message, { name: "BridgeRequestError" });
|
|
221
|
+
this.code = error.code;
|
|
222
|
+
this.domain = error.domain;
|
|
223
|
+
Object.defineProperty(this, "data", {
|
|
224
|
+
value: error.data,
|
|
225
|
+
enumerable: false,
|
|
226
|
+
writable: false
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
function isBridgeRequestError(error) {
|
|
231
|
+
return error instanceof BridgeRequestError;
|
|
232
|
+
}
|
|
233
|
+
function isBridgeCode(error, code) {
|
|
234
|
+
return isBridgeRequestError(error) && error.domain === BRIDGE_ERROR_DOMAIN && error.code === code;
|
|
235
|
+
}
|
|
236
|
+
function randomSessionId() {
|
|
237
|
+
const bytes = new Uint8Array(16);
|
|
238
|
+
const webcrypto = globalThis.crypto;
|
|
239
|
+
if (webcrypto?.getRandomValues) {
|
|
240
|
+
webcrypto.getRandomValues(bytes);
|
|
241
|
+
} else {
|
|
242
|
+
for (let i = 0; i < bytes.length; i += 1) {
|
|
243
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
247
|
+
}
|
|
248
|
+
var REPLAY_CACHE_SIZE = 32;
|
|
249
|
+
function createBridgeTransport(options) {
|
|
250
|
+
const { channel } = options;
|
|
251
|
+
const sessionId = options.sessionId ?? randomSessionId();
|
|
252
|
+
const defaultTimeoutMs = options.defaultTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
253
|
+
const stats = emptyStats();
|
|
254
|
+
const pending = /* @__PURE__ */ new Map();
|
|
255
|
+
const inbound = /* @__PURE__ */ new Map();
|
|
256
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
257
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
258
|
+
let counter = 0;
|
|
259
|
+
let closed = false;
|
|
260
|
+
const unsubscribe = channel.subscribe(onFrame);
|
|
261
|
+
function drop(reason) {
|
|
262
|
+
stats[reason] += 1;
|
|
263
|
+
}
|
|
264
|
+
function send(frame) {
|
|
265
|
+
channel.post(JSON.stringify(frame));
|
|
266
|
+
}
|
|
267
|
+
function notifyPending() {
|
|
268
|
+
options.onPendingChange?.(pending.size);
|
|
269
|
+
}
|
|
270
|
+
function forgetPending(id) {
|
|
271
|
+
const entry = pending.get(id);
|
|
272
|
+
if (!entry) return void 0;
|
|
273
|
+
pending.delete(id);
|
|
274
|
+
notifyPending();
|
|
275
|
+
return entry;
|
|
276
|
+
}
|
|
277
|
+
function settle(id, settleWith) {
|
|
278
|
+
const entry = forgetPending(id);
|
|
279
|
+
if (!entry) {
|
|
280
|
+
drop("unmatched-response");
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (entry.timer !== void 0) clearTimeout(entry.timer);
|
|
284
|
+
settleWith(entry);
|
|
285
|
+
}
|
|
286
|
+
function onFrame(raw) {
|
|
287
|
+
if (closed) {
|
|
288
|
+
drop("closed");
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const parsed = parseEnvelope(raw);
|
|
292
|
+
if (!parsed.ok) {
|
|
293
|
+
drop(parsed.reason);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const envelope = parsed.envelope;
|
|
297
|
+
if (envelope.kind === "response") {
|
|
298
|
+
settle(envelope.id, (entry) => {
|
|
299
|
+
if (envelope.ok) entry.resolve(envelope.result);
|
|
300
|
+
else entry.reject(new BridgeRequestError(envelope.error));
|
|
301
|
+
});
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (envelope.kind === "event") {
|
|
305
|
+
const listener = listeners.get(envelope.type);
|
|
306
|
+
if (!listener) {
|
|
307
|
+
drop("unknown-event");
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
listener(envelope.payload);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
void answer(envelope.id, envelope.method, envelope.params);
|
|
314
|
+
}
|
|
315
|
+
function settleInbound(id, response) {
|
|
316
|
+
inbound.set(id, { status: "settled", response });
|
|
317
|
+
if (inbound.size > REPLAY_CACHE_SIZE) {
|
|
318
|
+
for (const [key, value] of inbound) {
|
|
319
|
+
if (key !== id && value.status === "settled") {
|
|
320
|
+
inbound.delete(key);
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
send(response);
|
|
326
|
+
}
|
|
327
|
+
async function answer(id, method, params) {
|
|
328
|
+
const seen = inbound.get(id);
|
|
329
|
+
if (seen) {
|
|
330
|
+
drop("duplicate-request");
|
|
331
|
+
if (seen.status === "settled") send(seen.response);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const handler = handlers.get(method);
|
|
335
|
+
if (!handler) {
|
|
336
|
+
drop("unknown-method");
|
|
337
|
+
send({
|
|
338
|
+
kind: "response",
|
|
339
|
+
id,
|
|
340
|
+
ok: false,
|
|
341
|
+
error: { code: 4200, message: `Unsupported method: ${method}` }
|
|
342
|
+
});
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
inbound.set(id, { status: "running" });
|
|
346
|
+
try {
|
|
347
|
+
const result = await handler(params);
|
|
348
|
+
settleInbound(id, { kind: "response", id, ok: true, result: result ?? null });
|
|
349
|
+
} catch (error) {
|
|
350
|
+
settleInbound(id, {
|
|
351
|
+
kind: "response",
|
|
352
|
+
id,
|
|
353
|
+
ok: false,
|
|
354
|
+
error: {
|
|
355
|
+
code: -32603,
|
|
356
|
+
message: error instanceof Error ? error.message : "Page error"
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return {
|
|
362
|
+
stats,
|
|
363
|
+
request(method, params, requestOptions) {
|
|
364
|
+
if (closed) {
|
|
365
|
+
return Promise.reject(
|
|
366
|
+
new BridgeRequestError(
|
|
367
|
+
bridgeError(
|
|
368
|
+
BridgeErrorCode.CHANNEL_CLOSED,
|
|
369
|
+
"The connection to the app closed."
|
|
370
|
+
)
|
|
371
|
+
)
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
counter += 1;
|
|
375
|
+
const id = `${sessionId}.${counter}`;
|
|
376
|
+
const timeoutMs = requestOptions?.timeoutMs ?? defaultTimeoutMs;
|
|
377
|
+
return new Promise((resolve, reject) => {
|
|
378
|
+
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
379
|
+
forgetPending(id);
|
|
380
|
+
reject(
|
|
381
|
+
new BridgeRequestError(
|
|
382
|
+
requestOptions?.uncertainOnTimeout ? bridgeError(
|
|
383
|
+
BridgeErrorCode.SUBMISSION_UNCERTAIN,
|
|
384
|
+
"The app did not confirm whether the transaction was sent."
|
|
385
|
+
) : bridgeError(
|
|
386
|
+
BridgeErrorCode.REQUEST_TIMEOUT,
|
|
387
|
+
"The app did not respond."
|
|
388
|
+
)
|
|
389
|
+
)
|
|
390
|
+
);
|
|
391
|
+
}, timeoutMs) : void 0;
|
|
392
|
+
pending.set(id, {
|
|
393
|
+
resolve,
|
|
394
|
+
reject,
|
|
395
|
+
timer
|
|
396
|
+
});
|
|
397
|
+
notifyPending();
|
|
398
|
+
try {
|
|
399
|
+
send({ kind: "request", id, method, params });
|
|
400
|
+
} catch (error) {
|
|
401
|
+
forgetPending(id);
|
|
402
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
403
|
+
reject(
|
|
404
|
+
error instanceof NoHostChannelError ? new BridgeRequestError(
|
|
405
|
+
bridgeError(
|
|
406
|
+
BridgeErrorCode.CHANNEL_CLOSED,
|
|
407
|
+
"The connection to the app closed."
|
|
408
|
+
)
|
|
409
|
+
) : error
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
},
|
|
414
|
+
emit(type, payload) {
|
|
415
|
+
if (closed) return;
|
|
416
|
+
try {
|
|
417
|
+
send({ kind: "event", type, payload });
|
|
418
|
+
} catch {
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
handle(method, handler) {
|
|
422
|
+
handlers.set(method, handler);
|
|
423
|
+
},
|
|
424
|
+
on(type, handler) {
|
|
425
|
+
listeners.set(type, handler);
|
|
426
|
+
},
|
|
427
|
+
close() {
|
|
428
|
+
if (closed) return;
|
|
429
|
+
closed = true;
|
|
430
|
+
unsubscribe();
|
|
431
|
+
const closing = new BridgeRequestError(
|
|
432
|
+
bridgeError(
|
|
433
|
+
BridgeErrorCode.CHANNEL_CLOSED,
|
|
434
|
+
"The connection to the app closed."
|
|
435
|
+
)
|
|
436
|
+
);
|
|
437
|
+
for (const [, entry] of pending) {
|
|
438
|
+
if (entry.timer !== void 0) clearTimeout(entry.timer);
|
|
439
|
+
entry.reject(closing);
|
|
440
|
+
}
|
|
441
|
+
pending.clear();
|
|
442
|
+
notifyPending();
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// src/core/bridge/validate.ts
|
|
448
|
+
function isRecord2(value) {
|
|
449
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
450
|
+
}
|
|
451
|
+
var TX_HASH = /^0x[0-9a-fA-F]{64}$/;
|
|
452
|
+
var HEX = /^0x[0-9a-fA-F]+$/;
|
|
453
|
+
function isTxHash(value) {
|
|
454
|
+
return typeof value === "string" && TX_HASH.test(value);
|
|
455
|
+
}
|
|
456
|
+
function isHex(value) {
|
|
457
|
+
return typeof value === "string" && HEX.test(value);
|
|
458
|
+
}
|
|
459
|
+
function optionalString(value) {
|
|
460
|
+
return typeof value === "string" ? value : void 0;
|
|
461
|
+
}
|
|
462
|
+
function parseWalletState(value) {
|
|
463
|
+
if (!isRecord2(value)) return void 0;
|
|
464
|
+
if (typeof value.isReady !== "boolean") return void 0;
|
|
465
|
+
if (typeof value.isConnected !== "boolean") return void 0;
|
|
466
|
+
if (!Array.isArray(value.accounts)) return void 0;
|
|
467
|
+
const accounts = [];
|
|
468
|
+
for (const entry of value.accounts) {
|
|
469
|
+
if (isRecord2(entry) && typeof entry.caip10 === "string") {
|
|
470
|
+
accounts.push({ caip10: entry.caip10 });
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
const chainId = typeof value.chainId === "string" ? value.chainId : value.chainId === null ? null : void 0;
|
|
474
|
+
if (chainId === void 0) return void 0;
|
|
475
|
+
return {
|
|
476
|
+
isReady: value.isReady,
|
|
477
|
+
isConnected: value.isConnected,
|
|
478
|
+
accounts,
|
|
479
|
+
chainId,
|
|
480
|
+
icon: optionalString(value.icon),
|
|
481
|
+
name: optionalString(value.name)
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
function parseHelloResult(value) {
|
|
485
|
+
if (!isRecord2(value)) return void 0;
|
|
486
|
+
if (typeof value.protocol !== "number" || !Number.isFinite(value.protocol)) {
|
|
487
|
+
return void 0;
|
|
488
|
+
}
|
|
489
|
+
const host = isRecord2(value.host) ? value.host : void 0;
|
|
490
|
+
if (!host) return void 0;
|
|
491
|
+
const platform = host.platform === "ios" || host.platform === "android" ? host.platform : "other";
|
|
492
|
+
const wallet = parseWalletState(value.wallet);
|
|
493
|
+
if (!wallet) return void 0;
|
|
494
|
+
if (!isRecord2(value.config)) return void 0;
|
|
495
|
+
const capabilities = Array.isArray(value.capabilities) ? value.capabilities.filter(
|
|
496
|
+
(entry) => typeof entry === "string"
|
|
497
|
+
) : [];
|
|
498
|
+
return {
|
|
499
|
+
protocol: value.protocol,
|
|
500
|
+
host: {
|
|
501
|
+
platform,
|
|
502
|
+
app: optionalString(host.app),
|
|
503
|
+
version: optionalString(host.version)
|
|
504
|
+
},
|
|
505
|
+
capabilities,
|
|
506
|
+
config: value.config,
|
|
507
|
+
wallet
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// src/core/bridge/wallet.ts
|
|
512
|
+
import { createWalletClient, custom } from "viem";
|
|
513
|
+
var ALLOWED = new Set(ALLOWED_WALLET_METHODS);
|
|
514
|
+
var SWITCH_CONFIRMATION_TIMEOUT_MS = 15e3;
|
|
515
|
+
var HASH_PATTERN = /^0x[0-9a-fA-F]{64}$/;
|
|
516
|
+
var HEX_PATTERN = /^0x[0-9a-fA-F]*$/;
|
|
517
|
+
function evmAccount(state) {
|
|
518
|
+
return state.accounts.find((account) => account.caip10.startsWith("eip155:"))?.caip10;
|
|
519
|
+
}
|
|
520
|
+
function evmAddressFrom(state) {
|
|
521
|
+
const caip10 = evmAccount(state);
|
|
522
|
+
if (!caip10) return void 0;
|
|
523
|
+
const parts = caip10.split(":");
|
|
524
|
+
return parts.length === 3 ? parts[2] : void 0;
|
|
525
|
+
}
|
|
526
|
+
function evmChainIdFrom(caip2) {
|
|
527
|
+
if (!caip2) return void 0;
|
|
528
|
+
const parts = caip2.split(":");
|
|
529
|
+
if (parts.length !== 2 || parts[0] !== "eip155") return void 0;
|
|
530
|
+
const id = Number(parts[1]);
|
|
531
|
+
return Number.isInteger(id) && id > 0 ? id : void 0;
|
|
532
|
+
}
|
|
533
|
+
function switchTargetChainId(params) {
|
|
534
|
+
const first = Array.isArray(params) ? params[0] : void 0;
|
|
535
|
+
if (typeof first !== "object" || first === null) return void 0;
|
|
536
|
+
const hex = first.chainId;
|
|
537
|
+
if (typeof hex !== "string") return void 0;
|
|
538
|
+
const id = Number.parseInt(hex, 16);
|
|
539
|
+
return Number.isInteger(id) && id > 0 ? id : void 0;
|
|
540
|
+
}
|
|
541
|
+
function syntheticChain(chainId) {
|
|
542
|
+
return {
|
|
543
|
+
id: chainId,
|
|
544
|
+
name: getChainName(chainId),
|
|
545
|
+
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
|
|
546
|
+
rpcUrls: { default: { http: [] } }
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
function assertResultShape(method, result) {
|
|
550
|
+
const bad = (detail) => {
|
|
551
|
+
const error = new BridgeRequestError(
|
|
552
|
+
bridgeError(
|
|
553
|
+
BridgeErrorCode.MALFORMED_RESULT,
|
|
554
|
+
"The app returned something unexpected. Check your wallet activity before trying again.",
|
|
555
|
+
`${method}: ${detail}`
|
|
556
|
+
)
|
|
557
|
+
);
|
|
558
|
+
throw SUBMITTING_WALLET_METHODS.includes(method) ? markSubmissionUncertain(error) : error;
|
|
559
|
+
};
|
|
560
|
+
switch (method) {
|
|
561
|
+
case "eth_sendTransaction":
|
|
562
|
+
case "wallet_sendTransaction":
|
|
563
|
+
if (typeof result !== "string" || !HASH_PATTERN.test(result)) {
|
|
564
|
+
bad("expected a 32-byte transaction hash");
|
|
565
|
+
}
|
|
566
|
+
return;
|
|
567
|
+
case "eth_signTypedData_v4":
|
|
568
|
+
if (typeof result !== "string" || !HEX_PATTERN.test(result)) {
|
|
569
|
+
bad("expected a hex signature");
|
|
570
|
+
}
|
|
571
|
+
return;
|
|
572
|
+
case "eth_chainId":
|
|
573
|
+
if (typeof result !== "string" || !HEX_PATTERN.test(result)) {
|
|
574
|
+
bad("expected a hex quantity");
|
|
575
|
+
}
|
|
576
|
+
return;
|
|
577
|
+
case "eth_accounts":
|
|
578
|
+
if (!Array.isArray(result)) bad("expected an array of addresses");
|
|
579
|
+
return;
|
|
580
|
+
default:
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
function createBridgedWalletSession(options) {
|
|
585
|
+
let state = options.state;
|
|
586
|
+
const { transport, resolveChain } = options;
|
|
587
|
+
let cachedClient;
|
|
588
|
+
let cachedKey;
|
|
589
|
+
const stateWaiters = /* @__PURE__ */ new Set();
|
|
590
|
+
async function walletRequest(method, params) {
|
|
591
|
+
if (!ALLOWED.has(method)) {
|
|
592
|
+
throw new Error(`Method not supported over the bridge: ${method}`);
|
|
593
|
+
}
|
|
594
|
+
const target = switchTargetChainId(params);
|
|
595
|
+
const chainId = target !== void 0 ? `eip155:${target}` : state.chainId ?? void 0;
|
|
596
|
+
if (!chainId) {
|
|
597
|
+
throw new Error("No chain selected for this wallet request");
|
|
598
|
+
}
|
|
599
|
+
const submitting = SUBMITTING_WALLET_METHODS.includes(method);
|
|
600
|
+
const caip27 = { chainId, request: { method, params } };
|
|
601
|
+
let result;
|
|
602
|
+
try {
|
|
603
|
+
result = await transport.request(
|
|
604
|
+
BRIDGE_METHOD.WALLET_REQUEST,
|
|
605
|
+
caip27,
|
|
606
|
+
{ timeoutMs: USER_APPROVAL_TIMEOUT_MS, uncertainOnTimeout: submitting }
|
|
607
|
+
);
|
|
608
|
+
} catch (error) {
|
|
609
|
+
if (submitting && isBridgeRequestError(error) && error.code === -32603) {
|
|
610
|
+
throw markSubmissionUncertain(
|
|
611
|
+
new BridgeRequestError(
|
|
612
|
+
bridgeError(
|
|
613
|
+
BridgeErrorCode.SUBMISSION_UNCERTAIN,
|
|
614
|
+
"The app could not confirm whether the transaction was sent. Check your wallet activity before trying again.",
|
|
615
|
+
error.data
|
|
616
|
+
)
|
|
617
|
+
)
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
if (submitting && isBridgeCode(error, BridgeErrorCode.SUBMISSION_UNCERTAIN)) {
|
|
621
|
+
throw markSubmissionUncertain(error);
|
|
622
|
+
}
|
|
623
|
+
throw error;
|
|
624
|
+
}
|
|
625
|
+
assertResultShape(method, result);
|
|
626
|
+
return result;
|
|
627
|
+
}
|
|
628
|
+
const evmTransport = custom({
|
|
629
|
+
async request({ method, params }) {
|
|
630
|
+
return walletRequest(method, params);
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
function currentClient() {
|
|
634
|
+
const address = evmAddressFrom(state);
|
|
635
|
+
const chainId = evmChainIdFrom(state.chainId);
|
|
636
|
+
if (!address || chainId === void 0) return void 0;
|
|
637
|
+
const chain = resolveChain(chainId) ?? syntheticChain(chainId);
|
|
638
|
+
const key = `${address}:${chainId}`;
|
|
639
|
+
if (cachedClient && cachedKey === key) return cachedClient;
|
|
640
|
+
cachedClient = createWalletClient({
|
|
641
|
+
account: address,
|
|
642
|
+
chain,
|
|
643
|
+
transport: evmTransport
|
|
644
|
+
});
|
|
645
|
+
cachedKey = key;
|
|
646
|
+
return cachedClient;
|
|
647
|
+
}
|
|
648
|
+
function awaitChain(chainId) {
|
|
649
|
+
if (evmChainIdFrom(state.chainId) === chainId) return Promise.resolve();
|
|
650
|
+
return new Promise((resolve, reject) => {
|
|
651
|
+
let timer;
|
|
652
|
+
const waiter = (next) => {
|
|
653
|
+
if (evmChainIdFrom(next.chainId) !== chainId) return;
|
|
654
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
655
|
+
stateWaiters.delete(waiter);
|
|
656
|
+
resolve();
|
|
657
|
+
};
|
|
658
|
+
timer = setTimeout(() => {
|
|
659
|
+
stateWaiters.delete(waiter);
|
|
660
|
+
reject(
|
|
661
|
+
new Error(
|
|
662
|
+
`Switch to ${resolveChain(chainId)?.name ?? getChainName(chainId)} in your wallet to continue`
|
|
663
|
+
)
|
|
664
|
+
);
|
|
665
|
+
}, SWITCH_CONFIRMATION_TIMEOUT_MS);
|
|
666
|
+
stateWaiters.add(waiter);
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
const session = {
|
|
670
|
+
get walletClient() {
|
|
671
|
+
return currentClient();
|
|
672
|
+
},
|
|
673
|
+
get canSignEvm() {
|
|
674
|
+
return state.isConnected && currentClient() !== void 0;
|
|
675
|
+
},
|
|
676
|
+
// Reads fall back to `getPublicClient`; a bridged host has no
|
|
677
|
+
// wallet-backed client to offer.
|
|
678
|
+
publicClient: void 0,
|
|
679
|
+
get address() {
|
|
680
|
+
return evmAddressFrom(state);
|
|
681
|
+
},
|
|
682
|
+
get caipAddress() {
|
|
683
|
+
return evmAccount(state);
|
|
684
|
+
},
|
|
685
|
+
get isConnected() {
|
|
686
|
+
return state.isConnected;
|
|
687
|
+
},
|
|
688
|
+
get isReady() {
|
|
689
|
+
return state.isReady;
|
|
690
|
+
},
|
|
691
|
+
get icon() {
|
|
692
|
+
return state.icon;
|
|
693
|
+
},
|
|
694
|
+
isSolana: false,
|
|
695
|
+
solanaAddress: void 0,
|
|
696
|
+
solanaProvider: void 0,
|
|
697
|
+
solanaConnection: void 0,
|
|
698
|
+
openConnect: () => transport.emit("wallet.connectRequested"),
|
|
699
|
+
disconnect: () => transport.emit("wallet.disconnectRequested"),
|
|
700
|
+
switchChain: async (chainId) => {
|
|
701
|
+
try {
|
|
702
|
+
await walletRequest("wallet_switchEthereumChain", [
|
|
703
|
+
{ chainId: `0x${chainId.toString(16)}` }
|
|
704
|
+
]);
|
|
705
|
+
} catch (error) {
|
|
706
|
+
if (isBridgeRequestError(error)) throw new Error(error.message);
|
|
707
|
+
throw error;
|
|
708
|
+
}
|
|
709
|
+
await awaitChain(chainId);
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
return {
|
|
713
|
+
session,
|
|
714
|
+
applyWalletState(next) {
|
|
715
|
+
state = next;
|
|
716
|
+
for (const waiter of [...stateWaiters]) waiter(next);
|
|
717
|
+
options.onStateChange?.(next);
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// src/core/bridge/connect.ts
|
|
723
|
+
var BridgeHandshakeError = class extends Error {
|
|
724
|
+
constructor(message) {
|
|
725
|
+
super(message);
|
|
726
|
+
this.name = "BridgeHandshakeError";
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
async function connectToHost(options) {
|
|
730
|
+
let invalidFrames = 0;
|
|
731
|
+
const channel = createWindowChannel(options.target, () => {
|
|
732
|
+
invalidFrames += 1;
|
|
733
|
+
});
|
|
734
|
+
const transport = createBridgeTransport({
|
|
735
|
+
channel,
|
|
736
|
+
onPendingChange: options.onPendingChange
|
|
737
|
+
});
|
|
738
|
+
const params = {
|
|
739
|
+
protocol: PROTOCOL_VERSION,
|
|
740
|
+
modalVersion: MODAL_VERSION
|
|
741
|
+
};
|
|
742
|
+
let raw;
|
|
743
|
+
try {
|
|
744
|
+
raw = await transport.request(BRIDGE_METHOD.HELLO, params, {
|
|
745
|
+
timeoutMs: options.helloTimeoutMs
|
|
746
|
+
});
|
|
747
|
+
} catch (error) {
|
|
748
|
+
transport.close();
|
|
749
|
+
throw error;
|
|
750
|
+
}
|
|
751
|
+
const hello = parseHelloResult(raw);
|
|
752
|
+
if (!hello) {
|
|
753
|
+
transport.close();
|
|
754
|
+
throw new BridgeHandshakeError(
|
|
755
|
+
invalidFrames > 0 ? "The app answered the handshake with something that was not a string frame." : "The app answered the handshake with an unusable payload."
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
let config = hello.config;
|
|
759
|
+
const wallet = createBridgedWalletSession({
|
|
760
|
+
transport,
|
|
761
|
+
state: hello.wallet,
|
|
762
|
+
resolveChain: options.resolveChain,
|
|
763
|
+
onStateChange: options.onWalletState
|
|
764
|
+
});
|
|
765
|
+
transport.on("wallet.state", (payload) => {
|
|
766
|
+
const next = parseWalletState(payload);
|
|
767
|
+
if (next) wallet.applyWalletState(next);
|
|
768
|
+
});
|
|
769
|
+
transport.on("session.configure", (payload) => {
|
|
770
|
+
if (typeof payload !== "object" || payload === null) return;
|
|
771
|
+
config = payload;
|
|
772
|
+
options.onConfig?.(config);
|
|
773
|
+
});
|
|
774
|
+
return {
|
|
775
|
+
transport,
|
|
776
|
+
hello,
|
|
777
|
+
wallet,
|
|
778
|
+
get config() {
|
|
779
|
+
return config;
|
|
780
|
+
},
|
|
781
|
+
close() {
|
|
782
|
+
transport.close();
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// src/core/bridge/capabilities.ts
|
|
788
|
+
async function requestHostTransfer(transport, params) {
|
|
789
|
+
let result;
|
|
790
|
+
try {
|
|
791
|
+
result = await transport.request(
|
|
792
|
+
BRIDGE_METHOD.SEND_TRANSACTION,
|
|
793
|
+
params,
|
|
794
|
+
{ timeoutMs: USER_APPROVAL_TIMEOUT_MS, uncertainOnTimeout: true }
|
|
795
|
+
);
|
|
796
|
+
} catch (error) {
|
|
797
|
+
if (error instanceof Error && isBridgeCode(error, BridgeErrorCode.SUBMISSION_UNCERTAIN)) {
|
|
798
|
+
throw markSubmissionUncertain(error);
|
|
799
|
+
}
|
|
800
|
+
throw error;
|
|
801
|
+
}
|
|
802
|
+
if (!isTxHash(result?.txHash)) {
|
|
803
|
+
throw markSubmissionUncertain(
|
|
804
|
+
new Error(
|
|
805
|
+
"The app did not return a transaction hash. Check your activity before trying again."
|
|
806
|
+
)
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
return result.txHash;
|
|
810
|
+
}
|
|
811
|
+
async function requestHostRecoverySignature(transport, params) {
|
|
812
|
+
const result = await transport.request(
|
|
813
|
+
BRIDGE_METHOD.SIGN_RECOVERY,
|
|
814
|
+
params,
|
|
815
|
+
{ timeoutMs: USER_APPROVAL_TIMEOUT_MS }
|
|
816
|
+
);
|
|
817
|
+
if (!isHex(result?.signature)) {
|
|
818
|
+
throw new Error("The app did not return a signature.");
|
|
819
|
+
}
|
|
820
|
+
return result.signature;
|
|
821
|
+
}
|
|
822
|
+
async function requestHostBrowser(transport, url) {
|
|
823
|
+
let parsed;
|
|
824
|
+
try {
|
|
825
|
+
parsed = new URL(url);
|
|
826
|
+
} catch {
|
|
827
|
+
throw new Error("The payment page address could not be read.");
|
|
828
|
+
}
|
|
829
|
+
if (parsed.protocol !== "https:") {
|
|
830
|
+
throw new Error("The payment page address is not a secure link.");
|
|
831
|
+
}
|
|
832
|
+
await transport.request(BRIDGE_METHOD.OPEN_URL, {
|
|
833
|
+
url
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// src/core/bridge/ui-state.ts
|
|
838
|
+
var SETTLING_SCREENS = /* @__PURE__ */ new Set(["processing"]);
|
|
839
|
+
var HOME_SCREENS = /* @__PURE__ */ new Set(["connect", "form", "lookup"]);
|
|
840
|
+
function isHomeScreen(screen) {
|
|
841
|
+
return HOME_SCREENS.has(screen);
|
|
842
|
+
}
|
|
843
|
+
function dismissalFor(input) {
|
|
844
|
+
if (input.walletRequestPending) {
|
|
845
|
+
return {
|
|
846
|
+
state: "blocked",
|
|
847
|
+
reason: "wallet-request-pending",
|
|
848
|
+
message: "Waiting for your wallet."
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
if (input.submitting) {
|
|
852
|
+
return {
|
|
853
|
+
state: "blocked",
|
|
854
|
+
reason: "submission-in-flight",
|
|
855
|
+
message: "Confirming your deposit."
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
if (SETTLING_SCREENS.has(input.screen)) {
|
|
859
|
+
return {
|
|
860
|
+
state: "blocked",
|
|
861
|
+
reason: "settlement-in-progress",
|
|
862
|
+
message: "Your deposit is on its way."
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
return { state: "allowed" };
|
|
866
|
+
}
|
|
867
|
+
function canGoBackNow(input) {
|
|
868
|
+
return dismissalFor(input).state !== "blocked";
|
|
869
|
+
}
|
|
870
|
+
function uiStateFor(input) {
|
|
871
|
+
return { screen: input.screen, dismissal: dismissalFor(input) };
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// src/core/bridge/viewport.ts
|
|
875
|
+
var VIEWPORT_HEIGHT_PROPERTY = "--rs-embed-viewport-height";
|
|
876
|
+
function trackVisualViewport(win) {
|
|
877
|
+
const target = win;
|
|
878
|
+
const viewport = target?.visualViewport;
|
|
879
|
+
const style = target?.document?.documentElement?.style;
|
|
880
|
+
if (!viewport || !style) return () => {
|
|
881
|
+
};
|
|
882
|
+
const apply = () => {
|
|
883
|
+
style.setProperty(VIEWPORT_HEIGHT_PROPERTY, `${viewport.height}px`);
|
|
884
|
+
};
|
|
885
|
+
apply();
|
|
886
|
+
viewport.addEventListener("resize", apply);
|
|
887
|
+
viewport.addEventListener("scroll", apply);
|
|
888
|
+
return () => {
|
|
889
|
+
viewport.removeEventListener("resize", apply);
|
|
890
|
+
viewport.removeEventListener("scroll", apply);
|
|
891
|
+
style.removeProperty(VIEWPORT_HEIGHT_PROPERTY);
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// src/core/modal-ui-state.ts
|
|
896
|
+
var UNKNOWN_SCREEN = "unknown";
|
|
897
|
+
|
|
898
|
+
// src/embed.tsx
|
|
899
|
+
import { jsx } from "react/jsx-runtime";
|
|
900
|
+
var WithdrawModal = lazy(
|
|
901
|
+
() => import("./WithdrawModal-NVRX44IZ.mjs").then((m) => ({ default: m.WithdrawModal }))
|
|
902
|
+
);
|
|
903
|
+
var ClaimModal = lazy(
|
|
904
|
+
() => import("./ClaimModal-CREZO27I.mjs").then((m) => ({ default: m.ClaimModal }))
|
|
905
|
+
);
|
|
906
|
+
function missingField(config, keys) {
|
|
907
|
+
const missing = keys.find(
|
|
908
|
+
(key) => typeof config[key] !== "string" || config[key] === ""
|
|
909
|
+
);
|
|
910
|
+
return missing ? {
|
|
911
|
+
code: "EMBED_CONFIG_INVALID",
|
|
912
|
+
message: `The app did not supply ${String(missing)}.`
|
|
913
|
+
} : null;
|
|
914
|
+
}
|
|
915
|
+
var EVM_ADDRESS = /^0x[0-9a-fA-F]{40}$/;
|
|
916
|
+
function isEvmAddress(value) {
|
|
917
|
+
return typeof value === "string" && EVM_ADDRESS.test(value);
|
|
918
|
+
}
|
|
919
|
+
function isEvmChainId(value) {
|
|
920
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
921
|
+
}
|
|
922
|
+
function missingCapability(capabilities, needed, mode) {
|
|
923
|
+
return capabilities.includes(needed) ? null : {
|
|
924
|
+
code: "EMBED_CAPABILITY_MISSING",
|
|
925
|
+
message: `This app cannot run ${mode}: it does not implement ${needed}.`
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
function refusal(config, capabilities) {
|
|
929
|
+
switch (config.mode) {
|
|
930
|
+
case "deposit": {
|
|
931
|
+
const missing = missingField(config, [
|
|
932
|
+
"backendUrl",
|
|
933
|
+
"recipient",
|
|
934
|
+
"targetToken"
|
|
935
|
+
]);
|
|
936
|
+
if (missing) return missing;
|
|
937
|
+
const { targetChain } = config;
|
|
938
|
+
const validTarget = typeof targetChain === "number" && Number.isInteger(targetChain) && targetChain > 0 || targetChain === "solana" || targetChain === HYPERCORE_SPOT_CAIP2;
|
|
939
|
+
return validTarget ? null : {
|
|
940
|
+
code: "EMBED_CONFIG_INVALID",
|
|
941
|
+
message: `The app supplied a targetChain this page cannot resolve: ${JSON.stringify(targetChain)}.`
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
case "withdraw": {
|
|
945
|
+
const capability = missingCapability(
|
|
946
|
+
capabilities,
|
|
947
|
+
CAPABILITY.SEND_TRANSACTION,
|
|
948
|
+
"a withdrawal"
|
|
949
|
+
);
|
|
950
|
+
if (capability) return capability;
|
|
951
|
+
const missing = missingField(config, ["backendUrl", "accountAddress"]);
|
|
952
|
+
if (missing) return missing;
|
|
953
|
+
const badWithdrawField = !isEvmAddress(config.accountAddress) && "accountAddress" || !isEvmChainId(config.sourceChain) && "sourceChain" || !isEvmAddress(config.sourceToken) && "sourceToken";
|
|
954
|
+
return badWithdrawField ? {
|
|
955
|
+
code: "EMBED_CONFIG_INVALID",
|
|
956
|
+
message: `The app supplied a ${badWithdrawField} this page cannot use.`
|
|
957
|
+
} : null;
|
|
958
|
+
}
|
|
959
|
+
case "claim": {
|
|
960
|
+
const capability = missingCapability(
|
|
961
|
+
capabilities,
|
|
962
|
+
CAPABILITY.SIGN_RECOVERY,
|
|
963
|
+
"a claim"
|
|
964
|
+
);
|
|
965
|
+
if (capability) return capability;
|
|
966
|
+
return missingField(config, ["backendUrl"]);
|
|
967
|
+
}
|
|
968
|
+
default:
|
|
969
|
+
return {
|
|
970
|
+
code: "EMBED_MODE_UNSUPPORTED",
|
|
971
|
+
message: `This page cannot run in ${String(config.mode ?? "an unnamed")} mode.`
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
function sanitizeConfig(config) {
|
|
976
|
+
const str = (value) => typeof value === "string" && value !== "" ? value : void 0;
|
|
977
|
+
const num = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
978
|
+
const bool = (value) => typeof value === "boolean" ? value : void 0;
|
|
979
|
+
const arr = (value) => Array.isArray(value) ? value : void 0;
|
|
980
|
+
const obj = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
981
|
+
const target = (value) => {
|
|
982
|
+
if (typeof value === "number" && Number.isInteger(value) && value > 0) {
|
|
983
|
+
return value;
|
|
984
|
+
}
|
|
985
|
+
return value === "solana" || value === HYPERCORE_SPOT_CAIP2 ? value : void 0;
|
|
986
|
+
};
|
|
987
|
+
return {
|
|
988
|
+
// Raw, because it is the discriminant `refusal` switches on — anything
|
|
989
|
+
// else has to reach it to be named in the error.
|
|
990
|
+
mode: config.mode,
|
|
991
|
+
// Typed like everything else, even though `refusal` also checks them.
|
|
992
|
+
// Required is a property of the MODE, not of the field: `targetToken` and
|
|
993
|
+
// `recipient` are required for a deposit and optional seeds for a
|
|
994
|
+
// withdrawal, so passing them through raw let a withdraw config carry a
|
|
995
|
+
// non-string into `targetToken.toLowerCase()` and take the page down.
|
|
996
|
+
// Dropping first means `refusal` still fails the modes that need them, and
|
|
997
|
+
// the modes that do not simply see an absent seed.
|
|
998
|
+
backendUrl: str(config.backendUrl),
|
|
999
|
+
recipient: str(config.recipient),
|
|
1000
|
+
targetChain: target(config.targetChain),
|
|
1001
|
+
targetToken: str(config.targetToken),
|
|
1002
|
+
sourceChain: num(config.sourceChain),
|
|
1003
|
+
sourceToken: str(config.sourceToken),
|
|
1004
|
+
defaultAmount: str(config.defaultAmount),
|
|
1005
|
+
appBalanceUsd: num(config.appBalanceUsd),
|
|
1006
|
+
outputTokenRules: arr(config.outputTokenRules),
|
|
1007
|
+
rejectUnmapped: bool(config.rejectUnmapped),
|
|
1008
|
+
forceRegister: bool(config.forceRegister),
|
|
1009
|
+
enableWallet: bool(config.enableWallet),
|
|
1010
|
+
enableFiatOnramp: bool(config.enableFiatOnramp),
|
|
1011
|
+
enableQrTransfer: bool(config.enableQrTransfer),
|
|
1012
|
+
enableGaslessDeposit: bool(config.enableGaslessDeposit),
|
|
1013
|
+
enableExchangeConnect: bool(config.enableExchangeConnect),
|
|
1014
|
+
fiatMethods: obj(config.fiatMethods),
|
|
1015
|
+
assetMigrations: obj(config.assetMigrations),
|
|
1016
|
+
initialAssetMigration: str(
|
|
1017
|
+
config.initialAssetMigration
|
|
1018
|
+
),
|
|
1019
|
+
theme: obj(config.theme),
|
|
1020
|
+
uiConfig: obj(config.uiConfig),
|
|
1021
|
+
debug: bool(config.debug),
|
|
1022
|
+
accountAddress: str(config.accountAddress),
|
|
1023
|
+
defaultTxHash: str(config.defaultTxHash),
|
|
1024
|
+
defaultDestination: str(config.defaultDestination)
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
function onrampUnavailable(config, capabilities) {
|
|
1028
|
+
if (config.mode !== "deposit") return null;
|
|
1029
|
+
if (capabilities.includes(CAPABILITY.OPEN_URL)) return null;
|
|
1030
|
+
if (!config.enableFiatOnramp && !config.enableExchangeConnect) return null;
|
|
1031
|
+
return {
|
|
1032
|
+
code: "EMBED_CAPABILITY_MISSING",
|
|
1033
|
+
message: `Fiat and exchange funding are hidden: this app does not implement ${CAPABILITY.OPEN_URL}.`
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
function toModalProps(config, canOpenExternally) {
|
|
1037
|
+
return {
|
|
1038
|
+
isOpen: true,
|
|
1039
|
+
// Native draws the sheet. Inline is what stops the page drawing a scrim
|
|
1040
|
+
// and a second one inside it; the class carries back what an inline mount
|
|
1041
|
+
// otherwise loses — safe-area clearance and the input sizing that stops
|
|
1042
|
+
// iOS zooming on focus, both of which the stylesheet scopes to the overlay.
|
|
1043
|
+
inline: true,
|
|
1044
|
+
className: "rs-embed",
|
|
1045
|
+
onClose: () => {
|
|
1046
|
+
},
|
|
1047
|
+
targetChain: config.targetChain,
|
|
1048
|
+
targetToken: config.targetToken,
|
|
1049
|
+
recipient: config.recipient,
|
|
1050
|
+
backendUrl: config.backendUrl,
|
|
1051
|
+
sourceChain: config.sourceChain,
|
|
1052
|
+
sourceToken: config.sourceToken,
|
|
1053
|
+
defaultAmount: config.defaultAmount,
|
|
1054
|
+
appBalanceUsd: config.appBalanceUsd,
|
|
1055
|
+
outputTokenRules: config.outputTokenRules,
|
|
1056
|
+
rejectUnmapped: config.rejectUnmapped,
|
|
1057
|
+
forceRegister: config.forceRegister,
|
|
1058
|
+
enableWallet: config.enableWallet,
|
|
1059
|
+
enableFiatOnramp: canOpenExternally && config.enableFiatOnramp,
|
|
1060
|
+
enableQrTransfer: config.enableQrTransfer,
|
|
1061
|
+
enableGaslessDeposit: config.enableGaslessDeposit,
|
|
1062
|
+
enableExchangeConnect: canOpenExternally && config.enableExchangeConnect,
|
|
1063
|
+
fiatMethods: config.fiatMethods,
|
|
1064
|
+
assetMigrations: config.assetMigrations,
|
|
1065
|
+
initialAssetMigration: config.initialAssetMigration,
|
|
1066
|
+
theme: config.theme,
|
|
1067
|
+
uiConfig: config.uiConfig,
|
|
1068
|
+
debug: config.debug
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
function DepositEmbed({ target, onFatal }) {
|
|
1072
|
+
const [session, setSession] = useState(null);
|
|
1073
|
+
const [config, setConfig] = useState(null);
|
|
1074
|
+
const [walletPending, setWalletPending] = useState(false);
|
|
1075
|
+
const pendingRef = useRef(false);
|
|
1076
|
+
const completedRef = useRef(false);
|
|
1077
|
+
const uiRef = useRef({
|
|
1078
|
+
screen: UNKNOWN_SCREEN,
|
|
1079
|
+
submitting: false
|
|
1080
|
+
});
|
|
1081
|
+
const onFatalRef = useRef(onFatal);
|
|
1082
|
+
onFatalRef.current = onFatal;
|
|
1083
|
+
useEffect(
|
|
1084
|
+
() => trackVisualViewport(target ?? globalThis.window),
|
|
1085
|
+
[target]
|
|
1086
|
+
);
|
|
1087
|
+
useEffect(() => {
|
|
1088
|
+
let live = true;
|
|
1089
|
+
let opened = null;
|
|
1090
|
+
let chains = [];
|
|
1091
|
+
void (async () => {
|
|
1092
|
+
try {
|
|
1093
|
+
const next = await connectToHost({
|
|
1094
|
+
target: target ?? globalThis.window,
|
|
1095
|
+
resolveChain: (chainId) => chains.find((chain) => chain.id === chainId),
|
|
1096
|
+
onConfig: (updated) => {
|
|
1097
|
+
if (live) setConfig(sanitizeConfig(updated));
|
|
1098
|
+
},
|
|
1099
|
+
onPendingChange: (pending) => {
|
|
1100
|
+
pendingRef.current = pending > 0;
|
|
1101
|
+
if (live) setWalletPending(pending > 0);
|
|
1102
|
+
}
|
|
1103
|
+
});
|
|
1104
|
+
if (!live) {
|
|
1105
|
+
next.close();
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
opened = next;
|
|
1109
|
+
next.transport.handle(HOST_METHOD.BACK, () => {
|
|
1110
|
+
if (!canGoBackNow({
|
|
1111
|
+
...uiRef.current,
|
|
1112
|
+
walletRequestPending: pendingRef.current
|
|
1113
|
+
})) {
|
|
1114
|
+
return { handled: true };
|
|
1115
|
+
}
|
|
1116
|
+
const back = uiRef.current.back;
|
|
1117
|
+
back?.();
|
|
1118
|
+
return { handled: Boolean(back) };
|
|
1119
|
+
});
|
|
1120
|
+
setEmbedHost(next.hello.host);
|
|
1121
|
+
setSession(next);
|
|
1122
|
+
setConfig(sanitizeConfig(next.hello.config));
|
|
1123
|
+
void primeRuntimeChains(next.hello.config.backendUrl).then((result) => {
|
|
1124
|
+
chains = result;
|
|
1125
|
+
});
|
|
1126
|
+
} catch (error) {
|
|
1127
|
+
if (live) onFatalRef.current?.(error);
|
|
1128
|
+
}
|
|
1129
|
+
})();
|
|
1130
|
+
return () => {
|
|
1131
|
+
live = false;
|
|
1132
|
+
opened?.close();
|
|
1133
|
+
setEmbedHost(null);
|
|
1134
|
+
};
|
|
1135
|
+
}, [target]);
|
|
1136
|
+
const handleUiState = useCallback(
|
|
1137
|
+
(state) => {
|
|
1138
|
+
if (isHomeScreen(state.screen)) completedRef.current = false;
|
|
1139
|
+
uiRef.current = state;
|
|
1140
|
+
session?.transport.emit(
|
|
1141
|
+
"ui.state",
|
|
1142
|
+
uiStateFor({ ...state, walletRequestPending: walletPending })
|
|
1143
|
+
);
|
|
1144
|
+
},
|
|
1145
|
+
[session, walletPending]
|
|
1146
|
+
);
|
|
1147
|
+
const openExternally = useCallback(
|
|
1148
|
+
async (url) => {
|
|
1149
|
+
if (!session) throw new Error("The app is not connected.");
|
|
1150
|
+
await requestHostBrowser(session.transport, url);
|
|
1151
|
+
},
|
|
1152
|
+
[session]
|
|
1153
|
+
);
|
|
1154
|
+
useEffect(() => {
|
|
1155
|
+
if (!session) return;
|
|
1156
|
+
session.transport.emit(
|
|
1157
|
+
"ui.state",
|
|
1158
|
+
uiStateFor({ ...uiRef.current, walletRequestPending: walletPending })
|
|
1159
|
+
);
|
|
1160
|
+
}, [session, walletPending]);
|
|
1161
|
+
const refused = config && session ? refusal(config, session.hello.capabilities) : null;
|
|
1162
|
+
useEffect(() => {
|
|
1163
|
+
if (!session || !refused) return;
|
|
1164
|
+
session.transport.emit("error", refused);
|
|
1165
|
+
}, [session, refused?.code, refused?.message]);
|
|
1166
|
+
const onrampHidden = config && session && !refused ? onrampUnavailable(config, session.hello.capabilities) : null;
|
|
1167
|
+
useEffect(() => {
|
|
1168
|
+
if (!session || !onrampHidden) return;
|
|
1169
|
+
session.transport.emit("error", onrampHidden);
|
|
1170
|
+
}, [session, onrampHidden?.code, onrampHidden?.message]);
|
|
1171
|
+
if (!session || !config || refused) return null;
|
|
1172
|
+
const ready = config;
|
|
1173
|
+
const { transport } = session;
|
|
1174
|
+
const canOpenExternally = session.hello.capabilities.includes(CAPABILITY.OPEN_URL);
|
|
1175
|
+
const shared = {
|
|
1176
|
+
isOpen: true,
|
|
1177
|
+
inline: true,
|
|
1178
|
+
className: "rs-embed",
|
|
1179
|
+
backendUrl: ready.backendUrl,
|
|
1180
|
+
theme: config.theme,
|
|
1181
|
+
uiConfig: config.uiConfig,
|
|
1182
|
+
debug: config.debug,
|
|
1183
|
+
onReady: () => transport.emit("ready"),
|
|
1184
|
+
onEvent: (event) => transport.emit("analytics", event),
|
|
1185
|
+
onError: (data) => transport.emit("error", data),
|
|
1186
|
+
onClose: () => transport.emit("dismissRequested", {
|
|
1187
|
+
source: completedRef.current ? "flow-complete" : "close-button"
|
|
1188
|
+
})
|
|
1189
|
+
};
|
|
1190
|
+
if (ready.mode === "withdraw") {
|
|
1191
|
+
return /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
|
|
1192
|
+
WithdrawModal,
|
|
1193
|
+
{
|
|
1194
|
+
...shared,
|
|
1195
|
+
accountAddress: ready.accountAddress,
|
|
1196
|
+
sourceChain: ready.sourceChain,
|
|
1197
|
+
sourceToken: ready.sourceToken,
|
|
1198
|
+
targetChain: typeof ready.targetChain === "number" ? ready.targetChain : void 0,
|
|
1199
|
+
targetToken: ready.targetToken,
|
|
1200
|
+
recipient: ready.recipient,
|
|
1201
|
+
defaultAmount: ready.defaultAmount,
|
|
1202
|
+
forceRegister: ready.forceRegister,
|
|
1203
|
+
onSendTransaction: async (request) => ({
|
|
1204
|
+
txHash: await requestHostTransfer(transport, {
|
|
1205
|
+
chainId: request.chainId,
|
|
1206
|
+
token: request.token,
|
|
1207
|
+
amount: request.amount.toString(),
|
|
1208
|
+
to: request.to,
|
|
1209
|
+
from: ready.accountAddress
|
|
1210
|
+
})
|
|
1211
|
+
}),
|
|
1212
|
+
onUiState: handleUiState,
|
|
1213
|
+
onLifecycle: (event) => {
|
|
1214
|
+
if (event.type === "complete") completedRef.current = true;
|
|
1215
|
+
transport.emit("lifecycle", event);
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
) });
|
|
1219
|
+
}
|
|
1220
|
+
if (ready.mode === "claim") {
|
|
1221
|
+
return /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
|
|
1222
|
+
ClaimModal,
|
|
1223
|
+
{
|
|
1224
|
+
...shared,
|
|
1225
|
+
defaultTxHash: ready.defaultTxHash,
|
|
1226
|
+
defaultRefundDestination: ready.defaultDestination,
|
|
1227
|
+
signRecovery: async ({ chainId, signer, depositId, destination }) => (
|
|
1228
|
+
// The fields, not the struct: the host holds the canonical domain
|
|
1229
|
+
// and types, so this cannot become a request to sign anything else.
|
|
1230
|
+
await requestHostRecoverySignature(transport, {
|
|
1231
|
+
chainId,
|
|
1232
|
+
signer,
|
|
1233
|
+
depositId,
|
|
1234
|
+
destination
|
|
1235
|
+
})
|
|
1236
|
+
),
|
|
1237
|
+
onUiState: handleUiState,
|
|
1238
|
+
onLifecycle: (event) => {
|
|
1239
|
+
if (event.type === "complete") completedRef.current = true;
|
|
1240
|
+
transport.emit("lifecycle", event);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
) });
|
|
1244
|
+
}
|
|
1245
|
+
return /* @__PURE__ */ jsx(
|
|
1246
|
+
DepositModalInner,
|
|
1247
|
+
{
|
|
1248
|
+
...toModalProps(ready, canOpenExternally),
|
|
1249
|
+
openExternally: canOpenExternally ? openExternally : void 0,
|
|
1250
|
+
walletSession: session.wallet.session,
|
|
1251
|
+
hasWalletClientProp: false,
|
|
1252
|
+
onUiState: handleUiState,
|
|
1253
|
+
onReady: () => session.transport.emit("ready"),
|
|
1254
|
+
onLifecycle: (event) => {
|
|
1255
|
+
if (event.type === "complete") completedRef.current = true;
|
|
1256
|
+
session.transport.emit("lifecycle", event);
|
|
1257
|
+
},
|
|
1258
|
+
onEvent: (event) => session.transport.emit("analytics", event),
|
|
1259
|
+
onError: (data) => session.transport.emit("error", data),
|
|
1260
|
+
onClose: () => session.transport.emit("dismissRequested", {
|
|
1261
|
+
source: completedRef.current ? "flow-complete" : "close-button"
|
|
1262
|
+
})
|
|
1263
|
+
}
|
|
1264
|
+
);
|
|
1265
|
+
}
|
|
1266
|
+
export {
|
|
1267
|
+
DepositEmbed
|
|
1268
|
+
};
|