@funkit/connect 9.11.0 → 9.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.
Files changed (73) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +21 -0
  3. package/dist/__generated__/default_feature_gates.d.ts +1 -0
  4. package/dist/clients/lighter.d.ts +0 -18
  5. package/dist/clients/lighter.js +5 -99
  6. package/dist/clients/polymarket.d.ts +97 -9
  7. package/dist/clients/polymarket.js +181 -10
  8. package/dist/components/Dialog/DialogContent.css.d.ts +1 -1
  9. package/dist/components/FunNotificationBanner/FunNotificationBanner.d.ts +1 -1
  10. package/dist/components/FunOptionBox/FunOptionBox.css.d.ts +1 -1
  11. package/dist/components/TransactionStatus/AnimatedText.d.ts +4 -0
  12. package/dist/hooks/queries/useErc20Asset.d.ts +2 -1
  13. package/dist/hooks/queries/useRecentDeposits.d.ts +39 -49
  14. package/dist/hooks/useAssetPrice.d.ts +6 -2
  15. package/dist/hooks/useCheckoutDirectExecution.d.ts +7 -1
  16. package/dist/index.css +2 -2
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.js +3140 -2844
  19. package/dist/modals/CheckoutModal/SourceChange/FlatList.d.ts +31 -0
  20. package/dist/modals/CheckoutModal/SourceChange/GroupedList.d.ts +25 -0
  21. package/dist/modals/CheckoutModal/SourceChange/SourceList.d.ts +17 -9
  22. package/dist/modals/CheckoutModal/SourceChange/buildSourceGroups.d.ts +47 -0
  23. package/dist/modals/CheckoutModal/TransferToken/types.d.ts +1 -2
  24. package/dist/modals/ProfileDetails/FunProfileViews/Home/HomeCheckoutDisplayRow.css.d.ts +1 -1
  25. package/dist/modals/WithdrawalModal/ErrorMessage.d.ts +4 -0
  26. package/dist/modals/WithdrawalModal/LighterWithdrawal.d.ts +16 -0
  27. package/dist/modals/WithdrawalModal/RecipientAddress.d.ts +11 -0
  28. package/dist/providers/FunkitCheckoutContext/index.d.ts +2 -2
  29. package/dist/providers/FunkitCheckoutContext/types.d.ts +22 -1
  30. package/dist/providers/FunkitConfigContext.d.ts +2 -0
  31. package/dist/providers/FunkitStatsigProvider.d.ts +1 -0
  32. package/dist/providers/GeneralWalletProvider.d.ts +2 -4
  33. package/dist/utils/statsig/checkFeatureGate.d.ts +19 -0
  34. package/dist/utils/userInfo.d.ts +36 -0
  35. package/dist/wallets/Wallet.d.ts +11 -0
  36. package/dist/wallets/walletConnectors/bifrostWallet/bifrostWallet.js +2 -2
  37. package/dist/wallets/walletConnectors/bitgetWallet/bitgetWallet.js +2 -2
  38. package/dist/wallets/walletConnectors/bybitWallet/bybitWallet.js +2 -2
  39. package/dist/wallets/walletConnectors/clvWallet/clvWallet.js +2 -2
  40. package/dist/wallets/walletConnectors/coin98Wallet/coin98Wallet.js +2 -2
  41. package/dist/wallets/walletConnectors/coreWallet/coreWallet.js +2 -2
  42. package/dist/wallets/walletConnectors/foxWallet/foxWallet.js +2 -2
  43. package/dist/wallets/walletConnectors/frontierWallet/frontierWallet.js +2 -2
  44. package/dist/wallets/walletConnectors/gateWallet/gateWallet.js +2 -2
  45. package/dist/wallets/walletConnectors/index.js +30 -30
  46. package/dist/wallets/walletConnectors/metaMaskWallet/metaMaskWallet.js +2 -2
  47. package/dist/wallets/walletConnectors/okxWallet/okxWallet.js +2 -2
  48. package/dist/wallets/walletConnectors/rainbowWallet/rainbowWallet.js +2 -2
  49. package/dist/wallets/walletConnectors/roninWallet/roninWallet.js +2 -2
  50. package/dist/wallets/walletConnectors/safepalWallet/safepalWallet.js +2 -2
  51. package/dist/wallets/walletConnectors/subWallet/subWallet.js +2 -2
  52. package/dist/wallets/walletConnectors/tokenPocketWallet/tokenPocketWallet.js +2 -2
  53. package/dist/wallets/walletConnectors/trustWallet/trustWallet.js +2 -2
  54. package/dist/wallets/walletConnectors/zerionWallet/zerionWallet.js +2 -2
  55. package/package.json +20 -16
  56. package/dist/wallets/walletConnectors/{chunk-5TN5Z2WY.js → chunk-3ZJN3PXP.js} +3 -3
  57. package/dist/wallets/walletConnectors/{chunk-V6UOWTEZ.js → chunk-55VS2NKG.js} +3 -3
  58. package/dist/wallets/walletConnectors/{chunk-DEFRRPXB.js → chunk-7IEUTLHY.js} +3 -3
  59. package/dist/wallets/walletConnectors/{chunk-LCIPVVH5.js → chunk-AZYMJ4C6.js} +3 -3
  60. package/dist/wallets/walletConnectors/{chunk-T4ROGPMF.js → chunk-FWM4KTOV.js} +3 -3
  61. package/dist/wallets/walletConnectors/{chunk-YIEASHLS.js → chunk-IMNI4AGV.js} +3 -3
  62. package/dist/wallets/walletConnectors/{chunk-7V33VJAL.js → chunk-IRHK6SOW.js} +3 -3
  63. package/dist/wallets/walletConnectors/{chunk-UDTBQV4Q.js → chunk-J3PJOMO7.js} +3 -3
  64. package/dist/wallets/walletConnectors/{chunk-VJZWNQOF.js → chunk-LEAZMT5Y.js} +3 -3
  65. package/dist/wallets/walletConnectors/{chunk-6DRCY52E.js → chunk-OD6B2ISG.js} +3 -3
  66. package/dist/wallets/walletConnectors/{chunk-FG2LDVXL.js → chunk-OSOB6QYX.js} +3 -3
  67. package/dist/wallets/walletConnectors/{chunk-XVBSJCW5.js → chunk-RZQ4B4Z7.js} +3 -3
  68. package/dist/wallets/walletConnectors/{chunk-KWX2SYU2.js → chunk-UFYNHHDU.js} +3 -3
  69. package/dist/wallets/walletConnectors/{chunk-HXWUH73P.js → chunk-UYW6MV74.js} +3 -3
  70. package/dist/wallets/walletConnectors/{chunk-34HACM5U.js → chunk-VMMROPXK.js} +3 -3
  71. package/dist/wallets/walletConnectors/{chunk-ZPSPK6LH.js → chunk-YGMU5VWD.js} +3 -3
  72. package/dist/wallets/walletConnectors/{chunk-APHCF4DT.js → chunk-ZJJWGKB6.js} +3 -3
  73. package/dist/wallets/walletConnectors/{chunk-HRDPUW3V.js → chunk-ZL6XCMV5.js} +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  # @funkit/connect
