@dubsdotapp/expo 0.5.32 → 0.5.34

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.mts CHANGED
@@ -104,6 +104,14 @@ interface CreateGameParams {
104
104
  playerWallet: string;
105
105
  teamChoice: 'home' | 'away' | 'draw';
106
106
  wagerAmount: number;
107
+ /**
108
+ * When true, fund create+join with one of the user's active 0.01 SOL
109
+ * streak credits instead of paying rent + buy-in from their own
110
+ * wallet. SDK picks the oldest credit (FIFO). Treasury covers rent
111
+ * + buy-in; player only signs to consent. Buy-in is forced to the
112
+ * credit amount.
113
+ */
114
+ useCredit?: boolean;
107
115
  }
108
116
  interface CreateGameResult {
109
117
  gameId: string;
@@ -111,6 +119,11 @@ interface CreateGameResult {
111
119
  transaction: string;
112
120
  lockTimestamp: number;
113
121
  event: UnifiedEvent;
122
+ /** Set when sponsored — echoed back to confirmGame for mark-as-used. */
123
+ promoCode?: string;
124
+ sponsorWallet?: string;
125
+ /** Server-confirmed wager amount (== credit amount when sponsored). */
126
+ wagerAmount?: number;
114
127
  }
115
128
  interface CreateCustomGameParams {
116
129
  playerWallet: string;
@@ -131,11 +144,25 @@ interface JoinGameParams {
131
144
  gameId: string;
132
145
  teamChoice: 'home' | 'away' | 'draw';
133
146
  amount: number;
147
+ /**
148
+ * When true, spend one of the user's active 0.01 SOL streak credits
149
+ * instead of paying buy-in from the player's own wallet. The SDK
150
+ * picks the oldest credit (FIFO). The treasury covers buy-in + tx
151
+ * fee; the player only signs to consent. No-op if the user has no
152
+ * active credits — call useCredits() to check before enabling.
153
+ */
154
+ useCredit?: boolean;
134
155
  }
135
156
  interface JoinGameResult {
136
157
  gameId: string;
137
158
  transaction: string;
138
159
  gameAddress: string;
160
+ /**
161
+ * Set when this join was sponsored. The mutation hook echoes this
162
+ * back to confirmGame so the server can mark the credit as used.
163
+ */
164
+ promoCode?: string;
165
+ sponsorWallet?: string;
139
166
  }
140
167
  interface ConfirmGameParams {
141
168
  gameId: string;
@@ -145,6 +172,8 @@ interface ConfirmGameParams {
145
172
  wagerAmount?: number;
146
173
  role?: 'creator' | 'joiner';
147
174
  gameAddress?: string;
175
+ /** Echoed back from a sponsored joinGame so the server marks the credit as used. */
176
+ promoCode?: string;
148
177
  }
149
178
  interface ConfirmGameResult {
150
179
  gameId: string;
package/dist/index.d.ts CHANGED
@@ -104,6 +104,14 @@ interface CreateGameParams {
104
104
  playerWallet: string;
105
105
  teamChoice: 'home' | 'away' | 'draw';
106
106
  wagerAmount: number;
107
+ /**
108
+ * When true, fund create+join with one of the user's active 0.01 SOL
109
+ * streak credits instead of paying rent + buy-in from their own
110
+ * wallet. SDK picks the oldest credit (FIFO). Treasury covers rent
111
+ * + buy-in; player only signs to consent. Buy-in is forced to the
112
+ * credit amount.
113
+ */
114
+ useCredit?: boolean;
107
115
  }
108
116
  interface CreateGameResult {
109
117
  gameId: string;
@@ -111,6 +119,11 @@ interface CreateGameResult {
111
119
  transaction: string;
112
120
  lockTimestamp: number;
113
121
  event: UnifiedEvent;
122
+ /** Set when sponsored — echoed back to confirmGame for mark-as-used. */
123
+ promoCode?: string;
124
+ sponsorWallet?: string;
125
+ /** Server-confirmed wager amount (== credit amount when sponsored). */
126
+ wagerAmount?: number;
114
127
  }
115
128
  interface CreateCustomGameParams {
116
129
  playerWallet: string;
@@ -131,11 +144,25 @@ interface JoinGameParams {
131
144
  gameId: string;
132
145
  teamChoice: 'home' | 'away' | 'draw';
133
146
  amount: number;
147
+ /**
148
+ * When true, spend one of the user's active 0.01 SOL streak credits
149
+ * instead of paying buy-in from the player's own wallet. The SDK
150
+ * picks the oldest credit (FIFO). The treasury covers buy-in + tx
151
+ * fee; the player only signs to consent. No-op if the user has no
152
+ * active credits — call useCredits() to check before enabling.
153
+ */
154
+ useCredit?: boolean;
134
155
  }
135
156
  interface JoinGameResult {
136
157
  gameId: string;
137
158
  transaction: string;
138
159
  gameAddress: string;
160
+ /**
161
+ * Set when this join was sponsored. The mutation hook echoes this
162
+ * back to confirmGame so the server can mark the credit as used.
163
+ */
164
+ promoCode?: string;
165
+ sponsorWallet?: string;
139
166
  }
140
167
  interface ConfirmGameParams {
141
168
  gameId: string;
@@ -145,6 +172,8 @@ interface ConfirmGameParams {
145
172
  wagerAmount?: number;
146
173
  role?: 'creator' | 'joiner';
147
174
  gameAddress?: string;
175
+ /** Echoed back from a sponsored joinGame so the server marks the credit as used. */
176
+ promoCode?: string;
148
177
  }
149
178
  interface ConfirmGameResult {
150
179
  gameId: string;
package/dist/index.js CHANGED
@@ -370,6 +370,22 @@ var DubsClient = class {
370
370
  };
371
371
  }
372
372
  async createGame(params) {
373
+ if (params.useCredit) {
374
+ const res2 = await this.request("POST", "/games/create-sponsored", {
375
+ id: params.id,
376
+ teamChoice: params.teamChoice
377
+ });
378
+ return {
379
+ gameId: res2.gameId,
380
+ gameAddress: res2.gameAddress,
381
+ transaction: res2.transaction,
382
+ lockTimestamp: res2.lockTimestamp,
383
+ event: res2.event,
384
+ promoCode: res2.promoCode,
385
+ sponsorWallet: res2.sponsorWallet,
386
+ wagerAmount: res2.wagerAmount
387
+ };
388
+ }
373
389
  const res = await this.request(
374
390
  "POST",
375
391
  "/games/create",
@@ -384,6 +400,19 @@ var DubsClient = class {
384
400
  };
385
401
  }
386
402
  async joinGame(params) {
403
+ if (params.useCredit) {
404
+ const res2 = await this.request("POST", "/games/join-sponsored", {
405
+ gameId: params.gameId,
406
+ teamChoice: params.teamChoice
407
+ });
408
+ return {
409
+ gameId: res2.gameId,
410
+ transaction: res2.transaction,
411
+ gameAddress: res2.gameAddress,
412
+ promoCode: res2.promoCode,
413
+ sponsorWallet: res2.sponsorWallet
414
+ };
415
+ }
387
416
  const res = await this.request(
388
417
  "POST",
389
418
  "/games/join",
@@ -2167,14 +2196,18 @@ function useCreateGame() {
2167
2196
  console.log("[useCreateGame] Step 2 done. Signature:", signature);
2168
2197
  setStatus("confirming");
2169
2198
  console.log("[useCreateGame] Step 3: Confirming with backend...");
2199
+ const wagerAmount = createResult.wagerAmount ?? params.wagerAmount;
2170
2200
  const confirmResult = await client.confirmGame({
2171
2201
  gameId: createResult.gameId,
2172
2202
  playerWallet: params.playerWallet,
2173
2203
  signature,
2174
2204
  teamChoice: params.teamChoice,
2175
- wagerAmount: params.wagerAmount,
2205
+ wagerAmount,
2176
2206
  role: "creator",
2177
- gameAddress: createResult.gameAddress
2207
+ gameAddress: createResult.gameAddress,
2208
+ // Echo the credit code through so the server marks it as used
2209
+ // after the on-chain confirm succeeds.
2210
+ promoCode: createResult.promoCode
2178
2211
  });
2179
2212
  console.log("[useCreateGame] Step 3 done.");
2180
2213
  const result = {
@@ -2191,14 +2224,14 @@ function useCreateGame() {
2191
2224
  const home = event?.opponents?.[0]?.name ?? null;
2192
2225
  const away = event?.opponents?.[1]?.name ?? null;
2193
2226
  const teamLabel = params.teamChoice === "home" ? teamNickname(home) : params.teamChoice === "away" ? teamNickname(away) : "Draw";
2194
- const message = `I just placed a ${params.wagerAmount} SOL bet on ${teamLabel} - Join me!`;
2227
+ const message = `I just placed a ${wagerAmount} SOL bet on ${teamLabel} - Join me!`;
2195
2228
  const gameInvite = {
2196
2229
  gameId: createResult.gameId,
2197
2230
  gameAddress: createResult.gameAddress,
2198
2231
  title: event?.title,
2199
2232
  league: event?.league,
2200
2233
  gameType: "sports",
2201
- buyIn: params.wagerAmount,
2234
+ buyIn: wagerAmount,
2202
2235
  status: "waiting",
2203
2236
  homeTeam: home,
2204
2237
  awayTeam: away,
@@ -2263,7 +2296,10 @@ function useJoinGame() {
2263
2296
  teamChoice: params.teamChoice,
2264
2297
  wagerAmount: params.amount,
2265
2298
  role: "joiner",
2266
- gameAddress: joinResult.gameAddress
2299
+ gameAddress: joinResult.gameAddress,
2300
+ // Pass through when this was a sponsored join — server marks
2301
+ // the credit as used after the on-chain confirm succeeds.
2302
+ promoCode: joinResult.promoCode
2267
2303
  };
2268
2304
  console.log("[useJoinGame] Step 3: Confirming with backend...", confirmParams);
2269
2305
  const confirmResult = await client.confirmGame(confirmParams);
@@ -6077,10 +6113,14 @@ function JoinGameSheet({
6077
6113
  const t = useDubsTheme();
6078
6114
  const { wallet } = useDubs();
6079
6115
  const mutation = useJoinGame();
6116
+ const { credits, refetch: refetchCredits } = useCredits();
6080
6117
  const isCustomGame = game.gameMode === CUSTOM_GAME_MODE;
6081
6118
  const [selectedTeam, setSelectedTeam] = (0, import_react41.useState)(null);
6082
6119
  const [wager, setWager] = (0, import_react41.useState)(game.buyIn);
6083
6120
  const [showSuccess, setShowSuccess] = (0, import_react41.useState)(false);
6121
+ const [useCredit, setUseCredit] = (0, import_react41.useState)(false);
6122
+ const oldestCredit = credits.length > 0 ? credits[0] : null;
6123
+ const canUseCredit = !!oldestCredit && Math.round(oldestCredit.amountSOL * 1e9) >= Math.round(game.buyIn * 1e9);
6084
6124
  const overlayOpacity = (0, import_react41.useRef)(new import_react_native21.Animated.Value(0)).current;
6085
6125
  const successScale = (0, import_react41.useRef)(new import_react_native21.Animated.Value(0)).current;
6086
6126
  const successOpacity = (0, import_react41.useRef)(new import_react_native21.Animated.Value(0)).current;
@@ -6169,11 +6209,15 @@ function JoinGameSheet({
6169
6209
  playerWallet: wallet.publicKey.toBase58(),
6170
6210
  gameId: game.gameId,
6171
6211
  teamChoice: selectedTeam,
6172
- amount: wager
6212
+ // When useCredit is on, the on-chain instruction transfers
6213
+ // exactly the credit's amount; pass that as the wager.
6214
+ amount: useCredit && oldestCredit ? oldestCredit.amountSOL : wager,
6215
+ useCredit: useCredit && canUseCredit ? true : void 0
6173
6216
  });
6217
+ if (useCredit) refetchCredits();
6174
6218
  } catch {
6175
6219
  }
6176
- }, [selectedTeam, wallet.publicKey, mutation.execute, game.gameId, wager]);
6220
+ }, [selectedTeam, wallet.publicKey, mutation.execute, game.gameId, wager, useCredit, oldestCredit, canUseCredit, refetchCredits]);
6177
6221
  const statusLabel = STATUS_LABELS3[mutation.status] || "";
6178
6222
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
6179
6223
  import_react_native21.Modal,
@@ -6376,7 +6420,26 @@ function JoinGameSheet({
6376
6420
  ] })
6377
6421
  ] })
6378
6422
  ] }),
6379
- selectedTeam && !isPoolModeEnabled && !alreadyJoined && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6423
+ selectedTeam && !alreadyJoined && canUseCredit && oldestCredit && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
6424
+ import_react_native21.TouchableOpacity,
6425
+ {
6426
+ style: [styles15.creditRow, { backgroundColor: useCredit ? "#22C55E18" : t.surface, borderColor: useCredit ? "#22C55E" : t.border }],
6427
+ activeOpacity: 0.8,
6428
+ onPress: () => setUseCredit((v) => !v),
6429
+ children: [
6430
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native21.View, { style: styles15.creditRowText, children: [
6431
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native21.Text, { style: [styles15.creditTitle, { color: useCredit ? "#22C55E" : t.text }], children: [
6432
+ "\u{1F381} Use my ",
6433
+ formatSol(oldestCredit.amountSOL),
6434
+ " SOL credit"
6435
+ ] }),
6436
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Text, { style: [styles15.creditSub, { color: t.textMuted }], children: useCredit ? "Treasury covers this bet \u2014 wager locked to credit amount" : `${credits.length} credit${credits.length === 1 ? "" : "s"} available from your streak` })
6437
+ ] }),
6438
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.View, { style: [styles15.creditCheckbox, useCredit && { backgroundColor: "#22C55E", borderColor: "#22C55E" }], children: useCredit && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Text, { style: styles15.creditCheck, children: "\u2713" }) })
6439
+ ]
6440
+ }
6441
+ ),
6442
+ selectedTeam && !isPoolModeEnabled && !alreadyJoined && !useCredit && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6380
6443
  SolSlider,
