@moneypot/hub 1.9.0-dev.9 → 1.10.0-dev.1

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 (42) hide show
  1. package/dist/cli/add-casino.js +24 -17
  2. package/dist/src/__generated__/gql.d.ts +2 -2
  3. package/dist/src/__generated__/gql.js +1 -1
  4. package/dist/src/__generated__/graphql.d.ts +23 -5
  5. package/dist/src/__generated__/graphql.js +3 -1
  6. package/dist/src/context.d.ts +12 -0
  7. package/dist/src/context.js +31 -0
  8. package/dist/src/db/index.d.ts +3 -6
  9. package/dist/src/db/index.js +5 -13
  10. package/dist/src/db/types.d.ts +1 -0
  11. package/dist/src/db/util.d.ts +4 -0
  12. package/dist/src/hash-chain/plugins/hub-create-hash-chain.js +3 -2
  13. package/dist/src/index.d.ts +7 -4
  14. package/dist/src/index.js +23 -12
  15. package/dist/src/pg-versions/010-take-request-refunded-at.sql +1 -0
  16. package/dist/src/plugins/hub-add-casino.js +4 -2
  17. package/dist/src/plugins/hub-authenticate.js +3 -2
  18. package/dist/src/plugins/hub-claim-faucet.js +5 -3
  19. package/dist/src/plugins/hub-make-outcome-bet.d.ts +1 -10
  20. package/dist/src/plugins/hub-make-outcome-bet.js +20 -55
  21. package/dist/src/plugins/hub-withdraw.js +3 -2
  22. package/dist/src/process-transfers/index.d.ts +5 -2
  23. package/dist/src/process-transfers/index.js +15 -11
  24. package/dist/src/process-transfers/polling-processor.d.ts +3 -1
  25. package/dist/src/process-transfers/polling-processor.js +17 -13
  26. package/dist/src/process-transfers/process-transfer.d.ts +3 -1
  27. package/dist/src/process-transfers/process-transfer.js +7 -8
  28. package/dist/src/process-transfers/websocket-processor.d.ts +3 -1
  29. package/dist/src/process-transfers/websocket-processor.js +3 -1
  30. package/dist/src/process-withdrawal-request.d.ts +3 -1
  31. package/dist/src/process-withdrawal-request.js +6 -5
  32. package/dist/src/risk-policy.d.ts +20 -0
  33. package/dist/src/risk-policy.js +61 -0
  34. package/dist/src/server/graphile.config.d.ts +3 -1
  35. package/dist/src/server/graphile.config.js +6 -5
  36. package/dist/src/server/index.d.ts +3 -1
  37. package/dist/src/server/index.js +5 -4
  38. package/dist/src/server/middleware/authentication.d.ts +2 -1
  39. package/dist/src/server/middleware/authentication.js +5 -5
  40. package/dist/src/take-request/process-take-request.d.ts +13 -2
  41. package/dist/src/take-request/process-take-request.js +70 -23
  42. package/package.json +1 -1
@@ -5,7 +5,7 @@ import { GET_USER_FROM_USER_TOKEN } from "../graphql-queries.js";
5
5
  import { exactlyOneRow, maybeOneRow } from "../db/util.js";
6
6
  import { createGraphqlClient } from "../graphql-client.js";
7
7
  import { constant, context, error, object, sideEffect, } from "postgraphile/grafast";
8
- import { superuserPool, withPgPoolTransaction, } from "../db/index.js";
8
+ import { withPgPoolTransaction, } from "../db/index.js";
9
9
  import { logger } from "../logger.js";
10
10
  import * as jwtService from "../services/jwt-service.js";
11
11
  import { extractGraphQLErrorInfo, isGraphQLError } from "../GraphQLError.js";