2
2
 
3
+ ## 9.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ac8b104: Add `showWalletConnect` option to `uiCustomizations.sourceChangeScreen` to allow hiding the wallet connect payment method
8
+
9
+ ### Patch Changes
10
+
11
+ - 01826fb: chore(connect): bump postcss
12
+ - ab86ca3: refactor(connect): split `SourceList` into a thin composer + `FlatList`/`GroupedList` layout primitives. Internal-only; no public API change; rendered DOM unchanged.
13
+ - 6039a1b: When `externalUserId` is set for Lighter customers, use it directly as `userId` and `walletAddress` instead of wrapping via `generateSyntheticUserId`
14
+ - 3719658: Split useRecentDeposits into a raw API hook and a pure testable transform function
15
+ - ce7e633: Internal: add `testId` prop to `SourceList` for testability.
16
+ - 7743d5b: Extract withdrawal error notification helpers into ErrorMessage module
17
+ - 7743d5b: Extract WithdrawRecipientAddress into its own module and allow multi-method config to disable connected wallet
18
+ - 7743d5b: Use config source chain for withdrawal source token price lookup
19
+ - Updated dependencies [ec97ba2]
20
+ - @funkit/fun-relay@2.7.2
21
+
22
+ ## 9.12.0
23
+
24
+ ### Minor Changes
25
+
26
+ - ad8ebdf: Add gated combined-input overload to `createPolymarketWithdrawalConfig`. Pass both UDA and wallet-unwrap params; dispatch is decided at call time by the `enable-polymarket-wallet-withdrawal` Statsig gate. Single-flow overloads are now `@deprecated`.
27
+ - d9973bf: Add wallet-unwrap variant to `createPolymarketWithdrawalConfig` that encapsulates the pUSD → USDCe approve+unwrap and atomically batches it with the SDK's withdrawal transaction
28
+ - 1c4b50a: feat(connect): add `preWithdrawalAction` hook to `WalletWithdrawalConfig`
29
+ - 5a03929: feat(connect): add optional `sendTransactions` to `WithdrawalClient` for EIP-5792 atomic batching of approve + swap/deposit steps
30
+
31
+ ### Patch Changes
32
+
33
+ - 99062ff: chore(deps): bump vite to v8, vitest to 4.1.6, and storybook to v10
34
+ - 37cb800: feat(connect): improve swapped iframe skeleton loader
35
+ - ba2c3fa: Improved analytics for `createPolymarketWithdrawalConfig`.
36
+ - d9973bf: Lower wallet-unwrap variant of `createPolymarketWithdrawalConfig` minimum withdrawal to 0 USD
37
+ - 868ad27: Move useLighterWithdrawalBalances hook out of client entry into withdrawal modal
38
+ - 57f89a9: fix(connect): fix swapped iframe height
39
+ - fb64077: feat(connect): add notification banner text animations
40
+ - b592df7: Use config source chain for Lighter Secure withdrawal source token price lookup
41
+ - Updated dependencies [99062ff]
42
+ - @funkit/api-base@4.2.2
43
+ - @funkit/utils@3.0.1
44
+ - @funkit/chains@1.1.3
45
+ - @funkit/fun-relay@2.7.1
46
+
3
47
  ## 9.11.0
4
48
 
5
49
  ### Minor Changes
@@ -22,6 +66,17 @@
22
66
  - Updated dependencies [81e367b]
23
67
  - @funkit/api-base@4.2.1
24
68
 