6381
6444
  {
6382
6445
  value: wager,
@@ -6714,6 +6777,41 @@ var styles15 = import_react_native21.StyleSheet.create({
6714
6777
  flexDirection: "row",
6715
6778
  alignItems: "center",
6716
6779
  gap: 10
6780
+ },
6781
+ creditRow: {
6782
+ flexDirection: "row",
6783
+ alignItems: "center",
6784
+ gap: 12,
6785
+ marginTop: 12,
6786
+ padding: 12,
6787
+ borderRadius: 12,
6788
+ borderWidth: 1.5
6789
+ },
6790
+ creditRowText: {
6791
+ flex: 1,
6792
+ gap: 2
6793
+ },
6794
+ creditTitle: {
6795
+ fontSize: 14,
6796
+ fontWeight: "700"
6797
+ },
6798
+ creditSub: {
6799
+ fontSize: 12,
6800
+ fontWeight: "500"
6801
+ },
6802
+ creditCheckbox: {
6803
+ width: 22,
6804
+ height: 22,
6805
+ borderRadius: 11,
6806
+ borderWidth: 2,
6807
+ borderColor: "#3A3A3C",
6808
+ alignItems: "center",
6809
+ justifyContent: "center"
6810
+ },
6811
+ creditCheck: {
6812
+ color: "#FFFFFF",
6813
+ fontSize: 14,
6814
+ fontWeight: "900"
6717
6815
  }
6718
6816
  });
