@swype-org/deposit 0.3.15 → 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-MA7CVDFD.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
@@ -251,7 +251,13 @@ var ALLOWED_RPC_METHODS = /* @__PURE__ */ new Set([
251
251
  "wallet_addEthereumChain",
252
252
  "wallet_switchEthereumChain",
253
253
  "wallet_sendCalls",
254
- "wallet_watchAsset"
254
+ "wallet_watchAsset",
255
+ // EIP-2255: lets the SDK revoke the dApp's `eth_accounts` grant before
256
+ // re-prompting on "+ Add wallet" / back-from-setup, so MetaMask shows the
257
+ // account picker instead of silently reusing the previously-permitted
258
+ // account. Wallets that don't implement it return method-not-supported,
259
+ // which the SDK swallows.
260
+ "wallet_revokePermissions"
255
261
  ]);
256
262
  var FORWARDED_PROVIDER_EVENTS = [
257
263
  "accountsChanged",
@@ -523,15 +529,19 @@ var STYLES = `
523
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}
524
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}}
525
531
  `;
526
- function createIframe(url, containerElement) {
532
+ function createIframe(url, containerElement, options) {
527
533
  ensureStyles();
528
534
  let closed = false;
535
+ let hidden = options?.hidden === true;
529
536
  let closeCallback = null;
530
537
  const overlay = document.createElement("div");
531
538
  overlay.setAttribute("data-blink-overlay", "");
532
539
  if (shouldUseMobileSheetLayout()) {
533
540
  overlay.setAttribute("data-blink-mobile-sheet", "");
534
541
  }
542
+ if (hidden) {
543
+ overlay.style.display = "none";
544
+ }
535
545
  const container = document.createElement("div");
536
546
  container.setAttribute("data-blink-container", "");
537
547
  const iframe = document.createElement("iframe");
@@ -557,8 +567,8 @@ function createIframe(url, containerElement) {
557
567
  };
558
568
  attachBridge();
559
569
  iframe.addEventListener("load", attachBridge);
560
- const savedOverflow = document.body.style.overflow;
561
- document.body.style.overflow = "hidden";
570
+ let savedOverflow = "";
571
+ let scrollLocked = false;
562
572
  const onBackdropClick = (event) => {
563
573
  if (event.target === overlay) triggerClose();
564
574
  };
@@ -568,15 +578,29 @@ function createIframe(url, containerElement) {
568
578
  const onTouchMove = (event) => {
569
579
  if (event.target === overlay) event.preventDefault();
570
580
  };
571
- overlay.addEventListener("click", onBackdropClick);
572
- overlay.addEventListener("touchmove", onTouchMove, { passive: false });
573
- 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
+ }
574
596
  function removeListeners() {
575
597
  overlay.removeEventListener("click", onBackdropClick);
576
598
  overlay.removeEventListener("touchmove", onTouchMove);
577
599
  document.removeEventListener("keydown", onKeyDown);
578
600
  }
579
601
  function unlockScroll() {
602
+ if (!scrollLocked) return;
603
+ scrollLocked = false;
580
604
  document.body.style.overflow = savedOverflow;
581
605
  }
582
606
  function triggerClose() {
@@ -614,6 +638,21 @@ function createIframe(url, containerElement) {
614
638
  isClosed() {
615
639
  return closed;
616
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
+ },
617
656
  onClose(callback) {
618
657
  closeCallback = callback;
619
658
  },
@@ -785,6 +824,13 @@ function buildSignerRequest(request, webviewBaseUrl) {
785
824
 
786
825
  // src/checkout.ts
787
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
+ }
788
834
  var Deposit = class {
789
835
  config;
790
836
  iframe = null;
@@ -797,6 +843,9 @@ var Deposit = class {
797
843
  _error = null;
798
844
  flowTimer = null;
799
845
  lastSignerResponse = null;
846
+ warmIframe = null;
847
+ warmIframeReady = null;
848
+ warmIframeReadyCancel = null;
800
849
  listeners = {
801
850
  complete: /* @__PURE__ */ new Set(),
802
851
  error: /* @__PURE__ */ new Set(),
@@ -827,6 +876,93 @@ var Deposit = class {
827
876
  this.log("Deposit instance created", {
828
877
  signer: typeof config.signer === "string" ? config.signer : "<function>"
829
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
+ }
830
966
  }
831
967
  /**
832
968
  * Open the hosted payment flow for the given deposit.
@@ -914,9 +1050,10 @@ var Deposit = class {
914
1050
  /** Tear down the instance and release all resources. */
915
1051
  destroy() {
916
1052
  this.log("destroy() called");
1053
+ this.destroyed = true;
917
1054
  this.cleanup();
1055
+ this.discardWarmIframe();
918
1056
  this.setStatus("idle");
919
- this.destroyed = true;
920
1057
  for (const set of Object.values(this.listeners)) {
921
1058
  set.clear();
922
1059
  }
@@ -930,6 +1067,17 @@ var Deposit = class {
930
1067
  console.debug(`[BlinkDeposit] ${message}`);
931
1068
  }
932
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
+ }
933
1081
  setStatus(status) {
934
1082
  if (this._status === status) return;
935
1083
  this.log(`Status: ${this._status} \u2192 ${status}`);
@@ -938,15 +1086,35 @@ var Deposit = class {
938
1086
  }
939
1087
  async runSignerFlow(request, requestId, onComplete, onError) {
940
1088
  try {
941
- const webviewBaseUrl = this.config.webviewBaseUrl ?? DEFAULT_WEBVIEW_BASE_URL;
1089
+ const webviewBaseUrl = resolveWebviewBaseUrl(this.config);
942
1090
  this.hostedOrigin = this.config.hostedFlowOrigin ?? new URL(webviewBaseUrl).origin;
943
1091
  this.log("Calling signer", {
944
1092
  signer: typeof this.config.signer === "string" ? this.config.signer : "<function>"
945
1093
  });
946
1094
  const signerRequest = buildSignerRequest(request, webviewBaseUrl);
947
- const preloadUrl = new URL(webviewBaseUrl);
948
- preloadUrl.searchParams.set("preload", "true");
949
- 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
+ }
950
1118
  this.iframe = preloadIframe;
951
1119
  preloadIframe.onClose(() => {
952
1120
  onError(
@@ -963,7 +1131,6 @@ var Deposit = class {
963
1131
  signerRequest,
964
1132
  this.config.signerTimeoutMs
965
1133
  );
966
- const iframeReadyPromise = this.waitForIframeReady(preloadIframe);
967
1134
  const [signerResponse, iframeReady] = await Promise.all([
968
1135
  signerPromise,
969
1136
  iframeReadyPromise
@@ -995,6 +1162,7 @@ var Deposit = class {
995
1162
  hostedUrl.searchParams.set("merchantId", signerResponse.merchantId);
996
1163
  hostedUrl.searchParams.set("payload", signerResponse.payload);
997
1164
  hostedUrl.searchParams.set("signature", signerResponse.signature);
1165
+ this.applyFullWidgetParam(hostedUrl);
998
1166
  const targetUrl = hostedUrl.toString();
999
1167
  const iframeHandle = createIframe(targetUrl, this.config.containerElement);
1000
1168
  this.iframe = iframeHandle;
@@ -1027,24 +1195,38 @@ var Deposit = class {
1027
1195
  }
1028
1196
  }
1029
1197
  }
1030
- waitForIframeReady(iframeHandle) {
1031
- 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) => {
1032
1208
  const handler = (event) => {
1033
1209
  if (event.origin !== this.hostedOrigin) return;
1034
1210
  if (event.source !== iframeHandle.contentWindow) return;
1035
1211
  if (parseIframeReady(event.data)) {
1036
- window.removeEventListener("message", handler);
1037
- clearTimeout(timeout);
1038
- resolve(true);
1212
+ finish(true);
1039
1213
  }
1040
1214
  };
1041
1215
  window.addEventListener("message", handler);
1042
1216
  const timeout = setTimeout(() => {
1043
- window.removeEventListener("message", handler);
1044
1217
  this.log("Iframe ready timeout \u2014 falling back to URL params");
1045
- resolve(false);
1046
- }, 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
+ };
1047
1228
  });
1229
+ return { promise, cancel: () => finish(false) };
1048
1230
  }
1049
1231
  startMessageListener(iframeHandle, onComplete) {
1050
1232
  const handler = (event) => {
@@ -1104,6 +1286,9 @@ var Deposit = class {
1104
1286
  }
1105
1287
  this.hostedOrigin = null;
1106
1288
  this.lastSignerResponse = null;
1289
+ if (!this.destroyed && this.config.preload !== false) {
1290
+ this.schedulePreload();
1291
+ }
1107
1292
  }
1108
1293
  emit(event, ...args) {
1109
1294
  for (const handler of this.listeners[event]) {
@@ -1114,6 +1299,119 @@ var Deposit = class {
1114
1299
  }
1115
1300
  }
1116
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
+ };
1117
1415
 
1118
1416
  // src/react.ts
1119
1417
  function useBlinkDeposit(config) {
@@ -1157,6 +1455,7 @@ function useBlinkDeposit(config) {
1157
1455
  var useBlinkCheckout = useBlinkDeposit;
1158
1456
  var useSwypeCheckout = useBlinkDeposit;
1159
1457
 
1458
+ exports.BlinkDepositButton = BlinkDepositButton;
1160
1459
  exports.DepositError = DepositError;
1161
1460
  exports.getDisplayMessage = getDisplayMessage;
1162
1461
  exports.useBlinkCheckout = useBlinkCheckout;