69
+ ## 9.11.0-next.0
70
+
71
+ ### Minor Changes
72
+
73
+ - 2bf3417: feat(connect): add Rolly deposit actions factory via VaultDepositor
74
+
75
+ ### Patch Changes
76
+
77
+ - Updated dependencies [2bf3417]
78
+ - @funkit/api-base@4.2.1-next.0
79
+
25
80
  ## 9.10.0
26
81
 
27
82
  ### Minor Changes
package/README.md CHANGED
@@ -10,3 +10,24 @@ The `@funkit/connect` package elevates decentralize applications via web2 & web3
10
10
  ### 📝 Note
11
11
 
12
12
  This package is currently in closed beta. To learn more, reach out to [developers@fun.xyz](mailto:developers@fun.xyz).
13
+
14
+ ## Development
15
+
16
+ ### Running tests
17
+
18
+ From the monorepo root:
19
+
20
+ ```bash
21
+ # Run all connect tests once
22
+ pnpm --filter=@funkit/connect test
23
+
24
+ # Run with coverage (terminal summary + lcov + HTML report)
25
+ pnpm --filter=@funkit/connect test:coverage
26
+
27
+ # Open the Vitest UI (interactive dashboard with live coverage heatmap)
28
+ pnpm --filter=@funkit/connect test:ui
29
+ ```
30
+
31
+ After `test:coverage`, open `packages/connect/coverage/index.html` to browse the HTML report. The `coverage/` directory is gitignored.
32
+
33
+ Coverage is collected by [`@vitest/coverage-v8`](https://vitest.dev/guide/coverage) and excludes generated files, Storybook stories, vanilla-extract style files (`*.css.ts`), and translation JSON. Update the `coverage` block in `vitest.config.ts` to tweak include/exclude patterns.
@@ -3,6 +3,7 @@ declare const _default: {
3
3
  readonly 'enable-across-wallet-flow': false;
4
4
  readonly 'enable-empty-withdrawal-selection': false;
5
5
  readonly 'enable-permit-toggle': false;
6
+ readonly 'enable-polymarket-wallet-withdrawal': false;
6
7
  readonly 'enable-swapped-exchanges': false;
7
8
  readonly 'exact-in': false;
8
9
  readonly 'faster-notifications': false;
@@ -13,7 +13,6 @@
13
13
  * Both callbacks conform to CustomWithdrawalConfig.withdrawCallback; the
14
14
  * caller provides the actual on-chain implementation.
15
15
  */
16
- import type { LighterAccountIndex } from '@funkit/api-base';
17
16
  import type { Address } from 'viem';
18
17
  import type { CustomWithdrawalConfig, MultiMethodWithdrawalConfig } from '../providers/FunkitCheckoutContext/types';
19
18
  export type LighterTransferParams = {
@@ -94,21 +93,6 @@ export interface LighterSecureWithdrawalConfig {
94
93
  */
95
94
  getMinWithdrawalAmount?: (symbol: string) => number;
96
95
  }
97
- /** Free (withdrawable) balance for a single Lighter asset: balance - locked. */
98
- export declare function freeBalance(balance: string, lockedBalance: string): string;
99
- /**
100
- * Fetches all asset balances for a Lighter account. Returns:
101
- *
102
- * - `balances` — `Record<SYMBOL, freeBalance>` (uppercased symbol keys)
103
- * - `assets` — raw asset list from the API (for building source-token options)
104
- */
105
- export declare function useLighterWithdrawalBalances({ accountIndex, }: {
106
- accountIndex: LighterAccountIndex | undefined;
107
- }): {
108
- balances: Record<string, string>;
109
- assets: import("../utils/customer").LighterAsset[];
110
- isLoading: boolean;
111
- };
112
96
  /** Single-method config for Lighter's Secure withdrawal (bridge to Ethereum). */
113
97
  export declare function createLighterSecureWithdrawalConfig(config: LighterSecureWithdrawalConfig): CustomWithdrawalConfig;
114
98
  /**
@@ -118,8 +102,6 @@ export declare function createLighterSecureWithdrawalConfig(config: LighterSecur
118
102
  *
119
103
  * The hook owns every Lighter-specific concern that requires React context:
120
104
  * - the user's Lighter account index (resolved from `config.l1Address`)
121
- * - all Lighter asset balances (so the form validates against the real
122
- * account)
123
105
  * - the live Secure-withdrawal delay (`/api/v1/withdrawalDelay`)
124
106
  * - the active locale (so the disclaimer renders correctly)
125
107
  *
@@ -14,7 +14,7 @@ import { RELAY_LIGHTER_CHAIN_ID } from "@funkit/fun-relay";
14
14
  import { retry } from "@lifeomic/attempt";
15
15
  import { useQuery as useQuery2 } from "@tanstack/react-query";
16
16
  import i18next from "i18next";
17
- import React2, { useEffect, useMemo as useMemo2 } from "react";
17
+ import React2, { useMemo as useMemo2 } from "react";
18
18
  import { arbitrum, base, bsc, mainnet as mainnet2, optimism } from "viem/chains";
19
19
 
20
20
  // src/components/Icons/EvmWallet.tsx
@@ -120,6 +120,7 @@ var DEFAULT_UI_CUSTOMIZATIONS = {
120
120
  },
121
121
  showTargetAssetSelection: false,
122
122
  showWalletOnInsufficientBalance: false,
123
+ showWalletConnect: true,
123
124
  paymentMethodIcons: {}
124
125
  },
125
126
  inputAmountScreen: {
@@ -249,42 +250,6 @@ async function getLighterAccountsByL1Address(address) {
249
250
  const data = await response.json();
250
251
  return data;
251
252
  }
252
- async function getLighterAccount(lookup, activeOnly) {
253
- const params = new URLSearchParams({
254
- by: lookup.by,
255
- value: String(lookup.value),
256
- ...activeOnly !== void 0 && { active_only: String(activeOnly) }
257
- });
258
- const response = await fetch(
259
- `https://mainnet.zklighter.elliot.ai/api/v1/account?${params}`
260
- );
261
- if (!response.ok) {
262
- throw new Error(`Failed to fetch lighter account: ${response.statusText}`);
263
- }
264
- return response.json();
265
- }
266
- function useLighterAccount(lookup, options) {
267
- const { apiKey } = useFunkitConfig();
268
- const isLighter = isLighterxyzCustomer(apiKey);
269
- const enabled = isLighter && lookup !== null && (lookup.by === "index" ? true : !!lookup.value && lookup.value !== "0x");
270
- const query = useQuery({
271
- queryKey: [
272
- "lighterAccount",
273
- lookup?.by,
274
- lookup?.value,
275
- options?.activeOnly
276
- ],
277
- // biome-ignore lint/style/noNonNullAssertion: already checked for null
278
- queryFn: () => getLighterAccount(lookup, options?.activeOnly),
279
- enabled,
280
- staleTime: 3e4,
281
- retry: false
282
- });
283
- return {
284
- ...query,
285
- account: query.data?.accounts?.[0]
286
- };
287
- }
288
253
  function useLighterAccounts({
289
254
  address
290
255
  }) {
@@ -488,48 +453,6 @@ var LIGHTER_DEFAULT_SECURE_MIN_WITHDRAWAL_AMOUNTS = {
488
453
  LDO: 2,
489
454
  AZTEC: 100
490
455
  };
491
- function freeBalance(balance, lockedBalance) {
492
- return String(Math.max(0, Number(balance) - Number(lockedBalance)));
493
- }
494
- function useLighterWithdrawalBalances({
495
- accountIndex
496
- }) {
497
- const { account, isLoading, error } = useLighterAccount(
498
- accountIndex ? { by: "index", value: accountIndex } : null
499
- );
500
- useEffect(() => {
501
- logger.debug("lighter:withdrawal:balances:query", {
502
- accountIndex,
503
- isLoading,
504
- hasAccount: !!account,
505
- error: error instanceof Error ? error.message : error
506
- });
507
- }, [accountIndex, isLoading, account, error]);
508
- const balances = useMemo2(() => {
509
- if (!account) {
510
- return {};
511
- }
512
- const result = {};
513
- for (const asset of account.assets) {
514
- const symbol = asset.symbol.toUpperCase();
515
- if (symbol === "USDC") {
516
- continue;
517
- }
518
- result[symbol] = freeBalance(asset.balance, asset.locked_balance);
519
- }
520
- result.USDC = account.total_asset_value;
521
- logger.info("lighter:withdrawal:balances:resolved", {
522
- accountIndex,
523
- balances: result
524
- });
525
- return result;
526
- }, [account, accountIndex]);
527
- return {
528
- balances,
529
- assets: account?.assets ?? [],
530
- isLoading
531
- };
532
- }
533
456
  function buildSecureWithdrawalCallback({
534
457
  exec
535
458
  }) {
@@ -660,7 +583,6 @@ function buildLighterMultiMethodConfig({
660
583
  config,
661
584
  accountIndex,
662
585
  t,
663
- withBalance,
664
586
  secureDelayText
665
587
  }) {
666
588
  const fastConfig = buildLighterFastWalletWithdrawalConfig({
@@ -690,12 +612,7 @@ function buildLighterMultiMethodConfig({
690
612
  subtitleText: t("withdrawal.fastDisclaimer"),
691
613
  icon: /* @__PURE__ */ React2.createElement(EvmWallet, { size: 20 }),
692
614
  valueIcon: /* @__PURE__ */ React2.createElement(ChainIconStack, { chainIds: LIGHTER_FAST_PREVIEW_CHAIN_IDS }),
693
- config: withBalance ? {
694
- ...fastConfig,
695
- withdrawalSourceTokenBalance: withBalance(
696
- fastConfig.sourceTokenSymbol
697
- )
698
- } : fastConfig
615
+ config: fastConfig
699
616
  };
700
617
  const secure = {
701
618
  id: "lighter-secure",
@@ -708,12 +625,7 @@ function buildLighterMultiMethodConfig({
708
625
  }),
709
626
  icon: /* @__PURE__ */ React2.createElement(EvmWallet, { size: 20 }),
710
627
  valueIcon: /* @__PURE__ */ React2.createElement(ChainIconStack, { chainIds: LIGHTER_SECURE_PREVIEW_CHAIN_IDS }),
711
- config: withBalance ? {
712
- ...secureConfig,
713
- withdrawalSourceTokenBalance: withBalance(
714
- secureConfig.sourceTokenSymbol
715
- )
716
- } : secureConfig
628
+ config: secureConfig
717
629
  };
718
630
  return {
719
631
  modalTitle: config.modalTitle ?? t("withdrawal.withdraw"),
@@ -727,9 +639,6 @@ function useLighterWithdrawalConfig(config) {
727
639
  address: config?.l1Address
728
640
  });
729
641
  const accountIndex = mainAccountIndex && /^\d+$/.test(mainAccountIndex) ? mainAccountIndex : void 0;
730
- const { balances } = useLighterWithdrawalBalances({
731
- accountIndex
732
- });
733
642
  const secureDelayText = useLighterSecureDelayText();
734
643
  return useMemo2(() => {
735
644
  if (!config || !accountIndex) {
@@ -739,15 +648,12 @@ function useLighterWithdrawalConfig(config) {
739
648
  config,
740
649
  accountIndex,
741
650
  t,
742
- withBalance: (symbol) => () => balances[symbol.toUpperCase()] ?? "0",
743
651
  secureDelayText
744
652
  });
745
- }, [config, accountIndex, balances, t, secureDelayText]);
653
+ }, [config, accountIndex, t, secureDelayText]);
746
654
  }