6719
6817
 
@@ -7534,9 +7632,13 @@ function CreateGameSheet({
7534
7632
  const t = useDubsTheme();
7535
7633
  const { wallet } = useDubs();
7536
7634
  const mutation = useCreateGame();
7635
+ const { credits, refetch: refetchCredits } = useCredits();
7537
7636
  const [selectedTeam, setSelectedTeam] = (0, import_react46.useState)(null);
7538
7637
  const [wager, setWager] = (0, import_react46.useState)(0.01);
7539
7638
  const [showSuccess, setShowSuccess] = (0, import_react46.useState)(false);
7639
+ const [useCredit, setUseCredit] = (0, import_react46.useState)(false);
7640
+ const oldestCredit = credits.length > 0 ? credits[0] : null;
7641
+ const canUseCredit = !!oldestCredit;
7540
7642
  const overlayOpacity = (0, import_react46.useRef)(new import_react_native26.Animated.Value(0)).current;
7541
7643
  const successScale = (0, import_react46.useRef)(new import_react_native26.Animated.Value(0)).current;
7542
7644
  const successOpacity = (0, import_react46.useRef)(new import_react_native26.Animated.Value(0)).current;
@@ -7591,11 +7693,16 @@ function CreateGameSheet({
7591
7693
  id: event.id,
7592
7694
  playerWallet: wallet.publicKey.toBase58(),
7593
7695
  teamChoice: selectedTeam,
7594
- wagerAmount: wager
7696
+ // When sponsoring, the on-chain instruction forces buy-in to
7697
+ // the credit's amount; pass that so client-side wager state
7698
+ // matches what gets recorded.
7699
+ wagerAmount: useCredit && oldestCredit ? oldestCredit.amountSOL : wager,
7700
+ useCredit: useCredit && canUseCredit ? true : void 0
7595
7701
  });
7702
+ if (useCredit) refetchCredits();
7596
7703
  } catch {
7597
7704
  }
7598
- }, [selectedTeam, wallet.publicKey, mutation.execute, event.id, wager]);
7705
+ }, [selectedTeam, wallet.publicKey, mutation.execute, event.id, wager, useCredit, oldestCredit, canUseCredit, refetchCredits]);
7599
7706
  const statusLabel = STATUS_LABELS6[mutation.status] || "";