@@ -51,7 +51,8 @@ export const HubAuthenticatePlugin = makeExtendSchemaPlugin(() => {
51
51
  hubAuthenticate(_, { $input }) {
52
52
  try {
53
53
  const $context = context();
54
- const $success = sideEffect([$input, $context], ([rawInput, context]) => {
54
+ const $superuserPool = $context.get("superuserPool");
55
+ const $success = sideEffect([$input, $superuserPool, $context], ([rawInput, superuserPool, context]) => {
55
56
  return withPgPoolTransaction(superuserPool, async (pgClient) => {
56
57
  let input;
57
58
  try {
@@ -1,6 +1,6 @@
1
1
  import { object } from "grafast";
2
2
  import { gql, makeExtendSchemaPlugin } from "postgraphile/utils";
3
- import { superuserPool, withPgPoolTransaction, } from "../db/index.js";
3
+ import { withPgPoolTransaction, } from "../db/index.js";
4
4
  import { constant, context, sideEffect } from "postgraphile/grafast";
5
5
  import { assert } from "tsafe";
6
6
  const CLAIM_AMOUNT = 1000;
@@ -20,8 +20,10 @@ export const HubClaimFaucetPlugin = makeExtendSchemaPlugin(() => {
20
20
  Mutation: {
21
21
  plans: {
22
22
  hubClaimFaucet() {
23
- const $identity = context().get("identity");
24
- const $result = sideEffect([$identity], ([identity]) => {
23
+ const $context = context();
24
+ const $identity = $context.get("identity");
25
+ const $superuserPool = $context.get("superuserPool");
26
+ const $result = sideEffect([$identity, $superuserPool], ([identity, superuserPool]) => {
25
27
  if (identity?.kind !== "user") {
26
28
  throw new Error("Must be logged in as user");
27
29
  }
@@ -1,6 +1,7 @@
1
1
  import * as z from "zod";
2
2
  import { DbOutcome } from "../db/index.js";
3
3
  import { Result } from "../util.js";
4
+ import { RiskPolicy } from "../risk-policy.js";
4
5
  declare const InputSchema: z.ZodObject<{
5
6
  kind: z.ZodString;
6
7
  clientSeed: z.ZodString;
@@ -75,16 +76,6 @@ export type OutcomeBetConfig = {
75
76
  export type OutcomeBetConfigMap<BetKind extends string> = {
76
77
  [betKind in BetKind]: OutcomeBetConfig;
77
78
  };
78
- type AtLeastOneKey<T, Keys extends keyof T = keyof T> = Keys extends keyof T ? Required<Pick<T, Keys>> & Partial<Omit<T, Keys>> : never;
79
- export type RiskLimits = AtLeastOneKey<{
80
- maxWager?: number;
81
- maxPayout?: number;
82
- }>;
83
- export type RiskPolicy = (args: {
84
- currency: string;
85
- wager: number;
86
- bankroll: number;
87
- }) => RiskLimits;
88
79
  export declare function MakeOutcomeBetPlugin<BetKind extends string>({ betConfigs }: {
89
80
  betConfigs: OutcomeBetConfigMap<BetKind>;
90
81
  }): GraphileConfig.Plugin;
@@ -2,13 +2,13 @@ import { access, context, object, ObjectStep, sideEffect, } from "postgraphile/g
2
2
  import { gql, makeExtendSchemaPlugin } from "postgraphile/utils";
3
3
  import * as z from "zod";
4
4
  import { GraphQLError } from "graphql";
5
- import { DbHashKind, dbLockPlayerBalanceAndHouseBankroll, exactlyOneRow, maybeOneRow, superuserPool, withPgPoolTransaction, } from "../db/index.js";
5
+ import { DbHashKind, dbLockPlayerBalanceAndHouseBankroll, 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, getPreimageHash, } from "../hash-chain/get-hash.js";
9
9
  import { makeFinalHash, pickRandomOutcome } from "../hash-chain/util.js";
10
10
  import { logger } from "../logger.js";
11
- import { formatCurrency } from "../format-currency.js";
11
+ import { validateRisk } from "../risk-policy.js";
12
12
  const FLOAT_EPSILON = 1e-10;
13
13
  function sum(ns) {
14
14
  return ns.reduce((a, b) => a + b, 0);
@@ -65,15 +65,6 @@ const BetConfigsSchema = z.record(BetKindSchema, z.object({
65
65
  .function()
66
66
  .optional(),
67
67
  }));
68
- const RiskLimitsSchema = z
69
- .object({
70
- maxWager: z.number().int().positive().optional(),
71
- maxPayout: z.number().int().positive().optional(),
72
- })
73
- .strict()
74
- .refine((v) => v.maxWager !== undefined || v.maxPayout !== undefined, {
75
- message: "Provide at least one of maxWager or maxPayout.",
76
- });
77
68
  export function MakeOutcomeBetPlugin({ betConfigs }) {
78
69
  BetConfigsSchema.parse(betConfigs);
79
70
  const betKinds = Object.keys(betConfigs);
@@ -115,7 +106,8 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
115
106
  plans: {
116
107
  hubMakeOutcomeBet: (_, { $input }) => {
117
108
  const $identity = context().get("identity");
118
- const $result = sideEffect([$identity, $input], async ([identity, rawInput]) => {
109
+ const $superuserPool = context().get("superuserPool");
110
+ const $result = sideEffect([$identity, $superuserPool, $input], async ([identity, superuserPool, rawInput]) => {
119
111
  if (identity?.kind !== "user") {
120
112
  throw new GraphQLError("Unauthorized");
121
113
  }
@@ -196,47 +188,19 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
196
188
  throw new GraphQLError("You cannot afford the worst outcome");
197
189
  }
198
190
  const maxProfitMultiplier = Math.max(...input.outcomes.map((o) => o.profit));
199
- const maxPayout = input.wager * maxProfitMultiplier;
200
- if (maxPayout > dbHouseBankroll.amount) {
201
- throw new GraphQLError(`House cannot cover potential payout (${formatCurrency(maxPayout, {
202
- displayUnitName: dbCurrency.display_unit_name,
203
- displayUnitScale: dbCurrency.display_unit_scale,
204
- })}). Bankroll: ${formatCurrency(dbHouseBankroll.amount, {
205
- displayUnitName: dbCurrency.display_unit_name,
206
- displayUnitScale: dbCurrency.display_unit_scale,
207
- })}`);
208
- }
209
- const riskLimitsResult = RiskLimitsSchema.safeParse(betConfig.riskPolicy
210
- ? betConfig.riskPolicy({
211
- currency: input.currency,
212
- wager: input.wager,
213
- bankroll: dbHouseBankroll.amount,
214
- })
215
- : {});
216
- if (!riskLimitsResult.success) {
217
- logger.error(riskLimitsResult.error, "Invalid risk policy");
218
- throw new GraphQLError("Invalid risk policy");
219
- }
220
- const riskLimits = riskLimitsResult.data;
221
- if (riskLimits.maxWager != null &&
222
- input.wager > riskLimits.maxWager) {
223
- throw new GraphQLError(`Wager exceeds limit (${formatCurrency(riskLimits.maxWager, {
224
- displayUnitName: dbCurrency.display_unit_name,
225
- displayUnitScale: dbCurrency.display_unit_scale,
226
- })}). Your wager: ${formatCurrency(input.wager, {
227
- displayUnitName: dbCurrency.display_unit_name,
228
- displayUnitScale: dbCurrency.display_unit_scale,
229
- })}`);
230
- }
231
- if (riskLimits.maxPayout != null &&
232
- maxPayout > riskLimits.maxPayout) {
233
- throw new GraphQLError(`Payout exceeds limit (${formatCurrency(riskLimits.maxPayout, {
234
- displayUnitName: dbCurrency.display_unit_name,
235
- displayUnitScale: dbCurrency.display_unit_scale,
236
- })}). Your payout: ${formatCurrency(maxPayout, {
237
- displayUnitName: dbCurrency.display_unit_name,
238
- displayUnitScale: dbCurrency.display_unit_scale,
239
- })}`);
191
+ const maxPotentialPayout = input.wager * maxProfitMultiplier;
192
+ const riskResult = validateRisk({
193
+ currency: input.currency,
194
+ wager: input.wager,
195
+ bankroll: dbHouseBankroll.amount,
196
+ maxPotentialPayout,
197
+ riskPolicy: betConfig.riskPolicy,
198
+ displayUnitName: dbCurrency.display_unit_name,
199
+ displayUnitScale: dbCurrency.display_unit_scale,
200
+ outcomes: input.outcomes,
201
+ });
202
+ if (!riskResult.ok) {
203
+ throw new GraphQLError(riskResult.error);
240
204
  }
241
205
  const dbHashChain = await dbLockHubHashChain(pgClient, {
242
206
  userId: session.user_id,
@@ -254,6 +218,7 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
254
218
  if (dbHashChain.current_iteration === 1) {
255
219
  finishHashChainInBackground({
256
220
  hashChainId: input.hashChainId,
221
+ pool: superuserPool,
257
222
  }).catch((e) => {
258
223
  logger.error({ hashChainId: input.hashChainId, error: e }, "Error finishing hash chain in background");
259
224
  });
@@ -440,7 +405,7 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
440
405
  };
441
406
  }, "HubMakeOutcomeBetPlugin");
442
407
  }
443
- async function finishHashChainInBackground({ hashChainId, }) {
408
+ async function finishHashChainInBackground({ hashChainId, pool, }) {
444
409
  logger.debug({ hashChainId }, "Finishing hash chain in background");
445
410
  const preimageHashResult = await getPreimageHash({
446
411
  hashChainId,
@@ -453,7 +418,7 @@ async function finishHashChainInBackground({ hashChainId, }) {
453
418
  digest: preimageHashResult.hash,
454
419
  iteration: 0,
455
420
  }, "Inserting preimage hash");
456
- await withPgPoolTransaction(superuserPool, async (pgClient) => {
421
+ await withPgPoolTransaction(pool, async (pgClient) => {
457
422
  await dbInsertHubHash(pgClient, {
458
423
  hashChainId,
459
424
  kind: DbHashKind.PREIMAGE,
@@ -1,6 +1,6 @@
1
1
  import { constant, context, object, sideEffect } from "postgraphile/grafast";
2
2
  import { gql, makeExtendSchemaPlugin } from "postgraphile/utils";
3
- import { superuserPool, withPgPoolTransaction, } from "../db/index.js";
3
+ import { withPgPoolTransaction } from "../db/index.js";
4
4
  import { exactlyOneRow, maybeOneRow } from "../db/util.js";
5
5
  import { GraphQLError } from "graphql";
6
6
  export const HubWithdrawPlugin = makeExtendSchemaPlugin((build) => {
@@ -26,7 +26,8 @@ export const HubWithdrawPlugin = makeExtendSchemaPlugin((build) => {
26
26
  plans: {
27
27
  hubWithdraw(_, { $input }) {
28
28
  const $identity = context().get("identity");
29
- const $withdrawalRequestId = sideEffect([$input, $identity], ([input, identity]) => {
29
+ const $superuserPool = context().get("superuserPool");
30
+ const $withdrawalRequestId = sideEffect([$input, $identity, $superuserPool], ([input, identity, superuserPool]) => {
30
31
  if (identity?.kind !== "user") {
31
32
  throw new GraphQLError("You must be logged in");
32
33
  }
@@ -1,9 +1,12 @@
1
1
  import { DbCasino, DbCasinoSecret } from "../db/index.js";
2
+ import * as pg from "pg";
2
3
  export type CasinoWithSecret = DbCasino & DbCasinoSecret;
3
- export declare function startCasinoTransferProcessor({ casinoId, signal, }: {
4
+ export declare function startCasinoTransferProcessor({ casinoId, signal, pool, }: {
4
5
  casinoId: string;
5
6
  signal: AbortSignal;
7
+ pool: pg.Pool;
6
8
  }): Promise<void>;
7
- export declare function initializeTransferProcessors({ signal, }: {
9
+ export declare function initializeTransferProcessors({ signal, pool, }: {
8
10
  signal: AbortSignal;
11
+ pool: pg.Pool;
9
12
  }): void;
@@ -1,6 +1,5 @@
1
1
  import { startPollingProcessor } from "./polling-processor.js";
2
2
  import { startWebsocketProcessor } from "./websocket-processor.js";
3
- import { superuserPool } from "../db/index.js";
4
3
  import { dbGetCasinoById, dbGetCasinoSecretById } from "../db/internal.js";
5
4
  import assert from "assert";
6
5
  import { logger } from "../logger.js";
@@ -9,28 +8,29 @@ import * as db from "../db/index.js";
9
8
  import * as pg from "pg";
10
9
  import { z } from "zod";
11
10
  const activeCasinos = new Set();
12
- export async function startCasinoTransferProcessor({ casinoId, signal, }) {
11
+ export async function startCasinoTransferProcessor({ casinoId, signal, pool, }) {
13
12
  if (activeCasinos.has(casinoId)) {
14
13
  throw new Error(`processor already running for casino ${casinoId}`);
15
14
  }
16
- const casino = await dbGetCasinoById(superuserPool, casinoId);
17
- const secret = await dbGetCasinoSecretById(superuserPool, casinoId);
15
+ const casino = await dbGetCasinoById(pool, casinoId);
16
+ const secret = await dbGetCasinoSecretById(pool, casinoId);
18
17
  assert(casino, `Casino not found for casino id ${casinoId}`);
19
18
  assert(secret, `Secret not found for casino id ${casinoId}`);
20
19
  activeCasinos.add(casinoId);
21
- startPollingProcessor({ casinoId, signal });
20
+ startPollingProcessor({ casinoId, signal, pool });
22
21
  startWebsocketProcessor({
23
22
  casinoId,
24
23
  graphqlUrl: casino.graphql_url,
25
24
  signal,
26
25
  controllerId: secret.controller_id,
27
26
  apiKey: secret.api_key,
27
+ pool,
28
28
  });
29
29
  }
30
- export function initializeTransferProcessors({ signal, }) {
30
+ export function initializeTransferProcessors({ signal, pool, }) {
31
31
  (async () => {
32
32
  try {
33
- const casinos = await db.listCasinos(superuserPool);
33
+ const casinos = await db.listCasinos(pool);
34
34
  for (const casino of casinos) {
35
35
  if (!URL.canParse(casino.graphql_url)) {
36
36
  logger.warn(`Skipping casino ${casino.id} due to invalid graphql_url: "${casino.graphql_url}"`);
@@ -41,16 +41,16 @@ export function initializeTransferProcessors({ signal, }) {
41
41
  logger.warn(`${casino.id} has localhost endpoint "${casino.graphql_url}" while NODE_ENV=production.`);
42
42
  }
43
43
  logger.info(`Starting casino processor for "${casino.name}" at "${casino.graphql_url}"`);
44
- startCasinoTransferProcessor({ casinoId: casino.id, signal });
44
+ startCasinoTransferProcessor({ casinoId: casino.id, signal, pool });
45
45
  }
46
- await listenForNewCasinos({ signal });
46
+ await listenForNewCasinos({ signal, pool });
47
47
  }
48
48
  catch (e) {
49
49
  logger.error(e, `Error initializing transfer processors`);
50
50
  }
51
51
  })();
52
52
  }
53
- async function listenForNewCasinos({ signal }) {
53
+ async function listenForNewCasinos({ signal, pool, }) {
54
54
  const pgClient = new pg.Client(config.SUPERUSER_DATABASE_URL);
55
55
  await pgClient.connect();
56
56
  const NewCasinoPayload = z.object({
@@ -76,7 +76,11 @@ async function listenForNewCasinos({ signal }) {
76
76
  logger.error(result.error, "Error parsing new casino notification");
77
77
  return;
78
78
  }
79
- startCasinoTransferProcessor({ casinoId: result.data.id, signal });
79
+ startCasinoTransferProcessor({
80
+ casinoId: result.data.id,
81
+ signal,
82
+ pool,
83
+ });
80
84
  break;
81
85
  }
82
86
  }
@@ -1,10 +1,12 @@
1
1
  import * as db from "../db/index.js";
2
+ import * as pg from "pg";
2
3
  export declare const PAGINATE_TRANSFERS: import("@graphql-typed-document-node/core").TypedDocumentNode<import("../__generated__/graphql.js").PaginateTransfersQuery, import("../__generated__/graphql.js").Exact<{
3
4
  controllerId: import("../__generated__/graphql.js").Scalars["UUID"]["input"];
4
5
  after?: import("../__generated__/graphql.js").InputMaybe<import("../__generated__/graphql.js").Scalars["Cursor"]["input"]>;
5
6
  limit?: import("../__generated__/graphql.js").InputMaybe<import("../__generated__/graphql.js").Scalars["Int"]["input"]>;
6
7
  }>>;
7
- export declare function startPollingProcessor({ casinoId, signal, }: {
8
+ export declare function startPollingProcessor({ casinoId, signal, pool, }: {
8
9
  casinoId: db.DbCasino["id"];
9
10
  signal: AbortSignal;
11
+ pool: pg.Pool;
10
12
  }): void;
@@ -8,7 +8,6 @@ import { processWithdrawalRequests } from "../process-withdrawal-request.js";
8
8
  import { processTakeRequests } from "../take-request/process-take-request.js";
9
9
  import * as jwtService from "../services/jwt-service.js";
10
10
  import { dbGetCasinoById, dbGetCasinoSecretById } from "../db/internal.js";
11
- import { superuserPool } from "../db/index.js";
12
11
  import { TransferStatusKind } from "../__generated__/graphql.js";
13
12
  import { MP_COMPLETE_TRANSFER, processTransfer } from "./process-transfer.js";
14
13
  import { gql } from "../__generated__/gql.js";
@@ -44,7 +43,7 @@ export const PAGINATE_TRANSFERS = gql(`
44
43
  `);
45
44
  const MIN_BACKOFF_TIME = 5000;
46
45
  const MAX_BACKOFF_TIME = 30 * 1000;
47
- export function startPollingProcessor({ casinoId, signal, }) {
46
+ export function startPollingProcessor({ casinoId, signal, pool, }) {
48
47
  if (signal.aborted) {
49
48
  logger.info(`[startTransferProcessor] AbortSignal aborted. Not starting processor for casino ${casinoId}`);
50
49
  return;
@@ -55,7 +54,7 @@ export function startPollingProcessor({ casinoId, signal, }) {
55
54
  lastAttempt: 0,
56
55
  };
57
56
  (async () => {
58
- let cursor = await db.getTransferCursor(superuserPool, {
57
+ let cursor = await db.getTransferCursor(pool, {
59
58
  casinoId,
60
59
  });
61
60
  if (cursor && !isUuid(cursor)) {
@@ -80,13 +79,13 @@ export function startPollingProcessor({ casinoId, signal, }) {
80
79
  await new Promise((resolve) => setTimeout(resolve, timeToWait));
81
80
  }
82
81
  processorState.lastAttempt = Date.now();
83
- const casino = await dbGetCasinoById(superuserPool, casinoId);
82
+ const casino = await dbGetCasinoById(pool, casinoId);
84
83
  if (!casino) {
85
84
  logger.warn(`casino ${casinoId} not found. Stopping processor...`);
86
85
  shouldStop = true;
87
86
  break;
88
87
  }
89
- const casinoSecret = await dbGetCasinoSecretById(superuserPool, casino.id);
88
+ const casinoSecret = await dbGetCasinoSecretById(pool, casino.id);
90
89
  if (!casinoSecret) {
91
90
  logger.warn(`Casino secret hasn't been set for casino ${casinoId}. Skipping...`);
92
91
  continue;
@@ -99,7 +98,7 @@ export function startPollingProcessor({ casinoId, signal, }) {
99
98
  logger.info(`[startTransferProcessor] Aborted by graceful shutdown. (1)`);
100
99
  break;
101
100
  }
102
- await jwtService.refreshCasinoJwksTask(superuserPool, {
101
+ await jwtService.refreshCasinoJwksTask(pool, {
103
102
  casinoId: casino.id,
104
103
  graphqlClient,
105
104
  });
@@ -114,7 +113,7 @@ export function startPollingProcessor({ casinoId, signal, }) {
114
113
  return (res.allCurrencies?.nodes || []).flatMap((x) => x ? [x] : []);
115
114
  });
116
115
  if (currencies.length > 0) {
117
- await db.upsertCurrencies(superuserPool, {
116
+ await db.upsertCurrencies(pool, {
118
117
  casinoId: casino.id,
119
118
  currencies,
120
119
  });
@@ -130,6 +129,7 @@ export function startPollingProcessor({ casinoId, signal, }) {
130
129
  graphqlClient,
131
130
  casinoInfo: { ...casino, ...casinoSecret },
132
131
  signal,
132
+ pool,
133
133
  });
134
134
  if (signal.aborted) {
135
135
  logger.info(`[startTransferProcessor] Aborted by graceful shutdown. (4)`);
@@ -139,6 +139,7 @@ export function startPollingProcessor({ casinoId, signal, }) {
139
139
  abortSignal: signal,
140
140
  casinoId: casino.id,
141
141
  graphqlClient,
142
+ pool,
142
143
  });
143
144
  if (signal.aborted) {
144
145
  logger.info(`[startTransferProcessor] Aborted by graceful shutdown. (5)`);
@@ -149,6 +150,7 @@ export function startPollingProcessor({ casinoId, signal, }) {
149
150
  controllerId: casinoSecret.controller_id,
150
151
  casinoId: casino.id,
151
152
  graphqlClient,
153
+ pool,
152
154
  });
153
155
  processorState.backoffTime = MIN_BACKOFF_TIME;
154
156
  }
@@ -161,11 +163,11 @@ export function startPollingProcessor({ casinoId, signal, }) {
161
163
  logger.info(`processor stopped for casino ${casinoId}`);
162
164
  })();
163
165
  }
164
- async function processWithdrawals({ abortSignal, casinoId, graphqlClient, }) {
166
+ async function processWithdrawals({ abortSignal, casinoId, graphqlClient, pool, }) {
165
167
  if (abortSignal.aborted) {
166
168
  return;
167
169
  }
168
- const withdrawals = await db.getPendingWithdrawals(superuserPool, {
170
+ const withdrawals = await db.getPendingWithdrawals(pool, {
169
171
  casinoId,
170
172
  limit: 10,
171
173
  });
@@ -185,7 +187,7 @@ async function processWithdrawals({ abortSignal, casinoId, graphqlClient, }) {
185
187
  case undefined:
186
188
  break;
187
189
  case "CompleteTransferSuccess": {
188
- await db.settleWithdrawal({
190
+ await db.settleWithdrawal(pool, {
189
191
  withdrawalId: withdrawal.id,
190
192
  newStatus: "COMPLETED",
191
193
  });
@@ -198,7 +200,7 @@ async function processWithdrawals({ abortSignal, casinoId, graphqlClient, }) {
198
200
  switch (currentState) {
199
201
  case TransferStatusKind.Canceled:
200
202
  case TransferStatusKind.Completed:
201
- await db.settleWithdrawal({
203
+ await db.settleWithdrawal(pool, {
202
204
  withdrawalId: withdrawal.id,
203
205
  newStatus: currentState,
204
206
  });
@@ -224,13 +226,14 @@ async function processWithdrawals({ abortSignal, casinoId, graphqlClient, }) {
224
226
  }
225
227
  }
226
228
  }
227
- async function processTransfersUntilEmpty({ afterCursor, graphqlClient, casinoInfo, signal, }) {
229
+ async function processTransfersUntilEmpty({ afterCursor, graphqlClient, casinoInfo, signal, pool, }) {
228
230
  let hasNextPage = true;
229
231
  const timeout = (ms) => new Promise((res) => setTimeout(res, ms));
230
232
  while (hasNextPage && !signal.aborted) {
231
233
  await processWithdrawalRequests({
232
234
  casinoId: casinoInfo.id,
233
235
  graphqlClient,
236
+ pool,
234
237
  });
235
238
  if (signal.aborted) {
236
239
  logger.info(`[processTransfersUntilEmpty] Aborted by graceful shutdown.`);
@@ -266,8 +269,9 @@ async function processTransfersUntilEmpty({ afterCursor, graphqlClient, casinoIn
266
269
  controllerId: casinoInfo.controller_id,
267
270
  transfer,
268
271
  graphqlClient,
272
+ pool,
269
273
  });
270
- await db.setTransferCursor(superuserPool, {
274
+ await db.setTransferCursor(pool, {
271
275
  cursor,
272
276
  casinoId: casinoInfo.id,
273
277
  });
@@ -1,13 +1,15 @@
1
1
  import { TransferFieldsFragment } from "../__generated__/graphql.js";
2
2
  import { GraphQLClient } from "graphql-request";
3
+ import * as pg from "pg";
3
4
  export declare const MP_COMPLETE_TRANSFER: import("@graphql-typed-document-node/core").TypedDocumentNode<import("../__generated__/graphql.js").CompleteTransferMutation, import("../__generated__/graphql.js").Exact<{
4
5
  mpTransferId: import("../__generated__/graphql.js").Scalars["UUID"]["input"];
5
6
  }>>;
6
- export declare function processTransfer({ casinoId, controllerId, transfer, graphqlClient, }: {
7
+ export declare function processTransfer({ casinoId, controllerId, transfer, graphqlClient, pool, }: {
7
8
  casinoId: string;
8
9
  controllerId: string;
9
10
  transfer: Extract<TransferFieldsFragment, {
10
11
  __typename: "ExperienceTransfer";
11
12
  }>;
12
13
  graphqlClient: GraphQLClient;
14
+ pool: pg.Pool;
13
15
  }): Promise<void>;
@@ -1,6 +1,5 @@
1
1
  import * as db from "../db/index.js";
2
2
  import { TransferStatusKind, } from "../__generated__/graphql.js";
3
- import { superuserPool } from "../db/index.js";
4
3
  import { gql } from "../__generated__/gql.js";
5
4
  import { logger } from "../logger.js";
6
5
  import { assert } from "tsafe";
@@ -50,7 +49,7 @@ const MP_CLAIM_TRANSFER = gql(`
50
49
  }
51
50
  }
52
51
  `);
53
- export async function processTransfer({ casinoId, controllerId, transfer, graphqlClient, }) {
52
+ export async function processTransfer({ casinoId, controllerId, transfer, graphqlClient, pool, }) {
54
53
  assert(transfer, "Expected transfer");
55
54
  assert(transfer.__typename === "ExperienceTransfer", `Expected ExperienceTransfer but got ${transfer.__typename}`);
56
55
  logger.debug(`processing transfer ${transfer.id} for casino ${casinoId}...`);
@@ -62,17 +61,17 @@ export async function processTransfer({ casinoId, controllerId, transfer, graphq
62
61
  : transfer.holderByToHolderId;
63
62
  assert(user?.__typename === "User", "Expected user transfer participant");
64
63
  const currency = transfer.currencyByCurrency;
65
- const dbSender = await db.upsertUser(superuserPool, {
64
+ const dbSender = await db.upsertUser(pool, {
66
65
  casinoId,
67
66
  mpUserId: user.id,
68
67
  uname: user.uname,
69
68
  });
70
- const dbExperience = await db.upsertExperience(superuserPool, {
69
+ const dbExperience = await db.upsertExperience(pool, {
71
70
  casinoId,
72
71
  mpExperienceId: transfer.experienceByExperienceId.id,
73
72
  name: transfer.experienceByExperienceId.name,
74
73
  });
75
- await db.upsertCurrencies(superuserPool, {
74
+ await db.upsertCurrencies(pool, {
76
75
  casinoId,
77
76
  currencies: [currency],
78
77
  });
@@ -85,7 +84,7 @@ export async function processTransfer({ casinoId, controllerId, transfer, graphq
85
84
  case TransferStatusKind.Expired:
86
85
  return;
87
86
  case TransferStatusKind.Completed: {
88
- await db.insertDeposit({
87
+ await db.insertDeposit(pool, {
89
88
  casinoId,
90
89
  mpTransferId: transfer.id,
91
90
  userId: dbSender.id,
@@ -110,7 +109,7 @@ export async function processTransfer({ casinoId, controllerId, transfer, graphq
110
109
  if (data.claimTransfer?.result.__typename !== "ClaimTransferSuccess") {
111
110
  throw new Error(`Failed to claim transfer: ${JSON.stringify(data.claimTransfer)}`);
112
111
  }
113
- await db.insertDeposit({
112
+ await db.insertDeposit(pool, {
114
113
  casinoId,
115
114
  mpTransferId: transfer.id,
116
115
  userId: dbSender.id,
@@ -130,7 +129,7 @@ export async function processTransfer({ casinoId, controllerId, transfer, graphq
130
129
  logger.debug(`I sent ${user.uname} ${transfer.amount} base units of ${currency.id}`);
131
130
  switch (transfer.status) {
132
131
  case TransferStatusKind.Canceled:
133
- await db.settleWithdrawal({
132
+ await db.settleWithdrawal(pool, {
134
133
  withdrawalId: transfer.id,
135
134
  newStatus: "CANCELED",
136
135
  });
@@ -1,7 +1,9 @@
1
- export declare function startWebsocketProcessor({ casinoId, graphqlUrl, signal, controllerId, apiKey, }: {
1
+ import * as pg from "pg";
2
+ export declare function startWebsocketProcessor({ casinoId, graphqlUrl, signal, controllerId, apiKey, pool, }: {
2
3
  casinoId: string;
3
4
  graphqlUrl: string;
4
5
  signal: AbortSignal;
5
6
  controllerId: string;
6
7
  apiKey: string;
8
+ pool: pg.Pool;
7
9
  }): void;
@@ -49,7 +49,7 @@ async function mpGetExperienceTransfer(graphqlClient, id) {
49
49
  const mpTransfer = useFragment(TRANSFER_FIELDS, x);
50
50
  return mpTransfer;
51
51
  }
52
- export function startWebsocketProcessor({ casinoId, graphqlUrl, signal, controllerId, apiKey, }) {
52
+ export function startWebsocketProcessor({ casinoId, graphqlUrl, signal, controllerId, apiKey, pool, }) {
53
53
  logger.info(`Starting websocket processor for ${graphqlUrl} using apiKey ${apiKey}`);
54
54
  const client = createWebsocketClient({
55
55
  url: httpToWs(new URL(graphqlUrl)).toString(),
@@ -89,6 +89,7 @@ export function startWebsocketProcessor({ casinoId, graphqlUrl, signal, controll
89
89
  controllerId,
90
90
  transfer: mpTransfer,
91
91
  graphqlClient,
92
+ pool,
92
93
  });
93
94
  });
94
95
  const dispose2 = createSubscription(client, MP_NEW_TAKE_REQUEST, async (result) => {
@@ -107,6 +108,7 @@ export function startWebsocketProcessor({ casinoId, graphqlUrl, signal, controll
107
108
  mpTakeRequest,
108
109
  casinoId,
109
110
  graphqlClient,
111
+ pool,
110
112
  });
111
113
  });
112
114
  signal.addEventListener("abort", () => {
@@ -1,5 +1,7 @@
1
+ import * as pg from "pg";
1
2
  import { GraphQLClient } from "graphql-request";
2
- export declare function processWithdrawalRequests({ casinoId, graphqlClient, }: {
3
+ export declare function processWithdrawalRequests({ casinoId, graphqlClient, pool, }: {
3
4
  casinoId: string;
4
5
  graphqlClient: GraphQLClient;
6
+ pool: pg.Pool;
5
7
  }): Promise<void>;
@@ -1,11 +1,11 @@
1
1
  import { assert } from "tsafe";
2
2
  import { TransferStatusKind } from "./__generated__/graphql.js";
3
- import { withPgPoolTransaction, superuserPool } from "./db/index.js";
3
+ import { withPgPoolTransaction } from "./db/index.js";
4
4
  import { maybeOneRow, exactlyOneRow } from "./db/util.js";
5
5
  import { START_PENDING_EXPERIENCE_TRANSFER } from "./graphql-queries.js";
6
6
  import { logger } from "./logger.js";
7
- async function processWithdrawalRequest({ requestId, graphqlClient, }) {
8
- return withPgPoolTransaction(superuserPool, async (pgClient) => {
7
+ async function processWithdrawalRequest({ requestId, graphqlClient, pool, }) {
8
+ return withPgPoolTransaction(pool, async (pgClient) => {
9
9
  const dbWithdrawalRequest = await pgClient
10
10
  .query({
11
11
  text: `
@@ -120,8 +120,8 @@ async function processWithdrawalRequest({ requestId, graphqlClient, }) {
120
120
  });
121
121
  });
122
122
  }
123
- export async function processWithdrawalRequests({ casinoId, graphqlClient, }) {
124
- const pendingRequests = await superuserPool.query({
123
+ export async function processWithdrawalRequests({ casinoId, graphqlClient, pool, }) {
124
+ const pendingRequests = await pool.query({
125
125
  text: `
126
126
  SELECT wr.*
127
127
  FROM hub.withdrawal_request wr
@@ -136,6 +136,7 @@ export async function processWithdrawalRequests({ casinoId, graphqlClient, }) {
136
136
  await processWithdrawalRequest({
137
137
  requestId: request.id,
138
138
  graphqlClient,
139
+ pool,
139
140
  });
140
141
  }
141
142
  catch (error) {
@@ -0,0 +1,20 @@
1
+ import { DbOutcome } from "./db/types.js";
2
+ import { Result } from "./util.js";
3
+ export type RiskPolicyArgs = {
4
+ currency: string;
5
+ wager: number;
6
+ bankroll: number;
7
+ maxPotentialPayout: number;
8
+ outcomes: DbOutcome[];
9
+ };
10
+ export type RiskLimits = {
11
+ maxWager?: number;
12
+ maxPayout: number;
13
+ };
14
+ export type RiskPolicy = (args: RiskPolicyArgs) => RiskLimits;
15
+ export declare function validateRisk(options: RiskPolicyArgs & {
16
+ riskPolicy: RiskPolicy;
17
+ } & {
18
+ displayUnitName: string;
19
+ displayUnitScale: number;
20
+ }): Result<void, string>;