@moneypot/hub 1.16.0-dev.3 → 1.16.0-dev.4

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.
@@ -5,17 +5,13 @@ export declare function dbGetCasinoCurrencyByKey(pgClient: QueryExecutor, { curr
5
5
  currencyKey: string;
6
6
  casinoId: string;
7
7
  }): Promise<DbCurrency | undefined>;
8
- export declare function dbLockPlayerBalanceAndHouseBankroll(pgClient: PgClientInTransaction, { userId, casinoId, experienceId, currencyKey, }: {
8
+ export declare function dbLockPlayerBalance(pgClient: PgClientInTransaction, { userId, casinoId, experienceId, currencyKey, }: {
9
9
  userId: string;
10
10
  casinoId: string;
11
11
  experienceId: string;
12
12
  currencyKey: string;
13
- }): Promise<{
14
- found: false;
15
- dbPlayerBalance: null;
16
- dbHouseBankroll: null;
17
- } | {
18
- found: true;
19
- dbPlayerBalance: Pick<DbBalance, "id" | "amount">;
20
- dbHouseBankroll: Pick<DbBankroll, "id" | "amount">;
21
- }>;
13
+ }): Promise<Pick<DbBalance, "id" | "amount"> | null>;
14
+ export declare function dbLockHouseBankroll(pgClient: PgClientInTransaction, { casinoId, currencyKey }: {
15
+ casinoId: string;
16
+ currencyKey: string;
17
+ }): Promise<Pick<DbBankroll, "id" | "amount"> | null>;
@@ -1,5 +1,4 @@
1
1
  import { maybeOneRow } from "./util.js";
2
- import { assert } from "tsafe";
3
2
  export async function dbGetActiveSessionById(pgClient, sessionId) {
4
3
  return pgClient
5
4
  .query(`
@@ -19,40 +18,29 @@ export async function dbGetCasinoCurrencyByKey(pgClient, { currencyKey, casinoId
19
18
  `, [currencyKey, casinoId])
20
19
  .then(maybeOneRow);
21
20
  }