7600
7707
  const startTime = event.startTime ? new Date(event.startTime) : null;
7601
7708
  const timeLabel = startTime ? startTime.toLocaleDateString("en-US", { weekday: "short", month: "short", day: "numeric" }) + " at " + startTime.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" }) : "TBD";
@@ -7670,7 +7777,26 @@ function CreateGameSheet({
7670
7777
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_native26.Text, { style: [styles20.summaryValue, { color: t.success }], children: "Set the odds" })
7671
7778
  ] })
7672
7779
  ] }),
7673
- selectedTeam && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7780
+ selectedTeam && canUseCredit && oldestCredit && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
7781
+ import_react_native26.TouchableOpacity,
7782
+ {
7783
+ style: [styles20.creditRow, { backgroundColor: useCredit ? "#22C55E18" : t.surface, borderColor: useCredit ? "#22C55E" : t.border }],
7784
+ activeOpacity: 0.8,
7785
+ onPress: () => setUseCredit((v) => !v),
7786
+ children: [
7787
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react_native26.View, { style: styles20.creditRowText, children: [
7788
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react_native26.Text, { style: [styles20.creditTitle, { color: useCredit ? "#22C55E" : t.text }], children: [
7789
+ "\u{1F381} Use my ",
7790
+ formatSol2(oldestCredit.amountSOL),
7791
+ " SOL credit"
7792
+ ] }),
7793
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_native26.Text, { style: [styles20.creditSub, { color: t.textMuted }], children: useCredit ? "Treasury covers rent + buy-in" : `${credits.length} credit${credits.length === 1 ? "" : "s"} from your streak` })
7794
+ ] }),
7795
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_native26.View, { style: [styles20.creditCheckbox, useCredit && { backgroundColor: "#22C55E", borderColor: "#22C55E" }], children: useCredit && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_native26.Text, { style: styles20.creditCheck, children: "\u2713" }) })
7796
+ ]
7797
+ }
7798
+ ),
7799
+ selectedTeam && !useCredit && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7674
7800
  SolSlider,
