@swype-org/deposit 0.3.16 → 0.3.19

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/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { D as DepositStatus, a as DepositResult, b as DepositError, c as DepositConfig, d as DepositRequest } from './types-DYsZwNXq.js';
2
- export { C as CheckoutConfig, e as CheckoutError, f as CheckoutErrorCode, g as CheckoutStatus, h as DepositErrorCode, S as SignerFunction, i as SignerRequest, j as SignerResponse, T as TransferSummary, k as getDisplayMessage } from './types-DYsZwNXq.js';
1
+ import { D as DepositStatus, a as DepositResult, b as DepositError, c as DepositConfig, d as DepositRequest } from './types-Ds07N2zN.js';
2
+ export { C as CheckoutConfig, e as CheckoutError, f as CheckoutErrorCode, g as CheckoutStatus, h as DepositErrorCode, S as SignerFunction, i as SignerRequest, j as SignerResponse, T as TransferSummary, k as getDisplayMessage } from './types-Ds07N2zN.js';
3
3
 
4
4
  declare const DEFAULT_WEBVIEW_BASE_URL = "https://pay.blink.cash";
5
+ declare const SANDBOX_WEBVIEW_BASE_URL = "https://pay-sandbox.blink.cash";
5
6
  type EventMap = {
6
7
  complete: (result: DepositResult) => void;
7
8
  error: (error: DepositError) => void;
@@ -36,6 +37,9 @@ declare class Deposit {
36
37
  private _error;
37
38
  private flowTimer;
38
39
  private lastSignerResponse;
40
+ private warmIframe;
41
+ private warmIframeReady;
42
+ private warmIframeReadyCancel;
39
43
  private listeners;
40
44
  /** Current phase of the deposit flow. */
41
45
  get status(): DepositStatus;
@@ -46,6 +50,25 @@ declare class Deposit {
46
50
  /** Whether a deposit flow is currently in progress (signer loading or iframe open). */
47
51
  get isActive(): boolean;
48
52
  constructor(config: DepositConfig);
53
+ /**
54
+ * Warm up the hosted payment flow in a hidden iframe so `requestDeposit`
55
+ * opens an already-loaded page (JS bundle, auth session restore, and —
56
+ * when {@link DepositConfig.merchantId} is set — merchant config prefetch
57
+ * all happen in the background).
58
+ *
59
+ * Runs automatically after construction unless `preload: false`; call it
60
+ * manually to control the timing (e.g. on deposit-button hover). No-op
61
+ * when a warm iframe already exists or a deposit flow is active.
62
+ */
63
+ preload(): void;
64
+ private schedulePreload;
65
+ private buildPreloadUrl;
66
+ /**
67
+ * Detach the warm iframe for use by a starting flow. Returns null (and
68
+ * cleans up) when there is none or it was closed in the meantime.
69
+ */
70
+ private takeWarmIframe;
71
+ private discardWarmIframe;
49
72
  /**
50
73
  * Open the hosted payment flow for the given deposit.
51
74
  *
@@ -64,8 +87,21 @@ declare class Deposit {
64
87
  /** Tear down the instance and release all resources. */
65
88
  destroy(): void;
66
89
  private log;
90
+ /**
91
+ * Appends `enableFullWidget=false` to the hosted-flow URL when the merchant
92
+ * integration disables the full widget. Absence of the param means enabled
93
+ * (the default) — the hosted flow ANDs this with the merchant's backend
94
+ * config, so the full widget renders only when both allow it.
95
+ */
96
+ private applyFullWidgetParam;
67
97
  private setStatus;
68
98
  private runSignerFlow;
99
+ /**
100
+ * Resolves true when the iframe posts `blink:iframe-ready`, false on
101
+ * timeout. `cancel()` settles the promise as false immediately and releases
102
+ * the listener + timer — used to reap the generous warm-up wait once it no
103
+ * longer matters (warm iframe consumed or discarded).
104
+ */
69
105
  private waitForIframeReady;
70
106
  private startMessageListener;
71
107
  private cleanup;
@@ -232,4 +268,4 @@ declare function attachRpcHost(options: RpcHostOptions): RpcHostHandle;
232
268
 
233
269
  declare const VERSION = "0.3.0";
234
270
 
235
- export { ALLOWED_RPC_METHODS, type AdvertisedWallet, BRIDGE_PROTOCOL_VERSION, type EIP1193Provider as BridgeEIP1193Provider, type BridgeHelloMessage, type BridgeMessage, Checkout, DEFAULT_WEBVIEW_BASE_URL, Deposit, DepositConfig, DepositError, DepositRequest, DepositResult, DepositStatus, type EIP6963ProviderDetail, type EIP6963ProviderInfo, type RegisteredProvider, type RpcEventMessage, type RpcHostHandle, type RpcHostOptions, type RpcRequestMessage, type RpcResponseMessage, VERSION, type WalletDiscovererHandle, type WalletsAdvertisedMessage, attachRpcHost, createWalletDiscoverer, parseBridgeMessage };
271
+ export { ALLOWED_RPC_METHODS, type AdvertisedWallet, BRIDGE_PROTOCOL_VERSION, type EIP1193Provider as BridgeEIP1193Provider, type BridgeHelloMessage, type BridgeMessage, Checkout, DEFAULT_WEBVIEW_BASE_URL, Deposit, DepositConfig, DepositError, DepositRequest, DepositResult, DepositStatus, type EIP6963ProviderDetail, type EIP6963ProviderInfo, type RegisteredProvider, type RpcEventMessage, type RpcHostHandle, type RpcHostOptions, type RpcRequestMessage, type RpcResponseMessage, SANDBOX_WEBVIEW_BASE_URL, VERSION, type WalletDiscovererHandle, type WalletsAdvertisedMessage, attachRpcHost, createWalletDiscoverer, parseBridgeMessage };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { ALLOWED_RPC_METHODS, BRIDGE_PROTOCOL_VERSION, Checkout, CheckoutError, DEFAULT_WEBVIEW_BASE_URL, Deposit, DepositError, attachRpcHost, createWalletDiscoverer, getDisplayMessage, parseBridgeMessage } from './chunk-WD6DVQHT.js';
1
+ export { ALLOWED_RPC_METHODS, BRIDGE_PROTOCOL_VERSION, Checkout, CheckoutError, DEFAULT_WEBVIEW_BASE_URL, Deposit, DepositError, SANDBOX_WEBVIEW_BASE_URL, attachRpcHost, createWalletDiscoverer, getDisplayMessage, parseBridgeMessage } from './chunk-VMXCXXVN.js';
2
2
 
3
3
  // src/index.ts
4
4
  var VERSION = "0.3.0";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;AA2CO,IAAM,OAAA,GAAU","file":"index.js","sourcesContent":["export { Deposit, Checkout, DEFAULT_WEBVIEW_BASE_URL } from './checkout.ts';\nexport { DepositError, CheckoutError, getDisplayMessage } from './errors.ts';\nexport type { DepositErrorCode, CheckoutErrorCode } from './errors.ts';\nexport type {\n DepositConfig,\n CheckoutConfig,\n DepositStatus,\n CheckoutStatus,\n DepositRequest,\n DepositResult,\n SignerFunction,\n SignerRequest,\n SignerResponse,\n TransferSummary,\n} from './types.ts';\n\n// ── Wallet bridge (advanced) ──────────────────────────────────────────\n// Internal building blocks for the top-frame → iframe wallet bridge.\n// Default merchant integrations don't need these — `createIframe()` wires\n// them automatically. Re-exported so monorepo tests and any merchant\n// custom-iframe wrapper can poke at the protocol shapes.\nexport { createWalletDiscoverer } from './walletBridge/discover.ts';\nexport type { WalletDiscovererHandle, RegisteredProvider } from './walletBridge/discover.ts';\nexport { attachRpcHost } from './walletBridge/rpcHost.ts';\nexport type { RpcHostHandle, RpcHostOptions } from './walletBridge/rpcHost.ts';\nexport {\n ALLOWED_RPC_METHODS,\n BRIDGE_PROTOCOL_VERSION,\n parseBridgeMessage,\n} from './walletBridge/protocol.ts';\nexport type {\n AdvertisedWallet,\n BridgeMessage,\n BridgeHelloMessage,\n WalletsAdvertisedMessage,\n RpcRequestMessage,\n RpcResponseMessage,\n RpcEventMessage,\n EIP1193Provider as BridgeEIP1193Provider,\n EIP6963ProviderInfo,\n EIP6963ProviderDetail,\n} from './walletBridge/protocol.ts';\n\nexport const VERSION = '0.3.0';\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;AA2CO,IAAM,OAAA,GAAU","file":"index.js","sourcesContent":["export { Deposit, Checkout, DEFAULT_WEBVIEW_BASE_URL, SANDBOX_WEBVIEW_BASE_URL } from './checkout.ts';\nexport { DepositError, CheckoutError, getDisplayMessage } from './errors.ts';\nexport type { DepositErrorCode, CheckoutErrorCode } from './errors.ts';\nexport type {\n DepositConfig,\n CheckoutConfig,\n DepositStatus,\n CheckoutStatus,\n DepositRequest,\n DepositResult,\n SignerFunction,\n SignerRequest,\n SignerResponse,\n TransferSummary,\n} from './types.ts';\n\n// ── Wallet bridge (advanced) ──────────────────────────────────────────\n// Internal building blocks for the top-frame → iframe wallet bridge.\n// Default merchant integrations don't need these — `createIframe()` wires\n// them automatically. Re-exported so monorepo tests and any merchant\n// custom-iframe wrapper can poke at the protocol shapes.\nexport { createWalletDiscoverer } from './walletBridge/discover.ts';\nexport type { WalletDiscovererHandle, RegisteredProvider } from './walletBridge/discover.ts';\nexport { attachRpcHost } from './walletBridge/rpcHost.ts';\nexport type { RpcHostHandle, RpcHostOptions } from './walletBridge/rpcHost.ts';\nexport {\n ALLOWED_RPC_METHODS,\n BRIDGE_PROTOCOL_VERSION,\n parseBridgeMessage,\n} from './walletBridge/protocol.ts';\nexport type {\n AdvertisedWallet,\n BridgeMessage,\n BridgeHelloMessage,\n WalletsAdvertisedMessage,\n RpcRequestMessage,\n RpcResponseMessage,\n RpcEventMessage,\n EIP1193Provider as BridgeEIP1193Provider,\n EIP6963ProviderInfo,\n EIP6963ProviderDetail,\n} from './walletBridge/protocol.ts';\n\nexport const VERSION = '0.3.0';\n"]}
package/dist/react.cjs CHANGED
@@ -529,15 +529,19 @@ var STYLES = `
529
529
  [data-blink-overlay][data-blink-mobile-sheet][data-blink-closing] [data-blink-container]{opacity:1;animation:blink-slide-down-full ${CLOSE_DURATION_MS}ms ease-in forwards}
530
530
  @media(max-width:${MOBILE_SHEET_MAX_VIEWPORT_PX}px){[data-blink-overlay]{align-items:flex-end;touch-action:none;overscroll-behavior:contain}[data-blink-container]{width:100%;max-width:100%;height:79vh;border-radius:24px 24px 0 0;box-shadow:0 -8px 40px rgba(0,0,0,.25);animation:blink-slide-up-full .35s cubic-bezier(.32,.72,0,1);padding-bottom:env(safe-area-inset-bottom,0px)}[data-blink-overlay][data-blink-closing] [data-blink-container]{opacity:1;animation:blink-slide-down-full ${CLOSE_DURATION_MS}ms ease-in forwards}}
531
531
  `;
532
- function createIframe(url, containerElement) {
532
+ function createIframe(url, containerElement, options) {
533
533
  ensureStyles();
534
534
  let closed = false;
535
+ let hidden = options?.hidden === true;
535
536
  let closeCallback = null;
536
537
  const overlay = document.createElement("div");
537
538
  overlay.setAttribute("data-blink-overlay", "");
538
539
  if (shouldUseMobileSheetLayout()) {
539
540
  overlay.setAttribute("data-blink-mobile-sheet", "");
540
541
  }
542
+ if (hidden) {
543
+ overlay.style.display = "none";
544
+ }
541
545
  const container = document.createElement("div");
542
546
  container.setAttribute("data-blink-container", "");
543
547
  const iframe = document.createElement("iframe");
@@ -563,8 +567,8 @@ function createIframe(url, containerElement) {
563
567
  };
564
568
  attachBridge();
565
569
  iframe.addEventListener("load", attachBridge);
566
- const savedOverflow = document.body.style.overflow;
567
- document.body.style.overflow = "hidden";
570
+ let savedOverflow = "";
571
+ let scrollLocked = false;
568
572
  const onBackdropClick = (event) => {
569
573
  if (event.target === overlay) triggerClose();
570
574
  };
@@ -574,15 +578,29 @@ function createIframe(url, containerElement) {
574
578
  const onTouchMove = (event) => {
575
579
  if (event.target === overlay) event.preventDefault();
576
580
  };
577
- overlay.addEventListener("click", onBackdropClick);
578
- overlay.addEventListener("touchmove", onTouchMove, { passive: false });
579
- document.addEventListener("keydown", onKeyDown);
581
+ function lockScroll() {
582
+ if (scrollLocked) return;
583
+ scrollLocked = true;
584
+ savedOverflow = document.body.style.overflow;
585
+ document.body.style.overflow = "hidden";
586
+ }
587
+ function attachDismissalListeners() {
588
+ overlay.addEventListener("click", onBackdropClick);
589
+ overlay.addEventListener("touchmove", onTouchMove, { passive: false });
590
+ document.addEventListener("keydown", onKeyDown);
591
+ }
592
+ if (!hidden) {
593
+ lockScroll();
594
+ attachDismissalListeners();
595
+ }
580
596
  function removeListeners() {
581
597
  overlay.removeEventListener("click", onBackdropClick);
582
598
  overlay.removeEventListener("touchmove", onTouchMove);
583
599
  document.removeEventListener("keydown", onKeyDown);
584
600
  }
585
601
  function unlockScroll() {
602
+ if (!scrollLocked) return;
603
+ scrollLocked = false;
586
604
  document.body.style.overflow = savedOverflow;
587
605
  }
588
606
  function triggerClose() {
@@ -620,6 +638,21 @@ function createIframe(url, containerElement) {
620
638
  isClosed() {
621
639
  return closed;
622
640
  },
641
+ isHidden() {
642
+ return hidden;
643
+ },
644
+ reveal() {
645
+ if (closed || !hidden) return;
646
+ hidden = false;
647
+ if (shouldUseMobileSheetLayout()) {
648
+ overlay.setAttribute("data-blink-mobile-sheet", "");
649
+ } else {
650
+ overlay.removeAttribute("data-blink-mobile-sheet");
651
+ }
652
+ overlay.style.display = "";
653
+ lockScroll();
654
+ attachDismissalListeners();
655
+ },
623
656
  onClose(callback) {
624
657
  closeCallback = callback;
625
658
  },
@@ -791,6 +824,13 @@ function buildSignerRequest(request, webviewBaseUrl) {
791
824
 
792
825
  // src/checkout.ts
793
826
  var DEFAULT_WEBVIEW_BASE_URL = "https://pay.blink.cash";
827
+ var SANDBOX_WEBVIEW_BASE_URL = "https://pay-sandbox.blink.cash";
828
+ var IFRAME_READY_TIMEOUT_MS = 2e3;
829
+ var WARM_IFRAME_READY_TIMEOUT_MS = 6e4;
830
+ function resolveWebviewBaseUrl(config) {
831
+ if (config.webviewBaseUrl) return config.webviewBaseUrl;
832
+ return config.environment === "sandbox" ? SANDBOX_WEBVIEW_BASE_URL : DEFAULT_WEBVIEW_BASE_URL;
833
+ }
794
834
  var Deposit = class {
795
835
  config;
796
836
  iframe = null;
@@ -803,6 +843,9 @@ var Deposit = class {
803
843
  _error = null;
804
844
  flowTimer = null;
805
845
  lastSignerResponse = null;
846
+ warmIframe = null;
847
+ warmIframeReady = null;
848
+ warmIframeReadyCancel = null;
806
849
  listeners = {
807
850
  complete: /* @__PURE__ */ new Set(),
808
851
  error: /* @__PURE__ */ new Set(),
@@ -833,6 +876,93 @@ var Deposit = class {
833
876
  this.log("Deposit instance created", {
834
877
  signer: typeof config.signer === "string" ? config.signer : "<function>"
835
878
  });
879
+ if (config.preload !== false) {
880
+ this.schedulePreload();
881
+ }
882
+ }
883
+ /**
884
+ * Warm up the hosted payment flow in a hidden iframe so `requestDeposit`
885
+ * opens an already-loaded page (JS bundle, auth session restore, and —
886
+ * when {@link DepositConfig.merchantId} is set — merchant config prefetch
887
+ * all happen in the background).
888
+ *
889
+ * Runs automatically after construction unless `preload: false`; call it
890
+ * manually to control the timing (e.g. on deposit-button hover). No-op
891
+ * when a warm iframe already exists or a deposit flow is active.
892
+ */
893
+ preload() {
894
+ if (this.destroyed || this.isActive) return;
895
+ if (this.warmIframe && !this.warmIframe.isClosed()) return;
896
+ if (typeof document === "undefined" || typeof window === "undefined") return;
897
+ const webviewBaseUrl = resolveWebviewBaseUrl(this.config);
898
+ this.hostedOrigin = this.config.hostedFlowOrigin ?? new URL(webviewBaseUrl).origin;
899
+ const preloadUrl = this.buildPreloadUrl(webviewBaseUrl);
900
+ const iframe = createIframe(preloadUrl, this.config.containerElement, { hidden: true });
901
+ const ready = this.waitForIframeReady(iframe, WARM_IFRAME_READY_TIMEOUT_MS);
902
+ this.warmIframe = iframe;
903
+ this.warmIframeReady = ready.promise;
904
+ this.warmIframeReadyCancel = ready.cancel;
905
+ this.log("Warm-up iframe created", { url: preloadUrl });
906
+ }
907
+ // Defer the automatic warm-up until the host page has finished its own
908
+ // load and the main thread is idle — constructing a Deposit instance must
909
+ // never slow the merchant page down.
910
+ schedulePreload() {
911
+ if (typeof window === "undefined" || typeof document === "undefined") return;
912
+ const start = () => {
913
+ if (!this.destroyed && !this.isActive) this.preload();
914
+ };
915
+ const whenIdle = () => {
916
+ const w = window;
917
+ if (typeof w.requestIdleCallback === "function") {
918
+ w.requestIdleCallback(start, { timeout: 3e3 });
919
+ } else {
920
+ setTimeout(start, 250);
921
+ }
922
+ };
923
+ if (document.readyState === "complete") {
924
+ whenIdle();
925
+ } else {
926
+ window.addEventListener("load", whenIdle, { once: true });
927
+ }
928
+ }
929
+ buildPreloadUrl(webviewBaseUrl) {
930
+ const preloadUrl = new URL(webviewBaseUrl);
931
+ preloadUrl.searchParams.set("preload", "true");
932
+ if (this.config.merchantId) {
933
+ preloadUrl.searchParams.set("merchantId", this.config.merchantId);
934
+ }
935
+ this.applyFullWidgetParam(preloadUrl);
936
+ return preloadUrl.toString();
937
+ }
938
+ /**
939
+ * Detach the warm iframe for use by a starting flow. Returns null (and
940
+ * cleans up) when there is none or it was closed in the meantime.
941
+ */
942
+ takeWarmIframe() {
943
+ const iframe = this.warmIframe;
944
+ const ready = this.warmIframeReady;
945
+ const cancelReady = this.warmIframeReadyCancel ?? (() => {
946
+ });
947
+ this.warmIframe = null;
948
+ this.warmIframeReady = null;
949
+ this.warmIframeReadyCancel = null;
950
+ if (!iframe || !ready) return null;
951
+ if (iframe.isClosed()) {
952
+ cancelReady();
953
+ iframe.destroy();
954
+ return null;
955
+ }
956
+ return { iframe, ready, cancelReady };
957
+ }
958
+ discardWarmIframe() {
959
+ this.warmIframeReadyCancel?.();
960
+ this.warmIframeReadyCancel = null;
961
+ this.warmIframeReady = null;
962
+ if (this.warmIframe) {
963
+ this.warmIframe.destroy();
964
+ this.warmIframe = null;
965
+ }
836
966
  }
837
967
  /**
838
968
  * Open the hosted payment flow for the given deposit.
@@ -920,9 +1050,10 @@ var Deposit = class {
920
1050
  /** Tear down the instance and release all resources. */
921
1051
  destroy() {
922
1052
  this.log("destroy() called");
1053
+ this.destroyed = true;
923
1054
  this.cleanup();
1055
+ this.discardWarmIframe();
924
1056
  this.setStatus("idle");
925
- this.destroyed = true;
926
1057
  for (const set of Object.values(this.listeners)) {
927
1058
  set.clear();
928
1059
  }
@@ -936,6 +1067,17 @@ var Deposit = class {
936
1067
  console.debug(`[BlinkDeposit] ${message}`);
937
1068
  }
938
1069
  }
1070
+ /**
1071
+ * Appends `enableFullWidget=false` to the hosted-flow URL when the merchant
1072
+ * integration disables the full widget. Absence of the param means enabled
1073
+ * (the default) — the hosted flow ANDs this with the merchant's backend
1074
+ * config, so the full widget renders only when both allow it.
1075
+ */
1076
+ applyFullWidgetParam(url) {
1077
+ if (this.config.enableFullWidget === false) {
1078
+ url.searchParams.set("enableFullWidget", "false");
1079
+ }
1080
+ }
939
1081
  setStatus(status) {
940
1082
  if (this._status === status) return;
941
1083
  this.log(`Status: ${this._status} \u2192 ${status}`);
@@ -944,15 +1086,35 @@ var Deposit = class {
944
1086
  }
945
1087
  async runSignerFlow(request, requestId, onComplete, onError) {
946
1088
  try {
947
- const webviewBaseUrl = this.config.webviewBaseUrl ?? DEFAULT_WEBVIEW_BASE_URL;
1089
+ const webviewBaseUrl = resolveWebviewBaseUrl(this.config);
948
1090
  this.hostedOrigin = this.config.hostedFlowOrigin ?? new URL(webviewBaseUrl).origin;
949
1091
  this.log("Calling signer", {
950
1092
  signer: typeof this.config.signer === "string" ? this.config.signer : "<function>"
951
1093
  });
952
1094
  const signerRequest = buildSignerRequest(request, webviewBaseUrl);
953
- const preloadUrl = new URL(webviewBaseUrl);
954
- preloadUrl.searchParams.set("preload", "true");
955
- const preloadIframe = createIframe(preloadUrl.toString(), this.config.containerElement);
1095
+ let preloadIframe;
1096
+ let iframeReadyPromise;
1097
+ const warm = this.takeWarmIframe();
1098
+ if (warm) {
1099
+ warm.iframe.reveal();
1100
+ preloadIframe = warm.iframe;
1101
+ iframeReadyPromise = Promise.race([
1102
+ warm.ready,
1103
+ new Promise(
1104
+ (resolve) => setTimeout(() => resolve(false), IFRAME_READY_TIMEOUT_MS)
1105
+ )
1106
+ ]).then((ready) => {
1107
+ warm.cancelReady();
1108
+ return ready;
1109
+ });
1110
+ this.log("Reusing warm preload iframe");
1111
+ } else {
1112
+ preloadIframe = createIframe(
1113
+ this.buildPreloadUrl(webviewBaseUrl),
1114
+ this.config.containerElement
1115
+ );
1116
+ iframeReadyPromise = this.waitForIframeReady(preloadIframe).promise;
1117
+ }
956
1118
  this.iframe = preloadIframe;
957
1119
  preloadIframe.onClose(() => {
958
1120
  onError(
@@ -969,7 +1131,6 @@ var Deposit = class {
969
1131
  signerRequest,
970
1132
  this.config.signerTimeoutMs
971
1133
  );
972
- const iframeReadyPromise = this.waitForIframeReady(preloadIframe);
973
1134
  const [signerResponse, iframeReady] = await Promise.all([
974
1135
  signerPromise,
975
1136
  iframeReadyPromise
@@ -1001,6 +1162,7 @@ var Deposit = class {
1001
1162
  hostedUrl.searchParams.set("merchantId", signerResponse.merchantId);
1002
1163
  hostedUrl.searchParams.set("payload", signerResponse.payload);
1003
1164
  hostedUrl.searchParams.set("signature", signerResponse.signature);
1165
+ this.applyFullWidgetParam(hostedUrl);
1004
1166
  const targetUrl = hostedUrl.toString();
1005
1167
  const iframeHandle = createIframe(targetUrl, this.config.containerElement);
1006
1168
  this.iframe = iframeHandle;
@@ -1033,24 +1195,38 @@ var Deposit = class {
1033
1195
  }
1034
1196
  }
1035
1197
  }
1036
- waitForIframeReady(iframeHandle) {
1037
- return new Promise((resolve) => {
1198
+ /**
1199
+ * Resolves true when the iframe posts `blink:iframe-ready`, false on
1200
+ * timeout. `cancel()` settles the promise as false immediately and releases
1201
+ * the listener + timer — used to reap the generous warm-up wait once it no
1202
+ * longer matters (warm iframe consumed or discarded).
1203
+ */
1204
+ waitForIframeReady(iframeHandle, timeoutMs = IFRAME_READY_TIMEOUT_MS) {
1205
+ let finish = () => {
1206
+ };
1207
+ const promise = new Promise((resolve) => {
1038
1208
  const handler = (event) => {
1039
1209
  if (event.origin !== this.hostedOrigin) return;
1040
1210
  if (event.source !== iframeHandle.contentWindow) return;
1041
1211
  if (parseIframeReady(event.data)) {
1042
- window.removeEventListener("message", handler);
1043
- clearTimeout(timeout);
1044
- resolve(true);
1212
+ finish(true);
1045
1213
  }
1046
1214
  };
1047
1215
  window.addEventListener("message", handler);
1048
1216
  const timeout = setTimeout(() => {
1049
- window.removeEventListener("message", handler);
1050
1217
  this.log("Iframe ready timeout \u2014 falling back to URL params");
1051
- resolve(false);
1052
- }, 2e3);
1218
+ finish(false);
1219
+ }, timeoutMs);
1220
+ let settled = false;
1221
+ finish = (ready) => {
1222
+ if (settled) return;
1223
+ settled = true;
1224
+ window.removeEventListener("message", handler);
1225
+ clearTimeout(timeout);
1226
+ resolve(ready);
1227
+ };
1053
1228
  });
1229
+ return { promise, cancel: () => finish(false) };
1054
1230
  }
1055
1231
  startMessageListener(iframeHandle, onComplete) {
1056
1232
  const handler = (event) => {
@@ -1110,6 +1286,9 @@ var Deposit = class {
1110
1286
  }
1111
1287
  this.hostedOrigin = null;
1112
1288
  this.lastSignerResponse = null;
1289
+ if (!this.destroyed && this.config.preload !== false) {
1290
+ this.schedulePreload();
1291
+ }
1113
1292
  }
1114
1293
  emit(event, ...args) {
1115
1294
  for (const handler of this.listeners[event]) {
@@ -1120,6 +1299,119 @@ var Deposit = class {
1120
1299
  }
1121
1300
  }
1122
1301
  };
1302
+ function svgToDataUri(svg) {
1303
+ return `data:image/svg+xml,${encodeURIComponent(svg)}`;
1304
+ }
1305
+ var USDC_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2000 2000">
1306
+ <path d="M1000 2000c554.17 0 1000-445.83 1000-1000S1554.17 0 1000 0 0 445.83 0 1000s445.83 1000 1000 1000z" fill="#2775ca"/>
1307
+ <path d="M1275 1158.33c0-145.83-87.5-195.83-262.5-216.66-125-16.67-150-50-150-108.34s41.67-95.83 125-95.83c75 0 116.67 25 137.5 87.5 4.17 12.5 16.67 20.83 29.17 20.83h66.66c16.67 0 29.17-12.5 29.17-29.16v-4.17c-16.67-91.67-91.67-162.5-187.5-170.83v-100c0-16.67-12.5-29.17-33.33-33.34h-62.5c-16.67 0-29.17 12.5-33.34 33.34v95.83c-125 16.67-204.16 100-204.16 204.17 0 137.5 83.33 191.66 258.33 212.5 116.67 20.83 154.17 45.83 154.17 112.5s-58.34 112.5-137.5 112.5c-108.34 0-145.84-45.84-158.34-108.34-4.16-16.66-16.66-25-29.16-25h-70.84c-16.66 0-29.16 12.5-29.16 29.17v4.17c16.66 104.16 83.33 179.16 220.83 200v100c0 16.66 12.5 29.16 33.33 33.33h62.5c16.67 0 29.17-12.5 33.34-33.33v-100c125-20.84 208.33-108.34 208.33-220.84z" fill="#fff"/>
1308
+ <path d="M787.5 1595.83c-325-116.66-491.67-479.16-370.83-800 62.5-175 200-308.33 370.83-370.83 16.67-8.33 25-20.83 25-41.67V325c0-16.67-8.33-29.17-25-33.33-4.17 0-12.5 0-16.67 4.16-395.83 125-612.5 545.84-487.5 941.67 75 233.33 254.17 412.5 487.5 487.5 16.67 8.33 33.34 0 37.5-16.67 4.17-4.16 4.17-8.33 4.17-16.66v-58.34c0-12.5-12.5-29.16-25-37.5zM1229.17 295.83c-16.67-8.33-33.34 0-37.5 16.67-4.17 4.17-4.17 8.33-4.17 16.67v58.33c0 16.67 12.5 33.33 25 41.67 325 116.66 491.67 479.16 370.83 800-62.5 175-200 308.33-370.83 370.83-16.67 8.33-25 20.83-25 41.67V1700c0 16.67 8.33 29.17 25 33.33 4.17 0 12.5 0 16.67-4.16 395.83-125 612.5-545.84 487.5-941.67-75-237.5-258.34-416.67-487.5-491.67z" fill="#fff"/>
1309
+ </svg>`;
1310
+ var TETHER_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800">
1311
+ <circle fill="#009393" cx="400" cy="400" r="400"/>
1312
+ <path fill="#fff" fill-rule="evenodd" d="M400.49,428.59c68.79,0,126.28-11.63,140.33-27.17-11.93-13.18-55.08-23.56-109.88-26.4v32.83c-9.81.51-20.01.76-30.46.76s-20.65-.25-30.48-.76v-32.83c-54.78,2.84-97.95,13.22-109.88,26.4,14.07,15.54,71.57,27.17,140.36,27.17ZM522.71,274.06v45.21h-91.77v31.35c64.46,3.35,112.83,17.13,113.19,33.62v34.38c-.36,16.49-48.73,30.24-113.19,33.6v76.94h-60.93v-76.94c-64.46-3.35-112.81-17.11-113.17-33.6v-34.38c.36-16.49,48.71-30.27,113.17-33.62v-31.35h-91.77v-45.21h244.48ZM242.15,202.11h322.16c7.7,0,14.79,4.05,18.63,10.63l93.85,161.16c4.86,8.36,3.42,18.91-3.52,25.68l-258.34,252.18c-8.38,8.17-21.84,8.17-30.2,0L126.71,399.92c-7.09-6.94-8.43-17.79-3.2-26.19l100.33-161.49c3.91-6.28,10.85-10.12,18.32-10.12Z"/>
1313
+ </svg>`;
1314
+ var USDC_LOGO = svgToDataUri(USDC_SVG);
1315
+ var TETHER_LOGO = svgToDataUri(TETHER_SVG);
1316
+ function BlinkDepositButton({
1317
+ onClick,
1318
+ disabled,
1319
+ loading
1320
+ }) {
1321
+ const [hovered, setHovered] = react.useState(false);
1322
+ const [pressed, setPressed] = react.useState(false);
1323
+ const inactive = !!disabled || !!loading;
1324
+ return react.createElement(
1325
+ "button",
1326
+ {
1327
+ type: "button",
1328
+ onClick,
1329
+ disabled: inactive,
1330
+ onMouseEnter: () => setHovered(true),
1331
+ onMouseLeave: () => {
1332
+ setHovered(false);
1333
+ setPressed(false);
1334
+ },
1335
+ onMouseDown: () => setPressed(true),
1336
+ onMouseUp: () => setPressed(false),
1337
+ style: pillStyle({ hovered, pressed, inactive, loading: !!loading })
1338
+ },
1339
+ react.createElement(
1340
+ "span",
1341
+ { style: labelStackStyle },
1342
+ react.createElement("span", { style: titleStyle }, "Deposit stablecoins"),
1343
+ react.createElement("span", { style: subtitleStyle }, "In a Blink")
1344
+ ),
1345
+ react.createElement(
1346
+ "span",
1347
+ { style: coinsStyle },
1348
+ // The left (USDC) coin paints over the right one, against DOM paint order.
1349
+ react.createElement("img", {
1350
+ src: USDC_LOGO,
1351
+ alt: "",
1352
+ "aria-hidden": "true",
1353
+ style: { ...coinStyle, marginRight: -8, position: "relative", zIndex: 1 }
1354
+ }),
1355
+ react.createElement("img", { src: TETHER_LOGO, alt: "", "aria-hidden": "true", style: coinStyle })
1356
+ )
1357
+ );
1358
+ }
1359
+ var pillStyle = (state) => ({
1360
+ display: "flex",
1361
+ alignItems: "center",
1362
+ justifyContent: "space-between",
1363
+ gap: 8,
1364
+ width: "100%",
1365
+ height: 56,
1366
+ padding: "12px 15px 12px 22px",
1367
+ backgroundColor: "#000000",
1368
+ // A faint white film on hover rather than `filter: brightness()` — a
1369
+ // brightness multiply leaves pure black unchanged.
1370
+ backgroundImage: !state.inactive && state.hovered ? "linear-gradient(rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.12))" : void 0,
1371
+ color: "#fafafa",
1372
+ border: "none",
1373
+ borderRadius: 36,
1374
+ cursor: state.inactive ? state.loading ? "progress" : "not-allowed" : "pointer",
1375
+ opacity: state.inactive ? 0.72 : 1,
1376
+ boxSizing: "border-box",
1377
+ // Explicit stack so the button is self-sufficient on any host page.
1378
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
1379
+ textAlign: "left",
1380
+ transform: !state.inactive && state.pressed ? "scale(0.98)" : void 0,
1381
+ transition: "background-color 0.15s ease, transform 0.15s ease, opacity 0.15s ease"
1382
+ });
1383
+ var labelStackStyle = {
1384
+ display: "flex",
1385
+ flexDirection: "column",
1386
+ minWidth: 0
1387
+ };
1388
+ var titleStyle = {
1389
+ fontSize: 16,
1390
+ fontWeight: 700,
1391
+ lineHeight: "normal",
1392
+ whiteSpace: "nowrap"
1393
+ };
1394
+ var subtitleStyle = {
1395
+ fontSize: 16,
1396
+ fontWeight: 400,
1397
+ fontStyle: "italic",
1398
+ lineHeight: "normal",
1399
+ whiteSpace: "nowrap"
1400
+ };
1401
+ var coinsStyle = {
1402
+ display: "inline-flex",
1403
+ alignItems: "center",
1404
+ flexShrink: 0,
1405
+ // Scope the front coin's z-index so it can't escape over host page layers.
1406
+ isolation: "isolate"
1407
+ };
1408
+ var coinStyle = {
1409
+ width: 29,
1410
+ height: 29,
1411
+ borderRadius: "50%",
1412
+ objectFit: "cover",
1413
+ display: "block"
1414
+ };
1123
1415
 
1124
1416
  // src/react.ts
1125
1417
  function useBlinkDeposit(config) {
@@ -1163,6 +1455,7 @@ function useBlinkDeposit(config) {
1163
1455
  var useBlinkCheckout = useBlinkDeposit;
1164
1456
  var useSwypeCheckout = useBlinkDeposit;
1165
1457
 
1458
+ exports.BlinkDepositButton = BlinkDepositButton;
1166
1459
  exports.DepositError = DepositError;
1167
1460
  exports.getDisplayMessage = getDisplayMessage;
1168
1461
  exports.useBlinkCheckout = useBlinkCheckout;