747
655
  export {
748
656
  LIGHTER_DEFAULT_SECURE_MIN_WITHDRAWAL_AMOUNTS,
749
657
  createLighterSecureWithdrawalConfig,
750
- freeBalance,
751
- useLighterWithdrawalBalances,
752
658
  useLighterWithdrawalConfig
753
659
  };
@@ -1,15 +1,23 @@
1
1
  /**
2
2
  * Polymarket V2 PMCT withdrawal.
3
3
  *
4
- * Withdrawal flow:
5
- * 1. Generate a UDA (User Deposit Address) for the withdrawal via api-base
6
- * 2. Transfer PMCT from user's Polymarket proxy to the UDA
7
- * 3. Backend handles: unwrap PMCT → USDC via PermissionedRamp + route to user
4
+ * Two flows are supported, dispatched on input shape:
8
5
  *
9
- * The callback conforms to CustomWithdrawalConfig.withdrawCallback.
6
+ * 1. **UDA / PMCT transfer** (legacy) — caller supplies `userId` +
7
+ * `sendPmctTransfer`. The backend handles the unwrap/route via
8
+ * PermissionedRamp. Returns a {@link CustomWithdrawalConfig}.
9
+ *
10
+ * 2. **Wallet unwrap** — caller supplies `proxyAddress`, a `publicClient`,
11
+ * and a batched `sendTransactions` primitive. `preWithdrawalAction`
12
+ * reads the pUSD allowance and returns the approve (when needed) +
13
+ * `unwrap()` to USDCe as an array of {@link WithdrawalTransaction}s.
14
+ * The SDK prepends those to the withdrawal txn and atomically broadcasts
15
+ * the batch via the integrator's `sendTransactions`. Returns a
16
+ * {@link WalletWithdrawalConfig}.
10
17
  */