22
- export async function dbLockPlayerBalanceAndHouseBankroll(pgClient, { userId, casinoId, experienceId, currencyKey, }) {
23
- assert(pgClient._inTransaction, "pgClient must be in a transaction");
24
- const row = await pgClient
21
+ export async function dbLockPlayerBalance(pgClient, { userId, casinoId, experienceId, currencyKey, }) {
22
+ return pgClient
25
23
  .query(`
26
- WITH locked_balance AS (
27
- SELECT id, amount
28
- FROM hub.balance
29
- WHERE user_id = $1
30
- AND casino_id = $2
31
- AND experience_id = $3
32
- AND currency_key = $4
33
-
34
- FOR UPDATE
35
- )
36
- SELECT
37
- json_build_object('id', pb.id, 'amount', pb.amount) as player_balance,
38
- json_build_object('id', br.id, 'amount', br.amount) as house_bankroll
39
- FROM locked_balance pb
40
- JOIN hub.bankroll br
41
- ON br.casino_id = $2
42
- AND br.currency_key = $4
43
-
44
- FOR UPDATE OF br
45
- `, [userId, casinoId, experienceId, currencyKey])
46
- .then(maybeOneRow);
47
- return row
48
- ? {
49
- found: true,
50
- dbPlayerBalance: row.player_balance,
51
- dbHouseBankroll: row.house_bankroll,
52
- }
53
- : {
54
- found: false,
55
- dbPlayerBalance: null,
56
- dbHouseBankroll: null,
57
- };
24
+ SELECT *
25
+ FROM hub.balance
26
+ WHERE user_id = $1
27
+ AND casino_id = $2
28
+ AND experience_id = $3
29
+ AND currency_key = $4
30
+ FOR UPDATE
31
+ `, [userId, casinoId, experienceId, currencyKey])
32
+ .then(maybeOneRow)
33
+ .then((row) => row ?? null);
34
+ }
35
+ export async function dbLockHouseBankroll(pgClient, { casinoId, currencyKey }) {
36
+ return pgClient
37
+ .query(`
38
+ SELECT *
39
+ FROM hub.bankroll
40
+ WHERE casino_id = $1
41
+ AND currency_key = $2
42
+ FOR UPDATE
43
+ `, [casinoId, currencyKey])
44
+ .then(maybeOneRow)
45
+ .then((row) => row ?? null);
58
46
  }
@@ -127,36 +127,35 @@ export const HubAuthenticatePlugin = extendSchema(() => {
127
127
  const mpExperience = result.experience;
128
128
  assert(mpExperience);
129
129
  assert(mpExperience.userByUserId, "Expected mpExperience.userByUserId");
130
- const userInsertResult = await pgClient
130
+ const authUser = await pgClient
131
131
  .query({
132
132
  text: `
133
- WITH user_inserts AS (
134
- INSERT INTO hub.user(casino_id, mp_user_id, uname)
135
- VALUES
136
- ($1, $2, $3),
137
- ($1, $4, $5)
138
- ON CONFLICT (casino_id, mp_user_id) DO UPDATE
139
- SET uname = EXCLUDED.uname
140
- WHERE hub.user.uname IS DISTINCT FROM EXCLUDED.uname
141
- RETURNING id, mp_user_id
142
- )
143
- SELECT
144
- MAX(CASE WHEN mp_user_id = $2 THEN id END) as auth_user_id,
145
- MAX(CASE WHEN mp_user_id = $2 THEN uname END) as auth_user_uname,
146
- MAX(CASE WHEN mp_user_id = $4 THEN id END) as owner_user_id,
147
- MAX(CASE WHEN mp_user_id = $4 THEN uname END) as owner_user_uname
148
- FROM user_inserts
133
+ INSERT INTO hub.user(casino_id, mp_user_id, uname)
134
+ VALUES ($1, $2, $3)
135
+ ON CONFLICT (casino_id, mp_user_id) DO UPDATE
136
+ SET uname = EXCLUDED.uname
137
+ RETURNING id, uname
138
+ `,
139
+ values: [casino.id, mpUserId, uname],
140
+ })
141
+ .then(exactlyOneRow);
142
+ const ownerUser = await pgClient
143
+ .query({
144
+ text: `
145
+ INSERT INTO hub.user(casino_id, mp_user_id, uname)
146
+ VALUES ($1, $2, $3)
147
+ ON CONFLICT (casino_id, mp_user_id) DO UPDATE
148
+ SET uname = EXCLUDED.uname
149
+ RETURNING id
149
150
  `,
150
151
  values: [
151
152
  casino.id,
152
- mpUserId,
153
- uname,
154
153
  mpExperience.userByUserId.id,
155
154
  mpExperience.userByUserId.uname,
156
155
  ],
157
156
  })
158
157
  .then(exactlyOneRow);
159
- const userId = userInsertResult.auth_user_id;
158
+ const userId = authUser.id;
160
159
  const dbExperience = await pgClient
161
160
  .query({
162
161
  text: `
@@ -170,7 +169,7 @@ export const HubAuthenticatePlugin = extendSchema(() => {
170
169
  casino.id,
171
170
  mpExperience.id,
172
171
  mpExperience.name,
173
- userInsertResult.owner_user_id,
172
+ ownerUser.id,
174
173
  ],
175
174
  })
176
175
  .then(exactlyOneRow);
@@ -191,7 +190,7 @@ export const HubAuthenticatePlugin = extendSchema(() => {
191
190
  .then(exactlyOneRow);
192
191
  const ret = {
193
192
  userId,
194
- uname: userInsertResult.auth_user_uname,
193
+ uname: authUser.uname,
195
194
  experienceId: dbExperience.id,
196
195
  sessionKey: dbSession.key,
197
196
  };
@@ -1,8 +1,8 @@
1
1
  import { access, context, object, ObjectStep, sideEffect, } from "postgraphile/grafast";
2
- import { gql, makeExtendSchemaPlugin } from "postgraphile/utils";
2
+ import { gql, extendSchema } from "postgraphile/utils";
3
3
  import * as z from "zod";
4
4
  import { GraphQLError } from "graphql";
5
- import { DbHashKind, dbLockPlayerBalanceAndHouseBankroll, exactlyOneRow, maybeOneRow, withPgPoolTransaction, } from "../db/index.js";
5
+ import { DbHashKind, dbLockHouseBankroll, dbLockPlayerBalance, exactlyOneRow, maybeOneRow, withPgPoolTransaction, } from "../db/index.js";
6
6
  import { assert } from "tsafe";
7
7
  import { dbInsertHubHash, dbLockHubHashChain, } from "../hash-chain/db-hash-chain.js";
8
8
  import { getIntermediateHash } from "../hash-chain/get-hash.js";
@@ -71,7 +71,7 @@ const BetConfigsSchema = z.record(BetKindSchema, z.object({
71
71
  export function MakeOutcomeBetPlugin({ betConfigs }) {
72
72
  BetConfigsSchema.parse(betConfigs);
73
73
  const betKinds = Object.keys(betConfigs);
74
- return makeExtendSchemaPlugin((build) => {
74
+ return extendSchema((build) => {
75
75
  const outcomeBetTable = build.input.pgRegistry.pgResources.hub_outcome_bet;
76
76
  const typeDefs = gql `
77
77
  enum BetKind {
@@ -171,14 +171,14 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
171
171
  throw new GraphQLError("Currency not found");
172
172
  }
173
173
  return withPgPoolTransaction(superuserPool, async (pgClient) => {
174
- const { dbPlayerBalance, dbHouseBankroll, found } = await dbLockPlayerBalanceAndHouseBankroll(pgClient, {
174
+ const dbPlayerBalance = await dbLockPlayerBalance(pgClient, {
175
175
  userId: session.user_id,
176
176
  casinoId: session.casino_id,
177
177
  experienceId: session.experience_id,
178
178
  currencyKey: dbCurrency.key,
179
179
  });
180
- if (!found) {
181
- throw new GraphQLError("No balance entry found for player or house");
180
+ if (!dbPlayerBalance) {
181
+ throw new GraphQLError("No balance entry found for player");
182
182
  }
183
183
  const minProfit = Math.min(...input.outcomes.map((o) => o.profit));
184
184
  const maxPlayerLoss = Math.abs(input.wager * minProfit);
@@ -194,21 +194,6 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
194
194
  }
195
195
  throw new GraphQLError("You cannot afford the worst outcome");
196
196
  }
197
- const maxProfitMultiplier = Math.max(...input.outcomes.map((o) => o.profit));
198
- const maxPotentialPayout = input.wager * maxProfitMultiplier;
199
- const riskResult = validateRisk({
200
- currency: input.currency,
201
- wager: input.wager,
202
- bankroll: dbHouseBankroll.amount,
203
- maxPotentialPayout,
204
- riskPolicy: betConfig.riskPolicy,
205
- displayUnitName: dbCurrency.display_unit_name,
206
- displayUnitScale: dbCurrency.display_unit_scale,
207
- outcomes: input.outcomes,
208
- });
209
- if (!riskResult.ok) {
210
- throw new GraphQLError(riskResult.error);
211
- }
212
197
  const dbHashChain = await dbLockHubHashChain(pgClient, {
213
198
  userId: session.user_id,
214
199
  experienceId: session.experience_id,
@@ -289,26 +274,47 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
289
274
  const outcome = input.outcomes[outcomeIdx];
290
275
  const netPlayerAmount = input.wager * outcome.profit;
291
276
  const houseBankrollDelta = -netPlayerAmount;
292
- await pgClient.query({
293
- text: `
294
- UPDATE hub.balance
295
- SET amount = amount + $1
296
- WHERE id = $2
297
- `,
298
- values: [netPlayerAmount, dbPlayerBalance.id],
277
+ const dbHouseBankroll = await dbLockHouseBankroll(pgClient, {
278
+ casinoId: session.casino_id,
279
+ currencyKey: dbCurrency.key,
299
280
  });
281
+ if (!dbHouseBankroll) {
282
+ throw new GraphQLError("No house bankroll found");
283
+ }
284
+ const maxProfitMultiplier = Math.max(...input.outcomes.map((o) => o.profit));
285
+ const maxPotentialPayout = input.wager * maxProfitMultiplier;
286
+ const riskResult = validateRisk({
287
+ currency: input.currency,
288
+ wager: input.wager,
289
+ bankroll: dbHouseBankroll.amount,
290
+ maxPotentialPayout,
291
+ riskPolicy: betConfig.riskPolicy,
292
+ displayUnitName: dbCurrency.display_unit_name,
293
+ displayUnitScale: dbCurrency.display_unit_scale,
294
+ outcomes: input.outcomes,
295
+ });
296
+ if (!riskResult.ok) {
297
+ throw new GraphQLError(riskResult.error);
298
+ }
300
299
  await pgClient.query({
301
300
  text: `
301
+ WITH balance_update AS (
302
+ UPDATE hub.balance
303
+ SET amount = amount + $1
304
+ WHERE id = $2
305
+ )
302
306
  UPDATE hub.bankroll
303
- SET amount = amount + $2,
304
- bets = bets + 1,
305
- wagered = wagered + $3
306
- WHERE id = $1
307
+ SET amount = amount + $3,
308
+ wagered = wagered + $4,
309
+ bets = bets + 1
310
+ WHERE id = $5
307
311
  `,
308
312
  values: [
309
- dbHouseBankroll.id,
313
+ netPlayerAmount,
314
+ dbPlayerBalance.id,
310
315
  houseBankrollDelta,
311
316
  input.wager,
317
+ dbHouseBankroll.id,
312
318
  ],
313
319
  });
314
320
  const immutableData = structuredClone({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moneypot/hub",
3
- "version": "1.16.0-dev.3",
3
+ "version": "1.16.0-dev.4",
4
4
  "author": "moneypot.com",
5
5
  "homepage": "https://moneypot.com/hub",
6
6
  "keywords": [