7675
7801
  {
7676
7802
  value: wager,
@@ -7724,6 +7850,12 @@ var styles20 = import_react_native26.StyleSheet.create({
7724
7850
  ctaButton: { marginTop: 16, height: 54, borderRadius: 14, justifyContent: "center", alignItems: "center" },
7725
7851
  ctaText: { color: "#FFFFFF", fontSize: 16, fontWeight: "700" },
7726
7852
  ctaLoading: { flexDirection: "row", alignItems: "center", gap: 10 },
7853
+ creditRow: { flexDirection: "row", alignItems: "center", gap: 12, marginTop: 12, padding: 12, borderRadius: 12, borderWidth: 1.5 },
7854
+ creditRowText: { flex: 1, gap: 2 },
7855
+ creditTitle: { fontSize: 14, fontWeight: "700" },
7856
+ creditSub: { fontSize: 12, fontWeight: "500" },
7857
+ creditCheckbox: { width: 22, height: 22, borderRadius: 11, borderWidth: 2, borderColor: "#3A3A3C", alignItems: "center", justifyContent: "center" },
7858
+ creditCheck: { color: "#FFFFFF", fontSize: 14, fontWeight: "900" },
7727
7859
  successOverlay: { ...import_react_native26.StyleSheet.absoluteFillObject, zIndex: 100, alignItems: "center", justifyContent: "center", backgroundColor: "rgba(0,0,0,0.85)" },
7728
7860
  successContent: { alignItems: "center", gap: 12 },
7729
7861
  successEmoji: { fontSize: 64 },