11
- import type { Address } from 'viem';
12
- import type { CustomWithdrawalConfig } from '../providers/FunkitCheckoutContext/types';
18
+ import { type Address, type PublicClient, type TransactionReceipt } from 'viem';
19
+ import type { CustomWithdrawalConfig, FunkitWithdrawalConfig, WalletWithdrawalConfig } from '../providers/FunkitCheckoutContext/types';
20
+ import type { WithdrawalTransaction } from '../wallets/Wallet';
13
21
  export interface PolymarketWithdrawalCallbackConfig {
14
22
  userId: string;
15
23
  modalTitle?: string;
@@ -31,9 +39,69 @@ export interface PolymarketWithdrawalCallbackConfig {
31
39
  }) => Promise<void>;
32
40
  }
33
41
  /**
34
- * Creates a full CustomWithdrawalConfig for Polymarket V2 PMCT withdrawals.
42
+ * Caller-supplied batched submitter. Receives the full ordered list of txs
43
+ * (helper-injected approve+unwrap, then the SDK's withdrawal txn). Must
44
+ * broadcast them atomically in a single user signature and return a tx hash
45
+ * that {@link WithdrawalClient.confirmTransaction} can wait on.
46
+ */
47
+ export type PolymarketSendTransactions = (txs: WithdrawalTransaction[]) => Promise<string>;
48
+ export interface PolymarketWalletUnwrapWithdrawalConfig {
49
+ /** Polymarket proxy that holds pUSD and signs the batched tx. */
50
+ proxyAddress: Address;
51
+ /** Polygon public client used for the pre-flight allowance read. */
52
+ publicClient: PublicClient;
53
+ /**
54
+ * Batched send primitive. The SDK passes the full ordered list of txs —
55
+ * helper-emitted `approve(pUSD → offramp)` (only when allowance is
56
+ * insufficient) and `offramp.unwrap(USDCe, proxy, amount)` from
57
+ * `preWithdrawalAction`, followed by the withdrawal txn — and the
58
+ * integrator broadcasts them atomically in a single user signature.
59
+ */
60
+ sendTransactions: PolymarketSendTransactions;
61
+ /**
62
+ * Optional override for waiting on a tx receipt. Defaults to
63
+ * `publicClient.waitForTransactionReceipt`.
64
+ */
65
+ confirmTransaction?: (txHash: string, chainId: number) => Promise<TransactionReceipt>;
66
+ modalTitle?: string;
67
+ disableConnectedWallet?: boolean;
68
+ sourceTokenSymbol?: string;
69
+ sourceTokenAddress?: Address;
70
+ defaultReceiveToken?: string;
71
+ iconSrc?: string;
72
+ }
73
+ /**
74
+ * Combined input — supplies params for both flows. The runtime path is
75
+ * chosen at call time via the `enable-polymarket-wallet-withdrawal`
76
+ * Statsig gate (read synchronously through {@link checkFeatureGate}, so
77
+ * this stays a regular function callable from event handlers).
78
+ */
79
+ export interface PolymarketWithdrawalConfig extends PolymarketWithdrawalCallbackConfig, PolymarketWalletUnwrapWithdrawalConfig {
80
+ }
81
+ /**
82
+ * Combined / gated entry point. Returns a {@link CustomWithdrawalConfig}
83
+ * (UDA path) or {@link WalletWithdrawalConfig} (wallet-unwrap path)
84
+ * depending on the gate value at call time.
85
+ *
86
+ * ```ts
87
+ * const withdrawalConfig = createPolymarketWithdrawalConfig({
88
+ * userId: walletAddress,
89
+ * sendPmctTransfer: async ({ udaAddress, amountBaseUnit }) => { ... },
90
+ * proxyAddress,
91
+ * publicClient,
92
+ * sendTransactions: async (txs) => myBatchedSend(txs),
93
+ * })
94
+ * ```
95
+ */
96
+ export declare function createPolymarketWithdrawalConfig(config: PolymarketWithdrawalConfig): FunkitWithdrawalConfig;
97
+ /**
98
+ * Legacy UDA / PMCT transfer flow. Returns a {@link CustomWithdrawalConfig}.
99
+ *
100
+ * @deprecated Pass the combined {@link PolymarketWithdrawalConfig} instead so
101
+ * the wallet-unwrap rollout can be staged via the
102
+ * `enable-polymarket-wallet-withdrawal` Statsig gate. Direct callers of this
103
+ * single-flow overload bypass the gate.
35
104
  *
36
- * Usage:
37
105
  * ```ts
38
106
  * const withdrawalConfig = createPolymarketWithdrawalConfig({
39
107
  * userId: walletAddress,
@@ -42,3 +110,23 @@ export interface PolymarketWithdrawalCallbackConfig {
42
110
  * ```
43
111
  */
44
112
  export declare function createPolymarketWithdrawalConfig(config: PolymarketWithdrawalCallbackConfig): CustomWithdrawalConfig;
113
+ /**
114
+ * Wallet-flow with embedded pUSD → USDCe unwrap. Returns a
115
+ * {@link WalletWithdrawalConfig} whose `preWithdrawalAction` and `wallet`
116
+ * coordinate via a closure-scoped queue, so the integrator only sees the
117
+ * batched `sendTransactions` primitive.
118
+ *
119
+ * @deprecated Pass the combined {@link PolymarketWithdrawalConfig} instead so
120
+ * the wallet-unwrap rollout can be staged via the
121
+ * `enable-polymarket-wallet-withdrawal` Statsig gate. Direct callers of this
122
+ * single-flow overload bypass the gate.
123
+ *
124
+ * ```ts
125
+ * const withdrawalConfig = createPolymarketWithdrawalConfig({
126
+ * proxyAddress,
127
+ * publicClient,
128
+ * sendTransactions: async (txs) => myBatchedSend(txs),
129
+ * })
130
+ * ```
131
+ */
132
+ export declare function createPolymarketWithdrawalConfig(config: PolymarketWalletUnwrapWithdrawalConfig): WalletWithdrawalConfig;
@@ -8,6 +8,11 @@ import {
8
8
  POLYMARKET_API_KEY,
9
9
  initializeCheckoutTokenTransferAddress
10
10
  } from "@funkit/api-base";
11
+ import {
12
+ encodeFunctionData,
13
+ erc20Abi,
14
+ getAddress
15
+ } from "viem";
11
16
  import { polygon } from "viem/chains";
12
17
 
13
18
  // src/domains/paymentMethods.ts
@@ -323,9 +328,71 @@ function generateClientMetadataForTokenTransfer() {
323
328
  };
324
329
  }
325
330
 
331
+ // src/utils/statsig/checkFeatureGate.ts
332
+ import { StatsigClient } from "@statsig/react-bindings";
333
+
334
+ // src/providers/FunkitStatsigProvider.tsx
335
+ import { datadogLogs } from "@datadog/browser-logs";
336
+ import {
337
+ LogEventCompressionMode,
338
+ LogLevel,
339
+ StatsigProvider,
340
+ useClientAsyncInit
341
+ } from "@statsig/react-bindings";
342
+ import React8, { useEffect } from "react";
343
+ var STATSIG_CLIENT_KEY = "client-UmFd8WIJljA7cLmZuDqs3X25M8sKd5WIQP4BSC2bRbM";
344
+
345
+ // src/__generated__/default_feature_gates.ts
346
+ var default_feature_gates_default = {
347
+ "compliance-review-blocker": false,
348
+ "enable-across-wallet-flow": false,
349
+ "enable-empty-withdrawal-selection": false,
350
+ "enable-permit-toggle": false,
351
+ "enable-polymarket-wallet-withdrawal": false,
352
+ "enable-swapped-exchanges": false,
353
+ "exact-in": false,
354
+ "faster-notifications": false,
355
+ "new-token-transfer-config": false,
356
+ "new-withdrawal-config": false,
357
+ "polymarket-pusd-migration": false,
358
+ "saved-card-defaults-to-fiat-tab": false,
359
+ "test-testing-gate": false,
360
+ "wallet-flow-enable-exact-input-with-actions": false,
361
+ "you-receive-remove-swap-impact": false
362
+ };
363
+
364
+ // src/utils/statsig/checkFeatureGate.ts
365
+ function checkFeatureGate(name) {
366
+ try {
367
+ return StatsigClient.instance(STATSIG_CLIENT_KEY).checkGate(name);
368
+ } catch (err) {
369
+ logger.warn("checkFeatureGate:error", { name, err });
370
+ return default_feature_gates_default[name];
371
+ }
372
+ }
373
+
326
374
  // src/clients/polymarket.tsx
327
375
  var PMCT_WITHDRAW_ACTION_TYPE = "PMCT_WITHDRAW";
328
376
  var POLYGON_USDCE = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174";
377
+ var PUSD_TOKEN = {
378
+ address: getAddress("0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB"),
379
+ symbol: "pUSD",
380
+ iconSrc: "https://sdk-cdn.fun.xyz/images/pusd.svg"
381
+ };
382
+ var PUSD_OFFRAMP_ADDRESS = "0x2957922Eb93258b93368531d39fAcCA3B4dC5854";
383
+ var PUSD_OFFRAMP_ABI = [
384
+ {
385
+ name: "unwrap",
386
+ type: "function",
387
+ inputs: [
388
+ { name: "_asset", type: "address" },
389
+ { name: "_to", type: "address" },
390
+ { name: "_amount", type: "uint256" }
391
+ ],
392
+ outputs: [],
393
+ stateMutability: "nonpayable"
394
+ }
395
+ ];
329
396
  function createPolymarketWithdrawalCallback(config) {
330
397
  const { userId, sendPmctTransfer } = config;
331
398
  return async (param) => {
@@ -351,24 +418,20 @@ function createPolymarketWithdrawalCallback(config) {
351
418
  });
352
419
  const udaAddress = transferInit.depositAddr;
353
420
  logger.info("polymarket:withdrawal:udaGenerated", { udaAddress });
354
- const quote = funQuote;
355
- const amountStr = quote?.baseQuote?.estTotalFromAmountBaseUnit;
356
- if (!amountStr) {
357
- logger.error("polymarket:withdrawal:missingAmountInQuote", { quote });
358
- throw new Error("Missing withdrawal amount in quote");
359
- }
360
- const amount = BigInt(amountStr);
421
+ const amountBaseUnit = extractWithdrawalAmount(funQuote);
361
422
  await sendPmctTransfer({
362
423
  udaAddress,
363
- amountBaseUnit: amount
424
+ amountBaseUnit
364
425
  });
365
426
  logger.info("polymarket:withdrawal:transferSubmitted", {
366
427
  udaAddress,
367
- amountBaseUnit: amountStr
428
+ amountBaseUnit: amountBaseUnit.toString()
368
429
  });
430
+ return void 0;
369
431
  };
370
432
  }
371
- function createPolymarketWithdrawalConfig(config) {
433
+ function buildUdaWithdrawalConfig(config) {
434
+ logger.info("polymarket:withdrawal:flow", { flow: "uda" });
372
435
  return {
373
436
  modalTitle: config.modalTitle ?? "Withdraw",
374
437
  disableConnectedWallet: config.disableConnectedWallet,
@@ -381,6 +444,114 @@ function createPolymarketWithdrawalConfig(config) {
381
444
  getMinWithdrawalUSD: () => 3
382
445
  };
383
446
  }
447
+ function buildWalletUnwrapWithdrawalConfig(config) {
448
+ logger.info("polymarket:withdrawal:flow", { flow: "wallet-unwrap" });
449
+ const {
450
+ proxyAddress,
451
+ publicClient,
452
+ sendTransactions: callerSendTransactions,
453
+ confirmTransaction
454
+ } = config;
455
+ const wallet = {
456
+ address: () => proxyAddress,
457
+ getChainId: async () => polygon.id,
458
+ switchChain: async (chainId) => {
459
+ if (chainId !== polygon.id) {
460
+ throw new Error(
461
+ `Cannot switch chain: Polymarket withdrawal is fixed to ${polygon.id}, requested ${chainId}`
462
+ );
463
+ }
464
+ },
465
+ sendTransaction: async (_chainId, transaction) => callerSendTransactions([transaction]),
466
+ sendTransactions: async (_chainId, txs) => callerSendTransactions(txs),
467
+ confirmTransaction: confirmTransaction ?? (async (txHash) => publicClient.waitForTransactionReceipt({ hash: txHash }))
468
+ };
469
+ const preWithdrawalAction = async ({
470
+ funQuote
471
+ }) => {
472
+ logger.info("polymarket:withdrawal:wallet:start", { proxyAddress });
473
+ try {
474
+ const amountBaseUnit = extractWithdrawalAmount(funQuote);
475
+ const currentAllowance = await publicClient.readContract({
476
+ address: PUSD_TOKEN.address,
477
+ abi: erc20Abi,
478
+ functionName: "allowance",
479
+ args: [proxyAddress, PUSD_OFFRAMP_ADDRESS]
480
+ });
481
+ const needsApprovalTx = currentAllowance < amountBaseUnit;
482
+ logger.info("polymarket:withdrawal:wallet:allowanceChecked", {
483
+ proxyAddress,
484
+ amountBaseUnit: amountBaseUnit.toString(),
485
+ currentAllowance: currentAllowance.toString(),
486
+ needsApprovalTx
487
+ });
488
+ const txs = [];
489
+ if (needsApprovalTx) {
490
+ txs.push({
491
+ to: PUSD_TOKEN.address,
492
+ data: encodeFunctionData({
493
+ abi: erc20Abi,
494
+ functionName: "approve",
495
+ args: [PUSD_OFFRAMP_ADDRESS, amountBaseUnit]
496
+ }),
497
+ value: "0"
498
+ });
499
+ }
500
+ txs.push({
501
+ to: PUSD_OFFRAMP_ADDRESS,
502
+ data: encodeFunctionData({
503
+ abi: PUSD_OFFRAMP_ABI,
504
+ functionName: "unwrap",
505
+ args: [POLYGON_USDCE, proxyAddress, amountBaseUnit]
506
+ }),
507
+ value: "0"
508
+ });
509
+ logger.info("polymarket:withdrawal:wallet:txsStaged", {
510
+ proxyAddress,
511
+ txCount: txs.length,
512
+ needsApprovalTx
513
+ });
514
+ return txs;
515
+ } catch (err) {
516
+ logger.error("polymarket:withdrawal:wallet:error", err, {
517
+ phase: "preWithdrawal",
518
+ proxyAddress
519
+ });
520
+ throw err;
521
+ }
522
+ };
523
+ return {
524
+ modalTitle: config.modalTitle ?? "Withdraw",
525
+ disableConnectedWallet: config.disableConnectedWallet,
526
+ sourceChainId: polygon.id.toString(),
527
+ sourceTokenSymbol: config.sourceTokenSymbol ?? PUSD_TOKEN.symbol,
528
+ sourceTokenAddress: config.sourceTokenAddress ?? POLYGON_USDCE,
529
+ defaultReceiveToken: config.defaultReceiveToken ?? "USDC",
530
+ iconSrc: config.iconSrc ?? PUSD_TOKEN.iconSrc,
531
+ getMinWithdrawalUSD: () => 0,
532
+ wallet,
533
+ preWithdrawalAction
534
+ };
535
+ }
536
+ function extractWithdrawalAmount(funQuote) {
537
+ const quote = funQuote;
538
+ const amountStr = quote?.baseQuote?.estTotalFromAmountBaseUnit;
539
+ if (!amountStr) {
540
+ logger.error("polymarket:withdrawal:missingAmountInQuote", { quote });
541
+ throw new Error("Missing withdrawal amount in quote");
542
+ }
543
+ return BigInt(amountStr);
544
+ }
545
+ function createPolymarketWithdrawalConfig(config) {
546
+ if ("sendPmctTransfer" in config && "sendTransactions" in config) {
547
+ const walletUnwrapEnabled = checkFeatureGate(
548
+ "enable-polymarket-wallet-withdrawal"
549
+ );
550
+ logger.info("polymarket:withdrawal:gate", { walletUnwrapEnabled });
551
+ return walletUnwrapEnabled ? buildWalletUnwrapWithdrawalConfig(config) : buildUdaWithdrawalConfig(config);
552
+ }
553
+ return "sendPmctTransfer" in config ? buildUdaWithdrawalConfig(config) : buildWalletUnwrapWithdrawalConfig(config);
554
+ }
384
555
  export {
385
556
  createPolymarketWithdrawalConfig
386
557
  };
@@ -1,6 +1,6 @@
1
1
  export declare const DIALOG_WIDTH_WIDE = "450px";
2
2
  export declare const DIALOG_HEIGHT = 525;
3
- export declare const DIALOG_HEIGHT_SWAPPED_IFRAME = 580;
3
+ export declare const DIALOG_HEIGHT_SWAPPED_IFRAME = 525;
4
4
  export declare const DIALOG_INNER_PADDING_X = 12;
5
5
  export declare const SCROLL_BAR_WIDTH = 6;
6
6
  export declare const DIALOG_BOTTOM_PADDING = "15";
@@ -1,7 +1,7 @@
1
1
  import React, { type ReactNode } from 'react';
2
2
  import { type FunNotificationBannerIconProps } from './FunNotificationBannerIcon';
3
3
  interface FunNotificationBannerProps extends FunNotificationBannerIconProps {
4
- title: ReactNode;
4
+ title: string;
5
5
  description: ReactNode;
6
6
  /** Additional text-like content clarifying the description */
7
7
  disclaimer?: ReactNode;
@@ -1 +1 @@
1
- export declare const funOptionBoxStyle: Record<"activeBackground" | "baseStyle" | "compactListStyle" | "defaultBorder" | "defaultModeNotActiveBorder" | "activeBorder" | "disabledOptionBorder" | "defaultBackground" | "uninteractiveBackground", string>;
1
+ export declare const funOptionBoxStyle: Record<"baseStyle" | "compactListStyle" | "defaultBorder" | "defaultModeNotActiveBorder" | "activeBorder" | "disabledOptionBorder" | "defaultBackground" | "activeBackground" | "uninteractiveBackground", string>;